xref: /openbmc/u-boot/drivers/usb/emul/sandbox_hub.c (revision cf0bcd7d)
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 				       enum usb_device_speed *speed)
126 {
127 	struct udevice *dev;
128 	struct usb_generic_descriptor **gen_desc;
129 	struct usb_device_descriptor **dev_desc;
130 
131 	for (device_find_first_child(hub, &dev);
132 	     dev;
133 	     device_find_next_child(&dev)) {
134 		struct sandbox_hub_platdata *plat;
135 
136 		plat = dev_get_parent_platdata(dev);
137 		if (plat->port == port) {
138 			gen_desc = plat->plat.desc_list;
139 			gen_desc = usb_emul_find_descriptor(gen_desc,
140 							    USB_DT_DEVICE, 0);
141 			dev_desc = (struct usb_device_descriptor **)gen_desc;
142 
143 			switch (le16_to_cpu((*dev_desc)->bcdUSB)) {
144 			case 0x0100:
145 				*speed = USB_SPEED_LOW;
146 				break;
147 			case 0x0101:
148 				*speed = USB_SPEED_FULL;
149 				break;
150 			case 0x0200:
151 			default:
152 				*speed = USB_SPEED_HIGH;
153 				break;
154 			}
155 
156 			return dev;
157 		}
158 	}
159 
160 	return NULL;
161 }
162 
163 static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
164 {
165 	struct sandbox_hub_priv *priv = dev_get_priv(hub);
166 	int *status = &priv->status[port];
167 	int *change = &priv->change[port];
168 	int ret = 0;
169 
170 	if ((clear | set) & USB_PORT_STAT_POWER) {
171 		enum usb_device_speed speed;
172 		struct udevice *dev = hub_find_device(hub, port, &speed);
173 
174 		if (dev) {
175 			if (set & USB_PORT_STAT_POWER) {
176 				ret = device_probe(dev);
177 				debug("%s: %s: power on, probed, ret=%d\n",
178 				      __func__, dev->name, ret);
179 				if (!ret) {
180 					set |= USB_PORT_STAT_CONNECTION |
181 						USB_PORT_STAT_ENABLE;
182 					if (speed == USB_SPEED_LOW)
183 						set |= USB_PORT_STAT_LOW_SPEED;
184 					else if (speed == USB_SPEED_HIGH)
185 						set |= USB_PORT_STAT_HIGH_SPEED;
186 				}
187 
188 			} else if (clear & USB_PORT_STAT_POWER) {
189 				debug("%s: %s: power off, removed, ret=%d\n",
190 				      __func__, dev->name, ret);
191 				ret = device_remove(dev, DM_REMOVE_NORMAL);
192 				clear |= USB_PORT_STAT_CONNECTION;
193 			}
194 		}
195 	}
196 	*change |= *status & clear;
197 	*change |= ~*status & set;
198 	*change &= 0x1f;
199 	*status = (*status & ~clear) | set;
200 
201 	return ret;
202 }
203 
204 static int sandbox_hub_submit_control_msg(struct udevice *bus,
205 					  struct usb_device *udev,
206 					  unsigned long pipe,
207 					  void *buffer, int length,
208 					  struct devrequest *setup)
209 {
210 	struct sandbox_hub_priv *priv = dev_get_priv(bus);
211 	int ret = 0;
212 
213 	if (pipe == usb_rcvctrlpipe(udev, 0)) {
214 		switch (setup->requesttype) {
215 		case USB_RT_HUB | USB_DIR_IN:
216 			switch (setup->request) {
217 			case USB_REQ_GET_STATUS: {
218 				struct usb_hub_status *hubsts = buffer;
219 
220 				hubsts->wHubStatus = 0;
221 				hubsts->wHubChange = 0;
222 				udev->status = 0;
223 				udev->act_len = sizeof(*hubsts);
224 				return 0;
225 			}
226 			default:
227 				debug("%s: rx ctl requesttype=%x, request=%x\n",
228 				      __func__, setup->requesttype,
229 				      setup->request);
230 				break;
231 			}
232 		case USB_RT_PORT | USB_DIR_IN:
233 			switch (setup->request) {
234 			case USB_REQ_GET_STATUS: {
235 				struct usb_port_status *portsts = buffer;
236 				int port;
237 
238 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
239 				portsts->wPortStatus = priv->status[port];
240 				portsts->wPortChange = priv->change[port];
241 				udev->status = 0;
242 				udev->act_len = sizeof(*portsts);
243 				return 0;
244 			}
245 			}
246 		default:
247 			debug("%s: rx ctl requesttype=%x, request=%x\n",
248 			      __func__, setup->requesttype, setup->request);
249 			break;
250 		}
251 	} else if (pipe == usb_sndctrlpipe(udev, 0)) {
252 		switch (setup->requesttype) {
253 		case USB_RT_PORT:
254 			switch (setup->request) {
255 			case USB_REQ_SET_FEATURE: {
256 				int port;
257 
258 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
259 				debug("set feature port=%x, feature=%x\n",
260 				      port, setup->value);
261 				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
262 					ret = clrset_post_state(bus, port, 0,
263 							1 << setup->value);
264 				} else {
265 					debug("  ** Invalid feature\n");
266 				}
267 				return ret;
268 			}
269 			case USB_REQ_CLEAR_FEATURE: {
270 				int port;
271 
272 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
273 				debug("clear feature port=%x, feature=%x\n",
274 				      port, setup->value);
275 				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
276 					ret = clrset_post_state(bus, port,
277 							1 << setup->value, 0);
278 				} else {
279 					priv->change[port] &= 1 <<
280 						(setup->value - 16);
281 				}
282 				udev->status = 0;
283 				return 0;
284 			}
285 			default:
286 				debug("%s: tx ctl requesttype=%x, request=%x\n",
287 				      __func__, setup->requesttype,
288 				      setup->request);
289 				break;
290 			}
291 		default:
292 			debug("%s: tx ctl requesttype=%x, request=%x\n",
293 			      __func__, setup->requesttype, setup->request);
294 			break;
295 		}
296 	}
297 	debug("pipe=%lx\n", pipe);
298 
299 	return -EIO;
300 }
301 
302 static int sandbox_hub_bind(struct udevice *dev)
303 {
304 	return usb_emul_setup_device(dev, hub_strings, hub_desc_list);
305 }
306 
307 static int sandbox_child_post_bind(struct udevice *dev)
308 {
309 	struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev);
310 	struct usb_emul_platdata *emul = dev_get_uclass_platdata(dev);
311 
312 	plat->port = dev_read_u32_default(dev, "reg", -1);
313 	emul->port1 = plat->port + 1;
314 
315 	return 0;
316 }
317 
318 static const struct dm_usb_ops sandbox_usb_hub_ops = {
319 	.control	= sandbox_hub_submit_control_msg,
320 };
321 
322 static const struct udevice_id sandbox_usb_hub_ids[] = {
323 	{ .compatible = "sandbox,usb-hub" },
324 	{ }
325 };
326 
327 U_BOOT_DRIVER(usb_sandbox_hub) = {
328 	.name	= "usb_sandbox_hub",
329 	.id	= UCLASS_USB_EMUL,
330 	.of_match = sandbox_usb_hub_ids,
331 	.bind	= sandbox_hub_bind,
332 	.ops	= &sandbox_usb_hub_ops,
333 	.priv_auto_alloc_size = sizeof(struct sandbox_hub_priv),
334 	.per_child_platdata_auto_alloc_size =
335 			sizeof(struct sandbox_hub_platdata),
336 	.child_post_bind = sandbox_child_post_bind,
337 };
338