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