xref: /openbmc/linux/drivers/usb/core/endpoint.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
1aa1f3bb5SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
284412f62SGreg Kroah-Hartman /*
384412f62SGreg Kroah-Hartman  * drivers/usb/core/endpoint.c
484412f62SGreg Kroah-Hartman  *
584412f62SGreg Kroah-Hartman  * (C) Copyright 2002,2004,2006 Greg Kroah-Hartman
684412f62SGreg Kroah-Hartman  * (C) Copyright 2002,2004 IBM Corp.
784412f62SGreg Kroah-Hartman  * (C) Copyright 2006 Novell Inc.
884412f62SGreg Kroah-Hartman  *
9b65fba3dSGreg Kroah-Hartman  * Released under the GPLv2 only.
1084412f62SGreg Kroah-Hartman  *
11b65fba3dSGreg Kroah-Hartman  * Endpoint sysfs stuff
1284412f62SGreg Kroah-Hartman  */
1384412f62SGreg Kroah-Hartman 
1484412f62SGreg Kroah-Hartman #include <linux/kernel.h>
157e27780fSSarah Bailey #include <linux/spinlock.h>
165a0e3ad6STejun Heo #include <linux/slab.h>
1784412f62SGreg Kroah-Hartman #include <linux/usb.h>
1884412f62SGreg Kroah-Hartman #include "usb.h"
1984412f62SGreg Kroah-Hartman 
209bde7497SGreg Kroah-Hartman struct ep_device {
2184412f62SGreg Kroah-Hartman 	struct usb_endpoint_descriptor *desc;
2284412f62SGreg Kroah-Hartman 	struct usb_device *udev;
239bde7497SGreg Kroah-Hartman 	struct device dev;
2484412f62SGreg Kroah-Hartman };
259bde7497SGreg Kroah-Hartman #define to_ep_device(_dev) \
269bde7497SGreg Kroah-Hartman 	container_of(_dev, struct ep_device, dev)
2784412f62SGreg Kroah-Hartman 
2884412f62SGreg Kroah-Hartman struct ep_attribute {
2984412f62SGreg Kroah-Hartman 	struct attribute attr;
3084412f62SGreg Kroah-Hartman 	ssize_t (*show)(struct usb_device *,
3184412f62SGreg Kroah-Hartman 			struct usb_endpoint_descriptor *, char *);
3284412f62SGreg Kroah-Hartman };
3384412f62SGreg Kroah-Hartman #define to_ep_attribute(_attr) \
3484412f62SGreg Kroah-Hartman 	container_of(_attr, struct ep_attribute, attr)
3584412f62SGreg Kroah-Hartman 
3684412f62SGreg Kroah-Hartman #define usb_ep_attr(field, format_string)			\
37d03f254fSGreg Kroah-Hartman static ssize_t field##_show(struct device *dev,			\
389bde7497SGreg Kroah-Hartman 			       struct device_attribute *attr,	\
3984412f62SGreg Kroah-Hartman 			       char *buf)			\
4084412f62SGreg Kroah-Hartman {								\
419bde7497SGreg Kroah-Hartman 	struct ep_device *ep = to_ep_device(dev);		\
429bde7497SGreg Kroah-Hartman 	return sprintf(buf, format_string, ep->desc->field);	\
4384412f62SGreg Kroah-Hartman }								\
44d03f254fSGreg Kroah-Hartman static DEVICE_ATTR_RO(field)
4584412f62SGreg Kroah-Hartman 
46d03f254fSGreg Kroah-Hartman usb_ep_attr(bLength, "%02x\n");
47d03f254fSGreg Kroah-Hartman usb_ep_attr(bEndpointAddress, "%02x\n");
48d03f254fSGreg Kroah-Hartman usb_ep_attr(bmAttributes, "%02x\n");
49d03f254fSGreg Kroah-Hartman usb_ep_attr(bInterval, "%02x\n");
5084412f62SGreg Kroah-Hartman 
wMaxPacketSize_show(struct device * dev,struct device_attribute * attr,char * buf)51d03f254fSGreg Kroah-Hartman static ssize_t wMaxPacketSize_show(struct device *dev,
529bde7497SGreg Kroah-Hartman 				   struct device_attribute *attr, char *buf)
5384412f62SGreg Kroah-Hartman {
549bde7497SGreg Kroah-Hartman 	struct ep_device *ep = to_ep_device(dev);
555f9492ffSFelipe Balbi 	return sprintf(buf, "%04x\n", usb_endpoint_maxp(ep->desc));
5684412f62SGreg Kroah-Hartman }
57d03f254fSGreg Kroah-Hartman static DEVICE_ATTR_RO(wMaxPacketSize);
5884412f62SGreg Kroah-Hartman 
type_show(struct device * dev,struct device_attribute * attr,char * buf)59d03f254fSGreg Kroah-Hartman static ssize_t type_show(struct device *dev, struct device_attribute *attr,
609bde7497SGreg Kroah-Hartman 			 char *buf)
6184412f62SGreg Kroah-Hartman {
629bde7497SGreg Kroah-Hartman 	struct ep_device *ep = to_ep_device(dev);
6384412f62SGreg Kroah-Hartman 	char *type = "unknown";
6484412f62SGreg Kroah-Hartman 
652e0fe709SJulia Lawall 	switch (usb_endpoint_type(ep->desc)) {
6684412f62SGreg Kroah-Hartman 	case USB_ENDPOINT_XFER_CONTROL:
6784412f62SGreg Kroah-Hartman 		type = "Control";
6884412f62SGreg Kroah-Hartman 		break;
6984412f62SGreg Kroah-Hartman 	case USB_ENDPOINT_XFER_ISOC:
7084412f62SGreg Kroah-Hartman 		type = "Isoc";
7184412f62SGreg Kroah-Hartman 		break;
7284412f62SGreg Kroah-Hartman 	case USB_ENDPOINT_XFER_BULK:
7384412f62SGreg Kroah-Hartman 		type = "Bulk";
7484412f62SGreg Kroah-Hartman 		break;
7584412f62SGreg Kroah-Hartman 	case USB_ENDPOINT_XFER_INT:
7684412f62SGreg Kroah-Hartman 		type = "Interrupt";
7784412f62SGreg Kroah-Hartman 		break;
7884412f62SGreg Kroah-Hartman 	}
7984412f62SGreg Kroah-Hartman 	return sprintf(buf, "%s\n", type);
8084412f62SGreg Kroah-Hartman }
81d03f254fSGreg Kroah-Hartman static DEVICE_ATTR_RO(type);
8284412f62SGreg Kroah-Hartman 
interval_show(struct device * dev,struct device_attribute * attr,char * buf)83d03f254fSGreg Kroah-Hartman static ssize_t interval_show(struct device *dev, struct device_attribute *attr,
84d03f254fSGreg Kroah-Hartman 			     char *buf)
8584412f62SGreg Kroah-Hartman {
869bde7497SGreg Kroah-Hartman 	struct ep_device *ep = to_ep_device(dev);
87*fb95c7cfSChunfeng Yun 	unsigned int interval;
8884412f62SGreg Kroah-Hartman 	char unit;
8984412f62SGreg Kroah-Hartman 
90*fb95c7cfSChunfeng Yun 	interval = usb_decode_interval(ep->desc, ep->udev->speed);
91*fb95c7cfSChunfeng Yun 	if (interval % 1000) {
9284412f62SGreg Kroah-Hartman 		unit = 'u';
93*fb95c7cfSChunfeng Yun 	} else {
9484412f62SGreg Kroah-Hartman 		unit = 'm';
9584412f62SGreg Kroah-Hartman 		interval /= 1000;
9684412f62SGreg Kroah-Hartman 	}
9784412f62SGreg Kroah-Hartman 
9884412f62SGreg Kroah-Hartman 	return sprintf(buf, "%d%cs\n", interval, unit);
9984412f62SGreg Kroah-Hartman }
100d03f254fSGreg Kroah-Hartman static DEVICE_ATTR_RO(interval);
10184412f62SGreg Kroah-Hartman 
direction_show(struct device * dev,struct device_attribute * attr,char * buf)102d03f254fSGreg Kroah-Hartman static ssize_t direction_show(struct device *dev, struct device_attribute *attr,
103d03f254fSGreg Kroah-Hartman 			      char *buf)
10484412f62SGreg Kroah-Hartman {
1059bde7497SGreg Kroah-Hartman 	struct ep_device *ep = to_ep_device(dev);
10684412f62SGreg Kroah-Hartman 	char *direction;
10784412f62SGreg Kroah-Hartman 
1082e0fe709SJulia Lawall 	if (usb_endpoint_xfer_control(ep->desc))
10984412f62SGreg Kroah-Hartman 		direction = "both";
1102e0fe709SJulia Lawall 	else if (usb_endpoint_dir_in(ep->desc))
11184412f62SGreg Kroah-Hartman 		direction = "in";
11284412f62SGreg Kroah-Hartman 	else
11384412f62SGreg Kroah-Hartman 		direction = "out";
11484412f62SGreg Kroah-Hartman 	return sprintf(buf, "%s\n", direction);
11584412f62SGreg Kroah-Hartman }
116d03f254fSGreg Kroah-Hartman static DEVICE_ATTR_RO(direction);
11784412f62SGreg Kroah-Hartman 
1189bde7497SGreg Kroah-Hartman static struct attribute *ep_dev_attrs[] = {
1199bde7497SGreg Kroah-Hartman 	&dev_attr_bLength.attr,
1209bde7497SGreg Kroah-Hartman 	&dev_attr_bEndpointAddress.attr,
1219bde7497SGreg Kroah-Hartman 	&dev_attr_bmAttributes.attr,
1229bde7497SGreg Kroah-Hartman 	&dev_attr_bInterval.attr,
1239bde7497SGreg Kroah-Hartman 	&dev_attr_wMaxPacketSize.attr,
1249bde7497SGreg Kroah-Hartman 	&dev_attr_interval.attr,
1259bde7497SGreg Kroah-Hartman 	&dev_attr_type.attr,
1269bde7497SGreg Kroah-Hartman 	&dev_attr_direction.attr,
12784412f62SGreg Kroah-Hartman 	NULL,
12884412f62SGreg Kroah-Hartman };
1294154a4f7SRikard Falkeborn static const struct attribute_group ep_dev_attr_grp = {
1309bde7497SGreg Kroah-Hartman 	.attrs = ep_dev_attrs,
13184412f62SGreg Kroah-Hartman };
132a4dbd674SDavid Brownell static const struct attribute_group *ep_dev_groups[] = {
1332e5f10e4SAlan Stern 	&ep_dev_attr_grp,
1342e5f10e4SAlan Stern 	NULL
1352e5f10e4SAlan Stern };
13684412f62SGreg Kroah-Hartman 
ep_device_release(struct device * dev)1379bde7497SGreg Kroah-Hartman static void ep_device_release(struct device *dev)
1389bde7497SGreg Kroah-Hartman {
1399bde7497SGreg Kroah-Hartman 	struct ep_device *ep_dev = to_ep_device(dev);
1409bde7497SGreg Kroah-Hartman 
1419bde7497SGreg Kroah-Hartman 	kfree(ep_dev);
1429bde7497SGreg Kroah-Hartman }
14384412f62SGreg Kroah-Hartman 
1442d366846SLan Tianyu struct device_type usb_ep_device_type = {
1452d366846SLan Tianyu 	.name =		"usb_endpoint",
1462d366846SLan Tianyu 	.release = ep_device_release,
1472d366846SLan Tianyu };
1482d366846SLan Tianyu 
usb_create_ep_devs(struct device * parent,struct usb_host_endpoint * endpoint,struct usb_device * udev)1493b23dd6fSAlan Stern int usb_create_ep_devs(struct device *parent,
15084412f62SGreg Kroah-Hartman 			struct usb_host_endpoint *endpoint,
15184412f62SGreg Kroah-Hartman 			struct usb_device *udev)
15284412f62SGreg Kroah-Hartman {
1539bde7497SGreg Kroah-Hartman 	struct ep_device *ep_dev;
1549bde7497SGreg Kroah-Hartman 	int retval;
15584412f62SGreg Kroah-Hartman 
1569bde7497SGreg Kroah-Hartman 	ep_dev = kzalloc(sizeof(*ep_dev), GFP_KERNEL);
1579bde7497SGreg Kroah-Hartman 	if (!ep_dev) {
1589bde7497SGreg Kroah-Hartman 		retval = -ENOMEM;
15955129666SKay Sievers 		goto exit;
1607e27780fSSarah Bailey 	}
1619bde7497SGreg Kroah-Hartman 
1629bde7497SGreg Kroah-Hartman 	ep_dev->desc = &endpoint->desc;
1639bde7497SGreg Kroah-Hartman 	ep_dev->udev = udev;
1642e5f10e4SAlan Stern 	ep_dev->dev.groups = ep_dev_groups;
16555129666SKay Sievers 	ep_dev->dev.type = &usb_ep_device_type;
1669bde7497SGreg Kroah-Hartman 	ep_dev->dev.parent = parent;
16755129666SKay Sievers 	dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
1689bde7497SGreg Kroah-Hartman 
1699bde7497SGreg Kroah-Hartman 	retval = device_register(&ep_dev->dev);
1709bde7497SGreg Kroah-Hartman 	if (retval)
17155129666SKay Sievers 		goto error_register;
1729bde7497SGreg Kroah-Hartman 
17395622712SPeter Chen 	device_enable_async_suspend(&ep_dev->dev);
174d5477c11SAlan Stern 	endpoint->ep_dev = ep_dev;
1751b21d5e1SGreg Kroah-Hartman 	return retval;
1761b21d5e1SGreg Kroah-Hartman 
177d5477c11SAlan Stern error_register:
1787b3a766cSRahul Ruikar 	put_device(&ep_dev->dev);
179d5477c11SAlan Stern exit:
1801b21d5e1SGreg Kroah-Hartman 	return retval;
18184412f62SGreg Kroah-Hartman }
18284412f62SGreg Kroah-Hartman 
usb_remove_ep_devs(struct usb_host_endpoint * endpoint)1833b23dd6fSAlan Stern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
18484412f62SGreg Kroah-Hartman {
1857e27780fSSarah Bailey 	struct ep_device *ep_dev = endpoint->ep_dev;
18684412f62SGreg Kroah-Hartman 
1877e27780fSSarah Bailey 	if (ep_dev) {
1887e27780fSSarah Bailey 		device_unregister(&ep_dev->dev);
1899bde7497SGreg Kroah-Hartman 		endpoint->ep_dev = NULL;
19084412f62SGreg Kroah-Hartman 	}
191c40fd5eaSAlan Stern }
192