18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * USB 7 Segment Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2008 Harrison Metzger <harrisonmetz@gmail.com>
68c2ecf20Sopenharmony_ci * Based on usbled.c by Greg Kroah-Hartman (greg@kroah.com)
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/string.h>
148c2ecf20Sopenharmony_ci#include <linux/usb.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define DRIVER_AUTHOR "Harrison Metzger <harrisonmetz@gmail.com>"
188c2ecf20Sopenharmony_ci#define DRIVER_DESC "USB 7 Segment Driver"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define VENDOR_ID	0x0fc5
218c2ecf20Sopenharmony_ci#define PRODUCT_ID	0x1227
228c2ecf20Sopenharmony_ci#define MAXLEN		8
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* table of devices that work with this driver */
258c2ecf20Sopenharmony_cistatic const struct usb_device_id id_table[] = {
268c2ecf20Sopenharmony_ci	{ USB_DEVICE(VENDOR_ID, PRODUCT_ID) },
278c2ecf20Sopenharmony_ci	{ },
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, id_table);
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* the different text display modes the device is capable of */
328c2ecf20Sopenharmony_cistatic const char *display_textmodes[] = {"raw", "hex", "ascii"};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct usb_sevsegdev {
358c2ecf20Sopenharmony_ci	struct usb_device *udev;
368c2ecf20Sopenharmony_ci	struct usb_interface *intf;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	u8 powered;
398c2ecf20Sopenharmony_ci	u8 mode_msb;
408c2ecf20Sopenharmony_ci	u8 mode_lsb;
418c2ecf20Sopenharmony_ci	u8 decimals[MAXLEN];
428c2ecf20Sopenharmony_ci	u8 textmode;
438c2ecf20Sopenharmony_ci	u8 text[MAXLEN];
448c2ecf20Sopenharmony_ci	u16 textlength;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	u8 shadow_power; /* for PM */
478c2ecf20Sopenharmony_ci	u8 has_interface_pm;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* sysfs_streq can't replace this completely
518c2ecf20Sopenharmony_ci * If the device was in hex mode, and the user wanted a 0,
528c2ecf20Sopenharmony_ci * if str commands are used, we would assume the end of string
538c2ecf20Sopenharmony_ci * so mem commands are used.
548c2ecf20Sopenharmony_ci */
558c2ecf20Sopenharmony_cistatic inline size_t my_memlen(const char *buf, size_t count)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	if (count > 0 && buf[count-1] == '\n')
588c2ecf20Sopenharmony_ci		return count - 1;
598c2ecf20Sopenharmony_ci	else
608c2ecf20Sopenharmony_ci		return count;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic void update_display_powered(struct usb_sevsegdev *mydev)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	int rc;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (mydev->powered && !mydev->has_interface_pm) {
688c2ecf20Sopenharmony_ci		rc = usb_autopm_get_interface(mydev->intf);
698c2ecf20Sopenharmony_ci		if (rc < 0)
708c2ecf20Sopenharmony_ci			return;
718c2ecf20Sopenharmony_ci		mydev->has_interface_pm = 1;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	if (mydev->shadow_power != 1)
758c2ecf20Sopenharmony_ci		return;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	rc = usb_control_msg(mydev->udev,
788c2ecf20Sopenharmony_ci			usb_sndctrlpipe(mydev->udev, 0),
798c2ecf20Sopenharmony_ci			0x12,
808c2ecf20Sopenharmony_ci			0x48,
818c2ecf20Sopenharmony_ci			(80 * 0x100) + 10, /*  (power mode) */
828c2ecf20Sopenharmony_ci			(0x00 * 0x100) + (mydev->powered ? 1 : 0),
838c2ecf20Sopenharmony_ci			NULL,
848c2ecf20Sopenharmony_ci			0,
858c2ecf20Sopenharmony_ci			2000);
868c2ecf20Sopenharmony_ci	if (rc < 0)
878c2ecf20Sopenharmony_ci		dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (!mydev->powered && mydev->has_interface_pm) {
908c2ecf20Sopenharmony_ci		usb_autopm_put_interface(mydev->intf);
918c2ecf20Sopenharmony_ci		mydev->has_interface_pm = 0;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void update_display_mode(struct usb_sevsegdev *mydev)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	int rc;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if(mydev->shadow_power != 1)
1008c2ecf20Sopenharmony_ci		return;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	rc = usb_control_msg(mydev->udev,
1038c2ecf20Sopenharmony_ci			usb_sndctrlpipe(mydev->udev, 0),
1048c2ecf20Sopenharmony_ci			0x12,
1058c2ecf20Sopenharmony_ci			0x48,
1068c2ecf20Sopenharmony_ci			(82 * 0x100) + 10, /* (set mode) */
1078c2ecf20Sopenharmony_ci			(mydev->mode_msb * 0x100) + mydev->mode_lsb,
1088c2ecf20Sopenharmony_ci			NULL,
1098c2ecf20Sopenharmony_ci			0,
1108c2ecf20Sopenharmony_ci			2000);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (rc < 0)
1138c2ecf20Sopenharmony_ci		dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	int rc;
1198c2ecf20Sopenharmony_ci	int i;
1208c2ecf20Sopenharmony_ci	unsigned char *buffer;
1218c2ecf20Sopenharmony_ci	u8 decimals = 0;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if(mydev->shadow_power != 1)
1248c2ecf20Sopenharmony_ci		return;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	buffer = kzalloc(MAXLEN, mf);
1278c2ecf20Sopenharmony_ci	if (!buffer)
1288c2ecf20Sopenharmony_ci		return;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* The device is right to left, where as you write left to right */
1318c2ecf20Sopenharmony_ci	for (i = 0; i < mydev->textlength; i++)
1328c2ecf20Sopenharmony_ci		buffer[i] = mydev->text[mydev->textlength-1-i];
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	rc = usb_control_msg(mydev->udev,
1358c2ecf20Sopenharmony_ci			usb_sndctrlpipe(mydev->udev, 0),
1368c2ecf20Sopenharmony_ci			0x12,
1378c2ecf20Sopenharmony_ci			0x48,
1388c2ecf20Sopenharmony_ci			(85 * 0x100) + 10, /* (write text) */
1398c2ecf20Sopenharmony_ci			(0 * 0x100) + mydev->textmode, /* mode  */
1408c2ecf20Sopenharmony_ci			buffer,
1418c2ecf20Sopenharmony_ci			mydev->textlength,
1428c2ecf20Sopenharmony_ci			2000);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (rc < 0)
1458c2ecf20Sopenharmony_ci		dev_dbg(&mydev->udev->dev, "write retval = %d\n", rc);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	kfree(buffer);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/* The device is right to left, where as you write left to right */
1508c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(mydev->decimals); i++)
1518c2ecf20Sopenharmony_ci		decimals |= mydev->decimals[i] << i;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	rc = usb_control_msg(mydev->udev,
1548c2ecf20Sopenharmony_ci			usb_sndctrlpipe(mydev->udev, 0),
1558c2ecf20Sopenharmony_ci			0x12,
1568c2ecf20Sopenharmony_ci			0x48,
1578c2ecf20Sopenharmony_ci			(86 * 0x100) + 10, /* (set decimal) */
1588c2ecf20Sopenharmony_ci			(0 * 0x100) + decimals, /* decimals */
1598c2ecf20Sopenharmony_ci			NULL,
1608c2ecf20Sopenharmony_ci			0,
1618c2ecf20Sopenharmony_ci			2000);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	if (rc < 0)
1648c2ecf20Sopenharmony_ci		dev_dbg(&mydev->udev->dev, "decimal retval = %d\n", rc);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci#define MYDEV_ATTR_SIMPLE_UNSIGNED(name, update_fcn)		\
1688c2ecf20Sopenharmony_cistatic ssize_t name##_show(struct device *dev,			\
1698c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buf) 		\
1708c2ecf20Sopenharmony_ci{								\
1718c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);	\
1728c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
1738c2ecf20Sopenharmony_ci								\
1748c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", mydev->name);		\
1758c2ecf20Sopenharmony_ci}								\
1768c2ecf20Sopenharmony_ci								\
1778c2ecf20Sopenharmony_cistatic ssize_t name##_store(struct device *dev,			\
1788c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buf, size_t count) \
1798c2ecf20Sopenharmony_ci{								\
1808c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);	\
1818c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);	\
1828c2ecf20Sopenharmony_ci								\
1838c2ecf20Sopenharmony_ci	mydev->name = simple_strtoul(buf, NULL, 10);		\
1848c2ecf20Sopenharmony_ci	update_fcn(mydev); 					\
1858c2ecf20Sopenharmony_ci								\
1868c2ecf20Sopenharmony_ci	return count;						\
1878c2ecf20Sopenharmony_ci}								\
1888c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(name);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic ssize_t text_show(struct device *dev,
1918c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buf)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
1948c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return snprintf(buf, mydev->textlength, "%s\n", mydev->text);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic ssize_t text_store(struct device *dev,
2008c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buf, size_t count)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2038c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
2048c2ecf20Sopenharmony_ci	size_t end = my_memlen(buf, count);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (end > sizeof(mydev->text))
2078c2ecf20Sopenharmony_ci		return -EINVAL;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	memset(mydev->text, 0, sizeof(mydev->text));
2108c2ecf20Sopenharmony_ci	mydev->textlength = end;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (end > 0)
2138c2ecf20Sopenharmony_ci		memcpy(mydev->text, buf, end);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	update_display_visual(mydev, GFP_KERNEL);
2168c2ecf20Sopenharmony_ci	return count;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(text);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic ssize_t decimals_show(struct device *dev,
2228c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buf)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2258c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
2268c2ecf20Sopenharmony_ci	int i;
2278c2ecf20Sopenharmony_ci	int pos;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	for (i = 0; i < sizeof(mydev->decimals); i++) {
2308c2ecf20Sopenharmony_ci		pos = sizeof(mydev->decimals) - 1 - i;
2318c2ecf20Sopenharmony_ci		if (mydev->decimals[i] == 0)
2328c2ecf20Sopenharmony_ci			buf[pos] = '0';
2338c2ecf20Sopenharmony_ci		else if (mydev->decimals[i] == 1)
2348c2ecf20Sopenharmony_ci			buf[pos] = '1';
2358c2ecf20Sopenharmony_ci		else
2368c2ecf20Sopenharmony_ci			buf[pos] = 'x';
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	buf[sizeof(mydev->decimals)] = '\n';
2408c2ecf20Sopenharmony_ci	return sizeof(mydev->decimals) + 1;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic ssize_t decimals_store(struct device *dev,
2448c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buf, size_t count)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2478c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
2488c2ecf20Sopenharmony_ci	size_t end = my_memlen(buf, count);
2498c2ecf20Sopenharmony_ci	int i;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (end > sizeof(mydev->decimals))
2528c2ecf20Sopenharmony_ci		return -EINVAL;
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	for (i = 0; i < end; i++)
2558c2ecf20Sopenharmony_ci		if (buf[i] != '0' && buf[i] != '1')
2568c2ecf20Sopenharmony_ci			return -EINVAL;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	memset(mydev->decimals, 0, sizeof(mydev->decimals));
2598c2ecf20Sopenharmony_ci	for (i = 0; i < end; i++)
2608c2ecf20Sopenharmony_ci		if (buf[i] == '1')
2618c2ecf20Sopenharmony_ci			mydev->decimals[end-1-i] = 1;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	update_display_visual(mydev, GFP_KERNEL);
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return count;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(decimals);
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_cistatic ssize_t textmode_show(struct device *dev,
2718c2ecf20Sopenharmony_ci	struct device_attribute *attr, char *buf)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
2748c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
2758c2ecf20Sopenharmony_ci	int i;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	buf[0] = 0;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(display_textmodes); i++) {
2808c2ecf20Sopenharmony_ci		if (mydev->textmode == i) {
2818c2ecf20Sopenharmony_ci			strcat(buf, " [");
2828c2ecf20Sopenharmony_ci			strcat(buf, display_textmodes[i]);
2838c2ecf20Sopenharmony_ci			strcat(buf, "] ");
2848c2ecf20Sopenharmony_ci		} else {
2858c2ecf20Sopenharmony_ci			strcat(buf, " ");
2868c2ecf20Sopenharmony_ci			strcat(buf, display_textmodes[i]);
2878c2ecf20Sopenharmony_ci			strcat(buf, " ");
2888c2ecf20Sopenharmony_ci		}
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	strcat(buf, "\n");
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return strlen(buf);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic ssize_t textmode_store(struct device *dev,
2978c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buf, size_t count)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct usb_interface *intf = to_usb_interface(dev);
3008c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = usb_get_intfdata(intf);
3018c2ecf20Sopenharmony_ci	int i;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	i = sysfs_match_string(display_textmodes, buf);
3048c2ecf20Sopenharmony_ci	if (i < 0)
3058c2ecf20Sopenharmony_ci		return i;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	mydev->textmode = i;
3088c2ecf20Sopenharmony_ci	update_display_visual(mydev, GFP_KERNEL);
3098c2ecf20Sopenharmony_ci	return count;
3108c2ecf20Sopenharmony_ci}
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(textmode);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ciMYDEV_ATTR_SIMPLE_UNSIGNED(powered, update_display_powered);
3168c2ecf20Sopenharmony_ciMYDEV_ATTR_SIMPLE_UNSIGNED(mode_msb, update_display_mode);
3178c2ecf20Sopenharmony_ciMYDEV_ATTR_SIMPLE_UNSIGNED(mode_lsb, update_display_mode);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic struct attribute *sevseg_attrs[] = {
3208c2ecf20Sopenharmony_ci	&dev_attr_powered.attr,
3218c2ecf20Sopenharmony_ci	&dev_attr_text.attr,
3228c2ecf20Sopenharmony_ci	&dev_attr_textmode.attr,
3238c2ecf20Sopenharmony_ci	&dev_attr_decimals.attr,
3248c2ecf20Sopenharmony_ci	&dev_attr_mode_msb.attr,
3258c2ecf20Sopenharmony_ci	&dev_attr_mode_lsb.attr,
3268c2ecf20Sopenharmony_ci	NULL
3278c2ecf20Sopenharmony_ci};
3288c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(sevseg);
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int sevseg_probe(struct usb_interface *interface,
3318c2ecf20Sopenharmony_ci	const struct usb_device_id *id)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(interface);
3348c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev = NULL;
3358c2ecf20Sopenharmony_ci	int rc = -ENOMEM;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	mydev = kzalloc(sizeof(struct usb_sevsegdev), GFP_KERNEL);
3388c2ecf20Sopenharmony_ci	if (!mydev)
3398c2ecf20Sopenharmony_ci		goto error_mem;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	mydev->udev = usb_get_dev(udev);
3428c2ecf20Sopenharmony_ci	mydev->intf = interface;
3438c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, mydev);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/* PM */
3468c2ecf20Sopenharmony_ci	mydev->shadow_power = 1; /* currently active */
3478c2ecf20Sopenharmony_ci	mydev->has_interface_pm = 0; /* have not issued autopm_get */
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	/*set defaults */
3508c2ecf20Sopenharmony_ci	mydev->textmode = 0x02; /* ascii mode */
3518c2ecf20Sopenharmony_ci	mydev->mode_msb = 0x06; /* 6 characters */
3528c2ecf20Sopenharmony_ci	mydev->mode_lsb = 0x3f; /* scanmode for 6 chars */
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	dev_info(&interface->dev, "USB 7 Segment device now attached\n");
3558c2ecf20Sopenharmony_ci	return 0;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cierror_mem:
3588c2ecf20Sopenharmony_ci	return rc;
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic void sevseg_disconnect(struct usb_interface *interface)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	mydev = usb_get_intfdata(interface);
3668c2ecf20Sopenharmony_ci	usb_set_intfdata(interface, NULL);
3678c2ecf20Sopenharmony_ci	usb_put_dev(mydev->udev);
3688c2ecf20Sopenharmony_ci	kfree(mydev);
3698c2ecf20Sopenharmony_ci	dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	mydev = usb_get_intfdata(intf);
3778c2ecf20Sopenharmony_ci	mydev->shadow_power = 0;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	return 0;
3808c2ecf20Sopenharmony_ci}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_cistatic int sevseg_resume(struct usb_interface *intf)
3838c2ecf20Sopenharmony_ci{
3848c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	mydev = usb_get_intfdata(intf);
3878c2ecf20Sopenharmony_ci	mydev->shadow_power = 1;
3888c2ecf20Sopenharmony_ci	update_display_mode(mydev);
3898c2ecf20Sopenharmony_ci	update_display_visual(mydev, GFP_NOIO);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic int sevseg_reset_resume(struct usb_interface *intf)
3958c2ecf20Sopenharmony_ci{
3968c2ecf20Sopenharmony_ci	struct usb_sevsegdev *mydev;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	mydev = usb_get_intfdata(intf);
3998c2ecf20Sopenharmony_ci	mydev->shadow_power = 1;
4008c2ecf20Sopenharmony_ci	update_display_mode(mydev);
4018c2ecf20Sopenharmony_ci	update_display_visual(mydev, GFP_NOIO);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return 0;
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic struct usb_driver sevseg_driver = {
4078c2ecf20Sopenharmony_ci	.name =		"usbsevseg",
4088c2ecf20Sopenharmony_ci	.probe =	sevseg_probe,
4098c2ecf20Sopenharmony_ci	.disconnect =	sevseg_disconnect,
4108c2ecf20Sopenharmony_ci	.suspend =	sevseg_suspend,
4118c2ecf20Sopenharmony_ci	.resume =	sevseg_resume,
4128c2ecf20Sopenharmony_ci	.reset_resume =	sevseg_reset_resume,
4138c2ecf20Sopenharmony_ci	.id_table =	id_table,
4148c2ecf20Sopenharmony_ci	.dev_groups =	sevseg_groups,
4158c2ecf20Sopenharmony_ci	.supports_autosuspend = 1,
4168c2ecf20Sopenharmony_ci};
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_cimodule_usb_driver(sevseg_driver);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ciMODULE_AUTHOR(DRIVER_AUTHOR);
4218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRIVER_DESC);
4228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
423