Home | History | Annotate | Line # | Download | only in netbt
l2cap_misc.c revision 1.2.2.1
      1  1.2.2.1      mjf /*	$NetBSD: l2cap_misc.c,v 1.2.2.1 2007/07/11 20:11:12 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.2.2.1      mjf __KERNEL_RCSID(0, "$NetBSD: l2cap_misc.c,v 1.2.2.1 2007/07/11 20:11:12 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.2.2.1      mjf  * Set Link Mode on channel
     75  1.2.2.1      mjf  */
     76  1.2.2.1      mjf int
     77  1.2.2.1      mjf l2cap_setmode(struct l2cap_channel *chan)
     78  1.2.2.1      mjf {
     79  1.2.2.1      mjf 
     80  1.2.2.1      mjf 	KASSERT(chan != NULL);
     81  1.2.2.1      mjf 	KASSERT(chan->lc_link != NULL);
     82  1.2.2.1      mjf 
     83  1.2.2.1      mjf 	DPRINTF("CID #%d, auth %s, encrypt %s, secure %s\n", chan->lc_lcid,
     84  1.2.2.1      mjf 		(chan->lc_mode & L2CAP_LM_AUTH ? "yes" : "no"),
     85  1.2.2.1      mjf 		(chan->lc_mode & L2CAP_LM_ENCRYPT ? "yes" : "no"),
     86  1.2.2.1      mjf 		(chan->lc_mode & L2CAP_LM_SECURE ? "yes" : "no"));
     87  1.2.2.1      mjf 
     88  1.2.2.1      mjf 	if (chan->lc_mode & L2CAP_LM_AUTH)
     89  1.2.2.1      mjf 		chan->lc_link->hl_flags |= HCI_LINK_AUTH_REQ;
     90  1.2.2.1      mjf 
     91  1.2.2.1      mjf 	if (chan->lc_mode & L2CAP_LM_ENCRYPT)
     92  1.2.2.1      mjf 		chan->lc_link->hl_flags |= HCI_LINK_ENCRYPT_REQ;
     93  1.2.2.1      mjf 
     94  1.2.2.1      mjf 	if (chan->lc_mode & L2CAP_LM_SECURE)
     95  1.2.2.1      mjf 		chan->lc_link->hl_flags |= HCI_LINK_SECURE_REQ;
     96  1.2.2.1      mjf 
     97  1.2.2.1      mjf 	return hci_acl_setmode(chan->lc_link);
     98  1.2.2.1      mjf }
     99  1.2.2.1      mjf 
    100  1.2.2.1      mjf /*
    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.2.2.1      mjf 	callout_init(&req->lr_rtx, 0);
    134      1.1  gdamore 	callout_reset(&req->lr_rtx, l2cap_response_timeout*hz, l2cap_rtx, req);
    135      1.1  gdamore 
    136      1.1  gdamore 	TAILQ_INSERT_TAIL(&link->hl_reqs, req, lr_next);
    137      1.1  gdamore 
    138      1.1  gdamore 	return 0;
    139      1.1  gdamore }
    140      1.1  gdamore 
    141      1.1  gdamore /*
    142      1.1  gdamore  * Find a running request for this link
    143      1.1  gdamore  */
    144      1.1  gdamore struct l2cap_req *
    145      1.1  gdamore l2cap_request_lookup(struct hci_link *link, uint8_t id)
    146      1.1  gdamore {
    147      1.1  gdamore 	struct l2cap_req *req;
    148      1.1  gdamore 
    149      1.1  gdamore 	TAILQ_FOREACH(req, &link->hl_reqs, lr_next) {
    150      1.1  gdamore 		if (req->lr_id == id)
    151      1.1  gdamore 			return req;
    152      1.1  gdamore 	}
    153      1.1  gdamore 
    154      1.1  gdamore 	return NULL;
    155      1.1  gdamore }
    156      1.1  gdamore 
    157      1.1  gdamore /*
    158      1.1  gdamore  * Halt and free a request
    159      1.1  gdamore  */
    160      1.1  gdamore void
    161      1.1  gdamore l2cap_request_free(struct l2cap_req *req)
    162      1.1  gdamore {
    163      1.1  gdamore 	struct hci_link *link = req->lr_link;
    164      1.1  gdamore 
    165      1.1  gdamore 	callout_stop(&req->lr_rtx);
    166      1.1  gdamore 	if (callout_invoking(&req->lr_rtx))
    167      1.1  gdamore 		return;
    168      1.1  gdamore 
    169      1.1  gdamore 	TAILQ_REMOVE(&link->hl_reqs, req, lr_next);
    170      1.1  gdamore 	pool_put(&l2cap_req_pool, req);
    171      1.1  gdamore }
    172      1.1  gdamore 
    173      1.1  gdamore /*
    174      1.1  gdamore  * Response Timeout eXpired
    175      1.1  gdamore  *
    176      1.1  gdamore  * No response to our request, so deal with it as best we can.
    177      1.1  gdamore  *
    178      1.1  gdamore  * XXX should try again at least with ertx?
    179      1.1  gdamore  */
    180      1.1  gdamore void
    181      1.1  gdamore l2cap_rtx(void *arg)
    182      1.1  gdamore {
    183      1.1  gdamore 	struct l2cap_req *req = arg;
    184      1.1  gdamore 	struct l2cap_channel *chan;
    185      1.1  gdamore 	int s;
    186      1.1  gdamore 
    187      1.1  gdamore 	s = splsoftnet();
    188      1.1  gdamore 	callout_ack(&req->lr_rtx);
    189      1.1  gdamore 
    190      1.1  gdamore 	chan = req->lr_chan;
    191      1.1  gdamore 	l2cap_request_free(req);
    192      1.1  gdamore 
    193      1.1  gdamore 	DPRINTF("cid %d, ident %d\n", (chan ? chan->lc_lcid : 0), req->lr_id);
    194      1.1  gdamore 
    195      1.1  gdamore 	if (chan && chan->lc_state != L2CAP_CLOSED)
    196      1.1  gdamore 		l2cap_close(chan, ETIMEDOUT);
    197      1.1  gdamore 
    198      1.1  gdamore 	splx(s);
    199      1.1  gdamore }
    200      1.1  gdamore 
    201      1.1  gdamore /*
    202      1.1  gdamore  * Allocate next available CID to channel. We keep a single
    203      1.1  gdamore  * ordered list of channels, so find the first gap.
    204      1.1  gdamore  *
    205      1.1  gdamore  * If this turns out to be not enough (!), could use a
    206      1.1  gdamore  * list per HCI unit..
    207      1.1  gdamore  */
    208      1.1  gdamore int
    209      1.1  gdamore l2cap_cid_alloc(struct l2cap_channel *chan)
    210      1.1  gdamore {
    211      1.1  gdamore 	struct l2cap_channel *used, *prev = NULL;
    212      1.1  gdamore 	uint16_t cid = L2CAP_FIRST_CID;
    213      1.1  gdamore 
    214      1.1  gdamore 	if (chan->lc_lcid != L2CAP_NULL_CID || chan->lc_state != L2CAP_CLOSED)
    215      1.1  gdamore 		return EISCONN;
    216      1.1  gdamore 
    217      1.1  gdamore 	LIST_FOREACH(used, &l2cap_active_list, lc_ncid) {
    218      1.1  gdamore 		if (used->lc_lcid > cid)
    219      1.1  gdamore 			break;	/* found our gap */
    220      1.1  gdamore 
    221      1.1  gdamore 		KASSERT(used->lc_lcid == cid);
    222      1.1  gdamore 		cid++;
    223      1.1  gdamore 
    224      1.1  gdamore 		if (cid == L2CAP_LAST_CID)
    225      1.1  gdamore 			return ENFILE;
    226      1.1  gdamore 
    227      1.1  gdamore 		prev = used;	/* for insert after */
    228      1.1  gdamore 	}
    229      1.1  gdamore 
    230      1.1  gdamore 	chan->lc_lcid = cid;
    231      1.1  gdamore 
    232      1.1  gdamore 	if (prev)
    233      1.1  gdamore 		LIST_INSERT_AFTER(prev, chan, lc_ncid);
    234      1.1  gdamore 	else
    235      1.1  gdamore 		LIST_INSERT_HEAD(&l2cap_active_list, chan, lc_ncid);
    236      1.1  gdamore 
    237      1.1  gdamore 	return 0;
    238      1.1  gdamore }
    239      1.1  gdamore 
    240      1.1  gdamore /*
    241      1.1  gdamore  * Find channel with CID
    242      1.1  gdamore  */
    243      1.1  gdamore struct l2cap_channel *
    244      1.1  gdamore l2cap_cid_lookup(uint16_t cid)
    245      1.1  gdamore {
    246      1.1  gdamore 	struct l2cap_channel *chan;
    247      1.1  gdamore 
    248      1.1  gdamore 	LIST_FOREACH(chan, &l2cap_active_list, lc_ncid) {
    249      1.1  gdamore 		if (chan->lc_lcid == cid)
    250      1.1  gdamore 			return chan;
    251      1.1  gdamore 
    252      1.1  gdamore 		if (chan->lc_lcid > cid)
    253      1.1  gdamore 			return NULL;
    254      1.1  gdamore 	}
    255      1.1  gdamore 
    256      1.1  gdamore 	return NULL;
    257      1.1  gdamore }
    258