Netgraph port from FreeBSD - initial porting work
[dragonfly.git] / sys / netgraph7 / bluetooth / l2cap / ng_l2cap_cmds.c
1 /*
2  * ng_l2cap_cmds.c
3  */
4
5 /*-
6  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
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  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * $Id: ng_l2cap_cmds.c,v 1.2 2003/09/08 19:11:45 max Exp $
31  * $FreeBSD: src/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c,v 1.7 2007/03/28 21:25:56 emax Exp $
32  * $DragonFly: src/sys/netgraph7/bluetooth/l2cap/ng_l2cap_cmds.c,v 1.2 2008/06/26 23:05:40 dillon 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 "ng_message.h"
43 #include "netgraph.h"
44 #include "bluetooth/include/ng_bluetooth.h"
45 #include "bluetooth/include/ng_hci.h"
46 #include "bluetooth/include/ng_l2cap.h"
47 #include "bluetooth/l2cap/ng_l2cap_var.h"
48 #include "bluetooth/l2cap/ng_l2cap_cmds.h"
49 #include "bluetooth/l2cap/ng_l2cap_evnt.h"
50 #include "bluetooth/l2cap/ng_l2cap_llpi.h"
51 #include "bluetooth/l2cap/ng_l2cap_ulpi.h"
52 #include "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_CMD_REJ:
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
109         case NG_L2CAP_CON_REQ:
110                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
111                 if (error != 0) {
112                         ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
113                                 NG_L2CAP_NO_RESOURCES, 0);
114                         ng_l2cap_free_chan(cmd->ch); /* will free commands */
115                 } else
116                         ng_l2cap_command_timeout(cmd,
117                                 bluetooth_l2cap_rtx_timeout());
118                 break;
119
120         case NG_L2CAP_CON_RSP:
121                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
122                 ng_l2cap_unlink_cmd(cmd);
123                 if (cmd->ch != NULL) {
124                         ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
125                                 (error == 0)? NG_L2CAP_SUCCESS : 
126                                         NG_L2CAP_NO_RESOURCES);
127                         if (error != 0)
128                                 ng_l2cap_free_chan(cmd->ch);
129                 }
130                 ng_l2cap_free_cmd(cmd);
131                 break;
132
133         case NG_L2CAP_CFG_REQ:
134                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
135                 if (error != 0) {
136                         ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token,
137                                 NG_L2CAP_NO_RESOURCES);
138                         ng_l2cap_unlink_cmd(cmd);
139                         ng_l2cap_free_cmd(cmd);
140                 } else
141                         ng_l2cap_command_timeout(cmd,
142                                 bluetooth_l2cap_rtx_timeout());
143                 break;
144
145         case NG_L2CAP_CFG_RSP:
146                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
147                 ng_l2cap_unlink_cmd(cmd);
148                 if (cmd->ch != NULL)
149                         ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token,
150                                 (error == 0)? NG_L2CAP_SUCCESS :
151                                         NG_L2CAP_NO_RESOURCES);
152                 ng_l2cap_free_cmd(cmd);
153                 break;
154
155         case NG_L2CAP_DISCON_REQ:
156                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
157                 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
158                         (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES);
159                 if (error != 0)
160                         ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
161                 else
162                         ng_l2cap_command_timeout(cmd,
163                                 bluetooth_l2cap_rtx_timeout());
164                 break;
165
166         case NG_L2CAP_ECHO_REQ:
167                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
168                 if (error != 0) {
169                         ng_l2cap_l2ca_ping_rsp(con, cmd->token,
170                                         NG_L2CAP_NO_RESOURCES, NULL);
171                         ng_l2cap_unlink_cmd(cmd);
172                         ng_l2cap_free_cmd(cmd);
173                 } else
174                         ng_l2cap_command_timeout(cmd, 
175                                 bluetooth_l2cap_rtx_timeout());
176                 break;
177
178         case NG_L2CAP_INFO_REQ:
179                 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
180                 if (error != 0) {
181                         ng_l2cap_l2ca_get_info_rsp(con, cmd->token, 
182                                 NG_L2CAP_NO_RESOURCES, NULL);
183                         ng_l2cap_unlink_cmd(cmd);
184                         ng_l2cap_free_cmd(cmd);
185                 } else
186                         ng_l2cap_command_timeout(cmd, 
187                                 bluetooth_l2cap_rtx_timeout());
188                 break;
189
190         case NGM_L2CAP_L2CA_WRITE: {
191                 int     length = m->m_pkthdr.len;
192
193                 if (cmd->ch->dcid == NG_L2CAP_CLT_CID) {
194                         m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t));
195                         if (m == NULL)
196                                 error = ENOBUFS;
197                         else
198                                 mtod(m, ng_l2cap_clt_hdr_t *)->psm =
199                                                         htole16(cmd->ch->psm);
200                 }
201
202                 if (error == 0)
203                         error = ng_l2cap_lp_send(con, cmd->ch->dcid, m);
204
205                 ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token,
206                         (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES,
207                         length);
208
209                 ng_l2cap_unlink_cmd(cmd);
210                 ng_l2cap_free_cmd(cmd);
211                 } break;
212
213         /* XXX FIXME add other commands */
214
215         default:
216                 panic(
217 "%s: %s - unknown command code=%d\n",
218                         __func__, NG_NODE_NAME(con->l2cap->node), cmd->code);
219                 break;
220         }
221 } /* ng_l2cap_con_wakeup */
222
223 /*
224  * We have failed to open ACL connection to the remote unit. Could be negative
225  * confirmation or timeout. So fail any "delayed" commands, notify upper layer,
226  * remove all channels and remove connection descriptor.
227  */
228
229 void
230 ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)
231 {
232         ng_l2cap_p      l2cap = con->l2cap;
233         ng_l2cap_cmd_p  cmd = NULL;
234         ng_l2cap_chan_p ch = NULL;
235
236         NG_L2CAP_INFO(
237 "%s: %s - ACL connection failed, result=%d\n",
238                 __func__, NG_NODE_NAME(l2cap->node), result);
239
240         /* Connection is dying */
241         con->flags |= NG_L2CAP_CON_DYING;
242
243         /* Clean command queue */
244         while (!TAILQ_EMPTY(&con->cmd_list)) {
245                 cmd = TAILQ_FIRST(&con->cmd_list);
246
247                 ng_l2cap_unlink_cmd(cmd);
248                 if(cmd->flags & NG_L2CAP_CMD_PENDING)
249                         ng_l2cap_command_untimeout(cmd);
250
251                 KASSERT((cmd->con == con),
252 ("%s: %s - invalid connection pointer!\n",
253                         __func__, NG_NODE_NAME(l2cap->node)));
254
255                 switch (cmd->code) {
256                 case NG_L2CAP_CMD_REJ:
257                 case NG_L2CAP_DISCON_RSP:
258                 case NG_L2CAP_ECHO_RSP:
259                 case NG_L2CAP_INFO_RSP:
260                         break;
261
262                 case NG_L2CAP_CON_REQ:
263                         ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);
264                         break;
265
266                 case NG_L2CAP_CON_RSP:
267                         if (cmd->ch != NULL)
268                                 ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
269                                         result);
270                         break;
271
272                 case NG_L2CAP_CFG_REQ:
273                 case NG_L2CAP_CFG_RSP:
274                 case NGM_L2CAP_L2CA_WRITE:
275                         ng_l2cap_l2ca_discon_ind(cmd->ch);
276                         break;
277
278                 case NG_L2CAP_DISCON_REQ:
279                         ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
280                                 NG_L2CAP_SUCCESS);
281                         break;
282
283                 case NG_L2CAP_ECHO_REQ:
284                         ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
285                                 result, NULL);
286                         break;
287
288                 case NG_L2CAP_INFO_REQ:
289                         ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
290                                 result, NULL);
291                         break;
292
293                 /* XXX FIXME add other commands */
294
295                 default:
296                         panic(
297 "%s: %s - unexpected command code=%d\n",
298                                 __func__, NG_NODE_NAME(l2cap->node), cmd->code);
299                         break;
300                 }
301
302                 if (cmd->ch != NULL)
303                         ng_l2cap_free_chan(cmd->ch);
304
305                 ng_l2cap_free_cmd(cmd);
306         }
307
308         /*
309          * There still might be channels (in OPEN state?) that
310          * did not submit any commands, so diconnect them
311          */
312
313         LIST_FOREACH(ch, &l2cap->chan_list, next)
314                 if (ch->con == con)
315                         ng_l2cap_l2ca_discon_ind(ch);
316
317         /* Free connection descriptor */
318         ng_l2cap_free_con(con);
319 } /* ng_l2cap_con_fail */
320
321 /*
322  * Process L2CAP command timeout. In general - notify upper layer and destroy
323  * channel. Do not pay much attension to return code, just do our best.
324  */
325
326 void
327 ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
328 {
329         ng_l2cap_p      l2cap = NULL;
330         ng_l2cap_con_p  con = NULL;
331         ng_l2cap_cmd_p  cmd = NULL;
332         u_int16_t       con_handle = (arg2 & 0x0ffff);
333         u_int8_t        ident = ((arg2 >> 16) & 0xff);
334
335         if (NG_NODE_NOT_VALID(node)) {
336                 printf("%s: Netgraph node is not valid\n", __func__);
337                 return;
338         }
339
340         l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
341
342         con = ng_l2cap_con_by_handle(l2cap, con_handle);
343         if (con == NULL) {
344                 NG_L2CAP_ALERT(
345 "%s: %s - could not find connection, con_handle=%d\n",
346                         __func__, NG_NODE_NAME(node), con_handle);
347                 return;
348         }
349
350         cmd = ng_l2cap_cmd_by_ident(con, ident);
351         if (cmd == NULL) {
352                 NG_L2CAP_ALERT(
353 "%s: %s - could not find command, con_handle=%d, ident=%d\n",
354                         __func__, NG_NODE_NAME(node), con_handle, ident);
355                 return;
356         }
357
358         cmd->flags &= ~NG_L2CAP_CMD_PENDING;
359         ng_l2cap_unlink_cmd(cmd);
360
361         switch (cmd->code) {
362         case NG_L2CAP_CON_REQ:
363                 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);
364                 ng_l2cap_free_chan(cmd->ch); 
365                 break;
366
367         case NG_L2CAP_CFG_REQ:
368                 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
369                 break;
370
371         case NG_L2CAP_DISCON_REQ:
372                 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
373                 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
374                 break;
375
376         case NG_L2CAP_ECHO_REQ:
377                 /* Echo request timed out. Let the upper layer know */
378                 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
379                         NG_L2CAP_TIMEOUT, NULL);
380                 break;
381
382         case NG_L2CAP_INFO_REQ:
383                 /* Info request timed out. Let the upper layer know */
384                 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
385                         NG_L2CAP_TIMEOUT, NULL);
386                 break;
387
388         /* XXX FIXME add other commands */
389
390         default:
391                 panic(
392 "%s: %s - unexpected command code=%d\n",
393                         __func__, NG_NODE_NAME(l2cap->node), cmd->code);
394                 break;
395         }
396
397         ng_l2cap_free_cmd(cmd);
398 } /* ng_l2cap_process_command_timeout */
399