xref: /dragonfly/sys/netbt/rfcomm_session.c (revision 66e0d393)
183aacedeSHasso Tepper /* $OpenBSD: src/sys/netbt/rfcomm_session.c,v 1.3 2008/02/24 21:34:48 uwe Exp $ */
283aacedeSHasso Tepper /* $NetBSD: rfcomm_session.c,v 1.12 2008/01/31 19:30:23 plunky Exp $ */
30a9108ebSHasso Tepper 
40a9108ebSHasso Tepper /*-
50a9108ebSHasso Tepper  * Copyright (c) 2006 Itronix Inc.
60a9108ebSHasso Tepper  * All rights reserved.
70a9108ebSHasso Tepper  *
80a9108ebSHasso Tepper  * Written by Iain Hibbert for Itronix Inc.
90a9108ebSHasso Tepper  *
100a9108ebSHasso Tepper  * Redistribution and use in source and binary forms, with or without
110a9108ebSHasso Tepper  * modification, are permitted provided that the following conditions
120a9108ebSHasso Tepper  * are met:
130a9108ebSHasso Tepper  * 1. Redistributions of source code must retain the above copyright
140a9108ebSHasso Tepper  *    notice, this list of conditions and the following disclaimer.
150a9108ebSHasso Tepper  * 2. Redistributions in binary form must reproduce the above copyright
160a9108ebSHasso Tepper  *    notice, this list of conditions and the following disclaimer in the
170a9108ebSHasso Tepper  *    documentation and/or other materials provided with the distribution.
180a9108ebSHasso Tepper  * 3. The name of Itronix Inc. may not be used to endorse
190a9108ebSHasso Tepper  *    or promote products derived from this software without specific
200a9108ebSHasso Tepper  *    prior written permission.
210a9108ebSHasso Tepper  *
220a9108ebSHasso Tepper  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
230a9108ebSHasso Tepper  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
240a9108ebSHasso Tepper  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
250a9108ebSHasso Tepper  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
260a9108ebSHasso Tepper  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
270a9108ebSHasso Tepper  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
280a9108ebSHasso Tepper  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
290a9108ebSHasso Tepper  * ON ANY THEORY OF LIABILITY, WHETHER IN
300a9108ebSHasso Tepper  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
310a9108ebSHasso Tepper  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
320a9108ebSHasso Tepper  * POSSIBILITY OF SUCH DAMAGE.
330a9108ebSHasso Tepper  */
340a9108ebSHasso Tepper 
350a9108ebSHasso Tepper #include <sys/param.h>
360a9108ebSHasso Tepper #include <sys/kernel.h>
37805c8e8eSzrj #include <sys/malloc.h>
380a9108ebSHasso Tepper #include <sys/mbuf.h>
390a9108ebSHasso Tepper #include <sys/proc.h>
400a9108ebSHasso Tepper #include <sys/systm.h>
410a9108ebSHasso Tepper #include <sys/types.h>
420a9108ebSHasso Tepper #include <sys/endian.h>
430a9108ebSHasso Tepper 
440a9108ebSHasso Tepper #include <net/if.h>
450a9108ebSHasso Tepper 
460a9108ebSHasso Tepper #include <netbt/bluetooth.h>
470a9108ebSHasso Tepper #include <netbt/hci.h>
480a9108ebSHasso Tepper #include <netbt/l2cap.h>
490a9108ebSHasso Tepper #include <netbt/rfcomm.h>
500a9108ebSHasso Tepper 
510a9108ebSHasso Tepper /******************************************************************************
520a9108ebSHasso Tepper  *
530a9108ebSHasso Tepper  * RFCOMM Multiplexer Sessions sit directly on L2CAP channels, and can
540a9108ebSHasso Tepper  * multiplex up to 30 incoming and 30 outgoing connections.
550a9108ebSHasso Tepper  * Only one Multiplexer is allowed between any two devices.
560a9108ebSHasso Tepper  */
570a9108ebSHasso Tepper 
580a9108ebSHasso Tepper static void rfcomm_session_recv_sabm(struct rfcomm_session *, int);
590a9108ebSHasso Tepper static void rfcomm_session_recv_disc(struct rfcomm_session *, int);
600a9108ebSHasso Tepper static void rfcomm_session_recv_ua(struct rfcomm_session *, int);
610a9108ebSHasso Tepper static void rfcomm_session_recv_dm(struct rfcomm_session *, int);
620a9108ebSHasso Tepper static void rfcomm_session_recv_uih(struct rfcomm_session *, int, int, struct mbuf *, int);
630a9108ebSHasso Tepper static void rfcomm_session_recv_mcc(struct rfcomm_session *, struct mbuf *);
640a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_test(struct rfcomm_session *, int, struct mbuf *);
650a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_fcon(struct rfcomm_session *, int);
660a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_fcoff(struct rfcomm_session *, int);
670a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_msc(struct rfcomm_session *, int, struct mbuf *);
680a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_rpn(struct rfcomm_session *, int, struct mbuf *);
690a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_rls(struct rfcomm_session *, int, struct mbuf *);
700a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_pn(struct rfcomm_session *, int, struct mbuf *);
710a9108ebSHasso Tepper static void rfcomm_session_recv_mcc_nsc(struct rfcomm_session *, int, struct mbuf *);
720a9108ebSHasso Tepper 
730a9108ebSHasso Tepper /* L2CAP callbacks */
740a9108ebSHasso Tepper static void rfcomm_session_connecting(void *);
750a9108ebSHasso Tepper static void rfcomm_session_connected(void *);
760a9108ebSHasso Tepper static void rfcomm_session_disconnected(void *, int);
770a9108ebSHasso Tepper static void *rfcomm_session_newconn(void *, struct sockaddr_bt *, struct sockaddr_bt *);
780a9108ebSHasso Tepper static void rfcomm_session_complete(void *, int);
790a9108ebSHasso Tepper static void rfcomm_session_linkmode(void *, int);
800a9108ebSHasso Tepper static void rfcomm_session_input(void *, struct mbuf *);
810a9108ebSHasso Tepper 
820a9108ebSHasso Tepper static const struct btproto rfcomm_session_proto = {
830a9108ebSHasso Tepper 	rfcomm_session_connecting,
840a9108ebSHasso Tepper 	rfcomm_session_connected,
850a9108ebSHasso Tepper 	rfcomm_session_disconnected,
860a9108ebSHasso Tepper 	rfcomm_session_newconn,
870a9108ebSHasso Tepper 	rfcomm_session_complete,
880a9108ebSHasso Tepper 	rfcomm_session_linkmode,
890a9108ebSHasso Tepper 	rfcomm_session_input,
900a9108ebSHasso Tepper };
910a9108ebSHasso Tepper 
920a9108ebSHasso Tepper struct rfcomm_session_list
930a9108ebSHasso Tepper 	rfcomm_session_active = LIST_HEAD_INITIALIZER(rfcomm_session_active);
940a9108ebSHasso Tepper 
950a9108ebSHasso Tepper struct rfcomm_session_list
960a9108ebSHasso Tepper 	rfcomm_session_listen = LIST_HEAD_INITIALIZER(rfcomm_session_listen);
970a9108ebSHasso Tepper 
980a9108ebSHasso Tepper vm_zone_t rfcomm_credit_pool;
990a9108ebSHasso Tepper 
1000a9108ebSHasso Tepper /*
1010a9108ebSHasso Tepper  * RFCOMM System Parameters (see section 5.3)
1020a9108ebSHasso Tepper  */
1030a9108ebSHasso Tepper int rfcomm_mtu_default = 127;	/* bytes */
1040a9108ebSHasso Tepper int rfcomm_ack_timeout = 20;	/* seconds */
1050a9108ebSHasso Tepper int rfcomm_mcc_timeout = 20;	/* seconds */
1060a9108ebSHasso Tepper 
1070a9108ebSHasso Tepper /*
1080a9108ebSHasso Tepper  * Reversed CRC table as per TS 07.10 Annex B.3.5
1090a9108ebSHasso Tepper  */
1100a9108ebSHasso Tepper static const uint8_t crctable[256] = {	/* reversed, 8-bit, poly=0x07 */
1110a9108ebSHasso Tepper 	0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
1120a9108ebSHasso Tepper 	0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
1130a9108ebSHasso Tepper 	0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
1140a9108ebSHasso Tepper 	0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
1150a9108ebSHasso Tepper 
1160a9108ebSHasso Tepper 	0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
1170a9108ebSHasso Tepper 	0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
1180a9108ebSHasso Tepper 	0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
1190a9108ebSHasso Tepper 	0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
1200a9108ebSHasso Tepper 
1210a9108ebSHasso Tepper 	0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
1220a9108ebSHasso Tepper 	0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
1230a9108ebSHasso Tepper 	0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
1240a9108ebSHasso Tepper 	0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
1250a9108ebSHasso Tepper 
1260a9108ebSHasso Tepper 	0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
1270a9108ebSHasso Tepper 	0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
1280a9108ebSHasso Tepper 	0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
1290a9108ebSHasso Tepper 	0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
1300a9108ebSHasso Tepper 
1310a9108ebSHasso Tepper 	0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
1320a9108ebSHasso Tepper 	0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
1330a9108ebSHasso Tepper 	0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
1340a9108ebSHasso Tepper 	0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
1350a9108ebSHasso Tepper 
1360a9108ebSHasso Tepper 	0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
1370a9108ebSHasso Tepper 	0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
1380a9108ebSHasso Tepper 	0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
1390a9108ebSHasso Tepper 	0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
1400a9108ebSHasso Tepper 
1410a9108ebSHasso Tepper 	0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
1420a9108ebSHasso Tepper 	0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
1430a9108ebSHasso Tepper 	0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
1440a9108ebSHasso Tepper 	0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
1450a9108ebSHasso Tepper 
1460a9108ebSHasso Tepper 	0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
1470a9108ebSHasso Tepper 	0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
1480a9108ebSHasso Tepper 	0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
1490a9108ebSHasso Tepper 	0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
1500a9108ebSHasso Tepper };
1510a9108ebSHasso Tepper 
1520a9108ebSHasso Tepper #define FCS(f, d)	crctable[(f) ^ (d)]
1530a9108ebSHasso Tepper 
1540a9108ebSHasso Tepper /*
1550a9108ebSHasso Tepper  * rfcomm_init()
1560a9108ebSHasso Tepper  *
1570a9108ebSHasso Tepper  * initialize the "credit pool".
1580a9108ebSHasso Tepper  */
1590a9108ebSHasso Tepper void
rfcomm_init(void)1600a9108ebSHasso Tepper rfcomm_init(void)
1610a9108ebSHasso Tepper {
1620a9108ebSHasso Tepper 	rfcomm_credit_pool = zinit("rfcomm_credit",
1633091de50SMatthew Dillon 				   sizeof(struct rfcomm_credit),
1643091de50SMatthew Dillon 				   0, 0);
1650a9108ebSHasso Tepper }
1660a9108ebSHasso Tepper 
1670a9108ebSHasso Tepper /*
1680a9108ebSHasso Tepper  * rfcomm_session_alloc(list, sockaddr)
1690a9108ebSHasso Tepper  *
1700a9108ebSHasso Tepper  * allocate a new session and fill in the blanks, then
1710a9108ebSHasso Tepper  * attach session to front of specified list (active or listen)
1720a9108ebSHasso Tepper  */
1730a9108ebSHasso Tepper struct rfcomm_session *
rfcomm_session_alloc(struct rfcomm_session_list * list,struct sockaddr_bt * laddr)1740a9108ebSHasso Tepper rfcomm_session_alloc(struct rfcomm_session_list *list,
1750a9108ebSHasso Tepper 			struct sockaddr_bt *laddr)
1760a9108ebSHasso Tepper {
1770a9108ebSHasso Tepper 	struct rfcomm_session *rs;
1780a9108ebSHasso Tepper 	int err;
1790a9108ebSHasso Tepper 
1800a9108ebSHasso Tepper 	rs = kmalloc(sizeof(*rs), M_BLUETOOTH, M_NOWAIT | M_ZERO);
1810a9108ebSHasso Tepper 	if (rs == NULL)
1820a9108ebSHasso Tepper 		return NULL;
1830a9108ebSHasso Tepper 
1840a9108ebSHasso Tepper 	rs->rs_state = RFCOMM_SESSION_CLOSED;
1850a9108ebSHasso Tepper 
1860a9108ebSHasso Tepper 	callout_init(&rs->rs_timeout);
1870a9108ebSHasso Tepper 
1880a9108ebSHasso Tepper 	STAILQ_INIT(&rs->rs_credits);
1890a9108ebSHasso Tepper 	LIST_INIT(&rs->rs_dlcs);
1900a9108ebSHasso Tepper 
1910a9108ebSHasso Tepper 	err = l2cap_attach(&rs->rs_l2cap, &rfcomm_session_proto, rs);
1920a9108ebSHasso Tepper 	if (err) {
1930a9108ebSHasso Tepper 		kfree(rs, M_BLUETOOTH);
1940a9108ebSHasso Tepper 		return NULL;
1950a9108ebSHasso Tepper 	}
1960a9108ebSHasso Tepper 
1970a9108ebSHasso Tepper 	(void)l2cap_getopt(rs->rs_l2cap, SO_L2CAP_OMTU, &rs->rs_mtu);
1980a9108ebSHasso Tepper 
1990a9108ebSHasso Tepper 	if (laddr->bt_psm == L2CAP_PSM_ANY)
2000a9108ebSHasso Tepper 		laddr->bt_psm = L2CAP_PSM_RFCOMM;
2010a9108ebSHasso Tepper 
2020a9108ebSHasso Tepper 	(void)l2cap_bind(rs->rs_l2cap, laddr);
2030a9108ebSHasso Tepper 
2040a9108ebSHasso Tepper 	LIST_INSERT_HEAD(list, rs, rs_next);
2050a9108ebSHasso Tepper 
2060a9108ebSHasso Tepper 	return rs;
2070a9108ebSHasso Tepper }
2080a9108ebSHasso Tepper 
2090a9108ebSHasso Tepper /*
2100a9108ebSHasso Tepper  * rfcomm_session_free(rfcomm_session)
2110a9108ebSHasso Tepper  *
2120a9108ebSHasso Tepper  * release a session, including any cleanup
2130a9108ebSHasso Tepper  */
2140a9108ebSHasso Tepper void
rfcomm_session_free(struct rfcomm_session * rs)2150a9108ebSHasso Tepper rfcomm_session_free(struct rfcomm_session *rs)
2160a9108ebSHasso Tepper {
2170a9108ebSHasso Tepper 	struct rfcomm_credit *credit;
2180a9108ebSHasso Tepper 
2190a9108ebSHasso Tepper 	KKASSERT(rs != NULL);
2200a9108ebSHasso Tepper 	KKASSERT(LIST_EMPTY(&rs->rs_dlcs));
2210a9108ebSHasso Tepper 
2220a9108ebSHasso Tepper 	rs->rs_state = RFCOMM_SESSION_CLOSED;
2230a9108ebSHasso Tepper 
2240a9108ebSHasso Tepper 	/*
2250a9108ebSHasso Tepper 	 * If the callout is already invoked we have no way to stop it,
2260a9108ebSHasso Tepper 	 * but it will call us back right away (there are no DLC's) so
2270a9108ebSHasso Tepper 	 * not to worry.
2280a9108ebSHasso Tepper 	 */
2290a9108ebSHasso Tepper 	callout_stop(&rs->rs_timeout);
2300a9108ebSHasso Tepper 	if (callout_active(&rs->rs_timeout))
2310a9108ebSHasso Tepper 		return;
2320a9108ebSHasso Tepper 
2330a9108ebSHasso Tepper 	/*
2340a9108ebSHasso Tepper 	 * Take care that rfcomm_session_disconnected() doesnt call
2350a9108ebSHasso Tepper 	 * us back either as it will do if the l2cap_channel has not
2360a9108ebSHasso Tepper 	 * been closed when we detach it..
2370a9108ebSHasso Tepper 	 */
2380a9108ebSHasso Tepper 	if (rs->rs_flags & RFCOMM_SESSION_FREE)
2390a9108ebSHasso Tepper 		return;
2400a9108ebSHasso Tepper 
2410a9108ebSHasso Tepper 	rs->rs_flags |= RFCOMM_SESSION_FREE;
2420a9108ebSHasso Tepper 
2430a9108ebSHasso Tepper 	/* throw away any remaining credit notes */
2440a9108ebSHasso Tepper 	while ((credit = STAILQ_FIRST(&rs->rs_credits)) != NULL) {
2450a9108ebSHasso Tepper 		STAILQ_REMOVE_HEAD(&rs->rs_credits, rc_next);
2465179415aSSascha Wildner 		zfree(rfcomm_credit_pool, credit);
2470a9108ebSHasso Tepper 	}
2480a9108ebSHasso Tepper 
2490a9108ebSHasso Tepper 	KKASSERT(STAILQ_EMPTY(&rs->rs_credits));
2500a9108ebSHasso Tepper 
2510a9108ebSHasso Tepper 	/* Goodbye! */
2520a9108ebSHasso Tepper 	LIST_REMOVE(rs, rs_next);
2530a9108ebSHasso Tepper 	l2cap_detach(&rs->rs_l2cap);
2540a9108ebSHasso Tepper 	kfree(rs, M_BLUETOOTH);
2550a9108ebSHasso Tepper }
2560a9108ebSHasso Tepper 
2570a9108ebSHasso Tepper /*
2580a9108ebSHasso Tepper  * rfcomm_session_lookup(sockaddr, sockaddr)
2590a9108ebSHasso Tepper  *
2600a9108ebSHasso Tepper  * Find active rfcomm session matching src and dest addresses
2610a9108ebSHasso Tepper  * when src is BDADDR_ANY match any local address
2620a9108ebSHasso Tepper  */
2630a9108ebSHasso Tepper struct rfcomm_session *
rfcomm_session_lookup(struct sockaddr_bt * src,struct sockaddr_bt * dest)2640a9108ebSHasso Tepper rfcomm_session_lookup(struct sockaddr_bt *src, struct sockaddr_bt *dest)
2650a9108ebSHasso Tepper {
2660a9108ebSHasso Tepper 	struct rfcomm_session *rs;
2670a9108ebSHasso Tepper 	struct sockaddr_bt addr;
2680a9108ebSHasso Tepper 
2690a9108ebSHasso Tepper 	LIST_FOREACH(rs, &rfcomm_session_active, rs_next) {
2700a9108ebSHasso Tepper 		if (rs->rs_state == RFCOMM_SESSION_CLOSED)
2710a9108ebSHasso Tepper 			continue;
2720a9108ebSHasso Tepper 
2730a9108ebSHasso Tepper 		l2cap_sockaddr(rs->rs_l2cap, &addr);
2740a9108ebSHasso Tepper 
2750a9108ebSHasso Tepper 		if (bdaddr_same(&src->bt_bdaddr, &addr.bt_bdaddr) == 0)
2760a9108ebSHasso Tepper 			if (bdaddr_any(&src->bt_bdaddr) == 0)
2770a9108ebSHasso Tepper 				continue;
2780a9108ebSHasso Tepper 
2790a9108ebSHasso Tepper 		l2cap_peeraddr(rs->rs_l2cap, &addr);
2800a9108ebSHasso Tepper 
2810a9108ebSHasso Tepper 		if (addr.bt_psm != dest->bt_psm)
2820a9108ebSHasso Tepper 			continue;
2830a9108ebSHasso Tepper 
2840a9108ebSHasso Tepper 		if (bdaddr_same(&dest->bt_bdaddr, &addr.bt_bdaddr))
2850a9108ebSHasso Tepper 			break;
2860a9108ebSHasso Tepper 	}
2870a9108ebSHasso Tepper 
2880a9108ebSHasso Tepper 	return rs;
2890a9108ebSHasso Tepper }
2900a9108ebSHasso Tepper 
2910a9108ebSHasso Tepper /*
2920a9108ebSHasso Tepper  * rfcomm_session_timeout(rfcomm_session)
2930a9108ebSHasso Tepper  *
2940a9108ebSHasso Tepper  * Session timeouts are scheduled when a session is left or
2950a9108ebSHasso Tepper  * created with no DLCs, and when SABM(0) or DISC(0) are
2960a9108ebSHasso Tepper  * sent.
2970a9108ebSHasso Tepper  *
2980a9108ebSHasso Tepper  * So, if it is in an open state with DLC's attached then
2990a9108ebSHasso Tepper  * we leave it alone, otherwise the session is lost.
3000a9108ebSHasso Tepper  */
3010a9108ebSHasso Tepper void
rfcomm_session_timeout(void * arg)3020a9108ebSHasso Tepper rfcomm_session_timeout(void *arg)
3030a9108ebSHasso Tepper {
3040a9108ebSHasso Tepper 	struct rfcomm_session *rs = arg;
3050a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
3060a9108ebSHasso Tepper 
3070a9108ebSHasso Tepper 	KKASSERT(rs != NULL);
3080a9108ebSHasso Tepper 
3090a9108ebSHasso Tepper 	crit_enter();
3100a9108ebSHasso Tepper 
3110a9108ebSHasso Tepper 	if (rs->rs_state != RFCOMM_SESSION_OPEN) {
3120a9108ebSHasso Tepper 		DPRINTF("timeout\n");
3130a9108ebSHasso Tepper 		rs->rs_state = RFCOMM_SESSION_CLOSED;
3140a9108ebSHasso Tepper 
3150a9108ebSHasso Tepper 		while (!LIST_EMPTY(&rs->rs_dlcs)) {
3160a9108ebSHasso Tepper 			dlc = LIST_FIRST(&rs->rs_dlcs);
3170a9108ebSHasso Tepper 
3180a9108ebSHasso Tepper 			rfcomm_dlc_close(dlc, ETIMEDOUT);
3190a9108ebSHasso Tepper 		}
3200a9108ebSHasso Tepper 	}
3210a9108ebSHasso Tepper 
3220a9108ebSHasso Tepper 	if (LIST_EMPTY(&rs->rs_dlcs)) {
3230a9108ebSHasso Tepper 		DPRINTF("expiring\n");
3240a9108ebSHasso Tepper 		rfcomm_session_free(rs);
3250a9108ebSHasso Tepper 	}
3260a9108ebSHasso Tepper 	crit_exit();
3270a9108ebSHasso Tepper }
3280a9108ebSHasso Tepper 
3290a9108ebSHasso Tepper /***********************************************************************
3300a9108ebSHasso Tepper  *
3310a9108ebSHasso Tepper  *	RFCOMM Session L2CAP protocol callbacks
3320a9108ebSHasso Tepper  *
3330a9108ebSHasso Tepper  */
3340a9108ebSHasso Tepper 
3350a9108ebSHasso Tepper static void
rfcomm_session_connecting(void * arg)3360a9108ebSHasso Tepper rfcomm_session_connecting(void *arg)
3370a9108ebSHasso Tepper {
3380a9108ebSHasso Tepper 	/* struct rfcomm_session *rs = arg; */
3390a9108ebSHasso Tepper 
3400a9108ebSHasso Tepper 	DPRINTF("Connecting\n");
3410a9108ebSHasso Tepper }
3420a9108ebSHasso Tepper 
3430a9108ebSHasso Tepper static void
rfcomm_session_connected(void * arg)3440a9108ebSHasso Tepper rfcomm_session_connected(void *arg)
3450a9108ebSHasso Tepper {
3460a9108ebSHasso Tepper 	struct rfcomm_session *rs = arg;
3470a9108ebSHasso Tepper 
3480a9108ebSHasso Tepper 	DPRINTF("Connected\n");
3490a9108ebSHasso Tepper 
3500a9108ebSHasso Tepper 	/*
3510a9108ebSHasso Tepper 	 * L2CAP is open.
3520a9108ebSHasso Tepper 	 *
3530a9108ebSHasso Tepper 	 * If we are initiator, we can send our SABM(0)
3540a9108ebSHasso Tepper 	 * a timeout should be active?
3550a9108ebSHasso Tepper 	 *
3560a9108ebSHasso Tepper 	 * We must take note of the L2CAP MTU because currently
3570a9108ebSHasso Tepper 	 * the L2CAP implementation can only do Basic Mode.
3580a9108ebSHasso Tepper 	 */
3590a9108ebSHasso Tepper 	l2cap_getopt(rs->rs_l2cap, SO_L2CAP_OMTU, &rs->rs_mtu);
3600a9108ebSHasso Tepper 
3610a9108ebSHasso Tepper 	rs->rs_mtu -= 6; /* (RFCOMM overhead could be this big) */
3620a9108ebSHasso Tepper 	if (rs->rs_mtu < RFCOMM_MTU_MIN) {
3630a9108ebSHasso Tepper 		rfcomm_session_disconnected(rs, EINVAL);
3640a9108ebSHasso Tepper 		return;
3650a9108ebSHasso Tepper 	}
3660a9108ebSHasso Tepper 
3670a9108ebSHasso Tepper 	if (IS_INITIATOR(rs)) {
3680a9108ebSHasso Tepper 		int err;
3690a9108ebSHasso Tepper 
3700a9108ebSHasso Tepper 		err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_SABM, 0);
3710a9108ebSHasso Tepper 		if (err)
3720a9108ebSHasso Tepper 			rfcomm_session_disconnected(rs, err);
3730a9108ebSHasso Tepper 
3740a9108ebSHasso Tepper 		callout_reset(&rs->rs_timeout, rfcomm_ack_timeout * hz,
3750a9108ebSHasso Tepper 		    rfcomm_session_timeout, rs);
3760a9108ebSHasso Tepper 	}
3770a9108ebSHasso Tepper }
3780a9108ebSHasso Tepper 
3790a9108ebSHasso Tepper static void
rfcomm_session_disconnected(void * arg,int err)3800a9108ebSHasso Tepper rfcomm_session_disconnected(void *arg, int err)
3810a9108ebSHasso Tepper {
3820a9108ebSHasso Tepper 	struct rfcomm_session *rs = arg;
3830a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
3840a9108ebSHasso Tepper 
3850a9108ebSHasso Tepper 	DPRINTF("Disconnected\n");
3860a9108ebSHasso Tepper 
3870a9108ebSHasso Tepper 	rs->rs_state = RFCOMM_SESSION_CLOSED;
3880a9108ebSHasso Tepper 
3890a9108ebSHasso Tepper 	while (!LIST_EMPTY(&rs->rs_dlcs)) {
3900a9108ebSHasso Tepper 		dlc = LIST_FIRST(&rs->rs_dlcs);
3910a9108ebSHasso Tepper 
3920a9108ebSHasso Tepper 		rfcomm_dlc_close(dlc, err);
3930a9108ebSHasso Tepper 	}
3940a9108ebSHasso Tepper 
3950a9108ebSHasso Tepper 	rfcomm_session_free(rs);
3960a9108ebSHasso Tepper }
3970a9108ebSHasso Tepper 
3980a9108ebSHasso Tepper static void *
rfcomm_session_newconn(void * arg,struct sockaddr_bt * laddr,struct sockaddr_bt * raddr)3990a9108ebSHasso Tepper rfcomm_session_newconn(void *arg, struct sockaddr_bt *laddr,
4000a9108ebSHasso Tepper 				struct sockaddr_bt *raddr)
4010a9108ebSHasso Tepper {
4020a9108ebSHasso Tepper 	struct rfcomm_session *new, *rs = arg;
4030a9108ebSHasso Tepper 
4040a9108ebSHasso Tepper 	DPRINTF("New Connection\n");
4050a9108ebSHasso Tepper 
4060a9108ebSHasso Tepper 	/*
4070a9108ebSHasso Tepper 	 * Incoming session connect request. We should return a new
4080a9108ebSHasso Tepper 	 * session pointer if this is acceptable. The L2CAP layer
4090a9108ebSHasso Tepper 	 * passes local and remote addresses, which we must check as
4100a9108ebSHasso Tepper 	 * only one RFCOMM session is allowed between any two devices
4110a9108ebSHasso Tepper 	 */
4120a9108ebSHasso Tepper 	new = rfcomm_session_lookup(laddr, raddr);
4130a9108ebSHasso Tepper 	if (new != NULL)
4140a9108ebSHasso Tepper 		return NULL;
4150a9108ebSHasso Tepper 
4160a9108ebSHasso Tepper 	new = rfcomm_session_alloc(&rfcomm_session_active, laddr);
4170a9108ebSHasso Tepper 	if (new == NULL)
4180a9108ebSHasso Tepper 		return NULL;
4190a9108ebSHasso Tepper 
4200a9108ebSHasso Tepper 	new->rs_mtu = rs->rs_mtu;
4210a9108ebSHasso Tepper 	new->rs_state = RFCOMM_SESSION_WAIT_CONNECT;
4220a9108ebSHasso Tepper 
4230a9108ebSHasso Tepper 	/*
4240a9108ebSHasso Tepper 	 * schedule an expiry so that if nothing comes of it we
4250a9108ebSHasso Tepper 	 * can punt.
4260a9108ebSHasso Tepper 	 */
4270a9108ebSHasso Tepper 	callout_reset(&rs->rs_timeout, rfcomm_mcc_timeout * hz,
4280a9108ebSHasso Tepper 	    rfcomm_session_timeout, rs);
4290a9108ebSHasso Tepper 
4300a9108ebSHasso Tepper 	return new->rs_l2cap;
4310a9108ebSHasso Tepper }
4320a9108ebSHasso Tepper 
4330a9108ebSHasso Tepper static void
rfcomm_session_complete(void * arg,int count)4340a9108ebSHasso Tepper rfcomm_session_complete(void *arg, int count)
4350a9108ebSHasso Tepper {
4360a9108ebSHasso Tepper 	struct rfcomm_session *rs = arg;
4370a9108ebSHasso Tepper 	struct rfcomm_credit *credit;
4380a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
4390a9108ebSHasso Tepper 
4400a9108ebSHasso Tepper 	/*
4410a9108ebSHasso Tepper 	 * count L2CAP packets are 'complete', meaning that they are cleared
4420a9108ebSHasso Tepper 	 * our buffers (for best effort) or arrived safe (for guaranteed) so
4430a9108ebSHasso Tepper 	 * we can take it off our list and pass the message on, so that
4440a9108ebSHasso Tepper 	 * eventually the data can be removed from the sockbuf
4450a9108ebSHasso Tepper 	 */
4460a9108ebSHasso Tepper 	while (count-- > 0) {
4470a9108ebSHasso Tepper 		credit = STAILQ_FIRST(&rs->rs_credits);
4480a9108ebSHasso Tepper #ifdef DIAGNOSTIC
4490a9108ebSHasso Tepper 		if (credit == NULL) {
4500a9108ebSHasso Tepper 			kprintf("%s: too many packets completed!\n", __func__);
4510a9108ebSHasso Tepper 			break;
4520a9108ebSHasso Tepper 		}
4530a9108ebSHasso Tepper #endif
4540a9108ebSHasso Tepper 		dlc = credit->rc_dlc;
4550a9108ebSHasso Tepper 		if (dlc != NULL) {
4560a9108ebSHasso Tepper 			dlc->rd_pending--;
4570a9108ebSHasso Tepper 			(*dlc->rd_proto->complete)
4580a9108ebSHasso Tepper 					(dlc->rd_upper, credit->rc_len);
4590a9108ebSHasso Tepper 
4600a9108ebSHasso Tepper 			/*
4610a9108ebSHasso Tepper 			 * if not using credit flow control, we may push
4620a9108ebSHasso Tepper 			 * more data now
4630a9108ebSHasso Tepper 			 */
4640a9108ebSHasso Tepper 			if ((rs->rs_flags & RFCOMM_SESSION_CFC) == 0
4650a9108ebSHasso Tepper 			    && dlc->rd_state == RFCOMM_DLC_OPEN) {
4660a9108ebSHasso Tepper 				rfcomm_dlc_start(dlc);
4670a9108ebSHasso Tepper 			}
4680a9108ebSHasso Tepper 
4690a9108ebSHasso Tepper 			/*
4700a9108ebSHasso Tepper 			 * When shutdown is indicated, we are just waiting to
4710a9108ebSHasso Tepper 			 * clear outgoing data.
4720a9108ebSHasso Tepper 			 */
4730a9108ebSHasso Tepper 			if ((dlc->rd_flags & RFCOMM_DLC_SHUTDOWN)
4740a9108ebSHasso Tepper 			    && dlc->rd_txbuf == NULL && dlc->rd_pending == 0) {
4750a9108ebSHasso Tepper 				dlc->rd_state = RFCOMM_DLC_WAIT_DISCONNECT;
4760a9108ebSHasso Tepper 				rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC,
4770a9108ebSHasso Tepper 							    dlc->rd_dlci);
4780a9108ebSHasso Tepper 				callout_reset(&dlc->rd_timeout,
4790a9108ebSHasso Tepper 				    rfcomm_ack_timeout * hz,
4800a9108ebSHasso Tepper 				    rfcomm_dlc_timeout, dlc);
4810a9108ebSHasso Tepper 			}
4820a9108ebSHasso Tepper 		}
4830a9108ebSHasso Tepper 
4840a9108ebSHasso Tepper 		STAILQ_REMOVE_HEAD(&rs->rs_credits, rc_next);
4855179415aSSascha Wildner 		zfree(rfcomm_credit_pool, credit);
4860a9108ebSHasso Tepper 	}
4870a9108ebSHasso Tepper 
4880a9108ebSHasso Tepper 	/*
4890a9108ebSHasso Tepper 	 * If session is closed, we are just waiting to clear the queue
4900a9108ebSHasso Tepper 	 */
4910a9108ebSHasso Tepper 	if (rs->rs_state == RFCOMM_SESSION_CLOSED) {
4920a9108ebSHasso Tepper 		if (STAILQ_EMPTY(&rs->rs_credits))
4930a9108ebSHasso Tepper 			l2cap_disconnect(rs->rs_l2cap, 0);
4940a9108ebSHasso Tepper 	}
4950a9108ebSHasso Tepper }
4960a9108ebSHasso Tepper 
4970a9108ebSHasso Tepper /*
4980a9108ebSHasso Tepper  * Link Mode changed
4990a9108ebSHasso Tepper  *
5000a9108ebSHasso Tepper  * This is called when a mode change is complete. Proceed with connections
5010a9108ebSHasso Tepper  * where appropriate, or pass the new mode to any active DLCs.
5020a9108ebSHasso Tepper  */
5030a9108ebSHasso Tepper static void
rfcomm_session_linkmode(void * arg,int new)5040a9108ebSHasso Tepper rfcomm_session_linkmode(void *arg, int new)
5050a9108ebSHasso Tepper {
5060a9108ebSHasso Tepper 	struct rfcomm_session *rs = arg;
5070a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc, *next;
5080a9108ebSHasso Tepper 	int err, mode = 0;
5090a9108ebSHasso Tepper 
5100a9108ebSHasso Tepper 	DPRINTF("auth %s, encrypt %s, secure %s\n",
5110a9108ebSHasso Tepper 		(new & L2CAP_LM_AUTH ? "on" : "off"),
5120a9108ebSHasso Tepper 		(new & L2CAP_LM_ENCRYPT ? "on" : "off"),
5130a9108ebSHasso Tepper 		(new & L2CAP_LM_SECURE ? "on" : "off"));
5140a9108ebSHasso Tepper 
5150a9108ebSHasso Tepper 	if (new & L2CAP_LM_AUTH)
5160a9108ebSHasso Tepper 		mode |= RFCOMM_LM_AUTH;
5170a9108ebSHasso Tepper 
5180a9108ebSHasso Tepper 	if (new & L2CAP_LM_ENCRYPT)
5190a9108ebSHasso Tepper 		mode |= RFCOMM_LM_ENCRYPT;
5200a9108ebSHasso Tepper 
5210a9108ebSHasso Tepper 	if (new & L2CAP_LM_SECURE)
5220a9108ebSHasso Tepper 		mode |= RFCOMM_LM_SECURE;
5230a9108ebSHasso Tepper 
5240a9108ebSHasso Tepper 	next = LIST_FIRST(&rs->rs_dlcs);
5250a9108ebSHasso Tepper 	while ((dlc = next) != NULL) {
5260a9108ebSHasso Tepper 		next = LIST_NEXT(dlc, rd_next);
5270a9108ebSHasso Tepper 
5280a9108ebSHasso Tepper 		switch (dlc->rd_state) {
5290a9108ebSHasso Tepper 		case RFCOMM_DLC_WAIT_SEND_SABM:	/* we are connecting */
5300a9108ebSHasso Tepper 			if ((mode & dlc->rd_mode) != dlc->rd_mode) {
5310a9108ebSHasso Tepper 				rfcomm_dlc_close(dlc, ECONNABORTED);
5320a9108ebSHasso Tepper 			} else {
5330a9108ebSHasso Tepper 				err = rfcomm_session_send_frame(rs,
5340a9108ebSHasso Tepper 					    RFCOMM_FRAME_SABM, dlc->rd_dlci);
5350a9108ebSHasso Tepper 				if (err) {
5360a9108ebSHasso Tepper 					rfcomm_dlc_close(dlc, err);
5370a9108ebSHasso Tepper 				} else {
5380a9108ebSHasso Tepper 					dlc->rd_state = RFCOMM_DLC_WAIT_RECV_UA;
5390a9108ebSHasso Tepper 					callout_reset(&dlc->rd_timeout,
5400a9108ebSHasso Tepper 					    rfcomm_ack_timeout * hz,
5410a9108ebSHasso Tepper 				    	    rfcomm_dlc_timeout, dlc);
5420a9108ebSHasso Tepper 
5430a9108ebSHasso Tepper 					break;
5440a9108ebSHasso Tepper 				}
5450a9108ebSHasso Tepper 			}
5460a9108ebSHasso Tepper 
5470a9108ebSHasso Tepper 			/*
5480a9108ebSHasso Tepper 			 * If we aborted the connection and there are no more DLCs
5490a9108ebSHasso Tepper 			 * on the session, it is our responsibility to disconnect.
5500a9108ebSHasso Tepper 			 */
5510a9108ebSHasso Tepper 			if (!LIST_EMPTY(&rs->rs_dlcs))
5520a9108ebSHasso Tepper 				break;
5530a9108ebSHasso Tepper 
5540a9108ebSHasso Tepper 			rs->rs_state = RFCOMM_SESSION_WAIT_DISCONNECT;
5550a9108ebSHasso Tepper 			rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC, 0);
5560a9108ebSHasso Tepper 			callout_reset(&rs->rs_timeout, rfcomm_ack_timeout * hz,
5570a9108ebSHasso Tepper 			    rfcomm_session_timeout, rs);
5580a9108ebSHasso Tepper 			break;
5590a9108ebSHasso Tepper 
5600a9108ebSHasso Tepper 		case RFCOMM_DLC_WAIT_SEND_UA: /* they are connecting */
5610a9108ebSHasso Tepper 			if ((mode & dlc->rd_mode) != dlc->rd_mode) {
5620a9108ebSHasso Tepper 				rfcomm_session_send_frame(rs,
5630a9108ebSHasso Tepper 					    RFCOMM_FRAME_DM, dlc->rd_dlci);
5640a9108ebSHasso Tepper 				rfcomm_dlc_close(dlc, ECONNABORTED);
5650a9108ebSHasso Tepper 				break;
5660a9108ebSHasso Tepper 			}
5670a9108ebSHasso Tepper 
5680a9108ebSHasso Tepper 			err = rfcomm_session_send_frame(rs,
5690a9108ebSHasso Tepper 					    RFCOMM_FRAME_UA, dlc->rd_dlci);
5700a9108ebSHasso Tepper 			if (err) {
5710a9108ebSHasso Tepper 				rfcomm_session_send_frame(rs,
5720a9108ebSHasso Tepper 						RFCOMM_FRAME_DM, dlc->rd_dlci);
5730a9108ebSHasso Tepper 				rfcomm_dlc_close(dlc, err);
5740a9108ebSHasso Tepper 				break;
5750a9108ebSHasso Tepper 			}
5760a9108ebSHasso Tepper 
5770a9108ebSHasso Tepper 			err = rfcomm_dlc_open(dlc);
5780a9108ebSHasso Tepper 			if (err) {
5790a9108ebSHasso Tepper 				rfcomm_session_send_frame(rs,
5800a9108ebSHasso Tepper 						RFCOMM_FRAME_DM, dlc->rd_dlci);
5810a9108ebSHasso Tepper 				rfcomm_dlc_close(dlc, err);
5820a9108ebSHasso Tepper 				break;
5830a9108ebSHasso Tepper 			}
5840a9108ebSHasso Tepper 
5850a9108ebSHasso Tepper 			break;
5860a9108ebSHasso Tepper 
5870a9108ebSHasso Tepper 		case RFCOMM_DLC_WAIT_RECV_UA:
5880a9108ebSHasso Tepper 		case RFCOMM_DLC_OPEN: /* already established */
5890a9108ebSHasso Tepper 			(*dlc->rd_proto->linkmode)(dlc->rd_upper, mode);
5900a9108ebSHasso Tepper 			break;
5910a9108ebSHasso Tepper 
5920a9108ebSHasso Tepper 		default:
5930a9108ebSHasso Tepper 			break;
5940a9108ebSHasso Tepper 		}
5950a9108ebSHasso Tepper 	}
5960a9108ebSHasso Tepper }
5970a9108ebSHasso Tepper 
5980a9108ebSHasso Tepper /*
5990a9108ebSHasso Tepper  * Receive data from L2CAP layer for session. There is always exactly one
6000a9108ebSHasso Tepper  * RFCOMM frame contained in each L2CAP frame.
6010a9108ebSHasso Tepper  */
6020a9108ebSHasso Tepper static void
rfcomm_session_input(void * arg,struct mbuf * m)6030a9108ebSHasso Tepper rfcomm_session_input(void *arg, struct mbuf *m)
6040a9108ebSHasso Tepper {
6050a9108ebSHasso Tepper 	struct rfcomm_session *rs = arg;
6060a9108ebSHasso Tepper 	int dlci, len, type, pf;
6070a9108ebSHasso Tepper 	uint8_t fcs, b;
6080a9108ebSHasso Tepper 
6090a9108ebSHasso Tepper 	KKASSERT(m != NULL);
6100a9108ebSHasso Tepper 	KKASSERT(rs != NULL);
6110a9108ebSHasso Tepper 
6120a9108ebSHasso Tepper 	/*
6130a9108ebSHasso Tepper 	 * UIH frames: FCS is only calculated on address and control fields
6140a9108ebSHasso Tepper 	 * For other frames: FCS is calculated on address, control and length
6150a9108ebSHasso Tepper 	 * Length may extend to two octets
6160a9108ebSHasso Tepper 	 */
6170a9108ebSHasso Tepper 	fcs = 0xff;
6180a9108ebSHasso Tepper 
6190a9108ebSHasso Tepper 	if (m->m_pkthdr.len < 4) {
6200a9108ebSHasso Tepper 		DPRINTF("short frame (%d), discarded\n", m->m_pkthdr.len);
6210a9108ebSHasso Tepper 		goto done;
6220a9108ebSHasso Tepper 	}
6230a9108ebSHasso Tepper 
6240a9108ebSHasso Tepper 	/* address - one octet */
6250a9108ebSHasso Tepper 	m_copydata(m, 0, 1, &b);
6260a9108ebSHasso Tepper 	m_adj(m, 1);
6270a9108ebSHasso Tepper 	fcs = FCS(fcs, b);
6280a9108ebSHasso Tepper 	dlci = RFCOMM_DLCI(b);
6290a9108ebSHasso Tepper 
6300a9108ebSHasso Tepper 	/* control - one octet */
6310a9108ebSHasso Tepper 	m_copydata(m, 0, 1, &b);
6320a9108ebSHasso Tepper 	m_adj(m, 1);
6330a9108ebSHasso Tepper 	fcs = FCS(fcs, b);
6340a9108ebSHasso Tepper 	type = RFCOMM_TYPE(b);
6350a9108ebSHasso Tepper 	pf = RFCOMM_PF(b);
6360a9108ebSHasso Tepper 
6370a9108ebSHasso Tepper 	/* length - may be two octets */
6380a9108ebSHasso Tepper 	m_copydata(m, 0, 1, &b);
6390a9108ebSHasso Tepper 	m_adj(m, 1);
6400a9108ebSHasso Tepper 	if (type != RFCOMM_FRAME_UIH)
6410a9108ebSHasso Tepper 		fcs = FCS(fcs, b);
6420a9108ebSHasso Tepper 	len = (b >> 1) & 0x7f;
6430a9108ebSHasso Tepper 
6440a9108ebSHasso Tepper 	if (RFCOMM_EA(b) == 0) {
6450a9108ebSHasso Tepper 		if (m->m_pkthdr.len < 2) {
6460a9108ebSHasso Tepper 			DPRINTF("short frame (%d, EA = 0), discarded\n",
6470a9108ebSHasso Tepper 				m->m_pkthdr.len);
6480a9108ebSHasso Tepper 			goto done;
6490a9108ebSHasso Tepper 		}
6500a9108ebSHasso Tepper 
6510a9108ebSHasso Tepper 		m_copydata(m, 0, 1, &b);
6520a9108ebSHasso Tepper 		m_adj(m, 1);
6530a9108ebSHasso Tepper 		if (type != RFCOMM_FRAME_UIH)
6540a9108ebSHasso Tepper 			fcs = FCS(fcs, b);
6550a9108ebSHasso Tepper 
6560a9108ebSHasso Tepper 		len |= (b << 7);
6570a9108ebSHasso Tepper 	}
6580a9108ebSHasso Tepper 
6590a9108ebSHasso Tepper 	/* FCS byte is last octet in frame */
6600a9108ebSHasso Tepper 	m_copydata(m, m->m_pkthdr.len - 1, 1, &b);
6610a9108ebSHasso Tepper 	m_adj(m, -1);
6620a9108ebSHasso Tepper 	fcs = FCS(fcs, b);
6630a9108ebSHasso Tepper 
6640a9108ebSHasso Tepper 	if (fcs != 0xcf) {
6650a9108ebSHasso Tepper 		DPRINTF("Bad FCS value (%#2.2x), frame discarded\n", fcs);
6660a9108ebSHasso Tepper 		goto done;
6670a9108ebSHasso Tepper 	}
6680a9108ebSHasso Tepper 
6690a9108ebSHasso Tepper 	DPRINTFN(10, "dlci %d, type %2.2x, len = %d\n", dlci, type, len);
6700a9108ebSHasso Tepper 
6710a9108ebSHasso Tepper 	switch (type) {
6720a9108ebSHasso Tepper 	case RFCOMM_FRAME_SABM:
6730a9108ebSHasso Tepper 		if (pf)
6740a9108ebSHasso Tepper 			rfcomm_session_recv_sabm(rs, dlci);
6750a9108ebSHasso Tepper 		break;
6760a9108ebSHasso Tepper 
6770a9108ebSHasso Tepper 	case RFCOMM_FRAME_DISC:
6780a9108ebSHasso Tepper 		if (pf)
6790a9108ebSHasso Tepper 			rfcomm_session_recv_disc(rs, dlci);
6800a9108ebSHasso Tepper 		break;
6810a9108ebSHasso Tepper 
6820a9108ebSHasso Tepper 	case RFCOMM_FRAME_UA:
6830a9108ebSHasso Tepper 		if (pf)
6840a9108ebSHasso Tepper 			rfcomm_session_recv_ua(rs, dlci);
6850a9108ebSHasso Tepper 		break;
6860a9108ebSHasso Tepper 
6870a9108ebSHasso Tepper 	case RFCOMM_FRAME_DM:
6880a9108ebSHasso Tepper 		rfcomm_session_recv_dm(rs, dlci);
6890a9108ebSHasso Tepper 		break;
6900a9108ebSHasso Tepper 
6910a9108ebSHasso Tepper 	case RFCOMM_FRAME_UIH:
6920a9108ebSHasso Tepper 		rfcomm_session_recv_uih(rs, dlci, pf, m, len);
6930a9108ebSHasso Tepper 		return;	/* (no release) */
6940a9108ebSHasso Tepper 
6950a9108ebSHasso Tepper 	default:
6960a9108ebSHasso Tepper 		UNKNOWN(type);
6970a9108ebSHasso Tepper 		break;
6980a9108ebSHasso Tepper 	}
6990a9108ebSHasso Tepper 
7000a9108ebSHasso Tepper done:
7010a9108ebSHasso Tepper 	m_freem(m);
7020a9108ebSHasso Tepper }
7030a9108ebSHasso Tepper 
7040a9108ebSHasso Tepper /***********************************************************************
7050a9108ebSHasso Tepper  *
7060a9108ebSHasso Tepper  *	RFCOMM Session receive processing
7070a9108ebSHasso Tepper  */
7080a9108ebSHasso Tepper 
7090a9108ebSHasso Tepper /*
7100a9108ebSHasso Tepper  * rfcomm_session_recv_sabm(rfcomm_session, dlci)
7110a9108ebSHasso Tepper  *
7120a9108ebSHasso Tepper  * Set Asyncrhonous Balanced Mode - open the channel.
7130a9108ebSHasso Tepper  */
7140a9108ebSHasso Tepper static void
rfcomm_session_recv_sabm(struct rfcomm_session * rs,int dlci)7150a9108ebSHasso Tepper rfcomm_session_recv_sabm(struct rfcomm_session *rs, int dlci)
7160a9108ebSHasso Tepper {
7170a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
7180a9108ebSHasso Tepper 	int err;
7190a9108ebSHasso Tepper 
7200a9108ebSHasso Tepper 	DPRINTFN(5, "SABM(%d)\n", dlci);
7210a9108ebSHasso Tepper 
7220a9108ebSHasso Tepper 	if (dlci == 0) {	/* Open Session */
7230a9108ebSHasso Tepper 		rs->rs_state = RFCOMM_SESSION_OPEN;
7240a9108ebSHasso Tepper 		rfcomm_session_send_frame(rs, RFCOMM_FRAME_UA, 0);
7250a9108ebSHasso Tepper 		LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
7260a9108ebSHasso Tepper 			if (dlc->rd_state == RFCOMM_DLC_WAIT_SESSION)
7270a9108ebSHasso Tepper 				rfcomm_dlc_connect(dlc);
7280a9108ebSHasso Tepper 		}
7290a9108ebSHasso Tepper 		return;
7300a9108ebSHasso Tepper 	}
7310a9108ebSHasso Tepper 
7320a9108ebSHasso Tepper 	if (rs->rs_state != RFCOMM_SESSION_OPEN) {
7330a9108ebSHasso Tepper 		DPRINTF("session was not even open!\n");
7340a9108ebSHasso Tepper 		return;
7350a9108ebSHasso Tepper 	}
7360a9108ebSHasso Tepper 
7370a9108ebSHasso Tepper 	/* validate direction bit */
7380a9108ebSHasso Tepper 	if ((IS_INITIATOR(rs) && !RFCOMM_DIRECTION(dlci))
7390a9108ebSHasso Tepper 	    || (!IS_INITIATOR(rs) && RFCOMM_DIRECTION(dlci))) {
7400a9108ebSHasso Tepper 		DPRINTF("Invalid direction bit on DLCI\n");
7410a9108ebSHasso Tepper 		return;
7420a9108ebSHasso Tepper 	}
7430a9108ebSHasso Tepper 
7440a9108ebSHasso Tepper 	/*
7450a9108ebSHasso Tepper 	 * look for our DLC - this may exist if we received PN
7460a9108ebSHasso Tepper 	 * already, or we may have to fabricate a new one.
7470a9108ebSHasso Tepper 	 */
7480a9108ebSHasso Tepper 	dlc = rfcomm_dlc_lookup(rs, dlci);
7490a9108ebSHasso Tepper 	if (dlc == NULL) {
7500a9108ebSHasso Tepper 		dlc = rfcomm_dlc_newconn(rs, dlci);
7510a9108ebSHasso Tepper 		if (dlc == NULL)
7520a9108ebSHasso Tepper 			return;	/* (DM is sent) */
7530a9108ebSHasso Tepper 	}
7540a9108ebSHasso Tepper 
7550a9108ebSHasso Tepper 	/*
7560a9108ebSHasso Tepper 	 * ..but if this DLC is not waiting to connect, they did
7570a9108ebSHasso Tepper 	 * something wrong, ignore it.
7580a9108ebSHasso Tepper 	 */
7590a9108ebSHasso Tepper 	if (dlc->rd_state != RFCOMM_DLC_WAIT_CONNECT)
7600a9108ebSHasso Tepper 		return;
7610a9108ebSHasso Tepper 
7620a9108ebSHasso Tepper 	/* set link mode */
7630a9108ebSHasso Tepper 	err = rfcomm_dlc_setmode(dlc);
7640a9108ebSHasso Tepper 	if (err == EINPROGRESS) {
7650a9108ebSHasso Tepper 		dlc->rd_state = RFCOMM_DLC_WAIT_SEND_UA;
7660a9108ebSHasso Tepper 		(*dlc->rd_proto->connecting)(dlc->rd_upper);
7670a9108ebSHasso Tepper 		return;
7680a9108ebSHasso Tepper 	}
7690a9108ebSHasso Tepper 	if (err)
7700a9108ebSHasso Tepper 		goto close;
7710a9108ebSHasso Tepper 
7720a9108ebSHasso Tepper 	err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_UA, dlci);
7730a9108ebSHasso Tepper 	if (err)
7740a9108ebSHasso Tepper 		goto close;
7750a9108ebSHasso Tepper 
7760a9108ebSHasso Tepper 	/* and mark it open */
7770a9108ebSHasso Tepper 	err = rfcomm_dlc_open(dlc);
7780a9108ebSHasso Tepper 	if (err)
7790a9108ebSHasso Tepper 		goto close;
7800a9108ebSHasso Tepper 
7810a9108ebSHasso Tepper 	return;
7820a9108ebSHasso Tepper 
7830a9108ebSHasso Tepper close:
7840a9108ebSHasso Tepper 	rfcomm_dlc_close(dlc, err);
7850a9108ebSHasso Tepper }
7860a9108ebSHasso Tepper 
7870a9108ebSHasso Tepper /*
7880a9108ebSHasso Tepper  * Receive Disconnect Command
7890a9108ebSHasso Tepper  */
7900a9108ebSHasso Tepper static void
rfcomm_session_recv_disc(struct rfcomm_session * rs,int dlci)7910a9108ebSHasso Tepper rfcomm_session_recv_disc(struct rfcomm_session *rs, int dlci)
7920a9108ebSHasso Tepper {
7930a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
7940a9108ebSHasso Tepper 
7950a9108ebSHasso Tepper 	DPRINTFN(5, "DISC(%d)\n", dlci);
7960a9108ebSHasso Tepper 
7970a9108ebSHasso Tepper 	if (dlci == 0) {
7980a9108ebSHasso Tepper 		/*
7990a9108ebSHasso Tepper 		 * Disconnect Session
8000a9108ebSHasso Tepper 		 *
8010a9108ebSHasso Tepper 		 * We set the session state to CLOSED so that when
8020a9108ebSHasso Tepper 		 * the UA frame is clear the session will be closed
8030a9108ebSHasso Tepper 		 * automatically. We wont bother to close any DLC's
8040a9108ebSHasso Tepper 		 * just yet as there should be none. In the unlikely
8050a9108ebSHasso Tepper 		 * event that something is left, it will get flushed
8060a9108ebSHasso Tepper 		 * out as the session goes down.
8070a9108ebSHasso Tepper 		 */
8080a9108ebSHasso Tepper 		rfcomm_session_send_frame(rs, RFCOMM_FRAME_UA, 0);
8090a9108ebSHasso Tepper 		rs->rs_state = RFCOMM_SESSION_CLOSED;
8100a9108ebSHasso Tepper 		return;
8110a9108ebSHasso Tepper 	}
8120a9108ebSHasso Tepper 
8130a9108ebSHasso Tepper 	dlc = rfcomm_dlc_lookup(rs, dlci);
8140a9108ebSHasso Tepper 	if (dlc == NULL) {
8150a9108ebSHasso Tepper 		rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM, dlci);
8160a9108ebSHasso Tepper 		return;
8170a9108ebSHasso Tepper 	}
8180a9108ebSHasso Tepper 
8190a9108ebSHasso Tepper 	rfcomm_dlc_close(dlc, ECONNRESET);
8200a9108ebSHasso Tepper 	rfcomm_session_send_frame(rs, RFCOMM_FRAME_UA, dlci);
8210a9108ebSHasso Tepper }
8220a9108ebSHasso Tepper 
8230a9108ebSHasso Tepper /*
8240a9108ebSHasso Tepper  * Receive Unnumbered Acknowledgement Response
8250a9108ebSHasso Tepper  *
8260a9108ebSHasso Tepper  * This should be a response to a DISC or SABM frame that we
8270a9108ebSHasso Tepper  * have previously sent. If unexpected, ignore it.
8280a9108ebSHasso Tepper  */
8290a9108ebSHasso Tepper static void
rfcomm_session_recv_ua(struct rfcomm_session * rs,int dlci)8300a9108ebSHasso Tepper rfcomm_session_recv_ua(struct rfcomm_session *rs, int dlci)
8310a9108ebSHasso Tepper {
8320a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
8330a9108ebSHasso Tepper 
8340a9108ebSHasso Tepper 	DPRINTFN(5, "UA(%d)\n", dlci);
8350a9108ebSHasso Tepper 
8360a9108ebSHasso Tepper 	if (dlci == 0) {
8370a9108ebSHasso Tepper 		switch (rs->rs_state) {
8380a9108ebSHasso Tepper 		case RFCOMM_SESSION_WAIT_CONNECT:	/* We sent SABM */
8390a9108ebSHasso Tepper 			callout_stop(&rs->rs_timeout);
8400a9108ebSHasso Tepper 			rs->rs_state = RFCOMM_SESSION_OPEN;
8410a9108ebSHasso Tepper 			LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
8420a9108ebSHasso Tepper 				if (dlc->rd_state == RFCOMM_DLC_WAIT_SESSION)
8430a9108ebSHasso Tepper 					rfcomm_dlc_connect(dlc);
8440a9108ebSHasso Tepper 			}
8450a9108ebSHasso Tepper 			break;
8460a9108ebSHasso Tepper 
8470a9108ebSHasso Tepper 		case RFCOMM_SESSION_WAIT_DISCONNECT:	/* We sent DISC */
8480a9108ebSHasso Tepper 			callout_stop(&rs->rs_timeout);
8490a9108ebSHasso Tepper 			rs->rs_state = RFCOMM_SESSION_CLOSED;
8500a9108ebSHasso Tepper 			l2cap_disconnect(rs->rs_l2cap, 0);
8510a9108ebSHasso Tepper 			break;
8520a9108ebSHasso Tepper 
8530a9108ebSHasso Tepper 		default:
8540a9108ebSHasso Tepper 			DPRINTF("Received spurious UA(0)!\n");
8550a9108ebSHasso Tepper 			break;
8560a9108ebSHasso Tepper 		}
8570a9108ebSHasso Tepper 
8580a9108ebSHasso Tepper 		return;
8590a9108ebSHasso Tepper 	}
8600a9108ebSHasso Tepper 
8610a9108ebSHasso Tepper 	/*
8620a9108ebSHasso Tepper 	 * If we have no DLC on this dlci, we may have aborted
8630a9108ebSHasso Tepper 	 * without shutting down properly, so check if the session
8640a9108ebSHasso Tepper 	 * needs disconnecting.
8650a9108ebSHasso Tepper 	 */
8660a9108ebSHasso Tepper 	dlc = rfcomm_dlc_lookup(rs, dlci);
8670a9108ebSHasso Tepper 	if (dlc == NULL)
8680a9108ebSHasso Tepper 		goto check;
8690a9108ebSHasso Tepper 
8700a9108ebSHasso Tepper 	switch (dlc->rd_state) {
8710a9108ebSHasso Tepper 	case RFCOMM_DLC_WAIT_RECV_UA:		/* We sent SABM */
8720a9108ebSHasso Tepper 		rfcomm_dlc_open(dlc);
8730a9108ebSHasso Tepper 		return;
8740a9108ebSHasso Tepper 
8750a9108ebSHasso Tepper 	case RFCOMM_DLC_WAIT_DISCONNECT:	/* We sent DISC */
8760a9108ebSHasso Tepper 		rfcomm_dlc_close(dlc, 0);
8770a9108ebSHasso Tepper 		break;
8780a9108ebSHasso Tepper 
8790a9108ebSHasso Tepper 	default:
8800a9108ebSHasso Tepper 		DPRINTF("Received spurious UA(%d)!\n", dlci);
8810a9108ebSHasso Tepper 		return;
8820a9108ebSHasso Tepper 	}
8830a9108ebSHasso Tepper 
8840a9108ebSHasso Tepper check:	/* last one out turns out the light */
8850a9108ebSHasso Tepper 	if (LIST_EMPTY(&rs->rs_dlcs)) {
8860a9108ebSHasso Tepper 		rs->rs_state = RFCOMM_SESSION_WAIT_DISCONNECT;
8870a9108ebSHasso Tepper 		rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC, 0);
8880a9108ebSHasso Tepper 		callout_reset(&rs->rs_timeout, rfcomm_ack_timeout*hz,rfcomm_session_timeout,rs);
8890a9108ebSHasso Tepper 	}
8900a9108ebSHasso Tepper }
8910a9108ebSHasso Tepper 
8920a9108ebSHasso Tepper /*
8930a9108ebSHasso Tepper  * Receive Disconnected Mode Response
8940a9108ebSHasso Tepper  *
8950a9108ebSHasso Tepper  * If this does not apply to a known DLC then we may ignore it.
8960a9108ebSHasso Tepper  */
8970a9108ebSHasso Tepper static void
rfcomm_session_recv_dm(struct rfcomm_session * rs,int dlci)8980a9108ebSHasso Tepper rfcomm_session_recv_dm(struct rfcomm_session *rs, int dlci)
8990a9108ebSHasso Tepper {
9000a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
9010a9108ebSHasso Tepper 
9020a9108ebSHasso Tepper 	DPRINTFN(5, "DM(%d)\n", dlci);
9030a9108ebSHasso Tepper 
9040a9108ebSHasso Tepper 	dlc = rfcomm_dlc_lookup(rs, dlci);
9050a9108ebSHasso Tepper 	if (dlc == NULL)
9060a9108ebSHasso Tepper 		return;
9070a9108ebSHasso Tepper 
9080a9108ebSHasso Tepper 	if (dlc->rd_state == RFCOMM_DLC_WAIT_CONNECT)
9090a9108ebSHasso Tepper 		rfcomm_dlc_close(dlc, ECONNREFUSED);
9100a9108ebSHasso Tepper 	else
9110a9108ebSHasso Tepper 		rfcomm_dlc_close(dlc, ECONNRESET);
9120a9108ebSHasso Tepper }
9130a9108ebSHasso Tepper 
9140a9108ebSHasso Tepper /*
9150a9108ebSHasso Tepper  * Receive Unnumbered Information with Header check (MCC or data packet)
9160a9108ebSHasso Tepper  */
9170a9108ebSHasso Tepper static void
rfcomm_session_recv_uih(struct rfcomm_session * rs,int dlci,int pf,struct mbuf * m,int len)9180a9108ebSHasso Tepper rfcomm_session_recv_uih(struct rfcomm_session *rs, int dlci,
9190a9108ebSHasso Tepper 			int pf, struct mbuf *m, int len)
9200a9108ebSHasso Tepper {
9210a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
9220a9108ebSHasso Tepper 	uint8_t credits = 0;
9230a9108ebSHasso Tepper 
9240a9108ebSHasso Tepper 	DPRINTFN(10, "UIH(%d)\n", dlci);
9250a9108ebSHasso Tepper 
9260a9108ebSHasso Tepper 	if (dlci == 0) {
9270a9108ebSHasso Tepper 		rfcomm_session_recv_mcc(rs, m);
9280a9108ebSHasso Tepper 		return;
9290a9108ebSHasso Tepper 	}
9300a9108ebSHasso Tepper 
9310a9108ebSHasso Tepper 	if (m->m_pkthdr.len != len + pf) {
9320a9108ebSHasso Tepper 		DPRINTF("Bad Frame Length (%d), frame discarded\n",
9330a9108ebSHasso Tepper 			    m->m_pkthdr.len);
9340a9108ebSHasso Tepper 
9350a9108ebSHasso Tepper 		goto discard;
9360a9108ebSHasso Tepper 	}
9370a9108ebSHasso Tepper 
9380a9108ebSHasso Tepper 	dlc = rfcomm_dlc_lookup(rs, dlci);
9390a9108ebSHasso Tepper 	if (dlc == NULL) {
9400a9108ebSHasso Tepper 		DPRINTF("UIH received for non existent DLC, discarded\n");
9410a9108ebSHasso Tepper 		rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM, dlci);
9420a9108ebSHasso Tepper 		goto discard;
9430a9108ebSHasso Tepper 	}
9440a9108ebSHasso Tepper 
9450a9108ebSHasso Tepper 	if (dlc->rd_state != RFCOMM_DLC_OPEN) {
9460a9108ebSHasso Tepper 		DPRINTF("non-open DLC (state = %d), discarded\n",
9470a9108ebSHasso Tepper 				dlc->rd_state);
9480a9108ebSHasso Tepper 		goto discard;
9490a9108ebSHasso Tepper 	}
9500a9108ebSHasso Tepper 
9510a9108ebSHasso Tepper 	/* if PF is set, credits were included */
9520a9108ebSHasso Tepper 	if (rs->rs_flags & RFCOMM_SESSION_CFC) {
9530a9108ebSHasso Tepper 		if (pf != 0) {
9540a9108ebSHasso Tepper 			if (m->m_pkthdr.len < sizeof(credits)) {
9550a9108ebSHasso Tepper 				DPRINTF("Bad PF value, UIH discarded\n");
9560a9108ebSHasso Tepper 				goto discard;
9570a9108ebSHasso Tepper 			}
9580a9108ebSHasso Tepper 
9590a9108ebSHasso Tepper 			m_copydata(m, 0, sizeof(credits), &credits);
9600a9108ebSHasso Tepper 			m_adj(m, sizeof(credits));
9610a9108ebSHasso Tepper 
9620a9108ebSHasso Tepper 			dlc->rd_txcred += credits;
9630a9108ebSHasso Tepper 
9640a9108ebSHasso Tepper 			if (credits > 0 && dlc->rd_txbuf != NULL)
9650a9108ebSHasso Tepper 				rfcomm_dlc_start(dlc);
9660a9108ebSHasso Tepper 		}
9670a9108ebSHasso Tepper 
9680a9108ebSHasso Tepper 		if (len == 0)
9690a9108ebSHasso Tepper 			goto discard;
9700a9108ebSHasso Tepper 
9710a9108ebSHasso Tepper 		if (dlc->rd_rxcred == 0) {
9720a9108ebSHasso Tepper 			DPRINTF("Credit limit reached, UIH discarded\n");
9730a9108ebSHasso Tepper 			goto discard;
9740a9108ebSHasso Tepper 		}
9750a9108ebSHasso Tepper 
9760a9108ebSHasso Tepper 		if (len > dlc->rd_rxsize) {
9770a9108ebSHasso Tepper 			DPRINTF("UIH frame exceeds rxsize, discarded\n");
9780a9108ebSHasso Tepper 			goto discard;
9790a9108ebSHasso Tepper 		}
9800a9108ebSHasso Tepper 
9810a9108ebSHasso Tepper 		dlc->rd_rxcred--;
9820a9108ebSHasso Tepper 		dlc->rd_rxsize -= len;
9830a9108ebSHasso Tepper 	}
9840a9108ebSHasso Tepper 
9850a9108ebSHasso Tepper 	(*dlc->rd_proto->input)(dlc->rd_upper, m);
9860a9108ebSHasso Tepper 	return;
9870a9108ebSHasso Tepper 
9880a9108ebSHasso Tepper discard:
9890a9108ebSHasso Tepper 	m_freem(m);
9900a9108ebSHasso Tepper }
9910a9108ebSHasso Tepper 
9920a9108ebSHasso Tepper /*
9930a9108ebSHasso Tepper  * Receive Multiplexer Control Command
9940a9108ebSHasso Tepper  */
9950a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc(struct rfcomm_session * rs,struct mbuf * m)9960a9108ebSHasso Tepper rfcomm_session_recv_mcc(struct rfcomm_session *rs, struct mbuf *m)
9970a9108ebSHasso Tepper {
9980a9108ebSHasso Tepper 	int type, cr, len;
9990a9108ebSHasso Tepper 	uint8_t b;
10000a9108ebSHasso Tepper 
10010a9108ebSHasso Tepper 	/*
10020a9108ebSHasso Tepper 	 * Extract MCC header.
10030a9108ebSHasso Tepper 	 *
10040a9108ebSHasso Tepper 	 * Fields are variable length using extension bit = 1 to signify the
10050a9108ebSHasso Tepper 	 * last octet in the sequence.
10060a9108ebSHasso Tepper 	 *
10070a9108ebSHasso Tepper 	 * Only single octet types are defined in TS 07.10/RFCOMM spec
10080a9108ebSHasso Tepper 	 *
10090a9108ebSHasso Tepper 	 * Length can realistically only use 15 bits (max RFCOMM MTU)
10100a9108ebSHasso Tepper 	 */
10110a9108ebSHasso Tepper 	if (m->m_pkthdr.len < sizeof(b)) {
10120a9108ebSHasso Tepper 		DPRINTF("Short MCC header, discarded\n");
10130a9108ebSHasso Tepper 		goto release;
10140a9108ebSHasso Tepper 	}
10150a9108ebSHasso Tepper 
10160a9108ebSHasso Tepper 	m_copydata(m, 0, sizeof(b), &b);
10170a9108ebSHasso Tepper 	m_adj(m, sizeof(b));
10180a9108ebSHasso Tepper 
10190a9108ebSHasso Tepper 	if (RFCOMM_EA(b) == 0) {	/* verify no extensions */
10200a9108ebSHasso Tepper 		DPRINTF("MCC type EA = 0, discarded\n");
10210a9108ebSHasso Tepper 		goto release;
10220a9108ebSHasso Tepper 	}
10230a9108ebSHasso Tepper 
10240a9108ebSHasso Tepper 	type = RFCOMM_MCC_TYPE(b);
10250a9108ebSHasso Tepper 	cr = RFCOMM_CR(b);
10260a9108ebSHasso Tepper 
10270a9108ebSHasso Tepper 	len = 0;
10280a9108ebSHasso Tepper 	do {
10290a9108ebSHasso Tepper 		if (m->m_pkthdr.len < sizeof(b)) {
10300a9108ebSHasso Tepper 			DPRINTF("Short MCC header, discarded\n");
10310a9108ebSHasso Tepper 			goto release;
10320a9108ebSHasso Tepper 		}
10330a9108ebSHasso Tepper 
10340a9108ebSHasso Tepper 		m_copydata(m, 0, sizeof(b), &b);
10350a9108ebSHasso Tepper 		m_adj(m, sizeof(b));
10360a9108ebSHasso Tepper 
10370a9108ebSHasso Tepper 		len = (len << 7) | (b >> 1);
10380a9108ebSHasso Tepper 		len = min(len, RFCOMM_MTU_MAX);
10390a9108ebSHasso Tepper 	} while (RFCOMM_EA(b) == 0);
10400a9108ebSHasso Tepper 
10410a9108ebSHasso Tepper 	if (len != m->m_pkthdr.len) {
10420a9108ebSHasso Tepper 		DPRINTF("Incorrect MCC length, discarded\n");
10430a9108ebSHasso Tepper 		goto release;
10440a9108ebSHasso Tepper 	}
10450a9108ebSHasso Tepper 
10460a9108ebSHasso Tepper 	DPRINTFN(2, "MCC %s type %2.2x (%d bytes)\n",
10470a9108ebSHasso Tepper 		(cr ? "command" : "response"), type, len);
10480a9108ebSHasso Tepper 
10490a9108ebSHasso Tepper 	/*
10500a9108ebSHasso Tepper 	 * pass to command handler
10510a9108ebSHasso Tepper 	 */
10520a9108ebSHasso Tepper 	switch(type) {
10530a9108ebSHasso Tepper 	case RFCOMM_MCC_TEST:	/* Test */
10540a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_test(rs, cr, m);
10550a9108ebSHasso Tepper 		break;
10560a9108ebSHasso Tepper 
10570a9108ebSHasso Tepper 	case RFCOMM_MCC_FCON:	/* Flow Control On */
10580a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_fcon(rs, cr);
10590a9108ebSHasso Tepper 		break;
10600a9108ebSHasso Tepper 
10610a9108ebSHasso Tepper 	case RFCOMM_MCC_FCOFF:	/* Flow Control Off */
10620a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_fcoff(rs, cr);
10630a9108ebSHasso Tepper 		break;
10640a9108ebSHasso Tepper 
10650a9108ebSHasso Tepper 	case RFCOMM_MCC_MSC:	/* Modem Status Command */
10660a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_msc(rs, cr, m);
10670a9108ebSHasso Tepper 		break;
10680a9108ebSHasso Tepper 
10690a9108ebSHasso Tepper 	case RFCOMM_MCC_RPN:	/* Remote Port Negotiation */
10700a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_rpn(rs, cr, m);
10710a9108ebSHasso Tepper 		break;
10720a9108ebSHasso Tepper 
10730a9108ebSHasso Tepper 	case RFCOMM_MCC_RLS:	/* Remote Line Status */
10740a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_rls(rs, cr, m);
10750a9108ebSHasso Tepper 		break;
10760a9108ebSHasso Tepper 
10770a9108ebSHasso Tepper 	case RFCOMM_MCC_PN:	/* Parameter Negotiation */
10780a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_pn(rs, cr, m);
10790a9108ebSHasso Tepper 		break;
10800a9108ebSHasso Tepper 
10810a9108ebSHasso Tepper 	case RFCOMM_MCC_NSC:	/* Non Supported Command */
10820a9108ebSHasso Tepper 		rfcomm_session_recv_mcc_nsc(rs, cr, m);
10830a9108ebSHasso Tepper 		break;
10840a9108ebSHasso Tepper 
10850a9108ebSHasso Tepper 	default:
10860a9108ebSHasso Tepper 		b = RFCOMM_MKMCC_TYPE(cr, type);
10870a9108ebSHasso Tepper 		rfcomm_session_send_mcc(rs, 0, RFCOMM_MCC_NSC, &b, sizeof(b));
10880a9108ebSHasso Tepper 	}
10890a9108ebSHasso Tepper 
10900a9108ebSHasso Tepper release:
10910a9108ebSHasso Tepper 	m_freem(m);
10920a9108ebSHasso Tepper }
10930a9108ebSHasso Tepper 
10940a9108ebSHasso Tepper /*
10950a9108ebSHasso Tepper  * process TEST command/response
10960a9108ebSHasso Tepper  */
10970a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_test(struct rfcomm_session * rs,int cr,struct mbuf * m)10980a9108ebSHasso Tepper rfcomm_session_recv_mcc_test(struct rfcomm_session *rs, int cr, struct mbuf *m)
10990a9108ebSHasso Tepper {
11000a9108ebSHasso Tepper 	void *data;
11010a9108ebSHasso Tepper 	int len;
11020a9108ebSHasso Tepper 
11030a9108ebSHasso Tepper 	if (cr == 0)	/* ignore ack */
11040a9108ebSHasso Tepper 		return;
11050a9108ebSHasso Tepper 
11060a9108ebSHasso Tepper 	/*
11070a9108ebSHasso Tepper 	 * we must send all the data they included back as is
11080a9108ebSHasso Tepper 	 */
11090a9108ebSHasso Tepper 
11100a9108ebSHasso Tepper 	len = m->m_pkthdr.len;
11110a9108ebSHasso Tepper 	if (len > RFCOMM_MTU_MAX)
11120a9108ebSHasso Tepper 		return;
11130a9108ebSHasso Tepper 
11140a9108ebSHasso Tepper 	data = kmalloc(len, M_BLUETOOTH, M_NOWAIT);
11150a9108ebSHasso Tepper 	if (data == NULL)
11160a9108ebSHasso Tepper 		return;
11170a9108ebSHasso Tepper 
11180a9108ebSHasso Tepper 	m_copydata(m, 0, len, data);
11190a9108ebSHasso Tepper 	rfcomm_session_send_mcc(rs, 0, RFCOMM_MCC_TEST, data, len);
11200a9108ebSHasso Tepper 	kfree(data, M_BLUETOOTH);
11210a9108ebSHasso Tepper }
11220a9108ebSHasso Tepper 
11230a9108ebSHasso Tepper /*
11240a9108ebSHasso Tepper  * process Flow Control ON command/response
11250a9108ebSHasso Tepper  */
11260a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_fcon(struct rfcomm_session * rs,int cr)11270a9108ebSHasso Tepper rfcomm_session_recv_mcc_fcon(struct rfcomm_session *rs, int cr)
11280a9108ebSHasso Tepper {
11290a9108ebSHasso Tepper 
11300a9108ebSHasso Tepper 	if (cr == 0)	/* ignore ack */
11310a9108ebSHasso Tepper 		return;
11320a9108ebSHasso Tepper 
11330a9108ebSHasso Tepper 	rs->rs_flags |= RFCOMM_SESSION_RFC;
11340a9108ebSHasso Tepper 	rfcomm_session_send_mcc(rs, 0, RFCOMM_MCC_FCON, NULL, 0);
11350a9108ebSHasso Tepper }
11360a9108ebSHasso Tepper 
11370a9108ebSHasso Tepper /*
11380a9108ebSHasso Tepper  * process Flow Control OFF command/response
11390a9108ebSHasso Tepper  */
11400a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_fcoff(struct rfcomm_session * rs,int cr)11410a9108ebSHasso Tepper rfcomm_session_recv_mcc_fcoff(struct rfcomm_session *rs, int cr)
11420a9108ebSHasso Tepper {
11430a9108ebSHasso Tepper 	if (cr == 0)	/* ignore ack */
11440a9108ebSHasso Tepper 		return;
11450a9108ebSHasso Tepper 
11460a9108ebSHasso Tepper 	rs->rs_flags &= ~RFCOMM_SESSION_RFC;
11470a9108ebSHasso Tepper 	rfcomm_session_send_mcc(rs, 0, RFCOMM_MCC_FCOFF, NULL, 0);
11480a9108ebSHasso Tepper }
11490a9108ebSHasso Tepper 
11500a9108ebSHasso Tepper /*
11510a9108ebSHasso Tepper  * process Modem Status Command command/response
11520a9108ebSHasso Tepper  */
11530a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_msc(struct rfcomm_session * rs,int cr,struct mbuf * m)11540a9108ebSHasso Tepper rfcomm_session_recv_mcc_msc(struct rfcomm_session *rs, int cr, struct mbuf *m)
11550a9108ebSHasso Tepper {
11560a9108ebSHasso Tepper 	struct rfcomm_mcc_msc msc;	/* (3 octets) */
11570a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
11580a9108ebSHasso Tepper 	int len = 0;
11590a9108ebSHasso Tepper 
11600a9108ebSHasso Tepper 	/* [ADDRESS] */
11610a9108ebSHasso Tepper 	if (m->m_pkthdr.len < sizeof(msc.address))
11620a9108ebSHasso Tepper 		return;
11630a9108ebSHasso Tepper 
11640a9108ebSHasso Tepper 	m_copydata(m, 0, sizeof(msc.address), &msc.address);
11650a9108ebSHasso Tepper 	m_adj(m, sizeof(msc.address));
11660a9108ebSHasso Tepper 	len += sizeof(msc.address);
11670a9108ebSHasso Tepper 
11680a9108ebSHasso Tepper 	dlc = rfcomm_dlc_lookup(rs, RFCOMM_DLCI(msc.address));
11690a9108ebSHasso Tepper 
11700a9108ebSHasso Tepper 	if (cr == 0) {	/* ignore acks */
11710a9108ebSHasso Tepper 		if (dlc != NULL)
11720a9108ebSHasso Tepper 			callout_stop(&dlc->rd_timeout);
11730a9108ebSHasso Tepper 
11740a9108ebSHasso Tepper 		return;
11750a9108ebSHasso Tepper 	}
11760a9108ebSHasso Tepper 
11770a9108ebSHasso Tepper 	if (dlc == NULL) {
11780a9108ebSHasso Tepper 		rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM,
11790a9108ebSHasso Tepper 						RFCOMM_DLCI(msc.address));
11800a9108ebSHasso Tepper 		return;
11810a9108ebSHasso Tepper 	}
11820a9108ebSHasso Tepper 
11830a9108ebSHasso Tepper 	/* [SIGNALS] */
11840a9108ebSHasso Tepper 	if (m->m_pkthdr.len < sizeof(msc.modem))
11850a9108ebSHasso Tepper 		return;
11860a9108ebSHasso Tepper 
11870a9108ebSHasso Tepper 	m_copydata(m, 0, sizeof(msc.modem), &msc.modem);
11880a9108ebSHasso Tepper 	m_adj(m, sizeof(msc.modem));
11890a9108ebSHasso Tepper 	len += sizeof(msc.modem);
11900a9108ebSHasso Tepper 
11910a9108ebSHasso Tepper 	dlc->rd_rmodem = msc.modem;
11920a9108ebSHasso Tepper 	/* XXX how do we signal this upstream? */
11930a9108ebSHasso Tepper 
11940a9108ebSHasso Tepper 	if (RFCOMM_EA(msc.modem) == 0) {
11950a9108ebSHasso Tepper 		if (m->m_pkthdr.len < sizeof(msc.brk))
11960a9108ebSHasso Tepper 			return;
11970a9108ebSHasso Tepper 
11980a9108ebSHasso Tepper 		m_copydata(m, 0, sizeof(msc.brk), &msc.brk);
11990a9108ebSHasso Tepper 		m_adj(m, sizeof(msc.brk));
12000a9108ebSHasso Tepper 		len += sizeof(msc.brk);
12010a9108ebSHasso Tepper 
12020a9108ebSHasso Tepper 		/* XXX how do we signal this upstream? */
12030a9108ebSHasso Tepper 	}
12040a9108ebSHasso Tepper 
12050a9108ebSHasso Tepper 	rfcomm_session_send_mcc(rs, 0, RFCOMM_MCC_MSC, &msc, len);
12060a9108ebSHasso Tepper }
12070a9108ebSHasso Tepper 
12080a9108ebSHasso Tepper /*
12090a9108ebSHasso Tepper  * process Remote Port Negotiation command/response
12100a9108ebSHasso Tepper  */
12110a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_rpn(struct rfcomm_session * rs,int cr,struct mbuf * m)12120a9108ebSHasso Tepper rfcomm_session_recv_mcc_rpn(struct rfcomm_session *rs, int cr, struct mbuf *m)
12130a9108ebSHasso Tepper {
12140a9108ebSHasso Tepper 	struct rfcomm_mcc_rpn rpn;
12150a9108ebSHasso Tepper 	uint16_t mask;
12160a9108ebSHasso Tepper 
12170a9108ebSHasso Tepper 	if (cr == 0)	/* ignore ack */
12180a9108ebSHasso Tepper 		return;
12190a9108ebSHasso Tepper 
12200a9108ebSHasso Tepper 	/* default values */
12210a9108ebSHasso Tepper 	rpn.bit_rate = RFCOMM_RPN_BR_9600;
12220a9108ebSHasso Tepper 	rpn.line_settings = RFCOMM_RPN_8_N_1;
12230a9108ebSHasso Tepper 	rpn.flow_control = RFCOMM_RPN_FLOW_NONE;
12240a9108ebSHasso Tepper 	rpn.xon_char = RFCOMM_RPN_XON_CHAR;
12250a9108ebSHasso Tepper 	rpn.xoff_char = RFCOMM_RPN_XOFF_CHAR;
12260a9108ebSHasso Tepper 
12270a9108ebSHasso Tepper 	if (m->m_pkthdr.len == sizeof(rpn)) {
122805d02a38SAaron LI 		m_copydata(m, 0, sizeof(rpn), &rpn);
12290a9108ebSHasso Tepper 		rpn.param_mask = RFCOMM_RPN_PM_ALL;
12300a9108ebSHasso Tepper 	} else if (m->m_pkthdr.len == 1) {
123105d02a38SAaron LI 		m_copydata(m, 0, 1, &rpn);
12320a9108ebSHasso Tepper 		rpn.param_mask = letoh16(rpn.param_mask);
12330a9108ebSHasso Tepper 	} else {
12340a9108ebSHasso Tepper 		DPRINTF("Bad RPN length (%d)\n", m->m_pkthdr.len);
12350a9108ebSHasso Tepper 		return;
12360a9108ebSHasso Tepper 	}
12370a9108ebSHasso Tepper 
12380a9108ebSHasso Tepper 	mask = 0;
12390a9108ebSHasso Tepper 
12400a9108ebSHasso Tepper 	if (rpn.param_mask & RFCOMM_RPN_PM_RATE)
12410a9108ebSHasso Tepper 		mask |= RFCOMM_RPN_PM_RATE;
12420a9108ebSHasso Tepper 
12430a9108ebSHasso Tepper 	if (rpn.param_mask & RFCOMM_RPN_PM_DATA
12440a9108ebSHasso Tepper 	    && RFCOMM_RPN_DATA_BITS(rpn.line_settings) == RFCOMM_RPN_DATA_8)
12450a9108ebSHasso Tepper 		mask |= RFCOMM_RPN_PM_DATA;
12460a9108ebSHasso Tepper 
12470a9108ebSHasso Tepper 	if (rpn.param_mask & RFCOMM_RPN_PM_STOP
12480a9108ebSHasso Tepper 	    && RFCOMM_RPN_STOP_BITS(rpn.line_settings) == RFCOMM_RPN_STOP_1)
12490a9108ebSHasso Tepper 		mask |= RFCOMM_RPN_PM_STOP;
12500a9108ebSHasso Tepper 
12510a9108ebSHasso Tepper 	if (rpn.param_mask & RFCOMM_RPN_PM_PARITY
12520a9108ebSHasso Tepper 	    && RFCOMM_RPN_PARITY(rpn.line_settings) == RFCOMM_RPN_PARITY_NONE)
12530a9108ebSHasso Tepper 		mask |= RFCOMM_RPN_PM_PARITY;
12540a9108ebSHasso Tepper 
12550a9108ebSHasso Tepper 	if (rpn.param_mask & RFCOMM_RPN_PM_XON
12560a9108ebSHasso Tepper 	    && rpn.xon_char == RFCOMM_RPN_XON_CHAR)
12570a9108ebSHasso Tepper 		mask |= RFCOMM_RPN_PM_XON;
12580a9108ebSHasso Tepper 
12590a9108ebSHasso Tepper 	if (rpn.param_mask & RFCOMM_RPN_PM_XOFF
12600a9108ebSHasso Tepper 	    && rpn.xoff_char == RFCOMM_RPN_XOFF_CHAR)
12610a9108ebSHasso Tepper 		mask |= RFCOMM_RPN_PM_XOFF;
12620a9108ebSHasso Tepper 
12630a9108ebSHasso Tepper 	if (rpn.param_mask & RFCOMM_RPN_PM_FLOW
12640a9108ebSHasso Tepper 	    && rpn.flow_control == RFCOMM_RPN_FLOW_NONE)
12650a9108ebSHasso Tepper 		mask |= RFCOMM_RPN_PM_FLOW;
12660a9108ebSHasso Tepper 
12670a9108ebSHasso Tepper 	rpn.param_mask = htole16(mask);
12680a9108ebSHasso Tepper 
12690a9108ebSHasso Tepper 	rfcomm_session_send_mcc(rs, 0, RFCOMM_MCC_RPN, &rpn, sizeof(rpn));
12700a9108ebSHasso Tepper }
12710a9108ebSHasso Tepper 
12720a9108ebSHasso Tepper /*
12730a9108ebSHasso Tepper  * process Remote Line Status command/response
12740a9108ebSHasso Tepper  */
12750a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_rls(struct rfcomm_session * rs,int cr,struct mbuf * m)12760a9108ebSHasso Tepper rfcomm_session_recv_mcc_rls(struct rfcomm_session *rs, int cr, struct mbuf *m)
12770a9108ebSHasso Tepper {
12780a9108ebSHasso Tepper 	struct rfcomm_mcc_rls rls;
12790a9108ebSHasso Tepper 
12800a9108ebSHasso Tepper 	if (cr == 0)	/* ignore ack */
12810a9108ebSHasso Tepper 		return;
12820a9108ebSHasso Tepper 
12830a9108ebSHasso Tepper 	if (m->m_pkthdr.len != sizeof(rls)) {
12840a9108ebSHasso Tepper 		DPRINTF("Bad RLS length %d\n", m->m_pkthdr.len);
12850a9108ebSHasso Tepper 		return;
12860a9108ebSHasso Tepper 	}
12870a9108ebSHasso Tepper 
128805d02a38SAaron LI 	m_copydata(m, 0, sizeof(rls), &rls);
12890a9108ebSHasso Tepper 
12900a9108ebSHasso Tepper 	/*
12910a9108ebSHasso Tepper 	 * So far as I can tell, we just send back what
12920a9108ebSHasso Tepper 	 * they sent us. This signifies errors that seem
12930a9108ebSHasso Tepper 	 * irrelevent for RFCOMM over L2CAP.
12940a9108ebSHasso Tepper 	 */
12950a9108ebSHasso Tepper 	rls.address |= 0x03;	/* EA = 1, CR = 1 */
12960a9108ebSHasso Tepper 	rls.status &= 0x0f;	/* only 4 bits valid */
12970a9108ebSHasso Tepper 
12980a9108ebSHasso Tepper 	rfcomm_session_send_mcc(rs, 0, RFCOMM_MCC_RLS, &rls, sizeof(rls));
12990a9108ebSHasso Tepper }
13000a9108ebSHasso Tepper 
13010a9108ebSHasso Tepper /*
13020a9108ebSHasso Tepper  * process Parameter Negotiation command/response
13030a9108ebSHasso Tepper  */
13040a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_pn(struct rfcomm_session * rs,int cr,struct mbuf * m)13050a9108ebSHasso Tepper rfcomm_session_recv_mcc_pn(struct rfcomm_session *rs, int cr, struct mbuf *m)
13060a9108ebSHasso Tepper {
13070a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
13080a9108ebSHasso Tepper 	struct rfcomm_mcc_pn pn;
13090a9108ebSHasso Tepper 	int err;
13100a9108ebSHasso Tepper 
13110a9108ebSHasso Tepper 	if (m->m_pkthdr.len != sizeof(pn)) {
13120a9108ebSHasso Tepper 		DPRINTF("Bad PN length %d\n", m->m_pkthdr.len);
13130a9108ebSHasso Tepper 		return;
13140a9108ebSHasso Tepper 	}
13150a9108ebSHasso Tepper 
131605d02a38SAaron LI 	m_copydata(m, 0, sizeof(pn), &pn);
13170a9108ebSHasso Tepper 
13180a9108ebSHasso Tepper 	pn.dlci &= 0x3f;
13190a9108ebSHasso Tepper 	pn.mtu = letoh16(pn.mtu);
13200a9108ebSHasso Tepper 
13210a9108ebSHasso Tepper 	dlc = rfcomm_dlc_lookup(rs, pn.dlci);
13220a9108ebSHasso Tepper 	if (cr) {	/* Command */
13230a9108ebSHasso Tepper 		/*
13240a9108ebSHasso Tepper 		 * If there is no DLC present, this is a new
13250a9108ebSHasso Tepper 		 * connection so attempt to make one
13260a9108ebSHasso Tepper 		 */
13270a9108ebSHasso Tepper 		if (dlc == NULL) {
13280a9108ebSHasso Tepper 			dlc = rfcomm_dlc_newconn(rs, pn.dlci);
13290a9108ebSHasso Tepper 			if (dlc == NULL)
13300a9108ebSHasso Tepper 				return;	/* (DM is sent) */
13310a9108ebSHasso Tepper 		}
13320a9108ebSHasso Tepper 
13330a9108ebSHasso Tepper 		/* accept any valid MTU, and offer it back */
13340a9108ebSHasso Tepper 		pn.mtu = min(pn.mtu, RFCOMM_MTU_MAX);
13350a9108ebSHasso Tepper 		pn.mtu = min(pn.mtu, rs->rs_mtu);
13360a9108ebSHasso Tepper 		pn.mtu = max(pn.mtu, RFCOMM_MTU_MIN);
13370a9108ebSHasso Tepper 		dlc->rd_mtu = pn.mtu;
13380a9108ebSHasso Tepper 		pn.mtu = htole16(pn.mtu);
13390a9108ebSHasso Tepper 
13400a9108ebSHasso Tepper 		/* credits are only set before DLC is open */
13410a9108ebSHasso Tepper 		if (dlc->rd_state == RFCOMM_DLC_WAIT_CONNECT
13420a9108ebSHasso Tepper 		    && (pn.flow_control & 0xf0) == 0xf0) {
13430a9108ebSHasso Tepper 			rs->rs_flags |= RFCOMM_SESSION_CFC;
13440a9108ebSHasso Tepper 			dlc->rd_txcred = pn.credits & 0x07;
13450a9108ebSHasso Tepper 
13460a9108ebSHasso Tepper 			dlc->rd_rxcred = (dlc->rd_rxsize / dlc->rd_mtu);
13470a9108ebSHasso Tepper 			dlc->rd_rxcred = min(dlc->rd_rxcred,
13480a9108ebSHasso Tepper 						RFCOMM_CREDITS_DEFAULT);
13490a9108ebSHasso Tepper 
13500a9108ebSHasso Tepper 			pn.flow_control = 0xe0;
13510a9108ebSHasso Tepper 			pn.credits = dlc->rd_rxcred;
13520a9108ebSHasso Tepper 		} else {
13530a9108ebSHasso Tepper 			pn.flow_control = 0x00;
13540a9108ebSHasso Tepper 			pn.credits = 0x00;
13550a9108ebSHasso Tepper 		}
13560a9108ebSHasso Tepper 
13570a9108ebSHasso Tepper 		/* unused fields must be ignored and set to zero */
13580a9108ebSHasso Tepper 		pn.ack_timer = 0;
13590a9108ebSHasso Tepper 		pn.max_retrans = 0;
13600a9108ebSHasso Tepper 
13610a9108ebSHasso Tepper 		/* send our response */
13620a9108ebSHasso Tepper 		err = rfcomm_session_send_mcc(rs, 0,
13630a9108ebSHasso Tepper 					RFCOMM_MCC_PN, &pn, sizeof(pn));
13640a9108ebSHasso Tepper 		if (err)
13650a9108ebSHasso Tepper 			goto close;
13660a9108ebSHasso Tepper 
13670a9108ebSHasso Tepper 	} else {	/* Response */
13680a9108ebSHasso Tepper 		/* ignore responses with no matching DLC */
13690a9108ebSHasso Tepper 		if (dlc == NULL)
13700a9108ebSHasso Tepper 			return;
13710a9108ebSHasso Tepper 
13720a9108ebSHasso Tepper 		callout_stop(&dlc->rd_timeout);
13730a9108ebSHasso Tepper 
13740a9108ebSHasso Tepper 		if (pn.mtu > RFCOMM_MTU_MAX || pn.mtu > dlc->rd_mtu) {
13750a9108ebSHasso Tepper 			dlc->rd_state = RFCOMM_DLC_WAIT_DISCONNECT;
13760a9108ebSHasso Tepper 			err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_DISC,
13770a9108ebSHasso Tepper 							pn.dlci);
13780a9108ebSHasso Tepper 			if (err)
13790a9108ebSHasso Tepper 				goto close;
13800a9108ebSHasso Tepper 
13810a9108ebSHasso Tepper 			callout_reset(&dlc->rd_timeout, rfcomm_ack_timeout * hz,
13820a9108ebSHasso Tepper 				    rfcomm_dlc_timeout, dlc);
13830a9108ebSHasso Tepper 			return;
13840a9108ebSHasso Tepper 		}
13850a9108ebSHasso Tepper 		dlc->rd_mtu = pn.mtu;
13860a9108ebSHasso Tepper 
13870a9108ebSHasso Tepper 		/* if DLC is not waiting to connect, we are done */
13880a9108ebSHasso Tepper 		if (dlc->rd_state != RFCOMM_DLC_WAIT_CONNECT)
13890a9108ebSHasso Tepper 			return;
13900a9108ebSHasso Tepper 
13910a9108ebSHasso Tepper 		/* set initial credits according to RFCOMM spec */
13920a9108ebSHasso Tepper 		if ((pn.flow_control & 0xf0) == 0xe0) {
13930a9108ebSHasso Tepper 			rs->rs_flags |= RFCOMM_SESSION_CFC;
13940a9108ebSHasso Tepper 			dlc->rd_txcred = (pn.credits & 0x07);
13950a9108ebSHasso Tepper 		}
13960a9108ebSHasso Tepper 
13970a9108ebSHasso Tepper 		callout_reset(&dlc->rd_timeout, rfcomm_ack_timeout * hz,
13980a9108ebSHasso Tepper 				    rfcomm_dlc_timeout, dlc);
13990a9108ebSHasso Tepper 
14000a9108ebSHasso Tepper 		/* set link mode */
14010a9108ebSHasso Tepper 		err = rfcomm_dlc_setmode(dlc);
14020a9108ebSHasso Tepper 		if (err == EINPROGRESS) {
14030a9108ebSHasso Tepper 			dlc->rd_state = RFCOMM_DLC_WAIT_SEND_SABM;
14040a9108ebSHasso Tepper 			(*dlc->rd_proto->connecting)(dlc->rd_upper);
14050a9108ebSHasso Tepper 			return;
14060a9108ebSHasso Tepper 		}
14070a9108ebSHasso Tepper 		if (err)
14080a9108ebSHasso Tepper 			goto close;
14090a9108ebSHasso Tepper 
14100a9108ebSHasso Tepper 		/* we can proceed now */
14110a9108ebSHasso Tepper 		err = rfcomm_session_send_frame(rs, RFCOMM_FRAME_SABM, pn.dlci);
14120a9108ebSHasso Tepper 		if (err)
14130a9108ebSHasso Tepper 			goto close;
14140a9108ebSHasso Tepper 
14150a9108ebSHasso Tepper 		dlc->rd_state = RFCOMM_DLC_WAIT_RECV_UA;
14160a9108ebSHasso Tepper 	}
14170a9108ebSHasso Tepper 	return;
14180a9108ebSHasso Tepper 
14190a9108ebSHasso Tepper close:
14200a9108ebSHasso Tepper 	rfcomm_dlc_close(dlc, err);
14210a9108ebSHasso Tepper }
14220a9108ebSHasso Tepper 
14230a9108ebSHasso Tepper /*
14240a9108ebSHasso Tepper  * process Non Supported Command command/response
14250a9108ebSHasso Tepper  */
14260a9108ebSHasso Tepper static void
rfcomm_session_recv_mcc_nsc(struct rfcomm_session * rs,int cr,struct mbuf * m)14270a9108ebSHasso Tepper rfcomm_session_recv_mcc_nsc(struct rfcomm_session *rs,
14280a9108ebSHasso Tepper     int cr, struct mbuf *m)
14290a9108ebSHasso Tepper {
14300a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc, *next;
14310a9108ebSHasso Tepper 
14320a9108ebSHasso Tepper 	/*
14330a9108ebSHasso Tepper 	 * Since we did nothing that is not mandatory,
14340a9108ebSHasso Tepper 	 * we just abort the whole session..
14350a9108ebSHasso Tepper 	 */
14360a9108ebSHasso Tepper 
14370a9108ebSHasso Tepper 	next = LIST_FIRST(&rs->rs_dlcs);
14380a9108ebSHasso Tepper 	while ((dlc = next) != NULL) {
14390a9108ebSHasso Tepper 		next = LIST_NEXT(dlc, rd_next);
14400a9108ebSHasso Tepper 		rfcomm_dlc_close(dlc, ECONNABORTED);
14410a9108ebSHasso Tepper 	}
14420a9108ebSHasso Tepper 
14430a9108ebSHasso Tepper 	rfcomm_session_free(rs);
14440a9108ebSHasso Tepper }
14450a9108ebSHasso Tepper 
14460a9108ebSHasso Tepper /***********************************************************************
14470a9108ebSHasso Tepper  *
14480a9108ebSHasso Tepper  *	RFCOMM Session outward frame/uih/mcc building
14490a9108ebSHasso Tepper  */
14500a9108ebSHasso Tepper 
14510a9108ebSHasso Tepper /*
14520a9108ebSHasso Tepper  * SABM/DISC/DM/UA frames are all minimal and mostly identical.
14530a9108ebSHasso Tepper  */
14540a9108ebSHasso Tepper int
rfcomm_session_send_frame(struct rfcomm_session * rs,int type,int dlci)14550a9108ebSHasso Tepper rfcomm_session_send_frame(struct rfcomm_session *rs, int type, int dlci)
14560a9108ebSHasso Tepper {
14570a9108ebSHasso Tepper 	struct rfcomm_cmd_hdr *hdr;
14580a9108ebSHasso Tepper 	struct rfcomm_credit *credit;
14590a9108ebSHasso Tepper 	struct mbuf *m;
14600a9108ebSHasso Tepper 	uint8_t fcs, cr;
14610a9108ebSHasso Tepper 
14625179415aSSascha Wildner 	credit = zalloc(rfcomm_credit_pool);
14630a9108ebSHasso Tepper 	if (credit == NULL)
14640a9108ebSHasso Tepper 		return ENOMEM;
14650a9108ebSHasso Tepper 
1466b5523eacSSascha Wildner 	m = m_gethdr(M_NOWAIT, MT_DATA);
14670a9108ebSHasso Tepper 	if (m == NULL) {
14685179415aSSascha Wildner 		zfree(rfcomm_credit_pool, credit);
14690a9108ebSHasso Tepper 		return ENOMEM;
14700a9108ebSHasso Tepper 	}
14710a9108ebSHasso Tepper 
14720a9108ebSHasso Tepper 	/*
14730a9108ebSHasso Tepper 	 * The CR (command/response) bit identifies the frame either as a
14740a9108ebSHasso Tepper 	 * commmand or a response and is used along with the DLCI to form
14750a9108ebSHasso Tepper 	 * the address. Commands contain the non-initiator address, whereas
14760a9108ebSHasso Tepper 	 * responses contain the initiator address, so the CR value is
14770a9108ebSHasso Tepper 	 * also dependent on the session direction.
14780a9108ebSHasso Tepper 	 */
14790a9108ebSHasso Tepper 	if (type == RFCOMM_FRAME_UA || type == RFCOMM_FRAME_DM)
14800a9108ebSHasso Tepper 		cr = IS_INITIATOR(rs) ? 0 : 1;
14810a9108ebSHasso Tepper 	else
14820a9108ebSHasso Tepper 		cr = IS_INITIATOR(rs) ? 1 : 0;
14830a9108ebSHasso Tepper 
14840a9108ebSHasso Tepper 	hdr = mtod(m, struct rfcomm_cmd_hdr *);
14850a9108ebSHasso Tepper 	hdr->address = RFCOMM_MKADDRESS(cr, dlci);
14860a9108ebSHasso Tepper 	hdr->control = RFCOMM_MKCONTROL(type, 1);   /* PF = 1 */
14870a9108ebSHasso Tepper 	hdr->length = (0x00 << 1) | 0x01;	    /* len = 0x00, EA = 1 */
14880a9108ebSHasso Tepper 
14890a9108ebSHasso Tepper 	fcs = 0xff;
14900a9108ebSHasso Tepper 	fcs = FCS(fcs, hdr->address);
14910a9108ebSHasso Tepper 	fcs = FCS(fcs, hdr->control);
14920a9108ebSHasso Tepper 	fcs = FCS(fcs, hdr->length);
14930a9108ebSHasso Tepper 	fcs = 0xff - fcs;	/* ones complement */
14940a9108ebSHasso Tepper 	hdr->fcs = fcs;
14950a9108ebSHasso Tepper 
14960a9108ebSHasso Tepper 	m->m_pkthdr.len = m->m_len = sizeof(struct rfcomm_cmd_hdr);
14970a9108ebSHasso Tepper 
14980a9108ebSHasso Tepper 	/* empty credit note */
14990a9108ebSHasso Tepper 	credit->rc_dlc = NULL;
15000a9108ebSHasso Tepper 	credit->rc_len = m->m_pkthdr.len;
15010a9108ebSHasso Tepper 	STAILQ_INSERT_TAIL(&rs->rs_credits, credit, rc_next);
15020a9108ebSHasso Tepper 
15030a9108ebSHasso Tepper 	DPRINTFN(5, "dlci %d type %2.2x (%d bytes, fcs=%#2.2x)\n",
15040a9108ebSHasso Tepper 		dlci, type, m->m_pkthdr.len, fcs);
15050a9108ebSHasso Tepper 
15060a9108ebSHasso Tepper 	return l2cap_send(rs->rs_l2cap, m);
15070a9108ebSHasso Tepper }
15080a9108ebSHasso Tepper 
15090a9108ebSHasso Tepper /*
15100a9108ebSHasso Tepper  * rfcomm_session_send_uih(rfcomm_session, rfcomm_dlc, credits, mbuf)
15110a9108ebSHasso Tepper  *
15120a9108ebSHasso Tepper  * UIH frame is per DLC data or Multiplexer Control Commands
15130a9108ebSHasso Tepper  * when no DLC is given. Data mbuf is optional (just credits
15140a9108ebSHasso Tepper  * will be sent in that case)
15150a9108ebSHasso Tepper  */
15160a9108ebSHasso Tepper int
rfcomm_session_send_uih(struct rfcomm_session * rs,struct rfcomm_dlc * dlc,int credits,struct mbuf * m)15170a9108ebSHasso Tepper rfcomm_session_send_uih(struct rfcomm_session *rs, struct rfcomm_dlc *dlc,
15180a9108ebSHasso Tepper 			int credits, struct mbuf *m)
15190a9108ebSHasso Tepper {
15200a9108ebSHasso Tepper 	struct rfcomm_credit *credit;
15210a9108ebSHasso Tepper 	struct mbuf *m0 = NULL;
15220a9108ebSHasso Tepper 	int err, len;
15230a9108ebSHasso Tepper 	uint8_t fcs, *hdr;
15240a9108ebSHasso Tepper 
15250a9108ebSHasso Tepper 	KKASSERT(rs != NULL);
15260a9108ebSHasso Tepper 
15270a9108ebSHasso Tepper 	len = (m == NULL) ? 0 : m->m_pkthdr.len;
15280a9108ebSHasso Tepper 	KKASSERT(!(credits == 0 && len == 0));
15290a9108ebSHasso Tepper 
15300a9108ebSHasso Tepper 	/*
15310a9108ebSHasso Tepper 	 * Make a credit note for the completion notification
15320a9108ebSHasso Tepper 	 */
15335179415aSSascha Wildner 	credit = zalloc(rfcomm_credit_pool);
15340a9108ebSHasso Tepper 	if (credit == NULL)
15350a9108ebSHasso Tepper 		goto nomem;
15360a9108ebSHasso Tepper 
15370a9108ebSHasso Tepper 	credit->rc_len = len;
15380a9108ebSHasso Tepper 	credit->rc_dlc = dlc;
15390a9108ebSHasso Tepper 
15400a9108ebSHasso Tepper 	/*
15410a9108ebSHasso Tepper 	 * Wrap UIH frame information around payload.
15420a9108ebSHasso Tepper 	 *
15430a9108ebSHasso Tepper 	 * [ADDRESS] [CONTROL] [LENGTH] [CREDITS] [...] [FCS]
15440a9108ebSHasso Tepper 	 *
15450a9108ebSHasso Tepper 	 * Address is one octet.
15460a9108ebSHasso Tepper 	 * Control is one octet.
15470a9108ebSHasso Tepper 	 * Length is one or two octets.
15480a9108ebSHasso Tepper 	 * Credits may be one octet.
15490a9108ebSHasso Tepper 	 *
15500a9108ebSHasso Tepper 	 * FCS is one octet and calculated on address and
15510a9108ebSHasso Tepper 	 *	control octets only.
15520a9108ebSHasso Tepper 	 *
15530a9108ebSHasso Tepper 	 * If there are credits to be sent, we will set the PF
15540a9108ebSHasso Tepper 	 * flag and include them in the frame.
15550a9108ebSHasso Tepper 	 */
1556b5523eacSSascha Wildner 	m0 = m_gethdr(M_NOWAIT, MT_DATA);
15570a9108ebSHasso Tepper 	if (m0 == NULL)
15580a9108ebSHasso Tepper 		goto nomem;
15590a9108ebSHasso Tepper 
15600a9108ebSHasso Tepper 	MH_ALIGN(m0, 5);	/* (max 5 header octets) */
15610a9108ebSHasso Tepper 	hdr = mtod(m0, uint8_t *);
15620a9108ebSHasso Tepper 
15630a9108ebSHasso Tepper 	/* CR bit is set according to the initiator of the session */
15640a9108ebSHasso Tepper 	*hdr = RFCOMM_MKADDRESS((IS_INITIATOR(rs) ? 1 : 0),
15650a9108ebSHasso Tepper 				(dlc ? dlc->rd_dlci : 0));
15660a9108ebSHasso Tepper 	fcs = FCS(0xff, *hdr);
15670a9108ebSHasso Tepper 	hdr++;
15680a9108ebSHasso Tepper 
15690a9108ebSHasso Tepper 	/* PF bit is set if credits are being sent */
15700a9108ebSHasso Tepper 	*hdr = RFCOMM_MKCONTROL(RFCOMM_FRAME_UIH, (credits > 0 ? 1 : 0));
15710a9108ebSHasso Tepper 	fcs = FCS(fcs, *hdr);
15720a9108ebSHasso Tepper 	hdr++;
15730a9108ebSHasso Tepper 
15740a9108ebSHasso Tepper 	if (len < (1 << 7)) {
15750a9108ebSHasso Tepper 		*hdr++ = ((len << 1) & 0xfe) | 0x01;	/* 7 bits, EA = 1 */
15760a9108ebSHasso Tepper 	} else {
15770a9108ebSHasso Tepper 		*hdr++ = ((len << 1) & 0xfe);		/* 7 bits, EA = 0 */
15780a9108ebSHasso Tepper 		*hdr++ = ((len >> 7) & 0xff);		/* 8 bits, no EA */
15790a9108ebSHasso Tepper 	}
15800a9108ebSHasso Tepper 
15810a9108ebSHasso Tepper 	if (credits > 0)
15820a9108ebSHasso Tepper 		*hdr++ = (uint8_t)credits;
15830a9108ebSHasso Tepper 
15840a9108ebSHasso Tepper 	m0->m_len = hdr - mtod(m0, uint8_t *);
15850a9108ebSHasso Tepper 
15860a9108ebSHasso Tepper 	/* Append payload */
15870a9108ebSHasso Tepper 	m0->m_next = m;
15880a9108ebSHasso Tepper 	m = NULL;
15890a9108ebSHasso Tepper 
15900a9108ebSHasso Tepper 	m0->m_pkthdr.len = m0->m_len + len;
15910a9108ebSHasso Tepper 
15920a9108ebSHasso Tepper 	/* Append FCS */
15930a9108ebSHasso Tepper 	fcs = 0xff - fcs;	/* ones complement */
15940a9108ebSHasso Tepper 	len = m0->m_pkthdr.len;
159544647b48SAaron LI 	if (m_copyback2(m0, len, sizeof(fcs), &fcs, M_NOWAIT) != 0)
15960a9108ebSHasso Tepper 		goto nomem;
15970a9108ebSHasso Tepper 
15980a9108ebSHasso Tepper 	DPRINTFN(10, "dlci %d, pktlen %d (%d data, %d credits), fcs=%#2.2x\n",
15990a9108ebSHasso Tepper 		dlc ? dlc->rd_dlci : 0, m0->m_pkthdr.len, credit->rc_len,
16000a9108ebSHasso Tepper 		credits, fcs);
16010a9108ebSHasso Tepper 
16020a9108ebSHasso Tepper 	/*
16030a9108ebSHasso Tepper 	 * UIH frame ready to go..
16040a9108ebSHasso Tepper 	 */
16050a9108ebSHasso Tepper 	err = l2cap_send(rs->rs_l2cap, m0);
16060a9108ebSHasso Tepper 	if (err)
16070a9108ebSHasso Tepper 		goto fail;
16080a9108ebSHasso Tepper 
16090a9108ebSHasso Tepper 	STAILQ_INSERT_TAIL(&rs->rs_credits, credit, rc_next);
16100a9108ebSHasso Tepper 	return 0;
16110a9108ebSHasso Tepper 
16120a9108ebSHasso Tepper nomem:
16130a9108ebSHasso Tepper 	err = ENOMEM;
16140a9108ebSHasso Tepper 
16150a9108ebSHasso Tepper 	if (m0 != NULL)
16160a9108ebSHasso Tepper 		m_freem(m0);
16170a9108ebSHasso Tepper 
16180a9108ebSHasso Tepper 	if (m != NULL)
16190a9108ebSHasso Tepper 		m_freem(m);
16200a9108ebSHasso Tepper 
16210a9108ebSHasso Tepper fail:
16220a9108ebSHasso Tepper 	if (credit != NULL)
16235179415aSSascha Wildner 		zfree(rfcomm_credit_pool, credit);
16240a9108ebSHasso Tepper 
16250a9108ebSHasso Tepper 	return err;
16260a9108ebSHasso Tepper }
16270a9108ebSHasso Tepper 
16280a9108ebSHasso Tepper /*
16290a9108ebSHasso Tepper  * send Multiplexer Control Command (or Response) on session
16300a9108ebSHasso Tepper  */
16310a9108ebSHasso Tepper int
rfcomm_session_send_mcc(struct rfcomm_session * rs,int cr,uint8_t type,void * data,int len)16320a9108ebSHasso Tepper rfcomm_session_send_mcc(struct rfcomm_session *rs, int cr,
16330a9108ebSHasso Tepper 			uint8_t type, void *data, int len)
16340a9108ebSHasso Tepper {
16350a9108ebSHasso Tepper 	struct mbuf *m;
16360a9108ebSHasso Tepper 	uint8_t *hdr;
16370a9108ebSHasso Tepper 	int hlen;
16380a9108ebSHasso Tepper 
1639b5523eacSSascha Wildner 	m = m_gethdr(M_NOWAIT, MT_DATA);
16400a9108ebSHasso Tepper 	if (m == NULL)
16410a9108ebSHasso Tepper 		return ENOMEM;
16420a9108ebSHasso Tepper 
16430a9108ebSHasso Tepper 	hdr = mtod(m, uint8_t *);
16440a9108ebSHasso Tepper 
16450a9108ebSHasso Tepper 	/*
16460a9108ebSHasso Tepper 	 * Technically the type field can extend past one octet, but none
16470a9108ebSHasso Tepper 	 * currently defined will do that.
16480a9108ebSHasso Tepper 	 */
16490a9108ebSHasso Tepper 	*hdr++ = RFCOMM_MKMCC_TYPE(cr, type);
16500a9108ebSHasso Tepper 
16510a9108ebSHasso Tepper 	/*
16520a9108ebSHasso Tepper 	 * In the frame, the max length size is 2 octets (15 bits) whereas
16530a9108ebSHasso Tepper 	 * no max length size is specified for MCC commands. We must allow
16540a9108ebSHasso Tepper 	 * for 3 octets since for MCC frames we use 7 bits + EA in each.
16550a9108ebSHasso Tepper 	 *
16560a9108ebSHasso Tepper 	 * Only test data can possibly be that big.
16570a9108ebSHasso Tepper 	 *
16580a9108ebSHasso Tepper 	 * XXX Should we check this against the MTU?
16590a9108ebSHasso Tepper 	 */
16600a9108ebSHasso Tepper 	if (len < (1 << 7)) {
16610a9108ebSHasso Tepper 		*hdr++ = ((len << 1) & 0xfe) | 0x01;	/* 7 bits, EA = 1 */
16620a9108ebSHasso Tepper 	} else if (len < (1 << 14)) {
16630a9108ebSHasso Tepper 		*hdr++ = ((len << 1) & 0xfe);		/* 7 bits, EA = 0 */
16640a9108ebSHasso Tepper 		*hdr++ = ((len >> 6) & 0xfe) | 0x01;	/* 7 bits, EA = 1 */
16650a9108ebSHasso Tepper 	} else if (len < (1 << 15)) {
16660a9108ebSHasso Tepper 		*hdr++ = ((len << 1) & 0xfe);		/* 7 bits, EA = 0 */
16670a9108ebSHasso Tepper 		*hdr++ = ((len >> 6) & 0xfe);		/* 7 bits, EA = 0 */
16680a9108ebSHasso Tepper 		*hdr++ = ((len >> 13) & 0x02) | 0x01;	/* 1 bit,  EA = 1 */
16690a9108ebSHasso Tepper 	} else {
16700a9108ebSHasso Tepper 		DPRINTF("incredible length! (%d)\n", len);
16710a9108ebSHasso Tepper 		m_freem(m);
16720a9108ebSHasso Tepper 		return EMSGSIZE;
16730a9108ebSHasso Tepper 	}
16740a9108ebSHasso Tepper 
16750a9108ebSHasso Tepper 	/*
16760a9108ebSHasso Tepper 	 * add command data (to same mbuf if possible)
16770a9108ebSHasso Tepper 	 */
16780a9108ebSHasso Tepper 	hlen = hdr - mtod(m, uint8_t *);
1679*66e0d393SAaron LI 	m->m_len = m->m_pkthdr.len = hlen; /* in case len == 0 */
16800a9108ebSHasso Tepper 
168144647b48SAaron LI 	if (len > 0 && m_copyback2(m, hlen, len, data, M_NOWAIT) != 0) {
16820a9108ebSHasso Tepper 		m_freem(m);
16830a9108ebSHasso Tepper 		return ENOMEM;
16840a9108ebSHasso Tepper 	}
16850a9108ebSHasso Tepper 
16860a9108ebSHasso Tepper 	DPRINTFN(5, "%s type %2.2x len %d\n",
16870a9108ebSHasso Tepper 		(cr ? "command" : "response"), type, m->m_pkthdr.len);
16880a9108ebSHasso Tepper 
16890a9108ebSHasso Tepper 	return rfcomm_session_send_uih(rs, NULL, 0, m);
16900a9108ebSHasso Tepper }
1691