linux-old/drivers/usb/powermate.c
<<
>>
Prefs
   1/*
   2 * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
   3 *
   4 * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
   5 *
   6 * This device is an anodized aluminium knob which connects over USB. It can measure
   7 * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
   8 * a spring for automatic release. The base contains a pair of LEDs which illuminate
   9 * the translucent base. It rotates without limit and reports its relative rotation
  10 * back to the host when polled by the USB controller.
  11 *
  12 * Testing with the knob I have has shown that it measures approximately 96 "clicks"
  13 * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was 
  14 * a variable speed cordless electric drill) has shown that the device can measure
  15 * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
  16 * the host. If it counts more than 7 clicks before it is polled, it will wrap back
  17 * to zero and start counting again. This was at quite high speed, however, almost
  18 * certainly faster than the human hand could turn it. Griffin say that it loses a
  19 * pulse or two on a direction change; the granularity is so fine that I never
  20 * noticed this in practice.
  21 *
  22 * The device's microcontroller can be programmed to set the LED to either a constant
  23 * intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
  24 *
  25 * Griffin were very happy to provide documentation and free hardware for development.
  26 *
  27 */
  28
  29#include <linux/kernel.h>
  30#include <linux/slab.h>
  31#include <linux/input.h>
  32#include <linux/module.h>
  33#include <linux/init.h>
  34#include <linux/usb.h>
  35
  36#define POWERMATE_VENDOR       0x077d    /* Griffin Technology, Inc. */
  37#define POWERMATE_PRODUCT_NEW  0x0410    /* Griffin PowerMate */
  38#define POWERMATE_PRODUCT_OLD  0x04AA    /* Griffin soundKnob */
  39
  40/* these are the command codes we send to the device */
  41#define SET_STATIC_BRIGHTNESS  0x01
  42#define SET_PULSE_ASLEEP       0x02
  43#define SET_PULSE_AWAKE        0x03
  44#define SET_PULSE_MODE         0x04
  45
  46/* these refer to bits in the powermate_device's requires_update field. */
  47#define UPDATE_STATIC_BRIGHTNESS (1<<0)
  48#define UPDATE_PULSE_ASLEEP      (1<<1)
  49#define UPDATE_PULSE_AWAKE       (1<<2)
  50#define UPDATE_PULSE_MODE        (1<<3)
  51
  52/* at least two versions of the hardware exist, with differing payload 
  53   sizes. the first three bytes always contain the "interesting" data in
  54   the relevant format. */
  55#define POWERMATE_PAYLOAD_SIZE_MAX 6
  56#define POWERMATE_PAYLOAD_SIZE_MIN 3
  57struct powermate_device {
  58        signed char data[POWERMATE_PAYLOAD_SIZE_MAX];
  59        struct urb irq, config;
  60        struct usb_ctrlrequest configdr;
  61        struct usb_device *udev;
  62        struct input_dev input;
  63        struct semaphore lock;
  64        int static_brightness;
  65        int pulse_speed;
  66        int pulse_table;
  67        int pulse_asleep;
  68        int pulse_awake;
  69        int requires_update; // physical settings which are out of sync
  70        char phys[64];
  71};
  72
  73static char pm_name_powermate[] = "Griffin PowerMate";
  74static char pm_name_soundknob[] = "Griffin SoundKnob";
  75
  76static void powermate_config_complete(struct urb *urb); /* forward declararation of callback */
  77
  78/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
  79static void powermate_irq(struct urb *urb)
  80{
  81        struct powermate_device *pm = urb->context;
  82
  83        if(urb->status)
  84                return;
  85
  86        /* handle updates to device state */
  87        input_report_key(&pm->input, BTN_0, pm->data[0] & 0x01);
  88        input_report_rel(&pm->input, REL_DIAL, pm->data[1]);
  89}
  90
  91/* Decide if we need to issue a control message and do so. Must be called with pm->lock down */
  92static void powermate_sync_state(struct powermate_device *pm)
  93{
  94        if(pm->requires_update == 0) 
  95                return; /* no updates are required */
  96        if(pm->config.status == -EINPROGRESS) 
  97                return; /* an update is already in progress; it'll issue this update when it completes */
  98
  99        if(pm->requires_update & UPDATE_PULSE_ASLEEP){
 100                pm->configdr.wValue = cpu_to_le16( SET_PULSE_ASLEEP );
 101                pm->configdr.wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
 102                pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
 103        }else if(pm->requires_update & UPDATE_PULSE_AWAKE){
 104                pm->configdr.wValue = cpu_to_le16( SET_PULSE_AWAKE );
 105                pm->configdr.wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
 106                pm->requires_update &= ~UPDATE_PULSE_AWAKE;
 107        }else if(pm->requires_update & UPDATE_PULSE_MODE){
 108                int op, arg;
 109                /* the powermate takes an operation and an argument for its pulse algorithm.
 110                   the operation can be:
 111                   0: divide the speed
 112                   1: pulse at normal speed
 113                   2: multiply the speed
 114                   the argument only has an effect for operations 0 and 2, and ranges between
 115                   1 (least effect) to 255 (maximum effect).
 116       
 117                   thus, several states are equivalent and are coalesced into one state.
 118
 119                   we map this onto a range from 0 to 510, with:
 120                   0 -- 254    -- use divide (0 = slowest)
 121                   255         -- use normal speed
 122                   256 -- 510  -- use multiple (510 = fastest).
 123
 124                   Only values of 'arg' quite close to 255 are particularly useful/spectacular.
 125                */    
 126                if(pm->pulse_speed < 255){
 127                        op = 0;                   // divide
 128                        arg = 255 - pm->pulse_speed;
 129                }else if(pm->pulse_speed > 255){
 130                        op = 2;                   // multiply
 131                        arg = pm->pulse_speed - 255;
 132                }else{
 133                        op = 1;                   // normal speed
 134                        arg = 0;                  // can be any value
 135                }
 136                pm->configdr.wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
 137                pm->configdr.wIndex = cpu_to_le16( (arg << 8) | op );
 138                pm->requires_update &= ~UPDATE_PULSE_MODE;
 139        }else if(pm->requires_update & UPDATE_STATIC_BRIGHTNESS){
 140                pm->configdr.wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
 141                pm->configdr.wIndex = cpu_to_le16( pm->static_brightness );
 142                pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
 143        }else{
 144                printk(KERN_ERR "powermate: unknown update required");
 145                pm->requires_update = 0; /* fudge the bug */
 146                return;
 147        }
 148
 149        pm->config.dev = pm->udev; /* is this necessary? */
 150        pm->configdr.bRequestType = 0x41; /* vendor request */
 151        pm->configdr.bRequest = 0x01;
 152        pm->configdr.wLength = 0;
 153
 154        FILL_CONTROL_URB(&pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0), 
 155                         (void*)&pm->configdr, 0, 0, powermate_config_complete, pm);    
 156
 157        if(usb_submit_urb(&pm->config))
 158                printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
 159}
 160
 161/* Called when our asynchronous control message completes. We may need to issue another immediately */
 162static void powermate_config_complete(struct urb *urb)
 163{
 164        struct powermate_device *pm = urb->context;
 165
 166        if(urb->status)
 167                printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
 168        
 169        down(&pm->lock);
 170        powermate_sync_state(pm);
 171        up(&pm->lock);
 172}
 173
 174/* Set the LED up as described and begin the sync with the hardware if required */
 175static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed, 
 176                                int pulse_table, int pulse_asleep, int pulse_awake)
 177{
 178        if(pulse_speed < 0)
 179                pulse_speed = 0;
 180        if(pulse_table < 0)
 181                pulse_table = 0;
 182        if(pulse_speed > 510)
 183                pulse_speed = 510;
 184        if(pulse_table > 2)
 185                pulse_table = 2;
 186
 187        pulse_asleep = !!pulse_asleep;
 188        pulse_awake = !!pulse_awake;
 189
 190        down(&pm->lock);
 191
 192        /* mark state updates which are required */
 193        /* we update the static brightness if we've changed pulse mode,
 194           because the hardware seems to stick at an arbitrary value rather
 195           than jumping back to the static brightness we requested */
 196        if(static_brightness != pm->static_brightness){
 197                pm->static_brightness = static_brightness;
 198                pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;                
 199        }
 200        if(pulse_asleep != pm->pulse_asleep){
 201                pm->pulse_asleep = pulse_asleep;
 202                pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
 203        }
 204        if(pulse_awake != pm->pulse_awake){
 205                pm->pulse_awake = pulse_awake;
 206                pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
 207        }
 208        if(pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table){
 209                pm->pulse_speed = pulse_speed;
 210                pm->pulse_table = pulse_table;
 211                pm->requires_update |= UPDATE_PULSE_MODE;
 212        }
 213
 214        powermate_sync_state(pm);
 215   
 216        up(&pm->lock);
 217}
 218
 219/* Callback from the Input layer when an event arrives from userspace to configure the LED */
 220static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
 221{
 222        unsigned int command = (unsigned int)_value;
 223        struct powermate_device *pm = dev->private;
 224
 225        if(type == EV_MSC && code == MSC_PULSELED){
 226                /*  
 227                    bits  0- 7: 8 bits: LED brightness
 228                    bits  8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
 229                    bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
 230                    bit     19: 1 bit : pulse whilst asleep?
 231                    bit     20: 1 bit : pulse constantly?
 232                */  
 233                int static_brightness = command & 0xFF;   // bits 0-7
 234                int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
 235                int pulse_table = (command >> 17) & 0x3;  // bits 17-18
 236                int pulse_asleep = (command >> 19) & 0x1; // bit 19
 237                int pulse_awake  = (command >> 20) & 0x1; // bit 20
 238  
 239                powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
 240        }
 241
 242        return 0;
 243}
 244
 245/* Called whenever a USB device matching one in our supported devices table is connected */
 246static void *powermate_probe(struct usb_device *udev, unsigned int ifnum, const struct usb_device_id *id)
 247{
 248        struct usb_interface_descriptor *interface;
 249        struct usb_endpoint_descriptor *endpoint;
 250        struct powermate_device *pm;
 251        int pipe, maxp;
 252
 253        interface = udev->config[0].interface[ifnum].altsetting + 0;
 254        endpoint = interface->endpoint + 0;
 255        if (!(endpoint->bEndpointAddress & 0x80)) return NULL;
 256        if ((endpoint->bmAttributes & 3) != 3) return NULL;
 257
 258        usb_set_protocol(udev, interface->bInterfaceNumber, 0);
 259        usb_set_idle(udev, interface->bInterfaceNumber, 0, 0);
 260
 261        if (!(pm = kmalloc(sizeof(struct powermate_device), GFP_KERNEL)))
 262                return NULL;
 263
 264        memset(pm, 0, sizeof(struct powermate_device));
 265        pm->udev = udev;
 266
 267        init_MUTEX(&pm->lock);
 268
 269        /* get a handle to the interrupt data pipe */
 270        pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
 271        maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
 272
 273        if(maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX){
 274                printk("powermate: Expected payload of %d--%d bytes, found %d bytes!\n", 
 275                       POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
 276                maxp = POWERMATE_PAYLOAD_SIZE_MAX;
 277        }
 278
 279
 280        FILL_INT_URB(&pm->irq, udev, pipe, pm->data, maxp, powermate_irq, pm, endpoint->bInterval);
 281
 282        /* register our interrupt URB with the USB system */
 283        if(usb_submit_urb(&pm->irq)) {
 284                kfree(pm);
 285                return NULL; /* failure */
 286        }
 287
 288        switch (udev->descriptor.idProduct) {
 289        case POWERMATE_PRODUCT_NEW: pm->input.name = pm_name_powermate; break;
 290        case POWERMATE_PRODUCT_OLD: pm->input.name = pm_name_soundknob; break;
 291        default: 
 292          pm->input.name = pm_name_soundknob;
 293          printk(KERN_WARNING "powermate: unknown product id %04x\n", udev->descriptor.idProduct);
 294        }
 295
 296        pm->input.private = pm;
 297        pm->input.evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_MSC);
 298        pm->input.keybit[LONG(BTN_0)] = BIT(BTN_0);
 299        pm->input.relbit[LONG(REL_DIAL)] = BIT(REL_DIAL);
 300        pm->input.mscbit[LONG(MSC_PULSELED)] = BIT(MSC_PULSELED);
 301        pm->input.idbus = BUS_USB;
 302        pm->input.idvendor = udev->descriptor.idVendor;
 303        pm->input.idproduct = udev->descriptor.idProduct;
 304        pm->input.idversion = udev->descriptor.bcdDevice;
 305        pm->input.event = powermate_input_event;
 306
 307        input_register_device(&pm->input);
 308
 309#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) /* Vojtech -- is this the correct version? */
 310        {
 311                char path[64];
 312                usb_make_path(udev, path, 64);
 313                snprintf(pm->phys, 64, "%s/input0", path);
 314                printk(KERN_INFO "input: %s on %s\n", pm->input.name, pm->input.path);
 315        }
 316#else
 317        printk(KERN_INFO "input: %s on input%d\n", pm->input.name, pm->input.number);
 318#endif
 319        
 320        /* force an update of everything */
 321        pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
 322        powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
 323  
 324        return pm;
 325}
 326
 327/* Called when a USB device we've accepted ownership of is removed */
 328static void powermate_disconnect(struct usb_device *dev, void *ptr)
 329{
 330        struct powermate_device *pm = ptr;
 331        down(&pm->lock);
 332        pm->requires_update = 0;
 333        usb_unlink_urb(&pm->irq);  
 334        input_unregister_device(&pm->input);
 335  
 336        kfree(pm);
 337}
 338
 339static struct usb_device_id powermate_devices [] = {
 340        { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
 341        { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
 342        { } /* Terminating entry */
 343};
 344
 345MODULE_DEVICE_TABLE (usb, powermate_devices);
 346
 347static struct usb_driver powermate_driver = {
 348        name:           "powermate",
 349        probe:          powermate_probe,
 350        disconnect:     powermate_disconnect,
 351        id_table:       powermate_devices,
 352};
 353
 354int powermate_init(void)
 355{
 356        if (usb_register(&powermate_driver) < 0)
 357                return -1;
 358        return 0;
 359}
 360
 361void powermate_cleanup(void)
 362{
 363        usb_deregister(&powermate_driver);
 364}
 365
 366module_init(powermate_init);
 367module_exit(powermate_cleanup);
 368
 369MODULE_AUTHOR( "William R Sowerbutts" );
 370MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
 371MODULE_LICENSE("GPL");
 372
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.