[BACK]Return to rfcomm_dlc.c CVS log [TXT][DIR] Up to [local] / sys / netbt

Annotation of sys/netbt/rfcomm_dlc.c, Revision 1.1

1.1     ! nbrk        1: /*     $OpenBSD: rfcomm_dlc.c,v 1.1 2007/06/01 02:46:12 uwe Exp $      */
        !             2: /*     $NetBSD: rfcomm_dlc.c,v 1.3 2007/04/21 06:15:23 plunky Exp $    */
        !             3:
        !             4: /*-
        !             5:  * Copyright (c) 2006 Itronix Inc.
        !             6:  * All rights reserved.
        !             7:  *
        !             8:  * Written by Iain Hibbert for Itronix Inc.
        !             9:  *
        !            10:  * Redistribution and use in source and binary forms, with or without
        !            11:  * modification, are permitted provided that the following conditions
        !            12:  * are met:
        !            13:  * 1. Redistributions of source code must retain the above copyright
        !            14:  *    notice, this list of conditions and the following disclaimer.
        !            15:  * 2. Redistributions in binary form must reproduce the above copyright
        !            16:  *    notice, this list of conditions and the following disclaimer in the
        !            17:  *    documentation and/or other materials provided with the distribution.
        !            18:  * 3. The name of Itronix Inc. may not be used to endorse
        !            19:  *    or promote products derived from this software without specific
        !            20:  *    prior written permission.
        !            21:  *
        !            22:  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
        !            23:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
        !            24:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
        !            25:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
        !            26:  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
        !            27:  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
        !            28:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
        !            29:  * ON ANY THEORY OF LIABILITY, WHETHER IN
        !            30:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        !            31:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        !            32:  * POSSIBILITY OF SUCH DAMAGE.
        !            33:  */
        !            34:
        !            35: #include <sys/cdefs.h>
        !            36:
        !            37: #include <sys/param.h>
        !            38: #include <sys/kernel.h>
        !            39: #include <sys/mbuf.h>
        !            40: #include <sys/proc.h>
        !            41: #include <sys/systm.h>
        !            42:
        !            43: #include <netbt/bluetooth.h>
        !            44: #include <netbt/hci.h>
        !            45: #include <netbt/l2cap.h>
        !            46: #include <netbt/rfcomm.h>
        !            47:
        !            48: /*
        !            49:  * rfcomm_dlc_lookup(rfcomm_session, dlci)
        !            50:  *
        !            51:  * Find DLC on session with matching dlci
        !            52:  */
        !            53: struct rfcomm_dlc *
        !            54: rfcomm_dlc_lookup(struct rfcomm_session *rs, int dlci)
        !            55: {
        !            56:        struct rfcomm_dlc *dlc;
        !            57:
        !            58:        LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
        !            59:                if (dlc->rd_dlci == dlci)
        !            60:                        break;
        !            61:        }
        !            62:
        !            63:        return dlc;
        !            64: }
        !            65:
        !            66: /*
        !            67:  * rfcomm_dlc_newconn(rfcomm_session, dlci)
        !            68:  *
        !            69:  * handle a new dlc request (since its called from a couple of places)
        !            70:  */
        !            71: struct rfcomm_dlc *
        !            72: rfcomm_dlc_newconn(struct rfcomm_session *rs, int dlci)
        !            73: {
        !            74:        struct rfcomm_session *ls;
        !            75:        struct rfcomm_dlc *new, *dlc, *any, *best;
        !            76:        struct sockaddr_bt laddr, raddr, addr;
        !            77:        int chan;
        !            78:
        !            79:        /*
        !            80:         * Search amongst the listening DLC community for the best match for
        !            81:         * address & channel. We keep listening DLC's hanging on listening
        !            82:         * sessions in a last first order, so scan the entire bunch and keep
        !            83:         * a note of the best address and BDADDR_ANY matches in order to find
        !            84:         * the oldest and most specific match.
        !            85:         */
        !            86:        l2cap_sockaddr(rs->rs_l2cap, &laddr);
        !            87:        l2cap_peeraddr(rs->rs_l2cap, &raddr);
        !            88:        chan = RFCOMM_CHANNEL(dlci);
        !            89:        new = NULL;
        !            90:
        !            91:        any = best = NULL;
        !            92:        LIST_FOREACH(ls, &rfcomm_session_listen, rs_next) {
        !            93:                l2cap_sockaddr(ls->rs_l2cap, &addr);
        !            94:
        !            95:                if (addr.bt_psm != laddr.bt_psm)
        !            96:                        continue;
        !            97:
        !            98:                if (bdaddr_same(&laddr.bt_bdaddr, &addr.bt_bdaddr)) {
        !            99:                        LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
        !           100:                                if (dlc->rd_laddr.bt_channel == chan)
        !           101:                                        best = dlc;
        !           102:                        }
        !           103:                }
        !           104:
        !           105:                if (bdaddr_any(&addr.bt_bdaddr)) {
        !           106:                        LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
        !           107:                                if (dlc->rd_laddr.bt_channel == chan)
        !           108:                                        any = dlc;
        !           109:                        }
        !           110:                }
        !           111:        }
        !           112:
        !           113:        dlc = best ? best : any;
        !           114:
        !           115:        /* XXX
        !           116:         * Note that if this fails, we could have missed a chance to open
        !           117:         * a connection - really need to rewrite the strategy for storing
        !           118:         * listening DLC's so all can be checked in turn..
        !           119:         */
        !           120:        if (dlc != NULL)
        !           121:                new = (*dlc->rd_proto->newconn)(dlc->rd_upper, &laddr, &raddr);
        !           122:
        !           123:        if (new == NULL) {
        !           124:                rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM, dlci);
        !           125:                return NULL;
        !           126:        }
        !           127:
        !           128:        new->rd_dlci = dlci;
        !           129:        new->rd_mtu = rfcomm_mtu_default;
        !           130:        new->rd_mode = dlc->rd_mode;
        !           131:
        !           132:        memcpy(&new->rd_laddr, &laddr, sizeof(struct sockaddr_bt));
        !           133:        new->rd_laddr.bt_channel = chan;
        !           134:
        !           135:        memcpy(&new->rd_raddr, &raddr, sizeof(struct sockaddr_bt));
        !           136:        new->rd_raddr.bt_channel = chan;
        !           137:
        !           138:        new->rd_session = rs;
        !           139:        new->rd_state = RFCOMM_DLC_WAIT_CONNECT;
        !           140:        LIST_INSERT_HEAD(&rs->rs_dlcs, new, rd_next);
        !           141:
        !           142:        return new;
        !           143: }
        !           144:
        !           145: /*
        !           146:  * rfcomm_dlc_close(dlc, error)
        !           147:  *
        !           148:  * detach DLC from session and clean up
        !           149:  */
        !           150: void
        !           151: rfcomm_dlc_close(struct rfcomm_dlc *dlc, int err)
        !           152: {
        !           153:        struct rfcomm_session *rs;
        !           154:        struct rfcomm_credit *credit;
        !           155:
        !           156:        KASSERT(dlc->rd_state != RFCOMM_DLC_CLOSED);
        !           157:
        !           158:        /* Clear credit history */
        !           159:        rs = dlc->rd_session;
        !           160:        SIMPLEQ_FOREACH(credit, &rs->rs_credits, rc_next)
        !           161:                if (credit->rc_dlc == dlc)
        !           162:                        credit->rc_dlc = NULL;
        !           163:
        !           164:        timeout_del(&dlc->rd_timeout);
        !           165:
        !           166:        LIST_REMOVE(dlc, rd_next);
        !           167:        dlc->rd_session = NULL;
        !           168:        dlc->rd_state = RFCOMM_DLC_CLOSED;
        !           169:
        !           170:        (*dlc->rd_proto->disconnected)(dlc->rd_upper, err);
        !           171:
        !           172:        /*
        !           173:         * It is the responsibility of the party who sends the last
        !           174:         * DISC(dlci) to disconnect the session, but we will schedule
        !           175:         * an expiry just in case that doesnt happen..
        !           176:         */
        !           177:        if (LIST_EMPTY(&rs->rs_dlcs)) {
        !           178:                if (rs->rs_state == RFCOMM_SESSION_LISTEN)
        !           179:                        rfcomm_session_free(rs);
        !           180:                else
        !           181:                        timeout_add(&rs->rs_timeout,
        !           182:                            rfcomm_ack_timeout * hz);
        !           183:        }
        !           184: }
        !           185:
        !           186: /*
        !           187:  * rfcomm_dlc_timeout(dlc)
        !           188:  *
        !           189:  * DLC timeout function is schedUled when we sent any of SABM,
        !           190:  * DISC, MCC_MSC, or MCC_PN and should be cancelled when we get
        !           191:  * the relevant response. There is nothing to do but shut this
        !           192:  * DLC down.
        !           193:  */
        !           194: void
        !           195: rfcomm_dlc_timeout(void *arg)
        !           196: {
        !           197:        struct rfcomm_dlc *dlc = arg;
        !           198:        int s;
        !           199:
        !           200:        s = splsoftnet();
        !           201:
        !           202:        if (dlc->rd_state != RFCOMM_DLC_CLOSED)
        !           203:                rfcomm_dlc_close(dlc, ETIMEDOUT);
        !           204:        else if (dlc->rd_flags & RFCOMM_DLC_DETACH)
        !           205:                free(dlc, M_BLUETOOTH);
        !           206:
        !           207:        splx(s);
        !           208: }
        !           209:
        !           210: /*
        !           211:  * rfcomm_dlc_setmode(rfcomm_dlc)
        !           212:  *
        !           213:  * Set link mode for DLC.  This is only called when the session is
        !           214:  * already open, so we don't need to worry about any previous mode
        !           215:  * settings.
        !           216:  */
        !           217: int
        !           218: rfcomm_dlc_setmode(struct rfcomm_dlc *dlc)
        !           219: {
        !           220:        int mode = 0;
        !           221:
        !           222:        KASSERT(dlc->rd_session != NULL);
        !           223:        KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
        !           224:
        !           225:        DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc->rd_dlci,
        !           226:                (dlc->rd_mode & RFCOMM_LM_AUTH ? "yes" : "no"),
        !           227:                (dlc->rd_mode & RFCOMM_LM_ENCRYPT ? "yes" : "no"),
        !           228:                (dlc->rd_mode & RFCOMM_LM_SECURE ? "yes" : "no"));
        !           229:
        !           230:        if (dlc->rd_mode & RFCOMM_LM_AUTH)
        !           231:                mode |= L2CAP_LM_AUTH;
        !           232:
        !           233:        if (dlc->rd_mode & RFCOMM_LM_ENCRYPT)
        !           234:                mode |= L2CAP_LM_ENCRYPT;
        !           235:
        !           236:        if (dlc->rd_mode & RFCOMM_LM_SECURE)
        !           237:                mode |= L2CAP_LM_SECURE;
        !           238:
        !           239:        return l2cap_setopt(dlc->rd_session->rs_l2cap, SO_L2CAP_LM, &mode);
        !           240: }
        !           241:
        !           242: /*
        !           243:  * rfcomm_dlc_connect(rfcomm_dlc)
        !           244:  *
        !           245:  * initiate DLC connection (session is already connected)
        !           246:  */
        !           247: int
        !           248: rfcomm_dlc_connect(struct rfcomm_dlc *dlc)
        !           249: {
        !           250:        struct rfcomm_mcc_pn pn;
        !           251:        int err = 0;
        !           252:
        !           253:        KASSERT(dlc->rd_session != NULL);
        !           254:        KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
        !           255:        KASSERT(dlc->rd_state == RFCOMM_DLC_WAIT_SESSION);
        !           256:
        !           257:        /*
        !           258:         * If we have not already sent a PN on the session, we must send
        !           259:         * a PN to negotiate Credit Flow Control, and this setting will
        !           260:         * apply to all future connections for this session. We ask for
        !           261:         * this every time, in order to establish initial credits.
        !           262:         */
        !           263:        memset(&pn, 0, sizeof(pn));
        !           264:        pn.dlci = dlc->rd_dlci;
        !           265:        pn.priority = dlc->rd_dlci | 0x07;
        !           266:        pn.mtu = htole16(dlc->rd_mtu);
        !           267:
        !           268:        pn.flow_control = 0xf0;
        !           269:        dlc->rd_rxcred = (dlc->rd_rxsize / dlc->rd_mtu);
        !           270:        dlc->rd_rxcred = min(dlc->rd_rxcred, RFCOMM_CREDITS_DEFAULT);
        !           271:        pn.credits = dlc->rd_rxcred;
        !           272:
        !           273:        err = rfcomm_session_send_mcc(dlc->rd_session, 1,
        !           274:                                        RFCOMM_MCC_PN, &pn, sizeof(pn));
        !           275:        if (err)
        !           276:                return err;
        !           277:
        !           278:        dlc->rd_state = RFCOMM_DLC_WAIT_CONNECT;
        !           279:        timeout_add(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
        !           280:
        !           281:        return 0;
        !           282: }
        !           283:
        !           284: /*
        !           285:  * rfcomm_dlc_open(rfcomm_dlc)
        !           286:  *
        !           287:  * send "Modem Status Command" and mark DLC as open.
        !           288:  */
        !           289: int
        !           290: rfcomm_dlc_open(struct rfcomm_dlc *dlc)
        !           291: {
        !           292:        struct rfcomm_mcc_msc msc;
        !           293:        int err;
        !           294:
        !           295:        KASSERT(dlc->rd_session != NULL);
        !           296:        KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
        !           297:
        !           298:        memset(&msc, 0, sizeof(msc));
        !           299:        msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci);
        !           300:        msc.modem = dlc->rd_lmodem & 0xfe;      /* EA = 0 */
        !           301:        msc.brk =       0x00       | 0x01;      /* EA = 1 */
        !           302:
        !           303:        err = rfcomm_session_send_mcc(dlc->rd_session, 1,
        !           304:                                RFCOMM_MCC_MSC, &msc, sizeof(msc));
        !           305:        if (err)
        !           306:                return err;
        !           307:
        !           308:        timeout_add(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
        !           309:
        !           310:        dlc->rd_state = RFCOMM_DLC_OPEN;
        !           311:        (*dlc->rd_proto->connected)(dlc->rd_upper);
        !           312:
        !           313:        return 0;
        !           314: }
        !           315:
        !           316: /*
        !           317:  * rfcomm_dlc_start(rfcomm_dlc)
        !           318:  *
        !           319:  * Start sending data (and/or credits) for DLC. Our strategy is to
        !           320:  * send anything we can down to the l2cap layer. When credits run
        !           321:  * out, data will naturally bunch up. When not using credit flow
        !           322:  * control, we limit the number of packets we have pending to reduce
        !           323:  * flow control lag.
        !           324:  * We should deal with channel priority somehow.
        !           325:  */
        !           326: void
        !           327: rfcomm_dlc_start(struct rfcomm_dlc *dlc)
        !           328: {
        !           329:        struct rfcomm_session *rs = dlc->rd_session;
        !           330:        struct mbuf *m;
        !           331:        int len, credits;
        !           332:
        !           333:        KASSERT(rs != NULL);
        !           334:        KASSERT(rs->rs_state == RFCOMM_SESSION_OPEN);
        !           335:        KASSERT(dlc->rd_state == RFCOMM_DLC_OPEN);
        !           336:
        !           337:        for (;;) {
        !           338:                credits = 0;
        !           339:                len = dlc->rd_mtu;
        !           340:                if (rs->rs_flags & RFCOMM_SESSION_CFC) {
        !           341:                        credits = (dlc->rd_rxsize / dlc->rd_mtu);
        !           342:                        credits -= dlc->rd_rxcred;
        !           343:                        credits = min(credits, RFCOMM_CREDITS_MAX);
        !           344:
        !           345:                        if (credits > 0)
        !           346:                                len--;
        !           347:
        !           348:                        if (dlc->rd_txcred == 0)
        !           349:                                len = 0;
        !           350:                } else {
        !           351:                        if (rs->rs_flags & RFCOMM_SESSION_RFC)
        !           352:                                break;
        !           353:
        !           354:                        if (dlc->rd_rmodem & RFCOMM_MSC_FC)
        !           355:                                break;
        !           356:
        !           357:                        if (dlc->rd_pending > RFCOMM_CREDITS_DEFAULT)
        !           358:                                break;
        !           359:                }
        !           360:
        !           361:                if (dlc->rd_txbuf == NULL)
        !           362:                        len = 0;
        !           363:
        !           364:                if (len == 0) {
        !           365:                        if (credits == 0)
        !           366:                                break;
        !           367:
        !           368:                        /*
        !           369:                         * No need to send small numbers of credits on their
        !           370:                         * own unless the other end hasn't many left.
        !           371:                         */
        !           372:                        if (credits < RFCOMM_CREDITS_DEFAULT
        !           373:                            && dlc->rd_rxcred > RFCOMM_CREDITS_DEFAULT)
        !           374:                                break;
        !           375:
        !           376:                        m = NULL;
        !           377:                } else {
        !           378:                        /*
        !           379:                         * take what data we can from (front of) txbuf
        !           380:                         */
        !           381:                        m = dlc->rd_txbuf;
        !           382:                        if (len < m->m_pkthdr.len) {
        !           383:                                dlc->rd_txbuf = m_split(m, len, M_DONTWAIT);
        !           384:                                if (dlc->rd_txbuf == NULL) {
        !           385:                                        dlc->rd_txbuf = m;
        !           386:                                        break;
        !           387:                                }
        !           388:                        } else {
        !           389:                                dlc->rd_txbuf = NULL;
        !           390:                                len = m->m_pkthdr.len;
        !           391:                        }
        !           392:                }
        !           393:
        !           394:                DPRINTFN(10, "dlci %d send %d bytes, %d credits, rxcred = %d\n",
        !           395:                        dlc->rd_dlci, len, credits, dlc->rd_rxcred);
        !           396:
        !           397:                if (rfcomm_session_send_uih(rs, dlc, credits, m)) {
        !           398:                        printf("%s: lost %d bytes on DLCI %d\n",
        !           399:                                __func__, len, dlc->rd_dlci);
        !           400:
        !           401:                        break;
        !           402:                }
        !           403:
        !           404:                dlc->rd_pending++;
        !           405:
        !           406:                if (rs->rs_flags & RFCOMM_SESSION_CFC) {
        !           407:                        if (len > 0)
        !           408:                                dlc->rd_txcred--;
        !           409:
        !           410:                        if (credits > 0)
        !           411:                                dlc->rd_rxcred += credits;
        !           412:                }
        !           413:        }
        !           414: }

CVSweb