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