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