1/*
2 * Copyright 2005-2008, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan Aßmus <superstippi@gmx.de>
7 *
8 * Portions of this code are based on Be Sample Code released under the
9 * Be Sample Code license. (USB sound device driver sample code IIRC.)
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16#include <Drivers.h>
17#include <KernelExport.h>
18#include <OS.h>
19#include <USB3.h>
20
21int32 api_version = B_CUR_DRIVER_API_VERSION;
22
23#define DEBUG_DRIVER 0
24
25#if DEBUG_DRIVER
26#	define DPRINTF_INFO(x) dprintf x;
27#	define DPRINTF_ERR(x) dprintf x;
28#else
29#	define DPRINTF_INFO(x)
30#	define DPRINTF_ERR(x) dprintf x;
31#endif
32
33typedef struct wacom_device wacom_device;
34
35struct wacom_device {
36	wacom_device*		next;
37
38	int					open;
39	int					number;
40
41	usb_device			dev;
42
43	usb_pipe			pipe;
44	char*				data;
45	size_t				max_packet_size;
46	size_t				length;
47
48	uint16				vendor;
49	uint16				product;
50
51	sem_id				notify_lock;
52	uint32				status;
53};
54
55// handy strings for referring to ourself
56#define ID "wacom: "
57static const char* kDriverName = "wacom";
58static const char* kBasePublishPath = "input/wacom/usb/";
59
60// list of device instances and names for publishing
61static wacom_device* sDeviceList = NULL;
62static sem_id sDeviceListLock = -1;
63static int sDeviceCount = 0;
64
65static char** sDeviceNames = NULL;
66
67// handle for the USB bus manager
68static usb_module_info* usb;
69
70// These rather inelegant routines are used to assign numbers to
71// device instances so that they have unique names in devfs.
72
73static uint32 sDeviceNumbers = 0;
74
75// get_number
76static int
77get_number()
78{
79	int num;
80
81	for (num = 0; num < 32; num++) {
82		if (!(sDeviceNumbers & (1 << num))) {
83			sDeviceNumbers |= (1 << num);
84			return num;
85		}
86	}
87
88	return -1;
89}
90
91// put_number
92static void
93put_number(int num)
94{
95	sDeviceNumbers &= ~(1 << num);
96}
97
98// #pragma mark - Device addition and removal
99//
100// add_device() and remove_device() are used to create and tear down
101// device instances.  They are used from the callbacks device_added()
102// and device_removed() which are invoked by the USB bus manager.
103
104// add_device
105static wacom_device*
106add_device(usb_device dev)
107{
108	wacom_device *device = NULL;
109	int num, ifc, alt;
110	const usb_interface_info *ii;
111	status_t st;
112	const usb_device_descriptor* udd;
113	const usb_configuration_info *conf;
114	bool setConfiguration = false;
115
116	// we need these four for a Wacom tablet
117	size_t controlTransferLength;
118	int tryCount;
119	char repData[2] = { 0x02, 0x02 };
120	char retData[2] = { 0x00, 0x00 };
121
122	conf = usb->get_configuration(dev);
123	DPRINTF_INFO((ID "add_device(%ld, %p)\n", dev, conf));
124
125	if ((num = get_number()) < 0)
126		return NULL;
127
128	udd = usb->get_device_descriptor(dev);
129	// only pick up wacom tablets
130	if (udd && udd->vendor_id == 0x056a) {
131
132		DPRINTF_ERR((ID "add_device() - wacom detected\n"));
133
134		// see if the device has been configured already
135		if (!conf) {
136			conf = usb->get_nth_configuration(dev, 0);
137			setConfiguration = true;
138		}
139
140		if (!conf)
141			goto fail;
142
143		for (ifc = 0; ifc < conf->interface_count; ifc++) {
144			DPRINTF_INFO((ID "add_device() - examining interface: %d\n", ifc));
145			for (alt = 0; alt < conf->interface[ifc].alt_count; alt++) {
146				ii = &conf->interface[ifc].alt[alt];
147				DPRINTF_INFO((ID "add_device() - examining alt interface: "
148					"%d\n", alt));
149
150
151				// does it have the correct type of interface?
152				if (ii->descr->interface_class != 3) continue;
153				if (ii->descr->interface_subclass != 1) continue;
154				if (ii->endpoint_count != 1) continue;
155
156				// only accept input endpoints
157				if (ii->endpoint[0].descr->endpoint_address & 0x80) {
158					DPRINTF_INFO((ID "add_device() - found input endpoint\n"));
159					goto got_one;
160				}
161			}
162		}
163	} else
164		goto fail;
165
166fail:
167	put_number(num);
168	if (device) {
169		free(device->data);
170		free(device);
171	}
172	return NULL;
173
174got_one:
175	if ((device = (wacom_device *) malloc(sizeof(wacom_device))) == NULL)
176		goto fail;
177
178	device->dev = dev;
179	device->number = num;
180	device->open = 1;
181	device->notify_lock = -1;
182	device->data = NULL;
183
184//	if (setConfiguration) {
185		// the configuration has to be set yet (was not the current one)
186		DPRINTF_INFO((ID "add_device() - setting configuration...\n"));
187		if ((st = usb->set_configuration(dev, conf)) != B_OK) {
188			dprintf(ID "add_device() -> set_configuration() returns %ld\n", st);
189			goto fail;
190		} else
191			DPRINTF_ERR((ID " ... success!\n"));
192
193		if (conf->interface[ifc].active != ii) {
194			// the interface we found is not the active one and has to be set
195			DPRINTF_INFO((ID "add_device() - setting interface: %p...\n", ii));
196			if ((st = usb->set_alt_interface(dev, ii)) != B_OK) {
197				dprintf(ID "add_device() -> set_alt_interface() returns %ld\n",
198					st);
199				goto fail;
200			} else
201				DPRINTF_ERR((ID " ... success!\n"));
202		}
203		// see if the device is a Wacom tablet and needs some special treatment
204		// let's hope Wacom doesn't produce normal mice any time soon, or this
205		// check will have to be more specific about product_id...hehe
206		if (udd->vendor_id == 0x056a) {
207			// do the control transfers to set up absolute mode (default is HID
208			// mode)
209
210			// see 'Device Class Definition for HID 1.11' (HID1_11.pdf),
211			// par. 7.2 (available at www.usb.org/developers/hidpage)
212
213			// set protocol mode to 'report' (instead of 'boot')
214			controlTransferLength = 0;
215			// HID Class-Specific Request, Host to device (=0x21):
216			// SET_PROTOCOL (=0x0b) to Report Protocol (=1)
217			// of Interface #0 (=0)
218			st = usb->send_request(dev, 0x21, 0x0b, 1, 0, 0, 0,
219				&controlTransferLength);
220
221			if (st < B_OK) {
222				dprintf(ID "add_device() - control transfer 1 failed: %ld\n",
223					st);
224			}
225
226			// try up to five times to set the tablet to 'Wacom'-mode (enabling
227			// absolute mode, pressure data, etc.)
228			controlTransferLength = 2;
229
230			for (tryCount = 0; tryCount < 5; tryCount++) {
231				// HID Class-Specific Request, Host to device (=0x21):
232				// SET_REPORT (=0x09) type Feature (=3) with ID 2 (=2) of
233				// Interface #0 (=0) to repData (== { 0x02, 0x02 })
234				st = usb->send_request(dev, 0x21, 0x09, (3 << 8) + 2, 0, 2,
235					repData, &controlTransferLength);
236
237				if (st < B_OK) {
238					dprintf(ID "add_device() - control transfer 2 failed: %ld"
239						"\n", st);
240				}
241
242				// check if registers are set correctly
243
244				// HID Class-Specific Request, Device to host (=0xA1):
245				// GET_REPORT (=0x01) type Feature (=3) with ID 2 (=2) of
246				// Interface #0 (=0) to retData
247				st = usb->send_request(dev, 0xA1, 0x01, (3 << 8) + 2, 0, 2,
248					retData, &controlTransferLength);
249
250				if (st < B_OK) {
251					dprintf(ID "add_device() - control transfer 3 failed: %ld"
252						"\n", st);
253				}
254
255				DPRINTF_INFO((ID "add_device() - retData: %u - %u\n",
256					retData[0], retData[1]));
257
258				if (retData[0] == repData[0] && retData[1] == repData[1]) {
259					DPRINTF_INFO((ID "add_device() - successfully set "
260						"'Wacom'-mode\n"));
261					break;
262				}
263			}
264
265			DPRINTF_INFO((ID "add_device() - number of tries: %u\n",
266				tryCount + 1));
267
268			if (tryCount > 4) {
269				dprintf(ID "add_device() - set 'Wacom'-mode failed\n");
270			}
271		}
272//	}
273
274	// configure the rest of the wacom_device
275	device->pipe = ii->endpoint[0].handle;
276//DPRINTF_INFO((ID "add_device() - pipe id = %ld\n", device->pipe));
277	device->length = 0;
278	device->max_packet_size = ii->endpoint[0].descr->max_packet_size;
279	device->data = (char*)malloc(device->max_packet_size);
280	if (device->data == NULL)
281		goto fail;
282//DPRINTF_INFO((ID "add_device() - max packet length = %ld\n",
283//	device->max_packet_size));
284	device->status = 0;//B_USB_STATUS_SUCCESS;
285	device->vendor = udd->vendor_id;
286	device->product = udd->product_id;
287
288	DPRINTF_INFO((ID "add_device() - added %p (/dev/%s%d)\n", device,
289		kBasePublishPath, num));
290
291	// add it to the list of devices so it will be published, etc
292	acquire_sem(sDeviceListLock);
293	device->next = sDeviceList;
294	sDeviceList = device;
295	sDeviceCount++;
296	release_sem(sDeviceListLock);
297
298	return device;
299}
300
301// remove_device
302static void
303remove_device(wacom_device *device)
304{
305	put_number(device->number);
306
307	usb->cancel_queued_transfers(device->pipe);
308
309	delete_sem(device->notify_lock);
310	if (device->data)
311		free(device->data);
312	free(device);
313}
314
315// device_added
316static status_t
317device_added(usb_device dev, void** cookie)
318{
319	wacom_device* device;
320
321	DPRINTF_INFO((ID "device_added(%ld,...)\n", dev));
322
323	// first see, if this device is already added
324	acquire_sem(sDeviceListLock);
325	for (device = sDeviceList; device; device = device->next) {
326		DPRINTF_ERR((ID "device_added() - old device: %ld\n", device->dev));
327		if (device->dev == dev) {
328			DPRINTF_ERR((ID "device_added() - already added - done!\n"));
329			*cookie = (void*)device;
330			release_sem(sDeviceListLock);
331			return B_OK;
332		}
333	}
334	release_sem(sDeviceListLock);
335
336	if ((device = add_device(dev)) != NULL) {
337		*cookie = (void*)device;
338		DPRINTF_INFO((ID "device_added() - done!\n"));
339		return B_OK;
340	} else
341		DPRINTF_INFO((ID "device_added() - failed to add device!\n"));
342
343	return B_ERROR;
344}
345
346// device_removed
347static status_t
348device_removed(void *cookie)
349{
350	wacom_device *device = (wacom_device *) cookie;
351
352	DPRINTF_INFO((ID "device_removed(%p)\n", device));
353
354	if (device) {
355
356		acquire_sem(sDeviceListLock);
357
358		// remove it from the list of devices
359		if (device == sDeviceList) {
360			sDeviceList = device->next;
361		} else {
362			wacom_device *n;
363			for (n = sDeviceList; n; n = n->next) {
364				if (n->next == device) {
365					n->next = device->next;
366					break;
367				}
368			}
369		}
370		sDeviceCount--;
371
372		// tear it down if it's not open --
373		// otherwise the last device_free() will handle it
374
375		device->open--;
376
377		DPRINTF_ERR((ID "device_removed() open: %d\n", device->open));
378
379		if (device->open == 0) {
380			remove_device(device);
381		} else {
382			dprintf(ID "device /dev/%s%d still open -- marked for removal\n",
383				kBasePublishPath, device->number);
384		}
385
386		release_sem(sDeviceListLock);
387	}
388
389	return B_OK;
390}
391
392// #pragma mark - Device Hooks
393//
394// Here we implement the posixy driver hooks (open/close/read/write/ioctl)
395
396// device_open
397static status_t
398device_open(const char *dname, uint32 flags, void** cookie)
399{
400	wacom_device *device;
401	int n;
402	status_t ret = B_ERROR;
403
404	char controlDevicePath[1024];
405	sprintf(controlDevicePath, "%s%s", kBasePublishPath, "control");
406	if (strcmp(dname, controlDevicePath) == 0) {
407		dprintf(ID "device_open() -> refuse to open control device\n");
408		return B_ERROR;
409	}
410
411	n = atoi(dname + strlen(kBasePublishPath));
412
413	DPRINTF_INFO((ID "device_open(\"%s\",%d,...)\n", dname, flags));
414
415	acquire_sem(sDeviceListLock);
416	for (device = sDeviceList; device; device = device->next) {
417		if (device->number == n) {
418//			if (device->open <= 1) {
419				device->open++;
420				*cookie = device;
421				DPRINTF_ERR((ID "device_open() open: %d\n", device->open));
422
423				if (device->notify_lock < 0) {
424					device->notify_lock = create_sem(0, "notify_lock");
425					if (device->notify_lock < 0) {
426						ret = device->notify_lock;
427						device->open--;
428						*cookie = NULL;
429						dprintf(ID "device_open() -> create_sem() returns %ld\n",
430							ret);
431					} else {
432						ret = B_OK;
433					}
434				}
435				release_sem(sDeviceListLock);
436				return ret;
437//			} else {
438//				dprintf(ID "device_open() -> device is already open %ld\n", ret);
439//				release_sem(sDeviceListLock);
440//				return B_ERROR;
441//			}
442		}
443	}
444	release_sem(sDeviceListLock);
445	return ret;
446}
447
448// device_close
449static status_t
450device_close (void *cookie)
451{
452#if DEBUG_DRIVER
453	wacom_device *device = (wacom_device*) cookie;
454	DPRINTF_ERR((ID "device_close() name = \"%s%d\"\n", kBasePublishPath,
455		device->number));
456#endif
457	return B_OK;
458}
459
460// device_free
461static status_t
462device_free(void *cookie)
463{
464	wacom_device *device = (wacom_device *)cookie;
465
466	DPRINTF_INFO((ID "device_free() name = \"%s%d\"\n", kBasePublishPath,
467		device->number));
468
469	acquire_sem(sDeviceListLock);
470
471	device->open--;
472
473	DPRINTF_INFO((ID "device_free() open: %ld\n", device->open));
474
475	if (device->open == 0) {
476		remove_device(device);
477	}
478	release_sem(sDeviceListLock);
479
480	return B_OK;
481}
482
483// device_interupt_callback
484static void
485device_interupt_callback(void* cookie, status_t status, void* data,
486	uint32 actualLength)
487{
488	wacom_device* device = (wacom_device*)cookie;
489	uint32 length = min_c(actualLength, device->max_packet_size);
490
491	DPRINTF_INFO((ID "device_interupt_callback(%p) name = \"%s%d\" -> "
492		"status: %ld, length: %ld\n", cookie, kBasePublishPath, device->number,
493		status, actualLength));
494
495	device->status = status;
496	if (device->notify_lock >= 0) {
497		if (status == B_OK) {
498			memcpy(device->data, data, length);
499			device->length = length;
500		} else {
501			device->length = 0;
502		}
503		release_sem(device->notify_lock);
504	}
505
506	DPRINTF_INFO((ID "device_interupt_callback() - done\n"));
507}
508
509// read_header
510static void
511read_header(const wacom_device* device, void* buffer)
512{
513	uint16* ids = (uint16*)buffer;
514	uint32* size = (uint32*)buffer;
515
516	ids[0] = device->vendor;
517	ids[1] = device->product;
518	size[1] = device->max_packet_size;
519}
520
521// device_read
522static status_t
523device_read(void* cookie, off_t pos, void* buf, size_t* count)
524{
525	wacom_device* device = (wacom_device*) cookie;
526	status_t ret = B_BAD_VALUE;
527	uint8* buffer = (uint8*)buf;
528	uint32 dataLength;
529
530	if (!device)
531		return ret;
532
533	ret = device->notify_lock;
534
535	DPRINTF_INFO((ID "device_read(%p,%Ld,0x%x,%d) name = \"%s%d\"\n",
536			 cookie, pos, buf, *count, kBasePublishPath, device->number));
537
538	if (ret >= B_OK) {
539		// what the client "reads" is decided depending on how much bytes are
540		// provided 8 bytes are needed to "read" vendor id, product id and max
541		// packet size in case the client wants to read more than 8 bytes, a usb
542		// interupt transfer is scheduled, and an error report is returned as
543		// appropriate
544		if (*count > 8) {
545			// queue the interrupt transfer
546			ret = usb->queue_interrupt(device->pipe, device->data,
547				device->max_packet_size, device_interupt_callback, device);
548			if (ret >= B_OK) {
549				// we will block here until the interrupt transfer has been done
550				ret = acquire_sem_etc(device->notify_lock, 1,
551					B_RELATIVE_TIMEOUT, 500 * 1000);
552				// handle time out
553				if (ret < B_OK) {
554					usb->cancel_queued_transfers(device->pipe);
555					acquire_sem(device->notify_lock);
556						// collect the sem released by the cancel
557
558					if (ret == B_TIMED_OUT) {
559						// a time_out is ok, since it only means that the device
560						// had nothing to report (ie mouse/pen was not moved)
561						// within the given time interval
562						DPRINTF_INFO((ID "device_read(%p) name = \"%s%d\" -> "
563							"B_TIMED_OUT\n", cookie, kBasePublishPath,
564							device->number));
565						*count = 8;
566						read_header(device, buffer);
567						ret = B_OK;
568					} else {
569						// any other error trying to acquire the semaphore
570						*count = 0;
571					}
572				} else {
573					if (device->status == 0/*B_USBD_SUCCESS*/) {
574						DPRINTF_INFO((ID "interrupt transfer - success\n"));
575						// copy the data from the buffer
576						dataLength = min_c(device->length, *count - 8);
577						*count = dataLength + 8;
578						read_header(device, buffer);
579						memcpy(buffer + 8, device->data, dataLength);
580					} else {
581						// an error happened during the interrupt transfer
582						*count = 0;
583						dprintf(ID "interrupt transfer - failure: %ld\n",
584							device->status);
585						ret = B_ERROR;
586					}
587				}
588			} else {
589				*count = 0;
590				dprintf(ID "device_read(%p) name = \"%s%d\" -> error queuing "
591					"interrupt: %ld\n", cookie, kBasePublishPath,
592					device->number, ret);
593			}
594		} else if (*count == 8) {
595			read_header(device, buffer);
596			ret = B_OK;
597		} else {
598			dprintf(ID "device_read(%p) name = \"%s%d\" -> buffer size must be "
599				"at least 8 bytes!\n", cookie, kBasePublishPath,
600				device->number);
601			*count = 0;
602			ret = B_BAD_VALUE;
603		}
604	}
605
606	return ret;
607}
608
609// device_write
610static status_t
611device_write(void *cookie, off_t pos, const void *buf, size_t *count)
612{
613	return B_ERROR;
614}
615
616// device_control
617static status_t
618device_control (void *cookie, uint32 msg, void *arg1, size_t len)
619{
620	return B_ERROR;
621}
622
623// #pragma mark - Driver Hooks
624//
625// These functions provide the glue used by DevFS to load/unload
626// the driver and also handle registering with the USB bus manager
627// to receive device added and removed events
628
629static usb_notify_hooks notify_hooks =
630{
631	&device_added,
632	&device_removed
633};
634
635static const usb_support_descriptor kSupportedDevices[] =
636{
637	{ 3, 1, 2, 0, 0 }
638};
639
640// init_hardware
641status_t
642init_hardware(void)
643{
644	return B_OK;
645}
646
647// init_driver
648status_t
649init_driver(void)
650{
651	DPRINTF_INFO((ID "init_driver(), built %s %s\n", __DATE__, __TIME__));
652
653#if DEBUG_DRIVER && !defined(__HAIKU__)
654	if (load_driver_symbols(kDriverName) == B_OK) {
655		DPRINTF_INFO((ID "loaded symbols\n"));
656	} else {
657		DPRINTF_ERR((ID "no driver symbols loaded!\n"));
658	}
659#endif
660
661	if (get_module(B_USB_MODULE_NAME, (module_info**) &usb) != B_OK) {
662		DPRINTF_ERR((ID "cannot get module \"%s\"\n", B_USB_MODULE_NAME));
663		return B_ERROR;
664	}
665
666	if ((sDeviceListLock = create_sem(1,"sDeviceListLock")) < 0) {
667		put_module(B_USB_MODULE_NAME);
668		return sDeviceListLock;
669	}
670
671	usb->register_driver(kDriverName, kSupportedDevices, 1, NULL);
672	usb->install_notify(kDriverName, &notify_hooks);
673
674	return B_OK;
675}
676
677// uninit_driver
678void
679uninit_driver(void)
680{
681	int i;
682
683	DPRINTF_INFO((ID "uninit_driver()\n"));
684
685	usb->uninstall_notify(kDriverName);
686
687	delete_sem(sDeviceListLock);
688
689	put_module(B_USB_MODULE_NAME);
690
691	if (sDeviceNames) {
692		for (i = 0; sDeviceNames[i]; i++)
693			free(sDeviceNames[i]);
694		free(sDeviceNames);
695	}
696
697	DPRINTF_INFO((ID "uninit_driver() done\n"));
698}
699
700// publish_devices
701const char**
702publish_devices()
703{
704	wacom_device *device;
705	int i;
706
707	DPRINTF_INFO((ID "publish_devices()\n"));
708
709	if (sDeviceNames) {
710		for (i = 0; sDeviceNames[i]; i++)
711			free((char *) sDeviceNames[i]);
712		free(sDeviceNames);
713	}
714
715	acquire_sem(sDeviceListLock);
716	sDeviceNames = (char**)malloc(sizeof(char*) * (sDeviceCount + 2));
717	if (sDeviceNames) {
718		for (i = 0, device = sDeviceList; device; device = device->next) {
719			sDeviceNames[i] = (char*)malloc(strlen(kBasePublishPath) + 4);
720			if (sDeviceNames[i]) {
721				sprintf(sDeviceNames[i],"%s%d",kBasePublishPath,device->number);
722				DPRINTF_INFO((ID "publishing: \"/dev/%s\"\n",sDeviceNames[i]));
723				i++;
724			}
725		}
726		// publish the currently fake control device
727		sDeviceNames[i] = (char*)malloc(strlen(kBasePublishPath) + 8);
728		if (sDeviceNames[i])
729			sprintf(sDeviceNames[i], "%s%s", kBasePublishPath, "control");
730
731		sDeviceNames[i + 1] = NULL;
732	}
733
734	release_sem(sDeviceListLock);
735
736	return (const char**)sDeviceNames;
737}
738
739static device_hooks sDeviceHooks = {
740	device_open,
741	device_close,
742	device_free,
743	device_control,
744	device_read,
745	device_write
746};
747
748// find_device
749device_hooks*
750find_device(const char* name)
751{
752	return &sDeviceHooks;
753}
754