]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassBot.c
1. Added EFI_MEDIA_CHANGED and EFI_INVALID_PARAMETER returns in UsbMassReadBlocks().
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassBot.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 UsbMassBot.c\r
15\r
16Abstract:\r
17\r
18 Implementation of the USB mass storage Bulk-Only Transport protocol.\r
19\r
20Revision History\r
21\r
22\r
23**/\r
24\r
25#include "UsbMass.h"\r
26#include "UsbMassBot.h"\r
27\r
e237e7ae 28STATIC\r
29EFI_STATUS\r
30UsbBotResetDevice (\r
31 IN VOID *Context,\r
32 IN BOOLEAN ExtendedVerification\r
33 );\r
34\r
35\r
36/**\r
37 Initialize the USB mass storage class BOT transport protocol.\r
38 It will save its context which is a USB_BOT_PROTOCOL structure\r
39 in the Context if Context isn't NULL.\r
40\r
41 @param UsbIo The USB IO protocol to use\r
42 @param Controller The controller to init\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\r
49**/\r
50STATIC\r
51EFI_STATUS\r
52UsbBotInit (\r
53 IN EFI_USB_IO_PROTOCOL * UsbIo,\r
54 IN EFI_HANDLE Controller,\r
55 OUT VOID **Context OPTIONAL\r
56 )\r
57{\r
58 USB_BOT_PROTOCOL *UsbBot;\r
59 EFI_USB_INTERFACE_DESCRIPTOR *Interface;\r
60 EFI_USB_ENDPOINT_DESCRIPTOR EndPoint;\r
61 EFI_STATUS Status;\r
62 UINT8 Index;\r
63\r
64 //\r
65 // Allocate the BOT context, append two endpoint descriptors to it\r
66 //\r
67 UsbBot = AllocateZeroPool (\r
68 sizeof (USB_BOT_PROTOCOL) + 2 * sizeof (EFI_USB_ENDPOINT_DESCRIPTOR)\r
69 );\r
70 if (UsbBot == NULL) {\r
71 return EFI_OUT_OF_RESOURCES;\r
72 }\r
73\r
74 UsbBot->UsbIo = UsbIo;\r
75\r
76 //\r
77 // Get the interface descriptor and validate that it\r
78 // is a USB MSC BOT interface.\r
79 //\r
80 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &UsbBot->Interface);\r
81\r
82 if (EFI_ERROR (Status)) {\r
1c619535 83 DEBUG ((EFI_D_ERROR, "UsbBotInit: Get invalid BOT interface (%r)\n", Status));\r
e237e7ae 84 goto ON_ERROR;\r
85 }\r
86\r
87 Interface = &UsbBot->Interface;\r
88\r
89 if (Interface->InterfaceProtocol != USB_MASS_STORE_BOT) {\r
90 Status = EFI_UNSUPPORTED;\r
91 goto ON_ERROR;\r
92 }\r
93\r
94 //\r
95 // Locate and save the first bulk-in and bulk-out endpoint\r
96 //\r
97 for (Index = 0; Index < Interface->NumEndpoints; Index++) {\r
98 Status = UsbIo->UsbGetEndpointDescriptor (UsbIo, Index, &EndPoint);\r
99\r
100 if (EFI_ERROR (Status) || !USB_IS_BULK_ENDPOINT (EndPoint.Attributes)) {\r
101 continue;\r
102 }\r
103\r
104 if (USB_IS_IN_ENDPOINT (EndPoint.EndpointAddress) &&\r
105 (UsbBot->BulkInEndpoint == NULL)) {\r
106\r
107 UsbBot->BulkInEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1);\r
84b5c78e 108 CopyMem(UsbBot->BulkInEndpoint, &EndPoint, sizeof (EndPoint));\r
e237e7ae 109 }\r
110\r
111 if (USB_IS_OUT_ENDPOINT (EndPoint.EndpointAddress) &&\r
112 (UsbBot->BulkOutEndpoint == NULL)) {\r
113\r
114 UsbBot->BulkOutEndpoint = (EFI_USB_ENDPOINT_DESCRIPTOR *) (UsbBot + 1) + 1;\r
84b5c78e 115 CopyMem(UsbBot->BulkOutEndpoint, &EndPoint, sizeof(EndPoint));\r
e237e7ae 116 }\r
117 }\r
118\r
119 if ((UsbBot->BulkInEndpoint == NULL) || (UsbBot->BulkOutEndpoint == NULL)) {\r
1c619535 120 DEBUG ((EFI_D_ERROR, "UsbBotInit: In/Out Endpoint invalid\n"));\r
e237e7ae 121 Status = EFI_UNSUPPORTED;\r
122 goto ON_ERROR;\r
123 }\r
124\r
125 //\r
126 // The USB BOT protocol uses dCBWTag to match the CBW and CSW.\r
127 //\r
128 UsbBot->CbwTag = 0x01;\r
129\r
130 if (Context != NULL) {\r
131 *Context = UsbBot;\r
132 } else {\r
133 gBS->FreePool (UsbBot);\r
134 }\r
135\r
136 return EFI_SUCCESS;\r
137\r
138ON_ERROR:\r
139 gBS->FreePool (UsbBot);\r
140 return Status;\r
141}\r
142\r
143\r
144/**\r
145 Send the command to the device using Bulk-Out endpoint\r
146\r
147 @param UsbBot The USB BOT device\r
148 @param Cmd The command to transfer to device\r
149 @param CmdLen the length of the command\r
150 @param DataDir The direction of the data\r
151 @param TransLen The expected length of the data\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
158STATIC\r
159EFI_STATUS\r
160UsbBotSendCommand (\r
161 IN USB_BOT_PROTOCOL *UsbBot,\r
162 IN UINT8 *Cmd,\r
163 IN UINT8 CmdLen,\r
164 IN EFI_USB_DATA_DIRECTION DataDir,\r
165 IN UINT32 TransLen\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
c52fa98c 182 Cbw.Flag = (UINT8) ((DataDir == EfiUsbDataIn) ? 0x80 : 0);\r
e237e7ae 183 Cbw.Lun = 0;\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
41e8ff27 191 Timeout = USB_BOT_SEND_CBW_TIMEOUT / USB_MASS_1_MILLISECOND;\r
e237e7ae 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
235STATIC\r
236EFI_STATUS\r
237UsbBotDataTransfer (\r
238 IN USB_BOT_PROTOCOL *UsbBot,\r
239 IN EFI_USB_DATA_DIRECTION DataDir,\r
240 IN OUT UINT8 *Data,\r
241 IN OUT UINTN *TransLen,\r
242 IN UINT32 Timeout\r
243 )\r
244{\r
245 EFI_USB_ENDPOINT_DESCRIPTOR *Endpoint;\r
246 EFI_STATUS Status;\r
247 UINT32 Result;\r
248\r
249 //\r
250 // It's OK if no data to transfer\r
251 //\r
252 if ((DataDir == EfiUsbNoData) || (*TransLen == 0)) {\r
253 return EFI_SUCCESS;\r
254 }\r
255\r
256 //\r
257 // Select the endpoint then issue the transfer\r
258 //\r
259 if (DataDir == EfiUsbDataIn) {\r
260 Endpoint = UsbBot->BulkInEndpoint;\r
261 } else {\r
262 Endpoint = UsbBot->BulkOutEndpoint;\r
263 }\r
264\r
265 Result = 0;\r
41e8ff27 266 Timeout = Timeout / USB_MASS_1_MILLISECOND;\r
e237e7ae 267\r
268 Status = UsbBot->UsbIo->UsbBulkTransfer (\r
269 UsbBot->UsbIo,\r
270 Endpoint->EndpointAddress,\r
271 Data,\r
272 TransLen,\r
273 Timeout,\r
274 &Result\r
275 );\r
276 if (EFI_ERROR (Status)) {\r
1c619535 277 DEBUG ((EFI_D_ERROR, "UsbBotDataTransfer: (%r)\n", Status));\r
e237e7ae 278 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {\r
1c619535 279 DEBUG ((EFI_D_ERROR, "UsbBotDataTransfer: DataIn Stall\n"));\r
e237e7ae 280 UsbClearEndpointStall (UsbBot->UsbIo, Endpoint->EndpointAddress);\r
281 } else if (USB_IS_ERROR (Result, EFI_USB_ERR_NAK)) {\r
282 Status = EFI_NOT_READY;\r
283 }\r
284 }\r
285\r
286 return Status;\r
287}\r
288\r
289\r
290/**\r
291 Get the command execution status from device. BOT transfer is\r
292 composed of three phase, command, data, and status.\r
293 This function return the transfer status of the BOT's CSW status,\r
294 and return the high level command execution result in Result. So\r
295 even it returns EFI_SUCCESS, 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 Timeout The time to wait the command to complete\r
300 @param CmdStatus The result of the command execution.\r
301\r
302 @retval EFI_DEVICE_ERROR Failed to retrieve the command execute result\r
303 @retval EFI_SUCCESS Command execute result is retrieved and in the\r
304 Result.\r
305\r
306**/\r
307STATIC\r
308EFI_STATUS\r
309UsbBotGetStatus (\r
310 IN USB_BOT_PROTOCOL *UsbBot,\r
311 IN UINT32 TransLen,\r
312 OUT UINT8 *CmdStatus\r
313 )\r
314{\r
315 USB_BOT_CSW Csw;\r
316 UINTN Len;\r
317 UINT8 Endpoint;\r
318 EFI_STATUS Status;\r
319 UINT32 Result;\r
320 EFI_USB_IO_PROTOCOL *UsbIo;\r
321 UINT32 Index;\r
322 UINTN Timeout;\r
1c619535 323\r
e237e7ae 324 *CmdStatus = USB_BOT_COMMAND_ERROR;\r
325 Status = EFI_DEVICE_ERROR;\r
326 Endpoint = UsbBot->BulkInEndpoint->EndpointAddress;\r
327 UsbIo = UsbBot->UsbIo;\r
41e8ff27 328 Timeout = USB_BOT_RECV_CSW_TIMEOUT / USB_MASS_1_MILLISECOND;\r
e237e7ae 329\r
41e8ff27 330 for (Index = 0; Index < USB_BOT_RECV_CSW_RETRY; Index++) {\r
e237e7ae 331 //\r
332 // Attemp to the read CSW from bulk in endpoint\r
333 //\r
334 ZeroMem (&Csw, sizeof (USB_BOT_CSW));\r
335 Result = 0;\r
336 Len = sizeof (USB_BOT_CSW);\r
337 Status = UsbIo->UsbBulkTransfer (\r
338 UsbIo,\r
339 Endpoint,\r
340 &Csw,\r
341 &Len,\r
342 Timeout,\r
343 &Result\r
344 );\r
345 if (EFI_ERROR(Status)) {\r
1c619535 346 DEBUG ((EFI_D_ERROR, "UsbBotGetStatus (%r)\n", Status));\r
e237e7ae 347 if (USB_IS_ERROR (Result, EFI_USB_ERR_STALL)) {\r
1c619535 348 DEBUG ((EFI_D_ERROR, "UsbBotGetStatus: DataIn Stall\n"));\r
e237e7ae 349 UsbClearEndpointStall (UsbIo, Endpoint);\r
350 }\r
351 continue;\r
352 }\r
353\r
354 if (Csw.Signature != USB_BOT_CSW_SIGNATURE) {\r
355 //\r
356 // Invalid Csw need perform reset recovery\r
357 //\r
1c619535 358 DEBUG ((EFI_D_ERROR, "UsbBotGetStatus: Device return a invalid signature\n"));\r
e237e7ae 359 Status = UsbBotResetDevice (UsbBot, FALSE);\r
360 } else if (Csw.CmdStatus == USB_BOT_COMMAND_ERROR) {\r
361 //\r
362 // Respond phase error need perform reset recovery\r
363 //\r
1c619535 364 DEBUG ((EFI_D_ERROR, "UsbBotGetStatus: Device return a phase error\n"));\r
e237e7ae 365 Status = UsbBotResetDevice (UsbBot, FALSE);\r
366 } else {\r
367\r
368 *CmdStatus = Csw.CmdStatus;\r
369 break;\r
370 }\r
371 }\r
372 //\r
373 //The tag is increased even there is an error.\r
374 //\r
375 UsbBot->CbwTag++;\r
376\r
377 return Status;\r
378}\r
379\r
380\r
381/**\r
382 Call the Usb mass storage class transport protocol to issue\r
383 the command/data/status circle to execute the commands\r
384\r
385 @param Context The context of the BOT protocol, that is,\r
386 USB_BOT_PROTOCOL\r
387 @param Cmd The high level command\r
388 @param CmdLen The command length\r
389 @param DataDir The direction of the data transfer\r
390 @param Data The buffer to hold data\r
391 @param DataLen The length of the data\r
392 @param Timeout The time to wait command\r
393 @param CmdStatus The result of high level command execution\r
394\r
395 @retval EFI_DEVICE_ERROR Failed to excute command\r
396 @retval EFI_SUCCESS The command is executed OK, and result in CmdStatus\r
397\r
398**/\r
399STATIC\r
400EFI_STATUS\r
401UsbBotExecCommand (\r
402 IN VOID *Context,\r
403 IN VOID *Cmd,\r
404 IN UINT8 CmdLen,\r
405 IN EFI_USB_DATA_DIRECTION DataDir,\r
406 IN VOID *Data,\r
407 IN UINT32 DataLen,\r
408 IN UINT32 Timeout,\r
409 OUT UINT32 *CmdStatus\r
410 )\r
411{\r
412 USB_BOT_PROTOCOL *UsbBot;\r
413 EFI_STATUS Status;\r
414 UINTN TransLen;\r
415 UINT8 Result;\r
416\r
417 *CmdStatus = USB_MASS_CMD_FAIL;\r
418 UsbBot = (USB_BOT_PROTOCOL *) Context;\r
419\r
420 //\r
421 // Send the command to the device. Return immediately if device\r
422 // rejects the command.\r
423 //\r
424 Status = UsbBotSendCommand (UsbBot, Cmd, CmdLen, DataDir, DataLen);\r
425 if (EFI_ERROR (Status)) {\r
1c619535 426 DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotSendCommand (%r)\n", Status));\r
e237e7ae 427 return Status;\r
428 }\r
429\r
430 //\r
431 // Transfer the data. Don't return immediately even data transfer\r
432 // failed. The host should attempt to receive the CSW no matter\r
433 // whether it succeeds or failed.\r
434 //\r
435 TransLen = (UINTN) DataLen;\r
436 UsbBotDataTransfer (UsbBot, DataDir, Data, &TransLen, Timeout);\r
437\r
438 //\r
439 // Get the status, if that succeeds, interpret the result\r
440 //\r
441 Status = UsbBotGetStatus (UsbBot, DataLen, &Result);\r
442 if (EFI_ERROR (Status)) {\r
1c619535 443 DEBUG ((EFI_D_ERROR, "UsbBotExecCommand: UsbBotGetStatus (%r)\n", Status));\r
e237e7ae 444 return Status;\r
445 }\r
446\r
447 if (Result == 0) {\r
448 *CmdStatus = USB_MASS_CMD_SUCCESS;\r
449 }\r
450\r
451 return EFI_SUCCESS;\r
452}\r
453\r
454\r
455/**\r
456 Reset the mass storage device by BOT protocol\r
457\r
458 @param Context The context of the BOT protocol, that is,\r
459 USB_BOT_PROTOCOL\r
460\r
461 @retval EFI_SUCCESS The device is reset\r
462 @retval Others Failed to reset the device.\r
463\r
464**/\r
465STATIC\r
466EFI_STATUS\r
467UsbBotResetDevice (\r
468 IN VOID *Context,\r
469 IN BOOLEAN ExtendedVerification\r
470 )\r
471{\r
472 USB_BOT_PROTOCOL *UsbBot;\r
473 EFI_USB_DEVICE_REQUEST Request;\r
474 EFI_STATUS Status;\r
475 UINT32 Result;\r
476 UINT32 Timeout;\r
477\r
478 UsbBot = (USB_BOT_PROTOCOL *) Context;\r
479\r
480 if (ExtendedVerification) {\r
481 //\r
482 // If we need to do strictly reset, reset its parent hub port\r
483 //\r
484 Status = UsbBot->UsbIo->UsbPortReset (UsbBot->UsbIo);\r
485 if (EFI_ERROR (Status)) {\r
486 return Status;\r
487 }\r
488 }\r
489\r
490 //\r
491 // Issue a class specific "Bulk-Only Mass Storage Reset reqest.\r
492 // See the spec section 3.1\r
493 //\r
494 Request.RequestType = 0x21;\r
495 Request.Request = USB_BOT_RESET_REQUEST;\r
496 Request.Value = 0;\r
497 Request.Index = UsbBot->Interface.InterfaceNumber;\r
498 Request.Length = 0;\r
41e8ff27 499 Timeout = USB_BOT_RESET_DEVICE_TIMEOUT / USB_MASS_1_MILLISECOND;\r
e237e7ae 500\r
501 Status = UsbBot->UsbIo->UsbControlTransfer (\r
502 UsbBot->UsbIo,\r
503 &Request,\r
504 EfiUsbNoData,\r
505 Timeout,\r
506 NULL,\r
507 0,\r
508 &Result\r
509 );\r
510\r
511 if (EFI_ERROR (Status)) {\r
1c619535 512 DEBUG ((EFI_D_ERROR, "UsbBotResetDevice: (%r)\n", Status));\r
e237e7ae 513 return Status;\r
514 }\r
515\r
516 //\r
517 // The device shall NAK the host's request until the reset is\r
518 // complete. We can use this to sync the device and host. For\r
519 // now just stall 100ms to wait the device.\r
520 //\r
41e8ff27 521 gBS->Stall (USB_BOT_RESET_DEVICE_STALL);\r
e237e7ae 522\r
523 //\r
524 // Clear the Bulk-In and Bulk-Out stall condition.\r
525 //\r
526 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkInEndpoint->EndpointAddress);\r
527 UsbClearEndpointStall (UsbBot->UsbIo, UsbBot->BulkOutEndpoint->EndpointAddress);\r
528 return Status;\r
529}\r
530\r
531\r
532/**\r
533 Clean up the resource used by this BOT protocol\r
534\r
535 @param Context The context of the BOT protocol, that is,\r
536 USB_BOT_PROTOCOL\r
537\r
538 @retval EFI_SUCCESS The resource is cleaned up.\r
539\r
540**/\r
541STATIC\r
542EFI_STATUS\r
543UsbBotFini (\r
544 IN VOID *Context\r
545 )\r
546{\r
547 gBS->FreePool (Context);\r
548 return EFI_SUCCESS;\r
549}\r
550\r
551USB_MASS_TRANSPORT\r
552mUsbBotTransport = {\r
553 USB_MASS_STORE_BOT,\r
554 UsbBotInit,\r
555 UsbBotExecCommand,\r
556 UsbBotResetDevice,\r
557 UsbBotFini\r
558};\r