1 /* $NetBSD: l2cap_misc.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $ */ 2 3 /*- 4 * Copyright (c) 2005 Iain Hibbert. 5 * Copyright (c) 2006 Itronix Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. The name of Itronix Inc. may not be used to endorse 17 * or promote products derived from this software without specific 18 * prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 * ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: l2cap_misc.c,v 1.1 2006/06/19 15:44:45 gdamore Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/kernel.h> 38 #include <sys/mbuf.h> 39 #include <sys/proc.h> 40 #include <sys/queue.h> 41 #include <sys/systm.h> 42 43 #include <netbt/bluetooth.h> 44 #include <netbt/hci.h> 45 #include <netbt/l2cap.h> 46 47 struct l2cap_channel_list 48 l2cap_active_list = LIST_HEAD_INITIALIZER(l2cap_active_list); 49 struct l2cap_channel_list 50 l2cap_listen_list = LIST_HEAD_INITIALIZER(l2cap_listen_list); 51 52 POOL_INIT(l2cap_req_pool, sizeof(struct l2cap_req), 0, 0, 0, "l2cap_req", NULL); 53 POOL_INIT(l2cap_pdu_pool, sizeof(struct l2cap_pdu), 0, 0, 0, "l2cap_pdu", NULL); 54 55 const l2cap_qos_t l2cap_default_qos = { 56 0, /* flags */ 57 L2CAP_QOS_BEST_EFFORT, /* service type */ 58 0x00000000, /* token rate */ 59 0x00000000, /* token bucket size */ 60 0x00000000, /* peak bandwidth */ 61 0xffffffff, /* latency */ 62 0xffffffff /* delay variation */ 63 }; 64 65 /* 66 * L2CAP request timeouts 67 */ 68 int l2cap_response_timeout = 30; /* seconds */ 69 int l2cap_response_extended_timeout = 180; /* seconds */ 70 71 /* 72 * Allocate a new Request structure & ID and set the timer going 73 */ 74 int 75 l2cap_request_alloc(struct l2cap_channel *chan, uint8_t code) 76 { 77 struct hci_link *link = chan->lc_link; 78 struct l2cap_req *req; 79 int next_id; 80 81 if (link == NULL) 82 return ENETDOWN; 83 84 /* find next ID (0 is not allowed) */ 85 next_id = link->hl_lastid + 1; 86 if (next_id > 0xff) 87 next_id = 1; 88 89 /* Ouroboros check */ 90 req = TAILQ_FIRST(&link->hl_reqs); 91 if (req && req->lr_id == next_id) 92 return ENFILE; 93 94 req = pool_get(&l2cap_req_pool, PR_NOWAIT); 95 if (req == NULL) 96 return ENOMEM; 97 98 req->lr_id = link->hl_lastid = next_id; 99 100 req->lr_code = code; 101 req->lr_chan = chan; 102 req->lr_link = link; 103 104 callout_init(&req->lr_rtx); 105 callout_reset(&req->lr_rtx, l2cap_response_timeout*hz, l2cap_rtx, req); 106 107 TAILQ_INSERT_TAIL(&link->hl_reqs, req, lr_next); 108 109 return 0; 110 } 111 112 /* 113 * Find a running request for this link 114 */ 115 struct l2cap_req * 116 l2cap_request_lookup(struct hci_link *link, uint8_t id) 117 { 118 struct l2cap_req *req; 119 120 TAILQ_FOREACH(req, &link->hl_reqs, lr_next) { 121 if (req->lr_id == id) 122 return req; 123 } 124 125 return NULL; 126 } 127 128 /* 129 * Halt and free a request 130 */ 131 void 132 l2cap_request_free(struct l2cap_req *req) 133 { 134 struct hci_link *link = req->lr_link; 135 136 callout_stop(&req->lr_rtx); 137 if (callout_invoking(&req->lr_rtx)) 138 return; 139 140 TAILQ_REMOVE(&link->hl_reqs, req, lr_next); 141 pool_put(&l2cap_req_pool, req); 142 } 143 144 /* 145 * Response Timeout eXpired 146 * 147 * No response to our request, so deal with it as best we can. 148 * 149 * XXX should try again at least with ertx? 150 */ 151 void 152 l2cap_rtx(void *arg) 153 { 154 struct l2cap_req *req = arg; 155 struct l2cap_channel *chan; 156 int s; 157 158 s = splsoftnet(); 159 callout_ack(&req->lr_rtx); 160 161 chan = req->lr_chan; 162 l2cap_request_free(req); 163 164 DPRINTF("cid %d, ident %d\n", (chan ? chan->lc_lcid : 0), req->lr_id); 165 166 if (chan && chan->lc_state != L2CAP_CLOSED) 167 l2cap_close(chan, ETIMEDOUT); 168 169 splx(s); 170 } 171 172 /* 173 * Allocate next available CID to channel. We keep a single 174 * ordered list of channels, so find the first gap. 175 * 176 * If this turns out to be not enough (!), could use a 177 * list per HCI unit.. 178 */ 179 int 180 l2cap_cid_alloc(struct l2cap_channel *chan) 181 { 182 struct l2cap_channel *used, *prev = NULL; 183 uint16_t cid = L2CAP_FIRST_CID; 184 185 if (chan->lc_lcid != L2CAP_NULL_CID || chan->lc_state != L2CAP_CLOSED) 186 return EISCONN; 187 188 LIST_FOREACH(used, &l2cap_active_list, lc_ncid) { 189 if (used->lc_lcid > cid) 190 break; /* found our gap */ 191 192 KASSERT(used->lc_lcid == cid); 193 cid++; 194 195 if (cid == L2CAP_LAST_CID) 196 return ENFILE; 197 198 prev = used; /* for insert after */ 199 } 200 201 chan->lc_lcid = cid; 202 203 if (prev) 204 LIST_INSERT_AFTER(prev, chan, lc_ncid); 205 else 206 LIST_INSERT_HEAD(&l2cap_active_list, chan, lc_ncid); 207 208 return 0; 209 } 210 211 /* 212 * Find channel with CID 213 */ 214 struct l2cap_channel * 215 l2cap_cid_lookup(uint16_t cid) 216 { 217 struct l2cap_channel *chan; 218 219 LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { 220 if (chan->lc_lcid == cid) 221 return chan; 222 223 if (chan->lc_lcid > cid) 224 return NULL; 225 } 226 227 return NULL; 228 } 229