Home | History | Annotate | Line # | Download | only in netbt
rfcomm_upper.c revision 1.13
      1 /*	$NetBSD: rfcomm_upper.c,v 1.13 2010/01/04 19:20:05 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.13 2010/01/04 19:20:05 plunky 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/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(handle, proto, upper)
     60  *
     61  * attach a new RFCOMM DLC to handle, populate with reasonable defaults
     62  */
     63 int
     64 rfcomm_attach(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 = malloc(sizeof(struct rfcomm_dlc), M_BLUETOOTH, M_NOWAIT | M_ZERO);
     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(dlc, sockaddr)
    102  *
    103  * bind DLC to local address
    104  */
    105 int
    106 rfcomm_bind(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(dlc, sockaddr)
    118  *
    119  * return local address
    120  */
    121 int
    122 rfcomm_sockaddr(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(dlc, sockaddr)
    131  *
    132  * Initiate connection of RFCOMM DLC to remote address.
    133  */
    134 int
    135 rfcomm_connect(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(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(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(dlc, sockaddr)
    206  *
    207  * return remote address
    208  */
    209 int
    210 rfcomm_peeraddr(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(dlc, linger)
    219  *
    220  * disconnect RFCOMM DLC
    221  */
    222 int
    223 rfcomm_disconnect(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(handle)
    274  *
    275  * detach RFCOMM DLC from handle
    276  */
    277 int
    278 rfcomm_detach(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 		free(dlc, M_BLUETOOTH);
    302 	}
    303 
    304 	return 0;
    305 }
    306 
    307 /*
    308  * rfcomm_listen(dlc)
    309  *
    310  * This DLC is a listener. We look for an existing listening session
    311  * with a matching address to attach to or else create a new one on
    312  * the listeners list. If the ANY channel is given, allocate the first
    313  * available for the session.
    314  */
    315 int
    316 rfcomm_listen(struct rfcomm_dlc *dlc)
    317 {
    318 	struct rfcomm_session *rs;
    319 	struct rfcomm_dlc *used;
    320 	struct sockaddr_bt addr;
    321 	int err, channel;
    322 
    323 	if (dlc->rd_state != RFCOMM_DLC_CLOSED)
    324 		return EISCONN;
    325 
    326 	if (dlc->rd_laddr.bt_channel != RFCOMM_CHANNEL_ANY
    327 	    && (dlc->rd_laddr.bt_channel < RFCOMM_CHANNEL_MIN
    328 	    || dlc->rd_laddr.bt_channel > RFCOMM_CHANNEL_MAX))
    329 		return EADDRNOTAVAIL;
    330 
    331 	if (dlc->rd_laddr.bt_psm == L2CAP_PSM_ANY)
    332 		dlc->rd_laddr.bt_psm = L2CAP_PSM_RFCOMM;
    333 	else if (dlc->rd_laddr.bt_psm != L2CAP_PSM_RFCOMM
    334 	    && (dlc->rd_laddr.bt_psm < 0x1001
    335 	    || L2CAP_PSM_INVALID(dlc->rd_laddr.bt_psm)))
    336 		return EADDRNOTAVAIL;
    337 
    338 	LIST_FOREACH(rs, &rfcomm_session_listen, rs_next) {
    339 		l2cap_sockaddr(rs->rs_l2cap, &addr);
    340 
    341 		if (addr.bt_psm != dlc->rd_laddr.bt_psm)
    342 			continue;
    343 
    344 		if (bdaddr_same(&dlc->rd_laddr.bt_bdaddr, &addr.bt_bdaddr))
    345 			break;
    346 	}
    347 
    348 	if (rs == NULL) {
    349 		rs = rfcomm_session_alloc(&rfcomm_session_listen,
    350 						&dlc->rd_laddr);
    351 		if (rs == NULL)
    352 			return ENOMEM;
    353 
    354 		rs->rs_state = RFCOMM_SESSION_LISTEN;
    355 
    356 		err = l2cap_listen(rs->rs_l2cap);
    357 		if (err) {
    358 			rfcomm_session_free(rs);
    359 			return err;
    360 		}
    361 	}
    362 
    363 	if (dlc->rd_laddr.bt_channel == RFCOMM_CHANNEL_ANY) {
    364 		channel = RFCOMM_CHANNEL_MIN;
    365 		used = LIST_FIRST(&rs->rs_dlcs);
    366 
    367 		while (used != NULL) {
    368 			if (used->rd_laddr.bt_channel == channel) {
    369 				if (channel++ == RFCOMM_CHANNEL_MAX)
    370 					return EADDRNOTAVAIL;
    371 
    372 				used = LIST_FIRST(&rs->rs_dlcs);
    373 			} else {
    374 				used = LIST_NEXT(used, rd_next);
    375 			}
    376 		}
    377 
    378 		dlc->rd_laddr.bt_channel = channel;
    379 	}
    380 
    381 	dlc->rd_session = rs;
    382 	dlc->rd_state = RFCOMM_DLC_LISTEN;
    383 	LIST_INSERT_HEAD(&rs->rs_dlcs, dlc, rd_next);
    384 
    385 	return 0;
    386 }
    387 
    388 /*
    389  * rfcomm_send(dlc, mbuf)
    390  *
    391  * Output data on DLC. This is streamed data, so we add it
    392  * to our buffer and start the DLC, which will assemble
    393  * packets and send them if it can.
    394  */
    395 int
    396 rfcomm_send(struct rfcomm_dlc *dlc, struct mbuf *m)
    397 {
    398 
    399 	if (dlc->rd_txbuf != NULL) {
    400 		dlc->rd_txbuf->m_pkthdr.len += m->m_pkthdr.len;
    401 		m_cat(dlc->rd_txbuf, m);
    402 	} else {
    403 		dlc->rd_txbuf = m;
    404 	}
    405 
    406 	if (dlc->rd_state == RFCOMM_DLC_OPEN)
    407 		rfcomm_dlc_start(dlc);
    408 
    409 	return 0;
    410 }
    411 
    412 /*
    413  * rfcomm_rcvd(dlc, space)
    414  *
    415  * Indicate space now available in receive buffer
    416  *
    417  * This should be used to give an initial value of the receive buffer
    418  * size when the DLC is attached and anytime data is cleared from the
    419  * buffer after that.
    420  */
    421 int
    422 rfcomm_rcvd(struct rfcomm_dlc *dlc, size_t space)
    423 {
    424 
    425 	KASSERT(dlc != NULL);
    426 
    427 	dlc->rd_rxsize = space;
    428 
    429 	/*
    430 	 * if we are using credit based flow control, we may
    431 	 * want to send some credits..
    432 	 */
    433 	if (dlc->rd_state == RFCOMM_DLC_OPEN
    434 	    && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC))
    435 		rfcomm_dlc_start(dlc);
    436 
    437 	return 0;
    438 }
    439 
    440 /*
    441  * rfcomm_setopt(dlc, sopt)
    442  *
    443  * set DLC options
    444  */
    445 int
    446 rfcomm_setopt(struct rfcomm_dlc *dlc, const struct sockopt *sopt)
    447 {
    448 	int mode, err = 0;
    449 	uint16_t mtu;
    450 
    451 	switch (sopt->sopt_name) {
    452 	case SO_RFCOMM_MTU:
    453 		err = sockopt_get(sopt, &mtu, sizeof(mtu));
    454 		if (err)
    455 			break;
    456 
    457 		if (mtu < RFCOMM_MTU_MIN || mtu > RFCOMM_MTU_MAX)
    458 			err = EINVAL;
    459 		else if (dlc->rd_state == RFCOMM_DLC_CLOSED)
    460 			dlc->rd_mtu = mtu;
    461 		else
    462 			err = EBUSY;
    463 
    464 		break;
    465 
    466 	case SO_RFCOMM_LM:
    467 		err = sockopt_getint(sopt, &mode);
    468 		if (err)
    469 			break;
    470 
    471 		mode &= (RFCOMM_LM_SECURE | RFCOMM_LM_ENCRYPT | RFCOMM_LM_AUTH);
    472 
    473 		if (mode & RFCOMM_LM_SECURE)
    474 			mode |= RFCOMM_LM_ENCRYPT;
    475 
    476 		if (mode & RFCOMM_LM_ENCRYPT)
    477 			mode |= RFCOMM_LM_AUTH;
    478 
    479 		dlc->rd_mode = mode;
    480 
    481 		if (dlc->rd_state == RFCOMM_DLC_OPEN)
    482 			err = rfcomm_dlc_setmode(dlc);
    483 
    484 		break;
    485 
    486 	default:
    487 		err = ENOPROTOOPT;
    488 		break;
    489 	}
    490 	return err;
    491 }
    492 
    493 /*
    494  * rfcomm_getopt(dlc, sopt)
    495  *
    496  * get DLC options
    497  */
    498 int
    499 rfcomm_getopt(struct rfcomm_dlc *dlc, struct sockopt *sopt)
    500 {
    501 	struct rfcomm_fc_info fc;
    502 
    503 	switch (sopt->sopt_name) {
    504 	case SO_RFCOMM_MTU:
    505 		return sockopt_set(sopt, &dlc->rd_mtu, sizeof(uint16_t));
    506 
    507 	case SO_RFCOMM_FC_INFO:
    508 		memset(&fc, 0, sizeof(fc));
    509 		fc.lmodem = dlc->rd_lmodem;
    510 		fc.rmodem = dlc->rd_rmodem;
    511 		fc.tx_cred = max(dlc->rd_txcred, 0xff);
    512 		fc.rx_cred = max(dlc->rd_rxcred, 0xff);
    513 		if (dlc->rd_session
    514 		    && (dlc->rd_session->rs_flags & RFCOMM_SESSION_CFC))
    515 			fc.cfc = 1;
    516 
    517 		return sockopt_set(sopt, &fc, sizeof(fc));
    518 
    519 	case SO_RFCOMM_LM:
    520 		return sockopt_setint(sopt, dlc->rd_mode);
    521 
    522 	default:
    523 		break;
    524 	}
    525 
    526 	return ENOPROTOOPT;
    527 }
    528