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 const char *display_textmodes[] = {"raw", "hex", "ascii"}; 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; i < ARRAY_SIZE(display_textmodes); 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 i = sysfs_match_string(display_textmodes, buf); 308 if (i < 0) 309 return i; 310 311 mydev->textmode = i; 312 update_display_visual(mydev, GFP_KERNEL); 313 return count; 314 } 315 316 static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, 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 const 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) 346 goto error_mem; 347 348 mydev->udev = usb_get_dev(udev); 349 mydev->intf = interface; 350 usb_set_intfdata(interface, mydev); 351 352 /* PM */ 353 mydev->shadow_power = 1; /* currently active */ 354 mydev->has_interface_pm = 0; /* have not issued autopm_get */ 355 356 /*set defaults */ 357 mydev->textmode = 0x02; /* ascii mode */ 358 mydev->mode_msb = 0x06; /* 6 characters */ 359 mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ 360 361 rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp); 362 if (rc) 363 goto error; 364 365 dev_info(&interface->dev, "USB 7 Segment device now attached\n"); 366 return 0; 367 368 error: 369 usb_set_intfdata(interface, NULL); 370 usb_put_dev(mydev->udev); 371 kfree(mydev); 372 error_mem: 373 return rc; 374 } 375 376 static void sevseg_disconnect(struct usb_interface *interface) 377 { 378 struct usb_sevsegdev *mydev; 379 380 mydev = usb_get_intfdata(interface); 381 sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp); 382 usb_set_intfdata(interface, NULL); 383 usb_put_dev(mydev->udev); 384 kfree(mydev); 385 dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); 386 } 387 388 static int sevseg_suspend(struct usb_interface *intf, pm_message_t message) 389 { 390 struct usb_sevsegdev *mydev; 391 392 mydev = usb_get_intfdata(intf); 393 mydev->shadow_power = 0; 394 395 return 0; 396 } 397 398 static int sevseg_resume(struct usb_interface *intf) 399 { 400 struct usb_sevsegdev *mydev; 401 402 mydev = usb_get_intfdata(intf); 403 mydev->shadow_power = 1; 404 update_display_mode(mydev); 405 update_display_visual(mydev, GFP_NOIO); 406 407 return 0; 408 } 409 410 static int sevseg_reset_resume(struct usb_interface *intf) 411 { 412 struct usb_sevsegdev *mydev; 413 414 mydev = usb_get_intfdata(intf); 415 mydev->shadow_power = 1; 416 update_display_mode(mydev); 417 update_display_visual(mydev, GFP_NOIO); 418 419 return 0; 420 } 421 422 static struct usb_driver sevseg_driver = { 423 .name = "usbsevseg", 424 .probe = sevseg_probe, 425 .disconnect = sevseg_disconnect, 426 .suspend = sevseg_suspend, 427 .resume = sevseg_resume, 428 .reset_resume = sevseg_reset_resume, 429 .id_table = id_table, 430 .supports_autosuspend = 1, 431 }; 432 433 module_usb_driver(sevseg_driver); 434 435 MODULE_AUTHOR(DRIVER_AUTHOR); 436 MODULE_DESCRIPTION(DRIVER_DESC); 437 MODULE_LICENSE("GPL"); 438