0001
0002
0003
0004
0005
0006
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
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
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;
0047 u8 has_interface_pm;
0048 };
0049
0050
0051
0052
0053
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,
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,
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
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,
0122 (0 * 0x100) + mydev->textmode,
0123 &buffer, mydev->textlength, 2000, mf);
0124
0125 if (rc < 0)
0126 dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
0127
0128
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,
0134 (0 * 0x100) + 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
0320 mydev->shadow_power = 1;
0321 mydev->has_interface_pm = 0;
0322
0323
0324 mydev->textmode = 0x02;
0325 mydev->mode_msb = 0x06;
0326 mydev->mode_lsb = 0x3f;
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");