[PATCH] USB power usage reporting

Matt Burke mattblists at icritical.com
Thu Feb 14 11:33:50 UTC 2013

The following patch adds the ability to read power draw on usb devices.

I have used ioctl 135. I don't know what the protocol is for assigning
numbers, so this may be unacceptable?

ugen is patched to export the data via ioctl
libusb20 and usbconfig are patched to make use of it (end-of-line):

[mattb at falcon ~]$ usbconfig  
ugen0.1: <EHCI root HUB Intel> at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (0mA)
ugen1.1: <XHCI root HUB 0x1b21> at usbus1, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA)
ugen2.1: <XHCI root HUB 0x1b21> at usbus2, cfg=0 md=HOST spd=SUPER (5.0Gbps) pwr=SAVE (0mA)
ugen3.1: <EHCI root HUB Intel> at usbus3, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (0mA)
ugen0.2: <product 0x0024 vendor 0x8087> at usbus0, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (0mA)
ugen3.2: <product 0x0024 vendor 0x8087> at usbus3, cfg=0 md=HOST spd=HIGH (480Mbps) pwr=SAVE (0mA)
ugen3.3: <Dell USB Keyboard Dell> at usbus3, cfg=0 md=HOST spd=LOW (1.5Mbps) pwr=ON (70mA)
ugen3.4: <Gaming Mouse G400 Logitech> at usbus3, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (98mA)
ugen3.5: <Dell 1130 Laser Printer Dell> at usbus3, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (0mA)
ugen3.6: <product 0x3000 vendor 0x0cf3> at usbus3, cfg=0 md=HOST spd=FULL (12Mbps) pwr=ON (100mA)

diff --git a/lib/libusb/libusb20.3 b/lib/libusb/libusb20.3
index af80c6c..8753e06 100644
--- a/lib/libusb/libusb20.3
+++ b/lib/libusb/libusb20.3
@@ -149,6 +149,8 @@ USB access library (libusb -lusb)
 .Fn libusb20_dev_set_power_mode "struct libusb20_device *pdev" "uint8_t power_mode"
 .Ft uint8_t
 .Fn libusb20_dev_get_power_mode "struct libusb20_device *pdev"
+.Ft uint16_t
+.Fn libusb20_dev_get_power_usage "struct libusb20_device *pdev"
 .Ft int
 .Fn libusb20_dev_set_alt_index "struct libusb20_device *pdev" "uint8_t iface_index" "uint8_t alt_index"
@@ -740,6 +742,11 @@ USB device.
+.Fn libusb20_dev_get_power_usage
+returns the reported power usage in milliamps for the given USB device.
 .Fn libusb20_dev_set_alt_index
 will try to set the given alternate index for the given
 USB interface index.
diff --git a/lib/libusb/libusb20.c b/lib/libusb/libusb20.c
index aa45991..ce75511 100644
--- a/lib/libusb/libusb20.c
+++ b/lib/libusb/libusb20.c
@@ -71,6 +71,7 @@ dummy_callback(struct libusb20_transfer *xfer)
 #define        dummy_check_connected (void *)dummy_int
 #define        dummy_set_power_mode (void *)dummy_int
 #define        dummy_get_power_mode (void *)dummy_int
+#define        dummy_get_power_usage (void *)dummy_int
 #define        dummy_kernel_driver_active (void *)dummy_int
 #define        dummy_detach_kernel_driver (void *)dummy_int
 #define        dummy_do_request_sync (void *)dummy_int
@@ -717,6 +718,18 @@ libusb20_dev_get_power_mode(struct libusb20_device *pdev)
        return (power_mode);
+libusb20_dev_get_power_usage(struct libusb20_device *pdev)
+       int error;
+       uint16_t power_usage;
+       error = pdev->methods->get_power_usage(pdev, &power_usage);
+       if (error)
+               power_usage = 0;
+       return (power_usage);
 libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex)
diff --git a/lib/libusb/libusb20.h b/lib/libusb/libusb20.h
index 87e0572..81928b1 100644
--- a/lib/libusb/libusb20.h
+++ b/lib/libusb/libusb20.h
@@ -255,6 +255,7 @@ int libusb20_dev_reset(struct libusb20_device *pdev);
 int    libusb20_dev_check_connected(struct libusb20_device *pdev);
 int    libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode);
 uint8_t        libusb20_dev_get_power_mode(struct libusb20_device *pdev);
+uint16_t       libusb20_dev_get_power_usage(struct libusb20_device *pdev);
 int    libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index);
 int    libusb20_dev_get_info(struct libusb20_device *pdev, struct usb_device_info *pinfo);
 int    libusb20_dev_get_iface_desc(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len);
diff --git a/lib/libusb/libusb20_int.h b/lib/libusb/libusb20_int.h
index 0251c5f..6705c63 100644
--- a/lib/libusb/libusb20_int.h
+++ b/lib/libusb/libusb20_int.h
@@ -105,6 +105,7 @@ typedef int (libusb20_process_t)(struct libusb20_device *pdev);
 typedef int (libusb20_reset_device_t)(struct libusb20_device *pdev);
 typedef int (libusb20_set_power_mode_t)(struct libusb20_device *pdev, uint8_t power_mode);
 typedef int (libusb20_get_power_mode_t)(struct libusb20_device *pdev, uint8_t *power_mode);
+typedef int (libusb20_get_power_usage_t)(struct libusb20_device *pdev, uint16_t *power_usage);
 typedef int (libusb20_set_alt_index_t)(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index);
 typedef int (libusb20_set_config_index_t)(struct libusb20_device *pdev, uint8_t index);
 typedef int (libusb20_check_connected_t)(struct libusb20_device *pdev);
@@ -127,6 +128,7 @@ typedef void (libusb20_tr_cancel_async_t)(struct libusb20_transfer *xfer);
   m(n, check_connected) \
   m(n, set_power_mode) \
   m(n, get_power_mode) \
+  m(n, get_power_usage) \
   m(n, set_alt_index) \
   m(n, set_config_index) \
   m(n, tr_cancel_async) \
diff --git a/lib/libusb/libusb20_ugen20.c b/lib/libusb/libusb20_ugen20.c
index 2c67778..bff8e02 100644
--- a/lib/libusb/libusb20_ugen20.c
+++ b/lib/libusb/libusb20_ugen20.c
@@ -69,6 +69,7 @@ static libusb20_reset_device_t ugen20_reset_device;
 static libusb20_check_connected_t ugen20_check_connected;
 static libusb20_set_power_mode_t ugen20_set_power_mode;
 static libusb20_get_power_mode_t ugen20_get_power_mode;
+static libusb20_get_power_usage_t ugen20_get_power_usage;
 static libusb20_kernel_driver_active_t ugen20_kernel_driver_active;
 static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver;
 static libusb20_do_request_sync_t ugen20_do_request_sync;
@@ -639,6 +640,18 @@ ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode)
 static int
+ugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage)
+       int temp;
+       if (ioctl(pdev->file_ctrl, USB_GET_POWER_USAGE, &temp)) {
+               return (LIBUSB20_ERROR_OTHER);
+       }
+       *power_usage = temp;
+       return (0);                     /* success */
+static int
 ugen20_kernel_driver_active(struct libusb20_device *pdev,
     uint8_t iface_index)
diff --git a/sys/dev/usb/usb_generic.c b/sys/dev/usb/usb_generic.c
index 13f5bd6..ff1c47d 100644
--- a/sys/dev/usb/usb_generic.c
+++ b/sys/dev/usb/usb_generic.c
@@ -1834,6 +1834,17 @@ ugen_get_power_mode(struct usb_fifo *f)
 static int
+ugen_get_power_usage(struct usb_fifo *f)
+       struct usb_device *udev = f->udev;
+       if (udev == NULL)
+               return (0);
+       return (udev->power);
+static int
 ugen_do_port_feature(struct usb_fifo *f, uint8_t port_no,
     uint8_t set, uint16_t feature)
@@ -2195,6 +2206,10 @@ ugen_ioctl_post(struct usb_fifo *f, u_long cmd, void *addr, int fflags)
                *u.pint = ugen_get_power_mode(f);
+       case USB_GET_POWER_USAGE:
+               *u.pint = ugen_get_power_usage(f);
+               break;
        case USB_SET_PORT_ENABLE:
                error = ugen_do_port_feature(f,
                    *u.pint, 1, UHF_PORT_ENABLE);
diff --git a/sys/dev/usb/usb_ioctl.h b/sys/dev/usb/usb_ioctl.h
index 1d9b223..979240f 100644
--- a/sys/dev/usb/usb_ioctl.h
+++ b/sys/dev/usb/usb_ioctl.h
@@ -277,7 +277,8 @@ struct usb_gen_quirk {
 #define        USB_IFACE_DRIVER_DETACH _IOW ('U', 125, int)
 #define        USB_GET_PLUGTIME        _IOR ('U', 126, uint32_t)
 #define        USB_READ_DIR            _IOW ('U', 127, struct usb_read_dir)
-/* 128 - 135 unused */
+/* 128 - 134 unused */
+#define        USB_GET_POWER_USAGE     _IOR ('U', 135, int)
 #define        USB_SET_TX_FORCE_SHORT  _IOW ('U', 136, int)
 #define        USB_SET_TX_TIMEOUT      _IOW ('U', 137, int)
 #define        USB_GET_TX_FRAME_SIZE   _IOR ('U', 138, int)
diff --git a/usr.sbin/usbconfig/dump.c b/usr.sbin/usbconfig/dump.c
index 7ee89da..467c0de 100644
--- a/usr.sbin/usbconfig/dump.c
+++ b/usr.sbin/usbconfig/dump.c
@@ -226,12 +226,13 @@ dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv)
        char buf[128];
        uint8_t n;
-       printf("%s, cfg=%u md=%s spd=%s pwr=%s\n",
+       printf("%s, cfg=%u md=%s spd=%s pwr=%s (%dmA)\n",
-           dump_power_mode(libusb20_dev_get_power_mode(pdev)));
+           dump_power_mode(libusb20_dev_get_power_mode(pdev)),
+           libusb20_dev_get_power_usage(pdev));
        if (!show_ifdrv)

Sorry for the following...
The information contained in this message is confidential and intended for the addressee only. If you have received this message in error, or there are any problems with its content, please contact the sender. 

iCritical is a trading name of Critical Software Ltd. Registered in England: 04909220.
Registered Office: IC2, Keele Science Park, Keele, Staffordshire, ST5 5NH.

This message has been scanned for security threats by iCritical. www.icritical.com

More information about the freebsd-current mailing list