xref: /dflybsd-src/sys/netbt/rfcomm_dlc.c (revision 805c8e8e4093ceca2e27510ad3a66d4de8060a55)
183aacedeSHasso Tepper /* $OpenBSD: src/sys/netbt/rfcomm_dlc.c,v 1.2 2008/02/24 21:34:48 uwe Exp $ */
283aacedeSHasso Tepper /* $NetBSD: rfcomm_dlc.c,v 1.4 2007/11/03 17:20:17 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>
37*805c8e8eSzrj #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/endian.h>
420a9108ebSHasso Tepper 
430a9108ebSHasso Tepper #include <netbt/bluetooth.h>
440a9108ebSHasso Tepper #include <netbt/hci.h>
450a9108ebSHasso Tepper #include <netbt/l2cap.h>
460a9108ebSHasso Tepper #include <netbt/rfcomm.h>
470a9108ebSHasso Tepper 
480a9108ebSHasso Tepper /*
490a9108ebSHasso Tepper  * rfcomm_dlc_lookup(rfcomm_session, dlci)
500a9108ebSHasso Tepper  *
510a9108ebSHasso Tepper  * Find DLC on session with matching dlci
520a9108ebSHasso Tepper  */
530a9108ebSHasso Tepper struct rfcomm_dlc *
rfcomm_dlc_lookup(struct rfcomm_session * rs,int dlci)540a9108ebSHasso Tepper rfcomm_dlc_lookup(struct rfcomm_session *rs, int dlci)
550a9108ebSHasso Tepper {
560a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc;
570a9108ebSHasso Tepper 
580a9108ebSHasso Tepper 	LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
590a9108ebSHasso Tepper 		if (dlc->rd_dlci == dlci)
600a9108ebSHasso Tepper 			break;
610a9108ebSHasso Tepper 	}
620a9108ebSHasso Tepper 
630a9108ebSHasso Tepper 	return dlc;
640a9108ebSHasso Tepper }
650a9108ebSHasso Tepper 
660a9108ebSHasso Tepper /*
670a9108ebSHasso Tepper  * rfcomm_dlc_newconn(rfcomm_session, dlci)
680a9108ebSHasso Tepper  *
690a9108ebSHasso Tepper  * handle a new dlc request (since its called from a couple of places)
700a9108ebSHasso Tepper  */
710a9108ebSHasso Tepper struct rfcomm_dlc *
rfcomm_dlc_newconn(struct rfcomm_session * rs,int dlci)720a9108ebSHasso Tepper rfcomm_dlc_newconn(struct rfcomm_session *rs, int dlci)
730a9108ebSHasso Tepper {
740a9108ebSHasso Tepper 	struct rfcomm_session *ls;
750a9108ebSHasso Tepper 	struct rfcomm_dlc *new, *dlc, *any, *best;
760a9108ebSHasso Tepper 	struct sockaddr_bt laddr, raddr, addr;
770a9108ebSHasso Tepper 	int chan;
780a9108ebSHasso Tepper 
790a9108ebSHasso Tepper 	/*
800a9108ebSHasso Tepper 	 * Search amongst the listening DLC community for the best match for
810a9108ebSHasso Tepper 	 * address & channel. We keep listening DLC's hanging on listening
820a9108ebSHasso Tepper 	 * sessions in a last first order, so scan the entire bunch and keep
830a9108ebSHasso Tepper 	 * a note of the best address and BDADDR_ANY matches in order to find
840a9108ebSHasso Tepper 	 * the oldest and most specific match.
850a9108ebSHasso Tepper 	 */
860a9108ebSHasso Tepper 	l2cap_sockaddr(rs->rs_l2cap, &laddr);
870a9108ebSHasso Tepper 	l2cap_peeraddr(rs->rs_l2cap, &raddr);
880a9108ebSHasso Tepper 	chan = RFCOMM_CHANNEL(dlci);
890a9108ebSHasso Tepper 	new = NULL;
900a9108ebSHasso Tepper 
910a9108ebSHasso Tepper 	any = best = NULL;
920a9108ebSHasso Tepper 	LIST_FOREACH(ls, &rfcomm_session_listen, rs_next) {
930a9108ebSHasso Tepper 		l2cap_sockaddr(ls->rs_l2cap, &addr);
940a9108ebSHasso Tepper 
950a9108ebSHasso Tepper 		if (addr.bt_psm != laddr.bt_psm)
960a9108ebSHasso Tepper 			continue;
970a9108ebSHasso Tepper 
980a9108ebSHasso Tepper 		if (bdaddr_same(&laddr.bt_bdaddr, &addr.bt_bdaddr)) {
990a9108ebSHasso Tepper 			LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
1000a9108ebSHasso Tepper 				if (dlc->rd_laddr.bt_channel == chan)
1010a9108ebSHasso Tepper 					best = dlc;
1020a9108ebSHasso Tepper 			}
1030a9108ebSHasso Tepper 		}
1040a9108ebSHasso Tepper 
1050a9108ebSHasso Tepper 		if (bdaddr_any(&addr.bt_bdaddr)) {
1060a9108ebSHasso Tepper 			LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
1070a9108ebSHasso Tepper 				if (dlc->rd_laddr.bt_channel == chan)
1080a9108ebSHasso Tepper 					any = dlc;
1090a9108ebSHasso Tepper 			}
1100a9108ebSHasso Tepper 		}
1110a9108ebSHasso Tepper 	}
1120a9108ebSHasso Tepper 
1130a9108ebSHasso Tepper 	dlc = best ? best : any;
1140a9108ebSHasso Tepper 
1150a9108ebSHasso Tepper 	/* XXX
1160a9108ebSHasso Tepper 	 * Note that if this fails, we could have missed a chance to open
1170a9108ebSHasso Tepper 	 * a connection - really need to rewrite the strategy for storing
1180a9108ebSHasso Tepper 	 * listening DLC's so all can be checked in turn..
1190a9108ebSHasso Tepper 	 */
1200a9108ebSHasso Tepper 	if (dlc != NULL)
1210a9108ebSHasso Tepper 		new = (*dlc->rd_proto->newconn)(dlc->rd_upper, &laddr, &raddr);
1220a9108ebSHasso Tepper 
1230a9108ebSHasso Tepper 	if (new == NULL) {
1240a9108ebSHasso Tepper 		rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM, dlci);
1250a9108ebSHasso Tepper 		return NULL;
1260a9108ebSHasso Tepper 	}
1270a9108ebSHasso Tepper 
1280a9108ebSHasso Tepper 	new->rd_dlci = dlci;
1290a9108ebSHasso Tepper 	new->rd_mtu = rfcomm_mtu_default;
1300a9108ebSHasso Tepper 	new->rd_mode = dlc->rd_mode;
1310a9108ebSHasso Tepper 
1320a9108ebSHasso Tepper 	memcpy(&new->rd_laddr, &laddr, sizeof(struct sockaddr_bt));
1330a9108ebSHasso Tepper 	new->rd_laddr.bt_channel = chan;
1340a9108ebSHasso Tepper 
1350a9108ebSHasso Tepper 	memcpy(&new->rd_raddr, &raddr, sizeof(struct sockaddr_bt));
1360a9108ebSHasso Tepper 	new->rd_raddr.bt_channel = chan;
1370a9108ebSHasso Tepper 
1380a9108ebSHasso Tepper 	new->rd_session = rs;
1390a9108ebSHasso Tepper 	new->rd_state = RFCOMM_DLC_WAIT_CONNECT;
1400a9108ebSHasso Tepper 	LIST_INSERT_HEAD(&rs->rs_dlcs, new, rd_next);
1410a9108ebSHasso Tepper 
1420a9108ebSHasso Tepper 	return new;
1430a9108ebSHasso Tepper }
1440a9108ebSHasso Tepper 
1450a9108ebSHasso Tepper /*
1460a9108ebSHasso Tepper  * rfcomm_dlc_close(dlc, error)
1470a9108ebSHasso Tepper  *
1480a9108ebSHasso Tepper  * detach DLC from session and clean up
1490a9108ebSHasso Tepper  */
1500a9108ebSHasso Tepper void
rfcomm_dlc_close(struct rfcomm_dlc * dlc,int err)1510a9108ebSHasso Tepper rfcomm_dlc_close(struct rfcomm_dlc *dlc, int err)
1520a9108ebSHasso Tepper {
1530a9108ebSHasso Tepper 	struct rfcomm_session *rs;
1540a9108ebSHasso Tepper 	struct rfcomm_credit *credit;
1550a9108ebSHasso Tepper 
1560a9108ebSHasso Tepper 	KKASSERT(dlc->rd_state != RFCOMM_DLC_CLOSED);
1570a9108ebSHasso Tepper 
1580a9108ebSHasso Tepper 	/* Clear credit history */
1590a9108ebSHasso Tepper 	rs = dlc->rd_session;
1600a9108ebSHasso Tepper 	STAILQ_FOREACH(credit, &rs->rs_credits, rc_next)
1610a9108ebSHasso Tepper 		if (credit->rc_dlc == dlc)
1620a9108ebSHasso Tepper 			credit->rc_dlc = NULL;
1630a9108ebSHasso Tepper 
1640a9108ebSHasso Tepper 	callout_stop(&dlc->rd_timeout);
1650a9108ebSHasso Tepper 
1660a9108ebSHasso Tepper 	LIST_REMOVE(dlc, rd_next);
1670a9108ebSHasso Tepper 	dlc->rd_session = NULL;
1680a9108ebSHasso Tepper 	dlc->rd_state = RFCOMM_DLC_CLOSED;
1690a9108ebSHasso Tepper 
1700a9108ebSHasso Tepper 	(*dlc->rd_proto->disconnected)(dlc->rd_upper, err);
1710a9108ebSHasso Tepper 
1720a9108ebSHasso Tepper 	/*
1730a9108ebSHasso Tepper 	 * It is the responsibility of the party who sends the last
1740a9108ebSHasso Tepper 	 * DISC(dlci) to disconnect the session, but we will schedule
1750a9108ebSHasso Tepper 	 * an expiry just in case that doesnt happen..
1760a9108ebSHasso Tepper 	 */
1770a9108ebSHasso Tepper 	if (LIST_EMPTY(&rs->rs_dlcs)) {
1780a9108ebSHasso Tepper 		if (rs->rs_state == RFCOMM_SESSION_LISTEN)
1790a9108ebSHasso Tepper 			rfcomm_session_free(rs);
1800a9108ebSHasso Tepper 		else
1810a9108ebSHasso Tepper 			callout_reset(&rs->rs_timeout, rfcomm_ack_timeout * hz,
1820a9108ebSHasso Tepper 			    rfcomm_session_timeout, rs);
1830a9108ebSHasso Tepper 	}
1840a9108ebSHasso Tepper }
1850a9108ebSHasso Tepper 
1860a9108ebSHasso Tepper /*
1870a9108ebSHasso Tepper  * rfcomm_dlc_timeout(dlc)
1880a9108ebSHasso Tepper  *
1890a9108ebSHasso Tepper  * DLC timeout function is schedUled when we sent any of SABM,
1900a9108ebSHasso Tepper  * DISC, MCC_MSC, or MCC_PN and should be cancelled when we get
1910a9108ebSHasso Tepper  * the relevant response. There is nothing to do but shut this
1920a9108ebSHasso Tepper  * DLC down.
1930a9108ebSHasso Tepper  */
1940a9108ebSHasso Tepper void
rfcomm_dlc_timeout(void * arg)1950a9108ebSHasso Tepper rfcomm_dlc_timeout(void *arg)
1960a9108ebSHasso Tepper {
1970a9108ebSHasso Tepper 	struct rfcomm_dlc *dlc = arg;
1980a9108ebSHasso Tepper 
1990a9108ebSHasso Tepper 	crit_enter();
2000a9108ebSHasso Tepper 
2010a9108ebSHasso Tepper 	if (dlc->rd_state != RFCOMM_DLC_CLOSED)
2020a9108ebSHasso Tepper 		rfcomm_dlc_close(dlc, ETIMEDOUT);
2030a9108ebSHasso Tepper 	else if (dlc->rd_flags & RFCOMM_DLC_DETACH)
2040a9108ebSHasso Tepper 		kfree(dlc, M_BLUETOOTH);
2050a9108ebSHasso Tepper 
2060a9108ebSHasso Tepper 	crit_exit();
2070a9108ebSHasso Tepper }
2080a9108ebSHasso Tepper 
2090a9108ebSHasso Tepper /*
2100a9108ebSHasso Tepper  * rfcomm_dlc_setmode(rfcomm_dlc)
2110a9108ebSHasso Tepper  *
2120a9108ebSHasso Tepper  * Set link mode for DLC.  This is only called when the session is
2130a9108ebSHasso Tepper  * already open, so we don't need to worry about any previous mode
2140a9108ebSHasso Tepper  * settings.
2150a9108ebSHasso Tepper  */
2160a9108ebSHasso Tepper int
rfcomm_dlc_setmode(struct rfcomm_dlc * dlc)2170a9108ebSHasso Tepper rfcomm_dlc_setmode(struct rfcomm_dlc *dlc)
2180a9108ebSHasso Tepper {
2190a9108ebSHasso Tepper 	int mode = 0;
2200a9108ebSHasso Tepper 
2210a9108ebSHasso Tepper 	KKASSERT(dlc->rd_session != NULL);
2220a9108ebSHasso Tepper 	KKASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
2230a9108ebSHasso Tepper 
2240a9108ebSHasso Tepper 	DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc->rd_dlci,
2250a9108ebSHasso Tepper 		(dlc->rd_mode & RFCOMM_LM_AUTH ? "yes" : "no"),
2260a9108ebSHasso Tepper 		(dlc->rd_mode & RFCOMM_LM_ENCRYPT ? "yes" : "no"),
2270a9108ebSHasso Tepper 		(dlc->rd_mode & RFCOMM_LM_SECURE ? "yes" : "no"));
2280a9108ebSHasso Tepper 
2290a9108ebSHasso Tepper 	if (dlc->rd_mode & RFCOMM_LM_AUTH)
2300a9108ebSHasso Tepper 		mode |= L2CAP_LM_AUTH;
2310a9108ebSHasso Tepper 
2320a9108ebSHasso Tepper 	if (dlc->rd_mode & RFCOMM_LM_ENCRYPT)
2330a9108ebSHasso Tepper 		mode |= L2CAP_LM_ENCRYPT;
2340a9108ebSHasso Tepper 
2350a9108ebSHasso Tepper 	if (dlc->rd_mode & RFCOMM_LM_SECURE)
2360a9108ebSHasso Tepper 		mode |= L2CAP_LM_SECURE;
2370a9108ebSHasso Tepper 
2380a9108ebSHasso Tepper 	return l2cap_setopt(dlc->rd_session->rs_l2cap, SO_L2CAP_LM, &mode);
2390a9108ebSHasso Tepper }
2400a9108ebSHasso Tepper 
2410a9108ebSHasso Tepper /*
2420a9108ebSHasso Tepper  * rfcomm_dlc_connect(rfcomm_dlc)
2430a9108ebSHasso Tepper  *
2440a9108ebSHasso Tepper  * initiate DLC connection (session is already connected)
2450a9108ebSHasso Tepper  */
2460a9108ebSHasso Tepper int
rfcomm_dlc_connect(struct rfcomm_dlc * dlc)2470a9108ebSHasso Tepper rfcomm_dlc_connect(struct rfcomm_dlc *dlc)
2480a9108ebSHasso Tepper {
2490a9108ebSHasso Tepper 	struct rfcomm_mcc_pn pn;
2500a9108ebSHasso Tepper 	int err = 0;
2510a9108ebSHasso Tepper 
2520a9108ebSHasso Tepper 	KKASSERT(dlc->rd_session != NULL);
2530a9108ebSHasso Tepper 	KKASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
2540a9108ebSHasso Tepper 	KKASSERT(dlc->rd_state == RFCOMM_DLC_WAIT_SESSION);
2550a9108ebSHasso Tepper 
2560a9108ebSHasso Tepper 	/*
2570a9108ebSHasso Tepper 	 * If we have not already sent a PN on the session, we must send
2580a9108ebSHasso Tepper 	 * a PN to negotiate Credit Flow Control, and this setting will
2590a9108ebSHasso Tepper 	 * apply to all future connections for this session. We ask for
2600a9108ebSHasso Tepper 	 * this every time, in order to establish initial credits.
2610a9108ebSHasso Tepper 	 */
2620a9108ebSHasso Tepper 	memset(&pn, 0, sizeof(pn));
2630a9108ebSHasso Tepper 	pn.dlci = dlc->rd_dlci;
2640a9108ebSHasso Tepper 	pn.priority = dlc->rd_dlci | 0x07;
2650a9108ebSHasso Tepper 	pn.mtu = htole16(dlc->rd_mtu);
2660a9108ebSHasso Tepper 
2670a9108ebSHasso Tepper 	pn.flow_control = 0xf0;
2680a9108ebSHasso Tepper 	dlc->rd_rxcred = (dlc->rd_rxsize / dlc->rd_mtu);
2690a9108ebSHasso Tepper 	dlc->rd_rxcred = min(dlc->rd_rxcred, RFCOMM_CREDITS_DEFAULT);
2700a9108ebSHasso Tepper 	pn.credits = dlc->rd_rxcred;
2710a9108ebSHasso Tepper 
2720a9108ebSHasso Tepper 	err = rfcomm_session_send_mcc(dlc->rd_session, 1,
2730a9108ebSHasso Tepper 					RFCOMM_MCC_PN, &pn, sizeof(pn));
2740a9108ebSHasso Tepper 	if (err)
2750a9108ebSHasso Tepper 		return err;
2760a9108ebSHasso Tepper 
2770a9108ebSHasso Tepper 	dlc->rd_state = RFCOMM_DLC_WAIT_CONNECT;
2780a9108ebSHasso Tepper 	callout_reset(&dlc->rd_timeout, rfcomm_mcc_timeout * hz,
2790a9108ebSHasso Tepper 	    rfcomm_dlc_timeout, dlc);
2800a9108ebSHasso Tepper 	return 0;
2810a9108ebSHasso Tepper }
2820a9108ebSHasso Tepper 
2830a9108ebSHasso Tepper /*
2840a9108ebSHasso Tepper  * rfcomm_dlc_open(rfcomm_dlc)
2850a9108ebSHasso Tepper  *
2860a9108ebSHasso Tepper  * send "Modem Status Command" and mark DLC as open.
2870a9108ebSHasso Tepper  */
2880a9108ebSHasso Tepper int
rfcomm_dlc_open(struct rfcomm_dlc * dlc)2890a9108ebSHasso Tepper rfcomm_dlc_open(struct rfcomm_dlc *dlc)
2900a9108ebSHasso Tepper {
2910a9108ebSHasso Tepper 	struct rfcomm_mcc_msc msc;
2920a9108ebSHasso Tepper 	int err;
2930a9108ebSHasso Tepper 
2940a9108ebSHasso Tepper 	KKASSERT(dlc->rd_session != NULL);
2950a9108ebSHasso Tepper 	KKASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
2960a9108ebSHasso Tepper 
2970a9108ebSHasso Tepper 	memset(&msc, 0, sizeof(msc));
2980a9108ebSHasso Tepper 	msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci);
2990a9108ebSHasso Tepper 	msc.modem = dlc->rd_lmodem & 0xfe;	/* EA = 0 */
3000a9108ebSHasso Tepper 	msc.brk =	0x00	   | 0x01;	/* EA = 1 */
3010a9108ebSHasso Tepper 
3020a9108ebSHasso Tepper 	err = rfcomm_session_send_mcc(dlc->rd_session, 1,
3030a9108ebSHasso Tepper 				RFCOMM_MCC_MSC, &msc, sizeof(msc));
3040a9108ebSHasso Tepper 	if (err)
3050a9108ebSHasso Tepper 		return err;
3060a9108ebSHasso Tepper 
3070a9108ebSHasso Tepper 	callout_reset(&dlc->rd_timeout, rfcomm_mcc_timeout * hz,
3080a9108ebSHasso Tepper 	    rfcomm_dlc_timeout, dlc);
3090a9108ebSHasso Tepper 
3100a9108ebSHasso Tepper 	dlc->rd_state = RFCOMM_DLC_OPEN;
3110a9108ebSHasso Tepper 	(*dlc->rd_proto->connected)(dlc->rd_upper);
3120a9108ebSHasso Tepper 
3130a9108ebSHasso Tepper 	return 0;
3140a9108ebSHasso Tepper }
3150a9108ebSHasso Tepper 
3160a9108ebSHasso Tepper /*
3170a9108ebSHasso Tepper  * rfcomm_dlc_start(rfcomm_dlc)
3180a9108ebSHasso Tepper  *
3190a9108ebSHasso Tepper  * Start sending data (and/or credits) for DLC. Our strategy is to
3200a9108ebSHasso Tepper  * send anything we can down to the l2cap layer. When credits run
3210a9108ebSHasso Tepper  * out, data will naturally bunch up. When not using credit flow
3220a9108ebSHasso Tepper  * control, we limit the number of packets we have pending to reduce
3230a9108ebSHasso Tepper  * flow control lag.
3240a9108ebSHasso Tepper  * We should deal with channel priority somehow.
3250a9108ebSHasso Tepper  */
3260a9108ebSHasso Tepper void
rfcomm_dlc_start(struct rfcomm_dlc * dlc)3270a9108ebSHasso Tepper rfcomm_dlc_start(struct rfcomm_dlc *dlc)
3280a9108ebSHasso Tepper {
3290a9108ebSHasso Tepper 	struct rfcomm_session *rs = dlc->rd_session;
3300a9108ebSHasso Tepper 	struct mbuf *m;
3310a9108ebSHasso Tepper 	int len, credits;
3320a9108ebSHasso Tepper 
3330a9108ebSHasso Tepper 	KKASSERT(rs != NULL);
3340a9108ebSHasso Tepper 	KKASSERT(rs->rs_state == RFCOMM_SESSION_OPEN);
3350a9108ebSHasso Tepper 	KKASSERT(dlc->rd_state == RFCOMM_DLC_OPEN);
3360a9108ebSHasso Tepper 
3370a9108ebSHasso Tepper 	for (;;) {
3380a9108ebSHasso Tepper 		credits = 0;
3390a9108ebSHasso Tepper 		len = dlc->rd_mtu;
3400a9108ebSHasso Tepper 		if (rs->rs_flags & RFCOMM_SESSION_CFC) {
3410a9108ebSHasso Tepper 			credits = (dlc->rd_rxsize / dlc->rd_mtu);
3420a9108ebSHasso Tepper 			credits -= dlc->rd_rxcred;
3430a9108ebSHasso Tepper 			credits = min(credits, RFCOMM_CREDITS_MAX);
3440a9108ebSHasso Tepper 
3450a9108ebSHasso Tepper 			if (credits > 0)
3460a9108ebSHasso Tepper 				len--;
3470a9108ebSHasso Tepper 
3480a9108ebSHasso Tepper 			if (dlc->rd_txcred == 0)
3490a9108ebSHasso Tepper 				len = 0;
3500a9108ebSHasso Tepper 		} else {
3510a9108ebSHasso Tepper 			if (rs->rs_flags & RFCOMM_SESSION_RFC)
3520a9108ebSHasso Tepper 				break;
3530a9108ebSHasso Tepper 
3540a9108ebSHasso Tepper 			if (dlc->rd_rmodem & RFCOMM_MSC_FC)
3550a9108ebSHasso Tepper 				break;
3560a9108ebSHasso Tepper 
3570a9108ebSHasso Tepper 			if (dlc->rd_pending > RFCOMM_CREDITS_DEFAULT)
3580a9108ebSHasso Tepper 				break;
3590a9108ebSHasso Tepper 		}
3600a9108ebSHasso Tepper 
3610a9108ebSHasso Tepper 		if (dlc->rd_txbuf == NULL)
3620a9108ebSHasso Tepper 			len = 0;
3630a9108ebSHasso Tepper 
3640a9108ebSHasso Tepper 		if (len == 0) {
3650a9108ebSHasso Tepper 			if (credits == 0)
3660a9108ebSHasso Tepper 				break;
3670a9108ebSHasso Tepper 
3680a9108ebSHasso Tepper 			/*
3690a9108ebSHasso Tepper 			 * No need to send small numbers of credits on their
3700a9108ebSHasso Tepper 			 * own unless the other end hasn't many left.
3710a9108ebSHasso Tepper 			 */
3720a9108ebSHasso Tepper 			if (credits < RFCOMM_CREDITS_DEFAULT
3730a9108ebSHasso Tepper 			    && dlc->rd_rxcred > RFCOMM_CREDITS_DEFAULT)
3740a9108ebSHasso Tepper 				break;
3750a9108ebSHasso Tepper 
3760a9108ebSHasso Tepper 			m = NULL;
3770a9108ebSHasso Tepper 		} else {
3780a9108ebSHasso Tepper 			/*
3790a9108ebSHasso Tepper 			 * take what data we can from (front of) txbuf
3800a9108ebSHasso Tepper 			 */
3810a9108ebSHasso Tepper 			m = dlc->rd_txbuf;
3820a9108ebSHasso Tepper 			if (len < m->m_pkthdr.len) {
383b5523eacSSascha Wildner 				dlc->rd_txbuf = m_split(m, len, M_NOWAIT);
3840a9108ebSHasso Tepper 				if (dlc->rd_txbuf == NULL) {
3850a9108ebSHasso Tepper 					dlc->rd_txbuf = m;
3860a9108ebSHasso Tepper 					break;
3870a9108ebSHasso Tepper 				}
3880a9108ebSHasso Tepper 			} else {
3890a9108ebSHasso Tepper 				dlc->rd_txbuf = NULL;
3900a9108ebSHasso Tepper 				len = m->m_pkthdr.len;
3910a9108ebSHasso Tepper 			}
3920a9108ebSHasso Tepper 		}
3930a9108ebSHasso Tepper 
3940a9108ebSHasso Tepper 		DPRINTFN(10, "dlci %d send %d bytes, %d credits, rxcred = %d\n",
3950a9108ebSHasso Tepper 			dlc->rd_dlci, len, credits, dlc->rd_rxcred);
3960a9108ebSHasso Tepper 
3970a9108ebSHasso Tepper 		if (rfcomm_session_send_uih(rs, dlc, credits, m)) {
3980a9108ebSHasso Tepper 			kprintf("%s: lost %d bytes on DLCI %d\n",
3990a9108ebSHasso Tepper 				__func__, len, dlc->rd_dlci);
4000a9108ebSHasso Tepper 
4010a9108ebSHasso Tepper 			break;
4020a9108ebSHasso Tepper 		}
4030a9108ebSHasso Tepper 
4040a9108ebSHasso Tepper 		dlc->rd_pending++;
4050a9108ebSHasso Tepper 
4060a9108ebSHasso Tepper 		if (rs->rs_flags & RFCOMM_SESSION_CFC) {
4070a9108ebSHasso Tepper 			if (len > 0)
4080a9108ebSHasso Tepper 				dlc->rd_txcred--;
4090a9108ebSHasso Tepper 
4100a9108ebSHasso Tepper 			if (credits > 0)
4110a9108ebSHasso Tepper 				dlc->rd_rxcred += credits;
4120a9108ebSHasso Tepper 		}
4130a9108ebSHasso Tepper 	}
4140a9108ebSHasso Tepper }
415