Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d155eb5 authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman
Browse files

USB: full autosuspend and power management support for usbsevseg



This patch adds to the usbsevseg driver:

- suspend/resume support
- reset_resume support
- autosuspend using the display's power state to determine idleness

Signed-off-by: default avatarOliver Neukum <oliver@neukum.org>
Signed-off-by: default avatarHarrison Metzger <harrisonmetz@gmail.com>
parent d9bfbd16
Loading
Loading
Loading
Loading
+63 −6
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ static char *display_textmodes[] = {"raw", "hex", "ascii", NULL};

struct usb_sevsegdev {
	struct usb_device *udev;
	struct usb_interface *intf;

	u8 powered;
	u8 mode_msb;
@@ -46,6 +47,8 @@ struct usb_sevsegdev {
	u8 textmode;
	u8 text[MAXLEN];
	u16 textlength;

	u8 shadow_power; /* for PM */
};

/* sysfs_streq can't replace this completely
@@ -65,6 +68,12 @@ static void update_display_powered(struct usb_sevsegdev *mydev)
{
	int rc;

	if (!mydev->shadow_power && mydev->powered) {
		rc = usb_autopm_get_interface(mydev->intf);
		if (rc < 0)
			return;
	}

	rc = usb_control_msg(mydev->udev,
			usb_sndctrlpipe(mydev->udev, 0),
			0x12,
@@ -76,12 +85,18 @@ static void update_display_powered(struct usb_sevsegdev *mydev)
			2000);
	if (rc < 0)
		dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc);

	if (mydev->shadow_power && !mydev->powered)
		usb_autopm_put_interface(mydev->intf);
}

static void update_display_mode(struct usb_sevsegdev *mydev)
{
	int rc;

	if(mydev->shadow_power != 1)
		return;

	rc = usb_control_msg(mydev->udev,
			usb_sndctrlpipe(mydev->udev, 0),
			0x12,
@@ -96,14 +111,17 @@ static void update_display_mode(struct usb_sevsegdev *mydev)
		dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc);
}

static void update_display_visual(struct usb_sevsegdev *mydev)
static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf)
{
	int rc;
	int i;
	unsigned char *buffer;
	u8 decimals = 0;

	buffer = kzalloc(MAXLEN, GFP_KERNEL);
	if(mydev->shadow_power != 1)
		return;

	buffer = kzalloc(MAXLEN, mf);
	if (!buffer) {
		dev_err(&mydev->udev->dev, "out of memory\n");
		return;
@@ -194,7 +212,7 @@ static ssize_t set_attr_text(struct device *dev,
	if (end > 0)
		memcpy(mydev->text, buf, end);

	update_display_visual(mydev);
	update_display_visual(mydev, GFP_KERNEL);
	return count;
}

@@ -242,7 +260,7 @@ static ssize_t set_attr_decimals(struct device *dev,
		if (buf[i] == '1')
			mydev->decimals[end-1-i] = 1;

	update_display_visual(mydev);
	update_display_visual(mydev, GFP_KERNEL);

	return count;
}
@@ -286,7 +304,7 @@ static ssize_t set_attr_textmode(struct device *dev,
	for (i = 0; display_textmodes[i]; i++) {
		if (sysfs_streq(display_textmodes[i], buf)) {
			mydev->textmode = i;
			update_display_visual(mydev);
			update_display_visual(mydev, GFP_KERNEL);
			return count;
		}
	}
@@ -330,6 +348,7 @@ static int sevseg_probe(struct usb_interface *interface,
	}

	mydev->udev = usb_get_dev(udev);
	mydev->intf = interface;
	usb_set_intfdata(interface, mydev);

	/*set defaults */
@@ -364,11 +383,49 @@ static void sevseg_disconnect(struct usb_interface *interface)
	dev_info(&interface->dev, "USB 7 Segment now disconnected\n");
}

static int sevseg_suspend(struct usb_interface *intf, pm_message_t message)
{
	struct usb_sevsegdev *mydev;

	mydev = usb_get_intfdata(intf);
	mydev->shadow_power = 0;

	return 0;
}

static int sevseg_resume(struct usb_interface *intf)
{
	struct usb_sevsegdev *mydev;

	mydev = usb_get_intfdata(intf);
	mydev->shadow_power = 1;
	update_display_mode(mydev);
	update_display_visual(mydev, GFP_NOIO);

	return 0;
}

static int sevseg_reset_resume(struct usb_interface *intf)
{
	struct usb_sevsegdev *mydev;

	mydev = usb_get_intfdata(intf);
	mydev->shadow_power = 1;
	update_display_mode(mydev);
	update_display_visual(mydev, GFP_NOIO);

	return 0;
}

static struct usb_driver sevseg_driver = {
	.name =		"usbsevseg",
	.probe =	sevseg_probe,
	.disconnect =	sevseg_disconnect,
	.suspend =	sevseg_suspend,
	.resume =	sevseg_resume,
	.reset_resume =	sevseg_reset_resume,
	.id_table =	id_table,
	.supports_autosuspend = 1,
};

static int __init usb_sevseg_init(void)