1 /* $NetBSD: rfcomm_dlc.c,v 1.5 2008/04/24 11:38:37 ad Exp $ */
4 * Copyright (c) 2006 Itronix Inc.
7 * Written by Iain Hibbert for Itronix Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of Itronix Inc. may not be used to endorse
18 * or promote products derived from this software without specific
19 * prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: rfcomm_dlc.c,v 1.5 2008/04/24 11:38:37 ad Exp $");
37 #include <sys/param.h>
38 #include <sys/kernel.h>
41 #include <sys/socketvar.h>
42 #include <sys/systm.h>
44 #include <netbt/bluetooth.h>
45 #include <netbt/hci.h>
46 #include <netbt/l2cap.h>
47 #include <netbt/rfcomm.h>
50 * rfcomm_dlc_lookup(rfcomm_session, dlci)
52 * Find DLC on session with matching dlci
55 rfcomm_dlc_lookup(struct rfcomm_session
*rs
, int dlci
)
57 struct rfcomm_dlc
*dlc
;
59 LIST_FOREACH(dlc
, &rs
->rs_dlcs
, rd_next
) {
60 if (dlc
->rd_dlci
== dlci
)
68 * rfcomm_dlc_newconn(rfcomm_session, dlci)
70 * handle a new dlc request (since its called from a couple of places)
73 rfcomm_dlc_newconn(struct rfcomm_session
*rs
, int dlci
)
75 struct rfcomm_session
*ls
;
76 struct rfcomm_dlc
*new, *dlc
, *any
, *best
;
77 struct sockaddr_bt laddr
, raddr
, addr
;
81 * Search amongst the listening DLC community for the best match for
82 * address & channel. We keep listening DLC's hanging on listening
83 * sessions in a last first order, so scan the entire bunch and keep
84 * a note of the best address and BDADDR_ANY matches in order to find
85 * the oldest and most specific match.
87 l2cap_sockaddr(rs
->rs_l2cap
, &laddr
);
88 l2cap_peeraddr(rs
->rs_l2cap
, &raddr
);
89 chan
= RFCOMM_CHANNEL(dlci
);
93 LIST_FOREACH(ls
, &rfcomm_session_listen
, rs_next
) {
94 l2cap_sockaddr(ls
->rs_l2cap
, &addr
);
96 if (addr
.bt_psm
!= laddr
.bt_psm
)
99 if (bdaddr_same(&laddr
.bt_bdaddr
, &addr
.bt_bdaddr
)) {
100 LIST_FOREACH(dlc
, &ls
->rs_dlcs
, rd_next
) {
101 if (dlc
->rd_laddr
.bt_channel
== chan
)
106 if (bdaddr_any(&addr
.bt_bdaddr
)) {
107 LIST_FOREACH(dlc
, &ls
->rs_dlcs
, rd_next
) {
108 if (dlc
->rd_laddr
.bt_channel
== chan
)
114 dlc
= best
? best
: any
;
117 * Note that if this fails, we could have missed a chance to open
118 * a connection - really need to rewrite the strategy for storing
119 * listening DLC's so all can be checked in turn..
122 new = (*dlc
->rd_proto
->newconn
)(dlc
->rd_upper
, &laddr
, &raddr
);
125 rfcomm_session_send_frame(rs
, RFCOMM_FRAME_DM
, dlci
);
130 new->rd_mtu
= rfcomm_mtu_default
;
131 new->rd_mode
= dlc
->rd_mode
;
133 memcpy(&new->rd_laddr
, &laddr
, sizeof(struct sockaddr_bt
));
134 new->rd_laddr
.bt_channel
= chan
;
136 memcpy(&new->rd_raddr
, &raddr
, sizeof(struct sockaddr_bt
));
137 new->rd_raddr
.bt_channel
= chan
;
139 new->rd_session
= rs
;
140 new->rd_state
= RFCOMM_DLC_WAIT_CONNECT
;
141 LIST_INSERT_HEAD(&rs
->rs_dlcs
, new, rd_next
);
147 * rfcomm_dlc_close(dlc, error)
149 * detach DLC from session and clean up
152 rfcomm_dlc_close(struct rfcomm_dlc
*dlc
, int err
)
154 struct rfcomm_session
*rs
;
155 struct rfcomm_credit
*credit
;
157 KASSERT(dlc
->rd_state
!= RFCOMM_DLC_CLOSED
);
159 /* Clear credit history */
160 rs
= dlc
->rd_session
;
161 SIMPLEQ_FOREACH(credit
, &rs
->rs_credits
, rc_next
)
162 if (credit
->rc_dlc
== dlc
)
163 credit
->rc_dlc
= NULL
;
165 callout_stop(&dlc
->rd_timeout
);
167 LIST_REMOVE(dlc
, rd_next
);
168 dlc
->rd_session
= NULL
;
169 dlc
->rd_state
= RFCOMM_DLC_CLOSED
;
171 (*dlc
->rd_proto
->disconnected
)(dlc
->rd_upper
, err
);
174 * It is the responsibility of the party who sends the last
175 * DISC(dlci) to disconnect the session, but we will schedule
176 * an expiry just in case that doesnt happen..
178 if (LIST_EMPTY(&rs
->rs_dlcs
)) {
179 if (rs
->rs_state
== RFCOMM_SESSION_LISTEN
)
180 rfcomm_session_free(rs
);
182 callout_schedule(&rs
->rs_timeout
,
183 rfcomm_ack_timeout
* hz
);
188 * rfcomm_dlc_timeout(dlc)
190 * DLC timeout function is schedUled when we sent any of SABM,
191 * DISC, MCC_MSC, or MCC_PN and should be cancelled when we get
192 * the relevant response. There is nothing to do but shut this
196 rfcomm_dlc_timeout(void *arg
)
198 struct rfcomm_dlc
*dlc
= arg
;
200 mutex_enter(bt_lock
);
201 callout_ack(&dlc
->rd_timeout
);
203 if (dlc
->rd_state
!= RFCOMM_DLC_CLOSED
)
204 rfcomm_dlc_close(dlc
, ETIMEDOUT
);
205 else if (dlc
->rd_flags
& RFCOMM_DLC_DETACH
) {
206 callout_destroy(&dlc
->rd_timeout
);
207 free(dlc
, M_BLUETOOTH
);
214 * rfcomm_dlc_setmode(rfcomm_dlc)
216 * Set link mode for DLC. This is only called when the session is
217 * already open, so we don't need to worry about any previous mode
221 rfcomm_dlc_setmode(struct rfcomm_dlc
*dlc
)
226 KASSERT(dlc
->rd_session
!= NULL
);
227 KASSERT(dlc
->rd_session
->rs_state
== RFCOMM_SESSION_OPEN
);
229 DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc
->rd_dlci
,
230 (dlc
->rd_mode
& RFCOMM_LM_AUTH
? "yes" : "no"),
231 (dlc
->rd_mode
& RFCOMM_LM_ENCRYPT
? "yes" : "no"),
232 (dlc
->rd_mode
& RFCOMM_LM_SECURE
? "yes" : "no"));
234 if (dlc
->rd_mode
& RFCOMM_LM_AUTH
)
235 mode
|= L2CAP_LM_AUTH
;
237 if (dlc
->rd_mode
& RFCOMM_LM_ENCRYPT
)
238 mode
|= L2CAP_LM_ENCRYPT
;
240 if (dlc
->rd_mode
& RFCOMM_LM_SECURE
)
241 mode
|= L2CAP_LM_SECURE
;
243 sockopt_init(&sopt
, BTPROTO_L2CAP
, SO_L2CAP_LM
, 0);
244 sockopt_setint(&sopt
, mode
);
245 err
= l2cap_setopt(dlc
->rd_session
->rs_l2cap
, &sopt
);
246 sockopt_destroy(&sopt
);
252 * rfcomm_dlc_connect(rfcomm_dlc)
254 * initiate DLC connection (session is already connected)
257 rfcomm_dlc_connect(struct rfcomm_dlc
*dlc
)
259 struct rfcomm_mcc_pn pn
;
262 KASSERT(dlc
->rd_session
!= NULL
);
263 KASSERT(dlc
->rd_session
->rs_state
== RFCOMM_SESSION_OPEN
);
264 KASSERT(dlc
->rd_state
== RFCOMM_DLC_WAIT_SESSION
);
267 * If we have not already sent a PN on the session, we must send
268 * a PN to negotiate Credit Flow Control, and this setting will
269 * apply to all future connections for this session. We ask for
270 * this every time, in order to establish initial credits.
272 memset(&pn
, 0, sizeof(pn
));
273 pn
.dlci
= dlc
->rd_dlci
;
274 pn
.priority
= dlc
->rd_dlci
| 0x07;
275 pn
.mtu
= htole16(dlc
->rd_mtu
);
277 pn
.flow_control
= 0xf0;
278 dlc
->rd_rxcred
= (dlc
->rd_rxsize
/ dlc
->rd_mtu
);
279 dlc
->rd_rxcred
= min(dlc
->rd_rxcred
, RFCOMM_CREDITS_DEFAULT
);
280 pn
.credits
= dlc
->rd_rxcred
;
282 err
= rfcomm_session_send_mcc(dlc
->rd_session
, 1,
283 RFCOMM_MCC_PN
, &pn
, sizeof(pn
));
287 dlc
->rd_state
= RFCOMM_DLC_WAIT_CONNECT
;
288 callout_schedule(&dlc
->rd_timeout
, rfcomm_mcc_timeout
* hz
);
294 * rfcomm_dlc_open(rfcomm_dlc)
296 * send "Modem Status Command" and mark DLC as open.
299 rfcomm_dlc_open(struct rfcomm_dlc
*dlc
)
301 struct rfcomm_mcc_msc msc
;
304 KASSERT(dlc
->rd_session
!= NULL
);
305 KASSERT(dlc
->rd_session
->rs_state
== RFCOMM_SESSION_OPEN
);
307 memset(&msc
, 0, sizeof(msc
));
308 msc
.address
= RFCOMM_MKADDRESS(1, dlc
->rd_dlci
);
309 msc
.modem
= dlc
->rd_lmodem
& 0xfe; /* EA = 0 */
310 msc
.brk
= 0x00 | 0x01; /* EA = 1 */
312 err
= rfcomm_session_send_mcc(dlc
->rd_session
, 1,
313 RFCOMM_MCC_MSC
, &msc
, sizeof(msc
));
317 callout_schedule(&dlc
->rd_timeout
, rfcomm_mcc_timeout
* hz
);
319 dlc
->rd_state
= RFCOMM_DLC_OPEN
;
320 (*dlc
->rd_proto
->connected
)(dlc
->rd_upper
);
326 * rfcomm_dlc_start(rfcomm_dlc)
328 * Start sending data (and/or credits) for DLC. Our strategy is to
329 * send anything we can down to the l2cap layer. When credits run
330 * out, data will naturally bunch up. When not using credit flow
331 * control, we limit the number of packets we have pending to reduce
333 * We should deal with channel priority somehow.
336 rfcomm_dlc_start(struct rfcomm_dlc
*dlc
)
338 struct rfcomm_session
*rs
= dlc
->rd_session
;
343 KASSERT(rs
->rs_state
== RFCOMM_SESSION_OPEN
);
344 KASSERT(dlc
->rd_state
== RFCOMM_DLC_OPEN
);
349 if (rs
->rs_flags
& RFCOMM_SESSION_CFC
) {
350 credits
= (dlc
->rd_rxsize
/ dlc
->rd_mtu
);
351 credits
-= dlc
->rd_rxcred
;
352 credits
= min(credits
, RFCOMM_CREDITS_MAX
);
357 if (dlc
->rd_txcred
== 0)
360 if (rs
->rs_flags
& RFCOMM_SESSION_RFC
)
363 if (dlc
->rd_rmodem
& RFCOMM_MSC_FC
)
366 if (dlc
->rd_pending
> RFCOMM_CREDITS_DEFAULT
)
370 if (dlc
->rd_txbuf
== NULL
)
378 * No need to send small numbers of credits on their
379 * own unless the other end hasn't many left.
381 if (credits
< RFCOMM_CREDITS_DEFAULT
382 && dlc
->rd_rxcred
> RFCOMM_CREDITS_DEFAULT
)
388 * take what data we can from (front of) txbuf
391 if (len
< m
->m_pkthdr
.len
) {
392 dlc
->rd_txbuf
= m_split(m
, len
, M_DONTWAIT
);
393 if (dlc
->rd_txbuf
== NULL
) {
398 dlc
->rd_txbuf
= NULL
;
399 len
= m
->m_pkthdr
.len
;
403 DPRINTFN(10, "dlci %d send %d bytes, %d credits, rxcred = %d\n",
404 dlc
->rd_dlci
, len
, credits
, dlc
->rd_rxcred
);
406 if (rfcomm_session_send_uih(rs
, dlc
, credits
, m
)) {
407 printf("%s: lost %d bytes on DLCI %d\n",
408 __func__
, len
, dlc
->rd_dlci
);
415 if (rs
->rs_flags
& RFCOMM_SESSION_CFC
) {
420 dlc
->rd_rxcred
+= credits
;