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