Browse Source

[ehci] Support arbitrarily large transfers

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
8f418ee477
1 changed files with 49 additions and 14 deletions
  1. 49
    14
      src/drivers/usb/ehci.c

+ 49
- 14
src/drivers/usb/ehci.c View File

@@ -1220,6 +1220,30 @@ static int ehci_endpoint_message ( struct usb_endpoint *ep,
1220 1220
 	return 0;
1221 1221
 }
1222 1222
 
1223
+/**
1224
+ * Calculate number of transfer descriptors
1225
+ *
1226
+ * @v len		Length of data
1227
+ * @v zlp		Append a zero-length packet
1228
+ * @ret count		Number of transfer descriptors
1229
+ */
1230
+static unsigned int ehci_endpoint_count ( size_t len, int zlp ) {
1231
+	unsigned int count;
1232
+
1233
+	/* Split into 16kB transfers.  A single transfer can handle up
1234
+	 * to 20kB if it happens to be page-aligned, or up to 16kB
1235
+	 * with arbitrary alignment.  We simplify the code by assuming
1236
+	 * that we can fit only 16kB into each transfer.
1237
+	 */
1238
+	count = ( ( len + EHCI_MTU - 1 ) / EHCI_MTU );
1239
+
1240
+	/* Append a zero-length transfer if applicable */
1241
+	if ( zlp || ( count == 0 ) )
1242
+		count++;
1243
+
1244
+	return count;
1245
+}
1246
+
1223 1247
 /**
1224 1248
  * Enqueue stream transfer
1225 1249
  *
@@ -1232,29 +1256,40 @@ static int ehci_endpoint_stream ( struct usb_endpoint *ep,
1232 1256
 				  struct io_buffer *iobuf, int zlp ) {
1233 1257
 	struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep );
1234 1258
 	struct ehci_device *ehci = endpoint->ehci;
1259
+	void *data = iobuf->data;
1260
+	size_t len = iob_len ( iobuf );
1261
+	unsigned int count = ehci_endpoint_count ( len, zlp );
1235 1262
 	unsigned int input = ( ep->address & USB_DIR_IN );
1236
-	struct ehci_transfer xfers[2];
1263
+	unsigned int flags = ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT );
1264
+	struct ehci_transfer xfers[count];
1237 1265
 	struct ehci_transfer *xfer = xfers;
1238
-	size_t len = iob_len ( iobuf );
1266
+	size_t xfer_len;
1267
+	unsigned int i;
1239 1268
 	int rc;
1240 1269
 
1241
-	/* Create transfer */
1242
-	xfer->data = iobuf->data;
1243
-	xfer->len = len;
1244
-	xfer->flags = ( EHCI_FL_IOC |
1245
-			( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) );
1246
-	xfer++;
1247
-	if ( zlp ) {
1248
-		xfer->data = NULL;
1249
-		xfer->len = 0;
1250
-		assert ( ! input );
1251
-		xfer->flags = ( EHCI_FL_IOC | EHCI_FL_PID_OUT );
1270
+	/* Create transfers */
1271
+	for ( i = 0 ; i < count ; i++ ) {
1272
+
1273
+		/* Calculate transfer length */
1274
+		xfer_len = EHCI_MTU;
1275
+		if ( xfer_len > len )
1276
+			xfer_len = len;
1277
+
1278
+		/* Create transfer */
1279
+		xfer->data = data;
1280
+		xfer->len = xfer_len;
1281
+		xfer->flags = flags;
1282
+
1283
+		/* Move to next transfer */
1284
+		data += xfer_len;
1285
+		len -= xfer_len;
1252 1286
 		xfer++;
1253 1287
 	}
1288
+	xfer[-1].flags |= EHCI_FL_IOC;
1254 1289
 
1255 1290
 	/* Enqueue transfer */
1256 1291
 	if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers,
1257
-				   ( xfer - xfers ) ) ) != 0 )
1292
+				   count ) ) != 0 )
1258 1293
 		return rc;
1259 1294
 
1260 1295
 	return 0;

Loading…
Cancel
Save