xref: /openbmc/u-boot/drivers/usb/emul/sandbox_hub.c (revision 23ff8633)
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 	.DeviceRemovable	= {0, 0xff}, /* all ports removeable */
100 #if SANDBOX_NUM_PORTS > 8
101 #error "This code sets up an incorrect mask"
102 #endif
103 };
104 
105 static void *hub_desc_list[] = {
106 	&hub_device_desc,
107 	&hub_config1,
108 	&hub_interface0,
109 	&hub_endpoint0_in,
110 	&hub_desc,
111 	NULL,
112 };
113 
114 struct sandbox_hub_priv {
115 	int status[SANDBOX_NUM_PORTS];
116 	int change[SANDBOX_NUM_PORTS];
117 };
118 
119 static struct udevice *hub_find_device(struct udevice *hub, int port)
120 {
121 	struct udevice *dev;
122 
123 	for (device_find_first_child(hub, &dev);
124 	     dev;
125 	     device_find_next_child(&dev)) {
126 		struct sandbox_hub_platdata *plat;
127 
128 		plat = dev_get_parent_platdata(dev);
129 		if (plat->port == port)
130 			return dev;
131 	}
132 
133 	return NULL;
134 }
135 
136 static int clrset_post_state(struct udevice *hub, int port, int clear, int set)
137 {
138 	struct sandbox_hub_priv *priv = dev_get_priv(hub);
139 	int *status = &priv->status[port];
140 	int *change = &priv->change[port];
141 	int ret = 0;
142 
143 	if ((clear | set) & USB_PORT_STAT_POWER) {
144 		struct udevice *dev = hub_find_device(hub, port);
145 
146 		if (dev) {
147 			if (set & USB_PORT_STAT_POWER) {
148 				ret = device_probe(dev);
149 				debug("%s: %s: power on, probed, ret=%d\n",
150 				      __func__, dev->name, ret);
151 				if (!ret) {
152 					set |= USB_PORT_STAT_CONNECTION |
153 						USB_PORT_STAT_ENABLE;
154 				}
155 
156 			} else if (clear & USB_PORT_STAT_POWER) {
157 				debug("%s: %s: power off, removed, ret=%d\n",
158 				      __func__, dev->name, ret);
159 				ret = device_remove(dev);
160 				clear |= USB_PORT_STAT_CONNECTION;
161 			}
162 		}
163 	}
164 	*change |= *status & clear;
165 	*change |= ~*status & set;
166 	*change &= 0x1f;
167 	*status = (*status & ~clear) | set;
168 
169 	return ret;
170 }
171 
172 static int sandbox_hub_submit_control_msg(struct udevice *bus,
173 					  struct usb_device *udev,
174 					  unsigned long pipe,
175 					  void *buffer, int length,
176 					  struct devrequest *setup)
177 {
178 	struct sandbox_hub_priv *priv = dev_get_priv(bus);
179 	int ret = 0;
180 
181 	if (pipe == usb_rcvctrlpipe(udev, 0)) {
182 		switch (setup->requesttype) {
183 		case USB_RT_HUB | USB_DIR_IN:
184 			switch (setup->request) {
185 			case USB_REQ_GET_STATUS: {
186 				struct usb_hub_status *hubsts = buffer;
187 
188 				hubsts->wHubStatus = 0;
189 				hubsts->wHubChange = 0;
190 				udev->status = 0;
191 				udev->act_len = sizeof(*hubsts);
192 				return 0;
193 			}
194 			default:
195 				debug("%s: rx ctl requesttype=%x, request=%x\n",
196 				      __func__, setup->requesttype,
197 				      setup->request);
198 				break;
199 			}
200 		case USB_RT_PORT | USB_DIR_IN:
201 			switch (setup->request) {
202 			case USB_REQ_GET_STATUS: {
203 				struct usb_port_status *portsts = buffer;
204 				int port;
205 
206 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
207 				portsts->wPortStatus = priv->status[port];
208 				portsts->wPortChange = priv->change[port];
209 				udev->status = 0;
210 				udev->act_len = sizeof(*portsts);
211 				return 0;
212 			}
213 			}
214 		default:
215 			debug("%s: rx ctl requesttype=%x, request=%x\n",
216 			      __func__, setup->requesttype, setup->request);
217 			break;
218 		}
219 	} else if (pipe == usb_sndctrlpipe(udev, 0)) {
220 		switch (setup->requesttype) {
221 		case USB_RT_PORT:
222 			switch (setup->request) {
223 			case USB_REQ_SET_FEATURE: {
224 				int port;
225 
226 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
227 				debug("set feature port=%x, feature=%x\n",
228 				      port, setup->value);
229 				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
230 					ret = clrset_post_state(bus, port, 0,
231 							1 << setup->value);
232 				} else {
233 					debug("  ** Invalid feature\n");
234 				}
235 				return ret;
236 			}
237 			case USB_REQ_CLEAR_FEATURE: {
238 				int port;
239 
240 				port = (setup->index & USB_HUB_PORT_MASK) - 1;
241 				debug("clear feature port=%x, feature=%x\n",
242 				      port, setup->value);
243 				if (setup->value < USB_PORT_FEAT_C_CONNECTION) {
244 					ret = clrset_post_state(bus, port,
245 							1 << setup->value, 0);
246 				} else {
247 					priv->change[port] &= 1 <<
248 						(setup->value - 16);
249 				}
250 				udev->status = 0;
251 				return 0;
252 			}
253 			default:
254 				debug("%s: tx ctl requesttype=%x, request=%x\n",
255 				      __func__, setup->requesttype,
256 				      setup->request);
257 				break;
258 			}
259 		default:
260 			debug("%s: tx ctl requesttype=%x, request=%x\n",
261 			      __func__, setup->requesttype, setup->request);
262 			break;
263 		}
264 	}
265 	debug("pipe=%lx\n", pipe);
266 
267 	return -EIO;
268 }
269 
270 static int sandbox_hub_bind(struct udevice *dev)
271 {
272 	return usb_emul_setup_device(dev, PACKET_SIZE_64, hub_strings,
273 				     hub_desc_list);
274 }
275 
276 static int sandbox_child_post_bind(struct udevice *dev)
277 {
278 	struct sandbox_hub_platdata *plat = dev_get_parent_platdata(dev);
279 
280 	plat->port = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
281 
282 	return 0;
283 }
284 
285 static const struct dm_usb_ops sandbox_usb_hub_ops = {
286 	.control	= sandbox_hub_submit_control_msg,
287 };
288 
289 static const struct udevice_id sandbox_usb_hub_ids[] = {
290 	{ .compatible = "sandbox,usb-hub" },
291 	{ }
292 };
293 
294 U_BOOT_DRIVER(usb_sandbox_hub) = {
295 	.name	= "usb_sandbox_hub",
296 	.id	= UCLASS_USB_EMUL,
297 	.of_match = sandbox_usb_hub_ids,
298 	.bind	= sandbox_hub_bind,
299 	.ops	= &sandbox_usb_hub_ops,
300 	.priv_auto_alloc_size = sizeof(struct sandbox_hub_priv),
301 	.per_child_platdata_auto_alloc_size =
302 			sizeof(struct sandbox_hub_platdata),
303 	.child_post_bind = sandbox_child_post_bind,
304 };
305