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