linux/drivers/usb/misc/usbsevseg.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * USB 7 Segment Driver
   4 *
   5 * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com>
   6 * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com)
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/errno.h>
  11#include <linux/slab.h>
  12#include <linux/module.h>
  13#include <linux/string.h>
  14#include <linux/usb.h>
  15
  16
  17#define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>"
  18#define DRIVER_DESC "USB 7 Segment Driver"
  19
  20#define VENDOR_ID       0x0fc5
  21#define PRODUCT_ID      0x1227
  22#define MAXLEN          8
  23
  24/* table of devices that work with this driver */
  25static const struct usb_device_id id_table[] = {
  26        { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
  27        { },
  28};
  29MODULE_DEVICE_TABLE(usb, id_table);
  30
  31/* the different text display modes the device is capable of */
  32static const char *display_textmodes[] = {"raw", "hex", "ascii"};
  33
  34struct usb_sevsegdev {
  35        struct usb_device *udev;
  36        struct usb_interface *intf;
  37
  38        u8 powered;
  39        u8 mode_msb;
  40        u8 mode_lsb;
  41        u8 decimals[MAXLEN];
  42        u8 textmode;
  43        u8 text[MAXLEN];
  44        u16 textlength;
  45
  46        u8 shadow_power; /* for PM */
  47        u8 has_interface_pm;
  48};
  49
  50/* sysfs_streq can't replace this completely
  51 * If the device was in hex mode, and the user wanted a 0,
  52 * if str commands are used, we would assume the end of string
  53 * so mem commands are used.
  54 */
  55static inline size_t my_memlen(const char *buf, size_t count)
  56{
  57        if (count > 0 && buf[count-1] == '\n')
  58                return count - 1;
  59        else
  60                return count;
  61}
  62
  63static void update_display_powered(struct usb_sevsegdev *mydev)
  64{
  65        int rc;
  66
  67        if (mydev->powered && !mydev->has_interface_pm) {
  68                rc = usb_autopm_get_interface(mydev->intf);
  69                if (rc < 0)
  70                        return;
  71                mydev->has_interface_pm = 1;
  72        }
  73
  74        if (mydev->shadow_power != 1)
  75                return;
  76
  77        rc = usb_control_msg(mydev->udev,
  78                        usb_sndctrlpipe(mydev->udev, 0),
  79                        0x12,
  80                        0x48,
  81                        (80 * 0x100) + 10, /*  (power mode) */
  82                        (0x00 * 0x100) + (mydev->powered ? 1 : 0),
  83                        NULL,
  84                        0,
  85                        2000);
  86        if (rc < 0)
  87                dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);
  88
  89        if (!mydev->powered && mydev->has_interface_pm) {
  90                usb_autopm_put_interface(mydev->intf);
  91                mydev->has_interface_pm = 0;
  92        }
  93}
  94
  95static void update_display_mode(struct usb_sevsegdev *mydev)
  96{
  97        int rc;
  98
  99        if(mydev->shadow_power != 1)
 100                return;
 101
 102        rc = usb_control_msg(mydev->udev,
 103                        usb_sndctrlpipe(mydev->udev, 0),
 104                        0x12,
 105                        0x48,
 106                        (82 * 0x100) + 10, /* (set mode) */
 107                        (mydev->mode_msb * 0x100) + mydev->mode_lsb,
 108                        NULL,
 109                        0,
 110                        2000);
 111
 112        if (rc < 0)
 113                dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
 114}
 115
 116static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
 117{
 118        int rc;
 119        int i;
 120        unsigned char *buffer;
 121        u8 decimals = 0;
 122
 123        if(mydev->shadow_power != 1)
 124                return;
 125
 126        buffer = kzalloc(MAXLEN, mf);
 127        if (!buffer)
 128                return;
 129
 130        /* The device is right to left, where as you write left to right */
 131        for (i = 0; i < mydev->textlength; i++)
 132                buffer[i] = mydev->text[mydev->textlength-1-i];
 133
 134        rc = usb_control_msg(mydev->udev,
 135                        usb_sndctrlpipe(mydev->udev, 0),
 136                        0x12,
 137                        0x48,
 138                        (85 * 0x100) + 10, /* (write text) */
 139                        (0 * 0x100) + mydev->textmode, /* mode  */
 140                        buffer,
 141                        mydev->textlength,
 142                        2000);
 143
 144        if (rc < 0)
 145                dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
 146
 147        kfree(buffer);
 148
 149        /* The device is right to left, where as you write left to right */
 150        for (i = 0; i < sizeof(mydev->decimals); i++)
 151                decimals |= mydev->decimals[i] << i;
 152
 153        rc = usb_control_msg(mydev->udev,
 154                        usb_sndctrlpipe(mydev->udev, 0),
 155                        0x12,
 156                        0x48,
 157                        (86 * 0x100) + 10, /* (set decimal) */
 158                        (0 * 0x100) + decimals, /* decimals */
 159                        NULL,
 160                        0,
 161                        2000);
 162
 163        if (rc < 0)
 164                dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc);
 165}
 166
 167#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn)            \
 168static ssize_t name##_show(struct device *dev,                  \
 169        struct device_attribute *attr, char *buf)               \
 170{                                                               \
 171        struct usb_interface *intf = to_usb_interface(dev);     \
 172        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);   \
 173                                                                \
 174        return sprintf(buf, "%u\n", mydev->name);               \
 175}                                                               \
 176                                                                \
 177static ssize_t name##_store(struct device *dev,                 \
 178        struct device_attribute *attr, const char *buf, size_t count) \
 179{                                                               \
 180        struct usb_interface *intf = to_usb_interface(dev);     \
 181        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);   \
 182                                                                \
 183        mydev->name = simple_strtoul(buf, NULL, 10);            \
 184        update_fcn(mydev);                                      \
 185                                                                \
 186        return count;                                           \
 187}                                                               \
 188static DEVICE_ATTR_RW(name);
 189
 190static ssize_t text_show(struct device *dev,
 191        struct device_attribute *attr, char *buf)
 192{
 193        struct usb_interface *intf = to_usb_interface(dev);
 194        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
 195
 196        return snprintf(buf, mydev->textlength, "%s\n", mydev->text);
 197}
 198
 199static ssize_t text_store(struct device *dev,
 200        struct device_attribute *attr, const char *buf, size_t count)
 201{
 202        struct usb_interface *intf = to_usb_interface(dev);
 203        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
 204        size_t end = my_memlen(buf, count);
 205
 206        if (end > sizeof(mydev->text))
 207                return -EINVAL;
 208
 209        memset(mydev->text, 0, sizeof(mydev->text));
 210        mydev->textlength = end;
 211
 212        if (end > 0)
 213                memcpy(mydev->text, buf, end);
 214
 215        update_display_visual(mydev, GFP_KERNEL);
 216        return count;
 217}
 218
 219static DEVICE_ATTR_RW(text);
 220
 221static ssize_t decimals_show(struct device *dev,
 222        struct device_attribute *attr, char *buf)
 223{
 224        struct usb_interface *intf = to_usb_interface(dev);
 225        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
 226        int i;
 227        int pos;
 228
 229        for (i = 0; i < sizeof(mydev->decimals); i++) {
 230                pos = sizeof(mydev->decimals) - 1 - i;
 231                if (mydev->decimals[i] == 0)
 232                        buf[pos] = '0';
 233                else if (mydev->decimals[i] == 1)
 234                        buf[pos] = '1';
 235                else
 236                        buf[pos] = 'x';
 237        }
 238
 239        buf[sizeof(mydev->decimals)] = '\n';
 240        return sizeof(mydev->decimals) + 1;
 241}
 242
 243static ssize_t decimals_store(struct device *dev,
 244        struct device_attribute *attr, const char *buf, size_t count)
 245{
 246        struct usb_interface *intf = to_usb_interface(dev);
 247        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
 248        size_t end = my_memlen(buf, count);
 249        int i;
 250
 251        if (end > sizeof(mydev->decimals))
 252                return -EINVAL;
 253
 254        for (i = 0; i < end; i++)
 255                if (buf[i] != '0' && buf[i] != '1')
 256                        return -EINVAL;
 257
 258        memset(mydev->decimals, 0, sizeof(mydev->decimals));
 259        for (i = 0; i < end; i++)
 260                if (buf[i] == '1')
 261                        mydev->decimals[end-1-i] = 1;
 262
 263        update_display_visual(mydev, GFP_KERNEL);
 264
 265        return count;
 266}
 267
 268static DEVICE_ATTR_RW(decimals);
 269
 270static ssize_t textmode_show(struct device *dev,
 271        struct device_attribute *attr, char *buf)
 272{
 273        struct usb_interface *intf = to_usb_interface(dev);
 274        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
 275        int i;
 276
 277        buf[0] = 0;
 278
 279        for (i = 0; i < ARRAY_SIZE(display_textmodes); i++) {
 280                if (mydev->textmode == i) {
 281                        strcat(buf, " [");
 282                        strcat(buf, display_textmodes[i]);
 283                        strcat(buf, "] ");
 284                } else {
 285                        strcat(buf, " ");
 286                        strcat(buf, display_textmodes[i]);
 287                        strcat(buf, " ");
 288                }
 289        }
 290        strcat(buf, "\n");
 291
 292
 293        return strlen(buf);
 294}
 295
 296static ssize_t textmode_store(struct device *dev,
 297        struct device_attribute *attr, const char *buf, size_t count)
 298{
 299        struct usb_interface *intf = to_usb_interface(dev);
 300        struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
 301        int i;
 302
 303        i = sysfs_match_string(display_textmodes, buf);
 304        if (i < 0)
 305                return i;
 306
 307        mydev->textmode = i;
 308        update_display_visual(mydev, GFP_KERNEL);
 309        return count;
 310}
 311
 312static DEVICE_ATTR_RW(textmode);
 313
 314
 315MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
 316MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
 317MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
 318
 319static struct attribute *dev_attrs[] = {
 320        &dev_attr_powered.attr,
 321        &dev_attr_text.attr,
 322        &dev_attr_textmode.attr,
 323        &dev_attr_decimals.attr,
 324        &dev_attr_mode_msb.attr,
 325        &dev_attr_mode_lsb.attr,
 326        NULL
 327};
 328
 329static const struct attribute_group dev_attr_grp = {
 330        .attrs = dev_attrs,
 331};
 332
 333static int sevseg_probe(struct usb_interface *interface,
 334        const struct usb_device_id *id)
 335{
 336        struct usb_device *udev = interface_to_usbdev(interface);
 337        struct usb_sevsegdev *mydev = NULL;
 338        int rc = -ENOMEM;
 339
 340        mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
 341        if (!mydev)
 342                goto error_mem;
 343
 344        mydev->udev = usb_get_dev(udev);
 345        mydev->intf = interface;
 346        usb_set_intfdata(interface, mydev);
 347
 348        /* PM */
 349        mydev->shadow_power = 1; /* currently active */
 350        mydev->has_interface_pm = 0; /* have not issued autopm_get */
 351
 352        /*set defaults */
 353        mydev->textmode = 0x02; /* ascii mode */
 354        mydev->mode_msb = 0x06; /* 6 characters */
 355        mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
 356
 357        rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp);
 358        if (rc)
 359                goto error;
 360
 361        dev_info(&interface->dev, "USB 7 Segment device now attached\n");
 362        return 0;
 363
 364error:
 365        usb_set_intfdata(interface, NULL);
 366        usb_put_dev(mydev->udev);
 367        kfree(mydev);
 368error_mem:
 369        return rc;
 370}
 371
 372static void sevseg_disconnect(struct usb_interface *interface)
 373{
 374        struct usb_sevsegdev *mydev;
 375
 376        mydev = usb_get_intfdata(interface);
 377        sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp);
 378        usb_set_intfdata(interface, NULL);
 379        usb_put_dev(mydev->udev);
 380        kfree(mydev);
 381        dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
 382}
 383
 384static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
 385{
 386        struct usb_sevsegdev *mydev;
 387
 388        mydev = usb_get_intfdata(intf);
 389        mydev->shadow_power = 0;
 390
 391        return 0;
 392}
 393
 394static int sevseg_resume(struct usb_interface *intf)
 395{
 396        struct usb_sevsegdev *mydev;
 397
 398        mydev = usb_get_intfdata(intf);
 399        mydev->shadow_power = 1;
 400        update_display_mode(mydev);
 401        update_display_visual(mydev, GFP_NOIO);
 402
 403        return 0;
 404}
 405
 406static int sevseg_reset_resume(struct usb_interface *intf)
 407{
 408        struct usb_sevsegdev *mydev;
 409
 410        mydev = usb_get_intfdata(intf);
 411        mydev->shadow_power = 1;
 412        update_display_mode(mydev);
 413        update_display_visual(mydev, GFP_NOIO);
 414
 415        return 0;
 416}
 417
 418static struct usb_driver sevseg_driver = {
 419        .name =         "usbsevseg",
 420        .probe =        sevseg_probe,
 421        .disconnect =   sevseg_disconnect,
 422        .suspend =      sevseg_suspend,
 423        .resume =       sevseg_resume,
 424        .reset_resume = sevseg_reset_resume,
 425        .id_table =     id_table,
 426        .supports_autosuspend = 1,
 427};
 428
 429module_usb_driver(sevseg_driver);
 430
 431MODULE_AUTHOR(DRIVER_AUTHOR);
 432MODULE_DESCRIPTION(DRIVER_DESC);
 433MODULE_LICENSE("GPL");
 434