]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c
MFV: r361597
[FreeBSD/FreeBSD.git] / sys / netgraph / bluetooth / l2cap / ng_l2cap_cmds.c
1 /*
2  * ng_l2cap_cmds.c
3  */
4
5 /*-
6  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7  *
8  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
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_cmds.c,v 1.2 2003/09/08 19:11:45 max Exp $
33  * $FreeBSD$
34  */
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/endian.h>
40 #include <sys/malloc.h>
41 #include <sys/mbuf.h>
42 #include <sys/queue.h>
43 #include <netgraph/ng_message.h>
44 #include <netgraph/netgraph.h>
45 #include <netgraph/bluetooth/include/ng_bluetooth.h>
46 #include <netgraph/bluetooth/include/ng_hci.h>
47 #include <netgraph/bluetooth/include/ng_l2cap.h>
48 #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
49 #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
50 #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
51 #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
52 #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
53 #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
54
55 /******************************************************************************
56  ******************************************************************************
57  **                    L2CAP commands processing module
58  ******************************************************************************
59  ******************************************************************************/
60
61 /*
62  * Process L2CAP command queue on connection
63  */
64
65 void
66 ng_l2cap_con_wakeup(ng_l2cap_con_p con)
67 {
68         ng_l2cap_cmd_p   cmd = NULL;
69         struct mbuf     *m = NULL;
70         int              error = 0;
71
72         /* Find first non-pending command in the queue */
73         TAILQ_FOREACH(cmd, &con->cmd_list, next) {
74                 KASSERT((cmd->con == con),
75 ("%s: %s - invalid connection pointer!\n",
76                         __func__, NG_NODE_NAME(con->l2cap->node)));
77
78                 if (!(cmd->flags & NG_L2CAP_CMD_PENDING))
79                         break;
80         }
81
82         if (cmd == NULL)
83                 return;
84
85         /* Detach command packet */
86         m = cmd->aux;
87         cmd->aux = NULL;
88
89         /* Process command */
90         switch (cmd->code) {
91         case NG_L2CAP_DISCON_RSP:
92         case NG_L2CAP_ECHO_RSP:
93         case NG_L2CAP_INFO_RSP:
94                 /*
95                  * Do not check return ng_l2cap_lp_send() value, because
96                  * in these cases we do not really have a graceful way out.
97                  * ECHO and INFO responses are internal to the stack and not
98                  * visible to user. REJect is just being nice to remote end
99                  * (otherwise remote end will timeout anyway). DISCON is
100                  * probably most interesting here, however, if it fails
101                  * there is nothing we can do anyway.
102                  */
103
104                 (void) ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
105                 ng_l2cap_unlink_cmd(cmd);
106                 ng_l2cap_free_cmd(cmd);
107                 break;
108         case NG_L2CAP_CMD_REJ:
109                 (void) ng_l2cap_lp_send(con,
110                                         (con->linktype == NG_HCI_LINK_ACL)?
111                                         NG_L2CAP_SIGNAL_CID:
112                                         NG_L2CAP_LESIGNAL_CID
113                                         , m);
114                 ng_l2cap_unlink_cmd(cmd);
115                 ng_l2cap_free_cmd(cmd);
116                 break;
117                 
118         case NG_L2CAP_CON_REQ:
119                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
120                 if (error != 0) {
121                         ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
122                                 NG_L2CAP_NO_RESOURCES, 0);
123                         ng_l2cap_free_chan(cmd->ch); /* will free commands */
124                 } else
125                         ng_l2cap_command_timeout(cmd,
126                                 bluetooth_l2cap_rtx_timeout());
127                 break;
128         case NG_L2CAP_CON_RSP:
129                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
130                 ng_l2cap_unlink_cmd(cmd);
131                 if (cmd->ch != NULL) {
132                         ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
133                                 (error == 0)? NG_L2CAP_SUCCESS : 
134                                         NG_L2CAP_NO_RESOURCES);
135                         if (error != 0)
136                                 ng_l2cap_free_chan(cmd->ch);
137                 }
138                 ng_l2cap_free_cmd(cmd);
139                 break;
140
141         case NG_L2CAP_CFG_REQ:
142                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
143                 if (error != 0) {
144                         ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token,
145                                 NG_L2CAP_NO_RESOURCES);
146                         ng_l2cap_unlink_cmd(cmd);
147                         ng_l2cap_free_cmd(cmd);
148                 } else
149                         ng_l2cap_command_timeout(cmd,
150                                 bluetooth_l2cap_rtx_timeout());
151                 break;
152
153         case NG_L2CAP_CFG_RSP:
154                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
155                 ng_l2cap_unlink_cmd(cmd);
156                 if (cmd->ch != NULL)
157                         ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token,
158                                 (error == 0)? NG_L2CAP_SUCCESS :
159                                         NG_L2CAP_NO_RESOURCES);
160                 ng_l2cap_free_cmd(cmd);
161                 break;
162
163         case NG_L2CAP_DISCON_REQ:
164                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
165                 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
166                         (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES);
167                 if (error != 0)
168                         ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
169                 else
170                         ng_l2cap_command_timeout(cmd,
171                                 bluetooth_l2cap_rtx_timeout());
172                 break;
173
174         case NG_L2CAP_ECHO_REQ:
175                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
176                 if (error != 0) {
177                         ng_l2cap_l2ca_ping_rsp(con, cmd->token,
178                                         NG_L2CAP_NO_RESOURCES, NULL);
179                         ng_l2cap_unlink_cmd(cmd);
180                         ng_l2cap_free_cmd(cmd);
181                 } else
182                         ng_l2cap_command_timeout(cmd, 
183                                 bluetooth_l2cap_rtx_timeout());
184                 break;
185
186         case NG_L2CAP_INFO_REQ:
187                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
188                 if (error != 0) {
189                         ng_l2cap_l2ca_get_info_rsp(con, cmd->token, 
190                                 NG_L2CAP_NO_RESOURCES, NULL);
191                         ng_l2cap_unlink_cmd(cmd);
192                         ng_l2cap_free_cmd(cmd);
193                 } else
194                         ng_l2cap_command_timeout(cmd, 
195                                 bluetooth_l2cap_rtx_timeout());
196                 break;
197
198         case NGM_L2CAP_L2CA_WRITE: {
199                 int     length = m->m_pkthdr.len;
200
201                 if (cmd->ch->dcid == NG_L2CAP_CLT_CID) {
202                         m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t));
203                         if (m == NULL)
204                                 error = ENOBUFS;
205                         else
206                                 mtod(m, ng_l2cap_clt_hdr_t *)->psm =
207                                                         htole16(cmd->ch->psm);
208                 }
209
210                 if (error == 0)
211                         error = ng_l2cap_lp_send(con, cmd->ch->dcid, m);
212
213                 ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token,
214                         (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES,
215                         length);
216
217                 ng_l2cap_unlink_cmd(cmd);
218                 ng_l2cap_free_cmd(cmd);
219                 } break;
220         case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:
221                 error = ng_l2cap_lp_send(con, NG_L2CAP_LESIGNAL_CID, m);
222                 ng_l2cap_unlink_cmd(cmd);
223                 ng_l2cap_free_cmd(cmd);
224                 break;
225         case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST:
226                   /*TBD.*/
227         /* XXX FIXME add other commands */
228         default:
229                 panic(
230 "%s: %s - unknown command code=%d\n",
231                         __func__, NG_NODE_NAME(con->l2cap->node), cmd->code);
232                 break;
233         }
234 } /* ng_l2cap_con_wakeup */
235
236 /*
237  * We have failed to open ACL connection to the remote unit. Could be negative
238  * confirmation or timeout. So fail any "delayed" commands, notify upper layer,
239  * remove all channels and remove connection descriptor.
240  */
241
242 void
243 ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)
244 {
245         ng_l2cap_p      l2cap = con->l2cap;
246         ng_l2cap_cmd_p  cmd = NULL;
247         ng_l2cap_chan_p ch = NULL;
248
249         NG_L2CAP_INFO(
250 "%s: %s - ACL connection failed, result=%d\n",
251                 __func__, NG_NODE_NAME(l2cap->node), result);
252
253         /* Connection is dying */
254         con->flags |= NG_L2CAP_CON_DYING;
255
256         /* Clean command queue */
257         while (!TAILQ_EMPTY(&con->cmd_list)) {
258                 cmd = TAILQ_FIRST(&con->cmd_list);
259
260                 ng_l2cap_unlink_cmd(cmd);
261                 if(cmd->flags & NG_L2CAP_CMD_PENDING)
262                         ng_l2cap_command_untimeout(cmd);
263
264                 KASSERT((cmd->con == con),
265 ("%s: %s - invalid connection pointer!\n",
266                         __func__, NG_NODE_NAME(l2cap->node)));
267
268                 switch (cmd->code) {
269                 case NG_L2CAP_CMD_REJ:
270                 case NG_L2CAP_DISCON_RSP:
271                 case NG_L2CAP_ECHO_RSP:
272                 case NG_L2CAP_INFO_RSP:
273                 case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:
274                         break;
275
276                 case NG_L2CAP_CON_REQ:
277                         ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);
278                         break;
279
280                 case NG_L2CAP_CON_RSP:
281                         if (cmd->ch != NULL)
282                                 ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
283                                         result);
284                         break;
285
286                 case NG_L2CAP_CFG_REQ:
287                 case NG_L2CAP_CFG_RSP:
288                 case NGM_L2CAP_L2CA_WRITE:
289                         ng_l2cap_l2ca_discon_ind(cmd->ch);
290                         break;
291
292                 case NG_L2CAP_DISCON_REQ:
293                         ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
294                                 NG_L2CAP_SUCCESS);
295                         break;
296
297                 case NG_L2CAP_ECHO_REQ:
298                         ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
299                                 result, NULL);
300                         break;
301
302                 case NG_L2CAP_INFO_REQ:
303                         ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
304                                 result, NULL);
305                         break;
306
307                 /* XXX FIXME add other commands */
308
309                 default:
310                         panic(
311 "%s: %s - unexpected command code=%d\n",
312                                 __func__, NG_NODE_NAME(l2cap->node), cmd->code);
313                         break;
314                 }
315
316                 if (cmd->ch != NULL)
317                         ng_l2cap_free_chan(cmd->ch);
318
319                 ng_l2cap_free_cmd(cmd);
320         }
321
322         /*
323          * There still might be channels (in OPEN state?) that
324          * did not submit any commands, so disconnect them
325          */
326
327         LIST_FOREACH(ch, &l2cap->chan_list, next)
328                 if (ch->con == con)
329                         ng_l2cap_l2ca_discon_ind(ch);
330
331         /* Free connection descriptor */
332         ng_l2cap_free_con(con);
333 } /* ng_l2cap_con_fail */
334
335 /*
336  * Process L2CAP command timeout. In general - notify upper layer and destroy
337  * channel. Do not pay much attention to return code, just do our best.
338  */
339
340 void
341 ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
342 {
343         ng_l2cap_p      l2cap = NULL;
344         ng_l2cap_con_p  con = NULL;
345         ng_l2cap_cmd_p  cmd = NULL;
346         u_int16_t       con_handle = (arg2 & 0x0ffff);
347         u_int8_t        ident = ((arg2 >> 16) & 0xff);
348
349         if (NG_NODE_NOT_VALID(node)) {
350                 printf("%s: Netgraph node is not valid\n", __func__);
351                 return;
352         }
353
354         l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
355
356         con = ng_l2cap_con_by_handle(l2cap, con_handle);
357         if (con == NULL) {
358                 NG_L2CAP_ALERT(
359 "%s: %s - could not find connection, con_handle=%d\n",
360                         __func__, NG_NODE_NAME(node), con_handle);
361                 return;
362         }
363
364         cmd = ng_l2cap_cmd_by_ident(con, ident);
365         if (cmd == NULL) {
366                 NG_L2CAP_ALERT(
367 "%s: %s - could not find command, con_handle=%d, ident=%d\n",
368                         __func__, NG_NODE_NAME(node), con_handle, ident);
369                 return;
370         }
371
372         cmd->flags &= ~NG_L2CAP_CMD_PENDING;
373         ng_l2cap_unlink_cmd(cmd);
374
375         switch (cmd->code) {
376         case NG_L2CAP_CON_REQ:
377                 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);
378                 ng_l2cap_free_chan(cmd->ch); 
379                 break;
380
381         case NG_L2CAP_CFG_REQ:
382                 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
383                 break;
384
385         case NG_L2CAP_DISCON_REQ:
386                 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
387                 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
388                 break;
389
390         case NG_L2CAP_ECHO_REQ:
391                 /* Echo request timed out. Let the upper layer know */
392                 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
393                         NG_L2CAP_TIMEOUT, NULL);
394                 break;
395
396         case NG_L2CAP_INFO_REQ:
397                 /* Info request timed out. Let the upper layer know */
398                 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
399                         NG_L2CAP_TIMEOUT, NULL);
400                 break;
401
402         /* XXX FIXME add other commands */
403
404         default:
405                 panic(
406 "%s: %s - unexpected command code=%d\n",
407                         __func__, NG_NODE_NAME(l2cap->node), cmd->code);
408                 break;
409         }
410
411         ng_l2cap_free_cmd(cmd);
412 } /* ng_l2cap_process_command_timeout */
413