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