Home | History | Annotate | Line # | Download | only in netbt
l2cap_misc.c revision 1.1.24.2
      1  1.1.24.2       ad /*	$NetBSD: l2cap_misc.c,v 1.1.24.2 2007/06/08 14:17:41 ad 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.1.24.2       ad __KERNEL_RCSID(0, "$NetBSD: l2cap_misc.c,v 1.1.24.2 2007/06/08 14:17:41 ad 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.1.24.1       ad POOL_INIT(l2cap_req_pool, sizeof(struct l2cap_req), 0, 0, 0, "l2cap_req", NULL,
     53  1.1.24.1       ad     IPL_SOFTNET);
     54  1.1.24.1       ad POOL_INIT(l2cap_pdu_pool, sizeof(struct l2cap_pdu), 0, 0, 0, "l2cap_pdu", NULL,
     55  1.1.24.1       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.1.24.2       ad  * Set Link Mode on channel
     75  1.1.24.2       ad  */
     76  1.1.24.2       ad int
     77  1.1.24.2       ad l2cap_setmode(struct l2cap_channel *chan)
     78  1.1.24.2       ad {
     79  1.1.24.2       ad 
     80  1.1.24.2       ad 	KASSERT(chan != NULL);
     81  1.1.24.2       ad 	KASSERT(chan->lc_link != NULL);
     82  1.1.24.2       ad 
     83  1.1.24.2       ad 	DPRINTF("CID #%d, auth %s, encrypt %s, secure %s\n", chan->lc_lcid,
     84  1.1.24.2       ad 		(chan->lc_mode & L2CAP_LM_AUTH ? "yes" : "no"),
     85  1.1.24.2       ad 		(chan->lc_mode & L2CAP_LM_ENCRYPT ? "yes" : "no"),
     86  1.1.24.2       ad 		(chan->lc_mode & L2CAP_LM_SECURE ? "yes" : "no"));
     87  1.1.24.2       ad 
     88  1.1.24.2       ad 	if (chan->lc_mode & L2CAP_LM_AUTH)
     89  1.1.24.2       ad 		chan->lc_link->hl_flags |= HCI_LINK_AUTH_REQ;
     90  1.1.24.2       ad 
     91  1.1.24.2       ad 	if (chan->lc_mode & L2CAP_LM_ENCRYPT)
     92  1.1.24.2       ad 		chan->lc_link->hl_flags |= HCI_LINK_ENCRYPT_REQ;
     93  1.1.24.2       ad 
     94  1.1.24.2       ad 	if (chan->lc_mode & L2CAP_LM_SECURE)
     95  1.1.24.2       ad 		chan->lc_link->hl_flags |= HCI_LINK_SECURE_REQ;
     96  1.1.24.2       ad 
     97  1.1.24.2       ad 	return hci_acl_setmode(chan->lc_link);
     98  1.1.24.2       ad }
     99  1.1.24.2       ad 
    100  1.1.24.2       ad /*
    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.1  gdamore 	callout_init(&req->lr_rtx);
    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