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/*-
7 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18*/
19#include <KernelExport.h>
20#include <string.h>
21#include <lock.h>
22
23#include <NetBufferUtilities.h>
24
25#include <bluetooth/HCI/btHCI_transport.h>
26
27#include <btModules.h>
28#include <l2cap.h>
29
30#include "l2cap_internal.h"
31#include "l2cap_signal.h"
32#include "l2cap_upper.h"
33
34#include <btDebug.h>
35
36
37status_t
38l2cap_receive(HciConnection* conn, net_buffer* buffer)
39{
40	status_t error = B_OK;
41	uint16 dcid;
42	uint16 length;
43
44#ifdef DUMP_L2CAP_FRAME
45	dprintf("DUMP:");
46	for (uint i = 0; i < buffer->size; i++) {
47		uint8 c = 0;
48		gBufferModule->read(buffer, i, &c, 1);
49		dprintf("[%x]", c);
50	}
51	dprintf("\n");
52#endif
53	// Check packet
54	if (buffer->size < sizeof(l2cap_hdr_t)) {
55		ERROR("%s: invalid L2CAP packet. Packet too small, len=%" B_PRIu32 "\n",
56			__func__, buffer->size);
57		gBufferModule->free(buffer);
58		return EMSGSIZE;
59
60	}
61
62	// Get L2CAP header
63	NetBufferHeaderReader<l2cap_hdr_t> bufferHeader(buffer);
64	status_t status = bufferHeader.Status();
65	if (status < B_OK) {
66		return ENOBUFS;
67	}
68
69	length = bufferHeader->length = le16toh(bufferHeader->length);
70	dcid = bufferHeader->dcid = le16toh(bufferHeader->dcid);
71
72	TRACE("%s: len=%d cid=%x\n", __func__, length, dcid);
73
74	bufferHeader.Remove(); // pulling
75
76	// Check payload size
77	if (length != buffer->size ) {
78		ERROR("%s: Payload length mismatch, packetlen=%d, bufferlen=%" B_PRIu32
79			"\n", __func__, length, buffer->size);
80		gBufferModule->free(buffer);
81		return EMSGSIZE;
82	}
83
84	// Process packet
85	switch (dcid) {
86		case L2CAP_SIGNAL_CID: // L2CAP command
87			error = l2cap_process_signal_cmd(conn, buffer);
88		break;
89
90		case L2CAP_CLT_CID: // Connectionless packet
91			// error = l2cap_cl_receive(buffer);
92			TRACE("%s: CL FRAME!!\n", __func__);
93		break;
94
95		default: // Data packet
96			error = l2cap_co_receive(conn, buffer, dcid);
97		break;
98	}
99
100	return (error);
101
102}
103
104
105struct bt_hci_module_info* btDevices = NULL;
106
107#if 0
108#pragma mark - thread conn sched -
109#endif
110
111static thread_id sConnectionThread;
112
113
114static void
115AddL2capHeader(L2capFrame* frame)
116{
117	NetBufferPrepend<l2cap_hdr_t> bufferHeader(frame->buffer);
118	status_t status = bufferHeader.Status();
119
120	if (status < B_OK) {
121		ERROR("%s: header could not be prepended! code=%d\n", __func__,
122			frame->code);
123		return;
124	}
125
126	// fill
127	bufferHeader->length = htole16(frame->buffer->size - sizeof(l2cap_hdr_t));
128	switch (frame->type) {
129		case L2CAP_C_FRAME:
130			bufferHeader->dcid = L2CAP_SIGNAL_CID;
131		break;
132		case L2CAP_G_FRAME:
133			bufferHeader->dcid = L2CAP_CLT_CID;
134		break;
135		default:
136			bufferHeader->dcid = frame->channel->dcid;
137		break;
138	}
139}
140
141
142void
143purge_connection(HciConnection* conn)
144{
145	CALLED();
146	L2capFrame* frame;
147	bool containerCanBeDestroyed;
148
149	mutex_lock(&conn->fLock);
150
151	frame = conn->OutGoingFrames.RemoveHead();
152
153	mutex_unlock(&conn->fLock);
154
155//	while ( frame != NULL) {
156
157		// Here is the place to decide how many l2cap signals we want to have
158		// per l2cap packet. 1 ATM
159		if (frame->type == L2CAP_C_FRAME && IS_SIGNAL_REQ(frame->code)) {
160			btCoreData->TimeoutSignal(frame, bluetooth_l2cap_rtx_timeout);
161			btCoreData->QueueSignal(frame);
162			containerCanBeDestroyed = false;
163		} else
164			containerCanBeDestroyed = true;
165
166		// Add the l2cap header
167		if (frame->buffer == NULL)
168			panic("Malformed frame in ongoing queue");
169
170		AddL2capHeader(frame);
171
172		if (btDevices == NULL)
173		if (get_module(BT_HCI_MODULE_NAME, (module_info**)&btDevices) != B_OK) {
174			panic("l2cap: cannot get dev module");
175		} // TODO: someone put it
176
177
178		TRACE("%s: type=%d, code=%d frame %p tolower\n", __func__, frame->type,
179			frame->code, frame->buffer);
180
181		frame->buffer->type = conn->handle;
182		btDevices->PostACL(conn->ndevice->index, frame->buffer);
183
184		// Only in the case that we need a response the frame container needs
185		// to be kept: Request C-Frames
186		if (containerCanBeDestroyed) {
187			delete frame;
188		}
189
190//		frame = conn->OutGoingFrames.RemoveHead();
191//	}
192
193}
194
195
196static status_t
197connection_thread(void*)
198{
199	int32 code;
200	ssize_t	ssizePort;
201	ssize_t	ssizeRead;
202
203	HciConnection* conn = NULL;
204
205	// TODO: Keep this a static var
206	port_id fPort = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
207	if (fPort == B_NAME_NOT_FOUND) {
208		panic("BT Connection port has been deleted");
209	}
210
211	while ((ssizePort = port_buffer_size(fPort)) != B_BAD_PORT_ID) {
212
213		if (ssizePort <= 0) {
214			ERROR("%s: Error %s\n", __func__, strerror(ssizePort));
215			snooze(500 * 1000);
216			continue;
217		}
218
219		if (ssizePort > (ssize_t) sizeof(conn)) {
220			ERROR("%s: Message too big %ld\n", __func__,  ssizePort);
221			snooze(500 * 1000);
222			continue;
223		}
224
225		ssizeRead = read_port(fPort, &code, &conn, ssizePort);
226
227		if (ssizeRead != ssizePort) {
228			ERROR("%s: Mismatch size port=%ld read=%ld\n", __func__,
229				ssizePort, ssizeRead);
230			snooze(500 * 1000);
231			continue;
232		}
233
234		purge_connection(conn);
235	}
236
237	return B_OK;
238}
239
240
241status_t
242InitializeConnectionPurgeThread()
243{
244	port_id fPort = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
245	if (fPort == B_NAME_NOT_FOUND) {
246		TRACE("%s: Creating connection purge port\n", __func__);
247		fPort = create_port(16, BLUETOOTH_CONNECTION_SCHED_PORT);
248	}
249
250	// This thread has to catch up connections before first package is sent.
251	sConnectionThread = spawn_kernel_thread(connection_thread,
252				"bluetooth connection purge", B_URGENT_DISPLAY_PRIORITY, NULL);
253
254	if (sConnectionThread >= B_OK)
255		return resume_thread(sConnectionThread);
256	else
257		return B_ERROR;
258}
259
260
261status_t
262QuitConnectionPurgeThread()
263{
264	CALLED();
265	status_t status;
266
267	port_id fPort = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
268	if (fPort != B_NAME_NOT_FOUND)
269		close_port(fPort);
270
271	wait_for_thread(sConnectionThread, &status);
272	return status;
273}
274
275
276void
277SchedConnectionPurgeThread(HciConnection* conn)
278{
279	port_id port = find_port(BLUETOOTH_CONNECTION_SCHED_PORT);
280
281	HciConnection* temp = conn;
282
283	if (port == B_NAME_NOT_FOUND)
284		panic("BT Connection Port Deleted");
285
286	status_t error = write_port(port, (addr_t)conn, &temp, sizeof(conn));
287
288	if (error != B_OK)
289		panic("BT Connection sched failed");
290
291}
292