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