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