162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/slab.h>
462306a36Sopenharmony_ci#include <linux/module.h>
562306a36Sopenharmony_ci#include <linux/usb/input.h>
662306a36Sopenharmony_ci#include <asm/unaligned.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/*
962306a36Sopenharmony_ci * Pressure-threshold modules param code from Alex Perry <alex.perry@ieee.org>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciMODULE_AUTHOR("Josh Myer <josh@joshisanerd.com>");
1362306a36Sopenharmony_ciMODULE_DESCRIPTION("USB KB Gear JamStudio Tablet driver");
1462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define USB_VENDOR_ID_KBGEAR	0x084e
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int kb_pressure_click = 0x10;
1962306a36Sopenharmony_cimodule_param(kb_pressure_click, int, 0);
2062306a36Sopenharmony_ciMODULE_PARM_DESC(kb_pressure_click, "pressure threshold for clicks");
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct kbtab {
2362306a36Sopenharmony_ci	unsigned char *data;
2462306a36Sopenharmony_ci	dma_addr_t data_dma;
2562306a36Sopenharmony_ci	struct input_dev *dev;
2662306a36Sopenharmony_ci	struct usb_interface *intf;
2762306a36Sopenharmony_ci	struct urb *irq;
2862306a36Sopenharmony_ci	char phys[32];
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic void kbtab_irq(struct urb *urb)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct kbtab *kbtab = urb->context;
3462306a36Sopenharmony_ci	unsigned char *data = kbtab->data;
3562306a36Sopenharmony_ci	struct input_dev *dev = kbtab->dev;
3662306a36Sopenharmony_ci	int pressure;
3762306a36Sopenharmony_ci	int retval;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	switch (urb->status) {
4062306a36Sopenharmony_ci	case 0:
4162306a36Sopenharmony_ci		/* success */
4262306a36Sopenharmony_ci		break;
4362306a36Sopenharmony_ci	case -ECONNRESET:
4462306a36Sopenharmony_ci	case -ENOENT:
4562306a36Sopenharmony_ci	case -ESHUTDOWN:
4662306a36Sopenharmony_ci		/* this urb is terminated, clean up */
4762306a36Sopenharmony_ci		dev_dbg(&kbtab->intf->dev,
4862306a36Sopenharmony_ci			"%s - urb shutting down with status: %d\n",
4962306a36Sopenharmony_ci			__func__, urb->status);
5062306a36Sopenharmony_ci		return;
5162306a36Sopenharmony_ci	default:
5262306a36Sopenharmony_ci		dev_dbg(&kbtab->intf->dev,
5362306a36Sopenharmony_ci			"%s - nonzero urb status received: %d\n",
5462306a36Sopenharmony_ci			__func__, urb->status);
5562306a36Sopenharmony_ci		goto exit;
5662306a36Sopenharmony_ci	}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	input_report_key(dev, BTN_TOOL_PEN, 1);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	input_report_abs(dev, ABS_X, get_unaligned_le16(&data[1]));
6262306a36Sopenharmony_ci	input_report_abs(dev, ABS_Y, get_unaligned_le16(&data[3]));
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/*input_report_key(dev, BTN_TOUCH , data[0] & 0x01);*/
6562306a36Sopenharmony_ci	input_report_key(dev, BTN_RIGHT, data[0] & 0x02);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	pressure = data[5];
6862306a36Sopenharmony_ci	if (kb_pressure_click == -1)
6962306a36Sopenharmony_ci		input_report_abs(dev, ABS_PRESSURE, pressure);
7062306a36Sopenharmony_ci	else
7162306a36Sopenharmony_ci		input_report_key(dev, BTN_LEFT, pressure > kb_pressure_click ? 1 : 0);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	input_sync(dev);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci exit:
7662306a36Sopenharmony_ci	retval = usb_submit_urb(urb, GFP_ATOMIC);
7762306a36Sopenharmony_ci	if (retval)
7862306a36Sopenharmony_ci		dev_err(&kbtab->intf->dev,
7962306a36Sopenharmony_ci			"%s - usb_submit_urb failed with result %d\n",
8062306a36Sopenharmony_ci			__func__, retval);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic const struct usb_device_id kbtab_ids[] = {
8462306a36Sopenharmony_ci	{ USB_DEVICE(USB_VENDOR_ID_KBGEAR, 0x1001), .driver_info = 0 },
8562306a36Sopenharmony_ci	{ }
8662306a36Sopenharmony_ci};
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, kbtab_ids);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic int kbtab_open(struct input_dev *dev)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct kbtab *kbtab = input_get_drvdata(dev);
9362306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(kbtab->intf);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	kbtab->irq->dev = udev;
9662306a36Sopenharmony_ci	if (usb_submit_urb(kbtab->irq, GFP_KERNEL))
9762306a36Sopenharmony_ci		return -EIO;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void kbtab_close(struct input_dev *dev)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct kbtab *kbtab = input_get_drvdata(dev);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	usb_kill_urb(kbtab->irq);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int kbtab_probe(struct usb_interface *intf, const struct usb_device_id *id)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	struct usb_device *dev = interface_to_usbdev(intf);
11262306a36Sopenharmony_ci	struct usb_endpoint_descriptor *endpoint;
11362306a36Sopenharmony_ci	struct kbtab *kbtab;
11462306a36Sopenharmony_ci	struct input_dev *input_dev;
11562306a36Sopenharmony_ci	int error = -ENOMEM;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	if (intf->cur_altsetting->desc.bNumEndpoints < 1)
11862306a36Sopenharmony_ci		return -ENODEV;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	endpoint = &intf->cur_altsetting->endpoint[0].desc;
12162306a36Sopenharmony_ci	if (!usb_endpoint_is_int_in(endpoint))
12262306a36Sopenharmony_ci		return -ENODEV;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	kbtab = kzalloc(sizeof(struct kbtab), GFP_KERNEL);
12562306a36Sopenharmony_ci	input_dev = input_allocate_device();
12662306a36Sopenharmony_ci	if (!kbtab || !input_dev)
12762306a36Sopenharmony_ci		goto fail1;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	kbtab->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &kbtab->data_dma);
13062306a36Sopenharmony_ci	if (!kbtab->data)
13162306a36Sopenharmony_ci		goto fail1;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	kbtab->irq = usb_alloc_urb(0, GFP_KERNEL);
13462306a36Sopenharmony_ci	if (!kbtab->irq)
13562306a36Sopenharmony_ci		goto fail2;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	kbtab->intf = intf;
13862306a36Sopenharmony_ci	kbtab->dev = input_dev;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	usb_make_path(dev, kbtab->phys, sizeof(kbtab->phys));
14162306a36Sopenharmony_ci	strlcat(kbtab->phys, "/input0", sizeof(kbtab->phys));
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	input_dev->name = "KB Gear Tablet";
14462306a36Sopenharmony_ci	input_dev->phys = kbtab->phys;
14562306a36Sopenharmony_ci	usb_to_input_id(dev, &input_dev->id);
14662306a36Sopenharmony_ci	input_dev->dev.parent = &intf->dev;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	input_set_drvdata(input_dev, kbtab);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	input_dev->open = kbtab_open;
15162306a36Sopenharmony_ci	input_dev->close = kbtab_close;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
15462306a36Sopenharmony_ci	input_dev->keybit[BIT_WORD(BTN_LEFT)] |=
15562306a36Sopenharmony_ci		BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
15662306a36Sopenharmony_ci	input_dev->keybit[BIT_WORD(BTN_DIGI)] |=
15762306a36Sopenharmony_ci		BIT_MASK(BTN_TOOL_PEN) | BIT_MASK(BTN_TOUCH);
15862306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_X, 0, 0x2000, 4, 0);
15962306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_Y, 0, 0x1750, 4, 0);
16062306a36Sopenharmony_ci	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 0xff, 0, 0);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	usb_fill_int_urb(kbtab->irq, dev,
16362306a36Sopenharmony_ci			 usb_rcvintpipe(dev, endpoint->bEndpointAddress),
16462306a36Sopenharmony_ci			 kbtab->data, 8,
16562306a36Sopenharmony_ci			 kbtab_irq, kbtab, endpoint->bInterval);
16662306a36Sopenharmony_ci	kbtab->irq->transfer_dma = kbtab->data_dma;
16762306a36Sopenharmony_ci	kbtab->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	error = input_register_device(kbtab->dev);
17062306a36Sopenharmony_ci	if (error)
17162306a36Sopenharmony_ci		goto fail3;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	usb_set_intfdata(intf, kbtab);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci fail3:	usb_free_urb(kbtab->irq);
17862306a36Sopenharmony_ci fail2:	usb_free_coherent(dev, 8, kbtab->data, kbtab->data_dma);
17962306a36Sopenharmony_ci fail1:	input_free_device(input_dev);
18062306a36Sopenharmony_ci	kfree(kbtab);
18162306a36Sopenharmony_ci	return error;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void kbtab_disconnect(struct usb_interface *intf)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct kbtab *kbtab = usb_get_intfdata(intf);
18762306a36Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	usb_set_intfdata(intf, NULL);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	input_unregister_device(kbtab->dev);
19262306a36Sopenharmony_ci	usb_free_urb(kbtab->irq);
19362306a36Sopenharmony_ci	usb_free_coherent(udev, 8, kbtab->data, kbtab->data_dma);
19462306a36Sopenharmony_ci	kfree(kbtab);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic struct usb_driver kbtab_driver = {
19862306a36Sopenharmony_ci	.name =		"kbtab",
19962306a36Sopenharmony_ci	.probe =	kbtab_probe,
20062306a36Sopenharmony_ci	.disconnect =	kbtab_disconnect,
20162306a36Sopenharmony_ci	.id_table =	kbtab_ids,
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cimodule_usb_driver(kbtab_driver);
205