1 /* $NetBSD: l2cap_lower.c,v 1.8 2008/08/05 13:02:10 plunky Exp $ */
4 * Copyright (c) 2005 Iain Hibbert.
5 * Copyright (c) 2006 Itronix Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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.
33 #include <sys/cdefs.h>
34 __KERNEL_RCSID(0, "$NetBSD: l2cap_lower.c,v 1.8 2008/08/05 13:02:10 plunky Exp $");
36 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
41 #include <sys/queue.h>
42 #include <sys/systm.h>
44 #include <netbt/bluetooth.h>
45 #include <netbt/hci.h>
46 #include <netbt/l2cap.h>
48 /****************************************************************************
50 * L2CAP Channel Lower Layer interface
54 * L2CAP channel is disconnected, could be:
56 * HCI layer received "Disconnect Complete" event for ACL link
57 * some Request timed out
59 * Other end reported invalid CID
60 * Normal disconnection
61 * Change link mode failed
64 l2cap_close(struct l2cap_channel
*chan
, int err
)
66 struct l2cap_pdu
*pdu
;
67 struct l2cap_req
*req
, *n
;
69 if (chan
->lc_state
== L2CAP_CLOSED
)
73 * Since any potential PDU could be half sent we just let it go,
74 * but disassociate ourselves from it as links deal with ownerless
75 * PDU's in any case. We could try harder to flush unsent packets
76 * but maybe its better to leave them in the queue?
78 TAILQ_FOREACH(pdu
, &chan
->lc_link
->hl_txq
, lp_next
) {
79 if (pdu
->lp_chan
== chan
)
84 * and clear any outstanding requests..
86 req
= TAILQ_FIRST(&chan
->lc_link
->hl_reqs
);
88 n
= TAILQ_NEXT(req
, lr_next
);
89 if (req
->lr_chan
== chan
)
90 l2cap_request_free(req
);
96 chan
->lc_state
= L2CAP_CLOSED
;
97 hci_acl_close(chan
->lc_link
, err
);
100 (*chan
->lc_proto
->disconnected
)(chan
->lc_upper
, err
);
104 * Process incoming L2CAP frame from ACL link. We take off the B-Frame
105 * header (which is present in all packets), verify the data length
106 * and distribute the rest of the frame to the relevant channel
110 l2cap_recv_frame(struct mbuf
*m
, struct hci_link
*link
)
112 struct l2cap_channel
*chan
;
115 m_copydata(m
, 0, sizeof(hdr
), &hdr
);
116 m_adj(m
, sizeof(hdr
));
118 hdr
.length
= le16toh(hdr
.length
);
119 hdr
.dcid
= le16toh(hdr
.dcid
);
121 DPRINTFN(5, "(%s) received packet (%d bytes)\n",
122 device_xname(link
->hl_unit
->hci_dev
), hdr
.length
);
124 if (hdr
.length
!= m
->m_pkthdr
.len
)
127 if (hdr
.dcid
== L2CAP_SIGNAL_CID
) {
128 l2cap_recv_signal(m
, link
);
132 if (hdr
.dcid
== L2CAP_CLT_CID
) {
133 m_freem(m
); /* TODO */
137 chan
= l2cap_cid_lookup(hdr
.dcid
);
138 if (chan
!= NULL
&& chan
->lc_link
== link
139 && chan
->lc_imtu
>= hdr
.length
140 && chan
->lc_state
== L2CAP_OPEN
) {
141 (*chan
->lc_proto
->input
)(chan
->lc_upper
, m
);
145 DPRINTF("(%s) invalid L2CAP packet dropped, CID #%d, length %d\n",
146 device_xname(link
->hl_unit
->hci_dev
), hdr
.dcid
, hdr
.length
);
153 * Start another L2CAP packet on its way. This is called from l2cap_send
154 * (when no PDU is pending) and hci_acl_start (when PDU has been placed on
155 * device queue). Thus we can have more than one PDU waiting at the device
156 * if space is available but no single channel will hog the link.
159 l2cap_start(struct l2cap_channel
*chan
)
164 if (chan
->lc_state
!= L2CAP_OPEN
)
167 if (MBUFQ_FIRST(&chan
->lc_txq
) == NULL
) {
168 DPRINTFN(5, "no data, pending = %d\n", chan
->lc_pending
);
170 * If we are just waiting for the queue to flush
171 * and it has, we may disconnect..
173 if (chan
->lc_flags
& L2CAP_SHUTDOWN
174 && chan
->lc_pending
== 0) {
175 chan
->lc_state
= L2CAP_WAIT_DISCONNECT
;
176 err
= l2cap_send_disconnect_req(chan
);
178 l2cap_close(chan
, err
);
185 * We could check QoS/RFC mode here and optionally not send
186 * the packet if we are not ready for any reason
188 * Also to support flush timeout then we might want to start
189 * the timer going? (would need to keep some kind of record
190 * of packets sent, possibly change it so that we allocate
191 * the l2cap_pdu and fragment the packet, then hand it down
192 * and get it back when its completed). Hm.
195 MBUFQ_DEQUEUE(&chan
->lc_txq
, m
);
197 KASSERT(chan
->lc_link
!= NULL
);
200 DPRINTFN(5, "CID #%d sending packet (%d bytes)\n",
201 chan
->lc_lcid
, m
->m_pkthdr
.len
);
204 return hci_acl_send(m
, chan
->lc_link
, chan
);