]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c
[Description]:
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassCbi.c
1 /** @file
2
3 Copyright (c) 2007 - 2008, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 UsbMassCbi.c
15
16 Abstract:
17
18 Implementation of the USB mass storage Control/Bulk/Interrupt transpor.
19 Notice: it is being obseleted by the standard body in favor of the BOT
20 (Bulk-Only Transport).
21
22 Revision History
23
24
25 **/
26
27 #include "UsbMass.h"
28 #include "UsbMassCbi.h"
29
30 STATIC
31 EFI_STATUS
32 UsbCbiResetDevice (
33 IN VOID *Context,
34 IN BOOLEAN ExtendedVerification
35 );
36
37
38 /**
39 Initialize the USB mass storage class CBI transport protocol.
40 If Context isn't NULL, it will save its context in it.
41
42 @param UsbIo The USB IO to use
43 @param Context The variable to save context in
44
45 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
46 @retval EFI_UNSUPPORTED The device isn't supported
47 @retval EFI_SUCCESS The CBI protocol is initialized.
48
49 **/
50 STATIC
51 EFI_STATUS
52 UsbCbiInit (
53 IN EFI_USB_IO_PROTOCOL *UsbIo,
54 OUT VOID **Context OPTIONAL
55 )
56 {
57 USB_CBI_PROTOCOL *UsbCbi;
58 EFI_USB_INTERFACE_DESCRIPTOR *Interface;
59 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;
60 EFI_STATUS Status;
61 UINT8 Index;
62
63 //
64 // Allocate the CBI context
65 //
66 UsbCbi = AllocateZeroPool (
67 sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
68 );
69
70 if (UsbCbi == NULL) {
71 return EFI_OUT_OF_RESOURCES;
72 }
73
74 UsbCbi->UsbIo = UsbIo;
75
76 //
77 // Get the interface descriptor and validate that it is a USB mass
78 // storage class CBI interface.
79 //
80 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface);
81 if (EFI_ERROR (Status)) {
82 goto ON_ERROR;
83 }
84
85 Interface = &UsbCbi->Interface;
86 if ((Interface->InterfaceProtocol != USB_MASS_STORE_CBI0)
87 && (Interface->InterfaceProtocol != USB_MASS_STORE_CBI1)) {
88 Status = EFI_UNSUPPORTED;
89 goto ON_ERROR;
90 }
91
92 //
93 // Locate and save the bulk-in, bulk-out, and interrupt endpoint
94 //
95 for (Index = 0; Index < Interface->NumEndpoints; Index++) {
96 Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
97 if (EFI_ERROR (Status)) {
98 continue;
99 }
100
101 if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
102 //
103 // Use the first Bulk-In and Bulk-Out endpoints
104 //
105 if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
106 (UsbCbi->BulkInEndpoint == NULL)) {
107
108 UsbCbi->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1);
109 CopyMem(UsbCbi->BulkInEndpoint, &EndPoint, sizeof (EndPoint));;
110 }
111
112 if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
113 (UsbCbi->BulkOutEndpoint == NULL)) {
114
115 UsbCbi->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 1;
116 CopyMem(UsbCbi->BulkOutEndpoint, &EndPoint, sizeof (EndPoint));
117 }
118
119 } else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) {
120 //
121 // Use the first interrupt endpoint if it is CBI0
122 //
123 if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) &&
124 (UsbCbi->InterruptEndpoint == NULL)) {
125
126 UsbCbi->InterruptEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 2;
127 CopyMem(UsbCbi->InterruptEndpoint, &EndPoint, sizeof (EndPoint));
128 }
129 }
130 }
131
132 if ((UsbCbi->BulkInEndpoint == NULL)
133 || (UsbCbi->BulkOutEndpoint == NULL)
134 || ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0)
135 && (UsbCbi->InterruptEndpoint == NULL))) {
136 Status = EFI_UNSUPPORTED;
137 goto ON_ERROR;
138 }
139
140 if (Context != NULL) {
141 *Context = UsbCbi;
142 } else {
143 gBS->FreePool (UsbCbi);
144 }
145 return EFI_SUCCESS;
146
147 ON_ERROR:
148 gBS->FreePool (UsbCbi);
149 return Status;
150 }
151
152
153
154 /**
155 Send the command to the device using class specific control transfer.
156
157 @param UsbCbi The USB CBI protocol
158 @param Cmd The high level command to transfer to device
159 @param CmdLen The length of the command
160 @param Timeout The time to wait the command to finish
161
162 @retval EFI_SUCCESS The command is transferred to device
163 @retval Others The command failed to transfer to device
164
165 **/
166 STATIC
167 EFI_STATUS
168 UsbCbiSendCommand (
169 IN USB_CBI_PROTOCOL *UsbCbi,
170 IN UINT8 *Cmd,
171 IN UINT8 CmdLen,
172 IN UINT32 Timeout
173 )
174 {
175 EFI_USB_DEVICE_REQUEST Request;
176 EFI_STATUS Status;
177 UINT32 TransStatus;
178 UINTN DataLen;
179 INTN Retry;
180
181 //
182 // Fill in the device request, CBI use the "Accept Device-Specific
183 // Cmd" (ADSC) class specific request to send commands
184 //
185 Request.RequestType = 0x21;
186 Request.Request = 0;
187 Request.Value = 0;
188 Request.Index = UsbCbi->Interface.InterfaceNumber;
189 Request.Length = CmdLen;
190
191 Status = EFI_SUCCESS;
192 Timeout = Timeout / USB_MASS_1_MILLISECOND;
193
194 for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
195 //
196 // Use the UsbIo to send the command to the device
197 //
198 TransStatus = 0;
199 DataLen = CmdLen;
200
201 Status = UsbCbi->UsbIo->UsbControlTransfer (
202 UsbCbi->UsbIo,
203 &Request,
204 EfiUsbDataOut,
205 Timeout,
206 Cmd,
207 DataLen,
208 &TransStatus
209 );
210 //
211 // The device can fail the command by STALL the control endpoint.
212 // It can delay the command by NAK the data or status stage, this
213 // is a "class-specific exemption to the USB specification". Retry
214 // if the command is NAKed.
215 //
216 if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
217 continue;
218 }
219
220 break;
221 }
222
223 return Status;
224 }
225
226
227 /**
228 Transfer data between the device and host. The CBI contains three phase,
229 command, data, and status. This is data phase.
230
231 @param UsbCbi The USB CBI device
232 @param DataDir The direction of the data transfer
233 @param Data The buffer to hold the data
234 @param TransLen The expected transfer length
235 @param Timeout The time to wait the command to execute
236
237 @retval EFI_SUCCESS The data transfer succeeded
238 @retval Others Failed to transfer all the data
239
240 **/
241 STATIC
242 EFI_STATUS
243 UsbCbiDataTransfer (
244 IN USB_CBI_PROTOCOL *UsbCbi,
245 IN EFI_USB_DATA_DIRECTION DataDir,
246 IN OUT UINT8 *Data,
247 IN OUT UINTN *TransLen,
248 IN UINT32 Timeout
249 )
250 {
251 EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
252 EFI_STATUS Status;
253 UINT32 TransStatus;
254 UINTN Remain;
255 UINTN Increment;
256 UINT8 *Next;
257 UINTN Retry;
258
259 //
260 // It's OK if no data to transfer
261 //
262 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
263 return EFI_SUCCESS;
264 }
265
266 //
267 // Select the endpoint then issue the transfer
268 //
269 if (DataDir == EfiUsbDataIn) {
270 Endpoint = UsbCbi->BulkInEndpoint;
271 } else {
272 Endpoint = UsbCbi->BulkOutEndpoint;
273 }
274
275 Next = Data;
276 Remain = *TransLen;
277 Retry = 0;
278 Status = EFI_SUCCESS;
279 Timeout = Timeout / USB_MASS_1_MILLISECOND;
280
281 //
282 // Transfer the data, if the device returns NAK, retry it.
283 //
284 while (Remain > 0) {
285 TransStatus = 0;
286
287 if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) {
288 Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize;
289 } else {
290 Increment = Remain;
291 }
292
293 Status = UsbCbi->UsbIo->UsbBulkTransfer (
294 UsbCbi->UsbIo,
295 Endpoint->EndpointAddress,
296 Next,
297 &Increment,
298 Timeout,
299 &TransStatus
300 );
301 if (EFI_ERROR (Status)) {
302 if (TransStatus == EFI_USB_ERR_NAK) {
303 //
304 // The device can NAK the host if either the data/buffer isn't
305 // aviable or the command is in-progress. The data can be partly
306 // transferred. The transfer is aborted if several succssive data
307 // transfer commands are NAKed.
308 //
309 if (Increment == 0) {
310 if (++Retry > USB_CBI_MAX_RETRY) {
311 goto ON_EXIT;
312 }
313
314 } else {
315 Next += Increment;
316 Remain -= Increment;
317 Retry = 0;
318 }
319
320 continue;
321 }
322
323 //
324 // The device can fail the command by STALL the bulk endpoint.
325 // Clear the stall if that is the case.
326 //
327 if (TransStatus == EFI_USB_ERR_STALL) {
328 UsbClearEndpointStall (UsbCbi->UsbIo, Endpoint->EndpointAddress);
329 }
330
331 goto ON_EXIT;
332 }
333
334 Next += Increment;
335 Remain -= Increment;
336 }
337
338 ON_EXIT:
339 *TransLen -= Remain;
340 return Status;
341 }
342
343
344 /**
345 Get the result of high level command execution from interrupt
346 endpoint. This function returns the USB transfer status, and
347 put the high level command execution result in Result.
348
349 @param UsbCbi The USB CBI protocol
350 @param Timeout The time to wait the command to execute
351 @param Result GC_TODO: add argument description
352
353 @retval EFI_SUCCESS The high level command execution result is
354 retrieved in Result.
355 @retval Others Failed to retrieve the result.
356
357 **/
358 STATIC
359 EFI_STATUS
360 UsbCbiGetStatus (
361 IN USB_CBI_PROTOCOL *UsbCbi,
362 IN UINT32 Timeout,
363 OUT USB_CBI_STATUS *Result
364 )
365 {
366 UINTN Len;
367 UINT8 Endpoint;
368 EFI_STATUS Status;
369 UINT32 TransStatus;
370 INTN Retry;
371
372 Endpoint = UsbCbi->InterruptEndpoint->EndpointAddress;
373 Status = EFI_SUCCESS;
374 Timeout = Timeout / USB_MASS_1_MILLISECOND;
375
376 //
377 // Attemp to the read the result from interrupt endpoint
378 //
379 for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
380 TransStatus = 0;
381 Len = sizeof (USB_CBI_STATUS);
382
383 Status = UsbCbi->UsbIo->UsbSyncInterruptTransfer (
384 UsbCbi->UsbIo,
385 Endpoint,
386 Result,
387 &Len,
388 Timeout,
389 &TransStatus
390 );
391 //
392 // The CBI can NAK the interrupt endpoint if the command is in-progress.
393 //
394 if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
395 continue;
396 }
397
398 break;
399 }
400
401 return Status;
402 }
403
404
405 /**
406 Execute USB mass storage command through the CBI0/CBI1 transport protocol
407
408 @param Context The USB CBI device
409 @param Cmd The command to transfer to device
410 @param CmdLen The length of the command
411 @param DataDir The direction of data transfer
412 @param Data The buffer to hold the data
413 @param DataLen The length of the buffer
414 @param Lun Should be 0, this field for bot only
415 @param Timeout The time to wait
416 @param CmdStatus The result of the command execution
417
418 @retval EFI_SUCCESS The command is executed OK and result in CmdStatus.
419 @retval EFI_DEVICE_ERROR Failed to execute the command
420
421 **/
422 STATIC
423 EFI_STATUS
424 UsbCbiExecCommand (
425 IN VOID *Context,
426 IN VOID *Cmd,
427 IN UINT8 CmdLen,
428 IN EFI_USB_DATA_DIRECTION DataDir,
429 IN VOID *Data,
430 IN UINT32 DataLen,
431 IN UINT8 Lun,
432 IN UINT32 Timeout,
433 OUT UINT32 *CmdStatus
434 )
435 {
436 USB_CBI_PROTOCOL *UsbCbi;
437 USB_CBI_STATUS Result;
438 EFI_STATUS Status;
439 UINTN TransLen;
440
441 *CmdStatus = USB_MASS_CMD_SUCCESS;
442 UsbCbi = (USB_CBI_PROTOCOL *) Context;
443
444 //
445 // Send the command to the device. Return immediately if device
446 // rejects the command.
447 //
448 Status = UsbCbiSendCommand (UsbCbi, Cmd, CmdLen, Timeout);
449 if (EFI_ERROR (Status)) {
450 DEBUG ((EFI_D_ERROR, "UsbCbiExecCommand: UsbCbiSendCommand (%r)\n",Status));
451 return Status;
452 }
453
454 //
455 // Transfer the data, return this status if no interrupt endpoint
456 // is used to report the transfer status.
457 //
458 TransLen = (UINTN) DataLen;
459
460 Status = UsbCbiDataTransfer (UsbCbi, DataDir, Data, &TransLen, Timeout);
461 if (UsbCbi->InterruptEndpoint == NULL) {
462 DEBUG ((EFI_D_ERROR, "UsbCbiExecCommand: UsbCbiDataTransfer (%r)\n",Status));
463 return Status;
464 }
465
466 //
467 // Get the status, if that succeeds, interpret the result
468 //
469 Status = UsbCbiGetStatus (UsbCbi, Timeout, &Result);
470 if (EFI_ERROR (Status)) {
471 DEBUG ((EFI_D_ERROR, "UsbCbiExecCommand: UsbCbiGetStatus (%r)\n",Status));
472 return EFI_DEVICE_ERROR;
473 }
474
475 if (UsbCbi->Interface.InterfaceSubClass == USB_MASS_STORE_UFI) {
476 //
477 // For UFI device, ASC and ASCQ are returned.
478 //
479 if (Result.Type != 0) {
480 *CmdStatus = USB_MASS_CMD_FAIL;
481 }
482
483 } else {
484 //
485 // Check page 27, CBI spec 1.1 for vaious reture status.
486 //
487 switch (Result.Value & 0x03) {
488 case 0x00:
489 //
490 // Pass
491 //
492 *CmdStatus = USB_MASS_CMD_SUCCESS;
493 break;
494
495 case 0x02:
496 //
497 // Phase Error, response with reset. Fall through to Fail.
498 //
499 UsbCbiResetDevice (UsbCbi, FALSE);
500
501 case 0x01:
502 //
503 // Fail
504 //
505 *CmdStatus = USB_MASS_CMD_FAIL;
506 break;
507
508 case 0x03:
509 //
510 // Persistent Fail, need to send REQUEST SENSE.
511 //
512 *CmdStatus = USB_MASS_CMD_PERSISTENT;
513 break;
514 }
515 }
516
517 return EFI_SUCCESS;
518 }
519
520
521 /**
522 Call the Usb mass storage class transport protocol to
523 reset the device. The reset is defined as a Non-Data
524 command. Don't use UsbCbiExecCommand to send the command
525 to device because that may introduce recursive loop.
526
527 @param Context The USB CBI device protocol
528
529 @retval EFI_SUCCESS the device is reset
530 @retval Others Failed to reset the device
531
532 **/
533 STATIC
534 EFI_STATUS
535 UsbCbiResetDevice (
536 IN VOID *Context,
537 IN BOOLEAN ExtendedVerification
538 )
539 {
540 UINT8 ResetCmd[USB_CBI_RESET_CMD_LEN];
541 USB_CBI_PROTOCOL *UsbCbi;
542 USB_CBI_STATUS Result;
543 EFI_STATUS Status;
544 UINT32 Timeout;
545
546 UsbCbi = (USB_CBI_PROTOCOL *) Context;
547
548 //
549 // Fill in the reset command.
550 //
551 SetMem (ResetCmd, USB_CBI_RESET_CMD_LEN, 0xFF);
552
553 ResetCmd[0] = 0x1D;
554 ResetCmd[1] = 0x04;
555 Timeout = USB_CBI_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;
556
557 //
558 // Send the command to the device. Don't use UsbCbiExecCommand here.
559 //
560 Status = UsbCbiSendCommand (UsbCbi, ResetCmd, USB_CBI_RESET_CMD_LEN, Timeout);
561 if (EFI_ERROR (Status)) {
562 return Status;
563 }
564
565 //
566 // Just retrieve the status and ignore that. Then stall
567 // 50ms to wait it complete
568 //
569 UsbCbiGetStatus (UsbCbi, Timeout, &Result);
570 gBS->Stall (USB_CBI_RESET_DEVICE_STALL);
571
572 //
573 // Clear the Bulk-In and Bulk-Out stall condition and
574 // init data toggle.
575 //
576 UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkInEndpoint->EndpointAddress);
577 UsbClearEndpointStall (UsbCbi->UsbIo, UsbCbi->BulkOutEndpoint->EndpointAddress);
578 return Status;
579 }
580
581
582 /**
583 Clean up the CBI protocol's resource
584
585 @param Context The CBI protocol
586
587 @retval EFI_SUCCESS The resource is cleaned up.
588
589 **/
590 STATIC
591 EFI_STATUS
592 UsbCbiFini (
593 IN VOID *Context
594 )
595 {
596 gBS->FreePool (Context);
597 return EFI_SUCCESS;
598 }
599
600 USB_MASS_TRANSPORT
601 mUsbCbi0Transport = {
602 USB_MASS_STORE_CBI0,
603 UsbCbiInit,
604 UsbCbiExecCommand,
605 UsbCbiResetDevice,
606 NULL,
607 UsbCbiFini
608 };
609
610 USB_MASS_TRANSPORT
611 mUsbCbi1Transport = {
612 USB_MASS_STORE_CBI1,
613 UsbCbiInit,
614 UsbCbiExecCommand,
615 UsbCbiResetDevice,
616 NULL,
617 UsbCbiFini
618 };