1 /* $DragonFly: src/sys/netbt/l2cap_misc.c,v 1.2 2008/03/18 13:41:42 hasso Exp $ */ 2 /* $OpenBSD: src/sys/netbt/l2cap_misc.c,v 1.3 2008/02/24 21:34:48 uwe Exp $ */ 3 /* $NetBSD: l2cap_misc.c,v 1.5 2007/11/03 17:20:17 plunky 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/param.h> 36 #include <sys/kernel.h> 37 #include <sys/mbuf.h> 38 #include <sys/proc.h> 39 #include <sys/queue.h> 40 #include <sys/systm.h> 41 #include <net/if.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 vm_zone_t l2cap_req_pool; 53 vm_zone_t l2cap_pdu_pool; 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 void 72 l2cap_init(void) 73 { 74 ;; 75 } 76 77 /* 78 * Set Link Mode on channel 79 */ 80 int 81 l2cap_setmode(struct l2cap_channel *chan) 82 { 83 KKASSERT(chan != NULL); 84 KKASSERT(chan->lc_link != NULL); 85 86 DPRINTF("(%s) CID #%d, auth %s, encrypt %s, secure %s\n", 87 device_get_nameunit(chan->lc_link->hl_unit->hci_dev), chan->lc_lcid, 88 (chan->lc_mode & L2CAP_LM_AUTH ? "yes" : "no"), 89 (chan->lc_mode & L2CAP_LM_ENCRYPT ? "yes" : "no"), 90 (chan->lc_mode & L2CAP_LM_SECURE ? "yes" : "no")); 91 92 if (chan->lc_mode & L2CAP_LM_AUTH) 93 chan->lc_link->hl_flags |= HCI_LINK_AUTH_REQ; 94 95 if (chan->lc_mode & L2CAP_LM_ENCRYPT) 96 chan->lc_link->hl_flags |= HCI_LINK_ENCRYPT_REQ; 97 98 if (chan->lc_mode & L2CAP_LM_SECURE) 99 chan->lc_link->hl_flags |= HCI_LINK_SECURE_REQ; 100 101 return hci_acl_setmode(chan->lc_link); 102 } 103 104 /* 105 * Allocate a new Request structure & ID and set the timer going 106 */ 107 int 108 l2cap_request_alloc(struct l2cap_channel *chan, uint8_t code) 109 { 110 struct hci_link *link = chan->lc_link; 111 struct l2cap_req *req; 112 int next_id; 113 114 if (link == NULL) 115 return ENETDOWN; 116 117 /* find next ID (0 is not allowed) */ 118 next_id = link->hl_lastid + 1; 119 if (next_id > 0xff) 120 next_id = 1; 121 122 /* Ouroboros check */ 123 req = TAILQ_FIRST(&link->hl_reqs); 124 if (req && req->lr_id == next_id) 125 return ENFILE; 126 127 req = zalloc(l2cap_req_pool); 128 if (req == NULL) 129 return ENOMEM; 130 131 req->lr_id = link->hl_lastid = next_id; 132 133 req->lr_code = code; 134 req->lr_chan = chan; 135 req->lr_link = link; 136 137 callout_init(&req->lr_rtx); 138 callout_reset(&req->lr_rtx, l2cap_response_timeout * hz, 139 l2cap_rtx, req); 140 141 TAILQ_INSERT_TAIL(&link->hl_reqs, req, lr_next); 142 143 return 0; 144 } 145 146 /* 147 * Find a running request for this link 148 */ 149 struct l2cap_req * 150 l2cap_request_lookup(struct hci_link *link, uint8_t id) 151 { 152 struct l2cap_req *req; 153 154 TAILQ_FOREACH(req, &link->hl_reqs, lr_next) { 155 if (req->lr_id == id) 156 return req; 157 } 158 159 return NULL; 160 } 161 162 /* 163 * Halt and free a request 164 */ 165 void 166 l2cap_request_free(struct l2cap_req *req) 167 { 168 struct hci_link *link = req->lr_link; 169 170 callout_stop(&req->lr_rtx); 171 if (callout_active(&req->lr_rtx)) 172 return; 173 174 TAILQ_REMOVE(&link->hl_reqs, req, lr_next); 175 zfree(l2cap_req_pool, req); 176 } 177 178 /* 179 * Response Timeout eXpired 180 * 181 * No response to our request, so deal with it as best we can. 182 * 183 * XXX should try again at least with ertx? 184 */ 185 void 186 l2cap_rtx(void *arg) 187 { 188 struct l2cap_req *req = arg; 189 struct l2cap_channel *chan; 190 191 chan = req->lr_chan; 192 l2cap_request_free(req); 193 194 DPRINTF("cid %d, ident %d\n", (chan ? chan->lc_lcid : 0), req->lr_id); 195 196 if (chan && chan->lc_state != L2CAP_CLOSED) 197 l2cap_close(chan, ETIMEDOUT); 198 199 } 200 201 /* 202 * Allocate next available CID to channel. We keep a single 203 * ordered list of channels, so find the first gap. 204 * 205 * If this turns out to be not enough (!), could use a 206 * list per HCI unit.. 207 */ 208 int 209 l2cap_cid_alloc(struct l2cap_channel *chan) 210 { 211 struct l2cap_channel *used, *prev = NULL; 212 uint16_t cid = L2CAP_FIRST_CID; 213 214 if (chan->lc_lcid != L2CAP_NULL_CID || chan->lc_state != L2CAP_CLOSED) 215 return EISCONN; 216 217 LIST_FOREACH(used, &l2cap_active_list, lc_ncid) { 218 if (used->lc_lcid > cid) 219 break; /* found our gap */ 220 221 KKASSERT(used->lc_lcid == cid); 222 cid++; 223 224 if (cid == L2CAP_LAST_CID) 225 return ENFILE; 226 227 prev = used; /* for insert after */ 228 } 229 230 chan->lc_lcid = cid; 231 232 if (prev) 233 LIST_INSERT_AFTER(prev, chan, lc_ncid); 234 else 235 LIST_INSERT_HEAD(&l2cap_active_list, chan, lc_ncid); 236 237 return 0; 238 } 239 240 /* 241 * Find channel with CID 242 */ 243 struct l2cap_channel * 244 l2cap_cid_lookup(uint16_t cid) 245 { 246 struct l2cap_channel *chan; 247 248 LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) { 249 if (chan->lc_lcid == cid) 250 return chan; 251 252 if (chan->lc_lcid > cid) 253 return NULL; 254 } 255 256 return NULL; 257 } 258