Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : /* 3 : : * drivers/usb/core/endpoint.c 4 : : * 5 : : * (C) Copyright 2002,2004,2006 Greg Kroah-Hartman 6 : : * (C) Copyright 2002,2004 IBM Corp. 7 : : * (C) Copyright 2006 Novell Inc. 8 : : * 9 : : * Released under the GPLv2 only. 10 : : * 11 : : * Endpoint sysfs stuff 12 : : */ 13 : : 14 : : #include <linux/kernel.h> 15 : : #include <linux/spinlock.h> 16 : : #include <linux/slab.h> 17 : : #include <linux/usb.h> 18 : : #include "usb.h" 19 : : 20 : : struct ep_device { 21 : : struct usb_endpoint_descriptor *desc; 22 : : struct usb_device *udev; 23 : : struct device dev; 24 : : }; 25 : : #define to_ep_device(_dev) \ 26 : : container_of(_dev, struct ep_device, dev) 27 : : 28 : : struct ep_attribute { 29 : : struct attribute attr; 30 : : ssize_t (*show)(struct usb_device *, 31 : : struct usb_endpoint_descriptor *, char *); 32 : : }; 33 : : #define to_ep_attribute(_attr) \ 34 : : container_of(_attr, struct ep_attribute, attr) 35 : : 36 : : #define usb_ep_attr(field, format_string) \ 37 : : static ssize_t field##_show(struct device *dev, \ 38 : : struct device_attribute *attr, \ 39 : : char *buf) \ 40 : : { \ 41 : : struct ep_device *ep = to_ep_device(dev); \ 42 : : return sprintf(buf, format_string, ep->desc->field); \ 43 : : } \ 44 : : static DEVICE_ATTR_RO(field) 45 : : 46 : 0 : usb_ep_attr(bLength, "%02x\n"); 47 : 0 : usb_ep_attr(bEndpointAddress, "%02x\n"); 48 : 0 : usb_ep_attr(bmAttributes, "%02x\n"); 49 : 0 : usb_ep_attr(bInterval, "%02x\n"); 50 : : 51 : 0 : static ssize_t wMaxPacketSize_show(struct device *dev, 52 : : struct device_attribute *attr, char *buf) 53 : : { 54 : : struct ep_device *ep = to_ep_device(dev); 55 : 0 : return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); 56 : : } 57 : : static DEVICE_ATTR_RO(wMaxPacketSize); 58 : : 59 : 2 : static ssize_t type_show(struct device *dev, struct device_attribute *attr, 60 : : char *buf) 61 : : { 62 : : struct ep_device *ep = to_ep_device(dev); 63 : : char *type = "unknown"; 64 : : 65 : 2 : switch (usb_endpoint_type(ep->desc)) { 66 : : case USB_ENDPOINT_XFER_CONTROL: 67 : : type = "Control"; 68 : : break; 69 : : case USB_ENDPOINT_XFER_ISOC: 70 : : type = "Isoc"; 71 : : break; 72 : : case USB_ENDPOINT_XFER_BULK: 73 : : type = "Bulk"; 74 : : break; 75 : : case USB_ENDPOINT_XFER_INT: 76 : : type = "Interrupt"; 77 : : break; 78 : : } 79 : 2 : return sprintf(buf, "%s\n", type); 80 : : } 81 : : static DEVICE_ATTR_RO(type); 82 : : 83 : 0 : static ssize_t interval_show(struct device *dev, struct device_attribute *attr, 84 : : char *buf) 85 : : { 86 : : struct ep_device *ep = to_ep_device(dev); 87 : : char unit; 88 : : unsigned interval = 0; 89 : : unsigned in; 90 : : 91 : 0 : in = (ep->desc->bEndpointAddress & USB_DIR_IN); 92 : : 93 : 0 : switch (usb_endpoint_type(ep->desc)) { 94 : : case USB_ENDPOINT_XFER_CONTROL: 95 : 0 : if (ep->udev->speed == USB_SPEED_HIGH) 96 : : /* uframes per NAK */ 97 : 0 : interval = ep->desc->bInterval; 98 : : break; 99 : : 100 : : case USB_ENDPOINT_XFER_ISOC: 101 : 0 : interval = 1 << (ep->desc->bInterval - 1); 102 : 0 : break; 103 : : 104 : : case USB_ENDPOINT_XFER_BULK: 105 : 0 : if (ep->udev->speed == USB_SPEED_HIGH && !in) 106 : : /* uframes per NAK */ 107 : 0 : interval = ep->desc->bInterval; 108 : : break; 109 : : 110 : : case USB_ENDPOINT_XFER_INT: 111 : 0 : if (ep->udev->speed == USB_SPEED_HIGH) 112 : 0 : interval = 1 << (ep->desc->bInterval - 1); 113 : : else 114 : 0 : interval = ep->desc->bInterval; 115 : : break; 116 : : } 117 : 0 : interval *= (ep->udev->speed == USB_SPEED_HIGH) ? 125 : 1000; 118 : 0 : if (interval % 1000) 119 : : unit = 'u'; 120 : : else { 121 : : unit = 'm'; 122 : 0 : interval /= 1000; 123 : : } 124 : : 125 : 0 : return sprintf(buf, "%d%cs\n", interval, unit); 126 : : } 127 : : static DEVICE_ATTR_RO(interval); 128 : : 129 : 2 : static ssize_t direction_show(struct device *dev, struct device_attribute *attr, 130 : : char *buf) 131 : : { 132 : : struct ep_device *ep = to_ep_device(dev); 133 : : char *direction; 134 : : 135 : 2 : if (usb_endpoint_xfer_control(ep->desc)) 136 : : direction = "both"; 137 : 2 : else if (usb_endpoint_dir_in(ep->desc)) 138 : : direction = "in"; 139 : : else 140 : : direction = "out"; 141 : 2 : return sprintf(buf, "%s\n", direction); 142 : : } 143 : : static DEVICE_ATTR_RO(direction); 144 : : 145 : : static struct attribute *ep_dev_attrs[] = { 146 : : &dev_attr_bLength.attr, 147 : : &dev_attr_bEndpointAddress.attr, 148 : : &dev_attr_bmAttributes.attr, 149 : : &dev_attr_bInterval.attr, 150 : : &dev_attr_wMaxPacketSize.attr, 151 : : &dev_attr_interval.attr, 152 : : &dev_attr_type.attr, 153 : : &dev_attr_direction.attr, 154 : : NULL, 155 : : }; 156 : : static struct attribute_group ep_dev_attr_grp = { 157 : : .attrs = ep_dev_attrs, 158 : : }; 159 : : static const struct attribute_group *ep_dev_groups[] = { 160 : : &ep_dev_attr_grp, 161 : : NULL 162 : : }; 163 : : 164 : 0 : static void ep_device_release(struct device *dev) 165 : : { 166 : 0 : struct ep_device *ep_dev = to_ep_device(dev); 167 : : 168 : 0 : kfree(ep_dev); 169 : 0 : } 170 : : 171 : : struct device_type usb_ep_device_type = { 172 : : .name = "usb_endpoint", 173 : : .release = ep_device_release, 174 : : }; 175 : : 176 : 3 : int usb_create_ep_devs(struct device *parent, 177 : : struct usb_host_endpoint *endpoint, 178 : : struct usb_device *udev) 179 : : { 180 : : struct ep_device *ep_dev; 181 : : int retval; 182 : : 183 : 3 : ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL); 184 : 3 : if (!ep_dev) { 185 : : retval = -ENOMEM; 186 : : goto exit; 187 : : } 188 : : 189 : 3 : ep_dev->desc = &endpoint->desc; 190 : 3 : ep_dev->udev = udev; 191 : 3 : ep_dev->dev.groups = ep_dev_groups; 192 : 3 : ep_dev->dev.type = &usb_ep_device_type; 193 : 3 : ep_dev->dev.parent = parent; 194 : 3 : dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); 195 : : 196 : 3 : retval = device_register(&ep_dev->dev); 197 : 3 : if (retval) 198 : : goto error_register; 199 : : 200 : : device_enable_async_suspend(&ep_dev->dev); 201 : 3 : endpoint->ep_dev = ep_dev; 202 : 3 : return retval; 203 : : 204 : : error_register: 205 : 0 : put_device(&ep_dev->dev); 206 : : exit: 207 : 0 : return retval; 208 : : } 209 : : 210 : 0 : void usb_remove_ep_devs(struct usb_host_endpoint *endpoint) 211 : : { 212 : 0 : struct ep_device *ep_dev = endpoint->ep_dev; 213 : : 214 : 0 : if (ep_dev) { 215 : 0 : device_unregister(&ep_dev->dev); 216 : 0 : endpoint->ep_dev = NULL; 217 : : } 218 : 0 : }