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_send(mydev->udev, 0, 0x12, 0x48, 78 (80 * 0x100) + 10, /* (power mode) */ 79 (0x00 * 0x100) + (mydev->powered ? 1 : 0), 80 NULL, 0, 2000, GFP_KERNEL); 81 if (rc < 0) 82 dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); 83 84 if (!mydev->powered && mydev->has_interface_pm) { 85 usb_autopm_put_interface(mydev->intf); 86 mydev->has_interface_pm = 0; 87 } 88 } 89 90 static void update_display_mode(struct usb_sevsegdev *mydev) 91 { 92 int rc; 93 94 if(mydev->shadow_power != 1) 95 return; 96 97 rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48, 98 (82 * 0x100) + 10, /* (set mode) */ 99 (mydev->mode_msb * 0x100) + mydev->mode_lsb, 100 NULL, 0, 2000, GFP_NOIO); 101 102 if (rc < 0) 103 dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); 104 } 105 106 static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) 107 { 108 int rc; 109 int i; 110 unsigned char buffer[MAXLEN] = {0}; 111 u8 decimals = 0; 112 113 if(mydev->shadow_power != 1) 114 return; 115 116 /* The device is right to left, where as you write left to right */ 117 for (i = 0; i < mydev->textlength; i++) 118 buffer[i] = mydev->text[mydev->textlength-1-i]; 119 120 rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48, 121 (85 * 0x100) + 10, /* (write text) */ 122 (0 * 0x100) + mydev->textmode, /* mode */ 123 &buffer, mydev->textlength, 2000, mf); 124 125 if (rc < 0) 126 dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc); 127 128 /* The device is right to left, where as you write left to right */ 129 for (i = 0; i < sizeof(mydev->decimals); i++) 130 decimals |= mydev->decimals[i] << i; 131 132 rc = usb_control_msg_send(mydev->udev, 0, 0x12, 0x48, 133 (86 * 0x100) + 10, /* (set decimal) */ 134 (0 * 0x100) + decimals, /* decimals */ 135 NULL, 0, 2000, mf); 136 137 if (rc < 0) 138 dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc); 139 } 140 141 #define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn) \ 142 static ssize_t name##_show(struct device *dev, \ 143 struct device_attribute *attr, char *buf) \ 144 { \ 145 struct usb_interface *intf = to_usb_interface(dev); \ 146 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ 147 \ 148 return sprintf(buf, "%u\n", mydev->name); \ 149 } \ 150 \ 151 static ssize_t name##_store(struct device *dev, \ 152 struct device_attribute *attr, const char *buf, size_t count) \ 153 { \ 154 struct usb_interface *intf = to_usb_interface(dev); \ 155 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ 156 \ 157 mydev->name = simple_strtoul(buf, NULL, 10); \ 158 update_fcn(mydev); \ 159 \ 160 return count; \ 161 } \ 162 static DEVICE_ATTR_RW(name); 163 164 static ssize_t text_show(struct device *dev, 165 struct device_attribute *attr, char *buf) 166 { 167 struct usb_interface *intf = to_usb_interface(dev); 168 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 169 170 return sysfs_emit(buf, "%s\n", mydev->text); 171 } 172 173 static ssize_t text_store(struct device *dev, 174 struct device_attribute *attr, const char *buf, size_t count) 175 { 176 struct usb_interface *intf = to_usb_interface(dev); 177 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 178 size_t end = my_memlen(buf, count); 179 180 if (end > sizeof(mydev->text)) 181 return -EINVAL; 182 183 memset(mydev->text, 0, sizeof(mydev->text)); 184 mydev->textlength = end; 185 186 if (end > 0) 187 memcpy(mydev->text, buf, end); 188 189 update_display_visual(mydev, GFP_KERNEL); 190 return count; 191 } 192 193 static DEVICE_ATTR_RW(text); 194 195 static ssize_t decimals_show(struct device *dev, 196 struct device_attribute *attr, char *buf) 197 { 198 struct usb_interface *intf = to_usb_interface(dev); 199 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 200 int i; 201 int pos; 202 203 for (i = 0; i < sizeof(mydev->decimals); i++) { 204 pos = sizeof(mydev->decimals) - 1 - i; 205 if (mydev->decimals[i] == 0) 206 buf[pos] = '0'; 207 else if (mydev->decimals[i] == 1) 208 buf[pos] = '1'; 209 else 210 buf[pos] = 'x'; 211 } 212 213 buf[sizeof(mydev->decimals)] = '\n'; 214 return sizeof(mydev->decimals) + 1; 215 } 216 217 static ssize_t decimals_store(struct device *dev, 218 struct device_attribute *attr, const char *buf, size_t count) 219 { 220 struct usb_interface *intf = to_usb_interface(dev); 221 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 222 size_t end = my_memlen(buf, count); 223 int i; 224 225 if (end > sizeof(mydev->decimals)) 226 return -EINVAL; 227 228 for (i = 0; i < end; i++) 229 if (buf[i] != '0' && buf[i] != '1') 230 return -EINVAL; 231 232 memset(mydev->decimals, 0, sizeof(mydev->decimals)); 233 for (i = 0; i < end; i++) 234 if (buf[i] == '1') 235 mydev->decimals[end-1-i] = 1; 236 237 update_display_visual(mydev, GFP_KERNEL); 238 239 return count; 240 } 241 242 static DEVICE_ATTR_RW(decimals); 243 244 static ssize_t textmode_show(struct device *dev, 245 struct device_attribute *attr, char *buf) 246 { 247 struct usb_interface *intf = to_usb_interface(dev); 248 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 249 int i; 250 251 buf[0] = 0; 252 253 for (i = 0; i < ARRAY_SIZE(display_textmodes); i++) { 254 if (mydev->textmode == i) { 255 strcat(buf, " ["); 256 strcat(buf, display_textmodes[i]); 257 strcat(buf, "] "); 258 } else { 259 strcat(buf, " "); 260 strcat(buf, display_textmodes[i]); 261 strcat(buf, " "); 262 } 263 } 264 strcat(buf, "\n"); 265 266 267 return strlen(buf); 268 } 269 270 static ssize_t textmode_store(struct device *dev, 271 struct device_attribute *attr, const char *buf, size_t count) 272 { 273 struct usb_interface *intf = to_usb_interface(dev); 274 struct usb_sevsegdev *mydev = usb_get_intfdata(intf); 275 int i; 276 277 i = sysfs_match_string(display_textmodes, buf); 278 if (i < 0) 279 return i; 280 281 mydev->textmode = i; 282 update_display_visual(mydev, GFP_KERNEL); 283 return count; 284 } 285 286 static DEVICE_ATTR_RW(textmode); 287 288 289 MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered); 290 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode); 291 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode); 292 293 static struct attribute *sevseg_attrs[] = { 294 &dev_attr_powered.attr, 295 &dev_attr_text.attr, 296 &dev_attr_textmode.attr, 297 &dev_attr_decimals.attr, 298 &dev_attr_mode_msb.attr, 299 &dev_attr_mode_lsb.attr, 300 NULL 301 }; 302 ATTRIBUTE_GROUPS(sevseg); 303 304 static int sevseg_probe(struct usb_interface *interface, 305 const struct usb_device_id *id) 306 { 307 struct usb_device *udev = interface_to_usbdev(interface); 308 struct usb_sevsegdev *mydev = NULL; 309 int rc = -ENOMEM; 310 311 mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL); 312 if (!mydev) 313 goto error_mem; 314 315 mydev->udev = usb_get_dev(udev); 316 mydev->intf = interface; 317 usb_set_intfdata(interface, mydev); 318 319 /* PM */ 320 mydev->shadow_power = 1; /* currently active */ 321 mydev->has_interface_pm = 0; /* have not issued autopm_get */ 322 323 /*set defaults */ 324 mydev->textmode = 0x02; /* ascii mode */ 325 mydev->mode_msb = 0x06; /* 6 characters */ 326 mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */ 327 328 dev_info(&interface->dev, "USB 7 Segment device now attached\n"); 329 return 0; 330 331 error_mem: 332 return rc; 333 } 334 335 static void sevseg_disconnect(struct usb_interface *interface) 336 { 337 struct usb_sevsegdev *mydev; 338 339 mydev = usb_get_intfdata(interface); 340 usb_set_intfdata(interface, NULL); 341 usb_put_dev(mydev->udev); 342 kfree(mydev); 343 dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); 344 } 345 346 static int sevseg_suspend(struct usb_interface *intf, pm_message_t message) 347 { 348 struct usb_sevsegdev *mydev; 349 350 mydev = usb_get_intfdata(intf); 351 mydev->shadow_power = 0; 352 353 return 0; 354 } 355 356 static int sevseg_resume(struct usb_interface *intf) 357 { 358 struct usb_sevsegdev *mydev; 359 360 mydev = usb_get_intfdata(intf); 361 mydev->shadow_power = 1; 362 update_display_mode(mydev); 363 update_display_visual(mydev, GFP_NOIO); 364 365 return 0; 366 } 367 368 static int sevseg_reset_resume(struct usb_interface *intf) 369 { 370 struct usb_sevsegdev *mydev; 371 372 mydev = usb_get_intfdata(intf); 373 mydev->shadow_power = 1; 374 update_display_mode(mydev); 375 update_display_visual(mydev, GFP_NOIO); 376 377 return 0; 378 } 379 380 static struct usb_driver sevseg_driver = { 381 .name = "usbsevseg", 382 .probe = sevseg_probe, 383 .disconnect = sevseg_disconnect, 384 .suspend = sevseg_suspend, 385 .resume = sevseg_resume, 386 .reset_resume = sevseg_reset_resume, 387 .id_table = id_table, 388 .dev_groups = sevseg_groups, 389 .supports_autosuspend = 1, 390 }; 391 392 module_usb_driver(sevseg_driver); 393 394 MODULE_AUTHOR(DRIVER_AUTHOR); 395 MODULE_DESCRIPTION(DRIVER_DESC); 396 MODULE_LICENSE("GPL"); 397