xref: /haiku/src/add-ons/kernel/network/protocols/l2cap/l2cap_signal.cpp (revision 7749d0bb0c358a3279b1b9cc76d8376e900130a5)
1 /*
2  * Copyright 2008 Oliver Ruiz Dorantes, oliver.ruiz.dorantes_at_gmail.com
3  * All rights reserved. Distributed under the terms of the MIT License.
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 #include <KernelExport.h>
19 #include <string.h>
20 
21 #include <NetBufferUtilities.h>
22 
23 
24 #include <l2cap.h>
25 #include "l2cap_internal.h"
26 #include "l2cap_signal.h"
27 #include "l2cap_command.h"
28 #include "l2cap_upper.h"
29 #include "l2cap_lower.h"
30 
31 #define BT_DEBUG_THIS_MODULE
32 #define SUBMODULE_NAME "signal"
33 #define SUBMODULE_COLOR 36
34 #include <btDebug.h>
35 
36 #include <bluetooth/HCI/btHCI_command.h>
37 
38 typedef enum _option_status {
39 	OPTION_NOT_PRESENT = 0,
40 	OPTION_PRESENT = 1,
41 	HEADER_TOO_SHORT = -1,
42 	BAD_OPTION_LENGTH = -2,
43 	OPTION_UNKNOWN = -3
44 } option_status;
45 
46 
47 l2cap_flow_t default_qos = {
48 		/* flags */ 0x0,
49 		/* service_type */ HCI_SERVICE_TYPE_BEST_EFFORT,
50 		/* token_rate */ 0xffffffff, /* maximum */
51 		/* token_bucket_size */ 0xffffffff, /* maximum */
52 		/* peak_bandwidth */ 0x00000000, /* maximum */
53 		/* latency */ 0xffffffff, /* don't care */
54 		/* delay_variation */ 0xffffffff  /* don't care */
55 };
56 
57 
58 static status_t
59 l2cap_process_con_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
60 
61 static status_t
62 l2cap_process_con_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
63 
64 static status_t
65 l2cap_process_cfg_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
66 
67 static status_t
68 l2cap_process_cfg_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
69 
70 static status_t
71 l2cap_process_discon_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
72 
73 static status_t
74 l2cap_process_discon_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
75 
76 static status_t
77 l2cap_process_echo_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
78 
79 static status_t
80 l2cap_process_echo_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
81 
82 static status_t
83 l2cap_process_info_req(HciConnection* conn, uint8 ident, net_buffer* buffer);
84 
85 static status_t
86 l2cap_process_info_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer);
87 
88 static status_t
89 l2cap_process_cmd_rej(HciConnection* conn, uint8 ident, net_buffer* buffer);
90 
91 
92 /*
93  * Process L2CAP signaling command. We already know that destination channel ID
94  * is 0x1 that means we have received signaling command from peer's L2CAP layer.
95  * So get command header, decode and process it.
96  *
97  * XXX do we need to check signaling MTU here?
98  */
99 
100 status_t
101 l2cap_process_signal_cmd(HciConnection* conn, net_buffer* buffer)
102 {
103 	net_buffer* m = buffer;
104 
105 	debugf("Signal size=%ld\n", buffer->size);
106 
107 	while (m != NULL) {
108 
109 		/* Verify packet length */
110 		if (buffer->size < sizeof(l2cap_cmd_hdr_t)) {
111 			debugf("small L2CAP signaling command len=%ld\n", buffer->size);
112 			gBufferModule->free(buffer);
113 			return EMSGSIZE;
114 		}
115 
116 		/* Get COMMAND header */
117 		NetBufferHeaderReader<l2cap_cmd_hdr_t> commandHeader(buffer);
118 		status_t status = commandHeader.Status();
119 		if (status < B_OK) {
120 			return ENOBUFS;
121 		}
122 
123 		uint8 processingCode = commandHeader->code;
124 		uint8 processingIdent = commandHeader->ident;
125 		uint16 processingLength = le16toh(commandHeader->length);
126 
127 		/* Verify command length */
128 		if (buffer->size < processingLength) {
129 			debugf("invalid L2CAP signaling command, code=%#x, ident=%d,"
130 				" length=%d, buffer size=%ld\n", processingCode,
131 				processingIdent, processingLength, buffer->size);
132 			gBufferModule->free(buffer);
133 			return (EMSGSIZE);
134 		}
135 
136 
137 		commandHeader.Remove(); // pulling the header of the command
138 
139 		/* Process command processors responsible to delete the command*/
140 		switch (processingCode) {
141 			case L2CAP_CMD_REJ:
142 				l2cap_process_cmd_rej(conn, processingIdent, buffer);
143 				break;
144 
145 			case L2CAP_CON_REQ:
146 				l2cap_process_con_req(conn, processingIdent, buffer);
147 				break;
148 
149 			case L2CAP_CON_RSP:
150 				l2cap_process_con_rsp(conn, processingIdent, buffer);
151 				break;
152 
153 			case L2CAP_CFG_REQ:
154 				l2cap_process_cfg_req(conn, processingIdent, buffer);
155 				break;
156 
157 			case L2CAP_CFG_RSP:
158 				l2cap_process_cfg_rsp(conn, processingIdent, buffer);
159 				break;
160 
161 			case L2CAP_DISCON_REQ:
162 				l2cap_process_discon_req(conn, processingIdent, buffer);
163 				break;
164 
165 			case L2CAP_DISCON_RSP:
166 				l2cap_process_discon_rsp(conn, processingIdent, buffer);
167 				break;
168 
169 			case L2CAP_ECHO_REQ:
170 				l2cap_process_echo_req(conn, processingIdent, buffer);
171 				break;
172 
173 			case L2CAP_ECHO_RSP:
174 				l2cap_process_echo_rsp(conn, processingIdent, buffer);
175 				break;
176 
177 			case L2CAP_INFO_REQ:
178 				l2cap_process_info_req(conn, processingIdent, buffer);
179 				break;
180 
181 			case L2CAP_INFO_RSP:
182 				l2cap_process_info_rsp(conn, processingIdent, buffer);
183 				break;
184 
185 			default:
186 				debugf("unknown L2CAP signaling command, code=%#x, ident=%d\n",
187 						processingCode, processingIdent);
188 
189 				// Send L2CAP_CommandRej. Do not really care about the result
190 				// ORD: Remiaining commands in the same packet are also to
191 				// be rejected?
192 				// send_l2cap_reject(con, processingIdent,
193 				// L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
194 				gBufferModule->free(m);
195 				break;
196 		}
197 
198 		// Is there still remaining size? processors should have pulled its content...
199 		if (m->size == 0) {
200 			// free the buffer
201 			gBufferModule->free(m);
202 			m = NULL;
203 		}
204 	}
205 
206 	return B_OK;
207 }
208 
209 
210 #if 0
211 #pragma mark - Processing Incoming signals
212 #endif
213 
214 
215 /* Process L2CAP_ConnectReq command */
216 static status_t
217 l2cap_process_con_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
218 {
219 	L2capChannel* channel;
220 	uint16 dcid, psm;
221 
222 	/* Get con req data */
223 	NetBufferHeaderReader<l2cap_con_req_cp> command(buffer);
224 	status_t status = command.Status();
225 	if (status < B_OK) {
226 		return ENOBUFS;
227 	}
228 
229 	psm = le16toh(command->psm);
230 	dcid = le16toh(command->scid);
231 
232 	command.Remove(); // pull the command body
233 
234 	// Create new channel and send L2CA_ConnectInd
235 	// notification to the upper layer protocol.
236 	channel = btCoreData->AddChannel(conn, psm /*, dcid, ident*/);
237 	if (channel == NULL) {
238 		flowf("No resources to create channel\n");
239 		return (send_l2cap_con_rej(conn, ident, 0, dcid, L2CAP_NO_RESOURCES));
240 	} else {
241 		debugf("New channel created scid=%d\n", channel->scid);
242 	}
243 
244 	channel->dcid = dcid;
245 	channel->ident = ident;
246 
247 	status_t indicationStatus = l2cap_l2ca_con_ind(channel);
248 
249 	if ( indicationStatus == B_OK ) {
250 		// channel->state = L2CAP_CHAN_W4_L2CA_CON_RSP;
251 
252 	} else if (indicationStatus == B_NO_MEMORY) {
253 		/* send_l2cap_con_rej(con, ident, channel->scid, dcid, L2CAP_NO_RESOURCES);*/
254 		btCoreData->RemoveChannel(conn, channel->scid);
255 
256 	} else {
257 		send_l2cap_con_rej(conn, ident, channel->scid, dcid, L2CAP_PSM_NOT_SUPPORTED);
258 		btCoreData->RemoveChannel(conn, channel->scid);
259 	}
260 
261 	return (indicationStatus);
262 }
263 
264 
265 static status_t
266 l2cap_process_con_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
267 {
268 	L2capFrame* cmd = NULL;
269 	uint16 scid, dcid, result, status;
270 	status_t error = 0;
271 
272 	/* Get command parameters */
273 	NetBufferHeaderReader<l2cap_con_rsp_cp> command(buffer);
274 	if (command.Status() < B_OK) {
275 		return ENOBUFS;
276 	}
277 
278 	dcid = le16toh(command->dcid);
279 	scid = le16toh(command->scid);
280 	result = le16toh(command->result);
281 	status = le16toh(command->status);
282 
283 	command.Remove(); // pull the command body
284 
285 	debugf("dcid=%d scid=%d result=%d status%d\n", dcid, scid, result, status);
286 
287 	/* Check if we have pending command descriptor */
288 	cmd = btCoreData->SignalByIdent(conn, ident);
289 	if (cmd == NULL) {
290 		debugf("unexpected L2CAP_ConnectRsp command. ident=%d, "
291 		"con_handle=%d\n", ident, conn->handle);
292 		return ENOENT;
293 	}
294 
295 	/* Verify channel state, if invalid - do nothing */
296 	if (cmd->channel->state != L2CAP_CHAN_W4_L2CAP_CON_RSP) {
297 		debugf("unexpected L2CAP_ConnectRsp. Invalid channel state, "
298 		"cid=%d, state=%d\n", scid,	cmd->channel->state);
299 		goto reject;
300 	}
301 
302 	/* Verify CIDs and send reject if does not match */
303 	if (cmd->channel->scid != scid) {
304 		debugf("unexpected L2CAP_ConnectRsp. Channel IDs do not match, "
305 		"scid=%d(%d)\n", cmd->channel->scid, scid);
306 		goto reject;
307 	}
308 
309 	/*
310 	 * Looks good. We got confirmation from our peer. Now process
311 	 * it. First disable RTX timer. Then check the result and send
312 	 * notification to the upper layer. If command timeout already
313 	 * happened then ignore response.
314 	 */
315 
316 	if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0)
317 		return error;
318 
319 	if (result == L2CAP_PENDING) {
320 		/*
321 		 * Our peer wants more time to complete connection. We shall
322 		 * start ERTX timer and wait. Keep command in the list.
323 		 */
324 
325 		cmd->channel->dcid = dcid;
326 		btCoreData->TimeoutSignal(cmd, bluetooth_l2cap_ertx_timeout);
327 
328 		// TODO:
329 		// INDICATION error = ng_l2cap_l2ca_con_rsp(cmd->channel, cmd->token,
330 		// result, status);
331 		// if (error != B_OK)
332 		// btCoreData->RemoveChannel(conn, cmd->channel->scid);
333 
334 	} else {
335 
336 		if (result == L2CAP_SUCCESS) {
337 			/*
338 			 * Channel is open. Complete command and move to CONFIG
339 			 * state. Since we have sent positive confirmation we
340 			 * expect to receive L2CA_Config request from the upper
341 			 * layer protocol.
342 			 */
343 
344 			cmd->channel->dcid = dcid;
345 			cmd->channel->state = L2CAP_CHAN_CONFIG;
346 		}
347 
348 		error = l2cap_con_rsp_ind(conn, cmd->channel);
349 
350 		/* XXX do we have to remove the channel on error? */
351 		if (error != 0 || result != L2CAP_SUCCESS) {
352 			debugf("failed to open L2CAP channel, result=%d, status=%d\n",
353 				result, status);
354 			btCoreData->RemoveChannel(conn, cmd->channel->scid);
355 		}
356 
357 		btCoreData->AcknowledgeSignal(cmd);
358 	}
359 
360 	return error;
361 
362 reject:
363 	/* Send reject. Do not really care about the result */
364 	send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, dcid);
365 
366 	return 0;
367 }
368 
369 
370 static option_status
371 getNextSignalOption(net_buffer* nbuf, size_t* off, l2cap_cfg_opt_t* hdr,
372 	l2cap_cfg_opt_val_t* val)
373 {
374 	int hint;
375 	size_t len = nbuf->size - (*off);
376 
377 	if (len == 0)
378 		return OPTION_NOT_PRESENT;
379 	if (len < 0 || len < sizeof(*hdr))
380 		return HEADER_TOO_SHORT;
381 
382 	gBufferModule->read(nbuf, *off, hdr, sizeof(*hdr));
383 	*off += sizeof(*hdr);
384 	len  -= sizeof(*hdr);
385 
386 	hint = L2CAP_OPT_HINT(hdr->type);
387 	hdr->type &= L2CAP_OPT_HINT_MASK;
388 
389 	switch (hdr->type) {
390 		case L2CAP_OPT_MTU:
391 			if (hdr->length != L2CAP_OPT_MTU_SIZE || len < hdr->length)
392 				return BAD_OPTION_LENGTH;
393 
394 			gBufferModule->read(nbuf, *off, val, L2CAP_OPT_MTU_SIZE);
395 			val->mtu = le16toh(val->mtu);
396 			*off += L2CAP_OPT_MTU_SIZE;
397 			debugf("mtu %d specified\n", val->mtu);
398 		break;
399 
400 		case L2CAP_OPT_FLUSH_TIMO:
401 			if (hdr->length != L2CAP_OPT_FLUSH_TIMO_SIZE || len < hdr->length)
402 				return BAD_OPTION_LENGTH;
403 
404 			gBufferModule->read(nbuf, *off, val, L2CAP_OPT_FLUSH_TIMO_SIZE);
405 			val->flush_timo = le16toh(val->flush_timo);
406 			flowf("flush specified\n");
407 			*off += L2CAP_OPT_FLUSH_TIMO_SIZE;
408 		break;
409 
410 		case L2CAP_OPT_QOS:
411 			if (hdr->length != L2CAP_OPT_QOS_SIZE || len < hdr->length)
412 				return BAD_OPTION_LENGTH;
413 
414 			gBufferModule->read(nbuf, *off, val, L2CAP_OPT_QOS_SIZE);
415 			val->flow.token_rate = le32toh(val->flow.token_rate);
416 			val->flow.token_bucket_size =	le32toh(val->flow.token_bucket_size);
417 			val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
418 			val->flow.latency = le32toh(val->flow.latency);
419 			val->flow.delay_variation = le32toh(val->flow.delay_variation);
420 			*off += L2CAP_OPT_QOS_SIZE;
421 			flowf("qos specified\n");
422 		break;
423 
424 		default:
425 			if (hint)
426 				*off += hdr->length;
427 			else
428 				return OPTION_UNKNOWN;
429 		break;
430 	}
431 
432 	return OPTION_PRESENT;
433 }
434 
435 
436 static status_t
437 l2cap_process_cfg_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
438 {
439 	L2capChannel* channel = NULL;
440 	uint16 dcid;
441 	uint16 respond;
442 	uint16 result;
443 
444 	l2cap_cfg_opt_t hdr;
445 	l2cap_cfg_opt_val_t val;
446 
447 	size_t off;
448 	status_t error = 0;
449 
450 	debugf("configuration=%ld\n", buffer->size);
451 
452 	/* Get command parameters */
453 	NetBufferHeaderReader<l2cap_cfg_req_cp> command(buffer);
454 	status_t status = command.Status();
455 	if (status < B_OK) {
456 		return ENOBUFS;
457 	}
458 
459 	dcid = le16toh(command->dcid);
460 	respond = L2CAP_OPT_CFLAG(le16toh(command->flags));
461 
462 	command.Remove(); // pull configuration header
463 
464 	/* Check if we have this channel and it is in valid state */
465 	channel = btCoreData->ChannelBySourceID(conn, dcid);
466 	if (channel == NULL) {
467 		debugf("unexpected L2CAP_ConfigReq command. "
468 		"Channel does not exist, cid=%d\n", dcid);
469 		goto reject;
470 	}
471 
472 	/* Verify channel state */
473 	if (channel->state != L2CAP_CHAN_CONFIG
474 		&& channel->state != L2CAP_CHAN_OPEN) {
475 		debugf("unexpected L2CAP_ConfigReq. Invalid channel state, "
476 		"cid=%d, state=%d\n", dcid, channel->state);
477 		goto reject;
478 	}
479 
480 	if (channel->state == L2CAP_CHAN_OPEN) { /* Re-configuration */
481 		channel->cfgState = 0; // Reset configuration state
482 		channel->state = L2CAP_CHAN_CONFIG;
483 	}
484 
485 	for (result = 0, off = 0; ; ) {
486 		error = getNextSignalOption(buffer, &off, &hdr, &val);
487 
488 		if (error == OPTION_NOT_PRESENT) { /* We done with this packet */
489 			// TODO: configurations should have been pulled
490 			break;
491 		} else if (error > 0) { /* Got option */
492 			switch (hdr.type) {
493 			case L2CAP_OPT_MTU:
494 				channel->configuration->omtu = val.mtu;
495 				break;
496 
497 			case L2CAP_OPT_FLUSH_TIMO:
498 				channel->configuration->flush_timo = val.flush_timo;
499 				break;
500 
501 			case L2CAP_OPT_QOS:
502 				memcpy(&val.flow, &channel->configuration->iflow,
503 					sizeof(channel->configuration->iflow));
504 				break;
505 
506 			default: /* Ignore unknown hint option */
507 				break;
508 			}
509 		} else { /* Oops, something is wrong */
510 			respond = 1;
511 			if (error == OPTION_UNKNOWN) {
512 				// TODO: Remote to get the next possible option
513 				result = L2CAP_UNKNOWN_OPTION;
514 			} else {
515 				/* XXX FIXME Send other reject codes? */
516 				gBufferModule->free(buffer);
517 				result = L2CAP_REJECT;
518 			}
519 
520 			break;
521 		}
522 	}
523 
524 	debugf("Pulled %ld of configuration fields respond=%d remaining=%ld\n",
525 		off, respond, buffer->size);
526 	gBufferModule->remove_header(buffer, off);
527 
528 
529 	/*
530 	 * Now check and see if we have to respond. If everything was OK then
531 	 * respond contain "C flag" and (if set) we will respond with empty
532 	 * packet and will wait for more options.
533 	 *
534 	 * Other case is that we did not like peer's options and will respond
535 	 * with L2CAP_Config response command with Reject error code.
536 	 *
537 	 * When "respond == 0" than we have received all options and we will
538 	 * sent L2CA_ConfigInd event to the upper layer protocol.
539 	 */
540 
541 	if (respond) {
542 		flowf("Refusing cfg\n");
543 		error = send_l2cap_cfg_rsp(conn, ident, channel->dcid, result, buffer);
544 		if (error != 0) {
545 			// TODO:
546 			// INDICATION ng_l2cap_l2ca_discon_ind(ch);
547 			btCoreData->RemoveChannel(conn, channel->scid);
548 		}
549 	} else {
550 		/* Send L2CA_ConfigInd event to the upper layer protocol */
551 		channel->cfgState |= L2CAP_CFG_IN;
552 		channel->ident = ident; // sent ident to reply
553 		error = l2cap_cfg_req_ind(channel);
554 		if (error != 0)
555 			btCoreData->RemoveChannel(conn, channel->scid);
556 	}
557 
558 	return error;
559 
560 reject:
561 	/* Send reject. Do not really care about the result */
562 	gBufferModule->free(buffer);
563 
564 	send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, 0, dcid);
565 
566 	return B_OK;
567 }
568 
569 
570 /* Process L2CAP_ConfigRsp command */
571 static status_t
572 l2cap_process_cfg_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
573 {
574 	L2capFrame* cmd = NULL;
575 	uint16 scid, cflag, result;
576 
577 	l2cap_cfg_opt_t hdr;
578 	l2cap_cfg_opt_val_t val;
579 
580 	size_t off;
581 	status_t error = 0;
582 
583 	NetBufferHeaderReader<l2cap_cfg_rsp_cp> command(buffer);
584 	status_t status = command.Status();
585 	if (status < B_OK) {
586 		return ENOBUFS;
587 	}
588 
589 	scid = le16toh(command->scid);
590 	cflag = L2CAP_OPT_CFLAG(le16toh(command->flags));
591 	result = le16toh(command->result);
592 
593 	command.Remove();
594 
595 	debugf("scid=%d cflag=%d result=%d\n", scid, cflag, result);
596 
597 	/* Check if we have this command */
598 	cmd = btCoreData->SignalByIdent(conn, ident);
599 	if (cmd == NULL) {
600 		debugf("unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",
601 			ident, conn->handle);
602 		gBufferModule->free(buffer);
603 		return ENOENT;
604 	}
605 
606 	/* Verify CIDs and send reject if does not match */
607 	if (cmd->channel->scid != scid) {
608 		debugf("unexpected L2CAP_ConfigRsp.Channel ID does not match, "
609 		"scid=%d(%d)\n", cmd->channel->scid, scid);
610 		goto reject;
611 	}
612 
613 	/* Verify channel state and reject if invalid */
614 	if (cmd->channel->state != L2CAP_CHAN_CONFIG) {
615 		debugf("unexpected L2CAP_ConfigRsp. Invalid channel state, scid=%d, "
616 		"state=%d\n", cmd->channel->scid, cmd->channel->state);
617 		goto reject;
618 	}
619 
620 	/*
621 	 * Looks like it is our response, so process it. First parse options,
622 	 * then verify C flag. If it is set then we shall expect more
623 	 * configuration options from the peer and we will wait. Otherwise we
624 	 * have received all options and we will send L2CA_ConfigRsp event to
625 	 * the upper layer protocol. If command timeout already happened then
626 	 * ignore response.
627 	 */
628 
629 	if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) {
630 		gBufferModule->free(buffer);
631 		return error;
632 	}
633 
634 	for (off = 0; ; ) {
635 		error = getNextSignalOption(buffer, &off, &hdr, &val);
636 		// TODO: pull the option
637 
638 		if (error == OPTION_NOT_PRESENT) /* We done with this packet */
639 			break;
640 		else if (error > 0) { /* Got option */
641 			switch (hdr.type) {
642 			case L2CAP_OPT_MTU:
643 				cmd->channel->configuration->imtu = val.mtu;
644 			break;
645 
646 			case L2CAP_OPT_FLUSH_TIMO:
647 				cmd->channel->configuration->flush_timo = val.flush_timo;
648 				break;
649 
650 			case L2CAP_OPT_QOS:
651 				memcpy(&val.flow, &cmd->channel->configuration->oflow,
652 					sizeof(cmd->channel->configuration->oflow));
653 			break;
654 
655 			default: /* Ignore unknown hint option */
656 				break;
657 			}
658 		} else {
659 			/*
660 			 * XXX FIXME What to do here?
661 			 *
662 			 * This is really BAD :( options packet was broken, or
663 			 * peer sent us option that we did not understand. Let
664 			 * upper layer know and do not wait for more options.
665 			 */
666 
667 			debugf("fail parsing configuration options, error=%ld\n", error);
668 			// INDICATION
669 			result = L2CAP_UNKNOWN;
670 			cflag = 0;
671 
672 			break;
673 		}
674 	}
675 
676 	if (cflag) /* Restart timer and wait for more options */
677 		btCoreData->TimeoutSignal(cmd, bluetooth_l2cap_rtx_timeout);
678 	else {
679 		/* Send L2CA_Config response to the upper layer protocol */
680 		error = l2cap_cfg_rsp_ind(cmd->channel /*, cmd->token, result*/);
681 		if (error != 0) {
682 			/*
683 			 * XXX FIXME what to do here? we were not able to send
684 			 * response to the upper layer protocol, so for now
685 			 * just close the channel. Send L2CAP_Disconnect to
686 			 * remote peer?
687 			 */
688 
689 			debugf("failed to send L2CA_Config response, error=%ld\n", error);
690 
691 			btCoreData->RemoveChannel(conn, cmd->channel->scid);
692 		}
693 
694 		btCoreData->AcknowledgeSignal(cmd);
695 	}
696 
697 	return error;
698 
699 reject:
700 	/* Send reject. Do not really care about the result */
701 	gBufferModule->free(buffer);
702 	send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, 0);
703 
704 	return B_OK;
705 }
706 
707 
708 /* Process L2CAP_DisconnectReq command */
709 static status_t
710 l2cap_process_discon_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
711 {
712 	L2capChannel* channel = NULL;
713 	L2capFrame* cmd = NULL;
714 	net_buffer* buff = NULL;
715 	uint16 scid;
716 	uint16 dcid;
717 
718 	NetBufferHeaderReader<l2cap_discon_req_cp> command(buffer);
719 	status_t status = command.Status();
720 	if (status < B_OK) {
721 		return ENOBUFS;
722 	}
723 
724 	dcid = le16toh(command->dcid);
725 	scid = le16toh(command->scid);
726 
727 	command.Remove();
728 
729 	/* Check if we have this channel and it is in valid state */
730 	channel = btCoreData->ChannelBySourceID(conn, dcid);
731 	if (channel == NULL) {
732 		debugf("unexpected L2CAP_DisconnectReq message.Channel does not exist, "
733 			"cid=%x\n", dcid);
734 		goto reject;
735 	}
736 
737 	/* XXX Verify channel state and reject if invalid -- is that true? */
738 	if (channel->state != L2CAP_CHAN_OPEN
739 		&& channel->state != L2CAP_CHAN_CONFIG
740 		&& channel->state != L2CAP_CHAN_W4_L2CAP_DISCON_RSP) {
741 		debugf("unexpected L2CAP_DisconnectReq. Invalid channel state, cid=%d, "
742 			"state=%d\n", dcid, channel->state);
743 		goto reject;
744 	}
745 
746 	/* Match destination channel ID */
747 	if (channel->dcid != scid || channel->scid != dcid) {
748 		debugf("unexpected L2CAP_DisconnectReq. Channel IDs does not match, "
749 			"channel: scid=%d, dcid=%d, request: scid=%d, dcid=%d\n",
750 			channel->scid, channel->dcid, scid, dcid);
751 		goto reject;
752 	}
753 
754 	/*
755 	 * Looks good, so notify upper layer protocol that channel is about
756 	 * to be disconnected and send L2CA_DisconnectInd message. Then respond
757 	 * with L2CAP_DisconnectRsp.
758 	 */
759 
760 	flowf("Responding\n");
761 
762 	// inform upper if we were not actually already waiting
763 	if (channel->state != L2CAP_CHAN_W4_L2CAP_DISCON_RSP) {
764 		l2cap_discon_req_ind(channel); // do not care about result
765 	}
766 
767 	/* Send L2CAP_DisconnectRsp */
768 	buff = l2cap_discon_rsp(ident, dcid, scid);
769 	cmd = btCoreData->SpawnSignal(conn, channel, buff, ident, L2CAP_DISCON_RSP);
770 	if (cmd == NULL)
771 		return ENOMEM;
772 
773 	/* Link command to the queue */
774 	SchedConnectionPurgeThread(conn);
775 
776 	return B_OK;
777 
778 reject:
779 	flowf("Rejecting\n");
780 	/* Send reject. Do not really care about the result */
781 	send_l2cap_reject(conn, ident, L2CAP_REJ_INVALID_CID, 0, scid, dcid);
782 
783 	return B_OK;
784 }
785 
786 
787 /* Process L2CAP_DisconnectRsp command */
788 static status_t
789 l2cap_process_discon_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
790 {
791 	L2capFrame* cmd = NULL;
792 	int16 scid, dcid;
793 	status_t error = 0;
794 
795 	/* Get command parameters */
796 	NetBufferHeaderReader<l2cap_discon_rsp_cp> command(buffer);
797 	status_t status = command.Status();
798 	if (status < B_OK) {
799 		return ENOBUFS;
800 	}
801 
802 	dcid = le16toh(command->dcid);
803 	scid = le16toh(command->scid);
804 
805 	command.Remove();
806 	//? NG_FREE_M(con->rx_pkt);
807 
808 	/* Check if we have pending command descriptor */
809 	cmd = btCoreData->SignalByIdent(conn, ident);
810 	if (cmd == NULL) {
811 		debugf("unexpected L2CAP_DisconnectRsp command. ident=%d, "
812 			"con_handle=%d\n", ident, conn->handle);
813 		goto out;
814 	}
815 
816 	/* Verify channel state, do nothing if invalid */
817 	if (cmd->channel->state != L2CAP_CHAN_W4_L2CA_DISCON_RSP) {
818 		debugf("unexpected L2CAP_DisconnectRsp. Invalid state, cid=%d, "
819 		"state=%d\n", scid, cmd->channel->state);
820 		goto out;
821 	}
822 
823 	/* Verify CIDs and send reject if does not match */
824 	if (cmd->channel->scid != scid || cmd->channel->dcid != dcid) {
825 		debugf("unexpected L2CAP_DisconnectRsp. Channel IDs do not match, "
826 		"scid=%d(%d), dcid=%d(%d)\n", cmd->channel->scid, scid,
827 			cmd->channel->dcid, dcid);
828 		goto out;
829 	}
830 
831 	/*
832 	* Looks like we have successfuly disconnected channel, so notify
833 	* upper layer. If command timeout already happened then ignore
834 	* response.
835 	*/
836 
837 	if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0)
838 		goto out;
839 
840 	l2cap_discon_rsp_ind(cmd->channel/* results? */);
841 	btCoreData->RemoveChannel(conn, scid); /* this will free commands too */
842 
843 out:
844 	return error;
845 }
846 
847 
848 static status_t
849 l2cap_process_echo_req(HciConnection *conn, uint8 ident, net_buffer *buffer)
850 {
851 	L2capFrame* cmd = NULL;
852 
853 	cmd = btCoreData->SpawnSignal(conn, NULL, l2cap_echo_req(ident, NULL, 0),
854 		ident, L2CAP_ECHO_RSP);
855 	if (cmd == NULL) {
856 		gBufferModule->free(buffer);
857 		return ENOMEM;
858 	}
859 
860 	/* Attach data and link command to the queue */
861 	SchedConnectionPurgeThread(conn);
862 
863 	return B_OK;
864 }
865 
866 
867 /* Process L2CAP_EchoRsp command */
868 static status_t
869 l2cap_process_echo_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
870 {
871 	L2capFrame* cmd = NULL;
872 	status_t error = 0;
873 
874 	/* Check if we have this command */
875 	cmd = btCoreData->SignalByIdent(conn, ident);
876 	if (cmd != NULL) {
877 		/* If command timeout already happened then ignore response */
878 		if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) {
879 			return error;
880 		}
881 
882 		// INDICATION error = ng_l2cap_l2ca_ping_rsp(cmd->conn, cmd->token,
883 		// L2CAP_SUCCESS, conn->rx_pkt);
884 		btCoreData->AcknowledgeSignal(cmd);
885 
886 	} else {
887 		debugf("unexpected L2CAP_EchoRsp command. ident does not exist, "
888 		"ident=%d\n", ident);
889 		gBufferModule->free(buffer);
890 		error = B_ERROR;
891 	}
892 
893 	return error;
894 }
895 
896 
897 /* Process L2CAP_InfoReq command */
898 static status_t
899 l2cap_process_info_req(HciConnection* conn, uint8 ident, net_buffer* buffer)
900 {
901 	L2capFrame* cmd = NULL;
902 	net_buffer*	buf = NULL;
903 	uint16 type;
904 
905 	/* Get command parameters */
906     NetBufferHeaderReader<l2cap_info_req_cp> command(buffer);
907 	status_t status = command.Status();
908 	if (status < B_OK) {
909 		return ENOBUFS;
910 	}
911 
912 	// ??
913 	// command->type = le16toh(mtod(conn->rx_pkt,
914 	// ng_l2cap_info_req_cp *)->type);
915     type = le16toh(command->type);
916 
917 	command.Remove();
918 
919 	switch (type) {
920 	    case L2CAP_CONNLESS_MTU:
921 		    buf = l2cap_info_rsp(ident, L2CAP_CONNLESS_MTU, L2CAP_SUCCESS,
922 		    	L2CAP_MTU_DEFAULT);
923 		break;
924 
925 	    default:
926 		    buf = l2cap_info_rsp(ident, type, L2CAP_NOT_SUPPORTED, 0);
927 		break;
928 	}
929 
930 	cmd = btCoreData->SpawnSignal(conn, NULL, buf, ident, L2CAP_INFO_RSP);
931 	if (cmd == NULL)
932 		return ENOMEM;
933 
934 	/* Link command to the queue */
935 	SchedConnectionPurgeThread(conn);
936 
937 	return B_OK;
938 }
939 
940 
941 /* Process L2CAP_InfoRsp command */
942 static status_t
943 l2cap_process_info_rsp(HciConnection* conn, uint8 ident, net_buffer* buffer)
944 {
945 	l2cap_info_rsp_cp* cp = NULL;
946 	L2capFrame* cmd = NULL;
947 	status_t error = B_OK;
948 
949 	/* Get command parameters */
950 	NetBufferHeaderReader<l2cap_info_rsp_cp> command(buffer);
951 	status_t status = command.Status();
952 	if (status < B_OK) {
953 		return ENOBUFS;
954 	}
955 
956 	command->type = le16toh(command->type);
957 	command->result = le16toh(command->result);
958 
959 	command.Remove();
960 
961 	/* Check if we have pending command descriptor */
962 	cmd = btCoreData->SignalByIdent(conn, ident);
963 	if (cmd == NULL) {
964 		debugf("unexpected L2CAP_InfoRsp command. Requested ident does not "
965 		"exist, ident=%d\n", ident);
966 		gBufferModule->free(buffer);
967 		return ENOENT;
968 	}
969 
970 	/* If command timeout already happened then ignore response */
971 	if ((error = btCoreData->UnTimeoutSignal(cmd)) != 0) {
972 		gBufferModule->free(buffer);
973 		return error;
974 	}
975 
976 	if (command->result == L2CAP_SUCCESS) {
977 		switch (command->type) {
978 
979     		case L2CAP_CONNLESS_MTU:
980 				/* TODO: Check specs ??
981 	    	    if (conn->rx_pkt->m_pkthdr.len == sizeof(uint16)) {
982 				    *mtod(conn->rx_pkt, uint16 *) = le16toh(*mtod(conn->rx_pkt,uint16 *));
983 			    } else {
984 				    cp->result = L2CAP_UNKNOWN;  XXX
985 				    debugf("invalid L2CAP_InfoRsp command. Bad connectionless MTU parameter, len=%d\n", conn->rx_pkt->m_pkthdr.len);
986 			    }*/
987 			break;
988 
989 		default:
990 			debugf("invalid L2CAP_InfoRsp command. Unknown info type=%d\n", cp->type);
991 			break;
992 		}
993 	}
994 
995 	//INDICATION error = ng_l2cap_l2ca_get_info_rsp(cmd->conn, cmd->token, cp->result, conn->rx_pkt);
996 	btCoreData->AcknowledgeSignal(cmd);
997 
998 	return error;
999 }
1000 
1001 
1002 static status_t
1003 l2cap_process_cmd_rej(HciConnection* conn, uint8 ident, net_buffer* buffer)
1004 {
1005 	L2capFrame*	cmd = NULL;
1006 
1007 	/* TODO: review this command... Get command data */
1008 	NetBufferHeaderReader<l2cap_cmd_rej_cp> command(buffer);
1009 	status_t status = command.Status();
1010 	if (status < B_OK) {
1011 		return ENOBUFS;
1012 	}
1013 
1014 	command->reason = le16toh(command->reason);
1015 
1016 	debugf("reason=%d\n", command->reason);
1017 
1018 	command.Remove();
1019 
1020 	/* Check if we have pending command descriptor */
1021 	cmd = btCoreData->SignalByIdent(conn, ident);
1022 	if (cmd != NULL) {
1023 		/* If command timeout already happened then ignore reject */
1024 		if (btCoreData->UnTimeoutSignal(cmd) != 0) {
1025 			gBufferModule->free(buffer);
1026 			return ETIMEDOUT;
1027 		}
1028 
1029 
1030 		switch (cmd->code) {
1031 			case L2CAP_CON_REQ:
1032 				//INDICATION l2cap_l2ca_con_rsp(cmd->channel, cmd->token, cp->reason, 0);
1033 				btCoreData->RemoveChannel(conn, cmd->channel->scid);
1034 			break;
1035 
1036 			case L2CAP_CFG_REQ:
1037 				//INDICATION l2cap_l2ca_cfg_rsp(cmd->channel, cmd->token, cp->reason);
1038 			break;
1039 
1040 			case L2CAP_DISCON_REQ:
1041 				//INDICATION l2cap_l2ca_discon_rsp(cmd->channel, cmd->token, cp->reason);
1042 				btCoreData->RemoveChannel(conn, cmd->channel->scid);
1043 			break;
1044 
1045 			case L2CAP_ECHO_REQ:
1046 				//INDICATION l2cap_l2ca_ping_rsp(cmd->con, cmd->token, cp->reason, NULL);
1047 			break;
1048 
1049 			case L2CAP_INFO_REQ:
1050 				//INDICATION l2cap_l2ca_get_info_rsp(cmd->con, cmd->token, cp->reason, NULL);
1051 			break;
1052 
1053 		default:
1054 			debugf("unexpected L2CAP_CommandRej. Unexpected opcode=%d\n", cmd->code);
1055 			break;
1056 		}
1057 
1058 		btCoreData->AcknowledgeSignal(cmd);
1059 
1060 	} else
1061 		debugf("unexpected L2CAP_CommandRej command. Requested ident does not exist, ident=%d\n", ident);
1062 
1063 	return B_OK;
1064 }
1065 
1066 
1067 #if 0
1068 #pragma mark - Queuing Outgoing signals
1069 #endif
1070 
1071 
1072 status_t
1073 send_l2cap_reject(HciConnection* conn, uint8 ident, uint16 reason,
1074 	uint16 mtu, uint16 scid, uint16 dcid)
1075 {
1076 	L2capFrame*	cmd = NULL;
1077 
1078 	cmd = btCoreData->SpawnSignal(conn, NULL, l2cap_cmd_rej(ident, reason,
1079 		mtu, scid, dcid), ident, L2CAP_CMD_REJ);
1080 	if (cmd == NULL)
1081 		return ENOMEM;
1082 
1083 	/* Link command to the queue */
1084 	SchedConnectionPurgeThread(conn);
1085 
1086 	return B_OK;
1087 }
1088 
1089 
1090 status_t
1091 send_l2cap_con_rej(HciConnection* conn, uint8 ident, uint16 scid, uint16 dcid,
1092 	uint16 result)
1093 {
1094 	L2capFrame*	cmd = NULL;
1095 
1096 	cmd = btCoreData->SpawnSignal(conn, NULL,
1097 		l2cap_con_rsp(ident, scid, dcid, result, 0), ident, L2CAP_CON_RSP);
1098 	if (cmd == NULL)
1099 		return ENOMEM;
1100 
1101 	/* Link command to the queue */
1102 	SchedConnectionPurgeThread(conn);
1103 
1104 	return B_OK;
1105 }
1106 
1107 
1108 status_t
1109 send_l2cap_cfg_rsp(HciConnection* conn, uint8 ident, uint16 scid,
1110 	uint16 result, net_buffer* opt)
1111 {
1112 	L2capFrame*	cmd = NULL;
1113 
1114 	cmd = btCoreData->SpawnSignal(conn, NULL,
1115 		l2cap_cfg_rsp(ident, scid, 0, result, opt),	ident, L2CAP_CFG_RSP);
1116 	if (cmd == NULL) {
1117 		gBufferModule->free(opt);
1118 		return ENOMEM;
1119 	}
1120 
1121 	/* Link command to the queue */
1122 	SchedConnectionPurgeThread(conn);
1123 
1124 	return B_OK;
1125 }
1126