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/slab.h> 16#include <linux/module.h> 17#include <linux/string.h> 18#include <linux/usb.h> 19 20 21#define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>" 22#define DRIVER_DESC "USB 7 Segment Driver" 23 24#define VENDOR_ID 0x0fc5 25#define PRODUCT_ID 0x1227 26#define MAXLEN 8 27 28/* table of devices that work with this driver */ 29static const struct usb_device_id id_table[] = { 30 { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, 31 { }, 32}; 33MODULE_DEVICE_TABLE(usb, id_table); 34 35/* the different text display modes the device is capable of */ 36static char *display_textmodes[] = {"raw", "hex", "ascii", NULL}; 37 38struct usb_sevsegdev { 39 struct usb_device *udev; 40 struct usb_interface *intf; 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 u8 shadow_power; /* for PM */ 51 u8 has_interface_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 */ 59static 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 67static void update_display_powered(struct usb_sevsegdev *mydev) 68{ 69 int rc; 70 71 if (mydev->powered && !mydev->has_interface_pm) { 72 rc = usb_autopm_get_interface(mydev->intf); 73 if (rc < 0) 74 return; 75 mydev->has_interface_pm = 1; 76 } 77 78 if (mydev->shadow_power != 1) 79 return; 80 81 rc = usb_control_msg(mydev->udev, 82 usb_sndctrlpipe(mydev->udev, 0), 83 0x12, 84 0x48, 85 (80 * 0x100) + 10, /* (power mode) */ 86 (0x00 * 0x100) + (mydev->powered ? 1 : 0), 87 NULL, 88 0, 89 2000); 90 if (rc < 0) 91 dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); 92 93 if (!mydev->powered && mydev->has_interface_pm) { 94 usb_autopm_put_interface(mydev->intf); 95 mydev->has_interface_pm = 0; 96 } 97} 98 99static void update_display_mode(struct usb_sevsegdev *mydev) 100{ 101 int rc; 102 103 if(mydev->shadow_power != 1) 104 return; 105 106 rc = usb_control_msg(mydev->udev, 107 usb_sndctrlpipe(mydev->udev, 0), 108 0x12, 109 0x48, 110 (82 * 0x100) + 10, /* (set mode) */ 111 (mydev->mode_msb * 0x100) + mydev->mode_lsb, 112 NULL, 113 0, 114 2000); 115 116 if (rc < 0) 117 dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); 118} 119 120static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) 121{ 122 int rc; 123 int i; 124 unsigned char *buffer; 125 u8 decimals = 0; 126 127 if(mydev->shadow_power != 1) 128 return; 129 130 buffer = kzalloc(MAXLEN, mf); 131 if (!buffer) { 132 dev_err(&mydev->udev->dev, "out of memory\n"); 133 return; 134 } 135 136 /* The device is right to left, where as you write left to right */ 137 for (i = 0; i < mydev->textlength; i++) 138 buffer[i] = mydev->text[mydev->textlength-1-i]; 139 140 rc = usb_control_msg(mydev->udev, 141 usb_sndctrlpipe(mydev->udev, 0), 142 0x12, 143 0x48, 144 (85 * 0x100) + 10, /* (write text) */ 145 (0 * 0x100) + mydev->textmode, /* mode */ 146 buffer, 147 mydev->textlength, 148 2000); 149 150 if (rc < 0) 151 dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc); 152 153 kfree(buffer); 154 155 /* The device is right to left, where as you write left to right */ 156 for (i = 0; i < sizeof(mydev->decimals); i++) 157 decimals |= mydev->decimals[i] << i; 158 159 rc = usb_control_msg(mydev->udev, 160 usb_sndctrlpipe(mydev->udev, 0), 161 0x12, 162 0x48, 163 (86 * 0x100) + 10, /* (set decimal) */ 164 (0 * 0x100) + decimals, /* decimals */ 165 NULL, 166 0, 167 2000); 168 169 if (rc < 0) 170 dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc); 171} 172 173#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn) \ 174static ssize_t show_attr_##name(struct device *dev, \ 175 struct device_attribute *attr, char *buf) \ 176{ \ 177 struct usb_interface *intf = to_usb_interface(dev); \ 178 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ 179 \ 180 return sprintf(buf, "%u\n", mydev->name); \ 181} \ 182 \ 183static ssize_t set_attr_##name(struct device *dev, \ 184 struct device_attribute *attr, const char *buf, size_t count) \ 185{ \ 186 struct usb_interface *intf = to_usb_interface(dev); \ 187 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ 188 \ 189 mydev->name = simple_strtoul(buf, NULL, 10); \ 190 update_fcn(mydev); \ 191 \ 192 return count; \ 193} \ 194static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_attr_##name, set_attr_##name); 195 196static ssize_t show_attr_text(struct device *dev, 197 struct device_attribute *attr, char *buf) 198{ 199 struct usb_interface *intf = to_usb_interface(dev); 200 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 201 202 return snprintf(buf, mydev->textlength, "%s\n", mydev->text); 203} 204 205static ssize_t set_attr_text(struct device *dev, 206 struct device_attribute *attr, const char *buf, size_t count) 207{ 208 struct usb_interface *intf = to_usb_interface(dev); 209 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 210 size_t end = my_memlen(buf, count); 211 212 if (end > sizeof(mydev->text)) 213 return -EINVAL; 214 215 memset(mydev->text, 0, sizeof(mydev->text)); 216 mydev->textlength = end; 217 218 if (end > 0) 219 memcpy(mydev->text, buf, end); 220 221 update_display_visual(mydev, GFP_KERNEL); 222 return count; 223} 224 225static DEVICE_ATTR(text, S_IRUGO | S_IWUSR, show_attr_text, set_attr_text); 226 227static ssize_t show_attr_decimals(struct device *dev, 228 struct device_attribute *attr, char *buf) 229{ 230 struct usb_interface *intf = to_usb_interface(dev); 231 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 232 int i; 233 int pos; 234 235 for (i = 0; i < sizeof(mydev->decimals); i++) { 236 pos = sizeof(mydev->decimals) - 1 - i; 237 if (mydev->decimals[i] == 0) 238 buf[pos] = '0'; 239 else if (mydev->decimals[i] == 1) 240 buf[pos] = '1'; 241 else 242 buf[pos] = 'x'; 243 } 244 245 buf[sizeof(mydev->decimals)] = '\n'; 246 return sizeof(mydev->decimals) + 1; 247} 248 249static ssize_t set_attr_decimals(struct device *dev, 250 struct device_attribute *attr, const char *buf, size_t count) 251{ 252 struct usb_interface *intf = to_usb_interface(dev); 253 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 254 size_t end = my_memlen(buf, count); 255 int i; 256 257 if (end > sizeof(mydev->decimals)) 258 return -EINVAL; 259 260 for (i = 0; i < end; i++) 261 if (buf[i] != '0' && buf[i] != '1') 262 return -EINVAL; 263 264 memset(mydev->decimals, 0, sizeof(mydev->decimals)); 265 for (i = 0; i < end; i++) 266 if (buf[i] == '1') 267 mydev->decimals[end-1-i] = 1; 268 269 update_display_visual(mydev, GFP_KERNEL); 270 271 return count; 272} 273 274static DEVICE_ATTR(decimals, S_IRUGO | S_IWUSR, show_attr_decimals, set_attr_decimals); 275 276static ssize_t show_attr_textmode(struct device *dev, 277 struct device_attribute *attr, char *buf) 278{ 279 struct usb_interface *intf = to_usb_interface(dev); 280 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 281 int i; 282 283 buf[0] = 0; 284 285 for (i = 0; display_textmodes[i]; i++) { 286 if (mydev->textmode == i) { 287 strcat(buf, " ["); 288 strcat(buf, display_textmodes[i]); 289 strcat(buf, "] "); 290 } else { 291 strcat(buf, " "); 292 strcat(buf, display_textmodes[i]); 293 strcat(buf, " "); 294 } 295 } 296 strcat(buf, "\n"); 297 298 299 return strlen(buf); 300} 301 302static ssize_t set_attr_textmode(struct device *dev, 303 struct device_attribute *attr, const char *buf, size_t count) 304{ 305 struct usb_interface *intf = to_usb_interface(dev); 306 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 307 int i; 308 309 for (i = 0; display_textmodes[i]; i++) { 310 if (sysfs_streq(display_textmodes[i], buf)) { 311 mydev->textmode = i; 312 update_display_visual(mydev, GFP_KERNEL); 313 return count; 314 } 315 } 316 317 return -EINVAL; 318} 319 320static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, show_attr_textmode, set_attr_textmode); 321 322 323MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); 324MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); 325MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); 326 327static struct attribute *dev_attrs[] = { 328 &dev_attr_powered.attr, 329 &dev_attr_text.attr, 330 &dev_attr_textmode.attr, 331 &dev_attr_decimals.attr, 332 &dev_attr_mode_msb.attr, 333 &dev_attr_mode_lsb.attr, 334 NULL 335}; 336 337static struct attribute_group dev_attr_grp = { 338 .attrs = dev_attrs, 339}; 340 341static int sevseg_probe(struct usb_interface *interface, 342 const struct usb_device_id *id) 343{ 344 struct usb_device *udev = interface_to_usbdev(interface); 345 struct usb_sevsegdev *mydev = NULL; 346 int rc = -ENOMEM; 347 348 mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); 349 if (mydev == NULL) { 350 dev_err(&interface->dev, "Out of memory\n"); 351 goto error_mem; 352 } 353 354 mydev->udev = usb_get_dev(udev); 355 mydev->intf = interface; 356 usb_set_intfdata(interface, mydev); 357 358 /* PM */ 359 mydev->shadow_power = 1; /* currently active */ 360 mydev->has_interface_pm = 0; /* have not issued autopm_get */ 361 362 /*set defaults */ 363 mydev->textmode = 0x02; /* ascii mode */ 364 mydev->mode_msb = 0x06; /* 6 characters */ 365 mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ 366 367 rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp); 368 if (rc) 369 goto error; 370 371 dev_info(&interface->dev, "USB 7 Segment device now attached\n"); 372 return 0; 373 374error: 375 usb_set_intfdata(interface, NULL); 376 usb_put_dev(mydev->udev); 377 kfree(mydev); 378error_mem: 379 return rc; 380} 381 382static void sevseg_disconnect(struct usb_interface *interface) 383{ 384 struct usb_sevsegdev *mydev; 385 386 mydev = usb_get_intfdata(interface); 387 sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp); 388 usb_set_intfdata(interface, NULL); 389 usb_put_dev(mydev->udev); 390 kfree(mydev); 391 dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); 392} 393 394static int sevseg_suspend(struct usb_interface *intf, pm_message_t message) 395{ 396 struct usb_sevsegdev *mydev; 397 398 mydev = usb_get_intfdata(intf); 399 mydev->shadow_power = 0; 400 401 return 0; 402} 403 404static int sevseg_resume(struct usb_interface *intf) 405{ 406 struct usb_sevsegdev *mydev; 407 408 mydev = usb_get_intfdata(intf); 409 mydev->shadow_power = 1; 410 update_display_mode(mydev); 411 update_display_visual(mydev, GFP_NOIO); 412 413 return 0; 414} 415 416static int sevseg_reset_resume(struct usb_interface *intf) 417{ 418 struct usb_sevsegdev *mydev; 419 420 mydev = usb_get_intfdata(intf); 421 mydev->shadow_power = 1; 422 update_display_mode(mydev); 423 update_display_visual(mydev, GFP_NOIO); 424 425 return 0; 426} 427 428static struct usb_driver sevseg_driver = { 429 .name = "usbsevseg", 430 .probe = sevseg_probe, 431 .disconnect = sevseg_disconnect, 432 .suspend = sevseg_suspend, 433 .resume = sevseg_resume, 434 .reset_resume = sevseg_reset_resume, 435 .id_table = id_table, 436 .supports_autosuspend = 1, 437}; 438 439module_usb_driver(sevseg_driver); 440 441MODULE_AUTHOR(DRIVER_AUTHOR); 442MODULE_DESCRIPTION(DRIVER_DESC); 443MODULE_LICENSE("GPL"); 444