1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * USB 7 Segment Driver 4 * 5 * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com> 6 * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com) 7 */ 8 9 #include <linux/kernel.h> 10 #include <linux/errno.h> 11 #include <linux/slab.h> 12 #include <linux/module.h> 13 #include <linux/string.h> 14 #include <linux/usb.h> 15 16 17 #define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>" 18 #define DRIVER_DESC "USB 7 Segment Driver" 19 20 #define VENDOR_ID 0x0fc5 21 #define PRODUCT_ID 0x1227 22 #define MAXLEN 8 23 24 /* table of devices that work with this driver */ 25 static const struct usb_device_id id_table[] = { 26 { USB_DEVICE(VENDOR_ID, PRODUCT_ID) }, 27 { }, 28 }; 29 MODULE_DEVICE_TABLE(usb, id_table); 30 31 /* the different text display modes the device is capable of */ 32 static const char *display_textmodes[] = {"raw", "hex", "ascii"}; 33 34 struct usb_sevsegdev { 35 struct usb_device *udev; 36 struct usb_interface *intf; 37 38 u8 powered; 39 u8 mode_msb; 40 u8 mode_lsb; 41 u8 decimals[MAXLEN]; 42 u8 textmode; 43 u8 text[MAXLEN]; 44 u16 textlength; 45 46 u8 shadow_power; /* for PM */ 47 u8 has_interface_pm; 48 }; 49 50 /* sysfs_streq can't replace this completely 51 * If the device was in hex mode, and the user wanted a 0, 52 * if str commands are used, we would assume the end of string 53 * so mem commands are used. 54 */ 55 static inline size_t my_memlen(const char *buf, size_t count) 56 { 57 if (count > 0 && buf[count-1] == '\n') 58 return count - 1; 59 else 60 return count; 61 } 62 63 static void update_display_powered(struct usb_sevsegdev *mydev) 64 { 65 int rc; 66 67 if (mydev->powered && !mydev->has_interface_pm) { 68 rc = usb_autopm_get_interface(mydev->intf); 69 if (rc < 0) 70 return; 71 mydev->has_interface_pm = 1; 72 } 73 74 if (mydev->shadow_power != 1) 75 return; 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->powered && mydev->has_interface_pm) { 90 usb_autopm_put_interface(mydev->intf); 91 mydev->has_interface_pm = 0; 92 } 93 } 94 95 static void update_display_mode(struct usb_sevsegdev *mydev) 96 { 97 int rc; 98 99 if(mydev->shadow_power != 1) 100 return; 101 102 rc = usb_control_msg(mydev->udev, 103 usb_sndctrlpipe(mydev->udev, 0), 104 0x12, 105 0x48, 106 (82 * 0x100) + 10, /* (set mode) */ 107 (mydev->mode_msb * 0x100) + mydev->mode_lsb, 108 NULL, 109 0, 110 2000); 111 112 if (rc < 0) 113 dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); 114 } 115 116 static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) 117 { 118 int rc; 119 int i; 120 unsigned char *buffer; 121 u8 decimals = 0; 122 123 if(mydev->shadow_power != 1) 124 return; 125 126 buffer = kzalloc(MAXLEN, mf); 127 if (!buffer) 128 return; 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 name##_show(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 name##_store(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_RW(name); 189 190 static ssize_t text_show(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 text_store(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_RW(text); 220 221 static ssize_t decimals_show(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 decimals_store(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_RW(decimals); 269 270 static ssize_t textmode_show(struct device *dev, 271 struct device_attribute *attr, char *buf) 272 { 273 struct usb_interface *intf = to_usb_interface(dev); 274 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 275 int i; 276 277 buf[0] = 0; 278 279 for (i = 0; i < ARRAY_SIZE(display_textmodes); i++) { 280 if (mydev->textmode == i) { 281 strcat(buf, " ["); 282 strcat(buf, display_textmodes[i]); 283 strcat(buf, "] "); 284 } else { 285 strcat(buf, " "); 286 strcat(buf, display_textmodes[i]); 287 strcat(buf, " "); 288 } 289 } 290 strcat(buf, "\n"); 291 292 293 return strlen(buf); 294 } 295 296 static ssize_t textmode_store(struct device *dev, 297 struct device_attribute *attr, const char *buf, size_t count) 298 { 299 struct usb_interface *intf = to_usb_interface(dev); 300 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 301 int i; 302 303 i = sysfs_match_string(display_textmodes, buf); 304 if (i < 0) 305 return i; 306 307 mydev->textmode = i; 308 update_display_visual(mydev, GFP_KERNEL); 309 return count; 310 } 311 312 static DEVICE_ATTR_RW(textmode); 313 314 315 MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); 316 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); 317 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); 318 319 static struct attribute *sevseg_attrs[] = { 320 &dev_attr_powered.attr, 321 &dev_attr_text.attr, 322 &dev_attr_textmode.attr, 323 &dev_attr_decimals.attr, 324 &dev_attr_mode_msb.attr, 325 &dev_attr_mode_lsb.attr, 326 NULL 327 }; 328 ATTRIBUTE_GROUPS(sevseg); 329 330 static int sevseg_probe(struct usb_interface *interface, 331 const struct usb_device_id *id) 332 { 333 struct usb_device *udev = interface_to_usbdev(interface); 334 struct usb_sevsegdev *mydev = NULL; 335 int rc = -ENOMEM; 336 337 mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); 338 if (!mydev) 339 goto error_mem; 340 341 mydev->udev = usb_get_dev(udev); 342 mydev->intf = interface; 343 usb_set_intfdata(interface, mydev); 344 345 /* PM */ 346 mydev->shadow_power = 1; /* currently active */ 347 mydev->has_interface_pm = 0; /* have not issued autopm_get */ 348 349 /*set defaults */ 350 mydev->textmode = 0x02; /* ascii mode */ 351 mydev->mode_msb = 0x06; /* 6 characters */ 352 mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ 353 354 dev_info(&interface->dev, "USB 7 Segment device now attached\n"); 355 return 0; 356 357 error_mem: 358 return rc; 359 } 360 361 static void sevseg_disconnect(struct usb_interface *interface) 362 { 363 struct usb_sevsegdev *mydev; 364 365 mydev = usb_get_intfdata(interface); 366 usb_set_intfdata(interface, NULL); 367 usb_put_dev(mydev->udev); 368 kfree(mydev); 369 dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); 370 } 371 372 static int sevseg_suspend(struct usb_interface *intf, pm_message_t message) 373 { 374 struct usb_sevsegdev *mydev; 375 376 mydev = usb_get_intfdata(intf); 377 mydev->shadow_power = 0; 378 379 return 0; 380 } 381 382 static int sevseg_resume(struct usb_interface *intf) 383 { 384 struct usb_sevsegdev *mydev; 385 386 mydev = usb_get_intfdata(intf); 387 mydev->shadow_power = 1; 388 update_display_mode(mydev); 389 update_display_visual(mydev, GFP_NOIO); 390 391 return 0; 392 } 393 394 static int sevseg_reset_resume(struct usb_interface *intf) 395 { 396 struct usb_sevsegdev *mydev; 397 398 mydev = usb_get_intfdata(intf); 399 mydev->shadow_power = 1; 400 update_display_mode(mydev); 401 update_display_visual(mydev, GFP_NOIO); 402 403 return 0; 404 } 405 406 static struct usb_driver sevseg_driver = { 407 .name = "usbsevseg", 408 .probe = sevseg_probe, 409 .disconnect = sevseg_disconnect, 410 .suspend = sevseg_suspend, 411 .resume = sevseg_resume, 412 .reset_resume = sevseg_reset_resume, 413 .id_table = id_table, 414 .dev_groups = sevseg_groups, 415 .supports_autosuspend = 1, 416 }; 417 418 module_usb_driver(sevseg_driver); 419 420 MODULE_AUTHOR(DRIVER_AUTHOR); 421 MODULE_DESCRIPTION(DRIVER_DESC); 422 MODULE_LICENSE("GPL"); 423