Home | History | Annotate | Line # | Download | only in netbt
l2cap_misc.c revision 1.5.16.1
      1  1.5.16.1      mjf /*	$NetBSD: l2cap_misc.c,v 1.5.16.1 2008/06/02 13:24:23 mjf 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.5.16.1      mjf __KERNEL_RCSID(0, "$NetBSD: l2cap_misc.c,v 1.5.16.1 2008/06/02 13:24:23 mjf 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.5   plunky 	callout_setfunc(&req->lr_rtx, l2cap_rtx, req);
    135       1.5   plunky 	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.5   plunky 	callout_destroy(&req->lr_rtx);
    171       1.5   plunky 
    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 
    189  1.5.16.1      mjf 	mutex_enter(bt_lock);
    190       1.1  gdamore 	callout_ack(&req->lr_rtx);
    191       1.1  gdamore 
    192       1.1  gdamore 	chan = req->lr_chan;
    193       1.1  gdamore 	l2cap_request_free(req);
    194       1.1  gdamore 
    195       1.1  gdamore 	DPRINTF("cid %d, ident %d\n", (chan ? chan->lc_lcid : 0), req->lr_id);
    196       1.1  gdamore 
    197       1.1  gdamore 	if (chan && chan->lc_state != L2CAP_CLOSED)
    198       1.1  gdamore 		l2cap_close(chan, ETIMEDOUT);
    199       1.1  gdamore 
    200  1.5.16.1      mjf 	mutex_exit(bt_lock);
    201       1.1  gdamore }
    202       1.1  gdamore 
    203       1.1  gdamore /*
    204       1.1  gdamore  * Allocate next available CID to channel. We keep a single
    205       1.1  gdamore  * ordered list of channels, so find the first gap.
    206       1.1  gdamore  *
    207       1.1  gdamore  * If this turns out to be not enough (!), could use a
    208       1.1  gdamore  * list per HCI unit..
    209       1.1  gdamore  */
    210       1.1  gdamore int
    211       1.1  gdamore l2cap_cid_alloc(struct l2cap_channel *chan)
    212       1.1  gdamore {
    213       1.1  gdamore 	struct l2cap_channel *used, *prev = NULL;
    214       1.1  gdamore 	uint16_t cid = L2CAP_FIRST_CID;
    215       1.1  gdamore 
    216       1.1  gdamore 	if (chan->lc_lcid != L2CAP_NULL_CID || chan->lc_state != L2CAP_CLOSED)
    217       1.1  gdamore 		return EISCONN;
    218       1.1  gdamore 
    219       1.1  gdamore 	LIST_FOREACH(used, &l2cap_active_list, lc_ncid) {
    220       1.1  gdamore 		if (used->lc_lcid > cid)
    221       1.1  gdamore 			break;	/* found our gap */
    222       1.1  gdamore 
    223       1.1  gdamore 		KASSERT(used->lc_lcid == cid);
    224       1.1  gdamore 		cid++;
    225       1.1  gdamore 
    226       1.1  gdamore 		if (cid == L2CAP_LAST_CID)
    227       1.1  gdamore 			return ENFILE;
    228       1.1  gdamore 
    229       1.1  gdamore 		prev = used;	/* for insert after */
    230       1.1  gdamore 	}
    231       1.1  gdamore 
    232       1.1  gdamore 	chan->lc_lcid = cid;
    233       1.1  gdamore 
    234       1.1  gdamore 	if (prev)
    235       1.1  gdamore 		LIST_INSERT_AFTER(prev, chan, lc_ncid);
    236       1.1  gdamore 	else
    237       1.1  gdamore 		LIST_INSERT_HEAD(&l2cap_active_list, chan, lc_ncid);
    238       1.1  gdamore 
    239       1.1  gdamore 	return 0;
    240       1.1  gdamore }
    241       1.1  gdamore 
    242       1.1  gdamore /*
    243       1.1  gdamore  * Find channel with CID
    244       1.1  gdamore  */
    245       1.1  gdamore struct l2cap_channel *
    246       1.1  gdamore l2cap_cid_lookup(uint16_t cid)
    247       1.1  gdamore {
    248       1.1  gdamore 	struct l2cap_channel *chan;
    249       1.1  gdamore 
    250       1.1  gdamore 	LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) {
    251       1.1  gdamore 		if (chan->lc_lcid == cid)
    252       1.1  gdamore 			return chan;
    253       1.1  gdamore 
    254       1.1  gdamore 		if (chan->lc_lcid > cid)
    255       1.1  gdamore 			return NULL;
    256       1.1  gdamore 	}
    257       1.1  gdamore 
    258       1.1  gdamore 	return NULL;
    259       1.1  gdamore }
    260