162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2001-2005 Edouard TISSERANT <edouard.tisserant@wanadoo.fr> 462306a36Sopenharmony_ci * Copyright (c) 2004-2005 Stephane VOLTZ <svoltz@numericable.fr> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * USB Acecad "Acecad Flair" tablet support 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Changelog: 962306a36Sopenharmony_ci * v3.2 - Added sysfs support 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/usb/input.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciMODULE_AUTHOR("Edouard TISSERANT <edouard.tisserant@wanadoo.fr>"); 1862306a36Sopenharmony_ciMODULE_DESCRIPTION("USB Acecad Flair tablet driver"); 1962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define USB_VENDOR_ID_ACECAD 0x0460 2262306a36Sopenharmony_ci#define USB_DEVICE_ID_FLAIR 0x0004 2362306a36Sopenharmony_ci#define USB_DEVICE_ID_302 0x0008 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct usb_acecad { 2662306a36Sopenharmony_ci char name[128]; 2762306a36Sopenharmony_ci char phys[64]; 2862306a36Sopenharmony_ci struct usb_interface *intf; 2962306a36Sopenharmony_ci struct input_dev *input; 3062306a36Sopenharmony_ci struct urb *irq; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci unsigned char *data; 3362306a36Sopenharmony_ci dma_addr_t data_dma; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic void usb_acecad_irq(struct urb *urb) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct usb_acecad *acecad = urb->context; 3962306a36Sopenharmony_ci unsigned char *data = acecad->data; 4062306a36Sopenharmony_ci struct input_dev *dev = acecad->input; 4162306a36Sopenharmony_ci struct usb_interface *intf = acecad->intf; 4262306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 4362306a36Sopenharmony_ci int prox, status; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci switch (urb->status) { 4662306a36Sopenharmony_ci case 0: 4762306a36Sopenharmony_ci /* success */ 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci case -ECONNRESET: 5062306a36Sopenharmony_ci case -ENOENT: 5162306a36Sopenharmony_ci case -ESHUTDOWN: 5262306a36Sopenharmony_ci /* this urb is terminated, clean up */ 5362306a36Sopenharmony_ci dev_dbg(&intf->dev, "%s - urb shutting down with status: %d\n", 5462306a36Sopenharmony_ci __func__, urb->status); 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci default: 5762306a36Sopenharmony_ci dev_dbg(&intf->dev, "%s - nonzero urb status received: %d\n", 5862306a36Sopenharmony_ci __func__, urb->status); 5962306a36Sopenharmony_ci goto resubmit; 6062306a36Sopenharmony_ci } 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci prox = (data[0] & 0x04) >> 2; 6362306a36Sopenharmony_ci input_report_key(dev, BTN_TOOL_PEN, prox); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (prox) { 6662306a36Sopenharmony_ci int x = data[1] | (data[2] << 8); 6762306a36Sopenharmony_ci int y = data[3] | (data[4] << 8); 6862306a36Sopenharmony_ci /* Pressure should compute the same way for flair and 302 */ 6962306a36Sopenharmony_ci int pressure = data[5] | (data[6] << 8); 7062306a36Sopenharmony_ci int touch = data[0] & 0x01; 7162306a36Sopenharmony_ci int stylus = (data[0] & 0x10) >> 4; 7262306a36Sopenharmony_ci int stylus2 = (data[0] & 0x20) >> 5; 7362306a36Sopenharmony_ci input_report_abs(dev, ABS_X, x); 7462306a36Sopenharmony_ci input_report_abs(dev, ABS_Y, y); 7562306a36Sopenharmony_ci input_report_abs(dev, ABS_PRESSURE, pressure); 7662306a36Sopenharmony_ci input_report_key(dev, BTN_TOUCH, touch); 7762306a36Sopenharmony_ci input_report_key(dev, BTN_STYLUS, stylus); 7862306a36Sopenharmony_ci input_report_key(dev, BTN_STYLUS2, stylus2); 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci /* event termination */ 8262306a36Sopenharmony_ci input_sync(dev); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ciresubmit: 8562306a36Sopenharmony_ci status = usb_submit_urb(urb, GFP_ATOMIC); 8662306a36Sopenharmony_ci if (status) 8762306a36Sopenharmony_ci dev_err(&intf->dev, 8862306a36Sopenharmony_ci "can't resubmit intr, %s-%s/input0, status %d\n", 8962306a36Sopenharmony_ci udev->bus->bus_name, 9062306a36Sopenharmony_ci udev->devpath, status); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int usb_acecad_open(struct input_dev *dev) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct usb_acecad *acecad = input_get_drvdata(dev); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci acecad->irq->dev = interface_to_usbdev(acecad->intf); 9862306a36Sopenharmony_ci if (usb_submit_urb(acecad->irq, GFP_KERNEL)) 9962306a36Sopenharmony_ci return -EIO; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic void usb_acecad_close(struct input_dev *dev) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct usb_acecad *acecad = input_get_drvdata(dev); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci usb_kill_urb(acecad->irq); 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_id *id) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci struct usb_device *dev = interface_to_usbdev(intf); 11462306a36Sopenharmony_ci struct usb_host_interface *interface = intf->cur_altsetting; 11562306a36Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 11662306a36Sopenharmony_ci struct usb_acecad *acecad; 11762306a36Sopenharmony_ci struct input_dev *input_dev; 11862306a36Sopenharmony_ci int pipe, maxp; 11962306a36Sopenharmony_ci int err; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (interface->desc.bNumEndpoints != 1) 12262306a36Sopenharmony_ci return -ENODEV; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci endpoint = &interface->endpoint[0].desc; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if (!usb_endpoint_is_int_in(endpoint)) 12762306a36Sopenharmony_ci return -ENODEV; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); 13062306a36Sopenharmony_ci maxp = usb_maxpacket(dev, pipe); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL); 13362306a36Sopenharmony_ci input_dev = input_allocate_device(); 13462306a36Sopenharmony_ci if (!acecad || !input_dev) { 13562306a36Sopenharmony_ci err = -ENOMEM; 13662306a36Sopenharmony_ci goto fail1; 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci acecad->data = usb_alloc_coherent(dev, 8, GFP_KERNEL, &acecad->data_dma); 14062306a36Sopenharmony_ci if (!acecad->data) { 14162306a36Sopenharmony_ci err= -ENOMEM; 14262306a36Sopenharmony_ci goto fail1; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci acecad->irq = usb_alloc_urb(0, GFP_KERNEL); 14662306a36Sopenharmony_ci if (!acecad->irq) { 14762306a36Sopenharmony_ci err = -ENOMEM; 14862306a36Sopenharmony_ci goto fail2; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci acecad->intf = intf; 15262306a36Sopenharmony_ci acecad->input = input_dev; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (dev->manufacturer) 15562306a36Sopenharmony_ci strscpy(acecad->name, dev->manufacturer, sizeof(acecad->name)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (dev->product) { 15862306a36Sopenharmony_ci if (dev->manufacturer) 15962306a36Sopenharmony_ci strlcat(acecad->name, " ", sizeof(acecad->name)); 16062306a36Sopenharmony_ci strlcat(acecad->name, dev->product, sizeof(acecad->name)); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci usb_make_path(dev, acecad->phys, sizeof(acecad->phys)); 16462306a36Sopenharmony_ci strlcat(acecad->phys, "/input0", sizeof(acecad->phys)); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci input_dev->name = acecad->name; 16762306a36Sopenharmony_ci input_dev->phys = acecad->phys; 16862306a36Sopenharmony_ci usb_to_input_id(dev, &input_dev->id); 16962306a36Sopenharmony_ci input_dev->dev.parent = &intf->dev; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci input_set_drvdata(input_dev, acecad); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci input_dev->open = usb_acecad_open; 17462306a36Sopenharmony_ci input_dev->close = usb_acecad_close; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 17762306a36Sopenharmony_ci input_dev->keybit[BIT_WORD(BTN_DIGI)] = BIT_MASK(BTN_TOOL_PEN) | 17862306a36Sopenharmony_ci BIT_MASK(BTN_TOUCH) | BIT_MASK(BTN_STYLUS) | 17962306a36Sopenharmony_ci BIT_MASK(BTN_STYLUS2); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci switch (id->driver_info) { 18262306a36Sopenharmony_ci case 0: 18362306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, 5000, 4, 0); 18462306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, 3750, 4, 0); 18562306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 0, 512, 0, 0); 18662306a36Sopenharmony_ci if (!strlen(acecad->name)) 18762306a36Sopenharmony_ci snprintf(acecad->name, sizeof(acecad->name), 18862306a36Sopenharmony_ci "USB Acecad Flair Tablet %04x:%04x", 18962306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idVendor), 19062306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idProduct)); 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci case 1: 19462306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_X, 0, 53000, 4, 0); 19562306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_Y, 0, 2250, 4, 0); 19662306a36Sopenharmony_ci input_set_abs_params(input_dev, ABS_PRESSURE, 0, 1024, 0, 0); 19762306a36Sopenharmony_ci if (!strlen(acecad->name)) 19862306a36Sopenharmony_ci snprintf(acecad->name, sizeof(acecad->name), 19962306a36Sopenharmony_ci "USB Acecad 302 Tablet %04x:%04x", 20062306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idVendor), 20162306a36Sopenharmony_ci le16_to_cpu(dev->descriptor.idProduct)); 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci usb_fill_int_urb(acecad->irq, dev, pipe, 20662306a36Sopenharmony_ci acecad->data, maxp > 8 ? 8 : maxp, 20762306a36Sopenharmony_ci usb_acecad_irq, acecad, endpoint->bInterval); 20862306a36Sopenharmony_ci acecad->irq->transfer_dma = acecad->data_dma; 20962306a36Sopenharmony_ci acecad->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci err = input_register_device(acecad->input); 21262306a36Sopenharmony_ci if (err) 21362306a36Sopenharmony_ci goto fail3; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci usb_set_intfdata(intf, acecad); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci fail3: usb_free_urb(acecad->irq); 22062306a36Sopenharmony_ci fail2: usb_free_coherent(dev, 8, acecad->data, acecad->data_dma); 22162306a36Sopenharmony_ci fail1: input_free_device(input_dev); 22262306a36Sopenharmony_ci kfree(acecad); 22362306a36Sopenharmony_ci return err; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic void usb_acecad_disconnect(struct usb_interface *intf) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct usb_acecad *acecad = usb_get_intfdata(intf); 22962306a36Sopenharmony_ci struct usb_device *udev = interface_to_usbdev(intf); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci usb_set_intfdata(intf, NULL); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci input_unregister_device(acecad->input); 23462306a36Sopenharmony_ci usb_free_urb(acecad->irq); 23562306a36Sopenharmony_ci usb_free_coherent(udev, 8, acecad->data, acecad->data_dma); 23662306a36Sopenharmony_ci kfree(acecad); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic const struct usb_device_id usb_acecad_id_table[] = { 24062306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_FLAIR), .driver_info = 0 }, 24162306a36Sopenharmony_ci { USB_DEVICE(USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_302), .driver_info = 1 }, 24262306a36Sopenharmony_ci { } 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, usb_acecad_id_table); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic struct usb_driver usb_acecad_driver = { 24862306a36Sopenharmony_ci .name = "usb_acecad", 24962306a36Sopenharmony_ci .probe = usb_acecad_probe, 25062306a36Sopenharmony_ci .disconnect = usb_acecad_disconnect, 25162306a36Sopenharmony_ci .id_table = usb_acecad_id_table, 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cimodule_usb_driver(usb_acecad_driver); 255