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