hopefully final version of full autosuspend for usb hid
PATCH 1/1 hopefully final version of full autosuspend for usb hid
This uses the USB busy mechanism for aggessive autosuspend of USB HID devices. It autosuspends all opened devices supporting remote wakeup after a timeout unless
- output is being done to the device
- a key is being held down (remote wakeup isn't triggered upon key release)
- LED(s) are lit
- hiddev is opened
As in the current driver closed devices will be autosuspended even if they don't support remote wakeup.
The patch is quite large because output to devices is done in hard interrupt context meaning a lot a queuing and locking had to be touched. The LED stuff has been solved by means of a simple counter. Additions to the generic HID code could be avoided. In addition it now covers hidraw. It contains an embryonic version of an API to let the generic HID code tell the lower levels which capabilities with respect to power management are needed.
Regards
Oliver
---
drivers/hid/hid-core.c +16 -0
@@ -1719,6 +1719,22 @@ static DECLARE_WORK(hid_compat_work, hid_compat_load);static struct workqueue_struct *hid_compat_wq;#endif +int hid_check_keys_pressed (struct hid_device *hid) +{ +struct hid_input *hidinput; +int i; + +list_for_each_entry (hidinput, &hid->inputs, list) { +for (i =0 ; i <BITS_TO_LONGS (KEY_MAX ); i++) +if (hidinput->input->key[i]) +return 1 ; + } + +return 0 ; +} + +EXPORT_SYMBOL_GPL (hid_check_keys_pressed); +static int __init hid_init (void ) {int ret;
drivers/hid/hidraw.c +14 -3
@@ -181,9 +181,17 @@ static int hidraw_open(struct inode *inode, struct file *file) dev = hidraw_table[minor];if (!dev->open++) { +if (dev->hid->ll_driver->power) { + err = dev->hid->ll_driver->power (dev->hid,PM_HINT_FULLON ); +if (err <0 ) + goto out_unlock; + } err = dev->hid->ll_driver->open (dev->hid); -if (err <0 ) +if (err <0 ) { +if (dev->hid->ll_driver->power) + dev->hid->ll_driver->power (dev->hid,PM_HINT_NORMAL ); dev->open--; + } } out_unlock:
@@ -209,10 +217,13 @@ static int hidraw_release(struct inode * inode, struct file * file)list_del (&list->node); dev = hidraw_table[minor];if (!--dev->open) { -if (list->hidraw->exist) +if (list->hidraw->exist) { +if (dev->hid->ll_driver->power) + dev->hid->ll_driver->power (dev->hid,PM_HINT_NORMAL ); dev->hid->ll_driver->close (dev->hid); -else + }else {kfree (list->hidraw); + } }kfree (list);
drivers/hid/usbhid/hid-core.c +341 -94
725 lines not on this page · raw message
drivers/hid/usbhid/hiddev.c +12 -4
@@ -247,10 +247,12 @@ static int hiddev_release(struct inode * inode, struct file * file)spin_unlock_irqrestore (&list->hiddev->list_lock, flags);if (!--list->hiddev->open) { -if (list->hiddev->exist) +if (list->hiddev->exist) {usbhid_close (list->hiddev->hid); -else +usbhid_put_power (list->hiddev->hid); + }else {kfree (list->hiddev); + } }kfree (list);
@@ -265,6 +267,7 @@ static int hiddev_open(struct inode *inode, struct file *file) {struct hiddev_list *list;unsigned long flags; +int res;int i =iminor (inode) -HIDDEV_MINOR_BASE ;
@@ -283,8 +286,13 @@ static int hiddev_open(struct inode *inode, struct file *file) file->private_data = list;if (!list->hiddev->open++) -if (list->hiddev->exist) -usbhid_open (hiddev_table[i]->hid); +if (list->hiddev->exist) { +struct hid_device *hid = hiddev_table[i]->hid; + res =usbhid_get_power (hid); +if (res <0 ) +return -EIO ; +usbhid_open (hid); + }return 0 ; }
drivers/hid/usbhid/usbhid.h +7 -4
@@ -38,7 +38,10 @@ int usbhid_wait_io(struct hid_device* hid);void usbhid_close (struct hid_device *hid);int usbhid_open (struct hid_device *hid);void usbhid_init_reports (struct hid_device *hid); -void usbhid_submit_report (struct hid_device *hid,struct hid_report *report,unsigned char dir); +void usbhid_submit_report +(struct hid_device *hid,struct hid_report *report,unsigned char dir); +int usbhid_get_power (struct hid_device *hid); +void usbhid_put_power (struct hid_device *hid);/* * USB-specific HID struct, to be pointed to
@@ -56,7 +59,6 @@ struct usbhid_device {struct urb *urbin;/* Input URB */ char *inbuf;/* Input buffer */ dma_addr_t inbuf_dma;/* Input buffer dma */ -spinlock_t inlock;/* Input fifo spinlock */ struct urb *urbctrl;/* Control URB */ struct usb_ctrlrequest *cr;/* Control request struct */
@@ -65,21 +67,22 @@ struct usbhid_device {unsigned char ctrlhead, ctrltail;/* Control fifo head & tail */ char *ctrlbuf;/* Control buffer */ dma_addr_t ctrlbuf_dma;/* Control buffer dma */ -spinlock_t ctrllock;/* Control fifo spinlock */ struct urb *urbout;/* Output URB */ struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE ];/* Output pipe fifo */ unsigned char outhead, outtail;/* Output pipe fifo head & tail */ char *outbuf;/* Output buffer */ dma_addr_t outbuf_dma;/* Output buffer dma */ -spinlock_t outlock;/* Output fifo spinlock */ +spinlock_t lock;/* fifo spinlock */ unsigned long iofl;/* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ struct timer_list io_retry;/* Retry timer */ unsigned long stop_retry;/* Time to give up, in jiffies */ unsigned int retry_delay;/* Delay length in ms */ struct work_struct reset_work;/* Task context for resets */ +struct work_struct restart_work;/* waking up for output to be done in a task */ wait_queue_head_t wait;/* For sleeping */ +int ledcount;/* counting the number of active leds */ };#define hid_to_usb_dev (hid_dev) \
include/linux/hid.h +9 -0
@@ -411,6 +411,9 @@ struct hid_output_fifo {#define HID_CLEAR_HALT 6#define HID_DISCONNECTED 7#define HID_STARTED 8 +#define HID_REPORTED_IDLE 9 +#define HID_KEYS_PRESSED 10 +#define HID_LED_ON 11struct hid_input {struct list_head list;
@@ -607,12 +610,17 @@ struct hid_ll_driver {int (*open)(struct hid_device *hdev);void (*close)(struct hid_device *hdev); +int (*power)(struct hid_device *hdev,int level); +int (*hidinput_input_event) (struct input_dev *idev,unsigned int type,unsigned int code,int value);int (*parse)(struct hid_device *hdev); }; +#define PM_HINT_FULLON 1<<5 +#define PM_HINT_NORMAL 1<<1 +/* Applications from HID Usage Tables 4/8/99 Version 1.1 */ /* We ignore a few input applications that are not widely used */ #define IS_INPUT_APPLICATION (a) (((a >= 0x00010000) && (a <= 0x00010008)) || (a == 0x00010080) || (a == 0x000c0001) || (a == 0x000d0002))
@@ -645,6 +653,7 @@ int hidinput_find_field(struct hid_device *hid, unsigned int type, unsigned intvoid hid_output_report (struct hid_report *report,__u8 *data);struct hid_device *hid_allocate_device (void );int hid_parse_report (struct hid_device *hid,__u8 *start,unsigned size); +int hid_check_keys_pressed (struct hid_device *hid);int hid_connect (struct hid_device *hid,unsigned int connect_mask);/**
A message that is not in the archive (sent off-list or not yet received): 200812151616.16888.oliver-GvhC2dPhHPQdnm+yROfE0A@public.gmane.org
Quoted 8 lines · Oliver Neukum
This uses the USB busy mechanism for aggessive autosuspend of USB HID devices. It autosuspends all opened devices supporting remote wakeup after a timeout unless
- output is being done to the device
- a key is being held down (remote wakeup isn't triggered upon key release)
- LED(s) are lit
- hiddev is opened
It is also important to note that we always need the leve default to 'on' for HID devices, as it is known to not work properly for many of them (losing keypresses on resume, mice waking up only on button press not on movement, etc), so whitelisting the ones that are known to work properly is the only viable option. Preferably from userspace (HAL).
I don't have any objections to this version of the patch, thanks a lot Oliver for taking care to fix and respin so many times so far.
I have now pulled the 'autosuspend' branch into the branch that gets pulled into -next.
Signature · 6 lines
Jiri Kosina SUSE Labs -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html