linux/drivers/usb/misc/cytherm.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* -*- linux-c -*-
   3 * Cypress USB Thermometer driver 
   4 * 
   5 * Copyright (c) 2004 Erik Rigtorp <erkki@linux.nu> <erik@rigtorp.com>
   6 * 
   7 * This driver works with Elektor magazine USB Interface as published in 
   8 * issue #291. It should also work with the original starter kit/demo board
   9 * from Cypress.
  10 */
  11
  12
  13#include <linux/kernel.h>
  14#include <linux/errno.h>
  15#include <linux/slab.h>
  16#include <linux/module.h>
  17#include <linux/usb.h>
  18
  19#define DRIVER_AUTHOR "Erik Rigtorp"
  20#define DRIVER_DESC "Cypress USB Thermometer driver"
  21
  22#define USB_SKEL_VENDOR_ID      0x04b4
  23#define USB_SKEL_PRODUCT_ID     0x0002
  24
  25static const struct usb_device_id id_table[] = {
  26        { USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
  27        { }
  28};
  29MODULE_DEVICE_TABLE (usb, id_table);
  30
  31/* Structure to hold all of our device specific stuff */
  32struct usb_cytherm {
  33        struct usb_device    *udev;      /* save off the usb device pointer */
  34        struct usb_interface *interface; /* the interface for this device */
  35        int brightness;
  36};
  37
  38
  39/* local function prototypes */
  40static int cytherm_probe(struct usb_interface *interface, 
  41                         const struct usb_device_id *id);
  42static void cytherm_disconnect(struct usb_interface *interface);
  43
  44
  45/* usb specific object needed to register this driver with the usb subsystem */
  46static struct usb_driver cytherm_driver = {
  47        .name =         "cytherm",
  48        .probe =        cytherm_probe,
  49        .disconnect =   cytherm_disconnect,
  50        .id_table =     id_table,
  51};
  52
  53/* Vendor requests */
  54/* They all operate on one byte at a time */
  55#define PING       0x00
  56#define READ_ROM   0x01 /* Reads form ROM, value = address */
  57#define READ_RAM   0x02 /* Reads form RAM, value = address */
  58#define WRITE_RAM  0x03 /* Write to RAM, value = address, index = data */
  59#define READ_PORT  0x04 /* Reads from port, value = address */
  60#define WRITE_PORT 0x05 /* Write to port, value = address, index = data */ 
  61
  62
  63/* Send a vendor command to device */
  64static int vendor_command(struct usb_device *dev, unsigned char request, 
  65                          unsigned char value, unsigned char index,
  66                          void *buf, int size)
  67{
  68        return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
  69                               request, 
  70                               USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
  71                               value, 
  72                               index, buf, size,
  73                               USB_CTRL_GET_TIMEOUT);
  74}
  75
  76
  77
  78#define BRIGHTNESS 0x2c     /* RAM location for brightness value */
  79#define BRIGHTNESS_SEM 0x2b /* RAM location for brightness semaphore */
  80
  81static ssize_t brightness_show(struct device *dev, struct device_attribute *attr, char *buf)
  82{
  83        struct usb_interface *intf = to_usb_interface(dev);    
  84        struct usb_cytherm *cytherm = usb_get_intfdata(intf);     
  85
  86        return sprintf(buf, "%i", cytherm->brightness);
  87}
  88
  89static ssize_t brightness_store(struct device *dev, struct device_attribute *attr, const char *buf,
  90                              size_t count)
  91{
  92        struct usb_interface *intf = to_usb_interface(dev);
  93        struct usb_cytherm *cytherm = usb_get_intfdata(intf);
  94
  95        unsigned char *buffer;
  96        int retval;
  97   
  98        buffer = kmalloc(8, GFP_KERNEL);
  99        if (!buffer)
 100                return 0;
 101
 102        cytherm->brightness = simple_strtoul(buf, NULL, 10);
 103   
 104        if (cytherm->brightness > 0xFF)
 105                cytherm->brightness = 0xFF;
 106        else if (cytherm->brightness < 0)
 107                cytherm->brightness = 0;
 108   
 109        /* Set brightness */
 110        retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS, 
 111                                cytherm->brightness, buffer, 8);
 112        if (retval)
 113                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 114        /* Inform µC that we have changed the brightness setting */
 115        retval = vendor_command(cytherm->udev, WRITE_RAM, BRIGHTNESS_SEM,
 116                                0x01, buffer, 8);
 117        if (retval)
 118                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 119   
 120        kfree(buffer);
 121   
 122        return count;
 123}
 124static DEVICE_ATTR_RW(brightness);
 125
 126
 127#define TEMP 0x33 /* RAM location for temperature */
 128#define SIGN 0x34 /* RAM location for temperature sign */
 129
 130static ssize_t temp_show(struct device *dev, struct device_attribute *attr, char *buf)
 131{
 132
 133        struct usb_interface *intf = to_usb_interface(dev);
 134        struct usb_cytherm *cytherm = usb_get_intfdata(intf);
 135
 136        int retval;
 137        unsigned char *buffer;
 138
 139        int temp, sign;
 140   
 141        buffer = kmalloc(8, GFP_KERNEL);
 142        if (!buffer)
 143                return 0;
 144
 145        /* read temperature */
 146        retval = vendor_command(cytherm->udev, READ_RAM, TEMP, 0, buffer, 8);
 147        if (retval)
 148                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 149        temp = buffer[1];
 150   
 151        /* read sign */
 152        retval = vendor_command(cytherm->udev, READ_RAM, SIGN, 0, buffer, 8);
 153        if (retval)
 154                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 155        sign = buffer[1];
 156
 157        kfree(buffer);
 158   
 159        return sprintf(buf, "%c%i.%i", sign ? '-' : '+', temp >> 1,
 160                       5*(temp - ((temp >> 1) << 1)));
 161}
 162static DEVICE_ATTR_RO(temp);
 163
 164
 165#define BUTTON 0x7a
 166
 167static ssize_t button_show(struct device *dev, struct device_attribute *attr, char *buf)
 168{
 169
 170        struct usb_interface *intf = to_usb_interface(dev);
 171        struct usb_cytherm *cytherm = usb_get_intfdata(intf);
 172
 173        int retval;
 174        unsigned char *buffer;
 175
 176        buffer = kmalloc(8, GFP_KERNEL);
 177        if (!buffer)
 178                return 0;
 179
 180        /* check button */
 181        retval = vendor_command(cytherm->udev, READ_RAM, BUTTON, 0, buffer, 8);
 182        if (retval)
 183                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 184   
 185        retval = buffer[1];
 186
 187        kfree(buffer);
 188
 189        if (retval)
 190                return sprintf(buf, "1");
 191        else
 192                return sprintf(buf, "0");
 193}
 194static DEVICE_ATTR_RO(button);
 195
 196
 197static ssize_t port0_show(struct device *dev, struct device_attribute *attr, char *buf)
 198{
 199        struct usb_interface *intf = to_usb_interface(dev);
 200        struct usb_cytherm *cytherm = usb_get_intfdata(intf);
 201
 202        int retval;
 203        unsigned char *buffer;
 204
 205        buffer = kmalloc(8, GFP_KERNEL);
 206        if (!buffer)
 207                return 0;
 208
 209        retval = vendor_command(cytherm->udev, READ_PORT, 0, 0, buffer, 8);
 210        if (retval)
 211                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 212
 213        retval = buffer[1];
 214
 215        kfree(buffer);
 216
 217        return sprintf(buf, "%d", retval);
 218}
 219
 220
 221static ssize_t port0_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 222{
 223        struct usb_interface *intf = to_usb_interface(dev);
 224        struct usb_cytherm *cytherm = usb_get_intfdata(intf);
 225
 226        unsigned char *buffer;
 227        int retval;
 228        int tmp;
 229   
 230        buffer = kmalloc(8, GFP_KERNEL);
 231        if (!buffer)
 232                return 0;
 233
 234        tmp = simple_strtoul(buf, NULL, 10);
 235   
 236        if (tmp > 0xFF)
 237                tmp = 0xFF;
 238        else if (tmp < 0)
 239                tmp = 0;
 240   
 241        retval = vendor_command(cytherm->udev, WRITE_PORT, 0,
 242                                tmp, buffer, 8);
 243        if (retval)
 244                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 245
 246        kfree(buffer);
 247
 248        return count;
 249}
 250static DEVICE_ATTR_RW(port0);
 251
 252static ssize_t port1_show(struct device *dev, struct device_attribute *attr, char *buf)
 253{
 254        struct usb_interface *intf = to_usb_interface(dev);
 255        struct usb_cytherm *cytherm = usb_get_intfdata(intf);
 256
 257        int retval;
 258        unsigned char *buffer;
 259
 260        buffer = kmalloc(8, GFP_KERNEL);
 261        if (!buffer)
 262                return 0;
 263
 264        retval = vendor_command(cytherm->udev, READ_PORT, 1, 0, buffer, 8);
 265        if (retval)
 266                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 267   
 268        retval = buffer[1];
 269
 270        kfree(buffer);
 271
 272        return sprintf(buf, "%d", retval);
 273}
 274
 275
 276static ssize_t port1_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 277{
 278        struct usb_interface *intf = to_usb_interface(dev);
 279        struct usb_cytherm *cytherm = usb_get_intfdata(intf);
 280
 281        unsigned char *buffer;
 282        int retval;
 283        int tmp;
 284   
 285        buffer = kmalloc(8, GFP_KERNEL);
 286        if (!buffer)
 287                return 0;
 288
 289        tmp = simple_strtoul(buf, NULL, 10);
 290   
 291        if (tmp > 0xFF)
 292                tmp = 0xFF;
 293        else if (tmp < 0)
 294                tmp = 0;
 295   
 296        retval = vendor_command(cytherm->udev, WRITE_PORT, 1,
 297                                tmp, buffer, 8);
 298        if (retval)
 299                dev_dbg(&cytherm->udev->dev, "retval = %d\n", retval);
 300
 301        kfree(buffer);
 302
 303        return count;
 304}
 305static DEVICE_ATTR_RW(port1);
 306
 307
 308static int cytherm_probe(struct usb_interface *interface, 
 309                         const struct usb_device_id *id)
 310{
 311        struct usb_device *udev = interface_to_usbdev(interface);
 312        struct usb_cytherm *dev = NULL;
 313        int retval = -ENOMEM;
 314
 315        dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
 316        if (!dev)
 317                goto error_mem;
 318
 319        dev->udev = usb_get_dev(udev);
 320
 321        usb_set_intfdata (interface, dev);
 322
 323        dev->brightness = 0xFF;
 324
 325        retval = device_create_file(&interface->dev, &dev_attr_brightness);
 326        if (retval)
 327                goto error;
 328        retval = device_create_file(&interface->dev, &dev_attr_temp);
 329        if (retval)
 330                goto error;
 331        retval = device_create_file(&interface->dev, &dev_attr_button);
 332        if (retval)
 333                goto error;
 334        retval = device_create_file(&interface->dev, &dev_attr_port0);
 335        if (retval)
 336                goto error;
 337        retval = device_create_file(&interface->dev, &dev_attr_port1);
 338        if (retval)
 339                goto error;
 340
 341        dev_info (&interface->dev,
 342                  "Cypress thermometer device now attached\n");
 343        return 0;
 344error:
 345        device_remove_file(&interface->dev, &dev_attr_brightness);
 346        device_remove_file(&interface->dev, &dev_attr_temp);
 347        device_remove_file(&interface->dev, &dev_attr_button);
 348        device_remove_file(&interface->dev, &dev_attr_port0);
 349        device_remove_file(&interface->dev, &dev_attr_port1);
 350        usb_set_intfdata (interface, NULL);
 351        usb_put_dev(dev->udev);
 352        kfree(dev);
 353error_mem:
 354        return retval;
 355}
 356
 357static void cytherm_disconnect(struct usb_interface *interface)
 358{
 359        struct usb_cytherm *dev;
 360
 361        dev = usb_get_intfdata (interface);
 362
 363        device_remove_file(&interface->dev, &dev_attr_brightness);
 364        device_remove_file(&interface->dev, &dev_attr_temp);
 365        device_remove_file(&interface->dev, &dev_attr_button);
 366        device_remove_file(&interface->dev, &dev_attr_port0);
 367        device_remove_file(&interface->dev, &dev_attr_port1);
 368
 369        /* first remove the files, then NULL the pointer */
 370        usb_set_intfdata (interface, NULL);
 371
 372        usb_put_dev(dev->udev);
 373
 374        kfree(dev);
 375
 376        dev_info(&interface->dev, "Cypress thermometer now disconnected\n");
 377}
 378
 379module_usb_driver(cytherm_driver);
 380
 381MODULE_AUTHOR(DRIVER_AUTHOR);
 382MODULE_DESCRIPTION(DRIVER_DESC);
 383MODULE_LICENSE("GPL");
 384