Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * USB 7 Segment Driver
0004  *
0005  * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com>
0006  * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com)
0007  */
0008 
0009 #include <linux/kernel.h>
0010 #include <linux/errno.h>
0011 #include <linux/slab.h>
0012 #include <linux/module.h>
0013 #include <linux/string.h>
0014 #include <linux/usb.h>
0015 
0016 
0017 #define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>"
0018 #define DRIVER_DESC "USB 7 Segment Driver"
0019 
0020 #define VENDOR_ID   0x0fc5
0021 #define PRODUCT_ID  0x1227
0022 #define MAXLEN      8
0023 
0024 /* table of devices that work with this driver */
0025 static const struct usb_device_id id_table[] = {
0026     { USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
0027     { },
0028 };
0029 MODULE_DEVICE_TABLE(usb, id_table);
0030 
0031 /* the different text display modes the device is capable of */
0032 static const char *display_textmodes[] = {"raw", "hex", "ascii"};
0033 
0034 struct usb_sevsegdev {
0035     struct usb_device *udev;
0036     struct usb_interface *intf;
0037 
0038     u8 powered;
0039     u8 mode_msb;
0040     u8 mode_lsb;
0041     u8 decimals[MAXLEN];
0042     u8 textmode;
0043     u8 text[MAXLEN];
0044     u16 textlength;
0045 
0046     u8 shadow_power; /* for PM */
0047     u8 has_interface_pm;
0048 };
0049 
0050 /* sysfs_streq can't replace this completely
0051  * If the device was in hex mode, and the user wanted a 0,
0052  * if str commands are used, we would assume the end of string
0053  * so mem commands are used.
0054  */
0055 static inline size_t my_memlen(const char *buf, size_t count)
0056 {
0057     if (count > 0 && buf[count-1] == '\n')
0058         return count - 1;
0059     else
0060         return count;
0061 }
0062 
0063 static void update_display_powered(struct usb_sevsegdev *mydev)
0064 {
0065     int rc;
0066 
0067     if (mydev->powered && !mydev->has_interface_pm) {
0068         rc = usb_autopm_get_interface(mydev->intf);
0069         if (rc < 0)
0070             return;
0071         mydev->has_interface_pm = 1;
0072     }
0073 
0074     if (mydev->shadow_power != 1)
0075         return;
0076 
0077     rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
0078                   (80 * 0x100) + 10, /*  (power mode) */
0079                   (0x00 * 0x100) + (mydev->powered ? 1 : 0),
0080                   NULL, 0, 2000, GFP_KERNEL);
0081     if (rc < 0)
0082         dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);
0083 
0084     if (!mydev->powered && mydev->has_interface_pm) {
0085         usb_autopm_put_interface(mydev->intf);
0086         mydev->has_interface_pm = 0;
0087     }
0088 }
0089 
0090 static void update_display_mode(struct usb_sevsegdev *mydev)
0091 {
0092     int rc;
0093 
0094     if(mydev->shadow_power != 1)
0095         return;
0096 
0097     rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
0098                   (82 * 0x100) + 10, /* (set mode) */
0099                   (mydev->mode_msb * 0x100) + mydev->mode_lsb,
0100                   NULL, 0, 2000, GFP_NOIO);
0101 
0102     if (rc < 0)
0103         dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
0104 }
0105 
0106 static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
0107 {
0108     int rc;
0109     int i;
0110     unsigned char buffer[MAXLEN] = {0};
0111     u8 decimals = 0;
0112 
0113     if(mydev->shadow_power != 1)
0114         return;
0115 
0116     /* The device is right to left, where as you write left to right */
0117     for (i = 0; i < mydev->textlength; i++)
0118         buffer[i] = mydev->text[mydev->textlength-1-i];
0119 
0120     rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
0121                   (85 * 0x100) + 10, /* (write text) */
0122                   (0 * 0x100) + mydev->textmode, /* mode  */
0123                   &buffer, mydev->textlength, 2000, mf);
0124 
0125     if (rc < 0)
0126         dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
0127 
0128     /* The device is right to left, where as you write left to right */
0129     for (i = 0; i < sizeof(mydev->decimals); i++)
0130         decimals |= mydev->decimals[i] << i;
0131 
0132     rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48,
0133                   (86 * 0x100) + 10, /* (set decimal) */
0134                   (0 * 0x100) + decimals, /* decimals */
0135                   NULL, 0, 2000, mf);
0136 
0137     if (rc < 0)
0138         dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc);
0139 }
0140 
0141 #define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn)        \
0142 static ssize_t name##_show(struct device *dev,          \
0143     struct device_attribute *attr, char *buf)       \
0144 {                               \
0145     struct usb_interface *intf = to_usb_interface(dev); \
0146     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);   \
0147                                 \
0148     return sprintf(buf, "%u\n", mydev->name);       \
0149 }                               \
0150                                 \
0151 static ssize_t name##_store(struct device *dev,         \
0152     struct device_attribute *attr, const char *buf, size_t count) \
0153 {                               \
0154     struct usb_interface *intf = to_usb_interface(dev); \
0155     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);   \
0156                                 \
0157     mydev->name = simple_strtoul(buf, NULL, 10);        \
0158     update_fcn(mydev);                  \
0159                                 \
0160     return count;                       \
0161 }                               \
0162 static DEVICE_ATTR_RW(name);
0163 
0164 static ssize_t text_show(struct device *dev,
0165     struct device_attribute *attr, char *buf)
0166 {
0167     struct usb_interface *intf = to_usb_interface(dev);
0168     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
0169 
0170     return sysfs_emit(buf, "%s\n", mydev->text);
0171 }
0172 
0173 static ssize_t text_store(struct device *dev,
0174     struct device_attribute *attr, const char *buf, size_t count)
0175 {
0176     struct usb_interface *intf = to_usb_interface(dev);
0177     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
0178     size_t end = my_memlen(buf, count);
0179 
0180     if (end > sizeof(mydev->text))
0181         return -EINVAL;
0182 
0183     memset(mydev->text, 0, sizeof(mydev->text));
0184     mydev->textlength = end;
0185 
0186     if (end > 0)
0187         memcpy(mydev->text, buf, end);
0188 
0189     update_display_visual(mydev, GFP_KERNEL);
0190     return count;
0191 }
0192 
0193 static DEVICE_ATTR_RW(text);
0194 
0195 static ssize_t decimals_show(struct device *dev,
0196     struct device_attribute *attr, char *buf)
0197 {
0198     struct usb_interface *intf = to_usb_interface(dev);
0199     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
0200     int i;
0201     int pos;
0202 
0203     for (i = 0; i < sizeof(mydev->decimals); i++) {
0204         pos = sizeof(mydev->decimals) - 1 - i;
0205         if (mydev->decimals[i] == 0)
0206             buf[pos] = '0';
0207         else if (mydev->decimals[i] == 1)
0208             buf[pos] = '1';
0209         else
0210             buf[pos] = 'x';
0211     }
0212 
0213     buf[sizeof(mydev->decimals)] = '\n';
0214     return sizeof(mydev->decimals) + 1;
0215 }
0216 
0217 static ssize_t decimals_store(struct device *dev,
0218     struct device_attribute *attr, const char *buf, size_t count)
0219 {
0220     struct usb_interface *intf = to_usb_interface(dev);
0221     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
0222     size_t end = my_memlen(buf, count);
0223     int i;
0224 
0225     if (end > sizeof(mydev->decimals))
0226         return -EINVAL;
0227 
0228     for (i = 0; i < end; i++)
0229         if (buf[i] != '0' && buf[i] != '1')
0230             return -EINVAL;
0231 
0232     memset(mydev->decimals, 0, sizeof(mydev->decimals));
0233     for (i = 0; i < end; i++)
0234         if (buf[i] == '1')
0235             mydev->decimals[end-1-i] = 1;
0236 
0237     update_display_visual(mydev, GFP_KERNEL);
0238 
0239     return count;
0240 }
0241 
0242 static DEVICE_ATTR_RW(decimals);
0243 
0244 static ssize_t textmode_show(struct device *dev,
0245     struct device_attribute *attr, char *buf)
0246 {
0247     struct usb_interface *intf = to_usb_interface(dev);
0248     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
0249     int i;
0250 
0251     buf[0] = 0;
0252 
0253     for (i = 0; i < ARRAY_SIZE(display_textmodes); i++) {
0254         if (mydev->textmode == i) {
0255             strcat(buf, " [");
0256             strcat(buf, display_textmodes[i]);
0257             strcat(buf, "] ");
0258         } else {
0259             strcat(buf, " ");
0260             strcat(buf, display_textmodes[i]);
0261             strcat(buf, " ");
0262         }
0263     }
0264     strcat(buf, "\n");
0265 
0266 
0267     return strlen(buf);
0268 }
0269 
0270 static ssize_t textmode_store(struct device *dev,
0271     struct device_attribute *attr, const char *buf, size_t count)
0272 {
0273     struct usb_interface *intf = to_usb_interface(dev);
0274     struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
0275     int i;
0276 
0277     i = sysfs_match_string(display_textmodes, buf);
0278     if (i < 0)
0279         return i;
0280 
0281     mydev->textmode = i;
0282     update_display_visual(mydev, GFP_KERNEL);
0283     return count;
0284 }
0285 
0286 static DEVICE_ATTR_RW(textmode);
0287 
0288 
0289 MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
0290 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
0291 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
0292 
0293 static struct attribute *sevseg_attrs[] = {
0294     &dev_attr_powered.attr,
0295     &dev_attr_text.attr,
0296     &dev_attr_textmode.attr,
0297     &dev_attr_decimals.attr,
0298     &dev_attr_mode_msb.attr,
0299     &dev_attr_mode_lsb.attr,
0300     NULL
0301 };
0302 ATTRIBUTE_GROUPS(sevseg);
0303 
0304 static int sevseg_probe(struct usb_interface *interface,
0305     const struct usb_device_id *id)
0306 {
0307     struct usb_device *udev = interface_to_usbdev(interface);
0308     struct usb_sevsegdev *mydev = NULL;
0309     int rc = -ENOMEM;
0310 
0311     mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
0312     if (!mydev)
0313         goto error_mem;
0314 
0315     mydev->udev = usb_get_dev(udev);
0316     mydev->intf = interface;
0317     usb_set_intfdata(interface, mydev);
0318 
0319     /* PM */
0320     mydev->shadow_power = 1; /* currently active */
0321     mydev->has_interface_pm = 0; /* have not issued autopm_get */
0322 
0323     /*set defaults */
0324     mydev->textmode = 0x02; /* ascii mode */
0325     mydev->mode_msb = 0x06; /* 6 characters */
0326     mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
0327 
0328     dev_info(&interface->dev, "USB 7 Segment device now attached\n");
0329     return 0;
0330 
0331 error_mem:
0332     return rc;
0333 }
0334 
0335 static void sevseg_disconnect(struct usb_interface *interface)
0336 {
0337     struct usb_sevsegdev *mydev;
0338 
0339     mydev = usb_get_intfdata(interface);
0340     usb_set_intfdata(interface, NULL);
0341     usb_put_dev(mydev->udev);
0342     kfree(mydev);
0343     dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
0344 }
0345 
0346 static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
0347 {
0348     struct usb_sevsegdev *mydev;
0349 
0350     mydev = usb_get_intfdata(intf);
0351     mydev->shadow_power = 0;
0352 
0353     return 0;
0354 }
0355 
0356 static int sevseg_resume(struct usb_interface *intf)
0357 {
0358     struct usb_sevsegdev *mydev;
0359 
0360     mydev = usb_get_intfdata(intf);
0361     mydev->shadow_power = 1;
0362     update_display_mode(mydev);
0363     update_display_visual(mydev, GFP_NOIO);
0364 
0365     return 0;
0366 }
0367 
0368 static int sevseg_reset_resume(struct usb_interface *intf)
0369 {
0370     struct usb_sevsegdev *mydev;
0371 
0372     mydev = usb_get_intfdata(intf);
0373     mydev->shadow_power = 1;
0374     update_display_mode(mydev);
0375     update_display_visual(mydev, GFP_NOIO);
0376 
0377     return 0;
0378 }
0379 
0380 static struct usb_driver sevseg_driver = {
0381     .name =     "usbsevseg",
0382     .probe =    sevseg_probe,
0383     .disconnect =   sevseg_disconnect,
0384     .suspend =  sevseg_suspend,
0385     .resume =   sevseg_resume,
0386     .reset_resume = sevseg_reset_resume,
0387     .id_table = id_table,
0388     .dev_groups =   sevseg_groups,
0389     .supports_autosuspend = 1,
0390 };
0391 
0392 module_usb_driver(sevseg_driver);
0393 
0394 MODULE_AUTHOR(DRIVER_AUTHOR);
0395 MODULE_DESCRIPTION(DRIVER_DESC);
0396 MODULE_LICENSE("GPL");