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