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

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

uhci/rh: Fix debug printing macro format

Only print debug output on port status change.

  • Property mode set to 100644
File size: 16.4 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 <async.h>
31#include <ddi.h>
32#include <errno.h>
33#include <macros.h>
34#include <mem.h>
35#include <sys/time.h>
36
37#include <usb/debug.h>
38#include <usb/descriptor.h>
39#include <usb/classes/hub.h>
40#include <usb/request.h>
41#include <usb/usb.h>
42
43#include "uhci_rh.h"
44
45enum {
46 UHCI_RH_PORT_COUNT = 2,
47 UHCI_PORT_BYTES = STATUS_BYTES(UHCI_RH_PORT_COUNT),
48};
49
50/** Hub descriptor. */
51static const struct {
52 /** Common hub descriptor header */
53 usb_hub_descriptor_header_t header;
54 /** Port removable status bits */
55 uint8_t removable[UHCI_PORT_BYTES];
56 /** Port powered status bits */
57 uint8_t powered[UHCI_PORT_BYTES];
58} __attribute__((packed)) hub_descriptor = {
59 .header = {
60 .length = sizeof(hub_descriptor),
61 .descriptor_type = USB_DESCTYPE_HUB,
62 .port_count = UHCI_RH_PORT_COUNT,
63 .characteristics =
64 HUB_CHAR_NO_POWER_SWITCH_FLAG | HUB_CHAR_NO_OC_FLAG,
65 .power_good_time = 50,
66 .max_current = 0,
67 },
68 .removable = { 0 },
69 .powered = { 0xFF },
70};
71
72static usbvirt_device_ops_t ops;
73
74/** Initialize uhci rh structure.
75 * @param instance Memory place to initialize.
76 * @param ports Pointer to TWO UHCI RH port registers.
77 * @param name device name, passed to virthub init
78 * @return Error code, EOK on success.
79 */
80int uhci_rh_init(uhci_rh_t *instance, ioport16_t *ports, const char *name)
81{
82 assert(instance);
83 instance->ports[0] = ports;
84 instance->ports[1] = ports + 1;
85 instance->reset_changed[0] = false;
86 instance->reset_changed[1] = false;
87 return virthub_base_init(&instance->base, name, &ops, instance,
88 NULL, &hub_descriptor.header, HUB_STATUS_CHANGE_PIPE);
89}
90
91/** Schedule USB batch for the root hub.
92 *
93 * @param instance UHCI rh instance
94 * @param batch USB communication batch
95 * @return EOK.
96 *
97 * The result of scheduling is always EOK. The result of communication does
98 * not have to be.
99 */
100int uhci_rh_schedule(uhci_rh_t *instance, usb_transfer_batch_t *batch)
101{
102 assert(instance);
103 assert(batch);
104
105 const usb_target_t target = {{
106 .address = batch->ep->address,
107 .endpoint = batch->ep->endpoint
108 }};
109 batch->error = virthub_base_request(&instance->base, target,
110 usb_transfer_batch_direction(batch), (void*)batch->setup_buffer,
111 batch->buffer, batch->buffer_size, &batch->transfered_size);
112 usb_transfer_batch_finish(batch, NULL);
113 usb_transfer_batch_destroy(batch);
114 return EOK;
115}
116
117/** UHCI port register bits */
118enum {
119 STATUS_CONNECTED = (1 << 0),
120 STATUS_CONNECTED_CHANGED = (1 << 1),
121 STATUS_ENABLED = (1 << 2),
122 STATUS_ENABLED_CHANGED = (1 << 3),
123 STATUS_LINE_D_PLUS = (1 << 4),
124 STATUS_LINE_D_MINUS = (1 << 5),
125 STATUS_RESUME = (1 << 6),
126 STATUS_ALWAYS_ONE = (1 << 7),
127
128 STATUS_LOW_SPEED = (1 << 8),
129 STATUS_IN_RESET = (1 << 9),
130 STATUS_SUSPEND = (1 << 12),
131
132 STATUS_CHANGE_BITS = STATUS_CONNECTED_CHANGED | STATUS_ENABLED_CHANGED,
133 STATUS_WC_BITS = STATUS_CHANGE_BITS,
134};
135
136/* HUB ROUTINES IMPLEMENTATION */
137
138static void uhci_port_reset_enable(ioport16_t *port)
139{
140 assert(port);
141 uint16_t port_status = pio_read_16(port);
142 /* We don't wan to remove changes, that's hub drivers work */
143 port_status &= ~STATUS_WC_BITS;
144 port_status |= STATUS_IN_RESET;
145 pio_write_16(port, port_status);
146 async_usleep(50000);
147 port_status = pio_read_16(port);
148 port_status &= ~STATUS_IN_RESET;
149 pio_write_16(port, port_status);
150 while ((port_status = pio_read_16(port)) & STATUS_IN_RESET);
151 /* PIO delay, should not be longer than 3ms as the device might
152 * enter suspend state. */
153 udelay(10);
154 /* Drop ConnectionChange as some UHCI hw
155 * sets this bit after reset, that is incorrect */
156 port_status &= ~STATUS_WC_BITS;
157 pio_write_16(port, port_status | STATUS_ENABLED | STATUS_CONNECTED_CHANGED);
158}
159#define TEST_SIZE_INIT(size, port, hub) \
160do { \
161 if (uint16_usb2host(setup_packet->length) != size) \
162 return ESTALL; \
163 port = uint16_usb2host(setup_packet->index) - 1; \
164 if (port != 0 && port != 1) \
165 return EINVAL; \
166 hub = virthub_get_data(device); \
167 assert(hub);\
168} while (0)
169
170#define RH_DEBUG(hub, port, msg, ...) \
171do { \
172 if ((int)port >= 0) \
173 usb_log_debug("RH(%p-%d): " msg, hub, port, ##__VA_ARGS__); \
174 else \
175 usb_log_debug("RH(%p):" msg, hub, ##__VA_ARGS__); \
176} while (0)
177
178/** USB HUB port state request handler.
179 * @param device Virtual hub device
180 * @param setup_packet USB setup stage data.
181 * @param[out] data destination data buffer, size must be at least
182 * setup_packet->length bytes
183 * @param[out] act_size Sized of the valid response part of the buffer.
184 * @return Error code.
185 *
186 * Do not confuse with port status. Port state reports data line states,
187 * it is usefull for debuging purposes only.
188 */
189static int req_get_port_state(usbvirt_device_t *device,
190 const usb_device_request_setup_packet_t *setup_packet,
191 uint8_t *data, size_t *act_size)
192{
193 uhci_rh_t *hub;
194 unsigned port;
195 TEST_SIZE_INIT(1, port, hub);
196 if (setup_packet->value != 0)
197 return EINVAL;
198
199 const uint16_t value = pio_read_16(hub->ports[port]);
200 data[0] = ((value & STATUS_LINE_D_MINUS) ? 1 : 0)
201 | ((value & STATUS_LINE_D_PLUS) ? 2 : 0);
202 RH_DEBUG(hub, port, "Bus state %" PRIx8 "(source %" PRIx16")\n",
203 data[0], value);
204 *act_size = 1;
205 return EOK;
206}
207
208#define BIT_VAL(val, bit) \
209 ((val & bit) ? 1 : 0)
210#define UHCI2USB(val, bit, feat) \
211 (BIT_VAL(val, bit) << feat)
212
213/** Port status request handler.
214 * @param device Virtual hub device
215 * @param setup_packet USB setup stage data.
216 * @param[out] data destination data buffer, size must be at least
217 * setup_packet->length bytes
218 * @param[out] act_size Sized of the valid response part of the buffer.
219 * @return Error code.
220 *
221 * Converts status reported via ioport to USB format.
222 * @note: reset change status needs to be handled in sw.
223 */
224static int req_get_port_status(usbvirt_device_t *device,
225 const usb_device_request_setup_packet_t *setup_packet,
226 uint8_t *data, size_t *act_size)
227{
228 uhci_rh_t *hub;
229 unsigned port;
230 TEST_SIZE_INIT(4, port, hub);
231 if (setup_packet->value != 0)
232 return EINVAL;
233
234 const uint16_t val = pio_read_16(hub->ports[port]);
235 const uint32_t status = uint32_host2usb(
236 UHCI2USB(val, STATUS_CONNECTED, USB_HUB_FEATURE_PORT_CONNECTION) |
237 UHCI2USB(val, STATUS_ENABLED, USB_HUB_FEATURE_PORT_ENABLE) |
238 UHCI2USB(val, STATUS_SUSPEND, USB_HUB_FEATURE_PORT_SUSPEND) |
239 UHCI2USB(val, STATUS_IN_RESET, USB_HUB_FEATURE_PORT_RESET) |
240 UHCI2USB(val, STATUS_ALWAYS_ONE, USB_HUB_FEATURE_PORT_POWER) |
241 UHCI2USB(val, STATUS_LOW_SPEED, USB_HUB_FEATURE_PORT_LOW_SPEED) |
242 UHCI2USB(val, STATUS_CONNECTED_CHANGED, USB_HUB_FEATURE_C_PORT_CONNECTION) |
243 UHCI2USB(val, STATUS_ENABLED_CHANGED, USB_HUB_FEATURE_C_PORT_ENABLE) |
244// UHCI2USB(val, STATUS_SUSPEND, USB_HUB_FEATURE_C_PORT_SUSPEND) |
245 ((hub->reset_changed[port] ? 1 : 0) << USB_HUB_FEATURE_C_PORT_RESET)
246 );
247 RH_DEBUG(hub, port, "Port status %" PRIx32 " (source %" PRIx16
248 "%s)\n", uint32_usb2host(status), val,
249 hub->reset_changed[port] ? "-reset" : "");
250 memcpy(data, &status, sizeof(status));
251 *act_size = sizeof(status);;
252 return EOK;
253}
254
255/** Port clear feature request handler.
256 * @param device Virtual hub device
257 * @param setup_packet USB setup stage data.
258 * @param[out] data destination data buffer, size must be at least
259 * setup_packet->length bytes
260 * @param[out] act_size Sized of the valid response part of the buffer.
261 * @return Error code.
262 */
263static int req_clear_port_feature(usbvirt_device_t *device,
264 const usb_device_request_setup_packet_t *setup_packet,
265 uint8_t *data, size_t *act_size)
266{
267 uhci_rh_t *hub;
268 unsigned port;
269 TEST_SIZE_INIT(0, port, hub);
270 const unsigned feature = uint16_usb2host(setup_packet->value);
271 const uint16_t status = pio_read_16(hub->ports[port]);
272 const uint16_t val = status & (~STATUS_WC_BITS);
273 switch (feature) {
274 case USB_HUB_FEATURE_PORT_ENABLE:
275 RH_DEBUG(hub, port, "Clear port enable (status %"
276 PRIx16 ")\n", status);
277 pio_write_16(hub->ports[port], val & ~STATUS_ENABLED);
278 break;
279 case USB_HUB_FEATURE_PORT_SUSPEND:
280 RH_DEBUG(hub, port, "Clear port suspend (status %"
281 PRIx16 ")\n", status);
282 pio_write_16(hub->ports[port], val & ~STATUS_SUSPEND);
283 // TODO we should do resume magic
284 usb_log_warning("Resume is not implemented on port %u\n", port);
285 break;
286 case USB_HUB_FEATURE_PORT_POWER:
287 RH_DEBUG(hub, port, "Clear port power (status %" PRIx16 ")\n",
288 status);
289 /* We are always powered */
290 usb_log_warning("Tried to power off port %u\n", port);
291 break;
292 case USB_HUB_FEATURE_C_PORT_CONNECTION:
293 RH_DEBUG(hub, port, "Clear port conn change (status %"
294 PRIx16 ")\n", status);
295 pio_write_16(hub->ports[port], val | STATUS_CONNECTED_CHANGED);
296 break;
297 case USB_HUB_FEATURE_C_PORT_RESET:
298 RH_DEBUG(hub, port, "Clear port reset change (status %"
299 PRIx16 ")\n", status);
300 hub->reset_changed[port] = false;
301 break;
302 case USB_HUB_FEATURE_C_PORT_ENABLE:
303 RH_DEBUG(hub, port, "Clear port enable change (status %"
304 PRIx16 ")\n", status);
305 pio_write_16(hub->ports[port], status | STATUS_ENABLED_CHANGED);
306 break;
307 case USB_HUB_FEATURE_C_PORT_SUSPEND:
308 RH_DEBUG(hub, port, "Clear port suspend change (status %"
309 PRIx16 ")\n", status);
310 //TODO
311 return ENOTSUP;
312 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
313 RH_DEBUG(hub, port, "Clear port OC change (status %"
314 PRIx16 ")\n", status);
315 /* UHCI Does not report over current */
316 //TODO: newer chips do, but some have broken wiring
317 break;
318 default:
319 RH_DEBUG(hub, port, "Clear unknown feature %d (status %"
320 PRIx16 ")\n", feature, status);
321 usb_log_warning("Clearing feature %d is unsupported\n",
322 feature);
323 return ESTALL;
324 }
325 return EOK;
326}
327
328/** Port set feature request handler.
329 * @param device Virtual hub device
330 * @param setup_packet USB setup stage data.
331 * @param[out] data destination data buffer, size must be at least
332 * setup_packet->length bytes
333 * @param[out] act_size Sized of the valid response part of the buffer.
334 * @return Error code.
335 */
336static int req_set_port_feature(usbvirt_device_t *device,
337 const usb_device_request_setup_packet_t *setup_packet,
338 uint8_t *data, size_t *act_size)
339{
340 uhci_rh_t *hub;
341 unsigned port;
342 TEST_SIZE_INIT(0, port, hub);
343 const unsigned feature = uint16_usb2host(setup_packet->value);
344 const uint16_t status = pio_read_16(hub->ports[port]);
345 switch (feature) {
346 case USB_HUB_FEATURE_PORT_RESET:
347 RH_DEBUG(hub, port, "Set port reset before (status %" PRIx16
348 ")\n", status);
349 uhci_port_reset_enable(hub->ports[port]);
350 hub->reset_changed[port] = true;
351 RH_DEBUG(hub, port, "Set port reset after (status %" PRIx16
352 ")\n", pio_read_16(hub->ports[port]));
353 break;
354 case USB_HUB_FEATURE_PORT_SUSPEND:
355 RH_DEBUG(hub, port, "Set port suspend (status %" PRIx16
356 ")\n", status);
357 pio_write_16(hub->ports[port],
358 (status & ~STATUS_WC_BITS) | STATUS_SUSPEND);
359 usb_log_warning("Suspend is not implemented on port %u\n", port);
360 break;
361 case USB_HUB_FEATURE_PORT_POWER:
362 RH_DEBUG(hub, port, "Set port power (status %" PRIx16
363 ")\n", status);
364 /* We are always powered */
365 usb_log_warning("Tried to power port %u\n", port);
366 break;
367 case USB_HUB_FEATURE_C_PORT_CONNECTION:
368 case USB_HUB_FEATURE_C_PORT_ENABLE:
369 case USB_HUB_FEATURE_C_PORT_SUSPEND:
370 case USB_HUB_FEATURE_C_PORT_OVER_CURRENT:
371 RH_DEBUG(hub, port, "Set port change flag (status %" PRIx16
372 ")\n", status);
373 /* These are voluntary and don't have to be set
374 * there is no way we could do it on UHCI anyway */
375 break;
376 default:
377 RH_DEBUG(hub, port, "Set unknown feature %d (status %" PRIx16
378 ")\n", feature, status);
379 usb_log_warning("Setting feature %d is unsupported\n",
380 feature);
381 return ESTALL;
382 }
383 return EOK;
384}
385
386/** Status change handler.
387 * @param device Virtual hub device
388 * @param endpoint Endpoint number
389 * @param tr_type Transfer type
390 * @param buffer Response destination
391 * @param buffer_size Bytes available in buffer
392 * @param actual_size Size us the used part of the dest buffer.
393 *
394 * Produces status mask. Bit 0 indicates hub status change the other bits
395 * represent port status change. Endian does not matter as UHCI root hubs
396 * only need 1 byte.
397 */
398static int req_status_change_handler(usbvirt_device_t *device,
399 usb_endpoint_t endpoint, usb_transfer_type_t tr_type,
400 void *buffer, size_t buffer_size, size_t *actual_size)
401{
402 uhci_rh_t *hub = virthub_get_data(device);
403 assert(hub);
404
405 if (buffer_size < 1)
406 return ESTALL;
407
408 const uint16_t status_a = pio_read_16(hub->ports[0]);
409 const uint16_t status_b = pio_read_16(hub->ports[1]);
410 const uint8_t status =
411 ((((status_a & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[0]) ?
412 0x2 : 0) |
413 ((((status_b & STATUS_CHANGE_BITS) != 0) || hub->reset_changed[1]) ?
414 0x4 : 0);
415 if (status)
416 RH_DEBUG(hub, -1, "Event mask %" PRIx8
417 " (status_a %" PRIx16 "%s),"
418 " (status_b %" PRIx16 "%s)\n", status,
419 status_a, hub->reset_changed[0] ? "-reset" : "",
420 status_b, hub->reset_changed[1] ? "-reset" : "" );
421 ((uint8_t *)buffer)[0] = status;
422 *actual_size = 1;
423 return EOK;
424}
425
426/** UHCI root hub request handlers */
427static const usbvirt_control_request_handler_t control_transfer_handlers[] = {
428 {
429 STD_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
430 .name = "GetDescriptor",
431 .callback = virthub_base_get_hub_descriptor,
432 },
433 {
434 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_DEVREQ_GET_DESCRIPTOR),
435 .name = "GetDescriptor",
436 .callback = virthub_base_get_hub_descriptor,
437 },
438 {
439 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_DESCRIPTOR),
440 .name = "GetHubDescriptor",
441 .callback = virthub_base_get_hub_descriptor,
442 },
443 {
444 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATE),
445 .name = "GetBusState",
446 .callback = req_get_port_state,
447 },
448 {
449 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
450 .name = "GetPortStatus",
451 .callback = req_get_port_status
452 },
453 {
454 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_CLEAR_FEATURE),
455 .name = "ClearHubFeature",
456 /* Hub features are overcurrent and supply good,
457 * this request may only clear changes that we never report*/
458 .callback = req_nop,
459 },
460 {
461 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_CLEAR_FEATURE),
462 .name = "ClearPortFeature",
463 .callback = req_clear_port_feature
464 },
465 {
466 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_GET_STATUS),
467 .name = "GetHubStatus",
468 /* UHCI can't report OC condition or,
469 * lose power source */
470 .callback = virthub_base_get_null_status,
471 },
472 {
473 CLASS_REQ_IN(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_GET_STATUS),
474 .name = "GetPortStatus",
475 .callback = req_get_port_status
476 },
477 {
478 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_DEVICE, USB_HUB_REQUEST_SET_FEATURE),
479 .name = "SetHubFeature",
480 /* Hub features are overcurrent and supply good,
481 * this request may only set changes that we never report*/
482 .callback = req_nop,
483 },
484 {
485 CLASS_REQ_OUT(USB_REQUEST_RECIPIENT_OTHER, USB_HUB_REQUEST_SET_FEATURE),
486 .name = "SetPortFeature",
487 .callback = req_set_port_feature
488 },
489 {
490 .callback = NULL
491 }
492};
493
494static usbvirt_device_ops_t ops = {
495 .control = control_transfer_handlers,
496 .data_in[HUB_STATUS_CHANGE_PIPE] = req_status_change_handler,
497};
Note: See TracBrowser for help on using the repository browser.