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 /*      $NetBSD: rfcomm_dlc.c,v 1.1.18.1 2007/07/19 16:04:19 liamjfoy Exp $     */
    2 
    3 /*-
    4  * Copyright (c) 2006 Itronix Inc.
    5  * All rights reserved.
    6  *
    7  * Written by Iain Hibbert for Itronix Inc.
    8  *
    9  * Redistribution and use in source and binary forms, with or without
   10  * modification, are permitted provided that the following conditions
   11  * are met:
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  * 3. The name of Itronix Inc. may not be used to endorse
   18  *    or promote products derived from this software without specific
   19  *    prior written permission.
   20  *
   21  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
   22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
   23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
   24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
   25  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   26  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   27  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   28  * ON ANY THEORY OF LIABILITY, WHETHER IN
   29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   31  * POSSIBILITY OF SUCH DAMAGE.
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __KERNEL_RCSID(0, "$NetBSD: rfcomm_dlc.c,v 1.1.18.1 2007/07/19 16:04:19 liamjfoy Exp $");
   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         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_schedule(&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         callout_ack(&dlc->rd_timeout);
  202 
  203         if (dlc->rd_state != RFCOMM_DLC_CLOSED)
  204                 rfcomm_dlc_close(dlc, ETIMEDOUT);
  205         else if (dlc->rd_flags & RFCOMM_DLC_DETACH)
  206                 free(dlc, M_BLUETOOTH);
  207 
  208         splx(s);
  209 }
  210 
  211 /*
  212  * rfcomm_dlc_setmode(rfcomm_dlc)
  213  *
  214  * Set link mode for DLC.  This is only called when the session is
  215  * already open, so we don't need to worry about any previous mode
  216  * settings.
  217  */
  218 int
  219 rfcomm_dlc_setmode(struct rfcomm_dlc *dlc)
  220 {
  221         int mode = 0;
  222 
  223         KASSERT(dlc->rd_session != NULL);
  224         KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  225 
  226         DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc->rd_dlci,
  227                 (dlc->rd_mode & RFCOMM_LM_AUTH ? "yes" : "no"),
  228                 (dlc->rd_mode & RFCOMM_LM_ENCRYPT ? "yes" : "no"),
  229                 (dlc->rd_mode & RFCOMM_LM_SECURE ? "yes" : "no"));
  230 
  231         if (dlc->rd_mode & RFCOMM_LM_AUTH)
  232                 mode |= L2CAP_LM_AUTH;
  233 
  234         if (dlc->rd_mode & RFCOMM_LM_ENCRYPT)
  235                 mode |= L2CAP_LM_ENCRYPT;
  236 
  237         if (dlc->rd_mode & RFCOMM_LM_SECURE)
  238                 mode |= L2CAP_LM_SECURE;
  239 
  240         return l2cap_setopt(dlc->rd_session->rs_l2cap, SO_L2CAP_LM, &mode);
  241 }
  242 
  243 /*
  244  * rfcomm_dlc_connect(rfcomm_dlc)
  245  *
  246  * initiate DLC connection (session is already connected)
  247  */
  248 int
  249 rfcomm_dlc_connect(struct rfcomm_dlc *dlc)
  250 {
  251         struct rfcomm_mcc_pn pn;
  252         int err = 0;
  253 
  254         KASSERT(dlc->rd_session != NULL);
  255         KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  256         KASSERT(dlc->rd_state == RFCOMM_DLC_WAIT_SESSION);
  257 
  258         /*
  259          * If we have not already sent a PN on the session, we must send
  260          * a PN to negotiate Credit Flow Control, and this setting will
  261          * apply to all future connections for this session. We ask for
  262          * this every time, in order to establish initial credits.
  263          */
  264         memset(&pn, 0, sizeof(pn));
  265         pn.dlci = dlc->rd_dlci;
  266         pn.priority = dlc->rd_dlci | 0x07;
  267         pn.mtu = htole16(dlc->rd_mtu);
  268 
  269         pn.flow_control = 0xf0;
  270         dlc->rd_rxcred = (dlc->rd_rxsize / dlc->rd_mtu);
  271         dlc->rd_rxcred = min(dlc->rd_rxcred, RFCOMM_CREDITS_DEFAULT);
  272         pn.credits = dlc->rd_rxcred;
  273 
  274         err = rfcomm_session_send_mcc(dlc->rd_session, 1,
  275                                         RFCOMM_MCC_PN, &pn, sizeof(pn));
  276         if (err)
  277                 return err;
  278 
  279         dlc->rd_state = RFCOMM_DLC_WAIT_CONNECT;
  280         callout_schedule(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
  281 
  282         return 0;
  283 }
  284 
  285 /*
  286  * rfcomm_dlc_open(rfcomm_dlc)
  287  *
  288  * send "Modem Status Command" and mark DLC as open.
  289  */
  290 int
  291 rfcomm_dlc_open(struct rfcomm_dlc *dlc)
  292 {
  293         struct rfcomm_mcc_msc msc;
  294         int err;
  295 
  296         KASSERT(dlc->rd_session != NULL);
  297         KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  298 
  299         memset(&msc, 0, sizeof(msc));
  300         msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci);
  301         msc.modem = dlc->rd_lmodem & 0xfe;      /* EA = 0 */
  302         msc.brk =       0x00       | 0x01;      /* EA = 1 */
  303 
  304         err = rfcomm_session_send_mcc(dlc->rd_session, 1,
  305                                 RFCOMM_MCC_MSC, &msc, sizeof(msc));
  306         if (err)
  307                 return err;
  308 
  309         callout_schedule(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
  310 
  311         dlc->rd_state = RFCOMM_DLC_OPEN;
  312         (*dlc->rd_proto->connected)(dlc->rd_upper);
  313 
  314         return 0;
  315 }
  316 
  317 /*
  318  * rfcomm_dlc_start(rfcomm_dlc)
  319  *
  320  * Start sending data (and/or credits) for DLC. Our strategy is to
  321  * send anything we can down to the l2cap layer. When credits run
  322  * out, data will naturally bunch up. When not using credit flow
  323  * control, we limit the number of packets we have pending to reduce
  324  * flow control lag.
  325  * We should deal with channel priority somehow.
  326  */
  327 void
  328 rfcomm_dlc_start(struct rfcomm_dlc *dlc)
  329 {
  330         struct rfcomm_session *rs = dlc->rd_session;
  331         struct mbuf *m;
  332         int len, credits;
  333 
  334         KASSERT(rs != NULL);
  335         KASSERT(rs->rs_state == RFCOMM_SESSION_OPEN);
  336         KASSERT(dlc->rd_state == RFCOMM_DLC_OPEN);
  337 
  338         for (;;) {
  339                 credits = 0;
  340                 len = dlc->rd_mtu;
  341                 if (rs->rs_flags & RFCOMM_SESSION_CFC) {
  342                         credits = (dlc->rd_rxsize / dlc->rd_mtu);
  343                         credits -= dlc->rd_rxcred;
  344                         credits = min(credits, RFCOMM_CREDITS_MAX);
  345 
  346                         if (credits > 0)
  347                                 len--;
  348 
  349                         if (dlc->rd_txcred == 0)
  350                                 len = 0;
  351                 } else {
  352                         if (rs->rs_flags & RFCOMM_SESSION_RFC)
  353                                 break;
  354 
  355                         if (dlc->rd_rmodem & RFCOMM_MSC_FC)
  356                                 break;
  357 
  358                         if (dlc->rd_pending > RFCOMM_CREDITS_DEFAULT)
  359                                 break;
  360                 }
  361 
  362                 if (dlc->rd_txbuf == NULL)
  363                         len = 0;
  364 
  365                 if (len == 0) {
  366                         if (credits == 0)
  367                                 break;
  368 
  369                         /*
  370                          * No need to send small numbers of credits on their
  371                          * own unless the other end hasn't many left.
  372                          */
  373                         if (credits < RFCOMM_CREDITS_DEFAULT
  374                             && dlc->rd_rxcred > RFCOMM_CREDITS_DEFAULT)
  375                                 break;
  376 
  377                         m = NULL;
  378                 } else {
  379                         /*
  380                          * take what data we can from (front of) txbuf
  381                          */
  382                         m = dlc->rd_txbuf;
  383                         if (len < m->m_pkthdr.len) {
  384                                 dlc->rd_txbuf = m_split(m, len, M_DONTWAIT);
  385                                 if (dlc->rd_txbuf == NULL) {
  386                                         dlc->rd_txbuf = m;
  387                                         break;
  388                                 }
  389                         } else {
  390                                 dlc->rd_txbuf = NULL;
  391                                 len = m->m_pkthdr.len;
  392                         }
  393                 }
  394 
  395                 DPRINTFN(10, "dlci %d send %d bytes, %d credits, rxcred = %d\n",
  396                         dlc->rd_dlci, len, credits, dlc->rd_rxcred);
  397 
  398                 if (rfcomm_session_send_uih(rs, dlc, credits, m)) {
  399                         printf("%s: lost %d bytes on DLCI %d\n",
  400                                 __func__, len, dlc->rd_dlci);
  401 
  402                         break;
  403                 }
  404 
  405                 dlc->rd_pending++;
  406 
  407                 if (rs->rs_flags & RFCOMM_SESSION_CFC) {
  408                         if (len > 0)
  409                                 dlc->rd_txcred--;
  410 
  411                         if (credits > 0)
  412                                 dlc->rd_rxcred += credits;
  413                 }
  414         }
  415 }

Cache object: 9c7edc16eeec4fac208cf7f68162876d


[ 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.