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