xref: /haiku/src/add-ons/kernel/drivers/common/usb_modeswitch.cpp (revision 5c6260dc232fcb2d4d5d1103c1623dba9663b753)
1 /*
2  * Copyright 2010, Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval, korli@users.berlios.de
7  */
8 
9 /*
10 	Devices and messages reference: usb-modeswitch-data-20100826
11 */
12 
13 #include <ByteOrder.h>
14 #include <Drivers.h>
15 #include <KernelExport.h>
16 #include <lock.h>
17 #include <USB3.h>
18 
19 #include <malloc.h>
20 #include <stdio.h>
21 #include <string.h>
22 
23 #define DRIVER_NAME			"usb_modeswitch"
24 
25 #define TRACE_USB_MODESWITCH 1
26 #ifdef TRACE_USB_MODESWITCH
27 #define TRACE(x...)			dprintf(DRIVER_NAME": "x)
28 #else
29 #define TRACE(x...)			/* nothing */
30 #endif
31 #define TRACE_ALWAYS(x...)	dprintf(DRIVER_NAME": "x)
32 #define ENTER()	TRACE("%s", __FUNCTION__)
33 
34 
35 enum msgType {
36 	MSG_NONE = 0,
37 	MSG_HUAWEI_1,
38 	MSG_HUAWEI_2,
39 	MSG_HUAWEI_3,
40 	MSG_NOKIA_1,
41 	MSG_OLIVETTI_1,
42 	MSG_OLIVETTI_2,
43 	MSG_OPTION_1,
44 	MSG_ATHEROS_1,
45 };
46 
47 
48 unsigned char kDevicesMsg[][31] = {
49 	{ 	/* MSG_HUAWEI_1 */
50 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
51 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
52 		0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
54 	},
55 	{	/* MSG_HUAWEI_2 */
56 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
57 		0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x0a, 0x11,
58 		0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
60 	},
61 	{	/* MSG_HUAWEI_3 */
62 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
63 		0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x01,
64 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
66 	},
67 	{	/* MSG_NOKIA_1 */
68 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
69 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b,
70 		0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
71 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
72 	},
73 	{	/* MSG_OLIVETTI_1 */
74 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
75 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b,
76 		0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
77 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
78 	},
79 	{	/* MSG_OLIVETTI_2 */
80 		0x55, 0x53, 0x42, 0x43, 0x12, 0x34, 0x56, 0x78,
81 		0xc0, 0x00, 0x00, 0x00, 0x80, 0x01, 0x06, 0x06,
82 		0xf5, 0x04, 0x02, 0x52, 0x70, 0x00, 0x00, 0x00,
83 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
84 	},
85 	{	/* MSG_OPTION_1 */
86 		0x55, 0x53, 0x42, 0x43, 0x78, 0x56, 0x34, 0x12,
87 		0x01, 0x00, 0x00, 0x00, 0x80, 0x00, 0x06, 0x10,
88 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
90 	},
91 	{	/* MSG_ATHEROS_1 */
92 		0x55, 0x53, 0x42, 0x43, 0x29, 0x00, 0x00, 0x00,
93 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x1b,
94 		0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
95 		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
96 	}
97 
98 };
99 
100 
101 #define HUAWEI_VENDOR	0x12d1
102 #define NOKIA_VENDOR	0x0421
103 #define NOVATEL_VENDOR	0x1410
104 #define ZYDAS_VENDOR	0x0ace
105 #define ZTE_VENDOR	0x19d2
106 #define OLIVETTI_VENDOR	0x0b3c
107 #define OPTION_VENDOR	0x0af0
108 #define ATHEROS_VENDOR	0x0cf3
109 
110 
111 static const struct {
112 	usb_support_descriptor desc;
113 	msgType type;
114 } kDevices[] = {
115 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x1446}, MSG_HUAWEI_1},
116 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x14ad}, MSG_HUAWEI_1},
117 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x14c1}, MSG_HUAWEI_1},
118 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x1520}, MSG_HUAWEI_1},
119 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x1521}, MSG_HUAWEI_1},
120 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x1523}, MSG_HUAWEI_1},
121 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x1557}, MSG_HUAWEI_1},
122 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x1031}, MSG_HUAWEI_2},
123 	{{ 0, 0, 0, HUAWEI_VENDOR, 0x101e}, MSG_HUAWEI_3},
124 	{{ 0, 0, 0, NOKIA_VENDOR, 0x060c}, MSG_NOKIA_1},
125 	{{ 0, 0, 0, NOKIA_VENDOR, 0x0610}, MSG_NOKIA_1},
126 	{{ 0, 0, 0, NOVATEL_VENDOR, 0x5010}, MSG_NOKIA_1},
127 	{{ 0, 0, 0, NOVATEL_VENDOR, 0x5020}, MSG_NOKIA_1},
128 	{{ 0, 0, 0, NOVATEL_VENDOR, 0x5030}, MSG_NOKIA_1},
129 	{{ 0, 0, 0, NOVATEL_VENDOR, 0x5031}, MSG_NOKIA_1},
130 	{{ 0, 0, 0, NOVATEL_VENDOR, 0x5041}, MSG_NOKIA_1},
131 	{{ 0, 0, 0, ZYDAS_VENDOR, 0x2011}, MSG_NOKIA_1},
132 	{{ 0, 0, 0, ZYDAS_VENDOR, 0x20ff}, MSG_NOKIA_1},
133 	{{ 0, 0, 0, ZTE_VENDOR, 0x0026}, MSG_NOKIA_1},
134 	{{ 0, 0, 0, ZTE_VENDOR, 0x0083}, MSG_NOKIA_1},
135 	{{ 0, 0, 0, ZTE_VENDOR, 0x0101}, MSG_NOKIA_1},
136 	{{ 0, 0, 0, ZTE_VENDOR, 0x0115}, MSG_NOKIA_1},
137 	{{ 0, 0, 0, ZTE_VENDOR, 0x1001}, MSG_NOKIA_1},
138 	{{ 0, 0, 0, ZTE_VENDOR, 0x1007}, MSG_NOKIA_1},
139 	{{ 0, 0, 0, ZTE_VENDOR, 0x1009}, MSG_NOKIA_1},
140 	{{ 0, 0, 0, ZTE_VENDOR, 0x1013}, MSG_NOKIA_1},
141 	{{ 0, 0, 0, OLIVETTI_VENDOR, 0xc700}, MSG_OLIVETTI_1},
142 	{{ 0, 0, 0, OLIVETTI_VENDOR, 0xf000}, MSG_OLIVETTI_2},
143 	{{ 0, 0, 0, OPTION_VENDOR, 0x6711}, MSG_OPTION_1},
144 	{{ 0, 0, 0, OPTION_VENDOR, 0x6731}, MSG_OPTION_1},
145 	{{ 0, 0, 0, OPTION_VENDOR, 0x6751}, MSG_OPTION_1},
146 	{{ 0, 0, 0, OPTION_VENDOR, 0x6771}, MSG_OPTION_1},
147 	{{ 0, 0, 0, OPTION_VENDOR, 0x6791}, MSG_OPTION_1},
148 	{{ 0, 0, 0, OPTION_VENDOR, 0x6811}, MSG_OPTION_1},
149 	{{ 0, 0, 0, OPTION_VENDOR, 0x6911}, MSG_OPTION_1},
150 	{{ 0, 0, 0, OPTION_VENDOR, 0x6951}, MSG_OPTION_1},
151 	{{ 0, 0, 0, OPTION_VENDOR, 0x6971}, MSG_OPTION_1},
152 	{{ 0, 0, 0, OPTION_VENDOR, 0x7011}, MSG_OPTION_1},
153 	{{ 0, 0, 0, OPTION_VENDOR, 0x7031}, MSG_OPTION_1},
154 	{{ 0, 0, 0, OPTION_VENDOR, 0x7051}, MSG_OPTION_1},
155 	{{ 0, 0, 0, OPTION_VENDOR, 0x7111}, MSG_OPTION_1},
156 	{{ 0, 0, 0, OPTION_VENDOR, 0x7211}, MSG_OPTION_1},
157 	{{ 0, 0, 0, OPTION_VENDOR, 0x7251}, MSG_OPTION_1},
158 	{{ 0, 0, 0, OPTION_VENDOR, 0x7271}, MSG_OPTION_1},
159 	{{ 0, 0, 0, OPTION_VENDOR, 0x7301}, MSG_OPTION_1},
160 	{{ 0, 0, 0, OPTION_VENDOR, 0x7311}, MSG_OPTION_1},
161 	{{ 0, 0, 0, OPTION_VENDOR, 0x7361}, MSG_OPTION_1},
162 	{{ 0, 0, 0, OPTION_VENDOR, 0x7381}, MSG_OPTION_1},
163 	{{ 0, 0, 0, OPTION_VENDOR, 0x7401}, MSG_OPTION_1},
164 	{{ 0, 0, 0, OPTION_VENDOR, 0x7501}, MSG_OPTION_1},
165 	{{ 0, 0, 0, OPTION_VENDOR, 0x7601}, MSG_OPTION_1},
166 	{{ 0, 0, 0, OPTION_VENDOR, 0x7701}, MSG_OPTION_1},
167 	{{ 0, 0, 0, OPTION_VENDOR, 0x7801}, MSG_OPTION_1},
168 	{{ 0, 0, 0, OPTION_VENDOR, 0x7901}, MSG_OPTION_1},
169 	{{ 0, 0, 0, OPTION_VENDOR, 0x8200}, MSG_OPTION_1},
170 	{{ 0, 0, 0, OPTION_VENDOR, 0x8201}, MSG_OPTION_1},
171 	{{ 0, 0, 0, OPTION_VENDOR, 0x8300}, MSG_OPTION_1},
172 	{{ 0, 0, 0, OPTION_VENDOR, 0x8302}, MSG_OPTION_1},
173 	{{ 0, 0, 0, OPTION_VENDOR, 0x8304}, MSG_OPTION_1},
174 	{{ 0, 0, 0, OPTION_VENDOR, 0x8400}, MSG_OPTION_1},
175 	{{ 0, 0, 0, OPTION_VENDOR, 0xc031}, MSG_OPTION_1},
176 	{{ 0, 0, 0, OPTION_VENDOR, 0xc100}, MSG_OPTION_1},
177 	{{ 0, 0, 0, OPTION_VENDOR, 0xc031}, MSG_OPTION_1},
178 	{{ 0, 0, 0, OPTION_VENDOR, 0xd013}, MSG_OPTION_1},
179 	{{ 0, 0, 0, OPTION_VENDOR, 0xd031}, MSG_OPTION_1},
180 	{{ 0, 0, 0, OPTION_VENDOR, 0xd033}, MSG_OPTION_1},
181 	{{ 0, 0, 0, OPTION_VENDOR, 0xd035}, MSG_OPTION_1},
182 	{{ 0, 0, 0, OPTION_VENDOR, 0xd055}, MSG_OPTION_1},
183 	{{ 0, 0, 0, OPTION_VENDOR, 0xd057}, MSG_OPTION_1},
184 	{{ 0, 0, 0, OPTION_VENDOR, 0xd058}, MSG_OPTION_1},
185 	{{ 0, 0, 0, OPTION_VENDOR, 0xd155}, MSG_OPTION_1},
186 	{{ 0, 0, 0, OPTION_VENDOR, 0xd157}, MSG_OPTION_1},
187 	{{ 0, 0, 0, OPTION_VENDOR, 0xd255}, MSG_OPTION_1},
188 	{{ 0, 0, 0, OPTION_VENDOR, 0xd257}, MSG_OPTION_1},
189 	{{ 0, 0, 0, OPTION_VENDOR, 0xd357}, MSG_OPTION_1},
190 	{{ 0, 0, 0, ATHEROS_VENDOR, 0x20ff}, MSG_ATHEROS_1},
191 };
192 static uint32 kDevicesCount = sizeof(kDevices) / sizeof(kDevices[0]);
193 
194 
195 typedef struct _my_device {
196 	usb_device	device;
197 	bool		removed;
198 	mutex		lock;
199 	struct _my_device *link;
200 
201 	// device state
202 	usb_pipe	bulk_in;
203 	usb_pipe	bulk_out;
204 	uint8		interface;
205 	uint8       alternate_setting;
206 
207 	// used to store callback information
208 	sem_id		notify;
209 	status_t	status;
210 	size_t		actual_length;
211 
212 	msgType		type;
213 } my_device;
214 
215 
216 int32 api_version = B_CUR_DRIVER_API_VERSION;
217 static usb_module_info *gUSBModule = NULL;
218 static my_device *gDeviceList = NULL;
219 static uint32 gDeviceCount = 0;
220 static mutex gDeviceListLock;
221 
222 
223 //
224 //#pragma mark - Device Allocation Helper Functions
225 //
226 
227 
228 static void
229 my_free_device(my_device *device)
230 {
231 	mutex_lock(&device->lock);
232 	mutex_destroy(&device->lock);
233 	delete_sem(device->notify);
234 	free(device);
235 }
236 
237 
238 //
239 //#pragma mark - Bulk-only Functions
240 //
241 
242 
243 static void
244 my_callback(void *cookie, status_t status, void *data,
245 	size_t actualLength)
246 {
247 	my_device *device = (my_device *)cookie;
248 	device->status = status;
249 	device->actual_length = actualLength;
250 	release_sem(device->notify);
251 }
252 
253 
254 static status_t
255 my_transfer_data(my_device *device, bool directionIn, void *data,
256 	size_t dataLength)
257 {
258 	status_t result = gUSBModule->queue_bulk(directionIn ? device->bulk_in
259 		: device->bulk_out, data, dataLength, my_callback, device);
260 	if (result != B_OK) {
261 		TRACE_ALWAYS("failed to queue data transfer\n");
262 		return result;
263 	}
264 
265 	do {
266 		bigtime_t timeout = directionIn ? 100000 : 100000;
267 		result = acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT,
268 			timeout);
269 		if (result == B_TIMED_OUT) {
270 			// Cancel the transfer and collect the sem that should now be
271 			// released through the callback on cancel. Handling of device
272 			// reset is done in usb_printer_operation() when it detects that
273 			// the transfer failed.
274 			gUSBModule->cancel_queued_transfers(directionIn ? device->bulk_in
275 				: device->bulk_out);
276 			acquire_sem_etc(device->notify, 1, B_RELATIVE_TIMEOUT, 0);
277 		}
278 	} while (result == B_INTERRUPTED);
279 
280 	if (result != B_OK) {
281 		TRACE_ALWAYS("acquire_sem failed while waiting for data transfer\n");
282 		return result;
283 	}
284 
285 	return B_OK;
286 }
287 
288 
289 enum msgType
290 my_get_msg_type(const usb_device_descriptor *desc)
291 {
292 	for (uint32 i = 0; i < kDevicesCount; i++) {
293 		if (kDevices[i].desc.dev_class != 0x0
294 			&& kDevices[i].desc.dev_class != desc->device_class)
295 			continue;
296 		if (kDevices[i].desc.dev_subclass != 0x0
297 			&& kDevices[i].desc.dev_subclass != desc->device_subclass)
298 			continue;
299 		if (kDevices[i].desc.dev_protocol != 0x0
300 			&& kDevices[i].desc.dev_protocol != desc->device_protocol)
301 			continue;
302 		if (kDevices[i].desc.vendor != 0x0
303 			&& kDevices[i].desc.vendor != desc->vendor_id)
304 			continue;
305 		if (kDevices[i].desc.product != 0x0
306 			&& kDevices[i].desc.product != desc->product_id)
307 			continue;
308 
309 		return kDevices[i].type;
310 	}
311 
312 	return MSG_NONE;
313 }
314 
315 
316 
317 status_t
318 my_modeswitch(my_device* device)
319 {
320 	if (device->type == MSG_NONE)
321 		return B_OK;
322 
323 	status_t err = my_transfer_data(device, false, kDevicesMsg[device->type],
324 		sizeof(kDevicesMsg[device->type]));
325 	if (err != B_OK) {
326 		TRACE_ALWAYS("inquire message failed\n");
327 		return err;
328 	}
329 
330 	TRACE("device switched: %p\n", device);
331 
332 	char data[36];
333 	err = my_transfer_data(device, true, data, sizeof(data));
334 	if (err != B_OK) {
335 		TRACE_ALWAYS("inquire response failed\n");
336 		return err;
337 	}
338 
339 	TRACE("device switched: %p %.8s %.16s %.4s\n", device, data + 8, data + 16,
340 		data + 32);
341 
342 	return B_OK;
343 }
344 
345 
346 //
347 //#pragma mark - Device Attach/Detach Notifications and Callback
348 //
349 
350 
351 static status_t
352 my_device_added(usb_device newDevice, void **cookie)
353 {
354 	TRACE("device_added(0x%08lx)\n", newDevice);
355 	my_device *device = (my_device *)malloc(sizeof(my_device));
356 	device->device = newDevice;
357 	device->removed = false;
358 	device->interface = 0xff;
359 	device->alternate_setting = 0;
360 
361 	// scan through the interfaces to find our bulk-only data interface
362 	const usb_configuration_info *configuration =
363 		gUSBModule->get_configuration(newDevice);
364 	if (configuration == NULL) {
365 		free(device);
366 		return B_ERROR;
367 	}
368 
369 	for (size_t i = 0; i < configuration->interface_count; i++) {
370 		usb_interface_info *interface = configuration->interface[i].active;
371 		if (interface == NULL)
372 			continue;
373 
374 		if (true) {
375 
376 			bool hasIn = false;
377 			bool hasOut = false;
378 			for (size_t j = 0; j < interface->endpoint_count; j++) {
379 				usb_endpoint_info *endpoint = &interface->endpoint[j];
380 				if (endpoint == NULL
381 					|| endpoint->descr->attributes != USB_ENDPOINT_ATTR_BULK)
382 					continue;
383 
384 				if (!hasIn && (endpoint->descr->endpoint_address
385 					& USB_ENDPOINT_ADDR_DIR_IN)) {
386 					device->bulk_in = endpoint->handle;
387 					hasIn = true;
388 				} else if (!hasOut && (endpoint->descr->endpoint_address
389 					& USB_ENDPOINT_ADDR_DIR_IN) == 0) {
390 					device->bulk_out = endpoint->handle;
391 					hasOut = true;
392 				}
393 
394 				if (hasIn && hasOut)
395 					break;
396 			}
397 
398 			if (!(hasIn && hasOut))
399 				continue;
400 
401 			device->interface = interface->descr->interface_number;
402 			device->alternate_setting = interface->descr->alternate_setting;
403 
404 			break;
405 		}
406 	}
407 
408 	if (device->interface == 0xff) {
409 		TRACE_ALWAYS("no valid interface found\n");
410 		free(device);
411 		return B_ERROR;
412 	}
413 
414 	const usb_device_descriptor *descriptor
415 		= gUSBModule->get_device_descriptor(newDevice);
416 	if (descriptor == NULL) {
417 		free(device);
418 		return B_ERROR;
419 	}
420 	device->type = my_get_msg_type(descriptor);
421 
422 	mutex_init(&device->lock, DRIVER_NAME " device lock");
423 
424 	device->notify = create_sem(0, DRIVER_NAME " callback notify");
425 	if (device->notify < B_OK) {
426 		mutex_destroy(&device->lock);
427 		free(device);
428 		return device->notify;
429 	}
430 
431 	mutex_lock(&gDeviceListLock);
432 	device->link = gDeviceList;
433 	gDeviceList = device;
434 	mutex_unlock(&gDeviceListLock);
435 
436 	*cookie = device;
437 
438 	return my_modeswitch(device);
439 }
440 
441 
442 static status_t
443 my_device_removed(void *cookie)
444 {
445 	TRACE("device_removed(0x%08lx)\n", (uint32)cookie);
446 	my_device *device = (my_device *)cookie;
447 
448 	mutex_lock(&gDeviceListLock);
449 	if (gDeviceList == device) {
450 		gDeviceList = device->link;
451 	} else {
452 		my_device *element = gDeviceList;
453 		while (element) {
454 			if (element->link == device) {
455 				element->link = device->link;
456 				break;
457 			}
458 
459 			element = element->link;
460 		}
461 	}
462 	gDeviceCount--;
463 
464 	device->removed = true;
465 	gUSBModule->cancel_queued_transfers(device->bulk_in);
466 	gUSBModule->cancel_queued_transfers(device->bulk_out);
467 	my_free_device(device);
468 
469 	mutex_unlock(&gDeviceListLock);
470 	return B_OK;
471 }
472 
473 
474 //
475 //#pragma mark - Driver Entry Points
476 //
477 
478 
479 status_t
480 init_hardware()
481 {
482 	TRACE("init_hardware()\n");
483 	return B_OK;
484 }
485 
486 
487 status_t
488 init_driver()
489 {
490 	TRACE("init_driver()\n");
491 	static usb_notify_hooks notifyHooks = {
492 		&my_device_added,
493 		&my_device_removed
494 	};
495 
496 	gDeviceList = NULL;
497 	gDeviceCount = 0;
498 	mutex_init(&gDeviceListLock, DRIVER_NAME " device list lock");
499 
500 	TRACE("trying module %s\n", B_USB_MODULE_NAME);
501 	status_t result = get_module(B_USB_MODULE_NAME,
502 		(module_info **)&gUSBModule);
503 	if (result < B_OK) {
504 		TRACE_ALWAYS("getting module failed 0x%08lx\n", result);
505 		mutex_destroy(&gDeviceListLock);
506 		return result;
507 	}
508 
509 	size_t descriptorsSize = kDevicesCount * sizeof(usb_support_descriptor);
510 	usb_support_descriptor *supportedDevices =
511 		(usb_support_descriptor *)malloc(descriptorsSize);
512 	if (supportedDevices == NULL) {
513 		TRACE_ALWAYS("descriptor allocation failed\n");
514 		put_module(B_USB_MODULE_NAME);
515 		mutex_destroy(&gDeviceListLock);
516 		return result;
517 	}
518 
519 	for (uint32 i = 0; i < kDevicesCount; i++)
520 		supportedDevices[i] = kDevices[i].desc;
521 
522 	gUSBModule->register_driver(DRIVER_NAME, supportedDevices, kDevicesCount,
523 		NULL);
524 	gUSBModule->install_notify(DRIVER_NAME, &notifyHooks);
525 	free(supportedDevices);
526 	return B_OK;
527 }
528 
529 
530 void
531 uninit_driver()
532 {
533 	TRACE("uninit_driver()\n");
534 	gUSBModule->uninstall_notify(DRIVER_NAME);
535 	mutex_lock(&gDeviceListLock);
536 	mutex_destroy(&gDeviceListLock);
537 	put_module(B_USB_MODULE_NAME);
538 }
539 
540 
541 const char **
542 publish_devices()
543 {
544 	TRACE("publish_devices()\n");
545 	return NULL;
546 }
547 
548 
549 device_hooks *
550 find_device(const char *name)
551 {
552 	TRACE("find_device()\n");
553 	return NULL;
554 }
555