]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c
Minor refinement for USB modules.
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBot.c
... / ...
CommitLineData
1/** @file\r
2 Implementation of the USB mass storage Bulk-Only Transport protocol,\r
3 according to USB Mass Storage Class Bulk-Only Transport, Revision 1.0.\r
4\r
5Copyright (c) 2007 - 2008, Intel Corporation\r
6All rights reserved. This program and the accompanying materials\r
7are licensed and made available under the terms and conditions of the BSD License\r
8which accompanies this distribution. The full text of the license may be found at\r
9http://opensource.org/licenses/bsd-license.php\r
10\r
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "UsbMassBoot.h"\r
17#include "UsbMassBot.h"\r
18\r
19//\r
20// Definition of USB BOT Transport Protocol\r
21//\r
22USB_MASS_TRANSPORT mUsbBotTransport = {\r
23 USB_MASS_STORE_BOT,\r
24 UsbBotInit,\r
25 UsbBotExecCommand,\r
26 UsbBotResetDevice,\r
27 UsbBotGetMaxLun,\r
28 UsbBotCleanUp\r
29};\r
30\r
31/**\r
32 Initializes USB BOT protocol.\r
33\r
34 This function initializes the USB mass storage class BOT protocol.\r
35 It will save its context which is a USB_BOT_PROTOCOL structure\r
36 in the Context if Context isn't NULL.\r
37\r
38 @param UsbIo The USB I/O Protocol instance\r
39 @param Context The buffer to save the context to\r
40\r
41 @retval EFI_SUCCESS The device is successfully initialized.\r
42 @retval EFI_UNSUPPORTED The transport protocol doesn't support the device.\r
43 @retval Other The USB BOT initialization fails.\r
44\r
45**/\r
46EFI_STATUS\r
47UsbBotInit (\r
48 IN EFI_USB_IO_PROTOCOL *UsbIo,\r
49 OUT VOID **Context OPTIONAL\r
50 )\r
51{\r
52 USB_BOT_PROTOCOL *UsbBot;\r
53 EFI_USB_INTERFACE_DESCRIPTOR *Interface;\r
54 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;\r
55 EFI_STATUS Status;\r
56 UINT8 Index;\r
57\r
58 //\r
59 // Allocate the BOT context for USB_BOT_PROTOCOL and two endpoint descriptors.\r
60 //\r
61 UsbBot = AllocateZeroPool (sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR));\r
62 ASSERT (UsbBot != NULL);\r
63\r
64 UsbBot->UsbIo = UsbIo;\r
65\r
66 //\r
67 // Get the interface descriptor and validate that it\r
68 // is a USB Mass Storage BOT interface.\r
69 //\r
70 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface);\r
71\r
72 if (EFI_ERROR (Status)) {\r
73 goto ON_ERROR;\r
74 }\r
75\r
76 Interface = &UsbBot->Interface;\r
77\r
78 if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) {\r
79 Status = EFI_UNSUPPORTED;\r
80 goto ON_ERROR;\r
81 }\r
82\r
83 //\r
84 // Locate and save the first bulk-in and bulk-out endpoint\r
85 //\r
86 for (Index = 0; Index < Interface->NumEndpoints; Index++) {\r
87 Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);\r
88\r
89 if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {\r
90 continue;\r
91 }\r
92\r
93 if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&\r
94 (UsbBot->BulkInEndpoint == NULL)) {\r
95\r
96 UsbBot->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1);\r
97 CopyMem(UsbBot->BulkInEndpoint, &EndPoint, sizeof (EndPoint));\r
98 }\r
99\r
100 if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&\r
101 (UsbBot->BulkOutEndpoint == NULL)) {\r
102\r
103 UsbBot->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1;\r
104 CopyMem (UsbBot->BulkOutEndpoint, &EndPoint, sizeof(EndPoint));\r
105 }\r
106 }\r
107\r
108 //\r
109 // If bulk-in or bulk-out endpoint is not found, report error.\r
110 //\r
111 if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) {\r
112 Status = EFI_UNSUPPORTED;\r
113 goto ON_ERROR;\r
114 }\r
115\r
116 //\r
117 // The USB BOT protocol uses CBWTag to match the CBW and CSW.\r
118 //\r
119 UsbBot->CbwTag = 0x01;\r
120\r
121 if (Context != NULL) {\r
122 *Context = UsbBot;\r
123 } else {\r
124 FreePool (UsbBot);\r
125 }\r
126\r
127 return EFI_SUCCESS;\r
128\r
129ON_ERROR:\r
130 FreePool (UsbBot);\r
131 return Status;\r
132}\r
133\r
134/**\r
135 Send the command to the device using Bulk-Out endpoint.\r
136\r
137 This function sends the command to the device using Bulk-Out endpoint.\r
138 BOT transfer is composed of three phases: Command, Data, and Status.\r
139 This is the Command phase.\r
140\r
141 @param UsbBot The USB BOT device\r
142 @param Cmd The command to transfer to device\r
143 @param CmdLen The length of the command\r
144 @param DataDir The direction of the data\r
145 @param TransLen The expected length of the data\r
146 @param Lun The number of logic unit\r
147\r
148 @retval EFI_SUCCESS The command is sent to the device.\r
149 @retval EFI_NOT_READY The device return NAK to the transfer\r
150 @retval Others Failed to send the command to device\r
151\r
152**/\r
153EFI_STATUS\r
154UsbBotSendCommand (\r
155 IN USB_BOT_PROTOCOL *UsbBot,\r
156 IN UINT8 *Cmd,\r
157 IN UINT8 CmdLen,\r
158 IN EFI_USB_DATA_DIRECTION DataDir,\r
159 IN UINT32 TransLen,\r
160 IN UINT8 Lun\r
161 )\r
162{\r
163 USB_BOT_CBW Cbw;\r
164 EFI_STATUS Status;\r
165 UINT32 Result;\r
166 UINTN DataLen;\r
167 UINTN Timeout;\r
168\r
169 ASSERT ((CmdLen > 0) && (CmdLen <= USB_BOT_MAX_CMDLEN));\r
170\r
171 //\r
172 // Fill in the Command Block Wrapper.\r
173 //\r
174 Cbw.Signature = USB_BOT_CBW_SIGNATURE;\r
175 Cbw.Tag = UsbBot->CbwTag;\r
176 Cbw.DataLen = TransLen;\r
177 Cbw.Flag = (UINT8) ((DataDir == EfiUsbDataIn) ? BIT7 : 0);\r
178 Cbw.Lun = Lun;\r
179 Cbw.CmdLen = CmdLen;\r
180\r
181 ZeroMem (Cbw.CmdBlock, USB_BOT_MAX_CMDLEN);\r
182 CopyMem (Cbw.CmdBlock, Cmd, CmdLen);\r
183\r
184 Result = 0;\r
185 DataLen = sizeof (USB_BOT_CBW);\r
186 Timeout = USB_BOT_SEND_CBW_TIMEOUT / USB_MASS_1_MILLISECOND;\r
187\r
188 //\r
189 // Use USB I/O Protocol to send the Command Block Wrapper to the device.\r
190 //\r
191 Status = UsbBot->UsbIo->UsbBulkTransfer (\r
192 UsbBot->UsbIo,\r
193 UsbBot->BulkOutEndpoint->EndpointAddress,\r
194 &Cbw,\r
195 &DataLen,\r
196 Timeout,\r
197 &Result\r
198 );\r
199 if (EFI_ERROR (Status)) {\r
200 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL) && DataDir == EfiUsbDataOut) {\r
201 //\r
202 // Respond to Bulk-Out endpoint stall with a Reset Recovery,\r
203 // according to section 5.3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.\r
204 //\r
205 UsbBotResetDevice (UsbBot, FALSE);\r
206 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {\r
207 Status = EFI_NOT_READY;\r
208 }\r
209 }\r
210\r
211 return Status;\r
212}\r
213\r
214\r
215/**\r
216 Transfer the data between the device and host.\r
217\r
218 This function transfers the data between the device and host.\r
219 BOT transfer is composed of three phases: Command, Data, and Status.\r
220 This is the Data phase.\r
221\r
222 @param UsbBot The USB BOT device\r
223 @param DataDir The direction of the data\r
224 @param Data The buffer to hold data\r
225 @param TransLen The expected length of the data\r
226 @param Timeout The time to wait the command to complete\r
227\r
228 @retval EFI_SUCCESS The data is transferred\r
229 @retval EFI_SUCCESS No data to transfer\r
230 @retval EFI_NOT_READY The device return NAK to the transfer\r
231 @retval Others Failed to transfer data\r
232\r
233**/\r
234EFI_STATUS\r
235UsbBotDataTransfer (\r
236 IN USB_BOT_PROTOCOL *UsbBot,\r
237 IN EFI_USB_DATA_DIRECTION DataDir,\r
238 IN OUT UINT8 *Data,\r
239 IN OUT UINTN *TransLen,\r
240 IN UINT32 Timeout\r
241 )\r
242{\r
243 EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;\r
244 EFI_STATUS Status;\r
245 UINT32 Result;\r
246\r
247 //\r
248 // If no data to transfer, just return EFI_SUCCESS.\r
249 //\r
250 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {\r
251 return EFI_SUCCESS;\r
252 }\r
253\r
254 //\r
255 // Select the endpoint then issue the transfer\r
256 //\r
257 if (DataDir == EfiUsbDataIn) {\r
258 Endpoint = UsbBot->BulkInEndpoint;\r
259 } else {\r
260 Endpoint = UsbBot->BulkOutEndpoint;\r
261 }\r
262\r
263 Result = 0;\r
264 Timeout = Timeout / USB_MASS_1_MILLISECOND;\r
265\r
266 Status = UsbBot->UsbIo->UsbBulkTransfer (\r
267 UsbBot->UsbIo,\r
268 Endpoint->EndpointAddress,\r
269 Data,\r
270 TransLen,\r
271 Timeout,\r
272 &Result\r
273 );\r
274 if (EFI_ERROR (Status)) {\r
275 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {\r
276 UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);\r
277 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {\r
278 Status = EFI_NOT_READY;\r
279 }\r
280 }\r
281\r
282 return Status;\r
283}\r
284\r
285\r
286/**\r
287 Get the command execution status from device.\r
288\r
289 This function gets the command execution status from device.\r
290 BOT transfer is composed of three phases: Command, Data, and Status.\r
291 This is the Status phase.\r
292\r
293 This function returns the transfer status of the BOT's CSW status,\r
294 and returns the high level command execution result in Result. So\r
295 even if EFI_SUCCESS is returned, the command may still have failed.\r
296\r
297 @param UsbBot The USB BOT device.\r
298 @param TransLen The expected length of the data.\r
299 @param CmdStatus The result of the command execution.\r
300\r
301 @retval EFI_SUCCESS Command execute result is retrieved and in the Result.\r
302 @retval Other Error occurred when trying to get status.\r
303\r
304**/\r
305EFI_STATUS\r
306UsbBotGetStatus (\r
307 IN USB_BOT_PROTOCOL *UsbBot,\r
308 IN UINT32 TransLen,\r
309 OUT UINT8 *CmdStatus\r
310 )\r
311{\r
312 USB_BOT_CSW Csw;\r
313 UINTN Len;\r
314 UINT8 Endpoint;\r
315 EFI_STATUS Status;\r
316 UINT32 Result;\r
317 EFI_USB_IO_PROTOCOL *UsbIo;\r
318 UINT32 Index;\r
319 UINTN Timeout;\r
320\r
321 *CmdStatus = USB_BOT_COMMAND_ERROR;\r
322 Status = EFI_DEVICE_ERROR;\r
323 Endpoint = UsbBot->BulkInEndpoint->EndpointAddress;\r
324 UsbIo = UsbBot->UsbIo;\r
325 Timeout = USB_BOT_RECV_CSW_TIMEOUT / USB_MASS_1_MILLISECOND;\r
326\r
327 for (Index = 0; Index < USB_BOT_RECV_CSW_RETRY; Index++) {\r
328 //\r
329 // Attemp to the read Command Status Wrapper from bulk in endpoint\r
330 //\r
331 ZeroMem (&Csw, sizeof (USB_BOT_CSW));\r
332 Result = 0;\r
333 Len = sizeof (USB_BOT_CSW);\r
334 Status = UsbIo->UsbBulkTransfer (\r
335 UsbIo,\r
336 Endpoint,\r
337 &Csw,\r
338 &Len,\r
339 Timeout,\r
340 &Result\r
341 );\r
342 if (EFI_ERROR(Status)) {\r
343 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {\r
344 UsbClearEndpointStall (UsbIo, Endpoint);\r
345 }\r
346 continue;\r
347 }\r
348\r
349 if (Csw.Signature != USB_BOT_CSW_SIGNATURE) {\r
350 //\r
351 // CSW is invalid, so perform reset recovery\r
352 //\r
353 Status = UsbBotResetDevice (UsbBot, FALSE);\r
354 } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) {\r
355 //\r
356 // Respond phase error also needs reset recovery\r
357 //\r
358 Status = UsbBotResetDevice (UsbBot, FALSE);\r
359 } else {\r
360 *CmdStatus = Csw.CmdStatus;\r
361 break;\r
362 }\r
363 }\r
364 //\r
365 //The tag is increased even if there is an error.\r
366 //\r
367 UsbBot->CbwTag++;\r
368\r
369 return Status;\r
370}\r
371\r
372\r
373/**\r
374 Call the USB Mass Storage Class BOT protocol to issue\r
375 the command/data/status circle to execute the commands.\r
376\r
377 @param Context The context of the BOT protocol, that is,\r
378 USB_BOT_PROTOCOL\r
379 @param Cmd The high level command\r
380 @param CmdLen The command length\r
381 @param DataDir The direction of the data transfer\r
382 @param Data The buffer to hold data\r
383 @param DataLen The length of the data\r
384 @param Lun The number of logic unit\r
385 @param Timeout The time to wait command\r
386 @param CmdStatus The result of high level command execution\r
387\r
388 @retval EFI_SUCCESS The command is executed successfully.\r
389 @retval Other Failed to excute command\r
390\r
391**/\r
392EFI_STATUS\r
393UsbBotExecCommand (\r
394 IN VOID *Context,\r
395 IN VOID *Cmd,\r
396 IN UINT8 CmdLen,\r
397 IN EFI_USB_DATA_DIRECTION DataDir,\r
398 IN VOID *Data,\r
399 IN UINT32 DataLen,\r
400 IN UINT8 Lun,\r
401 IN UINT32 Timeout,\r
402 OUT UINT32 *CmdStatus\r
403 )\r
404{\r
405 USB_BOT_PROTOCOL *UsbBot;\r
406 EFI_STATUS Status;\r
407 UINTN TransLen;\r
408 UINT8 Result;\r
409\r
410 *CmdStatus = USB_MASS_CMD_FAIL;\r
411 UsbBot = (USB_BOT_PROTOCOL *) Context;\r
412\r
413 //\r
414 // Send the command to the device. Return immediately if device\r
415 // rejects the command.\r
416 //\r
417 Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen, Lun);\r
418 if (EFI_ERROR (Status)) {\r
419 DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status));\r
420 return Status;\r
421 }\r
422\r
423 //\r
424 // Transfer the data. Don't return immediately even data transfer\r
425 // failed. The host should attempt to receive the CSW no matter\r
426 // whether it succeeds or fails.\r
427 //\r
428 TransLen = (UINTN) DataLen;\r
429 UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout);\r
430\r
431 //\r
432 // Get the status, if that succeeds, interpret the result\r
433 //\r
434 Status = UsbBotGetStatus (UsbBot, DataLen, &Result);\r
435 if (EFI_ERROR (Status)) {\r
436 DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status));\r
437 return Status;\r
438 }\r
439\r
440 if (Result == 0) {\r
441 *CmdStatus = USB_MASS_CMD_SUCCESS;\r
442 }\r
443\r
444 return EFI_SUCCESS;\r
445}\r
446\r
447\r
448/**\r
449 Reset the USB mass storage device by BOT protocol.\r
450\r
451 @param Context The context of the BOT protocol, that is,\r
452 USB_BOT_PROTOCOL.\r
453 @param ExtendedVerification If FALSE, just issue Bulk-Only Mass Storage Reset request.\r
454 If TRUE, additionally reset parent hub port.\r
455\r
456 @retval EFI_SUCCESS The device is reset.\r
457 @retval Others Failed to reset the device..\r
458\r
459**/\r
460EFI_STATUS\r
461UsbBotResetDevice (\r
462 IN VOID *Context,\r
463 IN BOOLEAN ExtendedVerification\r
464 )\r
465{\r
466 USB_BOT_PROTOCOL *UsbBot;\r
467 EFI_USB_DEVICE_REQUEST Request;\r
468 EFI_STATUS Status;\r
469 UINT32 Result;\r
470 UINT32 Timeout;\r
471\r
472 UsbBot = (USB_BOT_PROTOCOL *) Context;\r
473\r
474 if (ExtendedVerification) {\r
475 //\r
476 // If we need to do strictly reset, reset its parent hub port\r
477 //\r
478 Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);\r
479 if (EFI_ERROR (Status)) {\r
480 return Status;\r
481 }\r
482 }\r
483\r
484 //\r
485 // Issue a class specific Bulk-Only Mass Storage Reset request,\r
486 // according to section 3.1 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.\r
487 //\r
488 Request.RequestType = 0x21;\r
489 Request.Request = USB_BOT_RESET_REQUEST;\r
490 Request.Value = 0;\r
491 Request.Index = UsbBot->Interface.InterfaceNumber;\r
492 Request.Length = 0;\r
493 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;\r
494\r
495 Status = UsbBot->UsbIo->UsbControlTransfer (\r
496 UsbBot->UsbIo,\r
497 &Request,\r
498 EfiUsbNoData,\r
499 Timeout,\r
500 NULL,\r
501 0,\r
502 &Result\r
503 );\r
504\r
505 if (EFI_ERROR (Status)) {\r
506 return Status;\r
507 }\r
508\r
509 //\r
510 // The device shall NAK the host's request until the reset is\r
511 // complete. We can use this to sync the device and host. For\r
512 // now just stall 100ms to wait for the device.\r
513 //\r
514 gBS->Stall (USB_BOT_RESET_DEVICE_STALL);\r
515\r
516 //\r
517 // Clear the Bulk-In and Bulk-Out stall condition.\r
518 //\r
519 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);\r
520 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);\r
521\r
522 return Status;\r
523}\r
524\r
525\r
526/**\r
527 Get the max LUN (Logical Unit Number) of USB mass storage device.\r
528\r
529 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL\r
530 @param MaxLun Return pointer to the max number of LUN. (e.g. MaxLun=1 means LUN0 and\r
531 LUN1 in all.)\r
532\r
533 @retval EFI_SUCCESS Max LUN is got successfully.\r
534 @retval Others Fail to execute this request.\r
535\r
536**/\r
537EFI_STATUS\r
538UsbBotGetMaxLun (\r
539 IN VOID *Context,\r
540 OUT UINT8 *MaxLun\r
541 )\r
542{\r
543 USB_BOT_PROTOCOL *UsbBot;\r
544 EFI_USB_DEVICE_REQUEST Request;\r
545 EFI_STATUS Status;\r
546 UINT32 Result;\r
547 UINT32 Timeout;\r
548\r
549 ASSERT (Context);\r
550 \r
551 UsbBot = (USB_BOT_PROTOCOL *) Context;\r
552\r
553 //\r
554 // Issue a class specific Bulk-Only Mass Storage get max lun reqest.\r
555 // according to section 3.2 of USB Mass Storage Class Bulk-Only Transport Spec, v1.0.\r
556 //\r
557 Request.RequestType = 0xA1;\r
558 Request.Request = USB_BOT_GETLUN_REQUEST;\r
559 Request.Value = 0;\r
560 Request.Index = UsbBot->Interface.InterfaceNumber;\r
561 Request.Length = 1;\r
562 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;\r
563\r
564 Status = UsbBot->UsbIo->UsbControlTransfer (\r
565 UsbBot->UsbIo,\r
566 &Request,\r
567 EfiUsbDataIn,\r
568 Timeout,\r
569 (VOID *) MaxLun,\r
570 1,\r
571 &Result\r
572 );\r
573\r
574 return Status;\r
575}\r
576\r
577/**\r
578 Clean up the resource used by this BOT protocol.\r
579\r
580 @param Context The context of the BOT protocol, that is, USB_BOT_PROTOCOL.\r
581\r
582 @retval EFI_SUCCESS The resource is cleaned up.\r
583\r
584**/\r
585EFI_STATUS\r
586UsbBotCleanUp (\r
587 IN VOID *Context\r
588 )\r
589{\r
590 FreePool (Context);\r
591 return EFI_SUCCESS;\r
592}\r
593\r