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 struct usb_interface *intf; 42 43 u8 powered; 44 u8 mode_msb; 45 u8 mode_lsb; 46 u8 decimals[MAXLEN]; 47 u8 textmode; 48 u8 text[MAXLEN]; 49 u16 textlength; 50 51 u8 shadow_power; /* for PM */ 52 }; 53 54 /* sysfs_streq can't replace this completely 55 * If the device was in hex mode, and the user wanted a 0, 56 * if str commands are used, we would assume the end of string 57 * so mem commands are used. 58 */ 59 inline size_t my_memlen(const char *buf, size_t count) 60 { 61 if (count > 0 && buf[count-1] == '\n') 62 return count - 1; 63 else 64 return count; 65 } 66 67 static void update_display_powered(struct usb_sevsegdev *mydev) 68 { 69 int rc; 70 71 if (!mydev->shadow_power && mydev->powered) { 72 rc = usb_autopm_get_interface(mydev->intf); 73 if (rc < 0) 74 return; 75 } 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->shadow_power && !mydev->powered) 90 usb_autopm_put_interface(mydev->intf); 91 } 92 93 static void update_display_mode(struct usb_sevsegdev *mydev) 94 { 95 int rc; 96 97 if(mydev->shadow_power != 1) 98 return; 99 100 rc = usb_control_msg(mydev->udev, 101 usb_sndctrlpipe(mydev->udev, 0), 102 0x12, 103 0x48, 104 (82 * 0x100) + 10, /* (set mode) */ 105 (mydev->mode_msb * 0x100) + mydev->mode_lsb, 106 NULL, 107 0, 108 2000); 109 110 if (rc < 0) 111 dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); 112 } 113 114 static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) 115 { 116 int rc; 117 int i; 118 unsigned char *buffer; 119 u8 decimals = 0; 120 121 if(mydev->shadow_power != 1) 122 return; 123 124 buffer = kzalloc(MAXLEN, mf); 125 if (!buffer) { 126 dev_err(&mydev->udev->dev, "out of memory\n"); 127 return; 128 } 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) \ 168 static ssize_t show_attr_##name(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 \ 177 static ssize_t set_attr_##name(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 } \ 188 static DEVICE_ATTR(name, S_IWUGO | S_IRUGO, show_attr_##name, set_attr_##name); 189 190 static ssize_t show_attr_text(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 199 static ssize_t set_attr_text(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 219 static DEVICE_ATTR(text, S_IWUGO | S_IRUGO, show_attr_text, set_attr_text); 220 221 static ssize_t show_attr_decimals(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 243 static ssize_t set_attr_decimals(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 268 static DEVICE_ATTR(decimals, S_IWUGO | S_IRUGO, 269 show_attr_decimals, set_attr_decimals); 270 271 static ssize_t show_attr_textmode(struct device *dev, 272 struct device_attribute *attr, char *buf) 273 { 274 struct usb_interface *intf = to_usb_interface(dev); 275 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 276 int i; 277 278 buf[0] = 0; 279 280 for (i = 0; display_textmodes[i]; i++) { 281 if (mydev->textmode == i) { 282 strcat(buf, " ["); 283 strcat(buf, display_textmodes[i]); 284 strcat(buf, "] "); 285 } else { 286 strcat(buf, " "); 287 strcat(buf, display_textmodes[i]); 288 strcat(buf, " "); 289 } 290 } 291 strcat(buf, "\n"); 292 293 294 return strlen(buf); 295 } 296 297 static ssize_t set_attr_textmode(struct device *dev, 298 struct device_attribute *attr, const char *buf, size_t count) 299 { 300 struct usb_interface *intf = to_usb_interface(dev); 301 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 302 int i; 303 304 for (i = 0; display_textmodes[i]; i++) { 305 if (sysfs_streq(display_textmodes[i], buf)) { 306 mydev->textmode = i; 307 update_display_visual(mydev, GFP_KERNEL); 308 return count; 309 } 310 } 311 312 return -EINVAL; 313 } 314 315 static DEVICE_ATTR(textmode, S_IWUGO | S_IRUGO, 316 show_attr_textmode, set_attr_textmode); 317 318 319 MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); 320 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); 321 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); 322 323 static struct attribute *dev_attrs[] = { 324 &dev_attr_powered.attr, 325 &dev_attr_text.attr, 326 &dev_attr_textmode.attr, 327 &dev_attr_decimals.attr, 328 &dev_attr_mode_msb.attr, 329 &dev_attr_mode_lsb.attr, 330 NULL 331 }; 332 333 static struct attribute_group dev_attr_grp = { 334 .attrs = dev_attrs, 335 }; 336 337 static int sevseg_probe(struct usb_interface *interface, 338 const struct usb_device_id *id) 339 { 340 struct usb_device *udev = interface_to_usbdev(interface); 341 struct usb_sevsegdev *mydev = NULL; 342 int rc = -ENOMEM; 343 344 mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); 345 if (mydev == NULL) { 346 dev_err(&interface->dev, "Out of memory\n"); 347 goto error_mem; 348 } 349 350 mydev->udev = usb_get_dev(udev); 351 mydev->intf = interface; 352 usb_set_intfdata(interface, mydev); 353 354 /*set defaults */ 355 mydev->textmode = 0x02; /* ascii mode */ 356 mydev->mode_msb = 0x06; /* 6 characters */ 357 mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ 358 359 rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp); 360 if (rc) 361 goto error; 362 363 dev_info(&interface->dev, "USB 7 Segment device now attached\n"); 364 return 0; 365 366 error: 367 usb_set_intfdata(interface, NULL); 368 usb_put_dev(mydev->udev); 369 kfree(mydev); 370 error_mem: 371 return rc; 372 } 373 374 static void sevseg_disconnect(struct usb_interface *interface) 375 { 376 struct usb_sevsegdev *mydev; 377 378 mydev = usb_get_intfdata(interface); 379 sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp); 380 usb_set_intfdata(interface, NULL); 381 usb_put_dev(mydev->udev); 382 kfree(mydev); 383 dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); 384 } 385 386 static int sevseg_suspend(struct usb_interface *intf, pm_message_t message) 387 { 388 struct usb_sevsegdev *mydev; 389 390 mydev = usb_get_intfdata(intf); 391 mydev->shadow_power = 0; 392 393 return 0; 394 } 395 396 static int sevseg_resume(struct usb_interface *intf) 397 { 398 struct usb_sevsegdev *mydev; 399 400 mydev = usb_get_intfdata(intf); 401 mydev->shadow_power = 1; 402 update_display_mode(mydev); 403 update_display_visual(mydev, GFP_NOIO); 404 405 return 0; 406 } 407 408 static int sevseg_reset_resume(struct usb_interface *intf) 409 { 410 struct usb_sevsegdev *mydev; 411 412 mydev = usb_get_intfdata(intf); 413 mydev->shadow_power = 1; 414 update_display_mode(mydev); 415 update_display_visual(mydev, GFP_NOIO); 416 417 return 0; 418 } 419 420 static struct usb_driver sevseg_driver = { 421 .name = "usbsevseg", 422 .probe = sevseg_probe, 423 .disconnect = sevseg_disconnect, 424 .suspend = sevseg_suspend, 425 .resume = sevseg_resume, 426 .reset_resume = sevseg_reset_resume, 427 .id_table = id_table, 428 .supports_autosuspend = 1, 429 }; 430 431 static int __init usb_sevseg_init(void) 432 { 433 int rc = 0; 434 435 rc = usb_register(&sevseg_driver); 436 if (rc) 437 err("usb_register failed. Error number %d", rc); 438 return rc; 439 } 440 441 static void __exit usb_sevseg_exit(void) 442 { 443 usb_deregister(&sevseg_driver); 444 } 445 446 module_init(usb_sevseg_init); 447 module_exit(usb_sevseg_exit); 448 449 MODULE_AUTHOR(DRIVER_AUTHOR); 450 MODULE_DESCRIPTION(DRIVER_DESC); 451 MODULE_LICENSE("GPL"); 452