1 /* $NetBSD: l2cap_misc.c,v 1.6 2008/04/24 11:38:37 ad 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.6 2008/04/24 11:38:37 ad 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 IPL_SOFTNET); 54 POOL_INIT(l2cap_pdu_pool, sizeof(struct l2cap_pdu), 0, 0, 0, "l2cap_pdu", NULL, 55 IPL_SOFTNET); 56 57 const l2cap_qos_t l2cap_default_qos = { 58 0, /* flags */ 59 L2CAP_QOS_BEST_EFFORT, /* service type */ 60 0x00000000, /* token rate */ 61 0x00000000, /* token bucket size */ 62 0x00000000, /* peak bandwidth */ 63 0xffffffff, /* latency */ 64 0xffffffff /* delay variation */ 65 }; 66 67 /* 68 * L2CAP request timeouts 69 */ 70 int l2cap_response_timeout = 30; /* seconds */ 71 int l2cap_response_extended_timeout = 180; /* seconds */ 72 73 /* 74 * Set Link Mode on channel 75 */ 76 int 77 l2cap_setmode(struct l2cap_channel *chan) 78 { 79 80 KASSERT(chan != NULL); 81 KASSERT(chan->lc_link != NULL); 82 83 DPRINTF("CID #%d, auth %s, encrypt %s, secure %s\n", chan->lc_lcid, 84 (chan->lc_mode & L2CAP_LM_AUTH ? "yes" : "no"), 85 (chan->lc_mode & L2CAP_LM_ENCRYPT ? "yes" : "no"), 86 (chan->lc_mode & L2CAP_LM_SECURE ? "yes" : "no")); 87 88 if (chan->lc_mode & L2CAP_LM_AUTH) 89 chan->lc_link->hl_flags |= HCI_LINK_AUTH_REQ; 90 91 if (chan->lc_mode & L2CAP_LM_ENCRYPT) 92 chan->lc_link->hl_flags |= HCI_LINK_ENCRYPT_REQ; 93 94 if (chan->lc_mode & L2CAP_LM_SECURE) 95 chan->lc_link->hl_flags |= HCI_LINK_SECURE_REQ; 96 97 return hci_acl_setmode(chan->lc_link); 98 } 99 100 /* 101 * Allocate a new Request structure & ID and set the timer going 102 */ 103 int 104 l2cap_request_alloc(struct l2cap_channel *chan, uint8_t code) 105 { 106 struct hci_link *link = chan->lc_link; 107 struct l2cap_req *req; 108 int next_id; 109 110 if (link == NULL) 111 return ENETDOWN; 112 113 /* find next ID (0 is not allowed) */ 114 next_id = link->hl_lastid + 1; 115 if (next_id > 0xff) 116 next_id = 1; 117 118 /* Ouroboros check */ 119 req = TAILQ_FIRST(&link->hl_reqs); 120 if (req && req->lr_id == next_id) 121 return ENFILE; 122 123 req = pool_get(&l2cap_req_pool, PR_NOWAIT); 124 if (req == NULL) 125 return ENOMEM; 126 127 req->lr_id = link->hl_lastid = next_id; 128 129 req->lr_code = code; 130 req->lr_chan = chan; 131 req->lr_link = link; 132 133 callout_init(&req->lr_rtx, 0); 134 callout_setfunc(&req->lr_rtx, l2cap_rtx, req); 135 callout_schedule(&req->lr_rtx, l2cap_response_timeout * hz); 136 137 TAILQ_INSERT_TAIL(&link->hl_reqs, req, lr_next); 138 139 return 0; 140 } 141 142 /* 143 * Find a running request for this link 144 */ 145 struct l2cap_req * 146 l2cap_request_lookup(struct hci_link *link, uint8_t id) 147 { 148 struct l2cap_req *req; 149 150 TAILQ_FOREACH(req, &link->hl_reqs, lr_next) { 151 if (req->lr_id == id) 152 return req; 153 } 154 155 return NULL; 156 } 157 158 /* 159 * Halt and free a request 160 */ 161 void 162 l2cap_request_free(struct l2cap_req *req) 163 { 164 struct hci_link *link = req->lr_link; 165 166 callout_stop(&req->lr_rtx); 167 if (callout_invoking(&req->lr_rtx)) 168 return; 169 170 callout_destroy(&req->lr_rtx); 171 172 TAILQ_REMOVE(&link->hl_reqs, req, lr_next); 173 pool_put(&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 mutex_enter(bt_lock); 190 callout_ack(&req->lr_rtx); 191 192 chan = req->lr_chan; 193 l2cap_request_free(req); 194 195 DPRINTF("cid %d, ident %d\n", (chan ? chan->lc_lcid : 0), req->lr_id); 196 197 if (chan && chan->lc_state != L2CAP_CLOSED) 198 l2cap_close(chan, ETIMEDOUT); 199 200 mutex_exit(bt_lock); 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 KASSERT(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