e3f5c763904c358069b3d5bae28350c6425ccc52
[mirror_edk2.git] / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassImpl.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 UsbMassImpl.c
15
16 Abstract:
17
18 The implementation of USB mass storage class device driver.
19 The command set supported is "USB Mass Storage Specification
20 for Bootability".
21
22 Revision History
23
24
25 **/
26
27 #include "UsbMassImpl.h"
28
29 //
30 // The underlying transport protocol. CBI support isn't included
31 // in the current build. It is being obseleted by the standard
32 // body. If you want to enable it, remove the if directive here,
33 // then add the UsbMassCbi.c/.h to the driver's inf file.
34 //
35 STATIC
36 USB_MASS_TRANSPORT *mUsbMassTransport[] = {
37 &mUsbCbi0Transport,
38 &mUsbCbi1Transport,
39 &mUsbBotTransport,
40 NULL
41 };
42
43 UINTN mUsbMscInfo = DEBUG_INFO;
44 UINTN mUsbMscError = DEBUG_ERROR;
45
46
47 /**
48 Retrieve the media parameters such as disk gemotric for the
49 device's BLOCK IO protocol.
50
51 @param UsbMass The USB mass storage device
52
53 @retval EFI_SUCCESS The media parameters is updated successfully.
54 @retval Others Failed to get the media parameters.
55
56 **/
57 EFI_STATUS
58 UsbMassInitMedia (
59 IN USB_MASS_DEVICE *UsbMass
60 )
61 {
62 EFI_BLOCK_IO_MEDIA *Media;
63 EFI_STATUS Status;
64 UINTN Index;
65
66 Media = &UsbMass->BlockIoMedia;
67
68 //
69 // Initialize the MediaPrsent/ReadOnly and others to the default.
70 // We are not forced to get it right at this time, check UEFI2.0
71 // spec for more information:
72 //
73 // MediaPresent: This field shows the media present status as
74 // of the most recent ReadBlocks or WriteBlocks call.
75 //
76 // ReadOnly : This field shows the read-only status as of the
77 // recent WriteBlocks call.
78 //
79 // but remember to update MediaId/MediaPresent/ReadOnly status
80 // after ReadBlocks and WriteBlocks
81 //
82 Media->MediaPresent = FALSE;
83 Media->LogicalPartition = FALSE;
84 Media->ReadOnly = FALSE;
85 Media->WriteCaching = FALSE;
86 Media->IoAlign = 0;
87
88 //
89 // Some device may spend several seconds before it is ready.
90 // Try several times before giving up. Wait 5s at most.
91 //
92 Status = EFI_SUCCESS;
93
94 for (Index = 0; Index < USB_BOOT_WAIT_RETRY; Index++) {
95
96 Status = UsbBootGetParams (UsbMass);
97 if ((Status != EFI_MEDIA_CHANGED)
98 && (Status != EFI_NOT_READY)
99 && (Status != EFI_TIMEOUT)) {
100 break;
101 }
102
103 Status = UsbBootIsUnitReady (UsbMass);
104 if (EFI_ERROR (Status)) {
105 gBS->Stall (USB_BOOT_UNIT_READY_STALL * (Index + 1));
106 }
107
108 }
109
110 return Status;
111 }
112
113
114 /**
115 Reset the block device. ExtendedVerification is ignored for this.
116
117 @param This The BLOCK IO protocol
118 @param ExtendedVerification Whether to execute extended verfication.
119
120 @retval EFI_SUCCESS The device is successfully resetted.
121 @retval Others Failed to reset the device.
122
123 **/
124 EFI_STATUS
125 UsbMassReset (
126 IN EFI_BLOCK_IO_PROTOCOL *This,
127 IN BOOLEAN ExtendedVerification
128 )
129 {
130 USB_MASS_DEVICE *UsbMass;
131
132 UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);
133 return UsbMass->Transport->Reset (UsbMass->Context, ExtendedVerification);
134 }
135
136
137 /**
138 Read some blocks of data from the block device.
139
140 @param This The Block IO protocol
141 @param MediaId The media's ID of the device for current request
142 @param Lba The start block number
143 @param BufferSize The size of buffer to read data in
144 @param Buffer The buffer to read data to
145
146 @retval EFI_SUCCESS The data is successfully read
147 @retval EFI_NO_MEDIA Media isn't present
148 @retval EFI_MEDIA_CHANGED The device media has been changed, that is,
149 MediaId changed
150 @retval EFI_INVALID_PARAMETER Some parameters are invalid, such as Buffer is
151 NULL.
152 @retval EFI_BAD_BUFFER_SIZE The buffer size isn't a multiple of media's block
153 size, or overflow the last block number.
154
155 **/
156 EFI_STATUS
157 UsbMassReadBlocks (
158 IN EFI_BLOCK_IO_PROTOCOL *This,
159 IN UINT32 MediaId,
160 IN EFI_LBA Lba,
161 IN UINTN BufferSize,
162 OUT VOID *Buffer
163 )
164 {
165 USB_MASS_DEVICE *UsbMass;
166 EFI_BLOCK_IO_MEDIA *Media;
167 EFI_STATUS Status;
168 UINTN TotalBlock;
169
170 UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);
171 Media = &UsbMass->BlockIoMedia;
172
173 //
174 // First, validate the parameters
175 //
176 if ((Buffer == NULL) || (BufferSize == 0)) {
177 return EFI_INVALID_PARAMETER;
178 }
179
180 //
181 // If it is a remoable media, such as CD-Rom or Usb-Floppy,
182 // if, need to detect the media before each rw, while Usb-Flash
183 // needn't. However, it's hard to identify Usb-Floppy between
184 // Usb-Flash by now, so detect media every time.
185 //
186 Status = UsbBootDetectMedia (UsbMass);
187 if (EFI_ERROR (Status)) {
188 DEBUG ((mUsbMscError, "UsbMassReadBlocks: UsbBootDetectMedia (%r)\n", Status));
189 return Status;
190 }
191
192 //
193 // Make sure BlockSize and LBA is consistent with BufferSize
194 //
195 if ((BufferSize % Media->BlockSize) != 0) {
196 return EFI_BAD_BUFFER_SIZE;
197 }
198
199 TotalBlock = BufferSize / Media->BlockSize;
200
201 if (Lba + TotalBlock - 1 > Media->LastBlock) {
202 return EFI_BAD_BUFFER_SIZE;
203 }
204
205 Status = UsbBootReadBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);
206 if (EFI_ERROR (Status)) {
207 DEBUG ((mUsbMscError, "UsbMassReadBlocks: UsbBootReadBlocks (%r) -> Reset\n", Status));
208 UsbMassReset (This, TRUE);
209 }
210
211 return Status;
212 }
213
214
215 /**
216 Write some blocks of data to the block device.
217
218 @param This The Block IO protocol
219 @param MediaId The media's ID of the device for current request
220 @param Lba The start block number
221 @param BufferSize The size of buffer to write data to
222 @param Buffer The buffer to write data to
223
224 @retval EFI_SUCCESS The data is successfully written
225 @retval EFI_NO_MEDIA Media isn't present
226 @retval EFI_MEDIA_CHANGED The device media has been changed, that is,
227 MediaId changed
228 @retval EFI_INVALID_PARAMETER Some parameters are invalid, such as Buffer is
229 NULL.
230 @retval EFI_BAD_BUFFER_SIZE The buffer size isn't a multiple of media's block
231 size,
232
233 **/
234 EFI_STATUS
235 UsbMassWriteBlocks (
236 IN EFI_BLOCK_IO_PROTOCOL *This,
237 IN UINT32 MediaId,
238 IN EFI_LBA Lba,
239 IN UINTN BufferSize,
240 IN VOID *Buffer
241 )
242 {
243 USB_MASS_DEVICE *UsbMass;
244 EFI_BLOCK_IO_MEDIA *Media;
245 EFI_STATUS Status;
246 UINTN TotalBlock;
247
248 UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);
249 Media = &UsbMass->BlockIoMedia;
250
251 //
252 // First, validate the parameters
253 //
254 if ((Buffer == NULL) || (BufferSize == 0)) {
255 return EFI_INVALID_PARAMETER;
256 }
257
258 //
259 // If it is a remoable media, such as CD-Rom or Usb-Floppy,
260 // if, need to detect the media before each rw, while Usb-Flash
261 // needn't. However, it's hard to identify Usb-Floppy between
262 // Usb-Flash by now, so detect media every time.
263 //
264 Status = UsbBootDetectMedia (UsbMass);
265 if (EFI_ERROR (Status)) {
266 DEBUG ((mUsbMscError, "UsbMassWriteBlocks: UsbBootDetectMedia (%r)\n", Status));
267 return Status;
268 }
269
270 //
271 // Make sure BlockSize and LBA is consistent with BufferSize
272 //
273 if ((BufferSize % Media->BlockSize) != 0) {
274 return EFI_BAD_BUFFER_SIZE;
275 }
276
277 TotalBlock = BufferSize / Media->BlockSize;
278
279 if (Lba + TotalBlock - 1 > Media->LastBlock) {
280 return EFI_BAD_BUFFER_SIZE;
281 }
282
283 //
284 // Try to write the data even the device is marked as ReadOnly,
285 // and clear the status should the write succeed.
286 //
287 Status = UsbBootWriteBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);
288 if (EFI_ERROR (Status)) {
289 DEBUG ((mUsbMscError, "UsbMassWriteBlocks: UsbBootWriteBlocks (%r) -> Reset\n", Status));
290 UsbMassReset (This, TRUE);
291 }
292
293 return Status;
294 }
295
296
297 /**
298 Flush the cached writes to disks. USB mass storage device doesn't
299 support write cache, so return EFI_SUCCESS directly.
300
301 @param This The BLOCK IO protocol
302
303 @retval EFI_SUCCESS Always returns success
304
305 **/
306 EFI_STATUS
307 UsbMassFlushBlocks (
308 IN EFI_BLOCK_IO_PROTOCOL *This
309 )
310 {
311 return EFI_SUCCESS;
312 }
313
314
315 /**
316 Check whether the controller is a supported USB mass storage.
317
318 @param This The USB mass driver's driver binding.
319 @param Controller The device to test against.
320 @param RemainingDevicePath The remaining device path
321
322 @retval EFI_SUCCESS This device is a supported USB mass storage.
323 @retval EFI_UNSUPPORTED The device isn't supported
324 @retval Others Some error happened.
325
326 **/
327 EFI_STATUS
328 EFIAPI
329 USBMassDriverBindingSupported (
330 IN EFI_DRIVER_BINDING_PROTOCOL *This,
331 IN EFI_HANDLE Controller,
332 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
333 )
334 {
335 EFI_USB_IO_PROTOCOL *UsbIo;
336 EFI_USB_INTERFACE_DESCRIPTOR Interface;
337 USB_MASS_TRANSPORT *Transport;
338 EFI_STATUS Status;
339 INTN Index;
340
341 //
342 // Check whether the controlelr support USB_IO
343 //
344 Status = gBS->OpenProtocol (
345 Controller,
346 &gEfiUsbIoProtocolGuid,
347 (VOID **) &UsbIo,
348 This->DriverBindingHandle,
349 Controller,
350 EFI_OPEN_PROTOCOL_BY_DRIVER
351 );
352 if (EFI_ERROR (Status)) {
353 return Status;
354 }
355
356 //
357 // Get the interface to check the USB class and find a transport
358 // protocol handler.
359 //
360 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
361 if (EFI_ERROR (Status)) {
362 goto ON_EXIT;
363 }
364
365 Status = EFI_UNSUPPORTED;
366
367 if (Interface.InterfaceClass != USB_MASS_STORE_CLASS) {
368 goto ON_EXIT;
369 }
370
371 for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {
372 Transport = mUsbMassTransport[Index];
373 if (Interface.InterfaceProtocol == Transport->Protocol) {
374 Status = Transport->Init (UsbIo, Controller, NULL);
375 break;
376 }
377 }
378
379 DEBUG ((mUsbMscInfo, "Found a USB mass store device %r\n", Status));
380
381 ON_EXIT:
382 gBS->CloseProtocol (
383 Controller,
384 &gEfiUsbIoProtocolGuid,
385 This->DriverBindingHandle,
386 Controller
387 );
388
389 return Status;
390 }
391
392
393 /**
394 Start the USB mass storage device on the controller. It will
395 install a BLOCK_IO protocol on the device if everything is OK.
396
397 @param This The USB mass storage driver binding.
398 @param Controller The USB mass storage device to start on
399 @param RemainingDevicePath The remaining device path.
400
401 @retval EFI_SUCCESS The driver has started on the device.
402 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory
403 @retval Others Failed to start the driver on the device.
404
405 **/
406 EFI_STATUS
407 EFIAPI
408 USBMassDriverBindingStart (
409 IN EFI_DRIVER_BINDING_PROTOCOL *This,
410 IN EFI_HANDLE Controller,
411 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
412 )
413 {
414 EFI_USB_IO_PROTOCOL *UsbIo;
415 EFI_USB_INTERFACE_DESCRIPTOR Interface;
416 USB_MASS_DEVICE *UsbMass;
417 USB_MASS_TRANSPORT *Transport;
418 EFI_STATUS Status;
419 UINTN Index;
420
421 Status = gBS->OpenProtocol (
422 Controller,
423 &gEfiUsbIoProtocolGuid,
424 (VOID **) &UsbIo,
425 This->DriverBindingHandle,
426 Controller,
427 EFI_OPEN_PROTOCOL_BY_DRIVER
428 );
429
430 if (EFI_ERROR (Status)) {
431 return Status;
432 }
433
434 UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE));
435 if (UsbMass == NULL) {
436 return EFI_OUT_OF_RESOURCES;
437 }
438
439 //
440 // Initialize the transport protocols
441 //
442 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);
443 if (EFI_ERROR (Status)) {
444 DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbIo->UsbGetInterfaceDescriptor (%r)\n", Status));
445 goto ON_ERROR;
446 }
447
448 Status = EFI_UNSUPPORTED;
449
450 for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {
451 Transport = mUsbMassTransport[Index];
452
453 if (Interface.InterfaceProtocol == Transport->Protocol) {
454 UsbMass->Transport = Transport;
455 Status = Transport->Init (UsbIo, Controller, &UsbMass->Context);
456 break;
457 }
458 }
459
460 if (EFI_ERROR (Status)) {
461 DEBUG ((mUsbMscError, "USBMassDriverBindingStart: Transport->Init (%r)\n", Status));
462 goto ON_ERROR;
463 }
464
465 UsbMass->Signature = USB_MASS_SIGNATURE;
466 UsbMass->Controller = Controller;
467 UsbMass->UsbIo = UsbIo;
468 UsbMass->BlockIo.Media = &UsbMass->BlockIoMedia;
469 UsbMass->BlockIo.Reset = UsbMassReset;
470 UsbMass->BlockIo.ReadBlocks = UsbMassReadBlocks;
471 UsbMass->BlockIo.WriteBlocks = UsbMassWriteBlocks;
472 UsbMass->BlockIo.FlushBlocks = UsbMassFlushBlocks;
473 UsbMass->OpticalStorage = FALSE;
474
475 //
476 // Get the storage's parameters, such as last block number.
477 // then install the BLOCK_IO
478 //
479 Status = UsbMassInitMedia (UsbMass);
480 if (!EFI_ERROR (Status)) {
481 if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) &&
482 (UsbMass->Pdt != USB_PDT_CDROM) &&
483 (UsbMass->Pdt != USB_PDT_OPTICAL) &&
484 (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT)) {
485 DEBUG ((mUsbMscError, "USBMassDriverBindingStart: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt));
486 goto ON_ERROR;
487 }
488 } else if (Status != EFI_NO_MEDIA){
489 DEBUG ((mUsbMscError, "USBMassDriverBindingStart: UsbMassInitMedia (%r)\n", Status));
490 goto ON_ERROR;
491 }
492
493 Status = gBS->InstallProtocolInterface (
494 &Controller,
495 &gEfiBlockIoProtocolGuid,
496 EFI_NATIVE_INTERFACE,
497 &UsbMass->BlockIo
498 );
499 if (EFI_ERROR (Status)) {
500 goto ON_ERROR;
501 }
502
503 return EFI_SUCCESS;
504
505 ON_ERROR:
506 gBS->FreePool (UsbMass);
507
508 gBS->CloseProtocol (
509 Controller,
510 &gEfiUsbIoProtocolGuid,
511 This->DriverBindingHandle,
512 Controller
513 );
514
515 return Status;
516 }
517
518
519 /**
520 Stop controlling the device.
521
522 @param This The USB mass storage driver binding
523 @param Controller The device controller controlled by the driver.
524 @param NumberOfChildren The number of children of this device
525 @param ChildHandleBuffer The buffer of children handle.
526
527 @retval EFI_SUCCESS The driver stopped from controlling the device.
528 @retval Others Failed to stop the driver
529
530 **/
531 EFI_STATUS
532 EFIAPI
533 USBMassDriverBindingStop (
534 IN EFI_DRIVER_BINDING_PROTOCOL *This,
535 IN EFI_HANDLE Controller,
536 IN UINTN NumberOfChildren,
537 IN EFI_HANDLE *ChildHandleBuffer
538 )
539 {
540 EFI_STATUS Status;
541 USB_MASS_DEVICE *UsbMass;
542 EFI_BLOCK_IO_PROTOCOL *BlockIo;
543
544 //
545 // First, get our context back from the BLOCK_IO
546 //
547 Status = gBS->OpenProtocol (
548 Controller,
549 &gEfiBlockIoProtocolGuid,
550 (VOID **) &BlockIo,
551 This->DriverBindingHandle,
552 Controller,
553 EFI_OPEN_PROTOCOL_GET_PROTOCOL
554 );
555
556 if (EFI_ERROR (Status)) {
557 return Status;
558 }
559
560 UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (BlockIo);
561
562 //
563 // Uninstall Block I/O protocol from the device handle,
564 // then call the transport protocol to stop itself.
565 //
566 Status = gBS->UninstallProtocolInterface (
567 Controller,
568 &gEfiBlockIoProtocolGuid,
569 &UsbMass->BlockIo
570 );
571 if (EFI_ERROR (Status)) {
572 return Status;
573 }
574
575 gBS->CloseProtocol (
576 Controller,
577 &gEfiUsbIoProtocolGuid,
578 This->DriverBindingHandle,
579 Controller
580 );
581
582 UsbMass->Transport->Fini (UsbMass->Context);
583 gBS->FreePool (UsbMass);
584
585 return EFI_SUCCESS;
586 }
587
588 EFI_DRIVER_BINDING_PROTOCOL gUSBMassDriverBinding = {
589 USBMassDriverBindingSupported,
590 USBMassDriverBindingStart,
591 USBMassDriverBindingStop,
592 0x11,
593 NULL,
594 NULL
595 };
596
597 EFI_STATUS
598 EFIAPI
599 USBMassStorageEntryPoint (
600 IN EFI_HANDLE ImageHandle,
601 IN EFI_SYSTEM_TABLE *SystemTable
602 )
603 /*++
604
605 Routine Description:
606
607 The entry point for the driver, which will install the driver binding and
608 component name protocol
609
610 Arguments:
611
612 ImageHandle - The image handle of this driver
613 SystemTable - The system table
614
615 Returns:
616
617 EFI_SUCCESS - the protocols are installed OK
618 Others - Failed to install protocols.
619
620 --*/
621 {
622 EFI_STATUS Status;
623
624 //
625 // Install driver binding protocol
626 //
627 Status = EfiLibInstallDriverBindingComponentName2 (
628 ImageHandle,
629 SystemTable,
630 &gUSBMassDriverBinding,
631 ImageHandle,
632 &gUsbMassStorageComponentName,
633 &gUsbMassStorageComponentName2
634 );
635
636 return Status;
637 }