Browse Source

[usb] Add basic support for USB hubs

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
e17e771a13
5 changed files with 805 additions and 0 deletions
  1. 1
    0
      src/Makefile
  2. 3
    0
      src/drivers/bus/usb.c
  3. 552
    0
      src/drivers/usb/usbhub.c
  4. 248
    0
      src/drivers/usb/usbhub.h
  5. 1
    0
      src/include/ipxe/errfile.h

+ 1
- 0
src/Makefile View File

@@ -83,6 +83,7 @@ SRCDIRS		+= drivers/block
83 83
 SRCDIRS		+= drivers/nvs
84 84
 SRCDIRS		+= drivers/bitbash
85 85
 SRCDIRS		+= drivers/infiniband
86
+SRCDIRS		+= drivers/usb
86 87
 SRCDIRS		+= interface/pxe interface/efi interface/smbios
87 88
 SRCDIRS		+= interface/bofm
88 89
 SRCDIRS		+= interface/xen

+ 3
- 0
src/drivers/bus/usb.c View File

@@ -1646,3 +1646,6 @@ struct usb_port * usb_root_hub_port ( struct usb_device *usb ) {
1646 1646
 
1647 1647
 	return usb->port;
1648 1648
 }
1649
+
1650
+/* Drag in hub driver */
1651
+REQUIRE_OBJECT ( usbhub );

+ 552
- 0
src/drivers/usb/usbhub.c View File

@@ -0,0 +1,552 @@
1
+/*
2
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ */
19
+
20
+FILE_LICENCE ( GPL2_OR_LATER );
21
+
22
+#include <stdlib.h>
23
+#include <string.h>
24
+#include <unistd.h>
25
+#include <errno.h>
26
+#include <assert.h>
27
+#include <byteswap.h>
28
+#include <ipxe/usb.h>
29
+#include "usbhub.h"
30
+
31
+/** @file
32
+ *
33
+ * USB hub driver
34
+ *
35
+ */
36
+
37
+/**
38
+ * Refill interrupt ring
39
+ *
40
+ * @v hubdev		Hub device
41
+ */
42
+static void hub_refill ( struct usb_hub_device *hubdev ) {
43
+	struct io_buffer *iobuf;
44
+	size_t mtu = hubdev->intr.mtu;
45
+	int rc;
46
+
47
+	/* Enqueue any available I/O buffers */
48
+	while ( ( iobuf = list_first_entry ( &hubdev->intrs, struct io_buffer,
49
+					     list ) ) ) {
50
+
51
+		/* Reset size */
52
+		iob_put ( iobuf, ( mtu - iob_len ( iobuf ) ) );
53
+
54
+		/* Enqueue I/O buffer */
55
+		if ( ( rc = usb_stream ( &hubdev->intr, iobuf ) ) != 0 ) {
56
+			DBGC ( hubdev, "HUB %s could not enqueue interrupt: "
57
+			       "%s\n", hubdev->name, strerror ( rc ) );
58
+			/* Leave in available list and wait for next refill */
59
+			return;
60
+		}
61
+
62
+		/* Remove from available list */
63
+		list_del ( &iobuf->list );
64
+	}
65
+
66
+	/* Stop refill process */
67
+	process_del ( &hubdev->refill );
68
+}
69
+
70
+/** Refill process descriptor */
71
+static struct process_descriptor hub_refill_desc =
72
+	PROC_DESC ( struct usb_hub_device, refill, hub_refill );
73
+
74
+/**
75
+ * Complete interrupt transfer
76
+ *
77
+ * @v ep		USB endpoint
78
+ * @v iobuf		I/O buffer
79
+ * @v rc		Completion status code
80
+ */
81
+static void hub_complete ( struct usb_endpoint *ep,
82
+			   struct io_buffer *iobuf, int rc ) {
83
+	struct usb_hub_device *hubdev =
84
+		container_of ( ep, struct usb_hub_device, intr );
85
+	struct usb_hub *hub = hubdev->hub;
86
+	uint8_t *data = iobuf->data;
87
+	unsigned int bits = ( 8 * iob_len ( iobuf ) );
88
+	unsigned int i;
89
+
90
+	/* Ignore packets cancelled when the endpoint closes */
91
+	if ( ! ep->open )
92
+		goto done;
93
+
94
+	/* Ignore packets with errors */
95
+	if ( rc != 0 ) {
96
+		DBGC ( hubdev, "HUB %s interrupt failed: %s\n",
97
+		       hubdev->name, strerror ( rc ) );
98
+		DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
99
+		goto done;
100
+	}
101
+
102
+	/* Report any port status changes */
103
+	for ( i = 1 ; i <= hub->ports ; i++ ) {
104
+
105
+		/* Sanity check */
106
+		if ( i > bits ) {
107
+			DBGC ( hubdev, "HUB %s underlength interrupt:\n",
108
+			       hubdev->name );
109
+			DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) );
110
+			goto done;
111
+		}
112
+
113
+		/* Report port status change if applicable */
114
+		if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) {
115
+			DBGC2 ( hubdev, "HUB %s port %d status changed\n",
116
+				hubdev->name, i );
117
+			usb_port_changed ( usb_port ( hub, i ) );
118
+		}
119
+	}
120
+
121
+ done:
122
+	/* Return I/O buffer to available list */
123
+	list_add_tail ( &iobuf->list, &hubdev->intrs );
124
+
125
+	/* Start refill process */
126
+	process_add ( &hubdev->refill );
127
+}
128
+
129
+/** Interrupt endpoint operations */
130
+static struct usb_endpoint_driver_operations usb_hub_intr_operations = {
131
+	.complete = hub_complete,
132
+};
133
+
134
+/**
135
+ * Open hub
136
+ *
137
+ * @v hub		USB hub
138
+ * @ret rc		Return status code
139
+ */
140
+static int hub_open ( struct usb_hub *hub ) {
141
+	struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
142
+	struct usb_device *usb = hubdev->usb;
143
+	struct io_buffer *iobuf;
144
+	struct io_buffer *tmp;
145
+	unsigned int i;
146
+	int rc;
147
+
148
+	/* Ensure ports are powered */
149
+	for ( i = 1 ; i <= hub->ports ; i++ ) {
150
+		if ( ( rc = usb_hub_set_port_feature ( usb, i,
151
+						       USB_HUB_PORT_POWER,
152
+						       0 ) ) != 0 ) {
153
+			DBGC ( hubdev, "HUB %s port %d could not apply power: "
154
+			       "%s\n", hubdev->name, i, strerror ( rc ) );
155
+			goto err_power;
156
+		}
157
+	}
158
+
159
+	/* Allocate I/O buffers */
160
+	for ( i = 0 ; i < USB_HUB_INTR_FILL ; i++ ) {
161
+		iobuf = alloc_iob ( hubdev->intr.mtu );
162
+		if ( ! iobuf ) {
163
+			rc = -ENOMEM;
164
+			goto err_alloc_iob;
165
+		}
166
+		list_add ( &iobuf->list, &hubdev->intrs );
167
+	}
168
+
169
+	/* Open interrupt endpoint */
170
+	if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) {
171
+		DBGC ( hubdev, "HUB %s could not register interrupt: %s\n",
172
+		       hubdev->name, strerror ( rc ) );
173
+		goto err_open;
174
+	}
175
+
176
+	/* Start refill process */
177
+	process_add ( &hubdev->refill );
178
+
179
+	/* Refill interrupt ring */
180
+	hub_refill ( hubdev );
181
+
182
+	return 0;
183
+
184
+	usb_endpoint_close ( &hubdev->intr );
185
+ err_open:
186
+ err_alloc_iob:
187
+	list_for_each_entry_safe ( iobuf, tmp, &hubdev->intrs, list ) {
188
+		list_del ( &iobuf->list );
189
+		free_iob ( iobuf );
190
+	}
191
+ err_power:
192
+	return rc;
193
+}
194
+
195
+/**
196
+ * Close hub
197
+ *
198
+ * @v hub		USB hub
199
+ */
200
+static void hub_close ( struct usb_hub *hub ) {
201
+	struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
202
+	struct io_buffer *iobuf;
203
+	struct io_buffer *tmp;
204
+
205
+	/* Close interrupt endpoint */
206
+	usb_endpoint_close ( &hubdev->intr );
207
+
208
+	/* Stop refill process */
209
+	process_del ( &hubdev->refill );
210
+
211
+	/* Free I/O buffers */
212
+	list_for_each_entry_safe ( iobuf, tmp, &hubdev->intrs, list ) {
213
+		list_del ( &iobuf->list );
214
+		free_iob ( iobuf );
215
+	}
216
+}
217
+
218
+/**
219
+ * Enable port
220
+ *
221
+ * @v hub		USB hub
222
+ * @v port		USB port
223
+ * @ret rc		Return status code
224
+ */
225
+static int hub_enable ( struct usb_hub *hub, struct usb_port *port ) {
226
+	struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
227
+	struct usb_device *usb = hubdev->usb;
228
+	struct usb_hub_port_status status;
229
+	unsigned int current;
230
+	unsigned int i;
231
+	int rc;
232
+
233
+	/* Initiate reset if applicable */
234
+	if ( ( hub->protocol < USB_PROTO_3_0 ) &&
235
+	     ( ( rc = usb_hub_set_port_feature ( usb, port->address,
236
+						 USB_HUB_PORT_RESET, 0 ) )!=0)){
237
+		DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n",
238
+		       hubdev->name, port->address, strerror ( rc ) );
239
+		return rc;
240
+	}
241
+
242
+	/* Wait for port to become enabled */
243
+	for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) {
244
+
245
+		/* Check for port being enabled */
246
+		if ( ( rc = usb_hub_get_port_status ( usb, port->address,
247
+						      &status ) ) != 0 ) {
248
+			DBGC ( hubdev, "HUB %s port %d could not get status: "
249
+			       "%s\n", hubdev->name, port->address,
250
+			       strerror ( rc ) );
251
+			return rc;
252
+		}
253
+		current = le16_to_cpu ( status.current );
254
+		if ( current & ( 1 << USB_HUB_PORT_ENABLE ) )
255
+			return 0;
256
+
257
+		/* Delay */
258
+		mdelay ( 1 );
259
+	}
260
+
261
+	DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n",
262
+	       hubdev->name, port->address );
263
+	return -ETIMEDOUT;
264
+}
265
+
266
+/**
267
+ * Disable port
268
+ *
269
+ * @v hub		USB hub
270
+ * @v port		USB port
271
+ * @ret rc		Return status code
272
+ */
273
+static int hub_disable ( struct usb_hub *hub, struct usb_port *port ) {
274
+	struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
275
+	struct usb_device *usb = hubdev->usb;
276
+	int rc;
277
+
278
+	/* Disable port */
279
+	if ( ( rc = usb_hub_clear_port_feature ( usb, port->address,
280
+						 USB_HUB_PORT_ENABLE, 0 ) )!=0){
281
+		DBGC ( hubdev, "HUB %s port %d could not disable: %s\n",
282
+		       hubdev->name, port->address, strerror ( rc ) );
283
+		return rc;
284
+	}
285
+
286
+	return 0;
287
+}
288
+
289
+/**
290
+ * Clear port status change bits
291
+ *
292
+ * @v hubdev		USB hub device
293
+ * @v port		Port number
294
+ * @v changed		Port status change bits
295
+ * @ret rc		Return status code
296
+ */
297
+static int hub_clear_changes ( struct usb_hub_device *hubdev,
298
+			       unsigned int port, uint16_t changed ) {
299
+	struct usb_device *usb = hubdev->usb;
300
+	unsigned int bit;
301
+	unsigned int feature;
302
+	int rc;
303
+
304
+	/* Clear each set bit */
305
+	for ( bit = 0 ; bit < 16 ; bit++ ) {
306
+
307
+		/* Skip unset bits */
308
+		if ( ! ( changed & ( 1 << bit ) ) )
309
+			continue;
310
+
311
+		/* Skip unused features */
312
+		feature = USB_HUB_C_FEATURE ( bit );
313
+		if ( ! ( hubdev->features & ( 1 << feature ) ) )
314
+			continue;
315
+
316
+		/* Clear bit */
317
+		if ( ( rc = usb_hub_clear_port_feature ( usb, port,
318
+							 feature, 0 ) ) != 0 ) {
319
+			DBGC ( hubdev, "HUB %s port %d could not clear feature "
320
+			       "%d: %s\n", hubdev->name, port, feature,
321
+			       strerror ( rc ) );
322
+			return rc;
323
+		}
324
+	}
325
+
326
+	return 0;
327
+}
328
+
329
+/**
330
+ * Update port speed
331
+ *
332
+ * @v hub		USB hub
333
+ * @v port		USB port
334
+ * @ret rc		Return status code
335
+ */
336
+static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) {
337
+	struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub );
338
+	struct usb_device *usb = hubdev->usb;
339
+	struct usb_hub_port_status status;
340
+	unsigned int current;
341
+	unsigned int changed;
342
+	int rc;
343
+
344
+	/* Get port status */
345
+	if ( ( rc = usb_hub_get_port_status ( usb, port->address,
346
+					      &status ) ) != 0 ) {
347
+		DBGC ( hubdev, "HUB %s port %d could not get status: %s\n",
348
+		       hubdev->name, port->address, strerror ( rc ) );
349
+		return rc;
350
+	}
351
+	current = le16_to_cpu ( status.current );
352
+	changed = le16_to_cpu ( status.changed );
353
+	DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n",
354
+		hubdev->name, port->address, changed, current );
355
+
356
+	/* Update port speed */
357
+	if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) {
358
+		if ( hub->protocol >= USB_PROTO_3_0 ) {
359
+			port->speed = USB_SPEED_SUPER;
360
+		} else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) {
361
+			port->speed = USB_SPEED_LOW;
362
+		} else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) {
363
+			port->speed = USB_SPEED_HIGH;
364
+		} else {
365
+			port->speed = USB_SPEED_FULL;
366
+		}
367
+	} else {
368
+		port->speed = USB_SPEED_NONE;
369
+	}
370
+
371
+	/* Clear port status change bits */
372
+	if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0)
373
+		return rc;
374
+
375
+	return 0;
376
+}
377
+
378
+/** USB hub operations */
379
+static struct usb_hub_driver_operations hub_operations = {
380
+	.open = hub_open,
381
+	.close = hub_close,
382
+	.enable = hub_enable,
383
+	.disable = hub_disable,
384
+	.speed = hub_speed,
385
+};
386
+
387
+/**
388
+ * Probe USB hub
389
+ *
390
+ * @v func		USB function
391
+ * @v config		Configuration descriptor
392
+ * @ret rc		Return status code
393
+ */
394
+static int hub_probe ( struct usb_function *func,
395
+		       struct usb_configuration_descriptor *config ) {
396
+	struct usb_device *usb = func->usb;
397
+	struct usb_bus *bus = usb->port->hub->bus;
398
+	struct usb_hub_device *hubdev;
399
+	struct usb_interface_descriptor *interface;
400
+	union usb_hub_descriptor desc;
401
+	unsigned int depth;
402
+	unsigned int ports;
403
+	int enhanced;
404
+	int rc;
405
+
406
+	/* Allocate and initialise structure */
407
+	hubdev = zalloc ( sizeof ( *hubdev ) );
408
+	if ( ! hubdev ) {
409
+		rc = -ENOMEM;
410
+		goto err_alloc;
411
+	}
412
+	enhanced = ( usb->port->protocol >= USB_PROTO_3_0 );
413
+	hubdev->name = func->name;
414
+	hubdev->usb = usb;
415
+	hubdev->features =
416
+		( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES );
417
+	usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations );
418
+	INIT_LIST_HEAD ( &hubdev->intrs );
419
+	process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL );
420
+
421
+	/* Locate hub interface descriptor */
422
+	interface = usb_interface_descriptor ( config, func->interface[0], 0 );
423
+	if ( ! interface ) {
424
+		DBGC ( hubdev, "HUB %s has no interface descriptor\n",
425
+		       hubdev->name );
426
+		rc = -EINVAL;
427
+		goto err_interface;
428
+	}
429
+
430
+	/* Locate interrupt endpoint descriptor */
431
+	if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface,
432
+					     USB_INTERRUPT, 0 ) ) != 0 ) {
433
+		DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: "
434
+		       "%s\n", hubdev->name, strerror ( rc ) );
435
+		goto err_endpoint;
436
+	}
437
+
438
+	/* Set hub depth */
439
+	depth = usb_depth ( usb );
440
+	if ( enhanced ) {
441
+		if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) {
442
+			DBGC ( hubdev, "HUB %s could not set hub depth to %d: "
443
+			       "%s\n", hubdev->name, depth, strerror ( rc ) );
444
+			goto err_set_hub_depth;
445
+		}
446
+	}
447
+
448
+	/* Get hub descriptor */
449
+	if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) {
450
+		DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n",
451
+		       hubdev->name, strerror ( rc ) );
452
+		goto err_hub_descriptor;
453
+	}
454
+	ports = desc.basic.ports;
455
+	DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name,
456
+	       ports, depth, ( enhanced ? " (enhanced)" : "" ) );
457
+
458
+	/* Allocate hub */
459
+	hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations );
460
+	if ( ! hubdev->hub ) {
461
+		rc = -ENOMEM;
462
+		goto err_alloc_hub;
463
+	}
464
+	usb_hub_set_drvdata ( hubdev->hub, hubdev );
465
+
466
+	/* Register hub */
467
+	if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) {
468
+		DBGC ( hubdev, "HUB %s could not register: %s\n",
469
+		       hubdev->name, strerror ( rc ) );
470
+		goto err_register_hub;
471
+	}
472
+
473
+	usb_func_set_drvdata ( func, hubdev );
474
+	return 0;
475
+
476
+	unregister_usb_hub ( hubdev->hub );
477
+ err_register_hub:
478
+	free_usb_hub ( hubdev->hub );
479
+ err_alloc_hub:
480
+ err_hub_descriptor:
481
+ err_set_hub_depth:
482
+ err_endpoint:
483
+ err_interface:
484
+	free ( hubdev );
485
+ err_alloc:
486
+	return rc;
487
+}
488
+
489
+/**
490
+ * Remove USB hub
491
+ *
492
+ * @v func		USB function
493
+ * @ret rc		Return status code
494
+ */
495
+static void hub_remove ( struct usb_function *func ) {
496
+	struct usb_hub_device *hubdev = usb_func_get_drvdata ( func );
497
+	struct usb_hub *hub = hubdev->hub;
498
+	struct usb_device *usb = hubdev->usb;
499
+	struct usb_port *port;
500
+	unsigned int i;
501
+
502
+	/* If hub has been unplugged, mark all ports as unplugged */
503
+	if ( usb->port->speed == USB_SPEED_NONE ) {
504
+		for ( i = 1 ; i <= hub->ports ; i++ ) {
505
+			port = usb_port ( hub, i );
506
+			port->speed = USB_SPEED_NONE;
507
+		}
508
+	}
509
+
510
+	/* Unregister hub */
511
+	unregister_usb_hub ( hubdev->hub );
512
+	assert ( ! process_running ( &hubdev->refill ) );
513
+	assert ( list_empty ( &hubdev->intrs ) );
514
+
515
+	/* Free hub */
516
+	free_usb_hub ( hubdev->hub );
517
+
518
+	/* Free hub device */
519
+	free ( hubdev );
520
+}
521
+
522
+/** USB hub device IDs */
523
+static struct usb_device_id hub_ids[] = {
524
+	{
525
+		.name = "hub-1",
526
+		.vendor = USB_ANY_ID,
527
+		.product = USB_ANY_ID,
528
+		.class = {
529
+			.class = USB_CLASS_HUB,
530
+			.subclass = 0,
531
+			.protocol = 0,
532
+		},
533
+	},
534
+	{
535
+		.name = "hub-2",
536
+		.vendor = USB_ANY_ID,
537
+		.product = USB_ANY_ID,
538
+		.class = {
539
+			.class = USB_CLASS_HUB,
540
+			.subclass = 0,
541
+			.protocol = 1,
542
+		},
543
+	},
544
+};
545
+
546
+/** USB hub driver */
547
+struct usb_driver usb_hub_driver __usb_driver = {
548
+	.ids = hub_ids,
549
+	.id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ),
550
+	.probe = hub_probe,
551
+	.remove = hub_remove,
552
+};

+ 248
- 0
src/drivers/usb/usbhub.h View File

@@ -0,0 +1,248 @@
1
+#ifndef _USBHUB_H
2
+#define _USBHUB_H
3
+
4
+/** @file
5
+ *
6
+ * USB hubs
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <ipxe/usb.h>
13
+#include <ipxe/list.h>
14
+#include <ipxe/process.h>
15
+
16
+/** Request recipient is a port */
17
+#define USB_HUB_RECIP_PORT ( 3 << 0 )
18
+
19
+/** A basic USB hub descriptor */
20
+struct usb_hub_descriptor_basic {
21
+	/** Descriptor header */
22
+	struct usb_descriptor_header header;
23
+	/** Number of ports */
24
+	uint8_t ports;
25
+	/** Characteristics */
26
+	uint16_t characteristics;
27
+	/** Power-on delay (in 2ms intervals */
28
+	uint8_t delay;
29
+	/** Controller current (in mA) */
30
+	uint8_t current;
31
+} __attribute__ (( packed ));
32
+
33
+/** A basic USB hub descriptor */
34
+#define USB_HUB_DESCRIPTOR 41
35
+
36
+/** An enhanced USB hub descriptor */
37
+struct usb_hub_descriptor_enhanced {
38
+	/** Basic USB hub descriptor */
39
+	struct usb_hub_descriptor_basic basic;
40
+	/** Header decode latency */
41
+	uint8_t latency;
42
+	/** Maximum delay */
43
+	uint16_t delay;
44
+	/** Removable device bitmask */
45
+	uint16_t removable;
46
+} __attribute__ (( packed ));
47
+
48
+/** An enhanced USB hub descriptor */
49
+#define USB_HUB_DESCRIPTOR_ENHANCED 42
50
+
51
+/** A USB hub descriptor */
52
+union usb_hub_descriptor {
53
+	/** Descriptor header */
54
+	struct usb_descriptor_header header;
55
+	/** Basic hub descriptor */
56
+	struct usb_hub_descriptor_basic basic;
57
+	/** Enhanced hub descriptor */
58
+	struct usb_hub_descriptor_enhanced enhanced;
59
+} __attribute__ (( packed ));
60
+
61
+/** Port status */
62
+struct usb_hub_port_status {
63
+	/** Current status */
64
+	uint16_t current;
65
+	/** Changed status */
66
+	uint16_t changed;
67
+} __attribute__ (( packed ));
68
+
69
+/** Current connect status feature */
70
+#define USB_HUB_PORT_CONNECTION 0
71
+
72
+/** Port enabled/disabled feature */
73
+#define USB_HUB_PORT_ENABLE 1
74
+
75
+/** Port reset feature */
76
+#define USB_HUB_PORT_RESET 4
77
+
78
+/** Port power feature */
79
+#define USB_HUB_PORT_POWER 8
80
+
81
+/** Low-speed device attached */
82
+#define USB_HUB_PORT_LOW_SPEED 9
83
+
84
+/** High-speed device attached */
85
+#define USB_HUB_PORT_HIGH_SPEED 10
86
+
87
+/** Connect status changed */
88
+#define USB_HUB_C_PORT_CONNECTION 16
89
+
90
+/** Port enable/disable changed */
91
+#define	USB_HUB_C_PORT_ENABLE 17
92
+
93
+/** Suspend changed */
94
+#define USB_HUB_C_PORT_SUSPEND 18
95
+
96
+/** Over-current indicator changed */
97
+#define USB_HUB_C_PORT_OVER_CURRENT 19
98
+
99
+/** Reset changed */
100
+#define USB_HUB_C_PORT_RESET 20
101
+
102
+/** Link state changed */
103
+#define USB_HUB_C_PORT_LINK_STATE 25
104
+
105
+/** Configuration error */
106
+#define USB_HUB_C_PORT_CONFIG_ERROR 26
107
+
108
+/** Calculate feature from change bit number */
109
+#define USB_HUB_C_FEATURE( bit ) ( 16 + (bit) )
110
+
111
+/** USB features */
112
+#define USB_HUB_FEATURES						\
113
+	( ( 1 << USB_HUB_C_PORT_CONNECTION ) |				\
114
+	  ( 1 << USB_HUB_C_PORT_ENABLE ) |				\
115
+	  ( 1 << USB_HUB_C_PORT_SUSPEND ) |				\
116
+	  ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) |			\
117
+	  ( 1 << USB_HUB_C_PORT_RESET ) )
118
+
119
+/** USB features for enhanced hubs */
120
+#define USB_HUB_FEATURES_ENHANCED					\
121
+	( ( 1 << USB_HUB_C_PORT_CONNECTION ) |				\
122
+	  ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) |			\
123
+	  ( 1 << USB_HUB_C_PORT_RESET ) |				\
124
+	  ( 1 << USB_HUB_C_PORT_LINK_STATE ) |				\
125
+	  ( 1 << USB_HUB_C_PORT_CONFIG_ERROR ) )
126
+
127
+/** Set hub depth */
128
+#define USB_HUB_SET_HUB_DEPTH						\
129
+	( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE |		\
130
+	  USB_REQUEST_TYPE ( 12 ) )
131
+
132
+/**
133
+ * Get hub descriptor
134
+ *
135
+ * @v usb		USB device
136
+ * @v enhanced		Hub is an enhanced hub
137
+ * @v data		Hub descriptor to fill in
138
+ * @ret rc		Return status code
139
+ */
140
+static inline __attribute__ (( always_inline )) int
141
+usb_hub_get_descriptor ( struct usb_device *usb, int enhanced,
142
+			 union usb_hub_descriptor *data ) {
143
+	unsigned int desc;
144
+	size_t len;
145
+
146
+	/* Determine descriptor type and length */
147
+	desc = ( enhanced ? USB_HUB_DESCRIPTOR_ENHANCED : USB_HUB_DESCRIPTOR );
148
+	len = ( enhanced ? sizeof ( data->enhanced ) : sizeof ( data->basic ) );
149
+
150
+	return usb_get_descriptor ( usb, USB_TYPE_CLASS, desc, 0, 0,
151
+				    &data->header, len );
152
+}
153
+
154
+/**
155
+ * Get port status
156
+ *
157
+ * @v usb		USB device
158
+ * @v port		Port address
159
+ * @v status		Port status descriptor to fill in
160
+ * @ret rc		Return status code
161
+ */
162
+static inline __attribute__ (( always_inline )) int
163
+usb_hub_get_port_status ( struct usb_device *usb, unsigned int port,
164
+			  struct usb_hub_port_status *status ) {
165
+
166
+	return usb_get_status ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ),
167
+				port, status, sizeof ( *status ) );
168
+}
169
+
170
+/**
171
+ * Clear port feature
172
+ *
173
+ * @v usb		USB device
174
+ * @v port		Port address
175
+ * @v feature		Feature to clear
176
+ * @v index		Index (when clearing a port indicator)
177
+ * @ret rc		Return status code
178
+ */
179
+static inline __attribute__ (( always_inline )) int
180
+usb_hub_clear_port_feature ( struct usb_device *usb, unsigned int port,
181
+			     unsigned int feature, unsigned int index ) {
182
+
183
+	return usb_clear_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ),
184
+				   feature, ( ( index << 8 ) | port ) );
185
+}
186
+
187
+/**
188
+ * Set port feature
189
+ *
190
+ * @v usb		USB device
191
+ * @v port		Port address
192
+ * @v feature		Feature to clear
193
+ * @v index		Index (when clearing a port indicator)
194
+ * @ret rc		Return status code
195
+ */
196
+static inline __attribute__ (( always_inline )) int
197
+usb_hub_set_port_feature ( struct usb_device *usb, unsigned int port,
198
+			   unsigned int feature, unsigned int index ) {
199
+
200
+	return usb_set_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ),
201
+				 feature, ( ( index << 8 ) | port ) );
202
+}
203
+
204
+/**
205
+ * Set hub depth
206
+ *
207
+ * @v usb		USB device
208
+ * @v depth		Hub depth
209
+ * @ret rc		Return status code
210
+ */
211
+static inline __attribute__ (( always_inline )) int
212
+usb_hub_set_hub_depth ( struct usb_device *usb, unsigned int depth ) {
213
+
214
+	return usb_control ( usb, USB_HUB_SET_HUB_DEPTH, depth, 0, NULL, 0 );
215
+}
216
+
217
+/** A USB hub device */
218
+struct usb_hub_device {
219
+	/** Name */
220
+	const char *name;
221
+	/** USB device */
222
+	struct usb_device *usb;
223
+	/** USB hub */
224
+	struct usb_hub *hub;
225
+	/** Features */
226
+	unsigned int features;
227
+
228
+	/** Interrupt endpoint */
229
+	struct usb_endpoint intr;
230
+	/** Recycled interrupt I/O buffers */
231
+	struct list_head intrs;
232
+	/** Interrupt endpoint refill process */
233
+	struct process refill;
234
+};
235
+
236
+/** Interrupt ring fill level
237
+ *
238
+ * This is a policy decision.
239
+ */
240
+#define USB_HUB_INTR_FILL 4
241
+
242
+/** Maximum time to wait for port to become enabled
243
+ *
244
+ * This is a policy decision.
245
+ */
246
+#define USB_HUB_ENABLE_MAX_WAIT_MS 100
247
+
248
+#endif /* _USBHUB_H */

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -77,6 +77,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
77 77
 #define ERRFILE_linux		     ( ERRFILE_DRIVER | 0x00050000 )
78 78
 #define ERRFILE_pcivpd		     ( ERRFILE_DRIVER | 0x00060000 )
79 79
 #define ERRFILE_usb		     ( ERRFILE_DRIVER | 0x00070000 )
80
+#define ERRFILE_usbhub		     ( ERRFILE_DRIVER | 0x00080000 )
80 81
 
81 82
 #define ERRFILE_nvs		     ( ERRFILE_DRIVER | 0x00100000 )
82 83
 #define ERRFILE_spi		     ( ERRFILE_DRIVER | 0x00110000 )

Loading…
Cancel
Save