Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: Added driver for a Delcom USB 7-segment LED Display

Added basic support for a Delcom USB 7-segment LED Display

Signed-off by: Harrison Metzger <harrisonmetz@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Harrison Metzger and committed by
Greg Kroah-Hartman
eb86be54 c3d36c45

+493
+43
Documentation/ABI/testing/sysfs-bus-usb-devices-usbsevseg
··· 1 + Where: /sys/bus/usb/.../powered 2 + Date: August 2008 3 + Kernel Version: 2.6.26 4 + Contact: Harrison Metzger <harrisonmetz@gmail.com> 5 + Description: Controls whether the device's display will powered. 6 + A value of 0 is off and a non-zero value is on. 7 + 8 + Where: /sys/bus/usb/.../mode_msb 9 + Where: /sys/bus/usb/.../mode_lsb 10 + Date: August 2008 11 + Kernel Version: 2.6.26 12 + Contact: Harrison Metzger <harrisonmetz@gmail.com> 13 + Description: Controls the devices display mode. 14 + For a 6 character display the values are 15 + MSB 0x06; LSB 0x3F, and 16 + for an 8 character display the values are 17 + MSB 0x08; LSB 0xFF. 18 + 19 + Where: /sys/bus/usb/.../textmode 20 + Date: August 2008 21 + Kernel Version: 2.6.26 22 + Contact: Harrison Metzger <harrisonmetz@gmail.com> 23 + Description: Controls the way the device interprets its text buffer. 24 + raw: each character controls its segment manually 25 + hex: each character is between 0-15 26 + ascii: each character is between '0'-'9' and 'A'-'F'. 27 + 28 + Where: /sys/bus/usb/.../text 29 + Date: August 2008 30 + Kernel Version: 2.6.26 31 + Contact: Harrison Metzger <harrisonmetz@gmail.com> 32 + Description: The text (or data) for the device to display 33 + 34 + Where: /sys/bus/usb/.../decimals 35 + Date: August 2008 36 + Kernel Version: 2.6.26 37 + Contact: Harrison Metzger <harrisonmetz@gmail.com> 38 + Description: Controls the decimal places on the device. 39 + To set the nth decimal place, give this field 40 + the value of 10 ** n. Assume this field has 41 + the value k and has 1 or more decimal places set, 42 + to set the mth place (where m is not already set), 43 + change this fields value to k + 10 ** m.
+46
Documentation/usb/misc_usbsevseg.txt
··· 1 + USB 7-Segment Numeric Display 2 + Manufactured by Delcom Engineering 3 + 4 + Device Information 5 + ------------------ 6 + USB VENDOR_ID 0x0fc5 7 + USB PRODUCT_ID 0x1227 8 + Both the 6 character and 8 character displays have PRODUCT_ID, 9 + and according to Delcom Engineering no queryable information 10 + can be obtained from the device to tell them apart. 11 + 12 + Device Modes 13 + ------------ 14 + By default, the driver assumes the display is only 6 characters 15 + The mode for 6 characters is: 16 + MSB 0x06; LSB 0x3f 17 + For the 8 character display: 18 + MSB 0x08; LSB 0xff 19 + The device can accept "text" either in raw, hex, or ascii textmode. 20 + raw controls each segment manually, 21 + hex expects a value between 0-15 per character, 22 + ascii expects a value between '0'-'9' and 'A'-'F'. 23 + The default is ascii. 24 + 25 + Device Operation 26 + ---------------- 27 + 1. Turn on the device: 28 + echo 1 > /sys/bus/usb/.../powered 29 + 2. Set the device's mode: 30 + echo $mode_msb > /sys/bus/usb/.../mode_msb 31 + echo $mode_lsb > /sys/bus/usb/.../mode_lsb 32 + 3. Set the textmode: 33 + echo $textmode > /sys/bus/usb/.../textmode 34 + 4. set the text (for example): 35 + echo "123ABC" > /sys/bus/usb/.../text (ascii) 36 + echo "A1B2" > /sys/bus/usb/.../text (ascii) 37 + echo -ne "\x01\x02\x03" > /sys/bus/usb/.../text (hex) 38 + 5. Set the decimal places. 39 + The device has either 6 or 8 decimal points. 40 + to set the nth decimal place calculate 10 ** n 41 + and echo it in to /sys/bus/usb/.../decimals 42 + To set multiple decimals points sum up each power. 43 + For example, to set the 0th and 3rd decimal place 44 + echo 1001 > /sys/bus/usb/.../decimals 45 + 46 +
+9
drivers/usb/misc/Kconfig
··· 42 42 To compile this driver as a module, choose M here. The module 43 43 will be called adutux. 44 44 45 + config USB_SEVSEG 46 + tristate "USB 7-Segment LED Display" 47 + depends on USB 48 + help 49 + Say Y here if you have a USB 7-Segment Display by Delcom 50 + 51 + To compile this driver as a module, choose M here: the 52 + module will be called usbsevseg. 53 + 45 54 config USB_RIO500 46 55 tristate "USB Diamond Rio500 support" 47 56 depends on USB
+1
drivers/usb/misc/Makefile
··· 26 26 obj-$(CONFIG_USB_TEST) += usbtest.o 27 27 obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o 28 28 obj-$(CONFIG_USB_USS720) += uss720.o 29 + obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o 29 30 30 31 obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ 31 32
+394
drivers/usb/misc/usbsevseg.c
··· 1 + /* 2 + * USB 7 Segment Driver 3 + * 4 + * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com> 5 + * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com) 6 + * 7 + * This program is free software; you can redistribute it and/or 8 + * modify it under the terms of the GNU General Public License as 9 + * published by the Free Software Foundation, version 2. 10 + * 11 + */ 12 + 13 + #include <linux/kernel.h> 14 + #include <linux/errno.h> 15 + #include <linux/init.h> 16 + #include <linux/slab.h> 17 + #include <linux/module.h> 18 + #include <linux/string.h> 19 + #include <linux/usb.h> 20 + 21 + 22 + #define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>" 23 + #define DRIVER_DESC "USB 7 Segment Driver" 24 + 25 + #define VENDOR_ID 0x0fc5 26 + #define PRODUCT_ID 0x1227 27 + #define MAXLEN 6 28 + 29 + /* table of devices that work with this driver */ 30 + static struct usb_device_id id_table[] = { 31 + { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, 32 + { }, 33 + }; 34 + MODULE_DEVICE_TABLE(usb, id_table); 35 + 36 + /* the different text display modes the device is capable of */ 37 + static char *display_textmodes[] = {"raw", "hex", "ascii", NULL}; 38 + 39 + struct usb_sevsegdev { 40 + struct usb_device *udev; 41 + 42 + u8 powered; 43 + u8 mode_msb; 44 + u8 mode_lsb; 45 + u8 decimals[MAXLEN]; 46 + u8 textmode; 47 + u8 text[MAXLEN]; 48 + u16 textlength; 49 + }; 50 + 51 + /* sysfs_streq can't replace this completely 52 + * If the device was in hex mode, and the user wanted a 0, 53 + * if str commands are used, we would assume the end of string 54 + * so mem commands are used. 55 + */ 56 + inline size_t my_memlen(const char *buf, size_t count) 57 + { 58 + if (count > 0 && buf[count-1] == '\n') 59 + return count - 1; 60 + else 61 + return count; 62 + } 63 + 64 + static void update_display_powered(struct usb_sevsegdev *mydev) 65 + { 66 + int rc; 67 + 68 + rc = usb_control_msg(mydev->udev, 69 + usb_sndctrlpipe(mydev->udev, 0), 70 + 0x12, 71 + 0x48, 72 + (80 * 0x100) + 10, /* (power mode) */ 73 + (0x00 * 0x100) + (mydev->powered ? 1 : 0), 74 + NULL, 75 + 0, 76 + 2000); 77 + if (rc < 0) 78 + dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); 79 + } 80 + 81 + static void update_display_mode(struct usb_sevsegdev *mydev) 82 + { 83 + int rc; 84 + 85 + rc = usb_control_msg(mydev->udev, 86 + usb_sndctrlpipe(mydev->udev, 0), 87 + 0x12, 88 + 0x48, 89 + (82 * 0x100) + 10, /* (set mode) */ 90 + (mydev->mode_msb * 0x100) + mydev->mode_lsb, 91 + NULL, 92 + 0, 93 + 2000); 94 + 95 + if (rc < 0) 96 + dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); 97 + } 98 + 99 + static void update_display_visual(struct usb_sevsegdev *mydev) 100 + { 101 + int rc; 102 + int i; 103 + unsigned char *buffer; 104 + u8 decimals = 0; 105 + 106 + buffer = kzalloc(MAXLEN, GFP_KERNEL); 107 + if (!buffer) { 108 + dev_err(&mydev->udev->dev, "out of memory\n"); 109 + return; 110 + } 111 + 112 + /* The device is right to left, where as you write left to right */ 113 + for (i = 0; i < mydev->textlength; i++) 114 + buffer[i] = mydev->text[mydev->textlength-1-i]; 115 + 116 + rc = usb_control_msg(mydev->udev, 117 + usb_sndctrlpipe(mydev->udev, 0), 118 + 0x12, 119 + 0x48, 120 + (85 * 0x100) + 10, /* (write text) */ 121 + (0 * 0x100) + mydev->textmode, /* mode */ 122 + buffer, 123 + mydev->textlength, 124 + 2000); 125 + 126 + if (rc < 0) 127 + dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc); 128 + 129 + kfree(buffer); 130 + 131 + /* The device is right to left, where as you write left to right */ 132 + for (i = 0; i < sizeof(mydev->decimals); i++) 133 + decimals |= mydev->decimals[i] << i; 134 + 135 + rc = usb_control_msg(mydev->udev, 136 + usb_sndctrlpipe(mydev->udev, 0), 137 + 0x12, 138 + 0x48, 139 + (86 * 0x100) + 10, /* (set decimal) */ 140 + (0 * 0x100) + decimals, /* decimals */ 141 + NULL, 142 + 0, 143 + 2000); 144 + 145 + if (rc < 0) 146 + dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc); 147 + } 148 + 149 + #define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn) \ 150 + static ssize_t show_attr_##name(struct device *dev, \ 151 + struct device_attribute *attr, char *buf) \ 152 + { \ 153 + struct usb_interface *intf = to_usb_interface(dev); \ 154 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ 155 + \ 156 + return sprintf(buf, "%u\n", mydev->name); \ 157 + } \ 158 + \ 159 + static ssize_t set_attr_##name(struct device *dev, \ 160 + struct device_attribute *attr, const char *buf, size_t count) \ 161 + { \ 162 + struct usb_interface *intf = to_usb_interface(dev); \ 163 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ 164 + \ 165 + mydev->name = simple_strtoul(buf, NULL, 10); \ 166 + update_fcn(mydev); \ 167 + \ 168 + return count; \ 169 + } \ 170 + static DEVICE_ATTR(name, S_IWUGO | S_IRUGO, show_attr_##name, set_attr_##name); 171 + 172 + static ssize_t show_attr_text(struct device *dev, 173 + struct device_attribute *attr, char *buf) 174 + { 175 + struct usb_interface *intf = to_usb_interface(dev); 176 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 177 + 178 + return snprintf(buf, mydev->textlength, "%s\n", mydev->text); 179 + } 180 + 181 + static ssize_t set_attr_text(struct device *dev, 182 + struct device_attribute *attr, const char *buf, size_t count) 183 + { 184 + struct usb_interface *intf = to_usb_interface(dev); 185 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 186 + size_t end = my_memlen(buf, count); 187 + 188 + if (end > sizeof(mydev->text)) 189 + return -EINVAL; 190 + 191 + memset(mydev->text, 0, sizeof(mydev->text)); 192 + mydev->textlength = end; 193 + 194 + if (end > 0) 195 + memcpy(mydev->text, buf, end); 196 + 197 + update_display_visual(mydev); 198 + return count; 199 + } 200 + 201 + static DEVICE_ATTR(text, S_IWUGO | S_IRUGO, show_attr_text, set_attr_text); 202 + 203 + static ssize_t show_attr_decimals(struct device *dev, 204 + struct device_attribute *attr, char *buf) 205 + { 206 + struct usb_interface *intf = to_usb_interface(dev); 207 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 208 + int i; 209 + int pos; 210 + 211 + for (i = 0; i < sizeof(mydev->decimals); i++) { 212 + pos = sizeof(mydev->decimals) - 1 - i; 213 + if (mydev->decimals[i] == 0) 214 + buf[pos] = '0'; 215 + else if (mydev->decimals[i] == 1) 216 + buf[pos] = '1'; 217 + else 218 + buf[pos] = 'x'; 219 + } 220 + 221 + buf[sizeof(mydev->decimals)] = '\n'; 222 + return sizeof(mydev->decimals) + 1; 223 + } 224 + 225 + static ssize_t set_attr_decimals(struct device *dev, 226 + struct device_attribute *attr, const char *buf, size_t count) 227 + { 228 + struct usb_interface *intf = to_usb_interface(dev); 229 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 230 + size_t end = my_memlen(buf, count); 231 + int i; 232 + 233 + if (end > sizeof(mydev->decimals)) 234 + return -EINVAL; 235 + 236 + for (i = 0; i < end; i++) 237 + if (buf[i] != '0' && buf[i] != '1') 238 + return -EINVAL; 239 + 240 + memset(mydev->decimals, 0, sizeof(mydev->decimals)); 241 + for (i = 0; i < end; i++) 242 + if (buf[i] == '1') 243 + mydev->decimals[end-1-i] = 1; 244 + 245 + update_display_visual(mydev); 246 + 247 + return count; 248 + } 249 + 250 + static DEVICE_ATTR(decimals, S_IWUGO | S_IRUGO, 251 + show_attr_decimals, set_attr_decimals); 252 + 253 + static ssize_t show_attr_textmode(struct device *dev, 254 + struct device_attribute *attr, char *buf) 255 + { 256 + struct usb_interface *intf = to_usb_interface(dev); 257 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 258 + int i; 259 + 260 + buf[0] = 0; 261 + 262 + for (i = 0; display_textmodes[i]; i++) { 263 + if (mydev->textmode == i) { 264 + strcat(buf, " ["); 265 + strcat(buf, display_textmodes[i]); 266 + strcat(buf, "] "); 267 + } else { 268 + strcat(buf, " "); 269 + strcat(buf, display_textmodes[i]); 270 + strcat(buf, " "); 271 + } 272 + } 273 + strcat(buf, "\n"); 274 + 275 + 276 + return strlen(buf); 277 + } 278 + 279 + static ssize_t set_attr_textmode(struct device *dev, 280 + struct device_attribute *attr, const char *buf, size_t count) 281 + { 282 + struct usb_interface *intf = to_usb_interface(dev); 283 + struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 284 + int i; 285 + 286 + for (i = 0; display_textmodes[i]; i++) { 287 + if (sysfs_streq(display_textmodes[i], buf)) { 288 + mydev->textmode = i; 289 + update_display_visual(mydev); 290 + return count; 291 + } 292 + } 293 + 294 + return -EINVAL; 295 + } 296 + 297 + static DEVICE_ATTR(textmode, S_IWUGO | S_IRUGO, 298 + show_attr_textmode, set_attr_textmode); 299 + 300 + 301 + MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); 302 + MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); 303 + MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); 304 + 305 + static struct attribute *dev_attrs[] = { 306 + &dev_attr_powered.attr, 307 + &dev_attr_text.attr, 308 + &dev_attr_textmode.attr, 309 + &dev_attr_decimals.attr, 310 + &dev_attr_mode_msb.attr, 311 + &dev_attr_mode_lsb.attr, 312 + NULL 313 + }; 314 + 315 + static struct attribute_group dev_attr_grp = { 316 + .attrs = dev_attrs, 317 + }; 318 + 319 + static int sevseg_probe(struct usb_interface *interface, 320 + const struct usb_device_id *id) 321 + { 322 + struct usb_device *udev = interface_to_usbdev(interface); 323 + struct usb_sevsegdev *mydev = NULL; 324 + int rc = -ENOMEM; 325 + 326 + mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); 327 + if (mydev == NULL) { 328 + dev_err(&interface->dev, "Out of memory\n"); 329 + goto error_mem; 330 + } 331 + 332 + mydev->udev = usb_get_dev(udev); 333 + usb_set_intfdata(interface, mydev); 334 + 335 + /*set defaults */ 336 + mydev->textmode = 0x02; /* ascii mode */ 337 + mydev->mode_msb = 0x06; /* 6 characters */ 338 + mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ 339 + 340 + rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp); 341 + if (rc) 342 + goto error; 343 + 344 + dev_info(&interface->dev, "USB 7 Segment device now attached\n"); 345 + return 0; 346 + 347 + error: 348 + usb_set_intfdata(interface, NULL); 349 + usb_put_dev(mydev->udev); 350 + kfree(mydev); 351 + error_mem: 352 + return rc; 353 + } 354 + 355 + static void sevseg_disconnect(struct usb_interface *interface) 356 + { 357 + struct usb_sevsegdev *mydev; 358 + 359 + mydev = usb_get_intfdata(interface); 360 + sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp); 361 + usb_set_intfdata(interface, NULL); 362 + usb_put_dev(mydev->udev); 363 + kfree(mydev); 364 + dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); 365 + } 366 + 367 + static struct usb_driver sevseg_driver = { 368 + .name = "usbsevseg", 369 + .probe = sevseg_probe, 370 + .disconnect = sevseg_disconnect, 371 + .id_table = id_table, 372 + }; 373 + 374 + static int __init usb_sevseg_init(void) 375 + { 376 + int rc = 0; 377 + 378 + rc = usb_register(&sevseg_driver); 379 + if (rc) 380 + err("usb_register failed. Error number %d", rc); 381 + return rc; 382 + } 383 + 384 + static void __exit usb_sevseg_exit(void) 385 + { 386 + usb_deregister(&sevseg_driver); 387 + } 388 + 389 + module_init(usb_sevseg_init); 390 + module_exit(usb_sevseg_exit); 391 + 392 + MODULE_AUTHOR(DRIVER_AUTHOR); 393 + MODULE_DESCRIPTION(DRIVER_DESC); 394 + MODULE_LICENSE("GPL");