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