1/*
2 * Copyright (c) 2007-2008 by Michael Lotz
3 * Heavily based on the original usb_serial driver which is:
4 *
5 * Copyright (c) 2003 by Siarzhuk Zharski <imker@gmx.li>
6 * Distributed under the terms of the MIT License.
7 */
8#include <KernelExport.h>
9#include <Drivers.h>
10#include <malloc.h>
11#include <stdio.h>
12#include <stdlib.h>
13
14#include "Driver.h"
15#include "SerialDevice.h"
16#include "USB3.h"
17
18int32 api_version = B_CUR_DRIVER_API_VERSION;
19static const char *sDeviceBaseName = "ports/usb";
20SerialDevice *gSerialDevices[DEVICES_COUNT];
21char *gDeviceNames[DEVICES_COUNT + 1];
22usb_module_info *gUSBModule = NULL;
23tty_module_info *gTTYModule = NULL;
24sem_id gDriverLock = -1;
25
26
27status_t
28usb_serial_device_added(usb_device device, void **cookie)
29{
30	TRACE_FUNCALLS("> usb_serial_device_added(0x%08x, 0x%08x)\n", device, cookie);
31
32	status_t status = B_OK;
33	const usb_device_descriptor *descriptor
34		= gUSBModule->get_device_descriptor(device);
35
36	TRACE_ALWAYS("probing device: 0x%04x/0x%04x\n", descriptor->vendor_id,
37		descriptor->product_id);
38
39	*cookie = NULL;
40	SerialDevice *serialDevice = SerialDevice::MakeDevice(device,
41		descriptor->vendor_id, descriptor->product_id);
42
43	const usb_configuration_info *configuration;
44	for (int i = 0; i < descriptor->num_configurations; i++) {
45		configuration = gUSBModule->get_nth_configuration(device, i);
46		if (configuration == NULL)
47			continue;
48
49		status = serialDevice->AddDevice(configuration);
50		if (status == B_OK) {
51			// Found!
52			break;
53		}
54	}
55
56	if (status < B_OK) {
57		delete serialDevice;
58		return status;
59	}
60
61
62	acquire_sem(gDriverLock);
63	for (int32 i = 0; i < DEVICES_COUNT; i++) {
64		if (gSerialDevices[i] != NULL)
65			continue;
66
67		status = serialDevice->Init();
68		if (status < B_OK) {
69			delete serialDevice;
70			return status;
71		}
72
73		gSerialDevices[i] = serialDevice;
74		*cookie = serialDevice;
75
76		release_sem(gDriverLock);
77		TRACE_ALWAYS("%s (0x%04x/0x%04x) added\n", serialDevice->Description(),
78			descriptor->vendor_id, descriptor->product_id);
79		return B_OK;
80	}
81
82	release_sem(gDriverLock);
83	return B_ERROR;
84}
85
86
87status_t
88usb_serial_device_removed(void *cookie)
89{
90	TRACE_FUNCALLS("> usb_serial_device_removed(0x%08x)\n", cookie);
91
92	acquire_sem(gDriverLock);
93
94	SerialDevice *device = (SerialDevice *)cookie;
95	for (int32 i = 0; i < DEVICES_COUNT; i++) {
96		if (gSerialDevices[i] == device) {
97			if (device->IsOpen()) {
98				// the device will be deleted upon being freed
99				device->Removed();
100			} else {
101				delete device;
102				gSerialDevices[i] = NULL;
103			}
104			break;
105		}
106	}
107
108	release_sem(gDriverLock);
109	TRACE_FUNCRET("< usb_serial_device_removed() returns\n");
110	return B_OK;
111}
112
113
114//#pragma mark -
115
116
117/* init_hardware - called once the first time the driver is loaded */
118status_t
119init_hardware()
120{
121	TRACE("init_hardware\n");
122	return B_OK;
123}
124
125
126/* init_driver - called every time the driver is loaded. */
127status_t
128init_driver()
129{
130	load_settings();
131	create_log_file();
132
133	TRACE_FUNCALLS("> init_driver()\n");
134
135	status_t status = get_module(B_TTY_MODULE_NAME, (module_info **)&gTTYModule);
136	if (status < B_OK)
137		return status;
138
139	status = get_module(B_USB_MODULE_NAME, (module_info **)&gUSBModule);
140	if (status < B_OK) {
141		put_module(B_TTY_MODULE_NAME);
142		return status;
143	}
144
145	for (int32 i = 0; i < DEVICES_COUNT; i++)
146		gSerialDevices[i] = NULL;
147
148	gDeviceNames[0] = NULL;
149
150	gDriverLock = create_sem(1, DRIVER_NAME"_devices_table_lock");
151	if (gDriverLock < B_OK) {
152		put_module(B_USB_MODULE_NAME);
153		put_module(B_TTY_MODULE_NAME);
154		return gDriverLock;
155	}
156
157	static usb_notify_hooks notifyHooks = {
158		&usb_serial_device_added,
159		&usb_serial_device_removed
160	};
161
162	gUSBModule->register_driver(DRIVER_NAME, NULL, 0, NULL);
163	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
164	TRACE_FUNCRET("< init_driver() returns\n");
165	return B_OK;
166}
167
168
169/* uninit_driver - called every time the driver is unloaded */
170void
171uninit_driver()
172{
173	TRACE_FUNCALLS("> uninit_driver()\n");
174
175	gUSBModule->uninstall_notify(DRIVER_NAME);
176	acquire_sem(gDriverLock);
177
178	for (int32 i = 0; i < DEVICES_COUNT; i++) {
179		if (gSerialDevices[i]) {
180			delete gSerialDevices[i];
181			gSerialDevices[i] = NULL;
182		}
183	}
184
185	for (int32 i = 0; gDeviceNames[i]; i++)
186		free(gDeviceNames[i]);
187
188	delete_sem(gDriverLock);
189	put_module(B_USB_MODULE_NAME);
190	put_module(B_TTY_MODULE_NAME);
191
192	TRACE_FUNCRET("< uninit_driver() returns\n");
193}
194
195
196bool
197usb_serial_service(struct tty *tty, uint32 op, void *buffer, size_t length)
198{
199	TRACE_FUNCALLS("> usb_serial_service(%p, 0x%08lx, %p, %lu)\n", tty,
200		op, buffer, length);
201
202	for (int32 i = 0; i < DEVICES_COUNT; i++) {
203		if (gSerialDevices[i]
204			&& gSerialDevices[i]->Service(tty, op, buffer, length)) {
205			TRACE_FUNCRET("< usb_serial_service() returns: true\n");
206			return true;
207		}
208	}
209
210	TRACE_FUNCRET("< usb_serial_service() returns: false\n");
211	return false;
212}
213
214
215/* usb_serial_open - handle open() calls */
216static status_t
217usb_serial_open(const char *name, uint32 flags, void **cookie)
218{
219	TRACE_FUNCALLS("> usb_serial_open(%s, 0x%08x, 0x%08x)\n", name, flags, cookie);
220	acquire_sem(gDriverLock);
221	status_t status = ENODEV;
222
223	*cookie = NULL;
224	int i = strtol(name + strlen(sDeviceBaseName), NULL, 10);
225	if (i >= 0 && i < DEVICES_COUNT && gSerialDevices[i]) {
226		status = gSerialDevices[i]->Open(flags);
227		*cookie = gSerialDevices[i];
228	}
229
230	release_sem(gDriverLock);
231	TRACE_FUNCRET("< usb_serial_open() returns: 0x%08x\n", status);
232	return status;
233}
234
235
236/* usb_serial_read - handle read() calls */
237static status_t
238usb_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
239{
240	TRACE_FUNCALLS("> usb_serial_read(0x%08x, %lld, 0x%08x, %d)\n", cookie,
241		position, buffer, *numBytes);
242	SerialDevice *device = (SerialDevice *)cookie;
243	status_t status = device->Read((char *)buffer, numBytes);
244	TRACE_FUNCRET("< usb_serial_read() returns: 0x%08x\n", status);
245	return status;
246}
247
248
249/* usb_serial_write - handle write() calls */
250static status_t
251usb_serial_write(void *cookie, off_t position, const void *buffer,
252	size_t *numBytes)
253{
254	TRACE_FUNCALLS("> usb_serial_write(0x%08x, %lld, 0x%08x, %d)\n", cookie,
255		position, buffer, *numBytes);
256	SerialDevice *device = (SerialDevice *)cookie;
257	status_t status = device->Write((const char *)buffer, numBytes);
258	TRACE_FUNCRET("< usb_serial_write() returns: 0x%08x\n", status);
259	return status;
260}
261
262
263/* usb_serial_control - handle ioctl calls */
264static status_t
265usb_serial_control(void *cookie, uint32 op, void *arg, size_t length)
266{
267	TRACE_FUNCALLS("> usb_serial_control(0x%08x, 0x%08x, 0x%08x, %d)\n",
268		cookie, op, arg, length);
269	SerialDevice *device = (SerialDevice *)cookie;
270	status_t status = device->Control(op, arg, length);
271	TRACE_FUNCRET("< usb_serial_control() returns: 0x%08x\n", status);
272	return status;
273}
274
275
276/* usb_serial_select - handle select start */
277static status_t
278usb_serial_select(void *cookie, uint8 event, uint32 ref, selectsync *sync)
279{
280	TRACE_FUNCALLS("> usb_serial_select(0x%08x, 0x%08x, 0x%08x, %p)\n",
281		cookie, event, ref, sync);
282	SerialDevice *device = (SerialDevice *)cookie;
283	status_t status = device->Select(event, ref, sync);
284	TRACE_FUNCRET("< usb_serial_select() returns: 0x%08x\n", status);
285	return status;
286}
287
288
289/* usb_serial_deselect - handle select exit */
290static status_t
291usb_serial_deselect(void *cookie, uint8 event, selectsync *sync)
292{
293	TRACE_FUNCALLS("> usb_serial_deselect(0x%08x, 0x%08x, %p)\n",
294		cookie, event, sync);
295	SerialDevice *device = (SerialDevice *)cookie;
296	status_t status = device->DeSelect(event, sync);
297	TRACE_FUNCRET("< usb_serial_deselect() returns: 0x%08x\n", status);
298	return status;
299}
300
301
302/* usb_serial_close - handle close() calls */
303static status_t
304usb_serial_close(void *cookie)
305{
306	TRACE_FUNCALLS("> usb_serial_close(0x%08x)\n", cookie);
307	SerialDevice *device = (SerialDevice *)cookie;
308	status_t status = device->Close();
309	TRACE_FUNCRET("< usb_serial_close() returns: 0x%08x\n", status);
310	return status;
311}
312
313
314/* usb_serial_free - called after last device is closed, and all i/o complete. */
315static status_t
316usb_serial_free(void *cookie)
317{
318	TRACE_FUNCALLS("> usb_serial_free(0x%08x)\n", cookie);
319	SerialDevice *device = (SerialDevice *)cookie;
320	acquire_sem(gDriverLock);
321	status_t status = device->Free();
322	if (device->IsRemoved()) {
323		for (int32 i = 0; i < DEVICES_COUNT; i++) {
324			if (gSerialDevices[i] == device) {
325				// the device is removed already but as it was open the
326				// removed hook has not deleted the object
327				delete device;
328				gSerialDevices[i] = NULL;
329				break;
330			}
331		}
332	}
333
334	release_sem(gDriverLock);
335	TRACE_FUNCRET("< usb_serial_free() returns: 0x%08x\n", status);
336	return status;
337}
338
339
340/* publish_devices - null-terminated array of devices supported by this driver. */
341const char **
342publish_devices()
343{
344	TRACE_FUNCALLS("> publish_devices()\n");
345	for (int32 i = 0; gDeviceNames[i]; i++)
346		free(gDeviceNames[i]);
347
348	int32 j = 0;
349	acquire_sem(gDriverLock);
350	for(int32 i = 0; i < DEVICES_COUNT; i++) {
351		if (gSerialDevices[i]) {
352			gDeviceNames[j] = (char *)malloc(strlen(sDeviceBaseName) + 4);
353			if (gDeviceNames[j]) {
354				sprintf(gDeviceNames[j], "%s%" B_PRId32, sDeviceBaseName, i);
355				j++;
356			} else
357				TRACE_ALWAYS("publish_devices - no memory to allocate device names\n");
358		}
359	}
360
361	gDeviceNames[j] = NULL;
362	release_sem(gDriverLock);
363	return (const char **)&gDeviceNames[0];
364}
365
366
367/* find_device - return poiter to device hooks structure for a given device */
368device_hooks *
369find_device(const char *name)
370{
371	static device_hooks deviceHooks = {
372		usb_serial_open,			/* -> open entry point */
373		usb_serial_close,			/* -> close entry point */
374		usb_serial_free,			/* -> free cookie */
375		usb_serial_control,			/* -> control entry point */
376		usb_serial_read,			/* -> read entry point */
377		usb_serial_write,			/* -> write entry point */
378		usb_serial_select,			/* -> select entry point */
379		usb_serial_deselect			/* -> deselect entry point */
380	};
381
382	TRACE_FUNCALLS("> find_device(%s)\n", name);
383	return &deviceHooks;
384}
385