xref: /haiku/src/add-ons/kernel/drivers/ports/usb_serial/Driver.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
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 
18 int32 api_version = B_CUR_DRIVER_API_VERSION;
19 static const char *sDeviceBaseName = "ports/usb";
20 SerialDevice *gSerialDevices[DEVICES_COUNT];
21 char *gDeviceNames[DEVICES_COUNT + 1];
22 usb_module_info *gUSBModule = NULL;
23 tty_module_info *gTTYModule = NULL;
24 sem_id gDriverLock = -1;
25 
26 
27 status_t
28 usb_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 
87 status_t
88 usb_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 */
118 status_t
119 init_hardware()
120 {
121 	TRACE("init_hardware\n");
122 	return B_OK;
123 }
124 
125 
126 /* init_driver - called every time the driver is loaded. */
127 status_t
128 init_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 */
170 void
171 uninit_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 
196 bool
197 usb_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 */
216 static status_t
217 usb_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 */
237 static status_t
238 usb_serial_read(void *cookie, off_t position, void *buffer, size_t *numBytes)
239 {
240 	TRACE_FUNCALLS("> usb_serial_read(0x%08x, %Ld, 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 */
250 static status_t
251 usb_serial_write(void *cookie, off_t position, const void *buffer,
252 	size_t *numBytes)
253 {
254 	TRACE_FUNCALLS("> usb_serial_write(0x%08x, %Ld, 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 */
264 static status_t
265 usb_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 */
277 static status_t
278 usb_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 */
290 static status_t
291 usb_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 */
303 static status_t
304 usb_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. */
315 static status_t
316 usb_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. */
341 const char **
342 publish_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 */
368 device_hooks *
369 find_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