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