]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassCbi.c
1. Added EFI_MEDIA_CHANGED and EFI_INVALID_PARAMETER returns in UsbMassReadBlocks().
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassCbi.c
1 /** @file
2
3 Copyright (c) 2007, 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 Controller The device controller
44 @param Context The variable to save context in
45
46 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
47 @retval EFI_UNSUPPORTED The device isn't supported
48 @retval EFI_SUCCESS The CBI protocol is initialized.
49
50 **/
51 STATIC
52 EFI_STATUS
53 UsbCbiInit (
54 IN EFI_USB_IO_PROTOCOL *UsbIo,
55 IN EFI_HANDLE Controller,
56 OUT VOID **Context OPTIONAL
57 )
58 {
59 USB_CBI_PROTOCOL *UsbCbi;
60 EFI_USB_INTERFACE_DESCRIPTOR *Interface;
61 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;
62 EFI_STATUS Status;
63 UINT8 Index;
64
65 //
66 // Allocate the CBI context
67 //
68 UsbCbi = AllocateZeroPool (
69 sizeof (USB_CBI_PROTOCOL) + 3 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)
70 );
71
72 if (UsbCbi == NULL) {
73 return EFI_OUT_OF_RESOURCES;
74 }
75
76 UsbCbi->UsbIo = UsbIo;
77
78 //
79 // Get the interface descriptor and validate that it is a USB mass
80 // storage class CBI interface.
81 //
82 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbCbi->Interface);
83 if (EFI_ERROR (Status)) {
84 goto ON_ERROR;
85 }
86
87 Interface = &UsbCbi->Interface;
88 if ((Interface->InterfaceProtocol != USB_MASS_STORE_CBI0)
89 && (Interface->InterfaceProtocol != USB_MASS_STORE_CBI1)) {
90 Status = EFI_UNSUPPORTED;
91 goto ON_ERROR;
92 }
93
94 //
95 // Locate and save the bulk-in, bulk-out, and interrupt endpoint
96 //
97 for (Index = 0; Index < Interface->NumEndpoints; Index++) {
98 Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);
99 if (EFI_ERROR (Status)) {
100 continue;
101 }
102
103 if (USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {
104 //
105 // Use the first Bulk-In and Bulk-Out endpoints
106 //
107 if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&
108 (UsbCbi->BulkInEndpoint == NULL)) {
109
110 UsbCbi->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1);
111 CopyMem(UsbCbi->BulkInEndpoint, &EndPoint, sizeof (EndPoint));;
112 }
113
114 if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&
115 (UsbCbi->BulkOutEndpoint == NULL)) {
116
117 UsbCbi->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 1;
118 CopyMem(UsbCbi->BulkOutEndpoint, &EndPoint, sizeof (EndPoint));
119 }
120
121 } else if (USB_IS_INTERRUPT_ENDPOINT (EndPoint.Attributes)) {
122 //
123 // Use the first interrupt endpoint if it is CBI0
124 //
125 if ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0) &&
126 (UsbCbi->InterruptEndpoint == NULL)) {
127
128 UsbCbi->InterruptEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbCbi + 1) + 2;
129 CopyMem(UsbCbi->InterruptEndpoint, &EndPoint, sizeof (EndPoint));
130 }
131 }
132 }
133
134 if ((UsbCbi->BulkInEndpoint == NULL)
135 || (UsbCbi->BulkOutEndpoint == NULL)
136 || ((Interface->InterfaceProtocol == USB_MASS_STORE_CBI0)
137 && (UsbCbi->InterruptEndpoint == NULL))) {
138 Status = EFI_UNSUPPORTED;
139 goto ON_ERROR;
140 }
141
142 if (Context != NULL) {
143 *Context = UsbCbi;
144 } else {
145 gBS->FreePool (UsbCbi);
146 }
147 return EFI_SUCCESS;
148
149 ON_ERROR:
150 gBS->FreePool (UsbCbi);
151 return Status;
152 }
153
154
155
156 /**
157 Send the command to the device using class specific control transfer.
158
159 @param UsbCbi The USB CBI protocol
160 @param Cmd The high level command to transfer to device
161 @param CmdLen The length of the command
162 @param Timeout The time to wait the command to finish
163
164 @retval EFI_SUCCESS The command is transferred to device
165 @retval Others The command failed to transfer to device
166
167 **/
168 STATIC
169 EFI_STATUS
170 UsbCbiSendCommand (
171 IN USB_CBI_PROTOCOL *UsbCbi,
172 IN UINT8 *Cmd,
173 IN UINT8 CmdLen,
174 IN UINT32 Timeout
175 )
176 {
177 EFI_USB_DEVICE_REQUEST Request;
178 EFI_STATUS Status;
179 UINT32 TransStatus;
180 UINTN DataLen;
181 INTN Retry;
182
183 //
184 // Fill in the device request, CBI use the "Accept Device-Specific
185 // Cmd" (ADSC) class specific request to send commands
186 //
187 Request.RequestType = 0x21;
188 Request.Request = 0;
189 Request.Value = 0;
190 Request.Index = UsbCbi->Interface.InterfaceNumber;
191 Request.Length = CmdLen;
192
193 Status = EFI_SUCCESS;
194 Timeout = Timeout / USB_MASS_1_MILLISECOND;
195
196 for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
197 //
198 // Use the UsbIo to send the command to the device
199 //
200 TransStatus = 0;
201 DataLen = CmdLen;
202
203 Status = UsbCbi->UsbIo->UsbControlTransfer (
204 UsbCbi->UsbIo,
205 &Request,
206 EfiUsbDataOut,
207 Timeout,
208 Cmd,
209 DataLen,
210 &TransStatus
211 );
212 //
213 // The device can fail the command by STALL the control endpoint.
214 // It can delay the command by NAK the data or status stage, this
215 // is a "class-specific exemption to the USB specification". Retry
216 // if the command is NAKed.
217 //
218 if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
219 continue;
220 }
221
222 break;
223 }
224
225 return Status;
226 }
227
228
229 /**
230 Transfer data between the device and host. The CBI contains three phase,
231 command, data, and status. This is data phase.
232
233 @param UsbCbi The USB CBI device
234 @param DataDir The direction of the data transfer
235 @param Data The buffer to hold the data
236 @param TransLen The expected transfer length
237 @param Timeout The time to wait the command to execute
238
239 @retval EFI_SUCCESS The data transfer succeeded
240 @retval Others Failed to transfer all the data
241
242 **/
243 STATIC
244 EFI_STATUS
245 UsbCbiDataTransfer (
246 IN USB_CBI_PROTOCOL *UsbCbi,
247 IN EFI_USB_DATA_DIRECTION DataDir,
248 IN OUT UINT8 *Data,
249 IN OUT UINTN *TransLen,
250 IN UINT32 Timeout
251 )
252 {
253 EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;
254 EFI_STATUS Status;
255 UINT32 TransStatus;
256 UINTN Remain;
257 UINTN Increment;
258 UINT8 *Next;
259 UINTN Retry;
260
261 //
262 // It's OK if no data to transfer
263 //
264 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {
265 return EFI_SUCCESS;
266 }
267
268 //
269 // Select the endpoint then issue the transfer
270 //
271 if (DataDir == EfiUsbDataIn) {
272 Endpoint = UsbCbi->BulkInEndpoint;
273 } else {
274 Endpoint = UsbCbi->BulkOutEndpoint;
275 }
276
277 Next = Data;
278 Remain = *TransLen;
279 Retry = 0;
280 Status = EFI_SUCCESS;
281 Timeout = Timeout / USB_MASS_1_MILLISECOND;
282
283 //
284 // Transfer the data, if the device returns NAK, retry it.
285 //
286 while (Remain > 0) {
287 TransStatus = 0;
288
289 if (Remain > (UINTN) USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize) {
290 Increment = USB_CBI_MAX_PACKET_NUM * Endpoint->MaxPacketSize;
291 } else {
292 Increment = Remain;
293 }
294
295 Status = UsbCbi->UsbIo->UsbBulkTransfer (
296 UsbCbi->UsbIo,
297 Endpoint->EndpointAddress,
298 Next,
299 &Increment,
300 Timeout,
301 &TransStatus
302 );
303 if (EFI_ERROR (Status)) {
304 if (TransStatus == EFI_USB_ERR_NAK) {
305 //
306 // The device can NAK the host if either the data/buffer isn't
307 // aviable or the command is in-progress. The data can be partly
308 // transferred. The transfer is aborted if several succssive data
309 // transfer commands are NAKed.
310 //
311 if (Increment == 0) {
312 if (++Retry > USB_CBI_MAX_RETRY) {
313 goto ON_EXIT;
314 }
315
316 } else {
317 Next += Increment;
318 Remain -= Increment;
319 Retry = 0;
320 }
321
322 continue;
323 }
324
325 //
326 // The device can fail the command by STALL the bulk endpoint.
327 // Clear the stall if that is the case.
328 //
329 if (TransStatus == EFI_USB_ERR_STALL) {
330 UsbClearEndpointStall (UsbCbi->UsbIo, Endpoint->EndpointAddress);
331 }
332
333 goto ON_EXIT;
334 }
335
336 Next += Increment;
337 Remain -= Increment;
338 }
339
340 ON_EXIT:
341 *TransLen -= Remain;
342 return Status;
343 }
344
345
346 /**
347 Get the result of high level command execution from interrupt
348 endpoint. This function returns the USB transfer status, and
349 put the high level command execution result in Result.
350
351 @param UsbCbi The USB CBI protocol
352 @param Timeout The time to wait the command to execute
353 @param Result GC_TODO: add argument description
354
355 @retval EFI_SUCCESS The high level command execution result is
356 retrieved in Result.
357 @retval Others Failed to retrieve the result.
358
359 **/
360 STATIC
361 EFI_STATUS
362 UsbCbiGetStatus (
363 IN USB_CBI_PROTOCOL *UsbCbi,
364 IN UINT32 Timeout,
365 OUT USB_CBI_STATUS *Result
366 )
367 {
368 UINTN Len;
369 UINT8 Endpoint;
370 EFI_STATUS Status;
371 UINT32 TransStatus;
372 INTN Retry;
373
374 Endpoint = UsbCbi->InterruptEndpoint->EndpointAddress;
375 Status = EFI_SUCCESS;
376 Timeout = Timeout / USB_MASS_1_MILLISECOND;
377
378 //
379 // Attemp to the read the result from interrupt endpoint
380 //
381 for (Retry = 0; Retry < USB_CBI_MAX_RETRY; Retry++) {
382 TransStatus = 0;
383 Len = sizeof (USB_CBI_STATUS);
384
385 Status = UsbCbi->UsbIo->UsbSyncInterruptTransfer (
386 UsbCbi->UsbIo,
387 Endpoint,
388 Result,
389 &Len,
390 Timeout,
391 &TransStatus
392 );
393 //
394 // The CBI can NAK the interrupt endpoint if the command is in-progress.
395 //
396 if (EFI_ERROR (Status) && (TransStatus == EFI_USB_ERR_NAK)) {
397 continue;
398 }
399
400 break;
401 }
402
403 return Status;
404 }
405
406
407 /**
408 Execute USB mass storage command through the CBI0/CBI1 transport protocol
409
410 @param Context The USB CBI device
411 @param Cmd The command to transfer to device
412 @param CmdLen The length of the command
413 @param DataDir The direction of data transfer
414 @param Data The buffer to hold the data
415 @param DataLen The length of the buffer
416 @param Timeout The time to wait
417 @param CmdStatus The result of the command execution
418
419 @retval EFI_SUCCESS The command is executed OK and result in CmdStatus.
420 @retval EFI_DEVICE_ERROR Failed to execute the command
421
422 **/
423 STATIC
424 EFI_STATUS
425 UsbCbiExecCommand (
426 IN VOID *Context,
427 IN VOID *Cmd,
428 IN UINT8 CmdLen,
429 IN EFI_USB_DATA_DIRECTION DataDir,
430 IN VOID *Data,
431 IN UINT32 DataLen,
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 UsbCbiFini
607 };
608
609 USB_MASS_TRANSPORT
610 mUsbCbi1Transport = {
611 USB_MASS_STORE_CBI1,
612 UsbCbiInit,
613 UsbCbiExecCommand,
614 UsbCbiResetDevice,
615 UsbCbiFini
616 };