18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drivers/usb/core/endpoint.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (C) Copyright 2002,2004,2006 Greg Kroah-Hartman 68c2ecf20Sopenharmony_ci * (C) Copyright 2002,2004 IBM Corp. 78c2ecf20Sopenharmony_ci * (C) Copyright 2006 Novell Inc. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Released under the GPLv2 only. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * Endpoint sysfs stuff 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/usb.h> 188c2ecf20Sopenharmony_ci#include "usb.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct ep_device { 218c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *desc; 228c2ecf20Sopenharmony_ci struct usb_device *udev; 238c2ecf20Sopenharmony_ci struct device dev; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci#define to_ep_device(_dev) \ 268c2ecf20Sopenharmony_ci container_of(_dev, struct ep_device, dev) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistruct ep_attribute { 298c2ecf20Sopenharmony_ci struct attribute attr; 308c2ecf20Sopenharmony_ci ssize_t (*show)(struct usb_device *, 318c2ecf20Sopenharmony_ci struct usb_endpoint_descriptor *, char *); 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci#define to_ep_attribute(_attr) \ 348c2ecf20Sopenharmony_ci container_of(_attr, struct ep_attribute, attr) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define usb_ep_attr(field, format_string) \ 378c2ecf20Sopenharmony_cistatic ssize_t field##_show(struct device *dev, \ 388c2ecf20Sopenharmony_ci struct device_attribute *attr, \ 398c2ecf20Sopenharmony_ci char *buf) \ 408c2ecf20Sopenharmony_ci{ \ 418c2ecf20Sopenharmony_ci struct ep_device *ep = to_ep_device(dev); \ 428c2ecf20Sopenharmony_ci return sprintf(buf, format_string, ep->desc->field); \ 438c2ecf20Sopenharmony_ci} \ 448c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(field) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ciusb_ep_attr(bLength, "%02x\n"); 478c2ecf20Sopenharmony_ciusb_ep_attr(bEndpointAddress, "%02x\n"); 488c2ecf20Sopenharmony_ciusb_ep_attr(bmAttributes, "%02x\n"); 498c2ecf20Sopenharmony_ciusb_ep_attr(bInterval, "%02x\n"); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic ssize_t wMaxPacketSize_show(struct device *dev, 528c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct ep_device *ep = to_ep_device(dev); 558c2ecf20Sopenharmony_ci return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc)); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(wMaxPacketSize); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic ssize_t type_show(struct device *dev, struct device_attribute *attr, 608c2ecf20Sopenharmony_ci char *buf) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct ep_device *ep = to_ep_device(dev); 638c2ecf20Sopenharmony_ci char *type = "unknown"; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci switch (usb_endpoint_type(ep->desc)) { 668c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_CONTROL: 678c2ecf20Sopenharmony_ci type = "Control"; 688c2ecf20Sopenharmony_ci break; 698c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_ISOC: 708c2ecf20Sopenharmony_ci type = "Isoc"; 718c2ecf20Sopenharmony_ci break; 728c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_BULK: 738c2ecf20Sopenharmony_ci type = "Bulk"; 748c2ecf20Sopenharmony_ci break; 758c2ecf20Sopenharmony_ci case USB_ENDPOINT_XFER_INT: 768c2ecf20Sopenharmony_ci type = "Interrupt"; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", type); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(type); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic ssize_t interval_show(struct device *dev, struct device_attribute *attr, 848c2ecf20Sopenharmony_ci char *buf) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci struct ep_device *ep = to_ep_device(dev); 878c2ecf20Sopenharmony_ci unsigned int interval; 888c2ecf20Sopenharmony_ci char unit; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci interval = usb_decode_interval(ep->desc, ep->udev->speed); 918c2ecf20Sopenharmony_ci if (interval % 1000) { 928c2ecf20Sopenharmony_ci unit = 'u'; 938c2ecf20Sopenharmony_ci } else { 948c2ecf20Sopenharmony_ci unit = 'm'; 958c2ecf20Sopenharmony_ci interval /= 1000; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci return sprintf(buf, "%d%cs\n", interval, unit); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(interval); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic ssize_t direction_show(struct device *dev, struct device_attribute *attr, 1038c2ecf20Sopenharmony_ci char *buf) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct ep_device *ep = to_ep_device(dev); 1068c2ecf20Sopenharmony_ci char *direction; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (usb_endpoint_xfer_control(ep->desc)) 1098c2ecf20Sopenharmony_ci direction = "both"; 1108c2ecf20Sopenharmony_ci else if (usb_endpoint_dir_in(ep->desc)) 1118c2ecf20Sopenharmony_ci direction = "in"; 1128c2ecf20Sopenharmony_ci else 1138c2ecf20Sopenharmony_ci direction = "out"; 1148c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", direction); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(direction); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic struct attribute *ep_dev_attrs[] = { 1198c2ecf20Sopenharmony_ci &dev_attr_bLength.attr, 1208c2ecf20Sopenharmony_ci &dev_attr_bEndpointAddress.attr, 1218c2ecf20Sopenharmony_ci &dev_attr_bmAttributes.attr, 1228c2ecf20Sopenharmony_ci &dev_attr_bInterval.attr, 1238c2ecf20Sopenharmony_ci &dev_attr_wMaxPacketSize.attr, 1248c2ecf20Sopenharmony_ci &dev_attr_interval.attr, 1258c2ecf20Sopenharmony_ci &dev_attr_type.attr, 1268c2ecf20Sopenharmony_ci &dev_attr_direction.attr, 1278c2ecf20Sopenharmony_ci NULL, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_cistatic struct attribute_group ep_dev_attr_grp = { 1308c2ecf20Sopenharmony_ci .attrs = ep_dev_attrs, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_cistatic const struct attribute_group *ep_dev_groups[] = { 1338c2ecf20Sopenharmony_ci &ep_dev_attr_grp, 1348c2ecf20Sopenharmony_ci NULL 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void ep_device_release(struct device *dev) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct ep_device *ep_dev = to_ep_device(dev); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci kfree(ep_dev); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistruct device_type usb_ep_device_type = { 1458c2ecf20Sopenharmony_ci .name = "usb_endpoint", 1468c2ecf20Sopenharmony_ci .release = ep_device_release, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ciint usb_create_ep_devs(struct device *parent, 1508c2ecf20Sopenharmony_ci struct usb_host_endpoint *endpoint, 1518c2ecf20Sopenharmony_ci struct usb_device *udev) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct ep_device *ep_dev; 1548c2ecf20Sopenharmony_ci int retval; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL); 1578c2ecf20Sopenharmony_ci if (!ep_dev) { 1588c2ecf20Sopenharmony_ci retval = -ENOMEM; 1598c2ecf20Sopenharmony_ci goto exit; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ep_dev->desc = &endpoint->desc; 1638c2ecf20Sopenharmony_ci ep_dev->udev = udev; 1648c2ecf20Sopenharmony_ci ep_dev->dev.groups = ep_dev_groups; 1658c2ecf20Sopenharmony_ci ep_dev->dev.type = &usb_ep_device_type; 1668c2ecf20Sopenharmony_ci ep_dev->dev.parent = parent; 1678c2ecf20Sopenharmony_ci dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci retval = device_register(&ep_dev->dev); 1708c2ecf20Sopenharmony_ci if (retval) 1718c2ecf20Sopenharmony_ci goto error_register; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci device_enable_async_suspend(&ep_dev->dev); 1748c2ecf20Sopenharmony_ci endpoint->ep_dev = ep_dev; 1758c2ecf20Sopenharmony_ci return retval; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cierror_register: 1788c2ecf20Sopenharmony_ci put_device(&ep_dev->dev); 1798c2ecf20Sopenharmony_ciexit: 1808c2ecf20Sopenharmony_ci return retval; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_civoid usb_remove_ep_devs(struct usb_host_endpoint *endpoint) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci struct ep_device *ep_dev = endpoint->ep_dev; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (ep_dev) { 1888c2ecf20Sopenharmony_ci device_unregister(&ep_dev->dev); 1898c2ecf20Sopenharmony_ci endpoint->ep_dev = NULL; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci} 192