Home | History | Annotate | Line # | Download | only in netbt
l2cap_misc.c revision 1.4.6.1
      1  1.4.6.1  jmcneill /*	$NetBSD: l2cap_misc.c,v 1.4.6.1 2007/11/04 21:03:37 jmcneill Exp $	*/
      2      1.1   gdamore 
      3      1.1   gdamore /*-
      4      1.1   gdamore  * Copyright (c) 2005 Iain Hibbert.
      5      1.1   gdamore  * Copyright (c) 2006 Itronix Inc.
      6      1.1   gdamore  * All rights reserved.
      7      1.1   gdamore  *
      8      1.1   gdamore  * Redistribution and use in source and binary forms, with or without
      9      1.1   gdamore  * modification, are permitted provided that the following conditions
     10      1.1   gdamore  * are met:
     11      1.1   gdamore  * 1. Redistributions of source code must retain the above copyright
     12      1.1   gdamore  *    notice, this list of conditions and the following disclaimer.
     13      1.1   gdamore  * 2. Redistributions in binary form must reproduce the above copyright
     14      1.1   gdamore  *    notice, this list of conditions and the following disclaimer in the
     15      1.1   gdamore  *    documentation and/or other materials provided with the distribution.
     16      1.1   gdamore  * 3. The name of Itronix Inc. may not be used to endorse
     17      1.1   gdamore  *    or promote products derived from this software without specific
     18      1.1   gdamore  *    prior written permission.
     19      1.1   gdamore  *
     20      1.1   gdamore  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
     21      1.1   gdamore  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22      1.1   gdamore  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23      1.1   gdamore  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
     24      1.1   gdamore  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     25      1.1   gdamore  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     26      1.1   gdamore  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     27      1.1   gdamore  * ON ANY THEORY OF LIABILITY, WHETHER IN
     28      1.1   gdamore  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     29      1.1   gdamore  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     30      1.1   gdamore  * POSSIBILITY OF SUCH DAMAGE.
     31      1.1   gdamore  */
     32      1.1   gdamore 
     33      1.1   gdamore #include <sys/cdefs.h>
     34  1.4.6.1  jmcneill __KERNEL_RCSID(0, "$NetBSD: l2cap_misc.c,v 1.4.6.1 2007/11/04 21:03:37 jmcneill Exp $");
     35      1.1   gdamore 
     36      1.1   gdamore #include <sys/param.h>
     37      1.1   gdamore #include <sys/kernel.h>
     38      1.1   gdamore #include <sys/mbuf.h>
     39      1.1   gdamore #include <sys/proc.h>
     40      1.1   gdamore #include <sys/queue.h>
     41      1.1   gdamore #include <sys/systm.h>
     42      1.1   gdamore 
     43      1.1   gdamore #include <netbt/bluetooth.h>
     44      1.1   gdamore #include <netbt/hci.h>
     45      1.1   gdamore #include <netbt/l2cap.h>
     46      1.1   gdamore 
     47      1.1   gdamore struct l2cap_channel_list
     48      1.1   gdamore 	l2cap_active_list = LIST_HEAD_INITIALIZER(l2cap_active_list);
     49      1.1   gdamore struct l2cap_channel_list
     50      1.1   gdamore 	l2cap_listen_list = LIST_HEAD_INITIALIZER(l2cap_listen_list);
     51      1.1   gdamore 
     52      1.2        ad POOL_INIT(l2cap_req_pool, sizeof(struct l2cap_req), 0, 0, 0, "l2cap_req", NULL,
     53      1.2        ad     IPL_SOFTNET);
     54      1.2        ad POOL_INIT(l2cap_pdu_pool, sizeof(struct l2cap_pdu), 0, 0, 0, "l2cap_pdu", NULL,
     55      1.2        ad     IPL_SOFTNET);
     56      1.1   gdamore 
     57      1.1   gdamore const l2cap_qos_t l2cap_default_qos = {
     58      1.1   gdamore 	0,			/* flags */
     59      1.1   gdamore 	L2CAP_QOS_BEST_EFFORT,	/* service type */
     60      1.1   gdamore 	0x00000000,		/* token rate */
     61      1.1   gdamore 	0x00000000,		/* token bucket size */
     62      1.1   gdamore 	0x00000000,		/* peak bandwidth */
     63      1.1   gdamore 	0xffffffff,		/* latency */
     64      1.1   gdamore 	0xffffffff		/* delay variation */
     65      1.1   gdamore };
     66      1.1   gdamore 
     67      1.1   gdamore /*
     68      1.1   gdamore  * L2CAP request timeouts
     69      1.1   gdamore  */
     70      1.1   gdamore int l2cap_response_timeout = 30;		/* seconds */
     71      1.1   gdamore int l2cap_response_extended_timeout = 180;	/* seconds */
     72      1.1   gdamore 
     73      1.1   gdamore /*
     74      1.3    plunky  * Set Link Mode on channel
     75      1.3    plunky  */
     76      1.3    plunky int
     77      1.3    plunky l2cap_setmode(struct l2cap_channel *chan)
     78      1.3    plunky {
     79      1.3    plunky 
     80      1.3    plunky 	KASSERT(chan != NULL);
     81      1.3    plunky 	KASSERT(chan->lc_link != NULL);
     82      1.3    plunky 
     83      1.3    plunky 	DPRINTF("CID #%d, auth %s, encrypt %s, secure %s\n", chan->lc_lcid,
     84      1.3    plunky 		(chan->lc_mode & L2CAP_LM_AUTH ? "yes" : "no"),
     85      1.3    plunky 		(chan->lc_mode & L2CAP_LM_ENCRYPT ? "yes" : "no"),
     86      1.3    plunky 		(chan->lc_mode & L2CAP_LM_SECURE ? "yes" : "no"));
     87      1.3    plunky 
     88      1.3    plunky 	if (chan->lc_mode & L2CAP_LM_AUTH)
     89      1.3    plunky 		chan->lc_link->hl_flags |= HCI_LINK_AUTH_REQ;
     90      1.3    plunky 
     91      1.3    plunky 	if (chan->lc_mode & L2CAP_LM_ENCRYPT)
     92      1.3    plunky 		chan->lc_link->hl_flags |= HCI_LINK_ENCRYPT_REQ;
     93      1.3    plunky 
     94      1.3    plunky 	if (chan->lc_mode & L2CAP_LM_SECURE)
     95      1.3    plunky 		chan->lc_link->hl_flags |= HCI_LINK_SECURE_REQ;
     96      1.3    plunky 
     97      1.3    plunky 	return hci_acl_setmode(chan->lc_link);
     98      1.3    plunky }
     99      1.3    plunky 
    100      1.3    plunky /*
    101      1.1   gdamore  * Allocate a new Request structure & ID and set the timer going
    102      1.1   gdamore  */
    103      1.1   gdamore int
    104      1.1   gdamore l2cap_request_alloc(struct l2cap_channel *chan, uint8_t code)
    105      1.1   gdamore {
    106      1.1   gdamore 	struct hci_link *link = chan->lc_link;
    107      1.1   gdamore 	struct l2cap_req *req;
    108      1.1   gdamore 	int next_id;
    109      1.1   gdamore 
    110      1.1   gdamore 	if (link == NULL)
    111      1.1   gdamore 		return ENETDOWN;
    112      1.1   gdamore 
    113      1.1   gdamore 	/* find next ID (0 is not allowed) */
    114      1.1   gdamore 	next_id = link->hl_lastid + 1;
    115      1.1   gdamore 	if (next_id > 0xff)
    116      1.1   gdamore 		next_id = 1;
    117      1.1   gdamore 
    118      1.1   gdamore 	/* Ouroboros check */
    119      1.1   gdamore 	req = TAILQ_FIRST(&link->hl_reqs);
    120      1.1   gdamore 	if (req && req->lr_id == next_id)
    121      1.1   gdamore 		return ENFILE;
    122      1.1   gdamore 
    123      1.1   gdamore 	req = pool_get(&l2cap_req_pool, PR_NOWAIT);
    124      1.1   gdamore 	if (req == NULL)
    125      1.1   gdamore 		return ENOMEM;
    126      1.1   gdamore 
    127      1.1   gdamore 	req->lr_id = link->hl_lastid = next_id;
    128      1.1   gdamore 
    129      1.1   gdamore 	req->lr_code = code;
    130      1.1   gdamore 	req->lr_chan = chan;
    131      1.1   gdamore 	req->lr_link = link;
    132      1.1   gdamore 
    133      1.4        ad 	callout_init(&req->lr_rtx, 0);
    134  1.4.6.1  jmcneill 	callout_setfunc(&req->lr_rtx, l2cap_rtx, req);
    135  1.4.6.1  jmcneill 	callout_schedule(&req->lr_rtx, l2cap_response_timeout * hz);
    136      1.1   gdamore 
    137      1.1   gdamore 	TAILQ_INSERT_TAIL(&link->hl_reqs, req, lr_next);
    138      1.1   gdamore 
    139      1.1   gdamore 	return 0;
    140      1.1   gdamore }
    141      1.1   gdamore 
    142      1.1   gdamore /*
    143      1.1   gdamore  * Find a running request for this link
    144      1.1   gdamore  */
    145      1.1   gdamore struct l2cap_req *
    146      1.1   gdamore l2cap_request_lookup(struct hci_link *link, uint8_t id)
    147      1.1   gdamore {
    148      1.1   gdamore 	struct l2cap_req *req;
    149      1.1   gdamore 
    150      1.1   gdamore 	TAILQ_FOREACH(req, &link->hl_reqs, lr_next) {
    151      1.1   gdamore 		if (req->lr_id == id)
    152      1.1   gdamore 			return req;
    153      1.1   gdamore 	}
    154      1.1   gdamore 
    155      1.1   gdamore 	return NULL;
    156      1.1   gdamore }
    157      1.1   gdamore 
    158      1.1   gdamore /*
    159      1.1   gdamore  * Halt and free a request
    160      1.1   gdamore  */
    161      1.1   gdamore void
    162      1.1   gdamore l2cap_request_free(struct l2cap_req *req)
    163      1.1   gdamore {
    164      1.1   gdamore 	struct hci_link *link = req->lr_link;
    165      1.1   gdamore 
    166      1.1   gdamore 	callout_stop(&req->lr_rtx);
    167      1.1   gdamore 	if (callout_invoking(&req->lr_rtx))
    168      1.1   gdamore 		return;
    169      1.1   gdamore 
    170  1.4.6.1  jmcneill 	callout_destroy(&req->lr_rtx);
    171  1.4.6.1  jmcneill 
    172      1.1   gdamore 	TAILQ_REMOVE(&link->hl_reqs, req, lr_next);
    173      1.1   gdamore 	pool_put(&l2cap_req_pool, req);
    174      1.1   gdamore }
    175      1.1   gdamore 
    176      1.1   gdamore /*
    177      1.1   gdamore  * Response Timeout eXpired
    178      1.1   gdamore  *
    179      1.1   gdamore  * No response to our request, so deal with it as best we can.
    180      1.1   gdamore  *
    181      1.1   gdamore  * XXX should try again at least with ertx?
    182      1.1   gdamore  */
    183      1.1   gdamore void
    184      1.1   gdamore l2cap_rtx(void *arg)
    185      1.1   gdamore {
    186      1.1   gdamore 	struct l2cap_req *req = arg;
    187      1.1   gdamore 	struct l2cap_channel *chan;
    188      1.1   gdamore 	int s;
    189      1.1   gdamore 
    190      1.1   gdamore 	s = splsoftnet();
    191      1.1   gdamore 	callout_ack(&req->lr_rtx);
    192      1.1   gdamore 
    193      1.1   gdamore 	chan = req->lr_chan;
    194      1.1   gdamore 	l2cap_request_free(req);
    195      1.1   gdamore 
    196      1.1   gdamore 	DPRINTF("cid %d, ident %d\n", (chan ? chan->lc_lcid : 0), req->lr_id);
    197      1.1   gdamore 
    198      1.1   gdamore 	if (chan && chan->lc_state != L2CAP_CLOSED)
    199      1.1   gdamore 		l2cap_close(chan, ETIMEDOUT);
    200      1.1   gdamore 
    201      1.1   gdamore 	splx(s);
    202      1.1   gdamore }
    203      1.1   gdamore 
    204      1.1   gdamore /*
    205      1.1   gdamore  * Allocate next available CID to channel. We keep a single
    206      1.1   gdamore  * ordered list of channels, so find the first gap.
    207      1.1   gdamore  *
    208      1.1   gdamore  * If this turns out to be not enough (!), could use a
    209      1.1   gdamore  * list per HCI unit..
    210      1.1   gdamore  */
    211      1.1   gdamore int
    212      1.1   gdamore l2cap_cid_alloc(struct l2cap_channel *chan)
    213      1.1   gdamore {
    214      1.1   gdamore 	struct l2cap_channel *used, *prev = NULL;
    215      1.1   gdamore 	uint16_t cid = L2CAP_FIRST_CID;
    216      1.1   gdamore 
    217      1.1   gdamore 	if (chan->lc_lcid != L2CAP_NULL_CID || chan->lc_state != L2CAP_CLOSED)
    218      1.1   gdamore 		return EISCONN;
    219      1.1   gdamore 
    220      1.1   gdamore 	LIST_FOREACH(used, &l2cap_active_list, lc_ncid) {
    221      1.1   gdamore 		if (used->lc_lcid > cid)
    222      1.1   gdamore 			break;	/* found our gap */
    223      1.1   gdamore 
    224      1.1   gdamore 		KASSERT(used->lc_lcid == cid);
    225      1.1   gdamore 		cid++;
    226      1.1   gdamore 
    227      1.1   gdamore 		if (cid == L2CAP_LAST_CID)
    228      1.1   gdamore 			return ENFILE;
    229      1.1   gdamore 
    230      1.1   gdamore 		prev = used;	/* for insert after */
    231      1.1   gdamore 	}
    232      1.1   gdamore 
    233      1.1   gdamore 	chan->lc_lcid = cid;
    234      1.1   gdamore 
    235      1.1   gdamore 	if (prev)
    236      1.1   gdamore 		LIST_INSERT_AFTER(prev, chan, lc_ncid);
    237      1.1   gdamore 	else
    238      1.1   gdamore 		LIST_INSERT_HEAD(&l2cap_active_list, chan, lc_ncid);
    239      1.1   gdamore 
    240      1.1   gdamore 	return 0;
    241      1.1   gdamore }
    242      1.1   gdamore 
    243      1.1   gdamore /*
    244      1.1   gdamore  * Find channel with CID
    245      1.1   gdamore  */
    246      1.1   gdamore struct l2cap_channel *
    247      1.1   gdamore l2cap_cid_lookup(uint16_t cid)
    248      1.1   gdamore {
    249      1.1   gdamore 	struct l2cap_channel *chan;
    250      1.1   gdamore 
    251      1.1   gdamore 	LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) {
    252      1.1   gdamore 		if (chan->lc_lcid == cid)
    253      1.1   gdamore 			return chan;
    254      1.1   gdamore 
    255      1.1   gdamore 		if (chan->lc_lcid > cid)
    256      1.1   gdamore 			return NULL;
    257      1.1   gdamore 	}
    258      1.1   gdamore 
    259      1.1   gdamore 	return NULL;
    260      1.1   gdamore }
    261