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