xref: /linux/drivers/usb/misc/usbsevseg.c (revision 5fd54ace4721fc5ce2bb5aef6318fcf17f421460)
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  *	This program is free software; you can redistribute it and/or
9  *	modify it under the terms of the GNU General Public License as
10  *	published by the Free Software Foundation, version 2.
11  *
12  */
13 
14 #include <linux/kernel.h>
15 #include <linux/errno.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 const char *display_textmodes[] = {"raw", "hex", "ascii"};
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 static 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 		return;
134 
135 	/* The device is right to left, where as you write left to right */
136 	for (i = 0; i < mydev->textlength; i++)
137 		buffer[i] = mydev->text[mydev->textlength-1-i];
138 
139 	rc = usb_control_msg(mydev->udev,
140 			usb_sndctrlpipe(mydev->udev, 0),
141 			0x12,
142 			0x48,
143 			(85 * 0x100) + 10, /* (write text) */
144 			(0 * 0x100) + mydev->textmode, /* mode  */
145 			buffer,
146 			mydev->textlength,
147 			2000);
148 
149 	if (rc < 0)
150 		dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
151 
152 	kfree(buffer);
153 
154 	/* The device is right to left, where as you write left to right */
155 	for (i = 0; i < sizeof(mydev->decimals); i++)
156 		decimals |= mydev->decimals[i] << i;
157 
158 	rc = usb_control_msg(mydev->udev,
159 			usb_sndctrlpipe(mydev->udev, 0),
160 			0x12,
161 			0x48,
162 			(86 * 0x100) + 10, /* (set decimal) */
163 			(0 * 0x100) + decimals, /* decimals */
164 			NULL,
165 			0,
166 			2000);
167 
168 	if (rc < 0)
169 		dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc);
170 }
171 
172 #define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn)		\
173 static ssize_t show_attr_##name(struct device *dev, 		\
174 	struct device_attribute *attr, char *buf) 		\
175 {								\
176 	struct usb_interface *intf = to_usb_interface(dev);	\
177 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
178 								\
179 	return sprintf(buf, "%u\n", mydev->name);		\
180 }								\
181 								\
182 static ssize_t set_attr_##name(struct device *dev, 		\
183 	struct device_attribute *attr, const char *buf, size_t count) \
184 {								\
185 	struct usb_interface *intf = to_usb_interface(dev);	\
186 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
187 								\
188 	mydev->name = simple_strtoul(buf, NULL, 10);		\
189 	update_fcn(mydev); 					\
190 								\
191 	return count;						\
192 }								\
193 static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, show_attr_##name, set_attr_##name);
194 
195 static ssize_t show_attr_text(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 
201 	return snprintf(buf, mydev->textlength, "%s\n", mydev->text);
202 }
203 
204 static ssize_t set_attr_text(struct device *dev,
205 	struct device_attribute *attr, const char *buf, size_t count)
206 {
207 	struct usb_interface *intf = to_usb_interface(dev);
208 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
209 	size_t end = my_memlen(buf, count);
210 
211 	if (end > sizeof(mydev->text))
212 		return -EINVAL;
213 
214 	memset(mydev->text, 0, sizeof(mydev->text));
215 	mydev->textlength = end;
216 
217 	if (end > 0)
218 		memcpy(mydev->text, buf, end);
219 
220 	update_display_visual(mydev, GFP_KERNEL);
221 	return count;
222 }
223 
224 static DEVICE_ATTR(text, S_IRUGO | S_IWUSR, show_attr_text, set_attr_text);
225 
226 static ssize_t show_attr_decimals(struct device *dev,
227 	struct device_attribute *attr, char *buf)
228 {
229 	struct usb_interface *intf = to_usb_interface(dev);
230 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
231 	int i;
232 	int pos;
233 
234 	for (i = 0; i < sizeof(mydev->decimals); i++) {
235 		pos = sizeof(mydev->decimals) - 1 - i;
236 		if (mydev->decimals[i] == 0)
237 			buf[pos] = '0';
238 		else if (mydev->decimals[i] == 1)
239 			buf[pos] = '1';
240 		else
241 			buf[pos] = 'x';
242 	}
243 
244 	buf[sizeof(mydev->decimals)] = '\n';
245 	return sizeof(mydev->decimals) + 1;
246 }
247 
248 static ssize_t set_attr_decimals(struct device *dev,
249 	struct device_attribute *attr, const char *buf, size_t count)
250 {
251 	struct usb_interface *intf = to_usb_interface(dev);
252 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
253 	size_t end = my_memlen(buf, count);
254 	int i;
255 
256 	if (end > sizeof(mydev->decimals))
257 		return -EINVAL;
258 
259 	for (i = 0; i < end; i++)
260 		if (buf[i] != '0' && buf[i] != '1')
261 			return -EINVAL;
262 
263 	memset(mydev->decimals, 0, sizeof(mydev->decimals));
264 	for (i = 0; i < end; i++)
265 		if (buf[i] == '1')
266 			mydev->decimals[end-1-i] = 1;
267 
268 	update_display_visual(mydev, GFP_KERNEL);
269 
270 	return count;
271 }
272 
273 static DEVICE_ATTR(decimals, S_IRUGO | S_IWUSR, show_attr_decimals, set_attr_decimals);
274 
275 static ssize_t show_attr_textmode(struct device *dev,
276 	struct device_attribute *attr, char *buf)
277 {
278 	struct usb_interface *intf = to_usb_interface(dev);
279 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
280 	int i;
281 
282 	buf[0] = 0;
283 
284 	for (i = 0; i < ARRAY_SIZE(display_textmodes); i++) {
285 		if (mydev->textmode == i) {
286 			strcat(buf, " [");
287 			strcat(buf, display_textmodes[i]);
288 			strcat(buf, "] ");
289 		} else {
290 			strcat(buf, " ");
291 			strcat(buf, display_textmodes[i]);
292 			strcat(buf, " ");
293 		}
294 	}
295 	strcat(buf, "\n");
296 
297 
298 	return strlen(buf);
299 }
300 
301 static ssize_t set_attr_textmode(struct device *dev,
302 	struct device_attribute *attr, const char *buf, size_t count)
303 {
304 	struct usb_interface *intf = to_usb_interface(dev);
305 	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
306 	int i;
307 
308 	i = sysfs_match_string(display_textmodes, buf);
309 	if (i < 0)
310 		return i;
311 
312 	mydev->textmode = i;
313 	update_display_visual(mydev, GFP_KERNEL);
314 	return count;
315 }
316 
317 static DEVICE_ATTR(textmode, S_IRUGO | S_IWUSR, show_attr_textmode, set_attr_textmode);
318 
319 
320 MYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
321 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
322 MYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
323 
324 static struct attribute *dev_attrs[] = {
325 	&dev_attr_powered.attr,
326 	&dev_attr_text.attr,
327 	&dev_attr_textmode.attr,
328 	&dev_attr_decimals.attr,
329 	&dev_attr_mode_msb.attr,
330 	&dev_attr_mode_lsb.attr,
331 	NULL
332 };
333 
334 static const struct attribute_group dev_attr_grp = {
335 	.attrs = dev_attrs,
336 };
337 
338 static int sevseg_probe(struct usb_interface *interface,
339 	const struct usb_device_id *id)
340 {
341 	struct usb_device *udev = interface_to_usbdev(interface);
342 	struct usb_sevsegdev *mydev = NULL;
343 	int rc = -ENOMEM;
344 
345 	mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
346 	if (!mydev)
347 		goto error_mem;
348 
349 	mydev->udev = usb_get_dev(udev);
350 	mydev->intf = interface;
351 	usb_set_intfdata(interface, mydev);
352 
353 	/* PM */
354 	mydev->shadow_power = 1; /* currently active */
355 	mydev->has_interface_pm = 0; /* have not issued autopm_get */
356 
357 	/*set defaults */
358 	mydev->textmode = 0x02; /* ascii mode */
359 	mydev->mode_msb = 0x06; /* 6 characters */
360 	mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
361 
362 	rc = sysfs_create_group(&interface->dev.kobj, &dev_attr_grp);
363 	if (rc)
364 		goto error;
365 
366 	dev_info(&interface->dev, "USB 7 Segment device now attached\n");
367 	return 0;
368 
369 error:
370 	usb_set_intfdata(interface, NULL);
371 	usb_put_dev(mydev->udev);
372 	kfree(mydev);
373 error_mem:
374 	return rc;
375 }
376 
377 static void sevseg_disconnect(struct usb_interface *interface)
378 {
379 	struct usb_sevsegdev *mydev;
380 
381 	mydev = usb_get_intfdata(interface);
382 	sysfs_remove_group(&interface->dev.kobj, &dev_attr_grp);
383 	usb_set_intfdata(interface, NULL);
384 	usb_put_dev(mydev->udev);
385 	kfree(mydev);
386 	dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
387 }
388 
389 static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
390 {
391 	struct usb_sevsegdev *mydev;
392 
393 	mydev = usb_get_intfdata(intf);
394 	mydev->shadow_power = 0;
395 
396 	return 0;
397 }
398 
399 static int sevseg_resume(struct usb_interface *intf)
400 {
401 	struct usb_sevsegdev *mydev;
402 
403 	mydev = usb_get_intfdata(intf);
404 	mydev->shadow_power = 1;
405 	update_display_mode(mydev);
406 	update_display_visual(mydev, GFP_NOIO);
407 
408 	return 0;
409 }
410 
411 static int sevseg_reset_resume(struct usb_interface *intf)
412 {
413 	struct usb_sevsegdev *mydev;
414 
415 	mydev = usb_get_intfdata(intf);
416 	mydev->shadow_power = 1;
417 	update_display_mode(mydev);
418 	update_display_visual(mydev, GFP_NOIO);
419 
420 	return 0;
421 }
422 
423 static struct usb_driver sevseg_driver = {
424 	.name =		"usbsevseg",
425 	.probe =	sevseg_probe,
426 	.disconnect =	sevseg_disconnect,
427 	.suspend =	sevseg_suspend,
428 	.resume =	sevseg_resume,
429 	.reset_resume =	sevseg_reset_resume,
430 	.id_table =	id_table,
431 	.supports_autosuspend = 1,
432 };
433 
434 module_usb_driver(sevseg_driver);
435 
436 MODULE_AUTHOR(DRIVER_AUTHOR);
437 MODULE_DESCRIPTION(DRIVER_DESC);
438 MODULE_LICENSE("GPL");
439