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