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