usbroothub.c revision 1.4.2.2 1 1.4.2.2 jdolecek /* $NetBSD: usbroothub.c,v 1.4.2.2 2017/12/03 11:37:36 jdolecek Exp $ */
2 1.4.2.2 jdolecek
3 1.4.2.2 jdolecek /*-
4 1.4.2.2 jdolecek * Copyright (c) 1998, 2004, 2011, 2012 The NetBSD Foundation, Inc.
5 1.4.2.2 jdolecek * All rights reserved.
6 1.4.2.2 jdolecek *
7 1.4.2.2 jdolecek * This code is derived from software contributed to The NetBSD Foundation
8 1.4.2.2 jdolecek * by Lennart Augustsson (lennart (at) augustsson.net) at
9 1.4.2.2 jdolecek * Carlstedt Research & Technology, Jared D. McNeill (jmcneill (at) invisible.ca),
10 1.4.2.2 jdolecek * Matthew R. Green (mrg (at) eterna.com.au) and Nick Hudson.
11 1.4.2.2 jdolecek *
12 1.4.2.2 jdolecek * Redistribution and use in source and binary forms, with or without
13 1.4.2.2 jdolecek * modification, are permitted provided that the following conditions
14 1.4.2.2 jdolecek * are met:
15 1.4.2.2 jdolecek * 1. Redistributions of source code must retain the above copyright
16 1.4.2.2 jdolecek * notice, this list of conditions and the following disclaimer.
17 1.4.2.2 jdolecek * 2. Redistributions in binary form must reproduce the above copyright
18 1.4.2.2 jdolecek * notice, this list of conditions and the following disclaimer in the
19 1.4.2.2 jdolecek * documentation and/or other materials provided with the distribution.
20 1.4.2.2 jdolecek *
21 1.4.2.2 jdolecek * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 1.4.2.2 jdolecek * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 1.4.2.2 jdolecek * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 1.4.2.2 jdolecek * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 1.4.2.2 jdolecek * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 1.4.2.2 jdolecek * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 1.4.2.2 jdolecek * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 1.4.2.2 jdolecek * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 1.4.2.2 jdolecek * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 1.4.2.2 jdolecek * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 1.4.2.2 jdolecek * POSSIBILITY OF SUCH DAMAGE.
32 1.4.2.2 jdolecek */
33 1.4.2.2 jdolecek
34 1.4.2.2 jdolecek /*
35 1.4.2.2 jdolecek * Copyright (c) 2008
36 1.4.2.2 jdolecek * Matthias Drochner. All rights reserved.
37 1.4.2.2 jdolecek *
38 1.4.2.2 jdolecek * Redistribution and use in source and binary forms, with or without
39 1.4.2.2 jdolecek * modification, are permitted provided that the following conditions
40 1.4.2.2 jdolecek * are met:
41 1.4.2.2 jdolecek * 1. Redistributions of source code must retain the above copyright
42 1.4.2.2 jdolecek * notice, this list of conditions and the following disclaimer.
43 1.4.2.2 jdolecek * 2. Redistributions in binary form must reproduce the above copyright
44 1.4.2.2 jdolecek * notice, this list of conditions and the following disclaimer in the
45 1.4.2.2 jdolecek * documentation and/or other materials provided with the distribution.
46 1.4.2.2 jdolecek *
47 1.4.2.2 jdolecek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
48 1.4.2.2 jdolecek * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
49 1.4.2.2 jdolecek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
50 1.4.2.2 jdolecek * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
51 1.4.2.2 jdolecek * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
52 1.4.2.2 jdolecek * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53 1.4.2.2 jdolecek * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54 1.4.2.2 jdolecek * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55 1.4.2.2 jdolecek * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
56 1.4.2.2 jdolecek * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 1.4.2.2 jdolecek *
58 1.4.2.2 jdolecek */
59 1.4.2.2 jdolecek
60 1.4.2.2 jdolecek #include <dev/usb/usb.h>
61 1.4.2.2 jdolecek #include <dev/usb/usbdi.h>
62 1.4.2.2 jdolecek #include <dev/usb/usbdivar.h>
63 1.4.2.2 jdolecek #include <dev/usb/usbroothub.h>
64 1.4.2.2 jdolecek #include <dev/usb/usbhist.h>
65 1.4.2.2 jdolecek
66 1.4.2.2 jdolecek extern int usbdebug;
67 1.4.2.2 jdolecek
68 1.4.2.2 jdolecek /* helper functions for USB root hub emulation */
69 1.4.2.2 jdolecek
70 1.4.2.2 jdolecek static usbd_status roothub_ctrl_transfer(struct usbd_xfer *);
71 1.4.2.2 jdolecek static usbd_status roothub_ctrl_start(struct usbd_xfer *);
72 1.4.2.2 jdolecek static void roothub_ctrl_abort(struct usbd_xfer *);
73 1.4.2.2 jdolecek static void roothub_ctrl_close(struct usbd_pipe *);
74 1.4.2.2 jdolecek static void roothub_ctrl_done(struct usbd_xfer *);
75 1.4.2.2 jdolecek static void roothub_noop(struct usbd_pipe *pipe);
76 1.4.2.2 jdolecek
77 1.4.2.2 jdolecek const struct usbd_pipe_methods roothub_ctrl_methods = {
78 1.4.2.2 jdolecek .upm_transfer = roothub_ctrl_transfer,
79 1.4.2.2 jdolecek .upm_start = roothub_ctrl_start,
80 1.4.2.2 jdolecek .upm_abort = roothub_ctrl_abort,
81 1.4.2.2 jdolecek .upm_close = roothub_ctrl_close,
82 1.4.2.2 jdolecek .upm_cleartoggle = roothub_noop,
83 1.4.2.2 jdolecek .upm_done = roothub_ctrl_done,
84 1.4.2.2 jdolecek };
85 1.4.2.2 jdolecek
86 1.4.2.2 jdolecek int
87 1.4.2.2 jdolecek usb_makestrdesc(usb_string_descriptor_t *p, int l, const char *s)
88 1.4.2.2 jdolecek {
89 1.4.2.2 jdolecek int i;
90 1.4.2.2 jdolecek
91 1.4.2.2 jdolecek if (l == 0)
92 1.4.2.2 jdolecek return 0;
93 1.4.2.2 jdolecek p->bLength = 2 * strlen(s) + 2;
94 1.4.2.2 jdolecek if (l == 1)
95 1.4.2.2 jdolecek return 1;
96 1.4.2.2 jdolecek p->bDescriptorType = UDESC_STRING;
97 1.4.2.2 jdolecek l -= 2;
98 1.4.2.2 jdolecek /* poor man's utf-16le conversion */
99 1.4.2.2 jdolecek for (i = 0; s[i] && l > 1; i++, l -= 2)
100 1.4.2.2 jdolecek USETW2(p->bString[i], 0, s[i]);
101 1.4.2.2 jdolecek return 2 * i + 2;
102 1.4.2.2 jdolecek }
103 1.4.2.2 jdolecek
104 1.4.2.2 jdolecek int
105 1.4.2.2 jdolecek usb_makelangtbl(usb_string_descriptor_t *p, int l)
106 1.4.2.2 jdolecek {
107 1.4.2.2 jdolecek
108 1.4.2.2 jdolecek if (l == 0)
109 1.4.2.2 jdolecek return 0;
110 1.4.2.2 jdolecek p->bLength = 4;
111 1.4.2.2 jdolecek if (l == 1)
112 1.4.2.2 jdolecek return 1;
113 1.4.2.2 jdolecek p->bDescriptorType = UDESC_STRING;
114 1.4.2.2 jdolecek if (l < 4)
115 1.4.2.2 jdolecek return 2;
116 1.4.2.2 jdolecek USETW(p->bString[0], 0x0409); /* english/US */
117 1.4.2.2 jdolecek return 4;
118 1.4.2.2 jdolecek }
119 1.4.2.2 jdolecek
120 1.4.2.2 jdolecek /*
121 1.4.2.2 jdolecek * Data structures and routines to emulate the root hub.
122 1.4.2.2 jdolecek */
123 1.4.2.2 jdolecek static const usb_device_descriptor_t usbroothub_devd1 = {
124 1.4.2.2 jdolecek .bLength = sizeof(usb_device_descriptor_t),
125 1.4.2.2 jdolecek .bDescriptorType = UDESC_DEVICE,
126 1.4.2.2 jdolecek .bcdUSB = {0x00, 0x01},
127 1.4.2.2 jdolecek .bDeviceClass = UDCLASS_HUB,
128 1.4.2.2 jdolecek .bDeviceSubClass = UDSUBCLASS_HUB,
129 1.4.2.2 jdolecek .bDeviceProtocol = UDPROTO_FSHUB,
130 1.4.2.2 jdolecek .bMaxPacketSize = 64,
131 1.4.2.2 jdolecek .idVendor = {0},
132 1.4.2.2 jdolecek .idProduct = {0},
133 1.4.2.2 jdolecek .bcdDevice = {0x00, 0x01},
134 1.4.2.2 jdolecek .iManufacturer = 1,
135 1.4.2.2 jdolecek .iProduct = 2,
136 1.4.2.2 jdolecek .iSerialNumber = 0,
137 1.4.2.2 jdolecek .bNumConfigurations = 1
138 1.4.2.2 jdolecek };
139 1.4.2.2 jdolecek
140 1.4.2.2 jdolecek static const struct usb_roothub_descriptors usbroothub_confd1 = {
141 1.4.2.2 jdolecek .urh_confd = {
142 1.4.2.2 jdolecek .bLength = USB_CONFIG_DESCRIPTOR_SIZE,
143 1.4.2.2 jdolecek .bDescriptorType = UDESC_CONFIG,
144 1.4.2.2 jdolecek .wTotalLength = USETWD(sizeof(usbroothub_confd1)),
145 1.4.2.2 jdolecek .bNumInterface = 1,
146 1.4.2.2 jdolecek .bConfigurationValue = 1,
147 1.4.2.2 jdolecek .iConfiguration = 0,
148 1.4.2.2 jdolecek .bmAttributes = UC_ATTR_MBO | UC_SELF_POWERED,
149 1.4.2.2 jdolecek .bMaxPower = 0,
150 1.4.2.2 jdolecek },
151 1.4.2.2 jdolecek .urh_ifcd = {
152 1.4.2.2 jdolecek .bLength = USB_INTERFACE_DESCRIPTOR_SIZE,
153 1.4.2.2 jdolecek .bDescriptorType = UDESC_INTERFACE,
154 1.4.2.2 jdolecek .bInterfaceNumber = 0,
155 1.4.2.2 jdolecek .bAlternateSetting = 0,
156 1.4.2.2 jdolecek .bNumEndpoints = 1,
157 1.4.2.2 jdolecek .bInterfaceClass = UICLASS_HUB,
158 1.4.2.2 jdolecek .bInterfaceSubClass = UISUBCLASS_HUB,
159 1.4.2.2 jdolecek .bInterfaceProtocol = UIPROTO_FSHUB,
160 1.4.2.2 jdolecek .iInterface = 0
161 1.4.2.2 jdolecek },
162 1.4.2.2 jdolecek .urh_endpd = {
163 1.4.2.2 jdolecek .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE,
164 1.4.2.2 jdolecek .bDescriptorType = UDESC_ENDPOINT,
165 1.4.2.2 jdolecek .bEndpointAddress = UE_DIR_IN | USBROOTHUB_INTR_ENDPT,
166 1.4.2.2 jdolecek .bmAttributes = UE_INTERRUPT,
167 1.4.2.2 jdolecek .wMaxPacketSize = USETWD(8), /* max packet */
168 1.4.2.2 jdolecek .bInterval = 255,
169 1.4.2.2 jdolecek },
170 1.4.2.2 jdolecek };
171 1.4.2.2 jdolecek
172 1.4.2.2 jdolecek /* USB 3.0 10.15.1 */
173 1.4.2.2 jdolecek static const usb_device_descriptor_t usbroothub_devd3 = {
174 1.4.2.2 jdolecek .bLength = sizeof(usb_device_descriptor_t),
175 1.4.2.2 jdolecek .bDescriptorType = UDESC_DEVICE,
176 1.4.2.2 jdolecek .bcdUSB = {0x00, 0x03},
177 1.4.2.2 jdolecek .bDeviceClass = UDCLASS_HUB,
178 1.4.2.2 jdolecek .bDeviceSubClass = UDSUBCLASS_HUB,
179 1.4.2.2 jdolecek .bDeviceProtocol = UDPROTO_SSHUB,
180 1.4.2.2 jdolecek .bMaxPacketSize = 9,
181 1.4.2.2 jdolecek .idVendor = {0},
182 1.4.2.2 jdolecek .idProduct = {0},
183 1.4.2.2 jdolecek .bcdDevice = {0x00, 0x01},
184 1.4.2.2 jdolecek .iManufacturer = 1,
185 1.4.2.2 jdolecek .iProduct = 2,
186 1.4.2.2 jdolecek .iSerialNumber = 0,
187 1.4.2.2 jdolecek .bNumConfigurations = 1
188 1.4.2.2 jdolecek };
189 1.4.2.2 jdolecek
190 1.4.2.2 jdolecek static const usb_device_descriptor_t usbroothub_devd2 = {
191 1.4.2.2 jdolecek .bLength = sizeof(usb_device_descriptor_t),
192 1.4.2.2 jdolecek .bDescriptorType = UDESC_DEVICE,
193 1.4.2.2 jdolecek .bcdUSB = {0x00, 0x02},
194 1.4.2.2 jdolecek .bDeviceClass = UDCLASS_HUB,
195 1.4.2.2 jdolecek .bDeviceSubClass = UDSUBCLASS_HUB,
196 1.4.2.2 jdolecek .bDeviceProtocol = UDPROTO_HSHUBSTT,
197 1.4.2.2 jdolecek .bMaxPacketSize = 64,
198 1.4.2.2 jdolecek .idVendor = {0},
199 1.4.2.2 jdolecek .idProduct = {0},
200 1.4.2.2 jdolecek .bcdDevice = {0x00, 0x01},
201 1.4.2.2 jdolecek .iManufacturer = 1,
202 1.4.2.2 jdolecek .iProduct = 2,
203 1.4.2.2 jdolecek .iSerialNumber = 0,
204 1.4.2.2 jdolecek .bNumConfigurations = 1
205 1.4.2.2 jdolecek };
206 1.4.2.2 jdolecek
207 1.4.2.2 jdolecek static const usb_device_qualifier_t usbroothub_odevd2 = {
208 1.4.2.2 jdolecek .bLength = USB_DEVICE_QUALIFIER_SIZE,
209 1.4.2.2 jdolecek .bDescriptorType = UDESC_DEVICE_QUALIFIER,
210 1.4.2.2 jdolecek .bcdUSB = {0x00, 0x02},
211 1.4.2.2 jdolecek .bDeviceClass = UDCLASS_HUB,
212 1.4.2.2 jdolecek .bDeviceSubClass = UDSUBCLASS_HUB,
213 1.4.2.2 jdolecek .bDeviceProtocol = UDPROTO_FSHUB,
214 1.4.2.2 jdolecek .bMaxPacketSize0 = 64,
215 1.4.2.2 jdolecek .bNumConfigurations = 1,
216 1.4.2.2 jdolecek };
217 1.4.2.2 jdolecek
218 1.4.2.2 jdolecek static const struct usb_roothub_descriptors usbroothub_confd2 = {
219 1.4.2.2 jdolecek .urh_confd = {
220 1.4.2.2 jdolecek .bLength = USB_CONFIG_DESCRIPTOR_SIZE,
221 1.4.2.2 jdolecek .bDescriptorType = UDESC_CONFIG,
222 1.4.2.2 jdolecek .wTotalLength = USETWD(sizeof(usbroothub_confd2)),
223 1.4.2.2 jdolecek .bNumInterface = 1,
224 1.4.2.2 jdolecek .bConfigurationValue = 1,
225 1.4.2.2 jdolecek .iConfiguration = 0,
226 1.4.2.2 jdolecek .bmAttributes = UC_ATTR_MBO | UC_SELF_POWERED,
227 1.4.2.2 jdolecek .bMaxPower = 0,
228 1.4.2.2 jdolecek },
229 1.4.2.2 jdolecek .urh_ifcd = {
230 1.4.2.2 jdolecek .bLength = USB_INTERFACE_DESCRIPTOR_SIZE,
231 1.4.2.2 jdolecek .bDescriptorType = UDESC_INTERFACE,
232 1.4.2.2 jdolecek .bInterfaceNumber = 0,
233 1.4.2.2 jdolecek .bAlternateSetting = 0,
234 1.4.2.2 jdolecek .bNumEndpoints = 1,
235 1.4.2.2 jdolecek .bInterfaceClass = UICLASS_HUB,
236 1.4.2.2 jdolecek .bInterfaceSubClass = UISUBCLASS_HUB,
237 1.4.2.2 jdolecek .bInterfaceProtocol = UIPROTO_HSHUBSTT,
238 1.4.2.2 jdolecek .iInterface = 0
239 1.4.2.2 jdolecek },
240 1.4.2.2 jdolecek .urh_endpd = {
241 1.4.2.2 jdolecek .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE,
242 1.4.2.2 jdolecek .bDescriptorType = UDESC_ENDPOINT,
243 1.4.2.2 jdolecek .bEndpointAddress = UE_DIR_IN | USBROOTHUB_INTR_ENDPT,
244 1.4.2.2 jdolecek .bmAttributes = UE_INTERRUPT,
245 1.4.2.2 jdolecek .wMaxPacketSize = USETWD(8), /* max packet */
246 1.4.2.2 jdolecek .bInterval = 12,
247 1.4.2.2 jdolecek },
248 1.4.2.2 jdolecek };
249 1.4.2.2 jdolecek
250 1.4.2.2 jdolecek static const struct usb3_roothub_descriptors usbroothub_confd3 = {
251 1.4.2.2 jdolecek .urh_confd = {
252 1.4.2.2 jdolecek .bLength = USB_CONFIG_DESCRIPTOR_SIZE,
253 1.4.2.2 jdolecek .bDescriptorType = UDESC_CONFIG,
254 1.4.2.2 jdolecek .wTotalLength = USETWD(sizeof(usbroothub_confd3)),
255 1.4.2.2 jdolecek .bNumInterface = 1,
256 1.4.2.2 jdolecek .bConfigurationValue = 1,
257 1.4.2.2 jdolecek .iConfiguration = 0,
258 1.4.2.2 jdolecek .bmAttributes = UC_SELF_POWERED, /* 10.13.1 */
259 1.4.2.2 jdolecek .bMaxPower = 0,
260 1.4.2.2 jdolecek },
261 1.4.2.2 jdolecek .urh_ifcd = {
262 1.4.2.2 jdolecek .bLength = USB_INTERFACE_DESCRIPTOR_SIZE,
263 1.4.2.2 jdolecek .bDescriptorType = UDESC_INTERFACE,
264 1.4.2.2 jdolecek .bInterfaceNumber = 0,
265 1.4.2.2 jdolecek .bAlternateSetting = 0,
266 1.4.2.2 jdolecek .bNumEndpoints = 1,
267 1.4.2.2 jdolecek .bInterfaceClass = UICLASS_HUB,
268 1.4.2.2 jdolecek .bInterfaceSubClass = UISUBCLASS_HUB,
269 1.4.2.2 jdolecek .bInterfaceProtocol = 0, /* UIPROTO_SSHUB ??? */
270 1.4.2.2 jdolecek .iInterface = 0
271 1.4.2.2 jdolecek },
272 1.4.2.2 jdolecek .urh_endpd = {
273 1.4.2.2 jdolecek .bLength = USB_ENDPOINT_DESCRIPTOR_SIZE,
274 1.4.2.2 jdolecek .bDescriptorType = UDESC_ENDPOINT,
275 1.4.2.2 jdolecek .bEndpointAddress = UE_DIR_IN | USBROOTHUB_INTR_ENDPT,
276 1.4.2.2 jdolecek .bmAttributes = UE_INTERRUPT,
277 1.4.2.2 jdolecek .wMaxPacketSize = USETWD(2), /* max packet */
278 1.4.2.2 jdolecek .bInterval = 8,
279 1.4.2.2 jdolecek },
280 1.4.2.2 jdolecek .urh_endpssd = {
281 1.4.2.2 jdolecek .bLength = USB_ENDPOINT_SS_COMP_DESCRIPTOR_SIZE,
282 1.4.2.2 jdolecek .bDescriptorType = UDESC_ENDPOINT_SS_COMP,
283 1.4.2.2 jdolecek .bMaxBurst = 0,
284 1.4.2.2 jdolecek .bmAttributes = 0,
285 1.4.2.2 jdolecek .wBytesPerInterval = USETWD(2)
286 1.4.2.2 jdolecek },
287 1.4.2.2 jdolecek };
288 1.4.2.2 jdolecek
289 1.4.2.2 jdolecek static const struct usb3_roothub_bos_descriptors usbroothub_bosd3 = {
290 1.4.2.2 jdolecek .urh_bosd = {
291 1.4.2.2 jdolecek .bLength = USB_BOS_DESCRIPTOR_SIZE,
292 1.4.2.2 jdolecek .bDescriptorType = UDESC_BOS,
293 1.4.2.2 jdolecek .wTotalLength = USETWD(sizeof(usbroothub_bosd3)),
294 1.4.2.2 jdolecek .bNumDeviceCaps = 3,
295 1.4.2.2 jdolecek },
296 1.4.2.2 jdolecek /* 9.6.2.1 USB 2.0 Extension */
297 1.4.2.2 jdolecek .urh_usb2extd = {
298 1.4.2.2 jdolecek .bLength = USB_DEVCAP_USB2EXT_DESCRIPTOR_SIZE,
299 1.4.2.2 jdolecek .bDescriptorType = 1,
300 1.4.2.2 jdolecek .bDevCapabilityType = 2,
301 1.4.2.2 jdolecek .bmAttributes[0] = 2,
302 1.4.2.2 jdolecek },
303 1.4.2.2 jdolecek /* 9.6.2.2 Superspeed device capability */
304 1.4.2.2 jdolecek .urh_ssd = {
305 1.4.2.2 jdolecek .bLength = USB_DEVCAP_SS_DESCRIPTOR_SIZE,
306 1.4.2.2 jdolecek .bDescriptorType = UDESC_DEVICE_CAPABILITY,
307 1.4.2.2 jdolecek .bDevCapabilityType = USB_DEVCAP_SUPER_SPEED,
308 1.4.2.2 jdolecek .bmAttributes = 0, /* USB_DEVCAP_SS_LTM */
309 1.4.2.2 jdolecek .wSpeedsSupported = USETWD(
310 1.4.2.2 jdolecek USB_DEVCAP_SS_SPEED_LS | USB_DEVCAP_SS_SPEED_FS |
311 1.4.2.2 jdolecek USB_DEVCAP_SS_SPEED_HS | USB_DEVCAP_SS_SPEED_SS),
312 1.4.2.2 jdolecek .bFunctionalitySupport = 8, /* SS is 3, i.e. 1 << 3? */
313 1.4.2.2 jdolecek .bU1DevExitLat = 255, /* Dummy... 0? */
314 1.4.2.2 jdolecek .wU2DevExitLat = USETWD(8), /* Also dummy... 0? */
315 1.4.2.2 jdolecek },
316 1.4.2.2 jdolecek /* 9.6.2.3 Container ID - see RFC 4122 */
317 1.4.2.2 jdolecek .urh_containerd = {
318 1.4.2.2 jdolecek .bLength = USB_DEVCAP_CONTAINER_ID_DESCRIPTOR_SIZE,
319 1.4.2.2 jdolecek .bDescriptorType = 1,
320 1.4.2.2 jdolecek .bDevCapabilityType = 4,
321 1.4.2.2 jdolecek .bReserved = 0,
322 1.4.2.2 jdolecek // ContainerID will be zero
323 1.4.2.2 jdolecek },
324 1.4.2.2 jdolecek };
325 1.4.2.2 jdolecek
326 1.4.2.2 jdolecek static const usb_hub_descriptor_t usbroothub_hubd = {
327 1.4.2.2 jdolecek .bDescLength = USB_HUB_DESCRIPTOR_SIZE,
328 1.4.2.2 jdolecek .bDescriptorType = UDESC_HUB,
329 1.4.2.2 jdolecek .bNbrPorts = 1,
330 1.4.2.2 jdolecek .wHubCharacteristics = USETWD(UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL),
331 1.4.2.2 jdolecek .bPwrOn2PwrGood = 50,
332 1.4.2.2 jdolecek .bHubContrCurrent = 0,
333 1.4.2.2 jdolecek .DeviceRemovable = {0}, /* port is removable */
334 1.4.2.2 jdolecek };
335 1.4.2.2 jdolecek
336 1.4.2.2 jdolecek /*
337 1.4.2.2 jdolecek * Simulate a hardware hub by handling all the necessary requests.
338 1.4.2.2 jdolecek */
339 1.4.2.2 jdolecek usbd_status
340 1.4.2.2 jdolecek roothub_ctrl_transfer(struct usbd_xfer *xfer)
341 1.4.2.2 jdolecek {
342 1.4.2.2 jdolecek struct usbd_pipe *pipe = xfer->ux_pipe;
343 1.4.2.2 jdolecek struct usbd_bus *bus = pipe->up_dev->ud_bus;
344 1.4.2.2 jdolecek usbd_status err;
345 1.4.2.2 jdolecek
346 1.4.2.2 jdolecek /* Insert last in queue. */
347 1.4.2.2 jdolecek mutex_enter(bus->ub_lock);
348 1.4.2.2 jdolecek err = usb_insert_transfer(xfer);
349 1.4.2.2 jdolecek mutex_exit(bus->ub_lock);
350 1.4.2.2 jdolecek if (err)
351 1.4.2.2 jdolecek return err;
352 1.4.2.2 jdolecek
353 1.4.2.2 jdolecek /* Pipe isn't running, start first */
354 1.4.2.2 jdolecek return roothub_ctrl_start(SIMPLEQ_FIRST(&xfer->ux_pipe->up_queue));
355 1.4.2.2 jdolecek }
356 1.4.2.2 jdolecek
357 1.4.2.2 jdolecek static usbd_status
358 1.4.2.2 jdolecek roothub_ctrl_start(struct usbd_xfer *xfer)
359 1.4.2.2 jdolecek {
360 1.4.2.2 jdolecek struct usbd_pipe *pipe = xfer->ux_pipe;
361 1.4.2.2 jdolecek struct usbd_bus *bus = pipe->up_dev->ud_bus;
362 1.4.2.2 jdolecek usb_device_request_t *req;
363 1.4.2.2 jdolecek usbd_status err = USBD_IOERROR; /* XXX STALL? */
364 1.4.2.2 jdolecek uint16_t len, value;
365 1.4.2.2 jdolecek int buflen, actlen;
366 1.4.2.2 jdolecek void *buf;
367 1.4.2.2 jdolecek
368 1.4.2.2 jdolecek USBHIST_FUNC(); USBHIST_CALLED(usbdebug);
369 1.4.2.2 jdolecek
370 1.4.2.2 jdolecek KASSERT(xfer->ux_rqflags & URQ_REQUEST);
371 1.4.2.2 jdolecek req = &xfer->ux_request;
372 1.4.2.2 jdolecek
373 1.4.2.2 jdolecek USBHIST_LOG(usbdebug, "type=%#2jx request=%#2jx", req->bmRequestType,
374 1.4.2.2 jdolecek req->bRequest, 0, 0);
375 1.4.2.2 jdolecek
376 1.4.2.2 jdolecek len = UGETW(req->wLength);
377 1.4.2.2 jdolecek value = UGETW(req->wValue);
378 1.4.2.2 jdolecek
379 1.4.2.2 jdolecek buf = len ? usbd_get_buffer(xfer) : NULL;
380 1.4.2.2 jdolecek buflen = 0;
381 1.4.2.2 jdolecek
382 1.4.2.2 jdolecek #define C(x,y) ((x) | ((y) << 8))
383 1.4.2.2 jdolecek switch (C(req->bRequest, req->bmRequestType)) {
384 1.4.2.2 jdolecek case C(UR_CLEAR_FEATURE, UT_WRITE_DEVICE):
385 1.4.2.2 jdolecek case C(UR_CLEAR_FEATURE, UT_WRITE_INTERFACE):
386 1.4.2.2 jdolecek case C(UR_CLEAR_FEATURE, UT_WRITE_ENDPOINT):
387 1.4.2.2 jdolecek /*
388 1.4.2.2 jdolecek * DEVICE_REMOTE_WAKEUP and ENDPOINT_HALT are no-ops
389 1.4.2.2 jdolecek * for the integrated root hub.
390 1.4.2.2 jdolecek */
391 1.4.2.2 jdolecek break;
392 1.4.2.2 jdolecek case C(UR_GET_CONFIG, UT_READ_DEVICE):
393 1.4.2.2 jdolecek if (len > 0) {
394 1.4.2.2 jdolecek uint8_t *out = buf;
395 1.4.2.2 jdolecek
396 1.4.2.2 jdolecek *out = bus->ub_rhconf;
397 1.4.2.2 jdolecek buflen = sizeof(*out);
398 1.4.2.2 jdolecek }
399 1.4.2.2 jdolecek break;
400 1.4.2.2 jdolecek case C(UR_GET_DESCRIPTOR, UT_READ_DEVICE):
401 1.4.2.2 jdolecek USBHIST_LOG(usbdebug, "wValue=%#4jx", value, 0, 0, 0);
402 1.4.2.2 jdolecek
403 1.4.2.2 jdolecek if (len == 0)
404 1.4.2.2 jdolecek break;
405 1.4.2.2 jdolecek switch (value) {
406 1.4.2.2 jdolecek case C(0, UDESC_DEVICE):
407 1.4.2.2 jdolecek if (bus->ub_revision >= USBREV_3_0) {
408 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_devd3));
409 1.4.2.2 jdolecek memcpy(buf, &usbroothub_devd3, buflen);
410 1.4.2.2 jdolecek } else if (bus->ub_revision == USBREV_2_0) {
411 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_devd2));
412 1.4.2.2 jdolecek memcpy(buf, &usbroothub_devd2, buflen);
413 1.4.2.2 jdolecek } else {
414 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_devd1));
415 1.4.2.2 jdolecek memcpy(buf, &usbroothub_devd1, buflen);
416 1.4.2.2 jdolecek }
417 1.4.2.2 jdolecek break;
418 1.4.2.2 jdolecek case C(0, UDESC_CONFIG):
419 1.4.2.2 jdolecek if (bus->ub_revision >= USBREV_3_0) {
420 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_confd3));
421 1.4.2.2 jdolecek memcpy(buf, &usbroothub_confd3, buflen);
422 1.4.2.2 jdolecek } else if (bus->ub_revision == USBREV_2_0) {
423 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_confd2));
424 1.4.2.2 jdolecek memcpy(buf, &usbroothub_confd2, buflen);
425 1.4.2.2 jdolecek } else {
426 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_confd1));
427 1.4.2.2 jdolecek memcpy(buf, &usbroothub_confd1, buflen);
428 1.4.2.2 jdolecek }
429 1.4.2.2 jdolecek break;
430 1.4.2.2 jdolecek case C(0, UDESC_DEVICE_QUALIFIER):
431 1.4.2.2 jdolecek if (bus->ub_revision == USBREV_2_0) {
432 1.4.2.2 jdolecek /*
433 1.4.2.2 jdolecek * We can't really operate at another speed,
434 1.4.2.2 jdolecek * but the spec says we need this descriptor.
435 1.4.2.2 jdolecek */
436 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_odevd2));
437 1.4.2.2 jdolecek memcpy(buf, &usbroothub_odevd2, buflen);
438 1.4.2.2 jdolecek } else
439 1.4.2.2 jdolecek goto fail;
440 1.4.2.2 jdolecek break;
441 1.4.2.2 jdolecek case C(0, UDESC_OTHER_SPEED_CONFIGURATION):
442 1.4.2.2 jdolecek if (bus->ub_revision == USBREV_2_0) {
443 1.4.2.2 jdolecek struct usb_roothub_descriptors confd;
444 1.4.2.2 jdolecek
445 1.4.2.2 jdolecek /*
446 1.4.2.2 jdolecek * We can't really operate at another speed,
447 1.4.2.2 jdolecek * but the spec says we need this descriptor.
448 1.4.2.2 jdolecek */
449 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_confd2));
450 1.4.2.2 jdolecek memcpy(&confd, &usbroothub_confd2, buflen);
451 1.4.2.2 jdolecek confd.urh_confd.bDescriptorType =
452 1.4.2.2 jdolecek UDESC_OTHER_SPEED_CONFIGURATION;
453 1.4.2.2 jdolecek memcpy(buf, &confd, buflen);
454 1.4.2.2 jdolecek } else
455 1.4.2.2 jdolecek goto fail;
456 1.4.2.2 jdolecek break;
457 1.4.2.2 jdolecek case C(0, UDESC_BOS):
458 1.4.2.2 jdolecek if (bus->ub_revision >= USBREV_3_0) {
459 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_bosd3));
460 1.4.2.2 jdolecek memcpy(buf, &usbroothub_bosd3, buflen);
461 1.4.2.2 jdolecek } else
462 1.4.2.2 jdolecek goto fail;
463 1.4.2.2 jdolecek break;
464 1.4.2.2 jdolecek #define sd ((usb_string_descriptor_t *)buf)
465 1.4.2.2 jdolecek case C(0, UDESC_STRING):
466 1.4.2.2 jdolecek /* Language table */
467 1.4.2.2 jdolecek buflen = usb_makelangtbl(sd, len);
468 1.4.2.2 jdolecek break;
469 1.4.2.2 jdolecek case C(1, UDESC_STRING):
470 1.4.2.2 jdolecek /* Vendor */
471 1.4.2.2 jdolecek buflen = usb_makestrdesc(sd, len, "NetBSD");
472 1.4.2.2 jdolecek break;
473 1.4.2.2 jdolecek case C(2, UDESC_STRING):
474 1.4.2.2 jdolecek /* Product */
475 1.4.2.2 jdolecek buflen = usb_makestrdesc(sd, len, "Root hub");
476 1.4.2.2 jdolecek break;
477 1.4.2.2 jdolecek #undef sd
478 1.4.2.2 jdolecek default:
479 1.4.2.2 jdolecek /* Default to error */
480 1.4.2.2 jdolecek buflen = -1;
481 1.4.2.2 jdolecek }
482 1.4.2.2 jdolecek break;
483 1.4.2.2 jdolecek case C(UR_GET_DESCRIPTOR, UT_READ_CLASS_DEVICE):
484 1.4.2.2 jdolecek buflen = min(len, sizeof(usbroothub_hubd));
485 1.4.2.2 jdolecek memcpy(buf, &usbroothub_hubd, buflen);
486 1.4.2.2 jdolecek break;
487 1.4.2.2 jdolecek case C(UR_GET_INTERFACE, UT_READ_INTERFACE):
488 1.4.2.2 jdolecek /* Get Interface, 9.4.4 */
489 1.4.2.2 jdolecek if (len > 0) {
490 1.4.2.2 jdolecek uint8_t *out = buf;
491 1.4.2.2 jdolecek
492 1.4.2.2 jdolecek *out = 0;
493 1.4.2.2 jdolecek buflen = sizeof(*out);
494 1.4.2.2 jdolecek }
495 1.4.2.2 jdolecek break;
496 1.4.2.2 jdolecek case C(UR_GET_STATUS, UT_READ_DEVICE):
497 1.4.2.2 jdolecek /* Get Status from device, 9.4.5 */
498 1.4.2.2 jdolecek if (len > 1) {
499 1.4.2.2 jdolecek usb_status_t *out = buf;
500 1.4.2.2 jdolecek
501 1.4.2.2 jdolecek USETW(out->wStatus, UDS_SELF_POWERED);
502 1.4.2.2 jdolecek buflen = sizeof(*out);
503 1.4.2.2 jdolecek }
504 1.4.2.2 jdolecek break;
505 1.4.2.2 jdolecek case C(UR_GET_STATUS, UT_READ_INTERFACE):
506 1.4.2.2 jdolecek case C(UR_GET_STATUS, UT_READ_ENDPOINT):
507 1.4.2.2 jdolecek /* Get Status from interface, endpoint, 9.4.5 */
508 1.4.2.2 jdolecek if (len > 1) {
509 1.4.2.2 jdolecek usb_status_t *out = buf;
510 1.4.2.2 jdolecek
511 1.4.2.2 jdolecek USETW(out->wStatus, 0);
512 1.4.2.2 jdolecek buflen = sizeof(*out);
513 1.4.2.2 jdolecek }
514 1.4.2.2 jdolecek break;
515 1.4.2.2 jdolecek case C(UR_SET_ADDRESS, UT_WRITE_DEVICE):
516 1.4.2.2 jdolecek /* Set Address, 9.4.6 */
517 1.4.2.2 jdolecek USBHIST_LOG(usbdebug, "UR_SET_ADDRESS, UT_WRITE_DEVICE: "
518 1.4.2.2 jdolecek "addr %jd", value, 0, 0, 0);
519 1.4.2.2 jdolecek if (value >= USB_MAX_DEVICES) {
520 1.4.2.2 jdolecek goto fail;
521 1.4.2.2 jdolecek }
522 1.4.2.2 jdolecek bus->ub_rhaddr = value;
523 1.4.2.2 jdolecek break;
524 1.4.2.2 jdolecek case C(UR_SET_CONFIG, UT_WRITE_DEVICE):
525 1.4.2.2 jdolecek /* Set Configuration, 9.4.7 */
526 1.4.2.2 jdolecek if (value != 0 && value != 1) {
527 1.4.2.2 jdolecek goto fail;
528 1.4.2.2 jdolecek }
529 1.4.2.2 jdolecek bus->ub_rhconf = value;
530 1.4.2.2 jdolecek break;
531 1.4.2.2 jdolecek case C(UR_SET_DESCRIPTOR, UT_WRITE_DEVICE):
532 1.4.2.2 jdolecek /* Set Descriptor, 9.4.8, not supported */
533 1.4.2.2 jdolecek break;
534 1.4.2.2 jdolecek case C(UR_SET_FEATURE, UT_WRITE_DEVICE):
535 1.4.2.2 jdolecek case C(UR_SET_FEATURE, UT_WRITE_INTERFACE):
536 1.4.2.2 jdolecek case C(UR_SET_FEATURE, UT_WRITE_ENDPOINT):
537 1.4.2.2 jdolecek /* Set Feature, 9.4.9, not supported */
538 1.4.2.2 jdolecek goto fail;
539 1.4.2.2 jdolecek case C(UR_SET_INTERFACE, UT_WRITE_INTERFACE):
540 1.4.2.2 jdolecek /* Set Interface, 9.4.10, not supported */
541 1.4.2.2 jdolecek break;
542 1.4.2.2 jdolecek case C(UR_SYNCH_FRAME, UT_WRITE_ENDPOINT):
543 1.4.2.2 jdolecek /* Synch Frame, 9.4.11, not supported */
544 1.4.2.2 jdolecek break;
545 1.4.2.2 jdolecek default:
546 1.4.2.2 jdolecek /* Default to error */
547 1.4.2.2 jdolecek buflen = -1;
548 1.4.2.2 jdolecek break;
549 1.4.2.2 jdolecek }
550 1.4.2.2 jdolecek
551 1.4.2.2 jdolecek actlen = bus->ub_methods->ubm_rhctrl(bus, req, buf, buflen);
552 1.4.2.2 jdolecek USBHIST_LOG(usbdebug, "xfer %#jx buflen %jd actlen %jd",
553 1.4.2.2 jdolecek (uintptr_t)xfer, buflen, actlen, 0);
554 1.4.2.2 jdolecek if (actlen < 0)
555 1.4.2.2 jdolecek goto fail;
556 1.4.2.2 jdolecek
557 1.4.2.2 jdolecek xfer->ux_actlen = actlen;
558 1.4.2.2 jdolecek err = USBD_NORMAL_COMPLETION;
559 1.4.2.2 jdolecek
560 1.4.2.2 jdolecek fail:
561 1.4.2.2 jdolecek USBHIST_LOG(usbdebug, "xfer %#jx err %jd", (uintptr_t)xfer, err, 0, 0);
562 1.4.2.2 jdolecek
563 1.4.2.2 jdolecek xfer->ux_status = err;
564 1.4.2.2 jdolecek mutex_enter(bus->ub_lock);
565 1.4.2.2 jdolecek usb_transfer_complete(xfer);
566 1.4.2.2 jdolecek mutex_exit(bus->ub_lock);
567 1.4.2.2 jdolecek
568 1.4.2.2 jdolecek return USBD_NORMAL_COMPLETION;
569 1.4.2.2 jdolecek }
570 1.4.2.2 jdolecek
571 1.4.2.2 jdolecek /* Abort a root control request. */
572 1.4.2.2 jdolecek Static void
573 1.4.2.2 jdolecek roothub_ctrl_abort(struct usbd_xfer *xfer)
574 1.4.2.2 jdolecek {
575 1.4.2.2 jdolecek
576 1.4.2.2 jdolecek /* Nothing to do, all transfers are synchronous. */
577 1.4.2.2 jdolecek }
578 1.4.2.2 jdolecek
579 1.4.2.2 jdolecek /* Close the root pipe. */
580 1.4.2.2 jdolecek Static void
581 1.4.2.2 jdolecek roothub_ctrl_close(struct usbd_pipe *pipe)
582 1.4.2.2 jdolecek {
583 1.4.2.2 jdolecek
584 1.4.2.2 jdolecek /* Nothing to do. */
585 1.4.2.2 jdolecek }
586 1.4.2.2 jdolecek
587 1.4.2.2 jdolecek Static void
588 1.4.2.2 jdolecek roothub_ctrl_done(struct usbd_xfer *xfer)
589 1.4.2.2 jdolecek {
590 1.4.2.2 jdolecek
591 1.4.2.2 jdolecek /* Nothing to do. */
592 1.4.2.2 jdolecek }
593 1.4.2.2 jdolecek
594 1.4.2.2 jdolecek static void
595 1.4.2.2 jdolecek roothub_noop(struct usbd_pipe *pipe)
596 1.4.2.2 jdolecek {
597 1.4.2.2 jdolecek
598 1.4.2.2 jdolecek }
599