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