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