Annotation of sys/netbt/l2cap_upper.c, Revision 1.1
1.1 ! nbrk 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: }
CVSweb