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.6 2008/08/06 15:01:24 plunky 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.6 2008/08/06 15:01:24 plunky 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/socketvar.h>
   42 #include <sys/systm.h>
   43 
   44 #include <netbt/bluetooth.h>
   45 #include <netbt/hci.h>
   46 #include <netbt/l2cap.h>
   47 #include <netbt/rfcomm.h>
   48 
   49 /*
   50  * rfcomm_dlc_lookup(rfcomm_session, dlci)
   51  *
   52  * Find DLC on session with matching dlci
   53  */
   54 struct rfcomm_dlc *
   55 rfcomm_dlc_lookup(struct rfcomm_session *rs, int dlci)
   56 {
   57         struct rfcomm_dlc *dlc;
   58 
   59         LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
   60                 if (dlc->rd_dlci == dlci)
   61                         break;
   62         }
   63 
   64         return dlc;
   65 }
   66 
   67 /*
   68  * rfcomm_dlc_newconn(rfcomm_session, dlci)
   69  *
   70  * handle a new dlc request (since its called from a couple of places)
   71  */
   72 struct rfcomm_dlc *
   73 rfcomm_dlc_newconn(struct rfcomm_session *rs, int dlci)
   74 {
   75         struct rfcomm_session *ls;
   76         struct rfcomm_dlc *new, *dlc, *any, *best;
   77         struct sockaddr_bt laddr, raddr, addr;
   78         int chan;
   79 
   80         /*
   81          * Search amongst the listening DLC community for the best match for
   82          * address & channel. We keep listening DLC's hanging on listening
   83          * sessions in a last first order, so scan the entire bunch and keep
   84          * a note of the best address and BDADDR_ANY matches in order to find
   85          * the oldest and most specific match.
   86          */
   87         l2cap_sockaddr(rs->rs_l2cap, &laddr);
   88         l2cap_peeraddr(rs->rs_l2cap, &raddr);
   89         chan = RFCOMM_CHANNEL(dlci);
   90         new = NULL;
   91 
   92         any = best = NULL;
   93         LIST_FOREACH(ls, &rfcomm_session_listen, rs_next) {
   94                 l2cap_sockaddr(ls->rs_l2cap, &addr);
   95 
   96                 if (addr.bt_psm != laddr.bt_psm)
   97                         continue;
   98 
   99                 if (bdaddr_same(&laddr.bt_bdaddr, &addr.bt_bdaddr)) {
  100                         LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
  101                                 if (dlc->rd_laddr.bt_channel == chan)
  102                                         best = dlc;
  103                         }
  104                 }
  105 
  106                 if (bdaddr_any(&addr.bt_bdaddr)) {
  107                         LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
  108                                 if (dlc->rd_laddr.bt_channel == chan)
  109                                         any = dlc;
  110                         }
  111                 }
  112         }
  113 
  114         dlc = best ? best : any;
  115 
  116         /* XXX
  117          * Note that if this fails, we could have missed a chance to open
  118          * a connection - really need to rewrite the strategy for storing
  119          * listening DLC's so all can be checked in turn..
  120          */
  121         if (dlc != NULL)
  122                 new = (*dlc->rd_proto->newconn)(dlc->rd_upper, &laddr, &raddr);
  123 
  124         if (new == NULL) {
  125                 rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM, dlci);
  126                 return NULL;
  127         }
  128 
  129         new->rd_dlci = dlci;
  130         new->rd_mtu = rfcomm_mtu_default;
  131         new->rd_mode = dlc->rd_mode;
  132 
  133         memcpy(&new->rd_laddr, &laddr, sizeof(struct sockaddr_bt));
  134         new->rd_laddr.bt_channel = chan;
  135 
  136         memcpy(&new->rd_raddr, &raddr, sizeof(struct sockaddr_bt));
  137         new->rd_raddr.bt_channel = chan;
  138 
  139         new->rd_session = rs;
  140         new->rd_state = RFCOMM_DLC_WAIT_CONNECT;
  141         LIST_INSERT_HEAD(&rs->rs_dlcs, new, rd_next);
  142 
  143         return new;
  144 }
  145 
  146 /*
  147  * rfcomm_dlc_close(dlc, error)
  148  *
  149  * detach DLC from session and clean up
  150  */
  151 void
  152 rfcomm_dlc_close(struct rfcomm_dlc *dlc, int err)
  153 {
  154         struct rfcomm_session *rs;
  155         struct rfcomm_credit *credit;
  156 
  157         KASSERT(dlc->rd_state != RFCOMM_DLC_CLOSED);
  158 
  159         /* Clear credit history */
  160         rs = dlc->rd_session;
  161         SIMPLEQ_FOREACH(credit, &rs->rs_credits, rc_next)
  162                 if (credit->rc_dlc == dlc)
  163                         credit->rc_dlc = NULL;
  164 
  165         callout_stop(&dlc->rd_timeout);
  166 
  167         LIST_REMOVE(dlc, rd_next);
  168         dlc->rd_session = NULL;
  169         dlc->rd_state = RFCOMM_DLC_CLOSED;
  170 
  171         (*dlc->rd_proto->disconnected)(dlc->rd_upper, err);
  172 
  173         /*
  174          * It is the responsibility of the party who sends the last
  175          * DISC(dlci) to disconnect the session, but we will schedule
  176          * an expiry just in case that doesnt happen..
  177          */
  178         if (LIST_EMPTY(&rs->rs_dlcs)) {
  179                 if (rs->rs_state == RFCOMM_SESSION_LISTEN)
  180                         rfcomm_session_free(rs);
  181                 else
  182                         callout_schedule(&rs->rs_timeout,
  183                                         rfcomm_ack_timeout * hz);
  184         }
  185 }
  186 
  187 /*
  188  * rfcomm_dlc_timeout(dlc)
  189  *
  190  * DLC timeout function is schedUled when we sent any of SABM,
  191  * DISC, MCC_MSC, or MCC_PN and should be cancelled when we get
  192  * the relevant response. There is nothing to do but shut this
  193  * DLC down.
  194  */
  195 void
  196 rfcomm_dlc_timeout(void *arg)
  197 {
  198         struct rfcomm_dlc *dlc = arg;
  199 
  200         mutex_enter(bt_lock);
  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                 callout_destroy(&dlc->rd_timeout);
  207                 free(dlc, M_BLUETOOTH);
  208         }
  209 
  210         mutex_exit(bt_lock);
  211 }
  212 
  213 /*
  214  * rfcomm_dlc_setmode(rfcomm_dlc)
  215  *
  216  * Set link mode for DLC.  This is only called when the session is
  217  * already open, so we don't need to worry about any previous mode
  218  * settings.
  219  */
  220 int
  221 rfcomm_dlc_setmode(struct rfcomm_dlc *dlc)
  222 {
  223         struct sockopt sopt;
  224         int mode = 0, err;
  225 
  226         KASSERT(dlc->rd_session != NULL);
  227         KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  228 
  229         DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc->rd_dlci,
  230                 (dlc->rd_mode & RFCOMM_LM_AUTH ? "yes" : "no"),
  231                 (dlc->rd_mode & RFCOMM_LM_ENCRYPT ? "yes" : "no"),
  232                 (dlc->rd_mode & RFCOMM_LM_SECURE ? "yes" : "no"));
  233 
  234         if (dlc->rd_mode & RFCOMM_LM_AUTH)
  235                 mode |= L2CAP_LM_AUTH;
  236 
  237         if (dlc->rd_mode & RFCOMM_LM_ENCRYPT)
  238                 mode |= L2CAP_LM_ENCRYPT;
  239 
  240         if (dlc->rd_mode & RFCOMM_LM_SECURE)
  241                 mode |= L2CAP_LM_SECURE;
  242 
  243         sockopt_init(&sopt, BTPROTO_L2CAP, SO_L2CAP_LM, 0);
  244         sockopt_setint(&sopt, mode);
  245         err = l2cap_setopt(dlc->rd_session->rs_l2cap, &sopt);
  246         sockopt_destroy(&sopt);
  247 
  248         return err;
  249 }
  250 
  251 /*
  252  * rfcomm_dlc_connect(rfcomm_dlc)
  253  *
  254  * initiate DLC connection (session is already connected)
  255  */
  256 int
  257 rfcomm_dlc_connect(struct rfcomm_dlc *dlc)
  258 {
  259         struct rfcomm_mcc_pn pn;
  260         int err = 0;
  261 
  262         KASSERT(dlc->rd_session != NULL);
  263         KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  264         KASSERT(dlc->rd_state == RFCOMM_DLC_WAIT_SESSION);
  265 
  266         /*
  267          * If we have not already sent a PN on the session, we must send
  268          * a PN to negotiate Credit Flow Control, and this setting will
  269          * apply to all future connections for this session. We ask for
  270          * this every time, in order to establish initial credits.
  271          */
  272         memset(&pn, 0, sizeof(pn));
  273         pn.dlci = dlc->rd_dlci;
  274         pn.priority = dlc->rd_dlci | 0x07;
  275         pn.mtu = htole16(dlc->rd_mtu);
  276 
  277         pn.flow_control = 0xf0;
  278         dlc->rd_rxcred = (dlc->rd_rxsize / dlc->rd_mtu);
  279         dlc->rd_rxcred = min(dlc->rd_rxcred, RFCOMM_CREDITS_DEFAULT);
  280         pn.credits = dlc->rd_rxcred;
  281 
  282         err = rfcomm_session_send_mcc(dlc->rd_session, 1,
  283                                         RFCOMM_MCC_PN, &pn, sizeof(pn));
  284         if (err)
  285                 return err;
  286 
  287         dlc->rd_state = RFCOMM_DLC_WAIT_CONNECT;
  288         callout_schedule(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
  289 
  290         return 0;
  291 }
  292 
  293 /*
  294  * rfcomm_dlc_open(rfcomm_dlc)
  295  *
  296  * send "Modem Status Command" and mark DLC as open.
  297  */
  298 int
  299 rfcomm_dlc_open(struct rfcomm_dlc *dlc)
  300 {
  301         struct rfcomm_mcc_msc msc;
  302         int err;
  303 
  304         KASSERT(dlc->rd_session != NULL);
  305         KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
  306 
  307         memset(&msc, 0, sizeof(msc));
  308         msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci);
  309         msc.modem = dlc->rd_lmodem & 0xfe;      /* EA = 0 */
  310         msc.brk =       0x00       | 0x01;      /* EA = 1 */
  311 
  312         err = rfcomm_session_send_mcc(dlc->rd_session, 1,
  313                                 RFCOMM_MCC_MSC, &msc, sizeof(msc));
  314         if (err)
  315                 return err;
  316 
  317         callout_schedule(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
  318 
  319         dlc->rd_state = RFCOMM_DLC_OPEN;
  320         (*dlc->rd_proto->connected)(dlc->rd_upper);
  321 
  322         return 0;
  323 }
  324 
  325 /*
  326  * rfcomm_dlc_start(rfcomm_dlc)
  327  *
  328  * Start sending data (and/or credits) for DLC. Our strategy is to
  329  * send anything we can down to the l2cap layer. When credits run
  330  * out, data will naturally bunch up. When not using credit flow
  331  * control, we limit the number of packets we have pending to reduce
  332  * flow control lag.
  333  * We should deal with channel priority somehow.
  334  */
  335 void
  336 rfcomm_dlc_start(struct rfcomm_dlc *dlc)
  337 {
  338         struct rfcomm_session *rs = dlc->rd_session;
  339         struct mbuf *m;
  340         int len, credits;
  341 
  342         KASSERT(rs != NULL);
  343         KASSERT(rs->rs_state == RFCOMM_SESSION_OPEN);
  344         KASSERT(dlc->rd_state == RFCOMM_DLC_OPEN);
  345 
  346         for (;;) {
  347                 credits = 0;
  348                 len = dlc->rd_mtu;
  349                 if (rs->rs_flags & RFCOMM_SESSION_CFC) {
  350                         credits = (dlc->rd_rxsize / dlc->rd_mtu);
  351                         credits -= dlc->rd_rxcred;
  352                         credits = min(credits, RFCOMM_CREDITS_MAX);
  353 
  354                         if (credits > 0)
  355                                 len--;
  356 
  357                         if (dlc->rd_txcred == 0)
  358                                 len = 0;
  359                 } else {
  360                         if (rs->rs_flags & RFCOMM_SESSION_RFC)
  361                                 break;
  362 
  363                         if (dlc->rd_rmodem & RFCOMM_MSC_FC)
  364                                 break;
  365 
  366                         if (dlc->rd_pending > RFCOMM_CREDITS_DEFAULT)
  367                                 break;
  368                 }
  369 
  370                 if (dlc->rd_txbuf == NULL)
  371                         len = 0;
  372 
  373                 if (len == 0) {
  374                         if (credits == 0)
  375                                 break;
  376 
  377                         /*
  378                          * No need to send small numbers of credits on their
  379                          * own unless the other end hasn't many left.
  380                          */
  381                         if (credits < RFCOMM_CREDITS_DEFAULT
  382                             && dlc->rd_rxcred > RFCOMM_CREDITS_DEFAULT)
  383                                 break;
  384 
  385                         m = NULL;
  386                 } else {
  387                         /*
  388                          * take what data we can from (front of) txbuf
  389                          */
  390                         m = dlc->rd_txbuf;
  391                         if (len < m->m_pkthdr.len) {
  392                                 dlc->rd_txbuf = m_split(m, len, M_DONTWAIT);
  393                                 if (dlc->rd_txbuf == NULL) {
  394                                         dlc->rd_txbuf = m;
  395                                         break;
  396                                 }
  397                         } else {
  398                                 dlc->rd_txbuf = NULL;
  399                                 len = m->m_pkthdr.len;
  400                         }
  401                 }
  402 
  403                 DPRINTFN(10, "dlci %d send %d bytes, %d credits, rxcred = %d\n",
  404                         dlc->rd_dlci, len, credits, dlc->rd_rxcred);
  405 
  406                 if (rfcomm_session_send_uih(rs, dlc, credits, m)) {
  407                         printf("%s: lost %d bytes on DLCI %d\n",
  408                                 __func__, len, dlc->rd_dlci);
  409 
  410                         break;
  411                 }
  412 
  413                 dlc->rd_pending++;
  414 
  415                 if (rs->rs_flags & RFCOMM_SESSION_CFC) {
  416                         if (len > 0)
  417                                 dlc->rd_txcred--;
  418 
  419                         if (credits > 0)
  420                                 dlc->rd_rxcred += credits;
  421                 }
  422         }
  423 }

Cache object: b5c9269ca0e66d56107cc833831d81c4


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