root/netbt/l2cap_upper.c

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

DEFINITIONS

This source file includes following definitions.
  1. l2cap_attach
  2. l2cap_bind
  3. l2cap_sockaddr
  4. l2cap_connect
  5. l2cap_peeraddr
  6. l2cap_disconnect
  7. l2cap_detach
  8. l2cap_listen
  9. l2cap_send
  10. l2cap_setopt
  11. l2cap_getopt

    1 /*      $OpenBSD: l2cap_upper.c,v 1.1 2007/06/01 02:46:11 uwe Exp $     */
    2 /*      $NetBSD: l2cap_upper.c,v 1.8 2007/04/29 20:23:36 msaitoh Exp $  */
    3 
    4 /*-
    5  * Copyright (c) 2005 Iain Hibbert.
    6  * Copyright (c) 2006 Itronix Inc.
    7  * All rights reserved.
    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 
   36 #include <sys/param.h>
   37 #include <sys/kernel.h>
   38 #include <sys/mbuf.h>
   39 #include <sys/proc.h>
   40 #include <sys/queue.h>
   41 #include <sys/socket.h>
   42 #include <sys/socketvar.h>
   43 #include <sys/systm.h>
   44 
   45 #include <netbt/bluetooth.h>
   46 #include <netbt/hci.h>
   47 #include <netbt/l2cap.h>
   48 
   49 /*******************************************************************************
   50  *
   51  *      L2CAP Channel - Upper Protocol API
   52  */
   53 
   54 /*
   55  * l2cap_attach(handle, btproto, upper)
   56  *
   57  *      attach new l2cap_channel to handle, populate
   58  *      with reasonable defaults
   59  */
   60 int
   61 l2cap_attach(struct l2cap_channel **handle,
   62                 const struct btproto *proto, void *upper)
   63 {
   64         struct l2cap_channel *chan;
   65 
   66         KASSERT(handle != NULL);
   67         KASSERT(proto != NULL);
   68         KASSERT(upper != NULL);
   69 
   70         chan = malloc(sizeof(struct l2cap_channel), M_BLUETOOTH,
   71                         M_NOWAIT);
   72         if (chan == NULL)
   73                 return ENOMEM;
   74         bzero(chan, sizeof *chan);
   75 
   76         chan->lc_proto = proto;
   77         chan->lc_upper = upper;
   78 
   79         chan->lc_state = L2CAP_CLOSED;
   80 
   81         chan->lc_lcid = L2CAP_NULL_CID;
   82         chan->lc_rcid = L2CAP_NULL_CID;
   83 
   84         chan->lc_laddr.bt_len = sizeof(struct sockaddr_bt);
   85         chan->lc_laddr.bt_family = AF_BLUETOOTH;
   86         chan->lc_laddr.bt_psm = L2CAP_PSM_ANY;
   87 
   88         chan->lc_raddr.bt_len = sizeof(struct sockaddr_bt);
   89         chan->lc_raddr.bt_family = AF_BLUETOOTH;
   90         chan->lc_raddr.bt_psm = L2CAP_PSM_ANY;
   91 
   92         chan->lc_imtu = L2CAP_MTU_DEFAULT;
   93         chan->lc_omtu = L2CAP_MTU_DEFAULT;
   94         chan->lc_flush = L2CAP_FLUSH_TIMO_DEFAULT;
   95 
   96         memcpy(&chan->lc_iqos, &l2cap_default_qos, sizeof(l2cap_qos_t));
   97         memcpy(&chan->lc_oqos, &l2cap_default_qos, sizeof(l2cap_qos_t));
   98 
   99         *handle = chan;
  100         return 0;
  101 }
  102 
  103 /*
  104  * l2cap_bind(l2cap_channel, sockaddr)
  105  *
  106  *      set local address of channel
  107  */
  108 int
  109 l2cap_bind(struct l2cap_channel *chan, struct sockaddr_bt *addr)
  110 {
  111 
  112         memcpy(&chan->lc_laddr, addr, sizeof(struct sockaddr_bt));
  113         return 0;
  114 }
  115 
  116 /*
  117  * l2cap_sockaddr(l2cap_channel, sockaddr)
  118  *
  119  *      get local address of channel
  120  */
  121 int
  122 l2cap_sockaddr(struct l2cap_channel *chan, struct sockaddr_bt *addr)
  123 {
  124 
  125         memcpy(addr, &chan->lc_laddr, sizeof(struct sockaddr_bt));
  126         return 0;
  127 }
  128 
  129 /*
  130  * l2cap_connect(l2cap_channel, sockaddr)
  131  *
  132  *      Initiate a connection to destination. This corresponds to
  133  *      "Open Channel Request" in the L2CAP specification and will
  134  *      result in one of the following:
  135  *
  136  *              proto->connected(upper)
  137  *              proto->disconnected(upper, error)
  138  *
  139  *      and, optionally
  140  *              proto->connecting(upper)
  141  */
  142 int
  143 l2cap_connect(struct l2cap_channel *chan, struct sockaddr_bt *dest)
  144 {
  145         struct hci_unit *unit;
  146         int err;
  147 
  148         memcpy(&chan->lc_raddr, dest, sizeof(struct sockaddr_bt));
  149 
  150         if (L2CAP_PSM_INVALID(chan->lc_raddr.bt_psm))
  151                 return EINVAL;
  152 
  153         if (bdaddr_any(&chan->lc_raddr.bt_bdaddr))
  154                 return EDESTADDRREQ;
  155 
  156         /* set local address if it needs setting */
  157         if (bdaddr_any(&chan->lc_laddr.bt_bdaddr)) {
  158                 err = hci_route_lookup(&chan->lc_laddr.bt_bdaddr,
  159                                         &chan->lc_raddr.bt_bdaddr);
  160                 if (err)
  161                         return err;
  162         }
  163 
  164         unit = hci_unit_lookup(&chan->lc_laddr.bt_bdaddr);
  165         if (unit == NULL)
  166                 return EHOSTUNREACH;
  167 
  168         /* attach to active list */
  169         err = l2cap_cid_alloc(chan);
  170         if (err)
  171                 return err;
  172 
  173         /* open link to remote device */
  174         chan->lc_link = hci_acl_open(unit, &chan->lc_raddr.bt_bdaddr);
  175         if (chan->lc_link == NULL)
  176                 return EHOSTUNREACH;
  177 
  178         /* set the link mode */
  179         err = l2cap_setmode(chan);
  180         if (err == EINPROGRESS) {
  181                 chan->lc_state = L2CAP_WAIT_SEND_CONNECT_REQ;
  182                 (*chan->lc_proto->connecting)(chan->lc_upper);
  183                 return 0;
  184         }
  185         if (err)
  186                 goto fail;
  187 
  188         /*
  189          * We can queue a connect request now even though the link may
  190          * not yet be open; Our mode setting is assured, and the queue
  191          * will be started automatically at the right time.
  192          */
  193         chan->lc_state = L2CAP_WAIT_RECV_CONNECT_RSP;
  194         err = l2cap_send_connect_req(chan);
  195         if (err)
  196                 goto fail;
  197 
  198         return 0;
  199 
  200 fail:
  201         chan->lc_state = L2CAP_CLOSED;
  202         hci_acl_close(chan->lc_link, err);
  203         chan->lc_link = NULL;
  204         return err;
  205 }
  206 
  207 /*
  208  * l2cap_peeraddr(l2cap_channel, sockaddr)
  209  *
  210  *      get remote address of channel
  211  */
  212 int
  213 l2cap_peeraddr(struct l2cap_channel *chan, struct sockaddr_bt *addr)
  214 {
  215 
  216         memcpy(addr, &chan->lc_raddr, sizeof(struct sockaddr_bt));
  217         return 0;
  218 }
  219 
  220 /*
  221  * l2cap_disconnect(l2cap_channel, linger)
  222  *
  223  *      Initiate L2CAP disconnection. This corresponds to
  224  *      "Close Channel Request" in the L2CAP specification
  225  *      and will result in a call to
  226  *
  227  *              proto->disconnected(upper, error)
  228  *
  229  *      when the disconnection is complete. If linger is set,
  230  *      the call will not be made until data has flushed from
  231  *      the queue.
  232  */
  233 int
  234 l2cap_disconnect(struct l2cap_channel *chan, int linger)
  235 {
  236         int err = 0;
  237 
  238         if (chan->lc_state == L2CAP_CLOSED
  239             || chan->lc_state == L2CAP_WAIT_DISCONNECT)
  240                 return EINVAL;
  241 
  242         chan->lc_flags |= L2CAP_SHUTDOWN;
  243 
  244         /*
  245          * no need to do anything unless the queue is empty or
  246          * we are not lingering..
  247          */
  248         if ((IF_IS_EMPTY(&chan->lc_txq) && chan->lc_pending == 0)
  249             || linger == 0) {
  250                 chan->lc_state = L2CAP_WAIT_DISCONNECT;
  251                 err = l2cap_send_disconnect_req(chan);
  252                 if (err)
  253                         l2cap_close(chan, err);
  254         }
  255         return err;
  256 }
  257 
  258 /*
  259  * l2cap_detach(handle)
  260  *
  261  *      Detach l2cap channel from handle & close it down
  262  */
  263 int
  264 l2cap_detach(struct l2cap_channel **handle)
  265 {
  266         struct l2cap_channel *chan;
  267 
  268         chan = *handle;
  269         *handle = NULL;
  270 
  271         if (chan->lc_state != L2CAP_CLOSED)
  272                 l2cap_close(chan, 0);
  273 
  274         if (chan->lc_lcid != L2CAP_NULL_CID) {
  275                 LIST_REMOVE(chan, lc_ncid);
  276                 chan->lc_lcid = L2CAP_NULL_CID;
  277         }
  278 
  279         IF_PURGE(&chan->lc_txq);
  280 
  281         /*
  282          * Could implement some kind of delayed expunge to make sure that the
  283          * CID is really dead before it becomes available for reuse?
  284          */
  285 
  286         free(chan, M_BLUETOOTH);
  287         return 0;
  288 }
  289 
  290 /*
  291  * l2cap_listen(l2cap_channel)
  292  *
  293  *      Use this channel as a listening post (until detached). This will
  294  *      result in calls to:
  295  *
  296  *              proto->newconn(upper, laddr, raddr)
  297  *
  298  *      for incoming connections matching the psm and local address of the
  299  *      channel (NULL psm/address are permitted and match any protocol/device).
  300  *
  301  *      The upper layer should create and return a new channel.
  302  *
  303  *      You cannot use this channel for anything else subsequent to this call
  304  */
  305 int
  306 l2cap_listen(struct l2cap_channel *chan)
  307 {
  308         struct l2cap_channel *used, *prev = NULL;
  309 
  310         if (chan->lc_lcid != L2CAP_NULL_CID)
  311                 return EINVAL;
  312 
  313         if (chan->lc_laddr.bt_psm != L2CAP_PSM_ANY
  314             && L2CAP_PSM_INVALID(chan->lc_laddr.bt_psm))
  315                 return EADDRNOTAVAIL;
  316 
  317         /*
  318          * This CID is irrelevant, as the channel is not stored on the active
  319          * list and the socket code does not allow operations on listening
  320          * sockets, but we set it so the detach code knows to LIST_REMOVE the
  321          * channel.
  322          */
  323         chan->lc_lcid = L2CAP_SIGNAL_CID;
  324 
  325         /*
  326          * The list of listening channels is stored in an order such that new
  327          * listeners dont usurp current listeners, but that specific listening
  328          * takes precedence over promiscuous, and the connect request code can
  329          * easily use the first matching entry.
  330          */
  331         LIST_FOREACH(used, &l2cap_listen_list, lc_ncid) {
  332                 if (used->lc_laddr.bt_psm < chan->lc_laddr.bt_psm)
  333                         break;
  334 
  335                 if (used->lc_laddr.bt_psm == chan->lc_laddr.bt_psm
  336                         && bdaddr_any(&used->lc_laddr.bt_bdaddr)
  337                         && !bdaddr_any(&chan->lc_laddr.bt_bdaddr))
  338                         break;
  339 
  340                 prev = used;
  341         }
  342 
  343         if (prev == NULL)
  344                 LIST_INSERT_HEAD(&l2cap_listen_list, chan, lc_ncid);
  345         else
  346                 LIST_INSERT_AFTER(prev, chan, lc_ncid);
  347 
  348         return 0;
  349 }
  350 
  351 /*
  352  * l2cap_send(l2cap_channel, mbuf)
  353  *
  354  *      Output SDU on channel described by channel. This corresponds
  355  *      to "Send Data Request" in the L2CAP specification. The upper
  356  *      layer will be notified when SDU's have completed sending by a
  357  *      call to:
  358  *
  359  *              proto->complete(upper, n)
  360  *
  361  *      (currently n == 1)
  362  *
  363  *      Note: I'm not sure how this will work out, but I think that
  364  *      if outgoing Retransmission Mode or Flow Control Mode is
  365  *      negotiated then this call will not be made until the SDU has
  366  *      been acknowleged by the peer L2CAP entity. For 'Best Effort'
  367  *      it will be made when the packet has cleared the controller
  368  *      buffers.
  369  *
  370  *      We only support Basic mode so far, so encapsulate with a
  371  *      B-Frame header and start sending if we are not already
  372  */
  373 int
  374 l2cap_send(struct l2cap_channel *chan, struct mbuf *m)
  375 {
  376         l2cap_hdr_t *hdr;
  377         int plen;
  378 
  379         if (chan->lc_state == L2CAP_CLOSED) {
  380                 m_freem(m);
  381                 return ENOTCONN;
  382         }
  383 
  384         plen = m->m_pkthdr.len;
  385 
  386         DPRINTFN(5, "send %d bytes on CID #%d (pending = %d)\n",
  387                 plen, chan->lc_lcid, chan->lc_pending);
  388 
  389         /* Encapsulate with B-Frame */
  390         M_PREPEND(m, sizeof(l2cap_hdr_t), M_DONTWAIT);
  391         if (m == NULL)
  392                 return ENOMEM;
  393 
  394         hdr = mtod(m, l2cap_hdr_t *);
  395         hdr->length = htole16(plen);
  396         hdr->dcid = htole16(chan->lc_rcid);
  397 
  398         /* Queue it on our list */
  399         IF_ENQUEUE(&chan->lc_txq, m);
  400 
  401         /* If we are not sending, then start doing so */
  402         if (chan->lc_pending == 0)
  403                 return l2cap_start(chan);
  404 
  405         return 0;
  406 }
  407 
  408 /*
  409  * l2cap_setopt(l2cap_channel, opt, addr)
  410  *
  411  *      Apply configuration options to channel. This corresponds to
  412  *      "Configure Channel Request" in the L2CAP specification.
  413  *
  414  *      for SO_L2CAP_LM, the settings will take effect when the
  415  *      channel is established. If the channel is already open,
  416  *      a call to
  417  *              proto->linkmode(upper, new)
  418  *
  419  *      will be made when the change is complete.
  420  */
  421 int
  422 l2cap_setopt(struct l2cap_channel *chan, int opt, void *addr)
  423 {
  424         int mode, err = 0;
  425         uint16_t mtu;
  426 
  427         switch (opt) {
  428         case SO_L2CAP_IMTU:     /* set Incoming MTU */
  429                 mtu = *(uint16_t *)addr;
  430                 if (mtu < L2CAP_MTU_MINIMUM)
  431                         err = EINVAL;
  432                 else if (chan->lc_state == L2CAP_CLOSED)
  433                         chan->lc_imtu = mtu;
  434                 else
  435                         err = EBUSY;
  436 
  437                 break;
  438 
  439         case SO_L2CAP_LM:       /* set link mode */
  440                 mode = *(int *)addr;
  441                 mode &= (L2CAP_LM_SECURE | L2CAP_LM_ENCRYPT | L2CAP_LM_AUTH);
  442 
  443                 if (mode & L2CAP_LM_SECURE)
  444                         mode |= L2CAP_LM_ENCRYPT;
  445 
  446                 if (mode & L2CAP_LM_ENCRYPT)
  447                         mode |= L2CAP_LM_AUTH;
  448 
  449                 chan->lc_mode = mode;
  450 
  451                 if (chan->lc_state == L2CAP_OPEN)
  452                         err = l2cap_setmode(chan);
  453 
  454                 break;
  455 
  456         case SO_L2CAP_OQOS:     /* set Outgoing QoS flow spec */
  457         case SO_L2CAP_FLUSH:    /* set Outgoing Flush Timeout */
  458         default:
  459                 err = ENOPROTOOPT;
  460                 break;
  461         }
  462 
  463         return err;
  464 }
  465 
  466 /*
  467  * l2cap_getopt(l2cap_channel, opt, addr)
  468  *
  469  *      Return configuration parameters.
  470  */
  471 int
  472 l2cap_getopt(struct l2cap_channel *chan, int opt, void *addr)
  473 {
  474 
  475         switch (opt) {
  476         case SO_L2CAP_IMTU:     /* get Incoming MTU */
  477                 *(uint16_t *)addr = chan->lc_imtu;
  478                 return sizeof(uint16_t);
  479 
  480         case SO_L2CAP_OMTU:     /* get Outgoing MTU */
  481                 *(uint16_t *)addr = chan->lc_omtu;
  482                 return sizeof(uint16_t);
  483 
  484         case SO_L2CAP_IQOS:     /* get Incoming QoS flow spec */
  485                 memcpy(addr, &chan->lc_iqos, sizeof(l2cap_qos_t));
  486                 return sizeof(l2cap_qos_t);
  487 
  488         case SO_L2CAP_OQOS:     /* get Outgoing QoS flow spec */
  489                 memcpy(addr, &chan->lc_oqos, sizeof(l2cap_qos_t));
  490                 return sizeof(l2cap_qos_t);
  491 
  492         case SO_L2CAP_FLUSH:    /* get Flush Timeout */
  493                 *(uint16_t *)addr = chan->lc_flush;
  494                 return sizeof(uint16_t);
  495 
  496         case SO_L2CAP_LM:       /* get link mode */
  497                 *(int *)addr = chan->lc_mode;
  498                 return sizeof(int);
  499 
  500         default:
  501                 break;
  502         }
  503 
  504         return 0;
  505 }

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