1 | /* $NetBSD: rfcomm_upper.c,v 1.22 2014/11/16 21:34:27 plunky Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 2006 Itronix Inc. |
5 | * All rights reserved. |
6 | * |
7 | * Written by Iain Hibbert for Itronix Inc. |
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/cdefs.h> |
35 | __KERNEL_RCSID(0, "$NetBSD: rfcomm_upper.c,v 1.22 2014/11/16 21:34:27 plunky Exp $" ); |
36 | |
37 | #include <sys/param.h> |
38 | #include <sys/kernel.h> |
39 | #include <sys/mbuf.h> |
40 | #include <sys/kmem.h> |
41 | #include <sys/socketvar.h> |
42 | #include <sys/systm.h> |
43 | |
44 | #include <netbt/bluetooth.h> |
45 | #include <netbt/hci.h> |
46 | #include <netbt/l2cap.h> |
47 | #include <netbt/rfcomm.h> |
48 | |
49 | /**************************************************************************** |
50 | * |
51 | * RFCOMM DLC - Upper Protocol API |
52 | * |
53 | * Currently the only 'Port Emulation Entity' is the RFCOMM socket code |
54 | * but it is should be possible to provide a pseudo-device for a direct |
55 | * tty interface. |
56 | */ |
57 | |
58 | /* |
59 | * rfcomm_attach_pcb(handle, proto, upper) |
60 | * |
61 | * attach a new RFCOMM DLC to handle, populate with reasonable defaults |
62 | */ |
63 | int |
64 | rfcomm_attach_pcb(struct rfcomm_dlc **handle, |
65 | const struct btproto *proto, void *upper) |
66 | { |
67 | struct rfcomm_dlc *dlc; |
68 | |
69 | KASSERT(handle != NULL); |
70 | KASSERT(proto != NULL); |
71 | KASSERT(upper != NULL); |
72 | |
73 | dlc = kmem_intr_zalloc(sizeof(struct rfcomm_dlc), KM_NOSLEEP); |
74 | if (dlc == NULL) |
75 | return ENOMEM; |
76 | |
77 | dlc->rd_state = RFCOMM_DLC_CLOSED; |
78 | dlc->rd_mtu = rfcomm_mtu_default; |
79 | |
80 | dlc->rd_proto = proto; |
81 | dlc->rd_upper = upper; |
82 | |
83 | dlc->rd_laddr.bt_len = sizeof(struct sockaddr_bt); |
84 | dlc->rd_laddr.bt_family = AF_BLUETOOTH; |
85 | dlc->rd_laddr.bt_psm = L2CAP_PSM_RFCOMM; |
86 | |
87 | dlc->rd_raddr.bt_len = sizeof(struct sockaddr_bt); |
88 | dlc->rd_raddr.bt_family = AF_BLUETOOTH; |
89 | dlc->rd_raddr.bt_psm = L2CAP_PSM_RFCOMM; |
90 | |
91 | dlc->rd_lmodem = RFCOMM_MSC_RTC | RFCOMM_MSC_RTR | RFCOMM_MSC_DV; |
92 | |
93 | callout_init(&dlc->rd_timeout, 0); |
94 | callout_setfunc(&dlc->rd_timeout, rfcomm_dlc_timeout, dlc); |
95 | |
96 | *handle = dlc; |
97 | return 0; |
98 | } |
99 | |
100 | /* |
101 | * rfcomm_bind_pcb(dlc, sockaddr) |
102 | * |
103 | * bind DLC to local address |
104 | */ |
105 | int |
106 | rfcomm_bind_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr) |
107 | { |
108 | |
109 | if (dlc->rd_state != RFCOMM_DLC_CLOSED) |
110 | return EINVAL; |
111 | |
112 | memcpy(&dlc->rd_laddr, addr, sizeof(struct sockaddr_bt)); |
113 | return 0; |
114 | } |
115 | |
116 | /* |
117 | * rfcomm_sockaddr_pcb(dlc, sockaddr) |
118 | * |
119 | * return local address |
120 | */ |
121 | int |
122 | rfcomm_sockaddr_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr) |
123 | { |
124 | |
125 | memcpy(addr, &dlc->rd_laddr, sizeof(struct sockaddr_bt)); |
126 | return 0; |
127 | } |
128 | |
129 | /* |
130 | * rfcomm_connect_pcb(dlc, sockaddr) |
131 | * |
132 | * Initiate connection of RFCOMM DLC to remote address. |
133 | */ |
134 | int |
135 | rfcomm_connect_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *dest) |
136 | { |
137 | struct rfcomm_session *rs; |
138 | int err = 0; |
139 | |
140 | if (dlc->rd_state != RFCOMM_DLC_CLOSED) |
141 | return EISCONN; |
142 | |
143 | memcpy(&dlc->rd_raddr, dest, sizeof(struct sockaddr_bt)); |
144 | |
145 | if (dlc->rd_raddr.bt_channel < RFCOMM_CHANNEL_MIN |
146 | || dlc->rd_raddr.bt_channel > RFCOMM_CHANNEL_MAX |
147 | || bdaddr_any(&dlc->rd_raddr.bt_bdaddr)) |
148 | return EDESTADDRREQ; |
149 | |
150 | if (dlc->rd_raddr.bt_psm == L2CAP_PSM_ANY) |
151 | dlc->rd_raddr.bt_psm = L2CAP_PSM_RFCOMM; |
152 | else if (dlc->rd_raddr.bt_psm != L2CAP_PSM_RFCOMM |
153 | && (dlc->rd_raddr.bt_psm < 0x1001 |
154 | || L2CAP_PSM_INVALID(dlc->rd_raddr.bt_psm))) |
155 | return EINVAL; |
156 | |
157 | /* |
158 | * We are allowed only one RFCOMM session between any 2 Bluetooth |
159 | * devices, so see if there is a session already otherwise create |
160 | * one and set it connecting. |
161 | */ |
162 | rs = rfcomm_session_lookup(&dlc->rd_laddr, &dlc->rd_raddr); |
163 | if (rs == NULL) { |
164 | rs = rfcomm_session_alloc(&rfcomm_session_active, |
165 | &dlc->rd_laddr); |
166 | if (rs == NULL) |
167 | return ENOMEM; |
168 | |
169 | rs->rs_flags |= RFCOMM_SESSION_INITIATOR; |
170 | rs->rs_state = RFCOMM_SESSION_WAIT_CONNECT; |
171 | |
172 | err = l2cap_connect_pcb(rs->rs_l2cap, &dlc->rd_raddr); |
173 | if (err) { |
174 | rfcomm_session_free(rs); |
175 | return err; |
176 | } |
177 | |
178 | /* |
179 | * This session will start up automatically when its |
180 | * L2CAP channel is connected. |
181 | */ |
182 | } |
183 | |
184 | /* construct DLC */ |
185 | dlc->rd_dlci = RFCOMM_MKDLCI(IS_INITIATOR(rs) ? 0:1, dest->bt_channel); |
186 | if (rfcomm_dlc_lookup(rs, dlc->rd_dlci)) |
187 | return EBUSY; |
188 | |
189 | l2cap_sockaddr_pcb(rs->rs_l2cap, &dlc->rd_laddr); |
190 | |
191 | /* |
192 | * attach the DLC to the session and start it off |
193 | */ |
194 | dlc->rd_session = rs; |
195 | dlc->rd_state = RFCOMM_DLC_WAIT_SESSION; |
196 | LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next); |
197 | |
198 | if (rs->rs_state == RFCOMM_SESSION_OPEN) |
199 | err = rfcomm_dlc_connect(dlc); |
200 | |
201 | return err; |
202 | } |
203 | |
204 | /* |
205 | * rfcomm_peeraddr_pcb(dlc, sockaddr) |
206 | * |
207 | * return remote address |
208 | */ |
209 | int |
210 | rfcomm_peeraddr_pcb(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr) |
211 | { |
212 | |
213 | memcpy(addr, &dlc->rd_raddr, sizeof(struct sockaddr_bt)); |
214 | return 0; |
215 | } |
216 | |
217 | /* |
218 | * rfcomm_disconnect_pcb(dlc, linger) |
219 | * |
220 | * disconnect RFCOMM DLC |
221 | */ |
222 | int |
223 | rfcomm_disconnect_pcb(struct rfcomm_dlc *dlc, int linger) |
224 | { |
225 | struct rfcomm_session *rs = dlc->rd_session; |
226 | int err = 0; |
227 | |
228 | KASSERT(dlc != NULL); |
229 | |
230 | switch (dlc->rd_state) { |
231 | case RFCOMM_DLC_CLOSED: |
232 | case RFCOMM_DLC_LISTEN: |
233 | return EINVAL; |
234 | |
235 | case RFCOMM_DLC_WAIT_SEND_UA: |
236 | err = rfcomm_session_send_frame(rs, |
237 | RFCOMM_FRAME_DM, dlc->rd_dlci); |
238 | |
239 | /* fall through */ |
240 | case RFCOMM_DLC_WAIT_SESSION: |
241 | case RFCOMM_DLC_WAIT_CONNECT: |
242 | case RFCOMM_DLC_WAIT_SEND_SABM: |
243 | rfcomm_dlc_close(dlc, 0); |
244 | break; |
245 | |
246 | case RFCOMM_DLC_OPEN: |
247 | if (dlc->rd_txbuf != NULL && linger != 0) { |
248 | dlc->rd_flags |= RFCOMM_DLC_SHUTDOWN; |
249 | break; |
250 | } |
251 | |
252 | /* else fall through */ |
253 | case RFCOMM_DLC_WAIT_RECV_UA: |
254 | dlc->rd_state = RFCOMM_DLC_WAIT_DISCONNECT; |
255 | err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC, |
256 | dlc->rd_dlci); |
257 | callout_schedule(&dlc->rd_timeout, rfcomm_ack_timeout * hz); |
258 | break; |
259 | |
260 | case RFCOMM_DLC_WAIT_DISCONNECT: |
261 | err = EALREADY; |
262 | break; |
263 | |
264 | default: |
265 | UNKNOWN(dlc->rd_state); |
266 | break; |
267 | } |
268 | |
269 | return err; |
270 | } |
271 | |
272 | /* |
273 | * rfcomm_detach_pcb(handle) |
274 | * |
275 | * detach RFCOMM DLC from handle |
276 | */ |
277 | void |
278 | rfcomm_detach_pcb(struct rfcomm_dlc **handle) |
279 | { |
280 | struct rfcomm_dlc *dlc = *handle; |
281 | |
282 | if (dlc->rd_state != RFCOMM_DLC_CLOSED) |
283 | rfcomm_dlc_close(dlc, 0); |
284 | |
285 | if (dlc->rd_txbuf != NULL) { |
286 | m_freem(dlc->rd_txbuf); |
287 | dlc->rd_txbuf = NULL; |
288 | } |
289 | |
290 | dlc->rd_upper = NULL; |
291 | *handle = NULL; |
292 | |
293 | /* |
294 | * If callout is invoking we can't free the DLC so |
295 | * mark it and let the callout release it. |
296 | */ |
297 | if (callout_invoking(&dlc->rd_timeout)) |
298 | dlc->rd_flags |= RFCOMM_DLC_DETACH; |
299 | else { |
300 | callout_destroy(&dlc->rd_timeout); |
301 | kmem_intr_free(dlc, sizeof(*dlc)); |
302 | } |
303 | } |
304 | |
305 | /* |
306 | * rfcomm_listen_pcb(dlc) |
307 | * |
308 | * This DLC is a listener. We look for an existing listening session |
309 | * with a matching address to attach to or else create a new one on |
310 | * the listeners list. If the ANY channel is given, allocate the first |
311 | * available for the session. |
312 | */ |
313 | int |
314 | rfcomm_listen_pcb(struct rfcomm_dlc *dlc) |
315 | { |
316 | struct rfcomm_session *rs; |
317 | struct rfcomm_dlc *used; |
318 | struct sockaddr_bt addr; |
319 | int err, channel; |
320 | |
321 | if (dlc->rd_state != RFCOMM_DLC_CLOSED) |
322 | return EISCONN; |
323 | |
324 | if (dlc->rd_laddr.bt_channel != RFCOMM_CHANNEL_ANY |
325 | && (dlc->rd_laddr.bt_channel < RFCOMM_CHANNEL_MIN |
326 | || dlc->rd_laddr.bt_channel > RFCOMM_CHANNEL_MAX)) |
327 | return EADDRNOTAVAIL; |
328 | |
329 | if (dlc->rd_laddr.bt_psm == L2CAP_PSM_ANY) |
330 | dlc->rd_laddr.bt_psm = L2CAP_PSM_RFCOMM; |
331 | else if (dlc->rd_laddr.bt_psm != L2CAP_PSM_RFCOMM |
332 | && (dlc->rd_laddr.bt_psm < 0x1001 |
333 | || L2CAP_PSM_INVALID(dlc->rd_laddr.bt_psm))) |
334 | return EADDRNOTAVAIL; |
335 | |
336 | LIST_FOREACH(rs, &rfcomm_session_listen, rs_next) { |
337 | l2cap_sockaddr_pcb(rs->rs_l2cap, &addr); |
338 | |
339 | if (addr.bt_psm != dlc->rd_laddr.bt_psm) |
340 | continue; |
341 | |
342 | if (bdaddr_same(&dlc->rd_laddr.bt_bdaddr, &addr.bt_bdaddr)) |
343 | break; |
344 | } |
345 | |
346 | if (rs == NULL) { |
347 | rs = rfcomm_session_alloc(&rfcomm_session_listen, |
348 | &dlc->rd_laddr); |
349 | if (rs == NULL) |
350 | return ENOMEM; |
351 | |
352 | rs->rs_state = RFCOMM_SESSION_LISTEN; |
353 | |
354 | err = l2cap_listen_pcb(rs->rs_l2cap); |
355 | if (err) { |
356 | rfcomm_session_free(rs); |
357 | return err; |
358 | } |
359 | } |
360 | |
361 | if (dlc->rd_laddr.bt_channel == RFCOMM_CHANNEL_ANY) { |
362 | channel = RFCOMM_CHANNEL_MIN; |
363 | used = LIST_FIRST(&rs->rs_dlcs); |
364 | |
365 | while (used != NULL) { |
366 | if (used->rd_laddr.bt_channel == channel) { |
367 | if (channel++ == RFCOMM_CHANNEL_MAX) |
368 | return EADDRNOTAVAIL; |
369 | |
370 | used = LIST_FIRST(&rs->rs_dlcs); |
371 | } else { |
372 | used = LIST_NEXT(used, rd_next); |
373 | } |
374 | } |
375 | |
376 | dlc->rd_laddr.bt_channel = channel; |
377 | } |
378 | |
379 | dlc->rd_session = rs; |
380 | dlc->rd_state = RFCOMM_DLC_LISTEN; |
381 | LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next); |
382 | |
383 | return 0; |
384 | } |
385 | |
386 | /* |
387 | * rfcomm_send_pcb(dlc, mbuf) |
388 | * |
389 | * Output data on DLC. This is streamed data, so we add it |
390 | * to our buffer and start the DLC, which will assemble |
391 | * packets and send them if it can. |
392 | */ |
393 | int |
394 | rfcomm_send_pcb(struct rfcomm_dlc *dlc, struct mbuf *m) |
395 | { |
396 | |
397 | if (dlc->rd_txbuf != NULL) { |
398 | dlc->rd_txbuf->m_pkthdr.len += m->m_pkthdr.len; |
399 | m_cat(dlc->rd_txbuf, m); |
400 | } else { |
401 | dlc->rd_txbuf = m; |
402 | } |
403 | |
404 | if (dlc->rd_state == RFCOMM_DLC_OPEN) |
405 | rfcomm_dlc_start(dlc); |
406 | |
407 | return 0; |
408 | } |
409 | |
410 | /* |
411 | * rfcomm_rcvd_pcb(dlc, space) |
412 | * |
413 | * Indicate space now available in receive buffer |
414 | * |
415 | * This should be used to give an initial value of the receive buffer |
416 | * size when the DLC is attached and anytime data is cleared from the |
417 | * buffer after that. |
418 | */ |
419 | int |
420 | rfcomm_rcvd_pcb(struct rfcomm_dlc *dlc, size_t space) |
421 | { |
422 | |
423 | KASSERT(dlc != NULL); |
424 | |
425 | dlc->rd_rxsize = space; |
426 | |
427 | /* |
428 | * if we are using credit based flow control, we may |
429 | * want to send some credits.. |
430 | */ |
431 | if (dlc->rd_state == RFCOMM_DLC_OPEN |
432 | && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC)) |
433 | rfcomm_dlc_start(dlc); |
434 | |
435 | return 0; |
436 | } |
437 | |
438 | /* |
439 | * rfcomm_setopt(dlc, sopt) |
440 | * |
441 | * set DLC options |
442 | */ |
443 | int |
444 | rfcomm_setopt(struct rfcomm_dlc *dlc, const struct sockopt *sopt) |
445 | { |
446 | int mode, err = 0; |
447 | uint16_t mtu; |
448 | |
449 | switch (sopt->sopt_name) { |
450 | case SO_RFCOMM_MTU: |
451 | err = sockopt_get(sopt, &mtu, sizeof(mtu)); |
452 | if (err) |
453 | break; |
454 | |
455 | if (mtu < RFCOMM_MTU_MIN || mtu > RFCOMM_MTU_MAX) |
456 | err = EINVAL; |
457 | else if (dlc->rd_state == RFCOMM_DLC_CLOSED) |
458 | dlc->rd_mtu = mtu; |
459 | else |
460 | err = EBUSY; |
461 | |
462 | break; |
463 | |
464 | case SO_RFCOMM_LM: |
465 | err = sockopt_getint(sopt, &mode); |
466 | if (err) |
467 | break; |
468 | |
469 | mode &= (RFCOMM_LM_SECURE | RFCOMM_LM_ENCRYPT | RFCOMM_LM_AUTH); |
470 | |
471 | if (mode & RFCOMM_LM_SECURE) |
472 | mode |= RFCOMM_LM_ENCRYPT; |
473 | |
474 | if (mode & RFCOMM_LM_ENCRYPT) |
475 | mode |= RFCOMM_LM_AUTH; |
476 | |
477 | dlc->rd_mode = mode; |
478 | |
479 | if (dlc->rd_state == RFCOMM_DLC_OPEN) |
480 | err = rfcomm_dlc_setmode(dlc); |
481 | |
482 | break; |
483 | |
484 | default: |
485 | err = ENOPROTOOPT; |
486 | break; |
487 | } |
488 | return err; |
489 | } |
490 | |
491 | /* |
492 | * rfcomm_getopt(dlc, sopt) |
493 | * |
494 | * get DLC options |
495 | */ |
496 | int |
497 | rfcomm_getopt(struct rfcomm_dlc *dlc, struct sockopt *sopt) |
498 | { |
499 | struct rfcomm_fc_info fc; |
500 | |
501 | switch (sopt->sopt_name) { |
502 | case SO_RFCOMM_MTU: |
503 | return sockopt_set(sopt, &dlc->rd_mtu, sizeof(uint16_t)); |
504 | |
505 | case SO_RFCOMM_FC_INFO: |
506 | memset(&fc, 0, sizeof(fc)); |
507 | fc.lmodem = dlc->rd_lmodem; |
508 | fc.rmodem = dlc->rd_rmodem; |
509 | fc.tx_cred = max(dlc->rd_txcred, 0xff); |
510 | fc.rx_cred = max(dlc->rd_rxcred, 0xff); |
511 | if (dlc->rd_session |
512 | && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC)) |
513 | fc.cfc = 1; |
514 | |
515 | return sockopt_set(sopt, &fc, sizeof(fc)); |
516 | |
517 | case SO_RFCOMM_LM: |
518 | return sockopt_setint(sopt, dlc->rd_mode); |
519 | |
520 | default: |
521 | break; |
522 | } |
523 | |
524 | return ENOPROTOOPT; |
525 | } |
526 | |