]> git.sur5r.net Git - u-boot/blob - drivers/usb/emul/sandbox_hub.c
Merge git://www.denx.de/git/u-boot-imx
[u-boot] / drivers / usb / emul / sandbox_hub.c
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 };