sys: Add size directives to assembly functions.
[dragonfly.git] / sys / netbt / rfcomm_upper.c
blobe04bf3f2c17890bfc125e2e3403c7730ec3eda0b
1 /* $DragonFly: src/sys/netbt/rfcomm_upper.c,v 1.3 2008/06/20 20:52:29 aggelos Exp $ */
2 /* $OpenBSD: src/sys/netbt/rfcomm_upper.c,v 1.4 2008/02/24 21:34:48 uwe Exp $ */
3 /* $NetBSD: rfcomm_upper.c,v 1.10 2007/11/20 20:25:57 plunky Exp $ */
5 /*-
6 * Copyright (c) 2006 Itronix Inc.
7 * All rights reserved.
9 * Written by Iain Hibbert for Itronix Inc.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The name of Itronix Inc. may not be used to endorse
20 * or promote products derived from this software without specific
21 * prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
36 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/mbuf.h>
39 #include <sys/proc.h>
40 #include <sys/systm.h>
41 #include <sys/socketvar.h>
43 #include <netbt/bluetooth.h>
44 #include <netbt/hci.h>
45 #include <netbt/l2cap.h>
46 #include <netbt/rfcomm.h>
48 /****************************************************************************
50 * RFCOMM DLC - Upper Protocol API
52 * Currently the only 'Port Emulation Entity' is the RFCOMM socket code
53 * but it is should be possible to provide a pseudo-device for a direct
54 * tty interface.
58 * rfcomm_attach(handle, proto, upper)
60 * attach a new RFCOMM DLC to handle, populate with reasonable defaults
62 int
63 rfcomm_attach(struct rfcomm_dlc **handle,
64 const struct btproto *proto, void *upper)
66 struct rfcomm_dlc *dlc;
68 KKASSERT(handle != NULL);
69 KKASSERT(proto != NULL);
70 KKASSERT(upper != NULL);
72 dlc = kmalloc(sizeof(*dlc), M_BLUETOOTH, M_NOWAIT | M_ZERO);
73 if (dlc == NULL)
74 return ENOMEM;
76 dlc->rd_state = RFCOMM_DLC_CLOSED;
77 dlc->rd_mtu = rfcomm_mtu_default;
79 dlc->rd_proto = proto;
80 dlc->rd_upper = upper;
82 dlc->rd_laddr.bt_len = sizeof(struct sockaddr_bt);
83 dlc->rd_laddr.bt_family = AF_BLUETOOTH;
84 dlc->rd_laddr.bt_psm = L2CAP_PSM_RFCOMM;
86 dlc->rd_raddr.bt_len = sizeof(struct sockaddr_bt);
87 dlc->rd_raddr.bt_family = AF_BLUETOOTH;
88 dlc->rd_raddr.bt_psm = L2CAP_PSM_RFCOMM;
90 dlc->rd_lmodem = RFCOMM_MSC_RTC | RFCOMM_MSC_RTR | RFCOMM_MSC_DV;
92 callout_init(&dlc->rd_timeout);
94 *handle = dlc;
95 return 0;
99 * rfcomm_bind(dlc, sockaddr)
101 * bind DLC to local address
104 rfcomm_bind(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr)
107 memcpy(&dlc->rd_laddr, addr, sizeof(struct sockaddr_bt));
108 return 0;
112 * rfcomm_sockaddr(dlc, sockaddr)
114 * return local address
117 rfcomm_sockaddr(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr)
120 memcpy(addr, &dlc->rd_laddr, sizeof(struct sockaddr_bt));
121 return 0;
125 * rfcomm_connect(dlc, sockaddr)
127 * Initiate connection of RFCOMM DLC to remote address.
130 rfcomm_connect(struct rfcomm_dlc *dlc, struct sockaddr_bt *dest)
132 struct rfcomm_session *rs;
133 int err = 0;
135 if (dlc->rd_state != RFCOMM_DLC_CLOSED)
136 return EISCONN;
138 memcpy(&dlc->rd_raddr, dest, sizeof(struct sockaddr_bt));
140 if (dlc->rd_raddr.bt_channel < RFCOMM_CHANNEL_MIN
141 || dlc->rd_raddr.bt_channel > RFCOMM_CHANNEL_MAX
142 || bdaddr_any(&dlc->rd_raddr.bt_bdaddr))
143 return EDESTADDRREQ;
145 if (dlc->rd_raddr.bt_psm == L2CAP_PSM_ANY)
146 dlc->rd_raddr.bt_psm = L2CAP_PSM_RFCOMM;
147 else if (dlc->rd_raddr.bt_psm != L2CAP_PSM_RFCOMM
148 && (dlc->rd_raddr.bt_psm < 0x1001
149 || L2CAP_PSM_INVALID(dlc->rd_raddr.bt_psm)))
150 return EINVAL;
153 * We are allowed only one RFCOMM session between any 2 Bluetooth
154 * devices, so see if there is a session already otherwise create
155 * one and set it connecting.
157 rs = rfcomm_session_lookup(&dlc->rd_laddr, &dlc->rd_raddr);
158 if (rs == NULL) {
159 rs = rfcomm_session_alloc(&rfcomm_session_active,
160 &dlc->rd_laddr);
161 if (rs == NULL)
162 return ENOMEM;
164 rs->rs_flags |= RFCOMM_SESSION_INITIATOR;
165 rs->rs_state = RFCOMM_SESSION_WAIT_CONNECT;
167 err = l2cap_connect(rs->rs_l2cap, &dlc->rd_raddr);
168 if (err) {
169 rfcomm_session_free(rs);
170 return err;
174 * This session will start up automatically when its
175 * L2CAP channel is connected.
179 /* construct DLC */
180 dlc->rd_dlci = RFCOMM_MKDLCI(IS_INITIATOR(rs) ? 0:1, dest->bt_channel);
181 if (rfcomm_dlc_lookup(rs, dlc->rd_dlci))
182 return EBUSY;
184 l2cap_sockaddr(rs->rs_l2cap, &dlc->rd_laddr);
187 * attach the DLC to the session and start it off
189 dlc->rd_session = rs;
190 dlc->rd_state = RFCOMM_DLC_WAIT_SESSION;
191 LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next);
193 if (rs->rs_state == RFCOMM_SESSION_OPEN)
194 err = rfcomm_dlc_connect(dlc);
196 return err;
200 * rfcomm_peeraddr(dlc, sockaddr)
202 * return remote address
205 rfcomm_peeraddr(struct rfcomm_dlc *dlc, struct sockaddr_bt *addr)
208 memcpy(addr, &dlc->rd_raddr, sizeof(struct sockaddr_bt));
209 return 0;
213 * rfcomm_disconnect(dlc, linger)
215 * disconnect RFCOMM DLC
218 rfcomm_disconnect(struct rfcomm_dlc *dlc, int linger)
220 struct rfcomm_session *rs = dlc->rd_session;
221 int err = 0;
223 KKASSERT(dlc != NULL);
225 switch (dlc->rd_state) {
226 case RFCOMM_DLC_CLOSED:
227 case RFCOMM_DLC_LISTEN:
228 return EINVAL;
230 case RFCOMM_DLC_WAIT_SEND_UA:
231 err = rfcomm_session_send_frame(rs,
232 RFCOMM_FRAME_DM, dlc->rd_dlci);
234 /* fall through */
235 case RFCOMM_DLC_WAIT_SESSION:
236 case RFCOMM_DLC_WAIT_CONNECT:
237 case RFCOMM_DLC_WAIT_SEND_SABM:
238 rfcomm_dlc_close(dlc, 0);
239 break;
241 case RFCOMM_DLC_OPEN:
242 if (dlc->rd_txbuf != NULL && linger != 0) {
243 dlc->rd_flags |= RFCOMM_DLC_SHUTDOWN;
244 break;
247 /* else fall through */
248 case RFCOMM_DLC_WAIT_RECV_UA:
249 dlc->rd_state = RFCOMM_DLC_WAIT_DISCONNECT;
250 err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC,
251 dlc->rd_dlci);
252 callout_reset(&dlc->rd_timeout, rfcomm_ack_timeout * hz,
253 rfcomm_dlc_timeout, dlc);
254 break;
256 case RFCOMM_DLC_WAIT_DISCONNECT:
257 err = EALREADY;
258 break;
260 default:
261 UNKNOWN(dlc->rd_state);
262 break;
265 return err;
269 * rfcomm_detach(handle)
271 * detach RFCOMM DLC from handle
274 rfcomm_detach(struct rfcomm_dlc **handle)
276 struct rfcomm_dlc *dlc = *handle;
278 if (dlc->rd_state != RFCOMM_DLC_CLOSED)
279 rfcomm_dlc_close(dlc, 0);
281 if (dlc->rd_txbuf != NULL) {
282 m_freem(dlc->rd_txbuf);
283 dlc->rd_txbuf = NULL;
286 dlc->rd_upper = NULL;
287 *handle = NULL;
290 * If callout is invoking we can't free the DLC so
291 * mark it and let the callout release it.
293 if (callout_active(&dlc->rd_timeout))
294 dlc->rd_flags |= RFCOMM_DLC_DETACH;
295 else
296 kfree(dlc, M_BLUETOOTH);
298 return 0;
302 * rfcomm_listen(dlc)
304 * This DLC is a listener. We look for an existing listening session
305 * with a matching address to attach to or else create a new one on
306 * the listeners list. If the ANY channel is given, allocate the first
307 * available for the session.
310 rfcomm_listen(struct rfcomm_dlc *dlc)
312 struct rfcomm_session *rs;
313 struct rfcomm_dlc *used;
314 struct sockaddr_bt addr;
315 int err, channel;
317 if (dlc->rd_state != RFCOMM_DLC_CLOSED)
318 return EISCONN;
320 if (dlc->rd_laddr.bt_channel != RFCOMM_CHANNEL_ANY
321 && (dlc->rd_laddr.bt_channel < RFCOMM_CHANNEL_MIN
322 || dlc->rd_laddr.bt_channel > RFCOMM_CHANNEL_MAX))
323 return EADDRNOTAVAIL;
325 if (dlc->rd_laddr.bt_psm == L2CAP_PSM_ANY)
326 dlc->rd_laddr.bt_psm = L2CAP_PSM_RFCOMM;
327 else if (dlc->rd_laddr.bt_psm != L2CAP_PSM_RFCOMM
328 && (dlc->rd_laddr.bt_psm < 0x1001
329 || L2CAP_PSM_INVALID(dlc->rd_laddr.bt_psm)))
330 return EADDRNOTAVAIL;
332 LIST_FOREACH(rs, &rfcomm_session_listen, rs_next) {
333 l2cap_sockaddr(rs->rs_l2cap, &addr);
335 if (addr.bt_psm != dlc->rd_laddr.bt_psm)
336 continue;
338 if (bdaddr_same(&dlc->rd_laddr.bt_bdaddr, &addr.bt_bdaddr))
339 break;
342 if (rs == NULL) {
343 rs = rfcomm_session_alloc(&rfcomm_session_listen,
344 &dlc->rd_laddr);
345 if (rs == NULL)
346 return ENOMEM;
348 rs->rs_state = RFCOMM_SESSION_LISTEN;
350 err = l2cap_listen(rs->rs_l2cap);
351 if (err) {
352 rfcomm_session_free(rs);
353 return err;
357 if (dlc->rd_laddr.bt_channel == RFCOMM_CHANNEL_ANY) {
358 channel = RFCOMM_CHANNEL_MIN;
359 used = LIST_FIRST(&rs->rs_dlcs);
361 while (used != NULL) {
362 if (used->rd_laddr.bt_channel == channel) {
363 if (channel++ == RFCOMM_CHANNEL_MAX)
364 return EADDRNOTAVAIL;
366 used = LIST_FIRST(&rs->rs_dlcs);
367 } else {
368 used = LIST_NEXT(used, rd_next);
372 dlc->rd_laddr.bt_channel = channel;
375 dlc->rd_session = rs;
376 dlc->rd_state = RFCOMM_DLC_LISTEN;
377 LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next);
379 return 0;
383 * rfcomm_send(dlc, mbuf)
385 * Output data on DLC. This is streamed data, so we add it
386 * to our buffer and start the DLC, which will assemble
387 * packets and send them if it can.
390 rfcomm_send(struct rfcomm_dlc *dlc, struct mbuf *m)
393 if (dlc->rd_txbuf != NULL) {
394 dlc->rd_txbuf->m_pkthdr.len += m->m_pkthdr.len;
395 m_cat(dlc->rd_txbuf, m);
396 } else {
397 dlc->rd_txbuf = m;
400 if (dlc->rd_state == RFCOMM_DLC_OPEN)
401 rfcomm_dlc_start(dlc);
403 return 0;
407 * rfcomm_rcvd(dlc, space)
409 * Indicate space now available in receive buffer
411 * This should be used to give an initial value of the receive buffer
412 * size when the DLC is attached and anytime data is cleared from the
413 * buffer after that.
416 rfcomm_rcvd(struct rfcomm_dlc *dlc, size_t space)
419 KKASSERT(dlc != NULL);
421 dlc->rd_rxsize = space;
424 * if we are using credit based flow control, we may
425 * want to send some credits..
427 if (dlc->rd_state == RFCOMM_DLC_OPEN
428 && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC))
429 rfcomm_dlc_start(dlc);
431 return 0;
435 * rfcomm_setopt(dlc, option, addr)
437 * set DLC options
440 rfcomm_setopt(struct rfcomm_dlc *dlc, int opt, void *addr)
442 int mode, err = 0;
443 uint16_t mtu;
445 switch (opt) {
446 case SO_RFCOMM_MTU:
447 mtu = *(uint16_t *)addr;
448 if (mtu < RFCOMM_MTU_MIN || mtu > RFCOMM_MTU_MAX)
449 err = EINVAL;
450 else if (dlc->rd_state == RFCOMM_DLC_CLOSED)
451 dlc->rd_mtu = mtu;
452 else
453 err = EBUSY;
455 break;
457 case SO_RFCOMM_LM:
458 mode = *(int *)addr;
459 mode &= (RFCOMM_LM_SECURE | RFCOMM_LM_ENCRYPT | RFCOMM_LM_AUTH);
461 if (mode & RFCOMM_LM_SECURE)
462 mode |= RFCOMM_LM_ENCRYPT;
464 if (mode & RFCOMM_LM_ENCRYPT)
465 mode |= RFCOMM_LM_AUTH;
467 dlc->rd_mode = mode;
469 if (dlc->rd_state == RFCOMM_DLC_OPEN)
470 err = rfcomm_dlc_setmode(dlc);
472 break;
474 default:
475 err = ENOPROTOOPT;
476 break;
478 return err;
483 rfcomm_setopt2(struct rfcomm_dlc *dlc, int opt, struct socket *so,
484 struct sockopt *sopt)
486 int mode, err = 0;
487 uint16_t mtu;
489 switch (opt) {
490 case SO_RFCOMM_MTU:
491 err = soopt_to_kbuf(sopt, &mtu, sizeof(uint16_t),
492 sizeof(uint16_t));
493 if (err) break;
495 if (mtu < RFCOMM_MTU_MIN || mtu > RFCOMM_MTU_MAX)
496 err = EINVAL;
497 else if (dlc->rd_state == RFCOMM_DLC_CLOSED)
498 dlc->rd_mtu = mtu;
499 else
500 err = EBUSY;
502 break;
504 case SO_RFCOMM_LM:
505 err = soopt_to_kbuf(sopt, &mode, sizeof(int), sizeof(int));
506 if (err) break;
508 mode &= (RFCOMM_LM_SECURE | RFCOMM_LM_ENCRYPT | RFCOMM_LM_AUTH);
510 if (mode & RFCOMM_LM_SECURE)
511 mode |= RFCOMM_LM_ENCRYPT;
513 if (mode & RFCOMM_LM_ENCRYPT)
514 mode |= RFCOMM_LM_AUTH;
516 dlc->rd_mode = mode;
518 if (dlc->rd_state == RFCOMM_DLC_OPEN)
519 err = rfcomm_dlc_setmode(dlc);
521 break;
523 default:
524 err = ENOPROTOOPT;
525 break;
527 return err;
531 * rfcomm_getopt(dlc, option, addr)
533 * get DLC options
536 rfcomm_getopt(struct rfcomm_dlc *dlc, int opt, void *addr)
538 struct rfcomm_fc_info *fc;
540 switch (opt) {
541 case SO_RFCOMM_MTU:
542 *(uint16_t *)addr = dlc->rd_mtu;
543 return sizeof(uint16_t);
545 case SO_RFCOMM_FC_INFO:
546 fc = addr;
547 memset(fc, 0, sizeof(*fc));
548 fc->lmodem = dlc->rd_lmodem;
549 fc->rmodem = dlc->rd_rmodem;
550 fc->tx_cred = max(dlc->rd_txcred, 0xff);
551 fc->rx_cred = max(dlc->rd_rxcred, 0xff);
552 if (dlc->rd_session
553 && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC))
554 fc->cfc = 1;
556 return sizeof(*fc);
558 case SO_RFCOMM_LM:
559 *(int *)addr = dlc->rd_mode;
560 return sizeof(int);
562 default:
563 break;
566 return 0;