xref: /openbmc/u-boot/drivers/usb/emul/sandbox_hub.c (revision fd1e959e)
1 /*
2  * (C) Copyright 2015 Google, Inc
3  * Written by Simon Glass <sjg@chromium.org>
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include <common.h>
9 #include <dm.h>
10 #include <usb.h>
11 #include <dm/device-internal.h>
12 
13 DECLARE_GLOBAL_DATA_PTR;
14 
15 /* We only support up to 8 */
16 #define SANDBOX_NUM_PORTS	4
17 
18 struct sandbox_hub_platdata {
19 	struct usb_dev_platdata plat;
20 	int port;	/* Port number (numbered from 0) */
21 };
22 
23 enum {
24 	STRING_MANUFACTURER = 1,
25 	STRING_PRODUCT,
26 	STRING_SERIAL,
27 
28 	STRING_count,
29 };
30 
31 static struct usb_string hub_strings[] = {
32 	{STRING_MANUFACTURER,	"sandbox"},
33 	{STRING_PRODUCT,	"hub"},
34 	{STRING_SERIAL,		"2345"},
35 	{},
36 };
37 
38 static struct usb_device_descriptor hub_device_desc = {
39 	.bLength =		sizeof(hub_device_desc),
40 	.bDescriptorType =	USB_DT_DEVICE,
41 
42 	.bcdUSB =		__constant_cpu_to_le16(0x0200),
43 
44 	.bDeviceClass =		USB_CLASS_HUB,
45 	.bDeviceSubClass =	0,
46 	.bDeviceProtocol =	0,
47 
48 	.idVendor =		__constant_cpu_to_le16(0x1234),
49 	.idProduct =		__constant_cpu_to_le16(0x5678),
50 	.iManufacturer =	STRING_MANUFACTURER,
51 	.iProduct =		STRING_PRODUCT,
52 	.iSerialNumber =	STRING_SERIAL,
53 	.bNumConfigurations =	1,
54 };
55 
56 static struct usb_config_descriptor hub_config1 = {
57 	.bLength		= sizeof(hub_config1),
58 	.bDescriptorType	= USB_DT_CONFIG,
59 
60 	/* wTotalLength is set up by usb-emul-uclass */
61 	.bNumInterfaces		= 1,
62 	.bConfigurationValue	= 0,
63 	.iConfiguration		= 0,
64 	.bmAttributes		= 1 << 7,
65 	.bMaxPower		= 50,
66 };
67 
68 static struct usb_interface_descriptor hub_interface0 = {
69 	.bLength		= sizeof(hub_interface0),
70 	.bDescriptorType	= USB_DT_INTERFACE,
71 
72 	.bInterfaceNumber	= 0,
73 	.bAlternateSetting	= 0,
74 	.bNumEndpoints		= 1,
75 	.bInterfaceClass	= USB_CLASS_HUB,
76 	.bInterfaceSubClass	= 0,
77 	.bInterfaceProtocol	= US_PR_CB,
78 	.iInterface		= 0,
79 };
80 
81 static struct usb_endpoint_descriptor hub_endpoint0_in = {
82 	.bLength		= USB_DT_ENDPOINT_SIZE,
83 	.bDescriptorType	= USB_DT_ENDPOINT,
84 
85 	.bEndpointAddress	= 1 | USB_DIR_IN,
86 	.bmAttributes		= USB_ENDPOINT_XFER_INT,
87 	.wMaxPacketSize		= __constant_cpu_to_le16(1024),
88 	.bInterval		= 0,
89 };
90 
91 static struct usb_hub_descriptor hub_desc = {
92 	.bLength		= sizeof(hub_desc),
93 	.bDescriptorType	= USB_DT_HUB,
94 	.bNbrPorts		= SANDBOX_NUM_PORTS,
95 	.wHubCharacteristics	= __constant_cpu_to_le16(1 << 0 | 1 << 3 |
96 								1 << 7),
97 	.bPwrOn2PwrGood		= 2,
98 	.bHubContrCurrent	= 5,
99 	{
100 		{
101 			/* all ports removeable */
102 			.DeviceRemovable	= {0, 0xff}
103 		}
104 	}
105 #if SANDBOX_NUM_PORTS > 8
106 #error "This code sets up an incorrect mask"
107 #endif
108 };
109 
110 static void *hub_desc_list[] = {
111 	&hub_device_desc,
112 	&hub_config1,
113 	&hub_interface0,
114 	&hub_endpoint0_in,
115 	&hub_desc,
116 	NULL,
117 };
118 
119 struct sandbox_hub_priv {
120 	int status[SANDBOX_NUM_PORTS];
121 	int change[SANDBOX_NUM_PORTS];
122 };
123 
124 static struct udevice *hub_find_device(struct udevice *hub, int port)
125 {
126 	struct udevice *dev;
127 
128 	for (device_find_first_child(hub, &dev);
129 	     dev;
130 	     device_find_next_child(&dev)) {
131 		struct sandbox_hub_platdata *plat;
132 
133 		plat = dev_get_parent_platdata(dev);
134 		if (plat->port == port)
135 			return dev;
136 	}
137 
138 	return NULL;
139 }
140 
141 static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
142 {
143 	struct sandbox_hub_priv *priv = dev_get_priv(hub);
144 	int *status = &priv->status[port];
145 	int *change = &priv->change[port];
146 	int ret = 0;
147 
148 	if ((clear | set) & USB_PORT_STAT_POWER) {
149 		struct udevice *dev = hub_find_device(hub, port);
150 
151 		if (dev) {
152 			if (set & USB_PORT_STAT_POWER) {
153 				ret = device_probe(dev);
154 				debug("%s: %s: power on, probed, ret=%d\n",
155 				      __func__, dev->name, ret);
156 				if (!ret) {
157 					set |= USB_PORT_STAT_CONNECTION |
158 						USB_PORT_STAT_ENABLE;
159 				}
160 
161 			} else if (clear & USB_PORT_STAT_POWER) {
162 				debug("%s: %s: power off, removed, ret=%d\n",
163 				      __func__, dev->name, ret);
164 				ret = device_remove(dev, DM_REMOVE_NORMAL);
165 				clear |= USB_PORT_STAT_CONNECTION;
166 			}
167 		}
168 	}
169 	*change |= *status & clear;
170 	*change |= ~*status & set;
171 	*change &= 0x1f;
172 	*status = (*status & ~clear) | set;
173 
174 	return ret;
175 }
176 
177 static int sandbox_hub_submit_control_msg(struct udevice *bus,
178 					  struct usb_device *udev,
179 					  unsigned long pipe,
180 					  void *buffer, int length,
181 					  struct devrequest *setup)
182 {
183 	struct sandbox_hub_priv *priv = dev_get_priv(bus);
184 	int ret = 0;
185 
186 	if (pipe == usb_rcvctrlpipe(udev, 0)) {
187 		switch (setup->requesttype) {
188 		case USB_RT_HUB | USB_DIR_IN:
189 			switch (setup->request) {
190 			case USB_REQ_GET_STATUS: {
191 				struct usb_hub_status *hubsts = buffer;
192 
193 				hubsts->wHubStatus = 0;
194 				hubsts->wHubChange = 0;
195 				udev->status = 0;
196 				udev->act_len = sizeof(*hubsts);
197 				return 0;
198 			}
199 			default:
200 				debug("%s: rx ctl requesttype=%x, request=%x\n",
201 				      __func__, setup->requesttype,
202 				      setup->request);
203 				break;
204 			}
205 		case USB_RT_PORT | USB_DIR_IN:
206 			switch (setup->request) {
207 			case USB_REQ_GET_STATUS: {
208 				struct usb_port_status *portsts = buffer;
209 				int port;
210 
211 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
212 				portsts->wPortStatus = priv->status[port];
213 				portsts->wPortChange = priv->change[port];
214 				udev->status = 0;
215 				udev->act_len = sizeof(*portsts);
216 				return 0;
217 			}
218 			}
219 		default:
220 			debug("%s: rx ctl requesttype=%x, request=%x\n",
221 			      __func__, setup->requesttype, setup->request);
222 			break;
223 		}
224 	} else if (pipe == usb_sndctrlpipe(udev, 0)) {
225 		switch (setup->requesttype) {
226 		case USB_RT_PORT:
227 			switch (setup->request) {
228 			case USB_REQ_SET_FEATURE: {
229 				int port;
230 
231 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
232 				debug("set feature port=%x, feature=%x\n",
233 				      port, setup->value);
234 				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
235 					ret = clrset_post_state(bus, port, 0,
236 							1 << setup->value);
237 				} else {
238 					debug("  ** Invalid feature\n");
239 				}
240 				return ret;
241 			}
242 			case USB_REQ_CLEAR_FEATURE: {
243 				int port;
244 
245 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
246 				debug("clear feature port=%x, feature=%x\n",
247 				      port, setup->value);
248 				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
249 					ret = clrset_post_state(bus, port,
250 							1 << setup->value, 0);
251 				} else {
252 					priv->change[port] &= 1 <<
253 						(setup->value - 16);
254 				}
255 				udev->status = 0;
256 				return 0;
257 			}
258 			default:
259 				debug("%s: tx ctl requesttype=%x, request=%x\n",
260 				      __func__, setup->requesttype,
261 				      setup->request);
262 				break;
263 			}
264 		default:
265 			debug("%s: tx ctl requesttype=%x, request=%x\n",
266 			      __func__, setup->requesttype, setup->request);
267 			break;
268 		}
269 	}
270 	debug("pipe=%lx\n", pipe);
271 
272 	return -EIO;
273 }
274 
275 static int sandbox_hub_bind(struct udevice *dev)
276 {
277 	return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings,
278 				     hub_desc_list);
279 }
280 
281 static int sandbox_child_post_bind(struct udevice *dev)
282 {
283 	struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev);
284 
285 	plat->port = dev_read_u32_default(dev, "reg", -1);
286 
287 	return 0;
288 }
289 
290 static const struct dm_usb_ops sandbox_usb_hub_ops = {
291 	.control	= sandbox_hub_submit_control_msg,
292 };
293 
294 static const struct udevice_id sandbox_usb_hub_ids[] = {
295 	{ .compatible = "sandbox,usb-hub" },
296 	{ }
297 };
298 
299 U_BOOT_DRIVER(usb_sandbox_hub) = {
300 	.name	= "usb_sandbox_hub",
301 	.id	= UCLASS_USB_EMUL,
302 	.of_match = sandbox_usb_hub_ids,
303 	.bind	= sandbox_hub_bind,
304 	.ops	= &sandbox_usb_hub_ops,
305 	.priv_auto_alloc_size = sizeof(struct sandbox_hub_priv),
306 	.per_child_platdata_auto_alloc_size =
307 			sizeof(struct sandbox_hub_platdata),
308 	.child_post_bind = sandbox_child_post_bind,
309 };
310