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