USB: add usbfs ioctl to retrieve the connection parameters

Recently usfbs gained availability to retrieve device speed, but there
is sill no way to determine the bus number or list of ports the device
is connected to when using usbfs. While this information can be obtained
from sysfs, not all environments allow sysfs access. In a jailed
environment a program might be simply given an opened file descriptor to
usbfs device, and it is really important that all data can be gathered
from said file descriptor.

This patch introduces a new ioctl, USBDEVFS_CONNINFO_EX, which return
extended connection information for the device, including the bus
number, address, port list and speed. The API allows kernel to extend
amount of data returned by the ioctl and userspace has an option of
adjusting the amount of data it is willing to consume. A new capability,
USBDEVFS_CAP_CONNINFO_EX, is introduced to help userspace in determining
whether the kernel supports this new ioctl.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Šī revīzija ir iekļauta:
Dmitry Torokhov
2019-06-10 15:36:58 -07:00
revīziju iesūtīja Greg Kroah-Hartman
vecāks 1a65a03561
revīzija 6d101f24f1
2 mainīti faili ar 67 papildinājumiem un 1 dzēšanām

Parādīt failu

@@ -1308,6 +1308,39 @@ static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
return 0;
}
static int proc_conninfo_ex(struct usb_dev_state *ps,
void __user *arg, size_t size)
{
struct usbdevfs_conninfo_ex ci;
struct usb_device *udev = ps->dev;
if (size < sizeof(ci.size))
return -EINVAL;
memset(&ci, 0, sizeof(ci));
ci.size = sizeof(ci);
ci.busnum = udev->bus->busnum;
ci.devnum = udev->devnum;
ci.speed = udev->speed;
while (udev && udev->portnum != 0) {
if (++ci.num_ports <= ARRAY_SIZE(ci.ports))
ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports] =
udev->portnum;
udev = udev->parent;
}
if (ci.num_ports < ARRAY_SIZE(ci.ports))
memmove(&ci.ports[0],
&ci.ports[ARRAY_SIZE(ci.ports) - ci.num_ports],
ci.num_ports);
if (copy_to_user(arg, &ci, min(sizeof(ci), size)))
return -EFAULT;
return 0;
}
static int proc_resetdevice(struct usb_dev_state *ps)
{
struct usb_host_config *actconfig = ps->dev->actconfig;
@@ -2250,7 +2283,7 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
USBDEVFS_CAP_DROP_PRIVILEGES;
USBDEVFS_CAP_DROP_PRIVILEGES | USBDEVFS_CAP_CONNINFO_EX;
if (!ps->dev->bus->no_stop_on_short)
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
if (ps->dev->bus->sg_tablesize)
@@ -2549,6 +2582,13 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
break;
}
/* Handle variable-length commands */
switch (cmd & ~IOCSIZE_MASK) {
case USBDEVFS_CONNINFO_EX(0):
ret = proc_conninfo_ex(ps, p, _IOC_SIZE(cmd));
break;
}
done:
usb_unlock_device(dev);
if (ret >= 0)