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