1*83aacedeSHasso Tepper /* $OpenBSD: src/sys/netbt/l2cap_misc.c,v 1.3 2008/02/24 21:34:48 uwe Exp $ */
2*83aacedeSHasso Tepper /* $NetBSD: l2cap_misc.c,v 1.5 2007/11/03 17:20:17 plunky Exp $ */
30a9108ebSHasso Tepper
40a9108ebSHasso Tepper /*-
50a9108ebSHasso Tepper * Copyright (c) 2005 Iain Hibbert.
60a9108ebSHasso Tepper * Copyright (c) 2006 Itronix Inc.
70a9108ebSHasso Tepper * All rights reserved.
80a9108ebSHasso Tepper *
90a9108ebSHasso Tepper * Redistribution and use in source and binary forms, with or without
100a9108ebSHasso Tepper * modification, are permitted provided that the following conditions
110a9108ebSHasso Tepper * are met:
120a9108ebSHasso Tepper * 1. Redistributions of source code must retain the above copyright
130a9108ebSHasso Tepper * notice, this list of conditions and the following disclaimer.
140a9108ebSHasso Tepper * 2. Redistributions in binary form must reproduce the above copyright
150a9108ebSHasso Tepper * notice, this list of conditions and the following disclaimer in the
160a9108ebSHasso Tepper * documentation and/or other materials provided with the distribution.
170a9108ebSHasso Tepper * 3. The name of Itronix Inc. may not be used to endorse
180a9108ebSHasso Tepper * or promote products derived from this software without specific
190a9108ebSHasso Tepper * prior written permission.
200a9108ebSHasso Tepper *
210a9108ebSHasso Tepper * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
220a9108ebSHasso Tepper * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
230a9108ebSHasso Tepper * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
240a9108ebSHasso Tepper * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
250a9108ebSHasso Tepper * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
260a9108ebSHasso Tepper * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
270a9108ebSHasso Tepper * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
280a9108ebSHasso Tepper * ON ANY THEORY OF LIABILITY, WHETHER IN
290a9108ebSHasso Tepper * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
300a9108ebSHasso Tepper * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
310a9108ebSHasso Tepper * POSSIBILITY OF SUCH DAMAGE.
320a9108ebSHasso Tepper */
330a9108ebSHasso Tepper
340a9108ebSHasso Tepper #include <sys/param.h>
350a9108ebSHasso Tepper #include <sys/kernel.h>
360a9108ebSHasso Tepper #include <sys/mbuf.h>
370a9108ebSHasso Tepper #include <sys/proc.h>
380a9108ebSHasso Tepper #include <sys/queue.h>
390a9108ebSHasso Tepper #include <sys/systm.h>
400a9108ebSHasso Tepper #include <net/if.h>
410a9108ebSHasso Tepper
420a9108ebSHasso Tepper #include <netbt/bluetooth.h>
430a9108ebSHasso Tepper #include <netbt/hci.h>
440a9108ebSHasso Tepper #include <netbt/l2cap.h>
450a9108ebSHasso Tepper
460a9108ebSHasso Tepper struct l2cap_channel_list
470a9108ebSHasso Tepper l2cap_active_list = LIST_HEAD_INITIALIZER(l2cap_active_list);
480a9108ebSHasso Tepper struct l2cap_channel_list
490a9108ebSHasso Tepper l2cap_listen_list = LIST_HEAD_INITIALIZER(l2cap_listen_list);
500a9108ebSHasso Tepper
510a9108ebSHasso Tepper vm_zone_t l2cap_req_pool;
520a9108ebSHasso Tepper vm_zone_t l2cap_pdu_pool;
530a9108ebSHasso Tepper
540a9108ebSHasso Tepper const l2cap_qos_t l2cap_default_qos = {
550a9108ebSHasso Tepper 0, /* flags */
560a9108ebSHasso Tepper L2CAP_QOS_BEST_EFFORT, /* service type */
570a9108ebSHasso Tepper 0x00000000, /* token rate */
580a9108ebSHasso Tepper 0x00000000, /* token bucket size */
590a9108ebSHasso Tepper 0x00000000, /* peak bandwidth */
600a9108ebSHasso Tepper 0xffffffff, /* latency */
610a9108ebSHasso Tepper 0xffffffff /* delay variation */
620a9108ebSHasso Tepper };
630a9108ebSHasso Tepper
640a9108ebSHasso Tepper /*
650a9108ebSHasso Tepper * L2CAP request timeouts
660a9108ebSHasso Tepper */
670a9108ebSHasso Tepper int l2cap_response_timeout = 30; /* seconds */
680a9108ebSHasso Tepper int l2cap_response_extended_timeout = 180; /* seconds */
690a9108ebSHasso Tepper
700a9108ebSHasso Tepper void
l2cap_init(void)710a9108ebSHasso Tepper l2cap_init(void)
720a9108ebSHasso Tepper {
730a9108ebSHasso Tepper }
740a9108ebSHasso Tepper
750a9108ebSHasso Tepper /*
760a9108ebSHasso Tepper * Set Link Mode on channel
770a9108ebSHasso Tepper */
780a9108ebSHasso Tepper int
l2cap_setmode(struct l2cap_channel * chan)790a9108ebSHasso Tepper l2cap_setmode(struct l2cap_channel *chan)
800a9108ebSHasso Tepper {
810a9108ebSHasso Tepper KKASSERT(chan != NULL);
820a9108ebSHasso Tepper KKASSERT(chan->lc_link != NULL);
830a9108ebSHasso Tepper
84*83aacedeSHasso Tepper DPRINTF("(%s) CID #%d, auth %s, encrypt %s, secure %s\n",
85*83aacedeSHasso Tepper device_get_nameunit(chan->lc_link->hl_unit->hci_dev), chan->lc_lcid,
860a9108ebSHasso Tepper (chan->lc_mode & L2CAP_LM_AUTH ? "yes" : "no"),
870a9108ebSHasso Tepper (chan->lc_mode & L2CAP_LM_ENCRYPT ? "yes" : "no"),
880a9108ebSHasso Tepper (chan->lc_mode & L2CAP_LM_SECURE ? "yes" : "no"));
890a9108ebSHasso Tepper
900a9108ebSHasso Tepper if (chan->lc_mode & L2CAP_LM_AUTH)
910a9108ebSHasso Tepper chan->lc_link->hl_flags |= HCI_LINK_AUTH_REQ;
920a9108ebSHasso Tepper
930a9108ebSHasso Tepper if (chan->lc_mode & L2CAP_LM_ENCRYPT)
940a9108ebSHasso Tepper chan->lc_link->hl_flags |= HCI_LINK_ENCRYPT_REQ;
950a9108ebSHasso Tepper
960a9108ebSHasso Tepper if (chan->lc_mode & L2CAP_LM_SECURE)
970a9108ebSHasso Tepper chan->lc_link->hl_flags |= HCI_LINK_SECURE_REQ;
980a9108ebSHasso Tepper
990a9108ebSHasso Tepper return hci_acl_setmode(chan->lc_link);
1000a9108ebSHasso Tepper }
1010a9108ebSHasso Tepper
1020a9108ebSHasso Tepper /*
1030a9108ebSHasso Tepper * Allocate a new Request structure & ID and set the timer going
1040a9108ebSHasso Tepper */
1050a9108ebSHasso Tepper int
l2cap_request_alloc(struct l2cap_channel * chan,uint8_t code)1060a9108ebSHasso Tepper l2cap_request_alloc(struct l2cap_channel *chan, uint8_t code)
1070a9108ebSHasso Tepper {
1080a9108ebSHasso Tepper struct hci_link *link = chan->lc_link;
1090a9108ebSHasso Tepper struct l2cap_req *req;
1100a9108ebSHasso Tepper int next_id;
1110a9108ebSHasso Tepper
1120a9108ebSHasso Tepper if (link == NULL)
1130a9108ebSHasso Tepper return ENETDOWN;
1140a9108ebSHasso Tepper
1150a9108ebSHasso Tepper /* find next ID (0 is not allowed) */
1160a9108ebSHasso Tepper next_id = link->hl_lastid + 1;
1170a9108ebSHasso Tepper if (next_id > 0xff)
1180a9108ebSHasso Tepper next_id = 1;
1190a9108ebSHasso Tepper
1200a9108ebSHasso Tepper /* Ouroboros check */
1210a9108ebSHasso Tepper req = TAILQ_FIRST(&link->hl_reqs);
1220a9108ebSHasso Tepper if (req && req->lr_id == next_id)
1230a9108ebSHasso Tepper return ENFILE;
1240a9108ebSHasso Tepper
1255179415aSSascha Wildner req = zalloc(l2cap_req_pool);
1260a9108ebSHasso Tepper if (req == NULL)
1270a9108ebSHasso Tepper return ENOMEM;
1280a9108ebSHasso Tepper
1290a9108ebSHasso Tepper req->lr_id = link->hl_lastid = next_id;
1300a9108ebSHasso Tepper
1310a9108ebSHasso Tepper req->lr_code = code;
1320a9108ebSHasso Tepper req->lr_chan = chan;
1330a9108ebSHasso Tepper req->lr_link = link;
1340a9108ebSHasso Tepper
1350a9108ebSHasso Tepper callout_init(&req->lr_rtx);
1360a9108ebSHasso Tepper callout_reset(&req->lr_rtx, l2cap_response_timeout * hz,
1370a9108ebSHasso Tepper l2cap_rtx, req);
1380a9108ebSHasso Tepper
1390a9108ebSHasso Tepper TAILQ_INSERT_TAIL(&link->hl_reqs, req, lr_next);
1400a9108ebSHasso Tepper
1410a9108ebSHasso Tepper return 0;
1420a9108ebSHasso Tepper }
1430a9108ebSHasso Tepper
1440a9108ebSHasso Tepper /*
1450a9108ebSHasso Tepper * Find a running request for this link
1460a9108ebSHasso Tepper */
1470a9108ebSHasso Tepper struct l2cap_req *
l2cap_request_lookup(struct hci_link * link,uint8_t id)1480a9108ebSHasso Tepper l2cap_request_lookup(struct hci_link *link, uint8_t id)
1490a9108ebSHasso Tepper {
1500a9108ebSHasso Tepper struct l2cap_req *req;
1510a9108ebSHasso Tepper
1520a9108ebSHasso Tepper TAILQ_FOREACH(req, &link->hl_reqs, lr_next) {
1530a9108ebSHasso Tepper if (req->lr_id == id)
1540a9108ebSHasso Tepper return req;
1550a9108ebSHasso Tepper }
1560a9108ebSHasso Tepper
1570a9108ebSHasso Tepper return NULL;
1580a9108ebSHasso Tepper }
1590a9108ebSHasso Tepper
1600a9108ebSHasso Tepper /*
1610a9108ebSHasso Tepper * Halt and free a request
1620a9108ebSHasso Tepper */
1630a9108ebSHasso Tepper void
l2cap_request_free(struct l2cap_req * req)1640a9108ebSHasso Tepper l2cap_request_free(struct l2cap_req *req)
1650a9108ebSHasso Tepper {
1660a9108ebSHasso Tepper struct hci_link *link = req->lr_link;
1670a9108ebSHasso Tepper
1680a9108ebSHasso Tepper callout_stop(&req->lr_rtx);
1690a9108ebSHasso Tepper if (callout_active(&req->lr_rtx))
1700a9108ebSHasso Tepper return;
1710a9108ebSHasso Tepper
1720a9108ebSHasso Tepper TAILQ_REMOVE(&link->hl_reqs, req, lr_next);
1735179415aSSascha Wildner zfree(l2cap_req_pool, req);
1740a9108ebSHasso Tepper }
1750a9108ebSHasso Tepper
1760a9108ebSHasso Tepper /*
1770a9108ebSHasso Tepper * Response Timeout eXpired
1780a9108ebSHasso Tepper *
1790a9108ebSHasso Tepper * No response to our request, so deal with it as best we can.
1800a9108ebSHasso Tepper *
1810a9108ebSHasso Tepper * XXX should try again at least with ertx?
1820a9108ebSHasso Tepper */
1830a9108ebSHasso Tepper void
l2cap_rtx(void * arg)1840a9108ebSHasso Tepper l2cap_rtx(void *arg)
1850a9108ebSHasso Tepper {
1860a9108ebSHasso Tepper struct l2cap_req *req = arg;
1870a9108ebSHasso Tepper struct l2cap_channel *chan;
1880a9108ebSHasso Tepper
1890a9108ebSHasso Tepper chan = req->lr_chan;
1900a9108ebSHasso Tepper l2cap_request_free(req);
1910a9108ebSHasso Tepper
1920a9108ebSHasso Tepper DPRINTF("cid %d, ident %d\n", (chan ? chan->lc_lcid : 0), req->lr_id);
1930a9108ebSHasso Tepper
1940a9108ebSHasso Tepper if (chan && chan->lc_state != L2CAP_CLOSED)
1950a9108ebSHasso Tepper l2cap_close(chan, ETIMEDOUT);
1960a9108ebSHasso Tepper
1970a9108ebSHasso Tepper }
1980a9108ebSHasso Tepper
1990a9108ebSHasso Tepper /*
2000a9108ebSHasso Tepper * Allocate next available CID to channel. We keep a single
2010a9108ebSHasso Tepper * ordered list of channels, so find the first gap.
2020a9108ebSHasso Tepper *
2030a9108ebSHasso Tepper * If this turns out to be not enough (!), could use a
2040a9108ebSHasso Tepper * list per HCI unit..
2050a9108ebSHasso Tepper */
2060a9108ebSHasso Tepper int
l2cap_cid_alloc(struct l2cap_channel * chan)2070a9108ebSHasso Tepper l2cap_cid_alloc(struct l2cap_channel *chan)
2080a9108ebSHasso Tepper {
2090a9108ebSHasso Tepper struct l2cap_channel *used, *prev = NULL;
2100a9108ebSHasso Tepper uint16_t cid = L2CAP_FIRST_CID;
2110a9108ebSHasso Tepper
2120a9108ebSHasso Tepper if (chan->lc_lcid != L2CAP_NULL_CID || chan->lc_state != L2CAP_CLOSED)
2130a9108ebSHasso Tepper return EISCONN;
2140a9108ebSHasso Tepper
2150a9108ebSHasso Tepper LIST_FOREACH(used, &l2cap_active_list, lc_ncid) {
2160a9108ebSHasso Tepper if (used->lc_lcid > cid)
2170a9108ebSHasso Tepper break; /* found our gap */
2180a9108ebSHasso Tepper
2190a9108ebSHasso Tepper KKASSERT(used->lc_lcid == cid);
2200a9108ebSHasso Tepper cid++;
2210a9108ebSHasso Tepper
2220a9108ebSHasso Tepper if (cid == L2CAP_LAST_CID)
2230a9108ebSHasso Tepper return ENFILE;
2240a9108ebSHasso Tepper
2250a9108ebSHasso Tepper prev = used; /* for insert after */
2260a9108ebSHasso Tepper }
2270a9108ebSHasso Tepper
2280a9108ebSHasso Tepper chan->lc_lcid = cid;
2290a9108ebSHasso Tepper
2300a9108ebSHasso Tepper if (prev)
2310a9108ebSHasso Tepper LIST_INSERT_AFTER(prev, chan, lc_ncid);
2320a9108ebSHasso Tepper else
2330a9108ebSHasso Tepper LIST_INSERT_HEAD(&l2cap_active_list, chan, lc_ncid);
2340a9108ebSHasso Tepper
2350a9108ebSHasso Tepper return 0;
2360a9108ebSHasso Tepper }
2370a9108ebSHasso Tepper
2380a9108ebSHasso Tepper /*
2390a9108ebSHasso Tepper * Find channel with CID
2400a9108ebSHasso Tepper */
2410a9108ebSHasso Tepper struct l2cap_channel *
l2cap_cid_lookup(uint16_t cid)2420a9108ebSHasso Tepper l2cap_cid_lookup(uint16_t cid)
2430a9108ebSHasso Tepper {
2440a9108ebSHasso Tepper struct l2cap_channel *chan;
2450a9108ebSHasso Tepper
2460a9108ebSHasso Tepper LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) {
2470a9108ebSHasso Tepper if (chan->lc_lcid == cid)
2480a9108ebSHasso Tepper return chan;
2490a9108ebSHasso Tepper
2500a9108ebSHasso Tepper if (chan->lc_lcid > cid)
2510a9108ebSHasso Tepper return NULL;
2520a9108ebSHasso Tepper }
2530a9108ebSHasso Tepper
2540a9108ebSHasso Tepper return NULL;
2550a9108ebSHasso Tepper }
256