18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This device is a anodised aluminium knob which connects over USB. It can measure 88c2ecf20Sopenharmony_ci * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with 98c2ecf20Sopenharmony_ci * a spring for automatic release. The base contains a pair of LEDs which illuminate 108c2ecf20Sopenharmony_ci * the translucent base. It rotates without limit and reports its relative rotation 118c2ecf20Sopenharmony_ci * back to the host when polled by the USB controller. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Testing with the knob I have has shown that it measures approximately 94 "clicks" 148c2ecf20Sopenharmony_ci * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was 158c2ecf20Sopenharmony_ci * a variable speed cordless electric drill) has shown that the device can measure 168c2ecf20Sopenharmony_ci * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from 178c2ecf20Sopenharmony_ci * the host. If it counts more than 7 clicks before it is polled, it will wrap back 188c2ecf20Sopenharmony_ci * to zero and start counting again. This was at quite high speed, however, almost 198c2ecf20Sopenharmony_ci * certainly faster than the human hand could turn it. Griffin say that it loses a 208c2ecf20Sopenharmony_ci * pulse or two on a direction change; the granularity is so fine that I never 218c2ecf20Sopenharmony_ci * noticed this in practice. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * The device's microcontroller can be programmed to set the LED to either a constant 248c2ecf20Sopenharmony_ci * intensity, or to a rhythmic pulsing. Several patterns and speeds are available. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * Griffin were very happy to provide documentation and free hardware for development. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * Some userspace tools are available on the web: http://sowerbutts.com/powermate/ 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#include <linux/kernel.h> 338c2ecf20Sopenharmony_ci#include <linux/slab.h> 348c2ecf20Sopenharmony_ci#include <linux/module.h> 358c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 368c2ecf20Sopenharmony_ci#include <linux/usb/input.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */ 398c2ecf20Sopenharmony_ci#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */ 408c2ecf20Sopenharmony_ci#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */ 438c2ecf20Sopenharmony_ci#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* these are the command codes we send to the device */ 468c2ecf20Sopenharmony_ci#define SET_STATIC_BRIGHTNESS 0x01 478c2ecf20Sopenharmony_ci#define SET_PULSE_ASLEEP 0x02 488c2ecf20Sopenharmony_ci#define SET_PULSE_AWAKE 0x03 498c2ecf20Sopenharmony_ci#define SET_PULSE_MODE 0x04 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci/* these refer to bits in the powermate_device's requires_update field. */ 528c2ecf20Sopenharmony_ci#define UPDATE_STATIC_BRIGHTNESS (1<<0) 538c2ecf20Sopenharmony_ci#define UPDATE_PULSE_ASLEEP (1<<1) 548c2ecf20Sopenharmony_ci#define UPDATE_PULSE_AWAKE (1<<2) 558c2ecf20Sopenharmony_ci#define UPDATE_PULSE_MODE (1<<3) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* at least two versions of the hardware exist, with differing payload 588c2ecf20Sopenharmony_ci sizes. the first three bytes always contain the "interesting" data in 598c2ecf20Sopenharmony_ci the relevant format. */ 608c2ecf20Sopenharmony_ci#define POWERMATE_PAYLOAD_SIZE_MAX 6 618c2ecf20Sopenharmony_ci#define POWERMATE_PAYLOAD_SIZE_MIN 3 628c2ecf20Sopenharmony_cistruct powermate_device { 638c2ecf20Sopenharmony_ci signed char *data; 648c2ecf20Sopenharmony_ci dma_addr_t data_dma; 658c2ecf20Sopenharmony_ci struct urb *irq, *config; 668c2ecf20Sopenharmony_ci struct usb_ctrlrequest *configcr; 678c2ecf20Sopenharmony_ci struct usb_device *udev; 688c2ecf20Sopenharmony_ci struct usb_interface *intf; 698c2ecf20Sopenharmony_ci struct input_dev *input; 708c2ecf20Sopenharmony_ci spinlock_t lock; 718c2ecf20Sopenharmony_ci int static_brightness; 728c2ecf20Sopenharmony_ci int pulse_speed; 738c2ecf20Sopenharmony_ci int pulse_table; 748c2ecf20Sopenharmony_ci int pulse_asleep; 758c2ecf20Sopenharmony_ci int pulse_awake; 768c2ecf20Sopenharmony_ci int requires_update; // physical settings which are out of sync 778c2ecf20Sopenharmony_ci char phys[64]; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic char pm_name_powermate[] = "Griffin PowerMate"; 818c2ecf20Sopenharmony_cistatic char pm_name_soundknob[] = "Griffin SoundKnob"; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic void powermate_config_complete(struct urb *urb); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* Callback for data arriving from the PowerMate over the USB interrupt pipe */ 868c2ecf20Sopenharmony_cistatic void powermate_irq(struct urb *urb) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct powermate_device *pm = urb->context; 898c2ecf20Sopenharmony_ci struct device *dev = &pm->intf->dev; 908c2ecf20Sopenharmony_ci int retval; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci switch (urb->status) { 938c2ecf20Sopenharmony_ci case 0: 948c2ecf20Sopenharmony_ci /* success */ 958c2ecf20Sopenharmony_ci break; 968c2ecf20Sopenharmony_ci case -ECONNRESET: 978c2ecf20Sopenharmony_ci case -ENOENT: 988c2ecf20Sopenharmony_ci case -ESHUTDOWN: 998c2ecf20Sopenharmony_ci /* this urb is terminated, clean up */ 1008c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - urb shutting down with status: %d\n", 1018c2ecf20Sopenharmony_ci __func__, urb->status); 1028c2ecf20Sopenharmony_ci return; 1038c2ecf20Sopenharmony_ci default: 1048c2ecf20Sopenharmony_ci dev_dbg(dev, "%s - nonzero urb status received: %d\n", 1058c2ecf20Sopenharmony_ci __func__, urb->status); 1068c2ecf20Sopenharmony_ci goto exit; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* handle updates to device state */ 1108c2ecf20Sopenharmony_ci input_report_key(pm->input, BTN_0, pm->data[0] & 0x01); 1118c2ecf20Sopenharmony_ci input_report_rel(pm->input, REL_DIAL, pm->data[1]); 1128c2ecf20Sopenharmony_ci input_sync(pm->input); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ciexit: 1158c2ecf20Sopenharmony_ci retval = usb_submit_urb (urb, GFP_ATOMIC); 1168c2ecf20Sopenharmony_ci if (retval) 1178c2ecf20Sopenharmony_ci dev_err(dev, "%s - usb_submit_urb failed with result: %d\n", 1188c2ecf20Sopenharmony_ci __func__, retval); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */ 1228c2ecf20Sopenharmony_cistatic void powermate_sync_state(struct powermate_device *pm) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci if (pm->requires_update == 0) 1258c2ecf20Sopenharmony_ci return; /* no updates are required */ 1268c2ecf20Sopenharmony_ci if (pm->config->status == -EINPROGRESS) 1278c2ecf20Sopenharmony_ci return; /* an update is already in progress; it'll issue this update when it completes */ 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (pm->requires_update & UPDATE_PULSE_ASLEEP){ 1308c2ecf20Sopenharmony_ci pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP ); 1318c2ecf20Sopenharmony_ci pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 ); 1328c2ecf20Sopenharmony_ci pm->requires_update &= ~UPDATE_PULSE_ASLEEP; 1338c2ecf20Sopenharmony_ci }else if (pm->requires_update & UPDATE_PULSE_AWAKE){ 1348c2ecf20Sopenharmony_ci pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE ); 1358c2ecf20Sopenharmony_ci pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 ); 1368c2ecf20Sopenharmony_ci pm->requires_update &= ~UPDATE_PULSE_AWAKE; 1378c2ecf20Sopenharmony_ci }else if (pm->requires_update & UPDATE_PULSE_MODE){ 1388c2ecf20Sopenharmony_ci int op, arg; 1398c2ecf20Sopenharmony_ci /* the powermate takes an operation and an argument for its pulse algorithm. 1408c2ecf20Sopenharmony_ci the operation can be: 1418c2ecf20Sopenharmony_ci 0: divide the speed 1428c2ecf20Sopenharmony_ci 1: pulse at normal speed 1438c2ecf20Sopenharmony_ci 2: multiply the speed 1448c2ecf20Sopenharmony_ci the argument only has an effect for operations 0 and 2, and ranges between 1458c2ecf20Sopenharmony_ci 1 (least effect) to 255 (maximum effect). 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci thus, several states are equivalent and are coalesced into one state. 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci we map this onto a range from 0 to 510, with: 1508c2ecf20Sopenharmony_ci 0 -- 254 -- use divide (0 = slowest) 1518c2ecf20Sopenharmony_ci 255 -- use normal speed 1528c2ecf20Sopenharmony_ci 256 -- 510 -- use multiple (510 = fastest). 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci Only values of 'arg' quite close to 255 are particularly useful/spectacular. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_ci if (pm->pulse_speed < 255) { 1578c2ecf20Sopenharmony_ci op = 0; // divide 1588c2ecf20Sopenharmony_ci arg = 255 - pm->pulse_speed; 1598c2ecf20Sopenharmony_ci } else if (pm->pulse_speed > 255) { 1608c2ecf20Sopenharmony_ci op = 2; // multiply 1618c2ecf20Sopenharmony_ci arg = pm->pulse_speed - 255; 1628c2ecf20Sopenharmony_ci } else { 1638c2ecf20Sopenharmony_ci op = 1; // normal speed 1648c2ecf20Sopenharmony_ci arg = 0; // can be any value 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE ); 1678c2ecf20Sopenharmony_ci pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op ); 1688c2ecf20Sopenharmony_ci pm->requires_update &= ~UPDATE_PULSE_MODE; 1698c2ecf20Sopenharmony_ci } else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) { 1708c2ecf20Sopenharmony_ci pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS ); 1718c2ecf20Sopenharmony_ci pm->configcr->wIndex = cpu_to_le16( pm->static_brightness ); 1728c2ecf20Sopenharmony_ci pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS; 1738c2ecf20Sopenharmony_ci } else { 1748c2ecf20Sopenharmony_ci printk(KERN_ERR "powermate: unknown update required"); 1758c2ecf20Sopenharmony_ci pm->requires_update = 0; /* fudge the bug */ 1768c2ecf20Sopenharmony_ci return; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci pm->configcr->bRequestType = 0x41; /* vendor request */ 1828c2ecf20Sopenharmony_ci pm->configcr->bRequest = 0x01; 1838c2ecf20Sopenharmony_ci pm->configcr->wLength = 0; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0), 1868c2ecf20Sopenharmony_ci (void *) pm->configcr, NULL, 0, 1878c2ecf20Sopenharmony_ci powermate_config_complete, pm); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (usb_submit_urb(pm->config, GFP_ATOMIC)) 1908c2ecf20Sopenharmony_ci printk(KERN_ERR "powermate: usb_submit_urb(config) failed"); 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/* Called when our asynchronous control message completes. We may need to issue another immediately */ 1948c2ecf20Sopenharmony_cistatic void powermate_config_complete(struct urb *urb) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci struct powermate_device *pm = urb->context; 1978c2ecf20Sopenharmony_ci unsigned long flags; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (urb->status) 2008c2ecf20Sopenharmony_ci printk(KERN_ERR "powermate: config urb returned %d\n", urb->status); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci spin_lock_irqsave(&pm->lock, flags); 2038c2ecf20Sopenharmony_ci powermate_sync_state(pm); 2048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pm->lock, flags); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* Set the LED up as described and begin the sync with the hardware if required */ 2088c2ecf20Sopenharmony_cistatic void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, 2098c2ecf20Sopenharmony_ci int pulse_table, int pulse_asleep, int pulse_awake) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci unsigned long flags; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci if (pulse_speed < 0) 2148c2ecf20Sopenharmony_ci pulse_speed = 0; 2158c2ecf20Sopenharmony_ci if (pulse_table < 0) 2168c2ecf20Sopenharmony_ci pulse_table = 0; 2178c2ecf20Sopenharmony_ci if (pulse_speed > 510) 2188c2ecf20Sopenharmony_ci pulse_speed = 510; 2198c2ecf20Sopenharmony_ci if (pulse_table > 2) 2208c2ecf20Sopenharmony_ci pulse_table = 2; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci pulse_asleep = !!pulse_asleep; 2238c2ecf20Sopenharmony_ci pulse_awake = !!pulse_awake; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci spin_lock_irqsave(&pm->lock, flags); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* mark state updates which are required */ 2298c2ecf20Sopenharmony_ci if (static_brightness != pm->static_brightness) { 2308c2ecf20Sopenharmony_ci pm->static_brightness = static_brightness; 2318c2ecf20Sopenharmony_ci pm->requires_update |= UPDATE_STATIC_BRIGHTNESS; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci if (pulse_asleep != pm->pulse_asleep) { 2348c2ecf20Sopenharmony_ci pm->pulse_asleep = pulse_asleep; 2358c2ecf20Sopenharmony_ci pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS); 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci if (pulse_awake != pm->pulse_awake) { 2388c2ecf20Sopenharmony_ci pm->pulse_awake = pulse_awake; 2398c2ecf20Sopenharmony_ci pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) { 2428c2ecf20Sopenharmony_ci pm->pulse_speed = pulse_speed; 2438c2ecf20Sopenharmony_ci pm->pulse_table = pulse_table; 2448c2ecf20Sopenharmony_ci pm->requires_update |= UPDATE_PULSE_MODE; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci powermate_sync_state(pm); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&pm->lock, flags); 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci/* Callback from the Input layer when an event arrives from userspace to configure the LED */ 2538c2ecf20Sopenharmony_cistatic int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci unsigned int command = (unsigned int)_value; 2568c2ecf20Sopenharmony_ci struct powermate_device *pm = input_get_drvdata(dev); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (type == EV_MSC && code == MSC_PULSELED){ 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci bits 0- 7: 8 bits: LED brightness 2618c2ecf20Sopenharmony_ci bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster. 2628c2ecf20Sopenharmony_ci bits 17-18: 2 bits: pulse table (0, 1, 2 valid) 2638c2ecf20Sopenharmony_ci bit 19: 1 bit : pulse whilst asleep? 2648c2ecf20Sopenharmony_ci bit 20: 1 bit : pulse constantly? 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci int static_brightness = command & 0xFF; // bits 0-7 2678c2ecf20Sopenharmony_ci int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16 2688c2ecf20Sopenharmony_ci int pulse_table = (command >> 17) & 0x3; // bits 17-18 2698c2ecf20Sopenharmony_ci int pulse_asleep = (command >> 19) & 0x1; // bit 19 2708c2ecf20Sopenharmony_ci int pulse_awake = (command >> 20) & 0x1; // bit 20 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake); 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci pm->data = usb_alloc_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX, 2818c2ecf20Sopenharmony_ci GFP_KERNEL, &pm->data_dma); 2828c2ecf20Sopenharmony_ci if (!pm->data) 2838c2ecf20Sopenharmony_ci return -1; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL); 2868c2ecf20Sopenharmony_ci if (!pm->configcr) 2878c2ecf20Sopenharmony_ci return -ENOMEM; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return 0; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci usb_free_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX, 2958c2ecf20Sopenharmony_ci pm->data, pm->data_dma); 2968c2ecf20Sopenharmony_ci kfree(pm->configcr); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci/* Called whenever a USB device matching one in our supported devices table is connected */ 3008c2ecf20Sopenharmony_cistatic int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct usb_device *udev = interface_to_usbdev (intf); 3038c2ecf20Sopenharmony_ci struct usb_host_interface *interface; 3048c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *endpoint; 3058c2ecf20Sopenharmony_ci struct powermate_device *pm; 3068c2ecf20Sopenharmony_ci struct input_dev *input_dev; 3078c2ecf20Sopenharmony_ci int pipe, maxp; 3088c2ecf20Sopenharmony_ci int error = -ENOMEM; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci interface = intf->cur_altsetting; 3118c2ecf20Sopenharmony_ci if (interface->desc.bNumEndpoints < 1) 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci endpoint = &interface->endpoint[0].desc; 3158c2ecf20Sopenharmony_ci if (!usb_endpoint_is_int_in(endpoint)) 3168c2ecf20Sopenharmony_ci return -EIO; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 3198c2ecf20Sopenharmony_ci 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE, 3208c2ecf20Sopenharmony_ci 0, interface->desc.bInterfaceNumber, NULL, 0, 3218c2ecf20Sopenharmony_ci USB_CTRL_SET_TIMEOUT); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL); 3248c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 3258c2ecf20Sopenharmony_ci if (!pm || !input_dev) 3268c2ecf20Sopenharmony_ci goto fail1; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (powermate_alloc_buffers(udev, pm)) 3298c2ecf20Sopenharmony_ci goto fail2; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci pm->irq = usb_alloc_urb(0, GFP_KERNEL); 3328c2ecf20Sopenharmony_ci if (!pm->irq) 3338c2ecf20Sopenharmony_ci goto fail2; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci pm->config = usb_alloc_urb(0, GFP_KERNEL); 3368c2ecf20Sopenharmony_ci if (!pm->config) 3378c2ecf20Sopenharmony_ci goto fail3; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci pm->udev = udev; 3408c2ecf20Sopenharmony_ci pm->intf = intf; 3418c2ecf20Sopenharmony_ci pm->input = input_dev; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci usb_make_path(udev, pm->phys, sizeof(pm->phys)); 3448c2ecf20Sopenharmony_ci strlcat(pm->phys, "/input0", sizeof(pm->phys)); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci spin_lock_init(&pm->lock); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci switch (le16_to_cpu(udev->descriptor.idProduct)) { 3498c2ecf20Sopenharmony_ci case POWERMATE_PRODUCT_NEW: 3508c2ecf20Sopenharmony_ci input_dev->name = pm_name_powermate; 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci case POWERMATE_PRODUCT_OLD: 3538c2ecf20Sopenharmony_ci input_dev->name = pm_name_soundknob; 3548c2ecf20Sopenharmony_ci break; 3558c2ecf20Sopenharmony_ci default: 3568c2ecf20Sopenharmony_ci input_dev->name = pm_name_soundknob; 3578c2ecf20Sopenharmony_ci printk(KERN_WARNING "powermate: unknown product id %04x\n", 3588c2ecf20Sopenharmony_ci le16_to_cpu(udev->descriptor.idProduct)); 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci input_dev->phys = pm->phys; 3628c2ecf20Sopenharmony_ci usb_to_input_id(udev, &input_dev->id); 3638c2ecf20Sopenharmony_ci input_dev->dev.parent = &intf->dev; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, pm); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci input_dev->event = powermate_input_event; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) | 3708c2ecf20Sopenharmony_ci BIT_MASK(EV_MSC); 3718c2ecf20Sopenharmony_ci input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); 3728c2ecf20Sopenharmony_ci input_dev->relbit[BIT_WORD(REL_DIAL)] = BIT_MASK(REL_DIAL); 3738c2ecf20Sopenharmony_ci input_dev->mscbit[BIT_WORD(MSC_PULSELED)] = BIT_MASK(MSC_PULSELED); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* get a handle to the interrupt data pipe */ 3768c2ecf20Sopenharmony_ci pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); 3778c2ecf20Sopenharmony_ci maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) { 3808c2ecf20Sopenharmony_ci printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n", 3818c2ecf20Sopenharmony_ci POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp); 3828c2ecf20Sopenharmony_ci maxp = POWERMATE_PAYLOAD_SIZE_MAX; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci usb_fill_int_urb(pm->irq, udev, pipe, pm->data, 3868c2ecf20Sopenharmony_ci maxp, powermate_irq, 3878c2ecf20Sopenharmony_ci pm, endpoint->bInterval); 3888c2ecf20Sopenharmony_ci pm->irq->transfer_dma = pm->data_dma; 3898c2ecf20Sopenharmony_ci pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* register our interrupt URB with the USB system */ 3928c2ecf20Sopenharmony_ci if (usb_submit_urb(pm->irq, GFP_KERNEL)) { 3938c2ecf20Sopenharmony_ci error = -EIO; 3948c2ecf20Sopenharmony_ci goto fail4; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci error = input_register_device(pm->input); 3988c2ecf20Sopenharmony_ci if (error) 3998c2ecf20Sopenharmony_ci goto fail5; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* force an update of everything */ 4038c2ecf20Sopenharmony_ci pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS; 4048c2ecf20Sopenharmony_ci powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci usb_set_intfdata(intf, pm); 4078c2ecf20Sopenharmony_ci return 0; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci fail5: usb_kill_urb(pm->irq); 4108c2ecf20Sopenharmony_ci fail4: usb_free_urb(pm->config); 4118c2ecf20Sopenharmony_ci fail3: usb_free_urb(pm->irq); 4128c2ecf20Sopenharmony_ci fail2: powermate_free_buffers(udev, pm); 4138c2ecf20Sopenharmony_ci fail1: input_free_device(input_dev); 4148c2ecf20Sopenharmony_ci kfree(pm); 4158c2ecf20Sopenharmony_ci return error; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci/* Called when a USB device we've accepted ownership of is removed */ 4198c2ecf20Sopenharmony_cistatic void powermate_disconnect(struct usb_interface *intf) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct powermate_device *pm = usb_get_intfdata (intf); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci usb_set_intfdata(intf, NULL); 4248c2ecf20Sopenharmony_ci if (pm) { 4258c2ecf20Sopenharmony_ci pm->requires_update = 0; 4268c2ecf20Sopenharmony_ci usb_kill_urb(pm->irq); 4278c2ecf20Sopenharmony_ci input_unregister_device(pm->input); 4288c2ecf20Sopenharmony_ci usb_kill_urb(pm->config); 4298c2ecf20Sopenharmony_ci usb_free_urb(pm->irq); 4308c2ecf20Sopenharmony_ci usb_free_urb(pm->config); 4318c2ecf20Sopenharmony_ci powermate_free_buffers(interface_to_usbdev(intf), pm); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci kfree(pm); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic const struct usb_device_id powermate_devices[] = { 4388c2ecf20Sopenharmony_ci { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) }, 4398c2ecf20Sopenharmony_ci { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) }, 4408c2ecf20Sopenharmony_ci { USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) }, 4418c2ecf20Sopenharmony_ci { } /* Terminating entry */ 4428c2ecf20Sopenharmony_ci}; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (usb, powermate_devices); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic struct usb_driver powermate_driver = { 4478c2ecf20Sopenharmony_ci .name = "powermate", 4488c2ecf20Sopenharmony_ci .probe = powermate_probe, 4498c2ecf20Sopenharmony_ci .disconnect = powermate_disconnect, 4508c2ecf20Sopenharmony_ci .id_table = powermate_devices, 4518c2ecf20Sopenharmony_ci}; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cimodule_usb_driver(powermate_driver); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ciMODULE_AUTHOR( "William R Sowerbutts" ); 4568c2ecf20Sopenharmony_ciMODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" ); 4578c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 458