xref: /linux/drivers/usb/misc/usbsevseg.c (revision 0d456bad36d42d16022be045c8a53ddbb59ee478)
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		8
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_IRUGO | S_IWUSR, 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_IRUGO | S_IWUSR, 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_IRUGO | S_IWUSR, show_attr_decimals, set_attr_decimals);
276 
277 static ssize_t show_attr_textmode(struct device *dev,
278 	struct device_attribute *attr, char *buf)
279 {
280 	struct usb_interface *intf = to_usb_interface(dev);
281 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
282 	int i;
283 
284 	buf[0] = 0;
285 
286 	for (i = 0; display_textmodes[i]; i++) {
287 		if (mydev->textmode == i) {
288 			strcat(buf, " [");
289 			strcat(buf, display_textmodes[i]);
290 			strcat(buf, "] ");
291 		} else {
292 			strcat(buf, " ");
293 			strcat(buf, display_textmodes[i]);
294 			strcat(buf, " ");
295 		}
296 	}
297 	strcat(buf, "\n");
298 
299 
300 	return strlen(buf);
301 }
302 
303 static ssize_t set_attr_textmode(struct device *dev,
304 	struct device_attribute *attr, const char *buf, size_t count)
305 {
306 	struct usb_interface *intf = to_usb_interface(dev);
307 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
308 	int i;
309 
310 	for (i = 0; display_textmodes[i]; i++) {
311 		if (sysfs_streq(display_textmodes[i], buf)) {
312 			mydev->textmode = i;
313 			update_display_visual(mydev, GFP_KERNEL);
314 			return count;
315 		}
316 	}
317 
318 	return -EINVAL;
319 }
320 
321 static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, show_attr_textmode, set_attr_textmode);
322 
323 
324 MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
325 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
326 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
327 
328 static struct attribute *dev_attrs[] = {
329 	&dev_attr_powered.attr,
330 	&dev_attr_text.attr,
331 	&dev_attr_textmode.attr,
332 	&dev_attr_decimals.attr,
333 	&dev_attr_mode_msb.attr,
334 	&dev_attr_mode_lsb.attr,
335 	NULL
336 };
337 
338 static struct attribute_group dev_attr_grp = {
339 	.attrs = dev_attrs,
340 };
341 
342 static int sevseg_probe(struct usb_interface *interface,
343 	const struct usb_device_id *id)
344 {
345 	struct usb_device *udev = interface_to_usbdev(interface);
346 	struct usb_sevsegdev *mydev = NULL;
347 	int rc = -ENOMEM;
348 
349 	mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
350 	if (mydev == NULL) {
351 		dev_err(&interface->dev, "Out of memory\n");
352 		goto error_mem;
353 	}
354 
355 	mydev->udev = usb_get_dev(udev);
356 	mydev->intf = interface;
357 	usb_set_intfdata(interface, mydev);
358 
359 	/* PM */
360 	mydev->shadow_power = 1; /* currently active */
361 	mydev->has_interface_pm = 0; /* have not issued autopm_get */
362 
363 	/*set defaults */
364 	mydev->textmode = 0x02; /* ascii mode */
365 	mydev->mode_msb = 0x06; /* 6 characters */
366 	mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
367 
368 	rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp);
369 	if (rc)
370 		goto error;
371 
372 	dev_info(&interface->dev, "USB 7 Segment device now attached\n");
373 	return 0;
374 
375 error:
376 	usb_set_intfdata(interface, NULL);
377 	usb_put_dev(mydev->udev);
378 	kfree(mydev);
379 error_mem:
380 	return rc;
381 }
382 
383 static void sevseg_disconnect(struct usb_interface *interface)
384 {
385 	struct usb_sevsegdev *mydev;
386 
387 	mydev = usb_get_intfdata(interface);
388 	sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp);
389 	usb_set_intfdata(interface, NULL);
390 	usb_put_dev(mydev->udev);
391 	kfree(mydev);
392 	dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
393 }
394 
395 static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
396 {
397 	struct usb_sevsegdev *mydev;
398 
399 	mydev = usb_get_intfdata(intf);
400 	mydev->shadow_power = 0;
401 
402 	return 0;
403 }
404 
405 static int sevseg_resume(struct usb_interface *intf)
406 {
407 	struct usb_sevsegdev *mydev;
408 
409 	mydev = usb_get_intfdata(intf);
410 	mydev->shadow_power = 1;
411 	update_display_mode(mydev);
412 	update_display_visual(mydev, GFP_NOIO);
413 
414 	return 0;
415 }
416 
417 static int sevseg_reset_resume(struct usb_interface *intf)
418 {
419 	struct usb_sevsegdev *mydev;
420 
421 	mydev = usb_get_intfdata(intf);
422 	mydev->shadow_power = 1;
423 	update_display_mode(mydev);
424 	update_display_visual(mydev, GFP_NOIO);
425 
426 	return 0;
427 }
428 
429 static struct usb_driver sevseg_driver = {
430 	.name =		"usbsevseg",
431 	.probe =	sevseg_probe,
432 	.disconnect =	sevseg_disconnect,
433 	.suspend =	sevseg_suspend,
434 	.resume =	sevseg_resume,
435 	.reset_resume =	sevseg_reset_resume,
436 	.id_table =	id_table,
437 	.supports_autosuspend = 1,
438 };
439 
440 module_usb_driver(sevseg_driver);
441 
442 MODULE_AUTHOR(DRIVER_AUTHOR);
443 MODULE_DESCRIPTION(DRIVER_DESC);
444 MODULE_LICENSE("GPL");
445