source: mainline/uspace/drv/bus/usb/uhci/uhci_rh.c@ be554d9

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since be554d9 was be554d9, checked in by Jan Vesely <jano.vesely@…>, 13 years ago

uhci: Add workaround for certain hw.

Fixes UHCI on acer aspire one(NM10/ICH10) after switch to new rh driver.

  • Property mode set to 100644
File size: 13.7 KB
Line 
1/*
2 * Copyright (c) 2013 Jan Vesely
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <assert.h>
30#include <macros.h>
31#include <usb/debug.h>
32#include <ddi.h>
33
34#include "uhci_rh.h"
35
36enum {
37 UHCI_RH_PORT_COUNT = 2,
38 /* 1 byte for hub status bit and 2 port status bits */
39 UHCI_PORT_BYTES = 1,
40};
41
42/** Hub descriptor. */
43static const struct {
44 usb_hub_descriptor_header_t header;
45 uint8_t removable[UHCI_PORT_BYTES];
46 uint8_t powered[UHCI_PORT_BYTES];
47} __attribute__((packed)) hub_descriptor = {
48 .header = {
49 .length = sizeof(hub_descriptor),
50 .descriptor_type = USB_DESCTYPE_HUB,
51 .port_count = UHCI_RH_PORT_COUNT,
52 .characteristics =
53 HUB_CHAR_NO_POWER_SWITCH_FLAG | HUB_CHAR_NO_OC_FLAG,
54 .power_good_time = 50,
55 .max_current = 0,
56 },
57 .removable = { 0 },
58 .powered = { 0xFF },
59};
60
61
62static usbvirt_device_ops_t ops;
63
64int uhci_rh_init(uhci_rh_t *instance, ioport16_t *ports, const char *name)
65{
66 assert(instance);
67 instance->ports[0] = ports;
68 instance->ports[1] = ports + 1;
69 instance->reset_changed[0] = false;
70 instance->reset_changed[1] = false;
71 return virthub_base_init(&instance->base, name, &ops, instance,
72 NULL, &hub_descriptor.header);
73}
74
75int uhci_rh_schedule(uhci_rh_t *instance, usb_transfer_batch_t *batch)
76{
77 assert(instance);
78 assert(batch);
79
80 const usb_target_t target = {{
81 .address = batch->ep->address,
82 .endpoint = batch->ep->endpoint
83 }};
84 batch->error = virthub_base_request(&instance->base, target,
85 usb_transfer_batch_direction(batch),
86 (void*)batch->setup_buffer, batch->buffer,
87 batch->buffer_size, &batch->transfered_size);
88 usb_transfer_batch_finish(batch, NULL);
89 usb_transfer_batch_destroy(batch);
90 return EOK;
91}
92enum {
93 STATUS_CONNECTED = (1 << 0),
94 STATUS_CONNECTED_CHANGED = (1 << 1),
95 STATUS_ENABLED = (1 << 2),
96 STATUS_ENABLED_CHANGED = (1 << 3),
97 STATUS_LINE_D_PLUS = (1 << 4),
98 STATUS_LINE_D_MINUS = (1 << 5),
99 STATUS_RESUME = (1 << 6),
100 STATUS_ALWAYS_ONE = (1 << 7),
101
102 STATUS_LOW_SPEED = (1 << 8),
103 STATUS_IN_RESET = (1 << 9),
104 STATUS_SUSPEND = (1 << 12),
105
106 STATUS_CHANGE_BITS = STATUS_CONNECTED_CHANGED | STATUS_ENABLED_CHANGED,
107 STATUS_WC_BITS = STATUS_CHANGE_BITS,
108};
109/* HUB ROUTINES IMPLEMENTATION */
110static void uhci_port_reset_enable(ioport16_t *port)
111{
112 assert(port);
113 uint16_t port_status = pio_read_16(port);
114 /* We don't wan to remove changes, that's hub drivers work */
115 port_status &= ~STATUS_WC_BITS;
116 port_status |= STATUS_IN_RESET;
117 pio_write_16(port, port_status);
118 async_usleep(50000);
119 port_status = pio_read_16(port);
120 port_status &= ~STATUS_IN_RESET;
121 pio_write_16(port, port_status);
122 while ((port_status = pio_read_16(port)) & STATUS_IN_RESET);
123 /* PIO delay, should not be longer than 3ms as the device might
124 * enter suspend state. */
125 udelay(10);
126 /* Drop ConnectionChange as some UHCI hw
127 * sets this bit after reset, that is incorrect */
128 port_status &= ~STATUS_WC_BITS;
129 pio_write_16(port, port_status | STATUS_ENABLED | STATUS_CONNECTED_CHANGED);
130}
131#define TEST_SIZE_INIT(size, port, hub) \
132do { \
133 if (uint16_usb2host(setup_packet->length) != size) \
134 return ESTALL; \
135 port = uint16_usb2host(setup_packet->index) - 1; \
136 if (port != 0 && port != 1) \
137 return EINVAL; \
138 hub = virthub_get_data(device); \
139 assert(hub);\
140} while (0)
141
142#define RH_DEBUG(d, port, msg, ...) \
143 if ((int)port >= 0) \
144 usb_log_debug("%s: rh-%d: " msg, d->name, port, ##__VA_ARGS__); \
145 else \
146 usb_log_debug("%s: rh: " msg, d->name, ##__VA_ARGS__) \
147
148static int req_get_port_state(usbvirt_device_t *device,
149 const usb_device_request_setup_packet_t *setup_packet,
150 uint8_t *data, size_t *act_size)
151{
152 uhci_rh_t *hub;
153 unsigned port;
154 TEST_SIZE_INIT(1, port, hub);
155 if (setup_packet->value != 0)
156 return EINVAL;
157
158 const uint16_t value = pio_read_16(hub->ports[port]);
159 data[0] = ((value & STATUS_LINE_D_MINUS) ? 1 : 0)
160 | ((value & STATUS_LINE_D_PLUS) ? 2 : 0);
161 RH_DEBUG(device, port, "Bus state %" PRIx8 "(source %" PRIx16")\n",
162 data[0], value);
163 *act_size = 1;
164 return EOK;
165}
166
167#define BIT_VAL(val, bit) \
168 ((val & bit) ? 1 : 0)
169#define UHCI2USB(val, bit, feat) \
170 (BIT_VAL(val, bit) << feat)
171
172static int req_get_port_status(usbvirt_device_t *device,
173 const usb_device_request_setup_packet_t *setup_packet,
174 uint8_t *data, size_t *act_size)
175{
176 uhci_rh_t *hub;
177 unsigned port;
178 TEST_SIZE_INIT(4, port, hub);
179 if (setup_packet->value != 0)
180 return EINVAL;
181
182 const uint16_t val = pio_read_16(hub->ports[port]);
183 const uint32_t status = uint32_host2usb(
184 UHCI2USB(val, STATUS_CONNECTED, USB_HUB_FEATURE_PORT_CONNECTION) |
185 UHCI2USB(val, STATUS_ENABLED, USB_HUB_FEATURE_PORT_ENABLE) |
186 UHCI2USB(val, STATUS_SUSPEND, USB_HUB_FEATURE_PORT_SUSPEND) |
187 UHCI2USB(val, STATUS_IN_RESET, USB_HUB_FEATURE_PORT_RESET) |
188 UHCI2USB(val, STATUS_ALWAYS_ONE, USB_HUB_FEATURE_PORT_POWER) |
189 UHCI2USB(val, STATUS_LOW_SPEED, USB_HUB_FEATURE_PORT_LOW_SPEED) |
190 UHCI2USB(val, STATUS_CONNECTED_CHANGED, USB_HUB_FEATURE_C_PORT_CONNECTION) |
191 UHCI2USB(val, STATUS_ENABLED_CHANGED, USB_HUB_FEATURE_C_PORT_ENABLE) |
192// UHCI2USB(val, STATUS_SUSPEND, USB_HUB_FEATURE_C_PORT_SUSPEND) |
193 ((hub->reset_changed[port] ? 1 : 0) << USB_HUB_FEATURE_C_PORT_RESET)
194 );
195 RH_DEBUG(device, port, "Port status %" PRIx32 " (source %" PRIx16
196 "%s)\n", uint32_usb2host(status), val,
197 hub->reset_changed[port] ? "-reset" : "");
198 memcpy(data, &status, sizeof(status));
199 *act_size = sizeof(status);;
200 return EOK;
201}
202
203static int req_clear_port_feature(usbvirt_device_t *device,
204 const usb_device_request_setup_packet_t *setup_packet,
205 uint8_t *data, size_t *act_size)
206{
207 uhci_rh_t *hub;
208 unsigned port;
209 TEST_SIZE_INIT(0, port, hub);
210 const unsigned feature = uint16_usb2host(setup_packet->value);
211 const uint16_t status = pio_read_16(hub->ports[port]);
212 const uint16_t val = status & (~STATUS_WC_BITS);
213 switch (feature) {
214 case USB_HUB_FEATURE_PORT_ENABLE:
215 RH_DEBUG(device, port, "Clear port enable (status %" PRIx16 ")\n",
216 status);
217 pio_write_16(hub->ports[port], val & ~STATUS_ENABLED);
218 break;
219 case USB_HUB_FEATURE_PORT_SUSPEND:
220 RH_DEBUG(device, port, "Clear port suspend (status %" PRIx16 ")\n",
221 status);
222 pio_write_16(hub->ports[port], val & ~STATUS_SUSPEND);
223 // TODO we should do resume magic
224 usb_log_warning("Resume is not implemented on port %u\n", port);
225 break;
226 case USB_HUB_FEATURE_PORT_POWER:
227 RH_DEBUG(device, port, "Clear port power (status %" PRIx16 ")\n",
228 status);
229 /* We are always powered */
230 usb_log_warning("Tried to power off port %u\n", port);
231 break;
232 case USB_HUB_FEATURE_C_PORT_CONNECTION:
233 RH_DEBUG(device, port, "Clear port conn change (status %" PRIx16
234 ")\n", status);
235 pio_write_16(hub->ports[port], val | STATUS_CONNECTED_CHANGED);
236 break;
237 case USB_HUB_FEATURE_C_PORT_RESET:
238 RH_DEBUG(device, port, "Clear port reset change (status %" PRIx16
239 ")\n", status);
240 hub->reset_changed[port] = false;
241 break;
242 case USB_HUB_FEATURE_C_PORT_ENABLE:
243 RH_DEBUG(device, port, "Clear port enable change (status %" PRIx16
244 ")\n", status);
245 pio_write_16(hub->ports[port], status | STATUS_ENABLED_CHANGED);
246 break;
247 case USB_HUB_FEATURE_C_PORT_SUSPEND:
248 RH_DEBUG(device, port, "Clear port suspend change (status %" PRIx16
249 ")\n", status);
250 //TODO
251 return ENOTSUP;
252 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
253 RH_DEBUG(device, port, "Clear port OC change (status %" PRIx16
254 ")\n", status);
255 /* UHCI Does not report over current */
256 break;
257 default:
258 RH_DEBUG(device, port, "Clear unknown feature %d (status %" PRIx16
259 ")\n", feature, status);
260 usb_log_warning("Clearing feature %d is unsupported\n",
261 feature);
262 return ESTALL;
263 }
264 return EOK;
265}
266
267static int req_set_port_feature(usbvirt_device_t *device,
268 const usb_device_request_setup_packet_t *setup_packet,
269 uint8_t *data, size_t *act_size)
270{
271 uhci_rh_t *hub;
272 unsigned port;
273 TEST_SIZE_INIT(0, port, hub);
274 const unsigned feature = uint16_usb2host(setup_packet->value);
275 const uint16_t status = pio_read_16(hub->ports[port]);
276 switch (feature) {
277 case USB_HUB_FEATURE_PORT_RESET:
278 RH_DEBUG(device, port, "Set port reset before (status %" PRIx16
279 ")\n", status);
280 uhci_port_reset_enable(hub->ports[port]);
281 hub->reset_changed[port] = true;
282 RH_DEBUG(device, port, "Set port reset after (status %" PRIx16
283 ")\n", pio_read_16(hub->ports[port]));
284 break;
285 case USB_HUB_FEATURE_PORT_SUSPEND:
286 RH_DEBUG(device, port, "Set port suspend (status %" PRIx16
287 ")\n", status);
288 pio_write_16(hub->ports[port],
289 (status & ~STATUS_WC_BITS) | STATUS_SUSPEND);
290 usb_log_warning("Suspend is not implemented on port %u\n", port);
291 break;
292 case USB_HUB_FEATURE_PORT_POWER:
293 RH_DEBUG(device, port, "Set port power (status %" PRIx16
294 ")\n", status);
295 /* We are always powered */
296 usb_log_warning("Tried to power port %u\n", port);
297 case USB_HUB_FEATURE_C_PORT_CONNECTION:
298 case USB_HUB_FEATURE_C_PORT_ENABLE:
299 case USB_HUB_FEATURE_C_PORT_SUSPEND:
300 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
301 RH_DEBUG(device, port, "Set port change flag (status %" PRIx16
302 ")\n", status);
303 /* These are voluntary and don't have to be set
304 * there is no way we could do it on UHCI anyway */
305 break;
306 default:
307 RH_DEBUG(device, port, "Set unknown feature %d (status %" PRIx16
308 ")\n", feature, status);
309 usb_log_warning("Setting feature %d is unsupported\n",
310 feature);
311 return ESTALL;
312 }
313 return EOK;
314}
315
316static usbvirt_control_request_handler_t control_transfer_handlers[] = {
317 {
318 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
319 .name = "GetDescriptor",
320 .callback = virthub_base_get_hub_descriptor,
321 },
322 {
323 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
324 .name = "GetDescriptor",
325 .callback = virthub_base_get_hub_descriptor,
326 },
327 {
328 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
329 .name = "GetHubDescriptor",
330 .callback = virthub_base_get_hub_descriptor,
331 },
332 {
333 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATE),
334 .name = "GetBusState",
335 .callback = req_get_port_state,
336 },
337 {
338 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
339 .name = "GetPortStatus",
340 .callback = req_get_port_status
341 },
342 {
343 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
344 .name = "ClearHubFeature",
345 /* Hub features are overcurrent and supply good,
346 * this request may only set changes that we never report*/
347 .callback = req_nop,
348 },
349 {
350 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
351 .name = "ClearPortFeature",
352 .callback = req_clear_port_feature
353 },
354 {
355 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
356 .name = "GetHubStatus",
357 /* UHCI can't report OC condition or,
358 * lose power source */
359 .callback = virthub_base_get_null_status,
360 },
361 {
362 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
363 .name = "GetPortStatus",
364 .callback = req_get_port_status
365 },
366 {
367 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
368 .name = "SetHubFeature",
369 /* Hub features are overcurrent and supply good,
370 * this request may only set changes that we never report*/
371 .callback = req_nop,
372 },
373 {
374 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
375 .name = "SetPortFeature",
376 .callback = req_set_port_feature
377 },
378 {
379 .callback = NULL
380 }
381};
382
383static int req_status_change_handler(usbvirt_device_t *dev,
384 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
385 void *buffer, size_t buffer_size, size_t *actual_size);
386
387static usbvirt_device_ops_t ops = {
388 .control = control_transfer_handlers,
389 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
390};
391
392static int req_status_change_handler(usbvirt_device_t *device,
393 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
394 void *buffer, size_t buffer_size, size_t *actual_size)
395{
396 if (buffer_size < 1)
397 return ESTALL;
398 uhci_rh_t *hub = virthub_get_data(device);
399 assert(hub);
400
401 const uint16_t status_a = pio_read_16(hub->ports[0]);
402 const uint16_t status_b = pio_read_16(hub->ports[1]);
403 const uint8_t status =
404 ((((status_a & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[0]) ?
405 0x2 : 0) |
406 ((((status_b & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[1]) ?
407 0x4 : 0);
408
409 RH_DEBUG(device, -1, "Event mask %" PRIx8
410 " (status_a %" PRIx16 "%s),"
411 " (status_b %" PRIx16 "%s)\n", status,
412 status_a, hub->reset_changed[0] ? "-reset" : "",
413 status_b, hub->reset_changed[1] ? "-reset" : "" );
414 ((uint8_t *)buffer)[0] = status;
415 *actual_size = 1;
416 return EOK;
417}
Note: See TracBrowser for help on using the repository browser.