The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netbt/rfcomm_dlc.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /* $DragonFly: src/sys/netbt/rfcomm_dlc.c,v 1.2 2008/03/18 13:41:42 hasso Exp $ */
    2 /* $OpenBSD: src/sys/netbt/rfcomm_dlc.c,v 1.2 2008/02/24 21:34:48 uwe Exp $ */
    3 /* $NetBSD: rfcomm_dlc.c,v 1.4 2007/11/03 17:20:17 plunky Exp $ */
    4 
    5 /*-
    6  * Copyright (c) 2006 Itronix Inc.
    7  * All rights reserved.
    8  *
    9  * Written by Iain Hibbert for Itronix Inc.
   10  *
   11  * Redistribution and use in source and binary forms, with or without
   12  * modification, are permitted provided that the following conditions
   13  * are met:
   14  * 1. Redistributions of source code must retain the above copyright
   15  *    notice, this list of conditions and the following disclaimer.
   16  * 2. Redistributions in binary form must reproduce the above copyright
   17  *    notice, this list of conditions and the following disclaimer in the
   18  *    documentation and/or other materials provided with the distribution.
   19  * 3. The name of Itronix Inc. may not be used to endorse
   20  *    or promote products derived from this software without specific
   21  *    prior written permission.
   22  *
   23  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
   24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
   27  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   30  * ON ANY THEORY OF LIABILITY, WHETHER IN
   31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33  * POSSIBILITY OF SUCH DAMAGE.
   34  */
   35 
   36 #include <sys/param.h>
   37 #include <sys/kernel.h>
   38 #include <sys/mbuf.h>
   39 #include <sys/proc.h>
   40 #include <sys/systm.h>
   41 #include <sys/endian.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         KKASSERT(dlc->rd_state != RFCOMM_DLC_CLOSED);
  157 
  158         /* Clear credit history */
  159         rs = dlc->rd_session;
  160         STAILQ_FOREACH(credit, &rs->rs_credits, rc_next)
  161                 if (credit->rc_dlc == dlc)
  162                         credit->rc_dlc = NULL;
  163 
  164         callout_stop(&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                         callout_reset(&rs->rs_timeout, rfcomm_ack_timeout * hz,
  182                             rfcomm_session_timeout, rs);
  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 
  199         crit_enter();
  200 
  201         if (dlc->rd_state != RFCOMM_DLC_CLOSED)
  202                 rfcomm_dlc_close(dlc, ETIMEDOUT);
  203         else if (dlc->rd_flags & RFCOMM_DLC_DETACH)
  204                 kfree(dlc, M_BLUETOOTH);
  205 
  206         crit_exit();
  207 }
  208 
  209 /*
  210  * rfcomm_dlc_setmode(rfcomm_dlc)
  211  *
  212  * Set link mode for DLC.  This is only called when the session is
  213  * already open, so we don't need to worry about any previous mode
  214  * settings.
  215  */
  216 int
  217 rfcomm_dlc_setmode(struct rfcomm_dlc *dlc)
  218 {
  219         int mode = 0;
  220 
  221         KKASSERT(dlc->rd_session != NULL);
  222         KKASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  223 
  224         DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc->rd_dlci,
  225                 (dlc->rd_mode & RFCOMM_LM_AUTH ? "yes" : "no"),
  226                 (dlc->rd_mode & RFCOMM_LM_ENCRYPT ? "yes" : "no"),
  227                 (dlc->rd_mode & RFCOMM_LM_SECURE ? "yes" : "no"));
  228 
  229         if (dlc->rd_mode & RFCOMM_LM_AUTH)
  230                 mode |= L2CAP_LM_AUTH;
  231 
  232         if (dlc->rd_mode & RFCOMM_LM_ENCRYPT)
  233                 mode |= L2CAP_LM_ENCRYPT;
  234 
  235         if (dlc->rd_mode & RFCOMM_LM_SECURE)
  236                 mode |= L2CAP_LM_SECURE;
  237 
  238         return l2cap_setopt(dlc->rd_session->rs_l2cap, SO_L2CAP_LM, &mode);
  239 }
  240 
  241 /*
  242  * rfcomm_dlc_connect(rfcomm_dlc)
  243  *
  244  * initiate DLC connection (session is already connected)
  245  */
  246 int
  247 rfcomm_dlc_connect(struct rfcomm_dlc *dlc)
  248 {
  249         struct rfcomm_mcc_pn pn;
  250         int err = 0;
  251 
  252         KKASSERT(dlc->rd_session != NULL);
  253         KKASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  254         KKASSERT(dlc->rd_state == RFCOMM_DLC_WAIT_SESSION);
  255 
  256         /*
  257          * If we have not already sent a PN on the session, we must send
  258          * a PN to negotiate Credit Flow Control, and this setting will
  259          * apply to all future connections for this session. We ask for
  260          * this every time, in order to establish initial credits.
  261          */
  262         memset(&pn, 0, sizeof(pn));
  263         pn.dlci = dlc->rd_dlci;
  264         pn.priority = dlc->rd_dlci | 0x07;
  265         pn.mtu = htole16(dlc->rd_mtu);
  266 
  267         pn.flow_control = 0xf0;
  268         dlc->rd_rxcred = (dlc->rd_rxsize / dlc->rd_mtu);
  269         dlc->rd_rxcred = min(dlc->rd_rxcred, RFCOMM_CREDITS_DEFAULT);
  270         pn.credits = dlc->rd_rxcred;
  271 
  272         err = rfcomm_session_send_mcc(dlc->rd_session, 1,
  273                                         RFCOMM_MCC_PN, &pn, sizeof(pn));
  274         if (err)
  275                 return err;
  276 
  277         dlc->rd_state = RFCOMM_DLC_WAIT_CONNECT;
  278         callout_reset(&dlc->rd_timeout, rfcomm_mcc_timeout * hz,
  279             rfcomm_dlc_timeout, dlc);
  280         return 0;
  281 }
  282 
  283 /*
  284  * rfcomm_dlc_open(rfcomm_dlc)
  285  *
  286  * send "Modem Status Command" and mark DLC as open.
  287  */
  288 int
  289 rfcomm_dlc_open(struct rfcomm_dlc *dlc)
  290 {
  291         struct rfcomm_mcc_msc msc;
  292         int err;
  293 
  294         KKASSERT(dlc->rd_session != NULL);
  295         KKASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  296 
  297         memset(&msc, 0, sizeof(msc));
  298         msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci);
  299         msc.modem = dlc->rd_lmodem & 0xfe;      /* EA = 0 */
  300         msc.brk =       0x00       | 0x01;      /* EA = 1 */
  301 
  302         err = rfcomm_session_send_mcc(dlc->rd_session, 1,
  303                                 RFCOMM_MCC_MSC, &msc, sizeof(msc));
  304         if (err)
  305                 return err;
  306 
  307         callout_reset(&dlc->rd_timeout, rfcomm_mcc_timeout * hz,
  308             rfcomm_dlc_timeout, dlc);
  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         KKASSERT(rs != NULL);
  334         KKASSERT(rs->rs_state == RFCOMM_SESSION_OPEN);
  335         KKASSERT(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, MB_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                         kprintf("%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 }

Cache object: b6d9c2e99631127d221ec5e866186be6


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.