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