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