diff options
author | Oliver Neukum <oliver@neukum.org> | 2009-07-13 10:45:47 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-09-23 09:46:28 -0400 |
commit | 4d155eb5f55b879e9947c3553b33764746fb15d5 (patch) | |
tree | 137129d9acdd55b04193d5ae91bf5f1a5a850f81 | |
parent | d9bfbd167b4dac51fed4edde7f6cfc378c9aea98 (diff) |
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: Oliver Neukum <oliver@neukum.org>
Signed-off-by: Harrison Metzger <harrisonmetz@gmail.com>
-rw-r--r-- | drivers/usb/misc/usbsevseg.c | 69 |
1 files changed, 63 insertions, 6 deletions
diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 28a6a3a09538..3db255537e79 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c | |||
@@ -38,6 +38,7 @@ static char *display_textmodes[] = {"raw", "hex", "ascii", NULL}; | |||
38 | 38 | ||
39 | struct usb_sevsegdev { | 39 | struct usb_sevsegdev { |
40 | struct usb_device *udev; | 40 | struct usb_device *udev; |
41 | struct usb_interface *intf; | ||
41 | 42 | ||
42 | u8 powered; | 43 | u8 powered; |
43 | u8 mode_msb; | 44 | u8 mode_msb; |
@@ -46,6 +47,8 @@ struct usb_sevsegdev { | |||
46 | u8 textmode; | 47 | u8 textmode; |
47 | u8 text[MAXLEN]; | 48 | u8 text[MAXLEN]; |
48 | u16 textlength; | 49 | u16 textlength; |
50 | |||
51 | u8 shadow_power; /* for PM */ | ||
49 | }; | 52 | }; |
50 | 53 | ||
51 | /* sysfs_streq can't replace this completely | 54 | /* sysfs_streq can't replace this completely |
@@ -65,6 +68,12 @@ static void update_display_powered(struct usb_sevsegdev *mydev) | |||
65 | { | 68 | { |
66 | int rc; | 69 | int rc; |
67 | 70 | ||
71 | if (!mydev->shadow_power && mydev->powered) { | ||
72 | rc = usb_autopm_get_interface(mydev->intf); | ||
73 | if (rc < 0) | ||
74 | return; | ||
75 | } | ||
76 | |||
68 | rc = usb_control_msg(mydev->udev, | 77 | rc = usb_control_msg(mydev->udev, |
69 | usb_sndctrlpipe(mydev->udev, 0), | 78 | usb_sndctrlpipe(mydev->udev, 0), |
70 | 0x12, | 79 | 0x12, |
@@ -76,12 +85,18 @@ static void update_display_powered(struct usb_sevsegdev *mydev) | |||
76 | 2000); | 85 | 2000); |
77 | if (rc < 0) | 86 | if (rc < 0) |
78 | dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); | 87 | dev_dbg(&mydev->udev->dev, "power retval = %d\n", rc); |
88 | |||
89 | if (mydev->shadow_power && !mydev->powered) | ||
90 | usb_autopm_put_interface(mydev->intf); | ||
79 | } | 91 | } |
80 | 92 | ||
81 | static void update_display_mode(struct usb_sevsegdev *mydev) | 93 | static void update_display_mode(struct usb_sevsegdev *mydev) |
82 | { | 94 | { |
83 | int rc; | 95 | int rc; |
84 | 96 | ||
97 | if(mydev->shadow_power != 1) | ||
98 | return; | ||
99 | |||
85 | rc = usb_control_msg(mydev->udev, | 100 | rc = usb_control_msg(mydev->udev, |
86 | usb_sndctrlpipe(mydev->udev, 0), | 101 | usb_sndctrlpipe(mydev->udev, 0), |
87 | 0x12, | 102 | 0x12, |
@@ -96,14 +111,17 @@ static void update_display_mode(struct usb_sevsegdev *mydev) | |||
96 | dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); | 111 | dev_dbg(&mydev->udev->dev, "mode retval = %d\n", rc); |
97 | } | 112 | } |
98 | 113 | ||
99 | static void update_display_visual(struct usb_sevsegdev *mydev) | 114 | static void update_display_visual(struct usb_sevsegdev *mydev, gfp_t mf) |
100 | { | 115 | { |
101 | int rc; | 116 | int rc; |
102 | int i; | 117 | int i; |
103 | unsigned char *buffer; | 118 | unsigned char *buffer; |
104 | u8 decimals = 0; | 119 | u8 decimals = 0; |
105 | 120 | ||
106 | buffer = kzalloc(MAXLEN, GFP_KERNEL); | 121 | if(mydev->shadow_power != 1) |
122 | return; | ||
123 | |||
124 | buffer = kzalloc(MAXLEN, mf); | ||
107 | if (!buffer) { | 125 | if (!buffer) { |
108 | dev_err(&mydev->udev->dev, "out of memory\n"); | 126 | dev_err(&mydev->udev->dev, "out of memory\n"); |
109 | return; | 127 | return; |
@@ -163,7 +181,7 @@ static ssize_t set_attr_##name(struct device *dev, \ | |||
163 | struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ | 181 | struct usb_sevsegdev *mydev = usb_get_intfdata(intf); \ |
164 | \ | 182 | \ |
165 | mydev->name = simple_strtoul(buf, NULL, 10); \ | 183 | mydev->name = simple_strtoul(buf, NULL, 10); \ |
166 | update_fcn(mydev); \ | 184 | update_fcn(mydev); \ |
167 | \ | 185 | \ |
168 | return count; \ | 186 | return count; \ |
169 | } \ | 187 | } \ |
@@ -194,7 +212,7 @@ static ssize_t set_attr_text(struct device *dev, | |||
194 | if (end > 0) | 212 | if (end > 0) |
195 | memcpy(mydev->text, buf, end); | 213 | memcpy(mydev->text, buf, end); |
196 | 214 | ||
197 | update_display_visual(mydev); | 215 | update_display_visual(mydev, GFP_KERNEL); |
198 | return count; | 216 | return count; |
199 | } | 217 | } |
200 | 218 | ||
@@ -242,7 +260,7 @@ static ssize_t set_attr_decimals(struct device *dev, | |||
242 | if (buf[i] == '1') | 260 | if (buf[i] == '1') |
243 | mydev->decimals[end-1-i] = 1; | 261 | mydev->decimals[end-1-i] = 1; |
244 | 262 | ||
245 | update_display_visual(mydev); | 263 | update_display_visual(mydev, GFP_KERNEL); |
246 | 264 | ||
247 | return count; | 265 | return count; |
248 | } | 266 | } |
@@ -286,7 +304,7 @@ static ssize_t set_attr_textmode(struct device *dev, | |||
286 | for (i = 0; display_textmodes[i]; i++) { | 304 | for (i = 0; display_textmodes[i]; i++) { |
287 | if (sysfs_streq(display_textmodes[i], buf)) { | 305 | if (sysfs_streq(display_textmodes[i], buf)) { |
288 | mydev->textmode = i; | 306 | mydev->textmode = i; |
289 | update_display_visual(mydev); | 307 | update_display_visual(mydev, GFP_KERNEL); |
290 | return count; | 308 | return count; |
291 | } | 309 | } |
292 | } | 310 | } |
@@ -330,6 +348,7 @@ static int sevseg_probe(struct usb_interface *interface, | |||
330 | } | 348 | } |
331 | 349 | ||
332 | mydev->udev = usb_get_dev(udev); | 350 | mydev->udev = usb_get_dev(udev); |
351 | mydev->intf = interface; | ||
333 | usb_set_intfdata(interface, mydev); | 352 | usb_set_intfdata(interface, mydev); |
334 | 353 | ||
335 | /*set defaults */ | 354 | /*set defaults */ |
@@ -364,11 +383,49 @@ static void sevseg_disconnect(struct usb_interface *interface) | |||
364 | dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); | 383 | dev_info(&interface->dev, "USB 7 Segment now disconnected\n"); |
365 | } | 384 | } |
366 | 385 | ||
386 | static int sevseg_suspend(struct usb_interface *intf, pm_message_t message) | ||
387 | { | ||
388 | struct usb_sevsegdev *mydev; | ||
389 | |||
390 | mydev = usb_get_intfdata(intf); | ||
391 | mydev->shadow_power = 0; | ||
392 | |||
393 | return 0; | ||
394 | } | ||
395 | |||
396 | static int sevseg_resume(struct usb_interface *intf) | ||
397 | { | ||
398 | struct usb_sevsegdev *mydev; | ||
399 | |||
400 | mydev = usb_get_intfdata(intf); | ||
401 | mydev->shadow_power = 1; | ||
402 | update_display_mode(mydev); | ||
403 | update_display_visual(mydev, GFP_NOIO); | ||
404 | |||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static int sevseg_reset_resume(struct usb_interface *intf) | ||
409 | { | ||
410 | struct usb_sevsegdev *mydev; | ||
411 | |||
412 | mydev = usb_get_intfdata(intf); | ||
413 | mydev->shadow_power = 1; | ||
414 | update_display_mode(mydev); | ||
415 | update_display_visual(mydev, GFP_NOIO); | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
367 | static struct usb_driver sevseg_driver = { | 420 | static struct usb_driver sevseg_driver = { |
368 | .name = "usbsevseg", | 421 | .name = "usbsevseg", |
369 | .probe = sevseg_probe, | 422 | .probe = sevseg_probe, |
370 | .disconnect = sevseg_disconnect, | 423 | .disconnect = sevseg_disconnect, |
424 | .suspend = sevseg_suspend, | ||
425 | .resume = sevseg_resume, | ||
426 | .reset_resume = sevseg_reset_resume, | ||
371 | .id_table = id_table, | 427 | .id_table = id_table, |
428 | .supports_autosuspend = 1, | ||
372 | }; | 429 | }; |
373 | 430 | ||
374 | static int __init usb_sevseg_init(void) | 431 | static int __init usb_sevseg_init(void) |