18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/kernel.h>
38c2ecf20Sopenharmony_ci#include <linux/slab.h>
48c2ecf20Sopenharmony_ci#include <linux/module.h>
58c2ecf20Sopenharmony_ci#include <linux/usb/input.h>
68c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/*
98c2ecf20Sopenharmony_ci * Pressure-threshold modules param code from Alex Perry <alex.perry@ieee.org>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciMODULE_AUTHOR("Josh Myer <josh@joshisanerd.com>");
138c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("USB KB Gear JamStudio Tablet driver");
148c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define USB_VENDOR_ID_KBGEAR	0x084e
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic int kb_pressure_click = 0x10;
198c2ecf20Sopenharmony_cimodule_param(kb_pressure_click, int, 0);
208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks");
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct kbtab {
238c2ecf20Sopenharmony_ci	unsigned char *data;
248c2ecf20Sopenharmony_ci	dma_addr_t data_dma;
258c2ecf20Sopenharmony_ci	struct input_dev *dev;
268c2ecf20Sopenharmony_ci	struct usb_interface *intf;
278c2ecf20Sopenharmony_ci	struct urb *irq;
288c2ecf20Sopenharmony_ci	char phys[32];
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void kbtab_irq(struct urb *urb)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct kbtab *kbtab = urb->context;
348c2ecf20Sopenharmony_ci	unsigned char *data = kbtab->data;
358c2ecf20Sopenharmony_ci	struct input_dev *dev = kbtab->dev;
368c2ecf20Sopenharmony_ci	int pressure;
378c2ecf20Sopenharmony_ci	int retval;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	switch (urb->status) {
408c2ecf20Sopenharmony_ci	case 0:
418c2ecf20Sopenharmony_ci		/* success */
428c2ecf20Sopenharmony_ci		break;
438c2ecf20Sopenharmony_ci	case -ECONNRESET:
448c2ecf20Sopenharmony_ci	case -ENOENT:
458c2ecf20Sopenharmony_ci	case -ESHUTDOWN:
468c2ecf20Sopenharmony_ci		/* this urb is terminated, clean up */
478c2ecf20Sopenharmony_ci		dev_dbg(&kbtab->intf->dev,
488c2ecf20Sopenharmony_ci			"%s - urb shutting down with status: %d\n",
498c2ecf20Sopenharmony_ci			__func__, urb->status);
508c2ecf20Sopenharmony_ci		return;
518c2ecf20Sopenharmony_ci	default:
528c2ecf20Sopenharmony_ci		dev_dbg(&kbtab->intf->dev,
538c2ecf20Sopenharmony_ci			"%s - nonzero urb status received: %d\n",
548c2ecf20Sopenharmony_ci			__func__, urb->status);
558c2ecf20Sopenharmony_ci		goto exit;
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	input_report_key(dev, BTN_TOOL_PEN, 1);
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	input_report_abs(dev, ABS_X, get_unaligned_le16(&data[1]));
628c2ecf20Sopenharmony_ci	input_report_abs(dev, ABS_Y, get_unaligned_le16(&data[3]));
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
658c2ecf20Sopenharmony_ci	input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	pressure = data[5];
688c2ecf20Sopenharmony_ci	if (kb_pressure_click == -1)
698c2ecf20Sopenharmony_ci		input_report_abs(dev, ABS_PRESSURE, pressure);
708c2ecf20Sopenharmony_ci	else
718c2ecf20Sopenharmony_ci		input_report_key(dev, BTN_LEFT, pressure > kb_pressure_click ? 1 : 0);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	input_sync(dev);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci exit:
768c2ecf20Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
778c2ecf20Sopenharmony_ci	if (retval)
788c2ecf20Sopenharmony_ci		dev_err(&kbtab->intf->dev,
798c2ecf20Sopenharmony_ci			"%s - usb_submit_urb failed with result %d\n",
808c2ecf20Sopenharmony_ci			__func__, retval);
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistatic const struct usb_device_id kbtab_ids[] = {
848c2ecf20Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
858c2ecf20Sopenharmony_ci	{ }
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, kbtab_ids);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic int kbtab_open(struct input_dev *dev)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	struct kbtab *kbtab = input_get_drvdata(dev);
938c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(kbtab->intf);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	kbtab->irq->dev = udev;
968c2ecf20Sopenharmony_ci	if (usb_submit_urb(kbtab->irq, GFP_KERNEL))
978c2ecf20Sopenharmony_ci		return -EIO;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic void kbtab_close(struct input_dev *dev)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct kbtab *kbtab = input_get_drvdata(dev);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	usb_kill_urb(kbtab->irq);
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	struct usb_device *dev = interface_to_usbdev(intf);
1128c2ecf20Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
1138c2ecf20Sopenharmony_ci	struct kbtab *kbtab;
1148c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
1158c2ecf20Sopenharmony_ci	int error = -ENOMEM;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (intf->cur_altsetting->desc.bNumEndpoints < 1)
1188c2ecf20Sopenharmony_ci		return -ENODEV;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	endpoint = &intf->cur_altsetting->endpoint[0].desc;
1218c2ecf20Sopenharmony_ci	if (!usb_endpoint_is_int_in(endpoint))
1228c2ecf20Sopenharmony_ci		return -ENODEV;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
1258c2ecf20Sopenharmony_ci	input_dev = input_allocate_device();
1268c2ecf20Sopenharmony_ci	if (!kbtab || !input_dev)
1278c2ecf20Sopenharmony_ci		goto fail1;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	kbtab->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &kbtab->data_dma);
1308c2ecf20Sopenharmony_ci	if (!kbtab->data)
1318c2ecf20Sopenharmony_ci		goto fail1;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
1348c2ecf20Sopenharmony_ci	if (!kbtab->irq)
1358c2ecf20Sopenharmony_ci		goto fail2;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	kbtab->intf = intf;
1388c2ecf20Sopenharmony_ci	kbtab->dev = input_dev;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys));
1418c2ecf20Sopenharmony_ci	strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys));
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	input_dev->name = "KB Gear Tablet";
1448c2ecf20Sopenharmony_ci	input_dev->phys = kbtab->phys;
1458c2ecf20Sopenharmony_ci	usb_to_input_id(dev, &input_dev->id);
1468c2ecf20Sopenharmony_ci	input_dev->dev.parent = &intf->dev;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, kbtab);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	input_dev->open = kbtab_open;
1518c2ecf20Sopenharmony_ci	input_dev->close = kbtab_close;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
1548c2ecf20Sopenharmony_ci	input_dev->keybit[BIT_WORD(BTN_LEFT)] |=
1558c2ecf20Sopenharmony_ci		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
1568c2ecf20Sopenharmony_ci	input_dev->keybit[BIT_WORD(BTN_DIGI)] |=
1578c2ecf20Sopenharmony_ci		BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH);
1588c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
1598c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
1608c2ecf20Sopenharmony_ci	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	usb_fill_int_urb(kbtab->irq, dev,
1638c2ecf20Sopenharmony_ci			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
1648c2ecf20Sopenharmony_ci			 kbtab->data, 8,
1658c2ecf20Sopenharmony_ci			 kbtab_irq, kbtab, endpoint->bInterval);
1668c2ecf20Sopenharmony_ci	kbtab->irq->transfer_dma = kbtab->data_dma;
1678c2ecf20Sopenharmony_ci	kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	error = input_register_device(kbtab->dev);
1708c2ecf20Sopenharmony_ci	if (error)
1718c2ecf20Sopenharmony_ci		goto fail3;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, kbtab);
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci fail3:	usb_free_urb(kbtab->irq);
1788c2ecf20Sopenharmony_ci fail2:	usb_free_coherent(dev, 8, kbtab->data, kbtab->data_dma);
1798c2ecf20Sopenharmony_ci fail1:	input_free_device(input_dev);
1808c2ecf20Sopenharmony_ci	kfree(kbtab);
1818c2ecf20Sopenharmony_ci	return error;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void kbtab_disconnect(struct usb_interface *intf)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct kbtab *kbtab = usb_get_intfdata(intf);
1878c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	input_unregister_device(kbtab->dev);
1928c2ecf20Sopenharmony_ci	usb_free_urb(kbtab->irq);
1938c2ecf20Sopenharmony_ci	usb_free_coherent(udev, 8, kbtab->data, kbtab->data_dma);
1948c2ecf20Sopenharmony_ci	kfree(kbtab);
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_cistatic struct usb_driver kbtab_driver = {
1988c2ecf20Sopenharmony_ci	.name =		"kbtab",
1998c2ecf20Sopenharmony_ci	.probe =	kbtab_probe,
2008c2ecf20Sopenharmony_ci	.disconnect =	kbtab_disconnect,
2018c2ecf20Sopenharmony_ci	.id_table =	kbtab_ids,
2028c2ecf20Sopenharmony_ci};
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cimodule_usb_driver(kbtab_driver);
205