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"); 395