Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netgraph/bluetooth/l2cap/ng_l2cap_llpi.c
34814 views
1
/*
2
* ng_l2cap_llpi.c
3
*/
4
5
/*-
6
* SPDX-License-Identifier: BSD-2-Clause
7
*
8
* Copyright (c) Maksim Yevmenkin <[email protected]>
9
* All rights reserved.
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
* SUCH DAMAGE.
31
*
32
* $Id: ng_l2cap_llpi.c,v 1.5 2003/09/08 19:11:45 max Exp $
33
*/
34
35
#include <sys/param.h>
36
#include <sys/systm.h>
37
#include <sys/kernel.h>
38
#include <sys/endian.h>
39
#include <sys/malloc.h>
40
#include <sys/mbuf.h>
41
#include <sys/queue.h>
42
#include <netgraph/ng_message.h>
43
#include <netgraph/netgraph.h>
44
#include <netgraph/bluetooth/include/ng_bluetooth.h>
45
#include <netgraph/bluetooth/include/ng_hci.h>
46
#include <netgraph/bluetooth/include/ng_l2cap.h>
47
#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
48
#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
49
#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
50
#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
51
#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
52
#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
53
54
/******************************************************************************
55
******************************************************************************
56
** Lower Layer Protocol (HCI) Interface module
57
******************************************************************************
58
******************************************************************************/
59
60
/*
61
* Send LP_ConnectReq event to the lower layer protocol. Create new connection
62
* descriptor and initialize it. Create LP_ConnectReq event and send it to the
63
* lower layer, then adjust connection state and start timer. The function WILL
64
* FAIL if connection to the remote unit already exists.
65
*/
66
67
int
68
ng_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr, int type)
69
{
70
struct ng_mesg *msg = NULL;
71
ng_hci_lp_con_req_ep *ep = NULL;
72
ng_l2cap_con_p con = NULL;
73
int error = 0;
74
75
/* Verify that we DO NOT have connection to the remote unit */
76
con = ng_l2cap_con_by_addr(l2cap, bdaddr, type);
77
if (con != NULL) {
78
NG_L2CAP_ALERT(
79
"%s: %s - unexpected LP_ConnectReq event. " \
80
"Connection already exists, state=%d, con_handle=%d\n",
81
__func__, NG_NODE_NAME(l2cap->node), con->state,
82
con->con_handle);
83
84
return (EEXIST);
85
}
86
87
/* Check if lower layer protocol is still connected */
88
if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
89
NG_L2CAP_ERR(
90
"%s: %s - hook \"%s\" is not connected or valid\n",
91
__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
92
93
return (ENOTCONN);
94
}
95
96
/* Create and intialize new connection descriptor */
97
con = ng_l2cap_new_con(l2cap, bdaddr, type);
98
if (con == NULL)
99
return (ENOMEM);
100
101
/* Create and send LP_ConnectReq event */
102
NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ,
103
sizeof(*ep), M_NOWAIT);
104
if (msg == NULL) {
105
ng_l2cap_free_con(con);
106
107
return (ENOMEM);
108
}
109
110
ep = (ng_hci_lp_con_req_ep *) (msg->data);
111
bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
112
ep->link_type = type;
113
114
con->flags |= NG_L2CAP_CON_OUTGOING;
115
con->state = NG_L2CAP_W4_LP_CON_CFM;
116
ng_l2cap_lp_timeout(con);
117
118
NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
119
if (error != 0) {
120
if (ng_l2cap_lp_untimeout(con) == 0)
121
ng_l2cap_free_con(con);
122
123
/*
124
* Do not free connection if ng_l2cap_lp_untimeout() failed
125
* let timeout handler deal with it. Always return error to
126
* the caller.
127
*/
128
}
129
130
return (error);
131
} /* ng_l2cap_lp_con_req */
132
133
/*
134
* Process LP_ConnectCfm event from the lower layer protocol. It could be
135
* positive or negative. Verify remote unit address then stop the timer and
136
* process event.
137
*/
138
139
int
140
ng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
141
{
142
ng_hci_lp_con_cfm_ep *ep = NULL;
143
ng_l2cap_con_p con = NULL;
144
int error = 0;
145
146
/* Check message */
147
if (msg->header.arglen != sizeof(*ep)) {
148
NG_L2CAP_ALERT(
149
"%s: %s - invalid LP_ConnectCfm[Neg] message size\n",
150
__func__, NG_NODE_NAME(l2cap->node));
151
error = EMSGSIZE;
152
goto out;
153
}
154
155
ep = (ng_hci_lp_con_cfm_ep *) (msg->data);
156
/* Check if we have requested/accepted this connection */
157
con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type);
158
if (con == NULL) {
159
NG_L2CAP_ERR(
160
"%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n",
161
__func__, NG_NODE_NAME(l2cap->node));
162
error = ENOENT;
163
goto out;
164
}
165
166
/* Check connection state */
167
if (con->state != NG_L2CAP_W4_LP_CON_CFM) {
168
NG_L2CAP_ALERT(
169
"%s: %s - unexpected LP_ConnectCfm event. " \
170
"Invalid connection state, state=%d, con_handle=%d\n",
171
__func__, NG_NODE_NAME(l2cap->node), con->state,
172
con->con_handle);
173
error = EINVAL;
174
goto out;
175
}
176
177
/*
178
* Looks like it is our confirmation. It is safe now to cancel
179
* connection timer and notify upper layer. If timeout already
180
* happened then ignore connection confirmation and let timeout
181
* handle that.
182
*/
183
184
if ((error = ng_l2cap_lp_untimeout(con)) != 0)
185
goto out;
186
187
if (ep->status == 0) {
188
con->state = NG_L2CAP_CON_OPEN;
189
con->con_handle = ep->con_handle;
190
ng_l2cap_lp_deliver(con);
191
} else /* Negative confirmation - remove connection descriptor */
192
ng_l2cap_con_fail(con, ep->status);
193
out:
194
return (error);
195
} /* ng_l2cap_lp_con_cfm */
196
197
/*
198
* Process LP_ConnectInd event from the lower layer protocol. This is a good
199
* place to put some extra check on remote unit address and/or class. We could
200
* even forward this information to control hook (or check against internal
201
* black list) and thus implement some kind of firewall. But for now be simple
202
* and create new connection descriptor, start timer and send LP_ConnectRsp
203
* event (i.e. accept connection).
204
*/
205
206
int
207
ng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
208
{
209
ng_hci_lp_con_ind_ep *ep = NULL;
210
ng_hci_lp_con_rsp_ep *rp = NULL;
211
struct ng_mesg *rsp = NULL;
212
ng_l2cap_con_p con = NULL;
213
int error = 0;
214
215
/* Check message */
216
if (msg->header.arglen != sizeof(*ep)) {
217
NG_L2CAP_ALERT(
218
"%s: %s - invalid LP_ConnectInd message size\n",
219
__func__, NG_NODE_NAME(l2cap->node));
220
221
return (EMSGSIZE);
222
}
223
224
ep = (ng_hci_lp_con_ind_ep *) (msg->data);
225
226
/* Make sure we have only one connection to the remote unit */
227
con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type);
228
if (con != NULL) {
229
NG_L2CAP_ALERT(
230
"%s: %s - unexpected LP_ConnectInd event. " \
231
"Connection already exists, state=%d, con_handle=%d\n",
232
__func__, NG_NODE_NAME(l2cap->node), con->state,
233
con->con_handle);
234
235
return (EEXIST);
236
}
237
238
/* Check if lower layer protocol is still connected */
239
if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
240
NG_L2CAP_ERR(
241
"%s: %s - hook \"%s\" is not connected or valid",
242
__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
243
244
return (ENOTCONN);
245
}
246
247
/* Create and intialize new connection descriptor */
248
con = ng_l2cap_new_con(l2cap, &ep->bdaddr, ep->link_type);
249
if (con == NULL)
250
return (ENOMEM);
251
252
/* Create and send LP_ConnectRsp event */
253
NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,
254
sizeof(*rp), M_NOWAIT);
255
if (rsp == NULL) {
256
ng_l2cap_free_con(con);
257
258
return (ENOMEM);
259
}
260
261
rp = (ng_hci_lp_con_rsp_ep *)(rsp->data);
262
rp->status = 0x00; /* accept connection */
263
rp->link_type = NG_HCI_LINK_ACL;
264
bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr));
265
266
con->state = NG_L2CAP_W4_LP_CON_CFM;
267
ng_l2cap_lp_timeout(con);
268
269
NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0);
270
if (error != 0) {
271
if (ng_l2cap_lp_untimeout(con) == 0)
272
ng_l2cap_free_con(con);
273
274
/*
275
* Do not free connection if ng_l2cap_lp_untimeout() failed
276
* let timeout handler deal with it. Always return error to
277
* the caller.
278
*/
279
}
280
281
return (error);
282
} /* ng_l2cap_lp_con_ind */
283
284
/*
285
* Process LP_DisconnectInd event from the lower layer protocol. We have been
286
* disconnected from the remote unit. So notify the upper layer protocol.
287
*/
288
289
int
290
ng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
291
{
292
ng_hci_lp_discon_ind_ep *ep = NULL;
293
ng_l2cap_con_p con = NULL;
294
int error = 0;
295
296
/* Check message */
297
if (msg->header.arglen != sizeof(*ep)) {
298
NG_L2CAP_ALERT(
299
"%s: %s - invalid LP_DisconnectInd message size\n",
300
__func__, NG_NODE_NAME(l2cap->node));
301
error = EMSGSIZE;
302
goto out;
303
}
304
305
ep = (ng_hci_lp_discon_ind_ep *) (msg->data);
306
307
/* Check if we have this connection */
308
con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
309
if (con == NULL) {
310
NG_L2CAP_ERR(
311
"%s: %s - unexpected LP_DisconnectInd event. " \
312
"Connection does not exist, con_handle=%d\n",
313
__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
314
error = ENOENT;
315
goto out;
316
}
317
318
/* XXX Verify connection state -- do we need to check this? */
319
if (con->state != NG_L2CAP_CON_OPEN) {
320
NG_L2CAP_ERR(
321
"%s: %s - unexpected LP_DisconnectInd event. " \
322
"Invalid connection state, state=%d, con_handle=%d\n",
323
__func__, NG_NODE_NAME(l2cap->node), con->state,
324
con->con_handle);
325
error = EINVAL;
326
goto out;
327
}
328
329
/*
330
* Notify upper layer and remove connection
331
* Note: The connection could have auto disconnect timeout set. Try
332
* to remove it. If auto disconnect timeout happened then ignore
333
* disconnect indication and let timeout handle that.
334
*/
335
336
if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)
337
if ((error = ng_l2cap_discon_untimeout(con)) != 0)
338
return (error);
339
340
ng_l2cap_con_fail(con, ep->reason);
341
out:
342
return (error);
343
} /* ng_l2cap_lp_discon_ind */
344
345
/*
346
* Send LP_QoSSetupReq event to the lower layer protocol
347
*/
348
349
int
350
ng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle,
351
ng_l2cap_flow_p flow)
352
{
353
struct ng_mesg *msg = NULL;
354
ng_hci_lp_qos_req_ep *ep = NULL;
355
ng_l2cap_con_p con = NULL;
356
int error = 0;
357
358
/* Verify that we have this connection */
359
con = ng_l2cap_con_by_handle(l2cap, con_handle);
360
if (con == NULL) {
361
NG_L2CAP_ERR(
362
"%s: %s - unexpected LP_QoSSetupReq event. " \
363
"Connection does not exist, con_handle=%d\n",
364
__func__, NG_NODE_NAME(l2cap->node), con_handle);
365
366
return (ENOENT);
367
}
368
369
/* Verify connection state */
370
if (con->state != NG_L2CAP_CON_OPEN) {
371
NG_L2CAP_ERR(
372
"%s: %s - unexpected LP_QoSSetupReq event. " \
373
"Invalid connection state, state=%d, con_handle=%d\n",
374
__func__, NG_NODE_NAME(l2cap->node), con->state,
375
con->con_handle);
376
377
return (EINVAL);
378
}
379
380
/* Check if lower layer protocol is still connected */
381
if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
382
NG_L2CAP_ERR(
383
"%s: %s - hook \"%s\" is not connected or valid",
384
__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
385
386
return (ENOTCONN);
387
}
388
389
/* Create and send LP_QoSSetupReq event */
390
NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ,
391
sizeof(*ep), M_NOWAIT);
392
if (msg == NULL)
393
return (ENOMEM);
394
395
ep = (ng_hci_lp_qos_req_ep *) (msg->data);
396
ep->con_handle = con_handle;
397
ep->flags = flow->flags;
398
ep->service_type = flow->service_type;
399
ep->token_rate = flow->token_rate;
400
ep->peak_bandwidth = flow->peak_bandwidth;
401
ep->latency = flow->latency;
402
ep->delay_variation = flow->delay_variation;
403
404
NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
405
406
return (error);
407
} /* ng_l2cap_lp_con_req */
408
409
/*
410
* Process LP_QoSSetupCfm from the lower layer protocol
411
*/
412
413
int
414
ng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
415
{
416
ng_hci_lp_qos_cfm_ep *ep = NULL;
417
int error = 0;
418
419
/* Check message */
420
if (msg->header.arglen != sizeof(*ep)) {
421
NG_L2CAP_ALERT(
422
"%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n",
423
__func__, NG_NODE_NAME(l2cap->node));
424
error = EMSGSIZE;
425
goto out;
426
}
427
428
ep = (ng_hci_lp_qos_cfm_ep *) (msg->data);
429
/* XXX FIXME do something */
430
out:
431
return (error);
432
} /* ng_l2cap_lp_qos_cfm */
433
434
/*
435
* Process LP_QoSViolationInd event from the lower layer protocol. Lower
436
* layer protocol has detected QoS Violation, so we MUST notify the
437
* upper layer.
438
*/
439
440
int
441
ng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
442
{
443
ng_hci_lp_qos_ind_ep *ep = NULL;
444
ng_l2cap_con_p con = NULL;
445
int error = 0;
446
447
/* Check message */
448
if (msg->header.arglen != sizeof(*ep)) {
449
NG_L2CAP_ALERT(
450
"%s: %s - invalid LP_QoSViolation message size\n",
451
__func__, NG_NODE_NAME(l2cap->node));
452
error = EMSGSIZE;
453
goto out;
454
}
455
456
ep = (ng_hci_lp_qos_ind_ep *) (msg->data);
457
458
/* Check if we have this connection */
459
con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
460
if (con == NULL) {
461
NG_L2CAP_ERR(
462
"%s: %s - unexpected LP_QoSViolationInd event. " \
463
"Connection does not exist, con_handle=%d\n",
464
__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
465
error = ENOENT;
466
goto out;
467
}
468
469
/* Verify connection state */
470
if (con->state != NG_L2CAP_CON_OPEN) {
471
NG_L2CAP_ERR(
472
"%s: %s - unexpected LP_QoSViolationInd event. " \
473
"Invalid connection state, state=%d, con_handle=%d\n",
474
__func__, NG_NODE_NAME(l2cap->node), con->state,
475
con->con_handle);
476
error = EINVAL;
477
goto out;
478
}
479
480
/* XXX FIXME Notify upper layer and terminate channels if required */
481
out:
482
return (error);
483
} /* ng_l2cap_qos_ind */
484
485
int
486
ng_l2cap_lp_enc_change(ng_l2cap_p l2cap, struct ng_mesg *msg)
487
{
488
ng_hci_lp_enc_change_ep *ep = NULL;
489
ng_l2cap_con_p con = NULL;
490
int error = 0;
491
ng_l2cap_chan_p ch = NULL;
492
/* Check message */
493
if (msg->header.arglen != sizeof(*ep)) {
494
NG_L2CAP_ALERT(
495
"%s: %s - invalid LP_ENCChange message size\n",
496
__func__, NG_NODE_NAME(l2cap->node));
497
error = EMSGSIZE;
498
goto out;
499
}
500
501
ep = (ng_hci_lp_enc_change_ep *) (msg->data);
502
503
/* Check if we have this connection */
504
con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
505
if (con == NULL) {
506
NG_L2CAP_ERR(
507
"%s: %s - unexpected LP_Enc Change Event. " \
508
"Connection does not exist, con_handle=%d\n",
509
__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
510
error = ENOENT;
511
goto out;
512
}
513
514
/* Verify connection state */
515
if (con->state != NG_L2CAP_CON_OPEN) {
516
NG_L2CAP_ERR(
517
"%s: %s - unexpected ENC_CHANGE event. " \
518
"Invalid connection state, state=%d, con_handle=%d\n",
519
__func__, NG_NODE_NAME(l2cap->node), con->state,
520
con->con_handle);
521
error = EINVAL;
522
goto out;
523
}
524
525
con->encryption = ep->status;
526
527
LIST_FOREACH(ch, &l2cap->chan_list, next){
528
if((ch->con->con_handle == ep->con_handle) &&
529
(ch->con->linktype == ep->link_type))
530
ng_l2cap_l2ca_encryption_change(ch, ep->status);
531
}
532
533
out:
534
return (error);
535
} /* ng_l2cap_enc_change */
536
537
/*
538
* Prepare L2CAP packet. Prepend packet with L2CAP packet header and then
539
* segment it according to HCI MTU.
540
*/
541
542
int
543
ng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0)
544
{
545
ng_l2cap_p l2cap = con->l2cap;
546
ng_l2cap_hdr_t *l2cap_hdr = NULL;
547
ng_hci_acldata_pkt_t *acl_hdr = NULL;
548
struct mbuf *m_last = NULL, *m = NULL;
549
int len, flag = (con->linktype == NG_HCI_LINK_ACL) ? NG_HCI_PACKET_START : NG_HCI_LE_PACKET_START;
550
551
KASSERT((con->tx_pkt == NULL),
552
("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node)));
553
KASSERT((l2cap->pkt_size > 0),
554
("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node)));
555
556
/* Prepend mbuf with L2CAP header */
557
m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr));
558
if (m0 == NULL) {
559
NG_L2CAP_ALERT(
560
"%s: %s - ng_l2cap_prepend(%zd) failed\n",
561
__func__, NG_NODE_NAME(l2cap->node),
562
sizeof(*l2cap_hdr));
563
564
goto fail;
565
}
566
567
l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *);
568
l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr));
569
l2cap_hdr->dcid = htole16(dcid);
570
571
/*
572
* Segment single L2CAP packet according to the HCI layer MTU. Convert
573
* each segment into ACL data packet and prepend it with ACL data packet
574
* header. Link all segments together via m_nextpkt link.
575
*
576
* XXX BC (Broadcast flag) will always be 0 (zero).
577
*/
578
579
while (m0 != NULL) {
580
/* Check length of the packet against HCI MTU */
581
len = m0->m_pkthdr.len;
582
if (len > l2cap->pkt_size) {
583
m = m_split(m0, l2cap->pkt_size, M_NOWAIT);
584
if (m == NULL) {
585
NG_L2CAP_ALERT(
586
"%s: %s - m_split(%d) failed\n", __func__, NG_NODE_NAME(l2cap->node),
587
l2cap->pkt_size);
588
goto fail;
589
}
590
591
len = l2cap->pkt_size;
592
}
593
594
/* Convert packet fragment into ACL data packet */
595
m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr));
596
if (m0 == NULL) {
597
NG_L2CAP_ALERT(
598
"%s: %s - ng_l2cap_prepend(%zd) failed\n",
599
__func__, NG_NODE_NAME(l2cap->node),
600
sizeof(*acl_hdr));
601
goto fail;
602
}
603
604
acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *);
605
acl_hdr->type = NG_HCI_ACL_DATA_PKT;
606
acl_hdr->length = htole16(len);
607
acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(
608
con->con_handle, flag, 0));
609
610
/* Add fragment to the chain */
611
m0->m_nextpkt = NULL;
612
613
if (con->tx_pkt == NULL)
614
con->tx_pkt = m_last = m0;
615
else {
616
m_last->m_nextpkt = m0;
617
m_last = m0;
618
}
619
620
NG_L2CAP_INFO(
621
"%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n",
622
__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
623
flag, len);
624
625
m0 = m;
626
m = NULL;
627
flag = NG_HCI_PACKET_FRAGMENT;
628
}
629
630
return (0);
631
fail:
632
NG_FREE_M(m0);
633
NG_FREE_M(m);
634
635
while (con->tx_pkt != NULL) {
636
m = con->tx_pkt->m_nextpkt;
637
m_freem(con->tx_pkt);
638
con->tx_pkt = m;
639
}
640
641
return (ENOBUFS);
642
} /* ng_l2cap_lp_send */
643
644
/*
645
* Receive ACL data packet from the HCI layer. First strip ACL packet header
646
* and get connection handle, PB (Packet Boundary) flag and payload length.
647
* Then find connection descriptor and verify its state. Then process ACL
648
* packet as follows.
649
*
650
* 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP
651
* header and get total length of the L2CAP packet. Then start new L2CAP
652
* packet.
653
*
654
* 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT)
655
* then add segment to the packet.
656
*/
657
658
int
659
ng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m)
660
{
661
ng_hci_acldata_pkt_t *acl_hdr = NULL;
662
ng_l2cap_hdr_t *l2cap_hdr = NULL;
663
ng_l2cap_con_p con = NULL;
664
u_int16_t con_handle, length, pb;
665
int error = 0;
666
667
/* Check ACL data packet */
668
if (m->m_pkthdr.len < sizeof(*acl_hdr)) {
669
NG_L2CAP_ERR(
670
"%s: %s - invalid ACL data packet. Packet too small, length=%d\n",
671
__func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len);
672
error = EMSGSIZE;
673
goto drop;
674
}
675
676
/* Strip ACL data packet header */
677
NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr));
678
if (m == NULL)
679
return (ENOBUFS);
680
681
acl_hdr = mtod(m, ng_hci_acldata_pkt_t *);
682
m_adj(m, sizeof(*acl_hdr));
683
684
/* Get ACL connection handle, PB flag and payload length */
685
acl_hdr->con_handle = le16toh(acl_hdr->con_handle);
686
con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle);
687
pb = NG_HCI_PB_FLAG(acl_hdr->con_handle);
688
length = le16toh(acl_hdr->length);
689
690
NG_L2CAP_INFO(
691
"%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n",
692
__func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length);
693
694
/* Get connection descriptor */
695
con = ng_l2cap_con_by_handle(l2cap, con_handle);
696
if (con == NULL) {
697
NG_L2CAP_ERR(
698
"%s: %s - unexpected ACL data packet. " \
699
"Connection does not exist, con_handle=%d\n",
700
__func__, NG_NODE_NAME(l2cap->node), con_handle);
701
error = ENOENT;
702
goto drop;
703
}
704
705
/* Verify connection state */
706
if (con->state != NG_L2CAP_CON_OPEN) {
707
NG_L2CAP_ERR(
708
"%s: %s - unexpected ACL data packet. Invalid connection state=%d\n",
709
__func__, NG_NODE_NAME(l2cap->node), con->state);
710
error = EHOSTDOWN;
711
goto drop;
712
}
713
714
/* Process packet */
715
if ((pb == NG_HCI_PACKET_START) || (pb == NG_HCI_LE_PACKET_START))
716
{
717
if (con->rx_pkt != NULL) {
718
NG_L2CAP_ERR(
719
"%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n",
720
__func__, NG_NODE_NAME(l2cap->node),
721
con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
722
NG_FREE_M(con->rx_pkt);
723
con->rx_pkt_len = 0;
724
}
725
726
/* Get L2CAP header */
727
if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) {
728
NG_L2CAP_ERR(
729
"%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n",
730
__func__, NG_NODE_NAME(l2cap->node),
731
m->m_pkthdr.len);
732
error = EMSGSIZE;
733
goto drop;
734
}
735
736
NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr));
737
if (m == NULL)
738
return (ENOBUFS);
739
740
l2cap_hdr = mtod(m, ng_l2cap_hdr_t *);
741
742
NG_L2CAP_INFO(
743
"%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n",
744
__func__, NG_NODE_NAME(l2cap->node), con_handle,
745
le16toh(l2cap_hdr->length));
746
747
/* Start new L2CAP packet */
748
con->rx_pkt = m;
749
con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr);
750
} else if (pb == NG_HCI_PACKET_FRAGMENT) {
751
if (con->rx_pkt == NULL) {
752
NG_L2CAP_ERR(
753
"%s: %s - unexpected ACL data packet fragment, con_handle=%d\n",
754
__func__, NG_NODE_NAME(l2cap->node),
755
con->con_handle);
756
goto drop;
757
}
758
759
/* Add fragment to the L2CAP packet */
760
m_cat(con->rx_pkt, m);
761
con->rx_pkt->m_pkthdr.len += length;
762
} else {
763
NG_L2CAP_ERR(
764
"%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n",
765
__func__, NG_NODE_NAME(l2cap->node), pb);
766
error = EINVAL;
767
goto drop;
768
}
769
770
con->rx_pkt_len -= length;
771
if (con->rx_pkt_len < 0) {
772
NG_L2CAP_ALERT(
773
"%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n",
774
__func__, NG_NODE_NAME(l2cap->node),
775
con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
776
NG_FREE_M(con->rx_pkt);
777
con->rx_pkt_len = 0;
778
} else if (con->rx_pkt_len == 0) {
779
/* OK, we have got complete L2CAP packet, so process it */
780
error = ng_l2cap_receive(con);
781
con->rx_pkt = NULL;
782
con->rx_pkt_len = 0;
783
}
784
785
return (error);
786
787
drop:
788
NG_FREE_M(m);
789
790
return (error);
791
} /* ng_l2cap_lp_receive */
792
793
/*
794
* Send queued ACL packets to the HCI layer
795
*/
796
797
void
798
ng_l2cap_lp_deliver(ng_l2cap_con_p con)
799
{
800
ng_l2cap_p l2cap = con->l2cap;
801
struct mbuf *m = NULL;
802
int error;
803
804
/* Check connection */
805
if (con->state != NG_L2CAP_CON_OPEN)
806
return;
807
808
if (con->tx_pkt == NULL)
809
ng_l2cap_con_wakeup(con);
810
811
if (con->tx_pkt == NULL)
812
return;
813
814
/* Check if lower layer protocol is still connected */
815
if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
816
NG_L2CAP_ERR(
817
"%s: %s - hook \"%s\" is not connected or valid",
818
__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
819
820
goto drop; /* XXX what to do with "pending"? */
821
}
822
823
/* Send ACL data packets */
824
while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) {
825
m = con->tx_pkt;
826
con->tx_pkt = con->tx_pkt->m_nextpkt;
827
m->m_nextpkt = NULL;
828
829
if(m->m_flags &M_PROTO2){
830
ng_l2cap_lp_receive(con->l2cap, m);
831
continue;
832
}
833
NG_L2CAP_INFO(
834
"%s: %s - sending ACL packet, con_handle=%d, len=%d\n",
835
__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
836
m->m_pkthdr.len);
837
838
NG_SEND_DATA_ONLY(error, l2cap->hci, m);
839
if (error != 0) {
840
NG_L2CAP_ERR(
841
"%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n",
842
__func__, NG_NODE_NAME(l2cap->node),
843
con->con_handle, error);
844
845
goto drop; /* XXX what to do with "pending"? */
846
}
847
848
con->pending ++;
849
}
850
851
NG_L2CAP_INFO(
852
"%s: %s - %d ACL packets have been sent, con_handle=%d\n",
853
__func__, NG_NODE_NAME(l2cap->node), con->pending,
854
con->con_handle);
855
856
return;
857
858
drop:
859
while (con->tx_pkt != NULL) {
860
m = con->tx_pkt->m_nextpkt;
861
m_freem(con->tx_pkt);
862
con->tx_pkt = m;
863
}
864
} /* ng_l2cap_lp_deliver */
865
866
/*
867
* Process connection timeout. Remove connection from the list. If there
868
* are any channels that wait for the connection then notify them. Free
869
* connection descriptor.
870
*/
871
872
void
873
ng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
874
{
875
ng_l2cap_p l2cap = NULL;
876
ng_l2cap_con_p con = NULL;
877
878
if (NG_NODE_NOT_VALID(node)) {
879
printf("%s: Netgraph node is not valid\n", __func__);
880
return;
881
}
882
883
l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
884
con = ng_l2cap_con_by_handle(l2cap, con_handle);
885
886
if (con == NULL) {
887
NG_L2CAP_ALERT(
888
"%s: %s - could not find connection, con_handle=%d\n",
889
__func__, NG_NODE_NAME(node), con_handle);
890
return;
891
}
892
893
if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) {
894
NG_L2CAP_ALERT(
895
"%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n",
896
__func__, NG_NODE_NAME(node), con_handle, con->state,
897
con->flags);
898
return;
899
}
900
901
/*
902
* Notify channels that connection has timed out. This will remove
903
* connection, channels and pending commands.
904
*/
905
906
con->flags &= ~NG_L2CAP_CON_LP_TIMO;
907
ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT);
908
} /* ng_l2cap_process_lp_timeout */
909
910
/*
911
* Process auto disconnect timeout and send LP_DisconReq event to the
912
* lower layer protocol
913
*/
914
915
void
916
ng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
917
{
918
ng_l2cap_p l2cap = NULL;
919
ng_l2cap_con_p con = NULL;
920
struct ng_mesg *msg = NULL;
921
ng_hci_lp_discon_req_ep *ep = NULL;
922
int error;
923
924
if (NG_NODE_NOT_VALID(node)) {
925
printf("%s: Netgraph node is not valid\n", __func__);
926
return;
927
}
928
929
l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
930
con = ng_l2cap_con_by_handle(l2cap, con_handle);
931
932
if (con == NULL) {
933
NG_L2CAP_ALERT(
934
"%s: %s - could not find connection, con_handle=%d\n",
935
__func__, NG_NODE_NAME(node), con_handle);
936
return;
937
}
938
939
if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) {
940
NG_L2CAP_ALERT(
941
"%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n",
942
__func__, NG_NODE_NAME(node), con_handle, con->state,
943
con->flags);
944
return;
945
}
946
947
con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;
948
949
/* Check if lower layer protocol is still connected */
950
if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
951
NG_L2CAP_ERR(
952
"%s: %s - hook \"%s\" is not connected or valid\n",
953
__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
954
return;
955
}
956
957
/* Create and send LP_DisconReq event */
958
NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ,
959
sizeof(*ep), M_NOWAIT);
960
if (msg == NULL)
961
return;
962
963
ep = (ng_hci_lp_discon_req_ep *) (msg->data);
964
ep->con_handle = con->con_handle;
965
ep->reason = 0x13; /* User Ended Connection */
966
967
NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
968
} /* ng_l2cap_process_discon_timeout */
969
970