root/netbt/rfcomm_dlc.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. rfcomm_dlc_lookup
  2. rfcomm_dlc_newconn
  3. rfcomm_dlc_close
  4. rfcomm_dlc_timeout
  5. rfcomm_dlc_setmode
  6. rfcomm_dlc_connect
  7. rfcomm_dlc_open
  8. rfcomm_dlc_start

    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 }

/* [<][>][^][v][top][bottom][index][help] */