Annotation of sys/netbt/rfcomm_dlc.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: rfcomm_dlc.c,v 1.1 2007/06/01 02:46:12 uwe Exp $ */
2: /* $NetBSD: rfcomm_dlc.c,v 1.3 2007/04/21 06:15:23 plunky Exp $ */
3:
4: /*-
5: * Copyright (c) 2006 Itronix Inc.
6: * All rights reserved.
7: *
8: * Written by Iain Hibbert for Itronix Inc.
9: *
10: * Redistribution and use in source and binary forms, with or without
11: * modification, are permitted provided that the following conditions
12: * are met:
13: * 1. Redistributions of source code must retain the above copyright
14: * notice, this list of conditions and the following disclaimer.
15: * 2. Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: * 3. The name of Itronix Inc. may not be used to endorse
19: * or promote products derived from this software without specific
20: * prior written permission.
21: *
22: * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
23: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
26: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
29: * ON ANY THEORY OF LIABILITY, WHETHER IN
30: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32: * POSSIBILITY OF SUCH DAMAGE.
33: */
34:
35: #include <sys/cdefs.h>
36:
37: #include <sys/param.h>
38: #include <sys/kernel.h>
39: #include <sys/mbuf.h>
40: #include <sys/proc.h>
41: #include <sys/systm.h>
42:
43: #include <netbt/bluetooth.h>
44: #include <netbt/hci.h>
45: #include <netbt/l2cap.h>
46: #include <netbt/rfcomm.h>
47:
48: /*
49: * rfcomm_dlc_lookup(rfcomm_session, dlci)
50: *
51: * Find DLC on session with matching dlci
52: */
53: struct rfcomm_dlc *
54: rfcomm_dlc_lookup(struct rfcomm_session *rs, int dlci)
55: {
56: struct rfcomm_dlc *dlc;
57:
58: LIST_FOREACH(dlc, &rs->rs_dlcs, rd_next) {
59: if (dlc->rd_dlci == dlci)
60: break;
61: }
62:
63: return dlc;
64: }
65:
66: /*
67: * rfcomm_dlc_newconn(rfcomm_session, dlci)
68: *
69: * handle a new dlc request (since its called from a couple of places)
70: */
71: struct rfcomm_dlc *
72: rfcomm_dlc_newconn(struct rfcomm_session *rs, int dlci)
73: {
74: struct rfcomm_session *ls;
75: struct rfcomm_dlc *new, *dlc, *any, *best;
76: struct sockaddr_bt laddr, raddr, addr;
77: int chan;
78:
79: /*
80: * Search amongst the listening DLC community for the best match for
81: * address & channel. We keep listening DLC's hanging on listening
82: * sessions in a last first order, so scan the entire bunch and keep
83: * a note of the best address and BDADDR_ANY matches in order to find
84: * the oldest and most specific match.
85: */
86: l2cap_sockaddr(rs->rs_l2cap, &laddr);
87: l2cap_peeraddr(rs->rs_l2cap, &raddr);
88: chan = RFCOMM_CHANNEL(dlci);
89: new = NULL;
90:
91: any = best = NULL;
92: LIST_FOREACH(ls, &rfcomm_session_listen, rs_next) {
93: l2cap_sockaddr(ls->rs_l2cap, &addr);
94:
95: if (addr.bt_psm != laddr.bt_psm)
96: continue;
97:
98: if (bdaddr_same(&laddr.bt_bdaddr, &addr.bt_bdaddr)) {
99: LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
100: if (dlc->rd_laddr.bt_channel == chan)
101: best = dlc;
102: }
103: }
104:
105: if (bdaddr_any(&addr.bt_bdaddr)) {
106: LIST_FOREACH(dlc, &ls->rs_dlcs, rd_next) {
107: if (dlc->rd_laddr.bt_channel == chan)
108: any = dlc;
109: }
110: }
111: }
112:
113: dlc = best ? best : any;
114:
115: /* XXX
116: * Note that if this fails, we could have missed a chance to open
117: * a connection - really need to rewrite the strategy for storing
118: * listening DLC's so all can be checked in turn..
119: */
120: if (dlc != NULL)
121: new = (*dlc->rd_proto->newconn)(dlc->rd_upper, &laddr, &raddr);
122:
123: if (new == NULL) {
124: rfcomm_session_send_frame(rs, RFCOMM_FRAME_DM, dlci);
125: return NULL;
126: }
127:
128: new->rd_dlci = dlci;
129: new->rd_mtu = rfcomm_mtu_default;
130: new->rd_mode = dlc->rd_mode;
131:
132: memcpy(&new->rd_laddr, &laddr, sizeof(struct sockaddr_bt));
133: new->rd_laddr.bt_channel = chan;
134:
135: memcpy(&new->rd_raddr, &raddr, sizeof(struct sockaddr_bt));
136: new->rd_raddr.bt_channel = chan;
137:
138: new->rd_session = rs;
139: new->rd_state = RFCOMM_DLC_WAIT_CONNECT;
140: LIST_INSERT_HEAD(&rs->rs_dlcs, new, rd_next);
141:
142: return new;
143: }
144:
145: /*
146: * rfcomm_dlc_close(dlc, error)
147: *
148: * detach DLC from session and clean up
149: */
150: void
151: rfcomm_dlc_close(struct rfcomm_dlc *dlc, int err)
152: {
153: struct rfcomm_session *rs;
154: struct rfcomm_credit *credit;
155:
156: KASSERT(dlc->rd_state != RFCOMM_DLC_CLOSED);
157:
158: /* Clear credit history */
159: rs = dlc->rd_session;
160: SIMPLEQ_FOREACH(credit, &rs->rs_credits, rc_next)
161: if (credit->rc_dlc == dlc)
162: credit->rc_dlc = NULL;
163:
164: timeout_del(&dlc->rd_timeout);
165:
166: LIST_REMOVE(dlc, rd_next);
167: dlc->rd_session = NULL;
168: dlc->rd_state = RFCOMM_DLC_CLOSED;
169:
170: (*dlc->rd_proto->disconnected)(dlc->rd_upper, err);
171:
172: /*
173: * It is the responsibility of the party who sends the last
174: * DISC(dlci) to disconnect the session, but we will schedule
175: * an expiry just in case that doesnt happen..
176: */
177: if (LIST_EMPTY(&rs->rs_dlcs)) {
178: if (rs->rs_state == RFCOMM_SESSION_LISTEN)
179: rfcomm_session_free(rs);
180: else
181: timeout_add(&rs->rs_timeout,
182: rfcomm_ack_timeout * hz);
183: }
184: }
185:
186: /*
187: * rfcomm_dlc_timeout(dlc)
188: *
189: * DLC timeout function is schedUled when we sent any of SABM,
190: * DISC, MCC_MSC, or MCC_PN and should be cancelled when we get
191: * the relevant response. There is nothing to do but shut this
192: * DLC down.
193: */
194: void
195: rfcomm_dlc_timeout(void *arg)
196: {
197: struct rfcomm_dlc *dlc = arg;
198: int s;
199:
200: s = splsoftnet();
201:
202: if (dlc->rd_state != RFCOMM_DLC_CLOSED)
203: rfcomm_dlc_close(dlc, ETIMEDOUT);
204: else if (dlc->rd_flags & RFCOMM_DLC_DETACH)
205: free(dlc, M_BLUETOOTH);
206:
207: splx(s);
208: }
209:
210: /*
211: * rfcomm_dlc_setmode(rfcomm_dlc)
212: *
213: * Set link mode for DLC. This is only called when the session is
214: * already open, so we don't need to worry about any previous mode
215: * settings.
216: */
217: int
218: rfcomm_dlc_setmode(struct rfcomm_dlc *dlc)
219: {
220: int mode = 0;
221:
222: KASSERT(dlc->rd_session != NULL);
223: KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
224:
225: DPRINTF("dlci %d, auth %s, encrypt %s, secure %s\n", dlc->rd_dlci,
226: (dlc->rd_mode & RFCOMM_LM_AUTH ? "yes" : "no"),
227: (dlc->rd_mode & RFCOMM_LM_ENCRYPT ? "yes" : "no"),
228: (dlc->rd_mode & RFCOMM_LM_SECURE ? "yes" : "no"));
229:
230: if (dlc->rd_mode & RFCOMM_LM_AUTH)
231: mode |= L2CAP_LM_AUTH;
232:
233: if (dlc->rd_mode & RFCOMM_LM_ENCRYPT)
234: mode |= L2CAP_LM_ENCRYPT;
235:
236: if (dlc->rd_mode & RFCOMM_LM_SECURE)
237: mode |= L2CAP_LM_SECURE;
238:
239: return l2cap_setopt(dlc->rd_session->rs_l2cap, SO_L2CAP_LM, &mode);
240: }
241:
242: /*
243: * rfcomm_dlc_connect(rfcomm_dlc)
244: *
245: * initiate DLC connection (session is already connected)
246: */
247: int
248: rfcomm_dlc_connect(struct rfcomm_dlc *dlc)
249: {
250: struct rfcomm_mcc_pn pn;
251: int err = 0;
252:
253: KASSERT(dlc->rd_session != NULL);
254: KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
255: KASSERT(dlc->rd_state == RFCOMM_DLC_WAIT_SESSION);
256:
257: /*
258: * If we have not already sent a PN on the session, we must send
259: * a PN to negotiate Credit Flow Control, and this setting will
260: * apply to all future connections for this session. We ask for
261: * this every time, in order to establish initial credits.
262: */
263: memset(&pn, 0, sizeof(pn));
264: pn.dlci = dlc->rd_dlci;
265: pn.priority = dlc->rd_dlci | 0x07;
266: pn.mtu = htole16(dlc->rd_mtu);
267:
268: pn.flow_control = 0xf0;
269: dlc->rd_rxcred = (dlc->rd_rxsize / dlc->rd_mtu);
270: dlc->rd_rxcred = min(dlc->rd_rxcred, RFCOMM_CREDITS_DEFAULT);
271: pn.credits = dlc->rd_rxcred;
272:
273: err = rfcomm_session_send_mcc(dlc->rd_session, 1,
274: RFCOMM_MCC_PN, &pn, sizeof(pn));
275: if (err)
276: return err;
277:
278: dlc->rd_state = RFCOMM_DLC_WAIT_CONNECT;
279: timeout_add(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
280:
281: return 0;
282: }
283:
284: /*
285: * rfcomm_dlc_open(rfcomm_dlc)
286: *
287: * send "Modem Status Command" and mark DLC as open.
288: */
289: int
290: rfcomm_dlc_open(struct rfcomm_dlc *dlc)
291: {
292: struct rfcomm_mcc_msc msc;
293: int err;
294:
295: KASSERT(dlc->rd_session != NULL);
296: KASSERT(dlc->rd_session->rs_state == RFCOMM_SESSION_OPEN);
297:
298: memset(&msc, 0, sizeof(msc));
299: msc.address = RFCOMM_MKADDRESS(1, dlc->rd_dlci);
300: msc.modem = dlc->rd_lmodem & 0xfe; /* EA = 0 */
301: msc.brk = 0x00 | 0x01; /* EA = 1 */
302:
303: err = rfcomm_session_send_mcc(dlc->rd_session, 1,
304: RFCOMM_MCC_MSC, &msc, sizeof(msc));
305: if (err)
306: return err;
307:
308: timeout_add(&dlc->rd_timeout, rfcomm_mcc_timeout * hz);
309:
310: dlc->rd_state = RFCOMM_DLC_OPEN;
311: (*dlc->rd_proto->connected)(dlc->rd_upper);
312:
313: return 0;
314: }
315:
316: /*
317: * rfcomm_dlc_start(rfcomm_dlc)
318: *
319: * Start sending data (and/or credits) for DLC. Our strategy is to
320: * send anything we can down to the l2cap layer. When credits run
321: * out, data will naturally bunch up. When not using credit flow
322: * control, we limit the number of packets we have pending to reduce
323: * flow control lag.
324: * We should deal with channel priority somehow.
325: */
326: void
327: rfcomm_dlc_start(struct rfcomm_dlc *dlc)
328: {
329: struct rfcomm_session *rs = dlc->rd_session;
330: struct mbuf *m;
331: int len, credits;
332:
333: KASSERT(rs != NULL);
334: KASSERT(rs->rs_state == RFCOMM_SESSION_OPEN);
335: KASSERT(dlc->rd_state == RFCOMM_DLC_OPEN);
336:
337: for (;;) {
338: credits = 0;
339: len = dlc->rd_mtu;
340: if (rs->rs_flags & RFCOMM_SESSION_CFC) {
341: credits = (dlc->rd_rxsize / dlc->rd_mtu);
342: credits -= dlc->rd_rxcred;
343: credits = min(credits, RFCOMM_CREDITS_MAX);
344:
345: if (credits > 0)
346: len--;
347:
348: if (dlc->rd_txcred == 0)
349: len = 0;
350: } else {
351: if (rs->rs_flags & RFCOMM_SESSION_RFC)
352: break;
353:
354: if (dlc->rd_rmodem & RFCOMM_MSC_FC)
355: break;
356:
357: if (dlc->rd_pending > RFCOMM_CREDITS_DEFAULT)
358: break;
359: }
360:
361: if (dlc->rd_txbuf == NULL)
362: len = 0;
363:
364: if (len == 0) {
365: if (credits == 0)
366: break;
367:
368: /*
369: * No need to send small numbers of credits on their
370: * own unless the other end hasn't many left.
371: */
372: if (credits < RFCOMM_CREDITS_DEFAULT
373: && dlc->rd_rxcred > RFCOMM_CREDITS_DEFAULT)
374: break;
375:
376: m = NULL;
377: } else {
378: /*
379: * take what data we can from (front of) txbuf
380: */
381: m = dlc->rd_txbuf;
382: if (len < m->m_pkthdr.len) {
383: dlc->rd_txbuf = m_split(m, len, M_DONTWAIT);
384: if (dlc->rd_txbuf == NULL) {
385: dlc->rd_txbuf = m;
386: break;
387: }
388: } else {
389: dlc->rd_txbuf = NULL;
390: len = m->m_pkthdr.len;
391: }
392: }
393:
394: DPRINTFN(10, "dlci %d send %d bytes, %d credits, rxcred = %d\n",
395: dlc->rd_dlci, len, credits, dlc->rd_rxcred);
396:
397: if (rfcomm_session_send_uih(rs, dlc, credits, m)) {
398: printf("%s: lost %d bytes on DLCI %d\n",
399: __func__, len, dlc->rd_dlci);
400:
401: break;
402: }
403:
404: dlc->rd_pending++;
405:
406: if (rs->rs_flags & RFCOMM_SESSION_CFC) {
407: if (len > 0)
408: dlc->rd_txcred--;
409:
410: if (credits > 0)
411: dlc->rd_rxcred += credits;
412: }
413: }
414: }
CVSweb