Browse Source

[usb] Provide generic framework for refilling receive endpoints

Provide a generic framework for allocating, refilling, and optionally
recycling I/O buffers used by bulk IN and interrupt endpoints.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
17fc79425e
2 changed files with 168 additions and 0 deletions
  1. 128
    0
      src/drivers/bus/usb.c
  2. 40
    0
      src/include/ipxe/usb.h

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

@@ -313,6 +313,8 @@ int usb_endpoint_open ( struct usb_endpoint *ep ) {
313 313
  err_open:
314 314
 	usb->ep[idx] = NULL;
315 315
  err_already:
316
+	if ( ep->max )
317
+		usb_flush ( ep );
316 318
 	return rc;
317 319
 }
318 320
 
@@ -331,9 +333,14 @@ void usb_endpoint_close ( struct usb_endpoint *ep ) {
331 333
 	/* Close endpoint */
332 334
 	ep->open = 0;
333 335
 	ep->host->close ( ep );
336
+	assert ( ep->fill == 0 );
334 337
 
335 338
 	/* Remove from endpoint list */
336 339
 	usb->ep[idx] = NULL;
340
+
341
+	/* Discard any recycled buffers, if applicable */
342
+	if ( ep->max )
343
+		usb_flush ( ep );
337 344
 }
338 345
 
339 346
 /**
@@ -443,6 +450,9 @@ int usb_message ( struct usb_endpoint *ep, unsigned int request,
443 450
 		return rc;
444 451
 	}
445 452
 
453
+	/* Increment fill level */
454
+	ep->fill++;
455
+
446 456
 	return 0;
447 457
 }
448 458
 
@@ -476,6 +486,9 @@ int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
476 486
 		return rc;
477 487
 	}
478 488
 
489
+	/* Increment fill level */
490
+	ep->fill++;
491
+
479 492
 	return 0;
480 493
 }
481 494
 
@@ -490,6 +503,10 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
490 503
 			int rc ) {
491 504
 	struct usb_device *usb = ep->usb;
492 505
 
506
+	/* Decrement fill level */
507
+	assert ( ep->fill > 0 );
508
+	ep->fill--;
509
+
493 510
 	/* Record error (if any) */
494 511
 	ep->rc = rc;
495 512
 	if ( ( rc != 0 ) && ep->open ) {
@@ -502,6 +519,117 @@ void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf,
502 519
 	ep->driver->complete ( ep, iobuf, rc );
503 520
 }
504 521
 
522
+/******************************************************************************
523
+ *
524
+ * Endpoint refilling
525
+ *
526
+ ******************************************************************************
527
+ */
528
+
529
+/**
530
+ * Prefill endpoint recycled buffer list
531
+ *
532
+ * @v ep		USB endpoint
533
+ * @ret rc		Return status code
534
+ */
535
+int usb_prefill ( struct usb_endpoint *ep ) {
536
+	struct io_buffer *iobuf;
537
+	size_t len = ( ep->len ? ep->len : ep->mtu );
538
+	unsigned int fill;
539
+	int rc;
540
+
541
+	/* Sanity checks */
542
+	assert ( ep->fill == 0 );
543
+	assert ( ep->max > 0 );
544
+	assert ( list_empty ( &ep->recycled ) );
545
+
546
+	/* Fill recycled buffer list */
547
+	for ( fill = 0 ; fill < ep->max ; fill++ ) {
548
+
549
+		/* Allocate I/O buffer */
550
+		iobuf = alloc_iob ( len );
551
+		if ( ! iobuf ) {
552
+			rc = -ENOMEM;
553
+			goto err_alloc;
554
+		}
555
+
556
+		/* Add to recycled buffer list */
557
+		list_add_tail ( &iobuf->list, &ep->recycled );
558
+	}
559
+
560
+	return 0;
561
+
562
+ err_alloc:
563
+	usb_flush ( ep );
564
+	return rc;
565
+}
566
+
567
+/**
568
+ * Refill endpoint
569
+ *
570
+ * @v ep		USB endpoint
571
+ * @ret rc		Return status code
572
+ */
573
+int usb_refill ( struct usb_endpoint *ep ) {
574
+	struct io_buffer *iobuf;
575
+	size_t len = ( ep->len ? ep->len : ep->mtu );
576
+	int rc;
577
+
578
+	/* Sanity checks */
579
+	assert ( ep->open );
580
+	assert ( ep->max > 0 );
581
+
582
+	/* Refill endpoint */
583
+	while ( ep->fill < ep->max ) {
584
+
585
+		/* Get or allocate buffer */
586
+		if ( list_empty ( &ep->recycled ) ) {
587
+			/* Recycled buffer list is empty; allocate new buffer */
588
+			iobuf = alloc_iob ( len );
589
+			if ( ! iobuf )
590
+				return -ENOMEM;
591
+		} else {
592
+			/* Get buffer from recycled buffer list */
593
+			iobuf = list_first_entry ( &ep->recycled,
594
+						   struct io_buffer, list );
595
+			assert ( iobuf != NULL );
596
+			list_del ( &iobuf->list );
597
+		}
598
+
599
+		/* Reset buffer to maximum size */
600
+		assert ( iob_len ( iobuf ) <= len );
601
+		iob_put ( iobuf, ( len - iob_len ( iobuf ) ) );
602
+
603
+		/* Enqueue buffer */
604
+		if ( ( rc = usb_stream ( ep, iobuf, 0 ) ) != 0 ) {
605
+			list_add ( &iobuf->list, &ep->recycled );
606
+			return rc;
607
+		}
608
+	}
609
+
610
+	return 0;
611
+}
612
+
613
+/**
614
+ * Discard endpoint recycled buffer list
615
+ *
616
+ * @v ep		USB endpoint
617
+ */
618
+void usb_flush ( struct usb_endpoint *ep ) {
619
+	struct io_buffer *iobuf;
620
+	struct io_buffer *tmp;
621
+
622
+	/* Sanity checks */
623
+	assert ( ! ep->open );
624
+	assert ( ep->max > 0 );
625
+
626
+	/* Free all I/O buffers */
627
+	list_for_each_entry_safe ( iobuf, tmp, &ep->recycled, list ) {
628
+		list_del ( &iobuf->list );
629
+		free_iob ( iobuf );
630
+	}
631
+}
632
+
505 633
 /******************************************************************************
506 634
  *
507 635
  * Control endpoint

+ 40
- 0
src/include/ipxe/usb.h View File

@@ -379,6 +379,8 @@ struct usb_endpoint {
379 379
 	int open;
380 380
 	/** Current failure state (if any) */
381 381
 	int rc;
382
+	/** Buffer fill level */
383
+	unsigned int fill;
382 384
 
383 385
 	/** Host controller operations */
384 386
 	struct usb_endpoint_host_operations *host;
@@ -386,6 +388,13 @@ struct usb_endpoint {
386 388
 	void *priv;
387 389
 	/** Driver operations */
388 390
 	struct usb_endpoint_driver_operations *driver;
391
+
392
+	/** Recycled I/O buffer list */
393
+	struct list_head recycled;
394
+	/** Refill buffer length */
395
+	size_t len;
396
+	/** Maximum fill level */
397
+	unsigned int max;
389 398
 };
390 399
 
391 400
 /** USB endpoint host controller operations */
@@ -553,6 +562,37 @@ extern int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf,
553 562
 extern void usb_complete_err ( struct usb_endpoint *ep,
554 563
 			       struct io_buffer *iobuf, int rc );
555 564
 
565
+/**
566
+ * Initialise USB endpoint refill
567
+ *
568
+ * @v ep		USB endpoint
569
+ * @v len		Refill buffer length (or zero to use endpoint's MTU)
570
+ * @v max		Maximum fill level
571
+ */
572
+static inline __attribute__ (( always_inline )) void
573
+usb_refill_init ( struct usb_endpoint *ep, size_t len, unsigned int max ) {
574
+
575
+	INIT_LIST_HEAD ( &ep->recycled );
576
+	ep->len = len;
577
+	ep->max = max;
578
+}
579
+
580
+/**
581
+ * Recycle I/O buffer
582
+ *
583
+ * @v ep		USB endpoint
584
+ * @v iobuf		I/O buffer
585
+ */
586
+static inline __attribute__ (( always_inline )) void
587
+usb_recycle ( struct usb_endpoint *ep, struct io_buffer *iobuf ) {
588
+
589
+	list_add_tail ( &iobuf->list, &ep->recycled );
590
+}
591
+
592
+extern int usb_prefill ( struct usb_endpoint *ep );
593
+extern int usb_refill ( struct usb_endpoint *ep );
594
+extern void usb_flush ( struct usb_endpoint *ep );
595
+
556 596
 /**
557 597
  * A USB function
558 598
  *

Loading…
Cancel
Save