[Description]:
[people/mcb30/edk2.git] / edk2 / MdeModulePkg / Bus / Usb / UsbMassStorageDxe / UsbMassImpl.c
1 /** @file\r
2 \r
3 Copyright (c) 2007 - 2008, Intel Corporation\r
4 All rights reserved. This program and the accompanying materials\r
5 are licensed and made available under the terms and conditions of the BSD License\r
6 which accompanies this distribution.  The full text of the license may be found at\r
7 http://opensource.org/licenses/bsd-license.php\r
8 \r
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
11 \r
12 Module Name:\r
13 \r
14   UsbMassImpl.c\r
15 \r
16 Abstract:\r
17 \r
18   The implementation of USB mass storage class device driver.\r
19   The command set supported is "USB Mass Storage Specification\r
20   for Bootability".\r
21 \r
22 Revision History\r
23 \r
24 \r
25 **/\r
26 \r
27 #include "UsbMassImpl.h"\r
28 \r
29 //\r
30 // The underlying transport protocol. CBI support isn't included\r
31 // in the current build. It is being obseleted by the standard\r
32 // body. If you want to enable it, remove the if directive here,\r
33 // then add the UsbMassCbi.c/.h to the driver's inf file.\r
34 //\r
35 STATIC\r
36 USB_MASS_TRANSPORT *mUsbMassTransport[] = {\r
37   &mUsbCbi0Transport,\r
38   &mUsbCbi1Transport,\r
39   &mUsbBotTransport,\r
40   NULL\r
41 };\r
42 \r
43 /**\r
44   Reset the block device. ExtendedVerification is ignored for this.\r
45 \r
46   @param  This                   The BLOCK IO protocol\r
47   @param  ExtendedVerification   Whether to execute extended verfication.\r
48 \r
49   @retval EFI_SUCCESS            The device is successfully resetted.\r
50   @retval Others                 Failed to reset the device.\r
51 \r
52 **/\r
53 EFI_STATUS\r
54 EFIAPI\r
55 UsbMassReset (\r
56   IN EFI_BLOCK_IO_PROTOCOL    *This,\r
57   IN BOOLEAN                  ExtendedVerification\r
58   )\r
59 {\r
60   USB_MASS_DEVICE *UsbMass;\r
61   EFI_TPL         OldTpl;\r
62   EFI_STATUS      Status;\r
63 \r
64   OldTpl  = gBS->RaiseTPL (USB_MASS_TPL);\r
65 \r
66   UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);\r
67   Status  = UsbMass->Transport->Reset (UsbMass->Context, ExtendedVerification);\r
68 \r
69   gBS->RestoreTPL (OldTpl);\r
70 \r
71   return Status;\r
72 }\r
73 \r
74 /**\r
75   Read some blocks of data from the block device.\r
76 \r
77   @param  This                   The Block IO protocol\r
78   @param  MediaId                The media's ID of the device for current request\r
79   @param  Lba                    The start block number\r
80   @param  BufferSize             The size of buffer to read data in\r
81   @param  Buffer                 The buffer to read data to\r
82 \r
83   @retval EFI_SUCCESS            The data is successfully read\r
84   @retval EFI_NO_MEDIA           Media isn't present\r
85   @retval EFI_MEDIA_CHANGED      The device media has been changed, that is,\r
86                                  MediaId changed\r
87   @retval EFI_INVALID_PARAMETER  Some parameters are invalid, such as Buffer is\r
88                                  NULL.\r
89   @retval EFI_BAD_BUFFER_SIZE    The buffer size isn't a multiple of media's block\r
90                                  size,  or overflow the last block number.\r
91 \r
92 **/\r
93 EFI_STATUS\r
94 EFIAPI\r
95 UsbMassReadBlocks (\r
96   IN EFI_BLOCK_IO_PROTOCOL    *This,\r
97   IN UINT32                   MediaId,\r
98   IN EFI_LBA                  Lba,\r
99   IN UINTN                    BufferSize,\r
100   OUT VOID                    *Buffer\r
101   )\r
102 {\r
103   USB_MASS_DEVICE     *UsbMass;\r
104   EFI_BLOCK_IO_MEDIA  *Media;\r
105   EFI_STATUS          Status;\r
106   EFI_TPL             OldTpl;\r
107   UINTN               TotalBlock;\r
108 \r
109   OldTpl  = gBS->RaiseTPL (USB_MASS_TPL);\r
110   UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);\r
111   Media   = &UsbMass->BlockIoMedia;\r
112 \r
113   //\r
114   // First, validate the parameters\r
115   //\r
116   if ((Buffer == NULL) || (BufferSize == 0)) {\r
117     Status = EFI_INVALID_PARAMETER;\r
118     goto ON_EXIT;\r
119   }\r
120 \r
121   //\r
122   // If it is a removable media, such as CD-Rom or Usb-Floppy,\r
123   // need to detect the media before each rw. While some of\r
124   // Usb-Flash is marked as removable media.\r
125   //\r
126   //\r
127   if (Media->RemovableMedia == TRUE) {\r
128     Status = UsbBootDetectMedia (UsbMass);\r
129     if (EFI_ERROR (Status)) {\r
130       DEBUG ((EFI_D_ERROR, "UsbMassReadBlocks: UsbBootDetectMedia (%r)\n", Status));\r
131       goto ON_EXIT;\r
132     }\r
133   }\r
134 \r
135   //\r
136   // Make sure BlockSize and LBA is consistent with BufferSize\r
137   //\r
138   if ((BufferSize % Media->BlockSize) != 0) {\r
139     Status = EFI_BAD_BUFFER_SIZE;\r
140     goto ON_EXIT;\r
141   }\r
142 \r
143   TotalBlock = BufferSize / Media->BlockSize;\r
144 \r
145   if (Lba + TotalBlock - 1 > Media->LastBlock) {\r
146     Status = EFI_INVALID_PARAMETER;\r
147     goto ON_EXIT;\r
148   }\r
149 \r
150   if (!(Media->MediaPresent)) {\r
151     Status = EFI_NO_MEDIA;\r
152     goto ON_EXIT;\r
153   }\r
154 \r
155   if (MediaId != Media->MediaId) {\r
156     Status = EFI_MEDIA_CHANGED;\r
157     goto ON_EXIT;\r
158   }\r
159 \r
160   Status = UsbBootReadBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);\r
161   if (EFI_ERROR (Status)) {\r
162     DEBUG ((EFI_D_ERROR, "UsbMassReadBlocks: UsbBootReadBlocks (%r) -> Reset\n", Status));\r
163     UsbMassReset (This, TRUE);\r
164   }\r
165 \r
166 ON_EXIT:\r
167   gBS->RestoreTPL (OldTpl);\r
168   return Status;\r
169 }\r
170 \r
171 \r
172 /**\r
173   Write some blocks of data to the block device.\r
174 \r
175   @param  This                   The Block IO protocol\r
176   @param  MediaId                The media's ID of the device for current request\r
177   @param  Lba                    The start block number\r
178   @param  BufferSize             The size of buffer to write data to\r
179   @param  Buffer                 The buffer to write data to\r
180 \r
181   @retval EFI_SUCCESS            The data is successfully written\r
182   @retval EFI_NO_MEDIA           Media isn't present\r
183   @retval EFI_MEDIA_CHANGED      The device media has been changed, that is,\r
184                                  MediaId changed\r
185   @retval EFI_INVALID_PARAMETER  Some parameters are invalid, such as Buffer is\r
186                                  NULL.\r
187   @retval EFI_BAD_BUFFER_SIZE    The buffer size isn't a multiple of media's block\r
188                                  size,\r
189 \r
190 **/\r
191 EFI_STATUS\r
192 EFIAPI\r
193 UsbMassWriteBlocks (\r
194   IN EFI_BLOCK_IO_PROTOCOL    *This,\r
195   IN UINT32                   MediaId,\r
196   IN EFI_LBA                  Lba,\r
197   IN UINTN                    BufferSize,\r
198   IN VOID                     *Buffer\r
199   )\r
200 {\r
201   USB_MASS_DEVICE     *UsbMass;\r
202   EFI_BLOCK_IO_MEDIA  *Media;\r
203   EFI_STATUS          Status;\r
204   EFI_TPL             OldTpl;\r
205   UINTN               TotalBlock;\r
206 \r
207   OldTpl  = gBS->RaiseTPL (USB_MASS_TPL);\r
208   UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (This);\r
209   Media   = &UsbMass->BlockIoMedia;\r
210 \r
211   //\r
212   // First, validate the parameters\r
213   //\r
214   if ((Buffer == NULL) || (BufferSize == 0)) {\r
215     Status = EFI_INVALID_PARAMETER;\r
216     goto ON_EXIT;\r
217   }\r
218 \r
219   //\r
220   // If it is a removable media, such as CD-Rom or Usb-Floppy,\r
221   // need to detect the media before each rw. While some of\r
222   // Usb-Flash is marked as removable media.\r
223   //\r
224   //\r
225   if (Media->RemovableMedia == TRUE) {\r
226     Status = UsbBootDetectMedia (UsbMass);\r
227     if (EFI_ERROR (Status)) {\r
228       DEBUG ((EFI_D_ERROR, "UsbMassWriteBlocks: UsbBootDetectMedia (%r)\n", Status));\r
229       goto ON_EXIT;\r
230     }\r
231   }\r
232 \r
233   //\r
234   // Make sure BlockSize and LBA is consistent with BufferSize\r
235   //\r
236   if ((BufferSize % Media->BlockSize) != 0) {\r
237     Status = EFI_BAD_BUFFER_SIZE;\r
238     goto ON_EXIT;\r
239   }\r
240 \r
241   TotalBlock = BufferSize / Media->BlockSize;\r
242 \r
243   if (Lba + TotalBlock - 1 > Media->LastBlock) {\r
244     Status = EFI_INVALID_PARAMETER;\r
245     goto ON_EXIT;\r
246   }\r
247 \r
248   if (!(Media->MediaPresent)) {\r
249     Status = EFI_NO_MEDIA;\r
250     goto ON_EXIT;\r
251   }\r
252 \r
253   if (MediaId != Media->MediaId) {\r
254     Status = EFI_MEDIA_CHANGED;\r
255     goto ON_EXIT;\r
256   }\r
257 \r
258   //\r
259   // Try to write the data even the device is marked as ReadOnly,\r
260   // and clear the status should the write succeed.\r
261   //\r
262   Status = UsbBootWriteBlocks (UsbMass, (UINT32) Lba, TotalBlock, Buffer);\r
263   if (EFI_ERROR (Status)) {\r
264     DEBUG ((EFI_D_ERROR, "UsbMassWriteBlocks: UsbBootWriteBlocks (%r) -> Reset\n", Status));\r
265     UsbMassReset (This, TRUE);\r
266   }\r
267 \r
268 ON_EXIT:\r
269   gBS->RestoreTPL (OldTpl);\r
270   return Status;\r
271 }\r
272 \r
273 /**\r
274   Flush the cached writes to disks. USB mass storage device doesn't\r
275   support write cache, so return EFI_SUCCESS directly.\r
276 \r
277   @param  This                   The BLOCK IO protocol\r
278 \r
279   @retval EFI_SUCCESS            Always returns success\r
280 \r
281 **/\r
282 EFI_STATUS\r
283 EFIAPI\r
284 UsbMassFlushBlocks (\r
285   IN EFI_BLOCK_IO_PROTOCOL  *This\r
286   )\r
287 {\r
288   return EFI_SUCCESS;\r
289 }\r
290 \r
291 /**\r
292   Retrieve the media parameters such as disk gemotric for the\r
293   device's BLOCK IO protocol.\r
294 \r
295   @param  UsbMass                The USB mass storage device\r
296 \r
297   @retval EFI_SUCCESS            The media parameters is updated successfully.\r
298   @retval Others                 Failed to get the media parameters.\r
299 \r
300 **/\r
301 EFI_STATUS\r
302 UsbMassInitMedia (\r
303   IN USB_MASS_DEVICE          *UsbMass\r
304   )\r
305 {\r
306   EFI_BLOCK_IO_MEDIA          *Media;\r
307   EFI_STATUS                  Status;\r
308   UINTN                       Index;\r
309 \r
310   Media = &UsbMass->BlockIoMedia;\r
311 \r
312   //\r
313   // Initialize the MediaPrsent/ReadOnly and others to the default.\r
314   // We are not forced to get it right at this time, check UEFI2.0\r
315   // spec for more information:\r
316   //\r
317   // MediaPresent: This field shows the media present status as\r
318   //               of the most recent ReadBlocks or WriteBlocks call.\r
319   //\r
320   // ReadOnly    : This field shows the read-only status as of the\r
321   //               recent WriteBlocks call.\r
322   //\r
323   // but remember to update MediaId/MediaPresent/ReadOnly status\r
324   // after ReadBlocks and WriteBlocks\r
325   //\r
326   Media->MediaPresent     = FALSE;\r
327   Media->LogicalPartition = FALSE;\r
328   Media->ReadOnly         = FALSE;\r
329   Media->WriteCaching     = FALSE;\r
330   Media->IoAlign          = 0;\r
331   Media->MediaId          = 1;\r
332 \r
333   //\r
334   // Some device may spend several seconds before it is ready.\r
335   // Try several times before giving up. Wait 5s at most.\r
336   //\r
337   Status = EFI_SUCCESS;\r
338 \r
339   for (Index = 0; Index < USB_BOOT_INIT_MEDIA_RETRY; Index++) {\r
340 \r
341     Status = UsbBootGetParams (UsbMass);\r
342     if ((Status != EFI_MEDIA_CHANGED)\r
343         && (Status != EFI_NOT_READY)\r
344         && (Status != EFI_TIMEOUT)) {\r
345       break;\r
346     }\r
347 \r
348     Status = UsbBootIsUnitReady (UsbMass);\r
349     if (EFI_ERROR (Status)) {\r
350       gBS->Stall (USB_BOOT_RETRY_UNIT_READY_STALL * (Index + 1));\r
351     }\r
352 \r
353   }\r
354 \r
355   return Status;\r
356 }\r
357 \r
358 STATIC\r
359 EFI_STATUS\r
360 UsbMassInitTransport (\r
361   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
362   IN  EFI_HANDLE                   Controller,\r
363   OUT USB_MASS_TRANSPORT           **Transport,\r
364   OUT VOID                         **Context,\r
365   OUT UINT8                        *MaxLun\r
366   )\r
367 {\r
368   EFI_USB_IO_PROTOCOL           *UsbIo;\r
369   EFI_USB_INTERFACE_DESCRIPTOR  Interface;\r
370   UINT8                         Index;\r
371   EFI_STATUS                    Status;\r
372  \r
373   Status = gBS->OpenProtocol (\r
374                   Controller,\r
375                   &gEfiUsbIoProtocolGuid,\r
376                   &UsbIo,\r
377                   This->DriverBindingHandle,\r
378                   Controller,\r
379                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
380                   );\r
381 \r
382   if (EFI_ERROR (Status)) {\r
383     DEBUG ((EFI_D_ERROR, "UsbMassInitTransport: OpenUsbIoProtocol By Driver (%r)\n", Status));\r
384     return Status;\r
385   }\r
386   \r
387   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);\r
388   if (EFI_ERROR (Status)) {\r
389     DEBUG ((EFI_D_ERROR, "UsbMassInitTransport: UsbIo->UsbGetInterfaceDescriptor (%r)\n", Status));\r
390     goto ON_EXIT;\r
391   }\r
392   \r
393   Status = EFI_UNSUPPORTED;\r
394 \r
395   for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {\r
396     *Transport = mUsbMassTransport[Index];\r
397 \r
398     if (Interface.InterfaceProtocol == (*Transport)->Protocol) {\r
399       Status  = (*Transport)->Init (UsbIo, Context);\r
400       break;\r
401     }\r
402   }\r
403 \r
404   if (EFI_ERROR (Status)) {\r
405     DEBUG ((EFI_D_ERROR, "UsbMassInitTransport: Transport->Init (%r)\n", Status));\r
406     goto ON_EXIT;\r
407   }\r
408 \r
409   //\r
410   // For bot device, try to get max lun. \r
411   // If maxlun=0, then non-lun device, else multi-lun device.\r
412   //\r
413   if ((*Transport)->Protocol == USB_MASS_STORE_BOT) {\r
414     (*Transport)->GetMaxLun (*Context, MaxLun);\r
415     DEBUG ((EFI_D_INFO, "UsbMassInitTransport: GetMaxLun = %d\n", *MaxLun));\r
416   }\r
417 \r
418 ON_EXIT:\r
419   gBS->CloseProtocol (\r
420          Controller,\r
421          &gEfiUsbIoProtocolGuid,\r
422          This->DriverBindingHandle,\r
423          Controller\r
424          );\r
425   return Status;  \r
426 }\r
427 \r
428 STATIC\r
429 EFI_STATUS\r
430 UsbMassInitMultiLun (\r
431   IN  EFI_DRIVER_BINDING_PROTOCOL  *This,\r
432   IN EFI_HANDLE                    Controller,\r
433   IN USB_MASS_TRANSPORT            *Transport,\r
434   IN VOID                          *Context,\r
435   IN EFI_DEVICE_PATH_PROTOCOL      *DevicePath,\r
436   IN UINT8                         MaxLun\r
437   )\r
438 {\r
439   USB_MASS_DEVICE                  *UsbMass;\r
440   EFI_USB_IO_PROTOCOL              *UsbIo;\r
441   DEVICE_LOGICAL_UNIT_DEVICE_PATH  LunNode;\r
442   UINT8                            Index;\r
443   EFI_STATUS                       Status;\r
444 \r
445   ASSERT (MaxLun > 0);\r
446 \r
447   for (Index = 0; Index <= MaxLun; Index++) { \r
448 \r
449     DEBUG ((EFI_D_INFO, "UsbMassInitMultiLun: Start to initialize No.%d logic unit\n", Index));\r
450     \r
451     UsbIo   = NULL;\r
452     UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE));\r
453     if (UsbMass == NULL) {\r
454       Status = EFI_OUT_OF_RESOURCES;\r
455       goto ON_ERROR;\r
456     }\r
457       \r
458     UsbMass->Signature            = USB_MASS_SIGNATURE;\r
459     UsbMass->UsbIo                = UsbIo;\r
460     UsbMass->BlockIo.Media        = &UsbMass->BlockIoMedia;\r
461     UsbMass->BlockIo.Reset        = UsbMassReset;\r
462     UsbMass->BlockIo.ReadBlocks   = UsbMassReadBlocks;\r
463     UsbMass->BlockIo.WriteBlocks  = UsbMassWriteBlocks;\r
464     UsbMass->BlockIo.FlushBlocks  = UsbMassFlushBlocks;\r
465     UsbMass->OpticalStorage       = FALSE;\r
466     UsbMass->Transport            = Transport;\r
467     UsbMass->Context              = Context;\r
468     UsbMass->Lun                  = Index;\r
469     \r
470     //\r
471     // Get the storage's parameters, such as last block number.\r
472     // then install the BLOCK_IO\r
473     //\r
474     Status = UsbMassInitMedia (UsbMass);\r
475     if (!EFI_ERROR (Status)) {\r
476       if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) && \r
477            (UsbMass->Pdt != USB_PDT_CDROM) &&\r
478            (UsbMass->Pdt != USB_PDT_OPTICAL) && \r
479            (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT)) {\r
480         DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt));\r
481         goto ON_ERROR;\r
482       }\r
483     } else if (Status != EFI_NO_MEDIA){\r
484       DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: UsbMassInitMedia (%r)\n", Status));\r
485       goto ON_ERROR;\r
486     }\r
487 \r
488     //\r
489     // Create a device path node of device logic unit, and append it \r
490     //\r
491     LunNode.Header.Type    = MESSAGING_DEVICE_PATH;\r
492     LunNode.Header.SubType = MSG_DEVICE_LOGICAL_UNIT_DP;\r
493     LunNode.Lun            = UsbMass->Lun;\r
494   \r
495     SetDevicePathNodeLength (&LunNode.Header, sizeof (LunNode));\r
496   \r
497     UsbMass->DevicePath = AppendDevicePathNode (DevicePath, &LunNode.Header);\r
498   \r
499     if (UsbMass->DevicePath == NULL) {\r
500       DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: failed to create device logic unit device path\n"));\r
501   \r
502       Status = EFI_OUT_OF_RESOURCES;\r
503       goto ON_ERROR;\r
504     }\r
505 \r
506     //\r
507     // Create a UsbMass handle for each lun, and install blockio and devicepath protocols.\r
508     //\r
509     Status = gBS->InstallMultipleProtocolInterfaces (\r
510                     &UsbMass->Controller,\r
511                     &gEfiDevicePathProtocolGuid,\r
512                     UsbMass->DevicePath,\r
513                     &gEfiBlockIoProtocolGuid,\r
514                     &UsbMass->BlockIo,\r
515                     NULL\r
516                     );\r
517     \r
518     if (EFI_ERROR (Status)) {\r
519       DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: InstallMultipleProtocolInterfaces (%r)\n", Status));\r
520       goto ON_ERROR;\r
521     }\r
522 \r
523     //\r
524     // Open UsbIo protocol by child to setup a parent-child relationship.\r
525     //\r
526     Status = gBS->OpenProtocol (\r
527                     Controller,\r
528                     &gEfiUsbIoProtocolGuid,\r
529                     &UsbIo,\r
530                     This->DriverBindingHandle,\r
531                     UsbMass->Controller,\r
532                     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
533                     );\r
534 \r
535     if (EFI_ERROR (Status)) {\r
536       DEBUG ((EFI_D_ERROR, "UsbMassInitMultiLun: OpenUsbIoProtocol By Child (%r)\n", Status));\r
537       gBS->UninstallMultipleProtocolInterfaces (\r
538              &UsbMass->Controller,\r
539              &gEfiDevicePathProtocolGuid,\r
540              UsbMass->DevicePath,\r
541              &gEfiBlockIoProtocolGuid,\r
542              &UsbMass->BlockIo,\r
543              NULL\r
544              );\r
545       goto ON_ERROR;\r
546     }\r
547     \r
548     DEBUG ((EFI_D_INFO, "UsbMassInitMultiLun: Success to initialize No.%d logic unit\n", Index));\r
549   }\r
550   \r
551   return EFI_SUCCESS;\r
552 \r
553 ON_ERROR:\r
554   if (UsbMass->DevicePath != NULL) {\r
555     gBS->FreePool (UsbMass->DevicePath);\r
556   }\r
557   if (UsbMass != NULL) {\r
558     gBS->FreePool (UsbMass);\r
559   }\r
560   if (UsbIo != NULL) {\r
561     gBS->CloseProtocol (\r
562            Controller,\r
563            &gEfiUsbIoProtocolGuid,\r
564            This->DriverBindingHandle,\r
565            UsbMass->Controller\r
566            );\r
567   }\r
568 \r
569   //\r
570   // If only success to initialize one lun, return success, or else return error\r
571   //\r
572   if (Index > 0) {\r
573     return EFI_SUCCESS; \r
574   } else {\r
575     return Status;\r
576   } \r
577 }\r
578 \r
579 STATIC\r
580 EFI_STATUS\r
581 UsbMassInitNonLun (\r
582   IN EFI_DRIVER_BINDING_PROTOCOL   *This,\r
583   IN EFI_HANDLE                    Controller,\r
584   IN USB_MASS_TRANSPORT            *Transport,\r
585   IN VOID                          *Context\r
586   )\r
587 {\r
588   USB_MASS_DEVICE             *UsbMass;\r
589   EFI_USB_IO_PROTOCOL         *UsbIo;\r
590   EFI_STATUS                  Status;\r
591 \r
592   UsbIo   = NULL;\r
593   UsbMass = AllocateZeroPool (sizeof (USB_MASS_DEVICE));\r
594   if (UsbMass == NULL) {\r
595     return EFI_OUT_OF_RESOURCES;\r
596   }\r
597   Status = gBS->OpenProtocol (\r
598                   Controller,\r
599                   &gEfiUsbIoProtocolGuid,\r
600                   &UsbIo,\r
601                   This->DriverBindingHandle,\r
602                   Controller,\r
603                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
604                   );\r
605 \r
606   if (EFI_ERROR (Status)) {\r
607     DEBUG ((EFI_D_ERROR, "UsbMassInitNonLun: OpenUsbIoProtocol By Driver (%r)\n", Status));\r
608     goto ON_ERROR;\r
609   }\r
610   \r
611   UsbMass->Signature            = USB_MASS_SIGNATURE;\r
612   UsbMass->Controller           = Controller;\r
613   UsbMass->UsbIo                = UsbIo;\r
614   UsbMass->BlockIo.Media        = &UsbMass->BlockIoMedia;\r
615   UsbMass->BlockIo.Reset        = UsbMassReset;\r
616   UsbMass->BlockIo.ReadBlocks   = UsbMassReadBlocks;\r
617   UsbMass->BlockIo.WriteBlocks  = UsbMassWriteBlocks;\r
618   UsbMass->BlockIo.FlushBlocks  = UsbMassFlushBlocks;\r
619   UsbMass->OpticalStorage       = FALSE;\r
620   UsbMass->Transport            = Transport;\r
621   UsbMass->Context              = Context;\r
622   \r
623   //\r
624   // Get the storage's parameters, such as last block number.\r
625   // then install the BLOCK_IO\r
626   //\r
627   Status = UsbMassInitMedia (UsbMass);\r
628   if (!EFI_ERROR (Status)) {\r
629     if ((UsbMass->Pdt != USB_PDT_DIRECT_ACCESS) && \r
630          (UsbMass->Pdt != USB_PDT_CDROM) &&\r
631          (UsbMass->Pdt != USB_PDT_OPTICAL) && \r
632          (UsbMass->Pdt != USB_PDT_SIMPLE_DIRECT)) {\r
633       DEBUG ((EFI_D_ERROR, "UsbMassInitNonLun: Found an unsupported peripheral type[%d]\n", UsbMass->Pdt));\r
634       goto ON_ERROR;\r
635     }\r
636   } else if (Status != EFI_NO_MEDIA){\r
637     DEBUG ((EFI_D_ERROR, "UsbMassInitNonLun: UsbMassInitMedia (%r)\n", Status));\r
638     goto ON_ERROR;\r
639   }\r
640     \r
641   Status = gBS->InstallProtocolInterface (\r
642                   &Controller,\r
643                   &gEfiBlockIoProtocolGuid,\r
644                   EFI_NATIVE_INTERFACE,\r
645                   &UsbMass->BlockIo\r
646                   );\r
647   if (EFI_ERROR (Status)) {\r
648     goto ON_ERROR;\r
649   }\r
650 \r
651   return EFI_SUCCESS;\r
652 \r
653 ON_ERROR:\r
654   if (UsbMass != NULL) {\r
655     gBS->FreePool (UsbMass);\r
656   }\r
657   gBS->CloseProtocol (\r
658          Controller,\r
659          &gEfiUsbIoProtocolGuid,\r
660          This->DriverBindingHandle,\r
661          Controller\r
662          );\r
663   return Status;  \r
664 }\r
665 \r
666 \r
667 /**\r
668   Check whether the controller is a supported USB mass storage.\r
669 \r
670   @param  This                   The USB mass driver's driver binding.\r
671   @param  Controller             The device to test against.\r
672   @param  RemainingDevicePath    The remaining device path\r
673 \r
674   @retval EFI_SUCCESS            This device is a supported USB mass storage.\r
675   @retval EFI_UNSUPPORTED        The device isn't supported\r
676   @retval Others                 Some error happened.\r
677 \r
678 **/\r
679 EFI_STATUS\r
680 EFIAPI\r
681 USBMassDriverBindingSupported (\r
682   IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
683   IN EFI_HANDLE                   Controller,\r
684   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
685   )\r
686 {\r
687   EFI_USB_IO_PROTOCOL           *UsbIo;\r
688   EFI_USB_INTERFACE_DESCRIPTOR  Interface;\r
689   USB_MASS_TRANSPORT            *Transport;\r
690   EFI_STATUS                    Status;\r
691   INTN                          Index;\r
692 \r
693   //\r
694   // Check whether the controlelr support USB_IO\r
695   //\r
696   Status = gBS->OpenProtocol (\r
697                   Controller,\r
698                   &gEfiUsbIoProtocolGuid,\r
699                   (VOID **) &UsbIo,\r
700                   This->DriverBindingHandle,\r
701                   Controller,\r
702                   EFI_OPEN_PROTOCOL_BY_DRIVER\r
703                   );\r
704   if (EFI_ERROR (Status)) {\r
705     return Status;\r
706   }\r
707 \r
708   //\r
709   // Get the interface to check the USB class and find a transport\r
710   // protocol handler.\r
711   //\r
712   Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &Interface);\r
713   if (EFI_ERROR (Status)) {\r
714     goto ON_EXIT;\r
715   }\r
716 \r
717   Status = EFI_UNSUPPORTED;\r
718 \r
719   if (Interface.InterfaceClass != USB_MASS_STORE_CLASS) {\r
720     goto ON_EXIT;\r
721   }\r
722 \r
723   for (Index = 0; mUsbMassTransport[Index] != NULL; Index++) {\r
724     Transport = mUsbMassTransport[Index];\r
725     if (Interface.InterfaceProtocol == Transport->Protocol) {\r
726       Status = Transport->Init (UsbIo, NULL);\r
727       break;\r
728     }\r
729   }\r
730 \r
731   DEBUG ((EFI_D_INFO, "Found a USB mass store device %r\n", Status));\r
732 \r
733 ON_EXIT:\r
734   gBS->CloseProtocol (\r
735         Controller,\r
736         &gEfiUsbIoProtocolGuid,\r
737         This->DriverBindingHandle,\r
738         Controller\r
739         );\r
740 \r
741   return Status;\r
742 }\r
743 \r
744 \r
745 /**\r
746   Start the USB mass storage device on the controller. It will\r
747   install a BLOCK_IO protocol on the device if everything is OK.\r
748 \r
749   @param  This                   The USB mass storage driver binding.\r
750   @param  Controller             The USB mass storage device to start on\r
751   @param  RemainingDevicePath    The remaining device path.\r
752 \r
753   @retval EFI_SUCCESS            The driver has started on the device.\r
754   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory\r
755   @retval Others                 Failed to start the driver on the device.\r
756 \r
757 **/\r
758 EFI_STATUS\r
759 EFIAPI\r
760 USBMassDriverBindingStart (\r
761   IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
762   IN EFI_HANDLE                   Controller,\r
763   IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath\r
764   )\r
765 {\r
766   USB_MASS_TRANSPORT            *Transport;\r
767   EFI_DEVICE_PATH_PROTOCOL      *DevicePath;\r
768   VOID                          *Context;\r
769   UINT8                         MaxLun;\r
770   EFI_STATUS                    Status;\r
771   \r
772   Transport = NULL;\r
773   Context   = NULL;\r
774   MaxLun    = 0;\r
775 \r
776   //\r
777   // Get interface and protocols, initialize transport\r
778   //\r
779   Status = UsbMassInitTransport (This, Controller, &Transport, &Context, &MaxLun);\r
780 \r
781   if (EFI_ERROR (Status)) {\r
782     DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbMassInitTransport (%r)\n", Status));\r
783     return Status;\r
784   }\r
785   if (MaxLun == 0) {\r
786     //\r
787     // Initialize No/Unsupported LUN device\r
788     //\r
789     Status = UsbMassInitNonLun(This, Controller, Transport, Context);\r
790     if (EFI_ERROR (Status)) { \r
791       DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbMassInitNonLun (%r)\n", Status));\r
792     }\r
793   } else {\r
794     //\r
795     // Open device path to perpare append Device Logic Unit node.\r
796     //\r
797     Status = gBS->OpenProtocol (\r
798                     Controller,\r
799                     &gEfiDevicePathProtocolGuid,\r
800                     (VOID **) &DevicePath,\r
801                     This->DriverBindingHandle,\r
802                     Controller,\r
803                     EFI_OPEN_PROTOCOL_BY_DRIVER\r
804                     );\r
805   \r
806     if (EFI_ERROR (Status)) {\r
807       DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: OpenDevicePathProtocol By Driver (%r)\n", Status));\r
808       return Status;\r
809     }\r
810 \r
811     //\r
812     // Try best to initialize all LUNs, and return success only if one of LUNs successed to initialized.\r
813     //\r
814     Status = UsbMassInitMultiLun(This, Controller, Transport, Context, DevicePath, MaxLun);\r
815     if (EFI_ERROR (Status)) {\r
816      gBS->CloseProtocol (\r
817             Controller,\r
818             &gEfiDevicePathProtocolGuid,\r
819             This->DriverBindingHandle,\r
820             Controller\r
821             );\r
822       DEBUG ((EFI_D_ERROR, "USBMassDriverBindingStart: UsbMassInitMultiLun (%r) with Maxlun=%d\n", Status, MaxLun));\r
823     }\r
824   }\r
825   return Status;\r
826 }\r
827 \r
828 \r
829 /**\r
830   Stop controlling the device.\r
831 \r
832   @param  This                   The USB mass storage driver binding\r
833   @param  Controller             The device controller controlled by the driver.\r
834   @param  NumberOfChildren       The number of children of this device\r
835   @param  ChildHandleBuffer      The buffer of children handle.\r
836 \r
837   @retval EFI_SUCCESS            The driver stopped from controlling the device.\r
838   @retval Others                 Failed to stop the driver\r
839 \r
840 **/\r
841 EFI_STATUS\r
842 EFIAPI\r
843 USBMassDriverBindingStop (\r
844   IN  EFI_DRIVER_BINDING_PROTOCOL *This,\r
845   IN  EFI_HANDLE                  Controller,\r
846   IN  UINTN                       NumberOfChildren,\r
847   IN  EFI_HANDLE                  *ChildHandleBuffer\r
848   )\r
849 {\r
850   EFI_STATUS            Status;\r
851   USB_MASS_DEVICE       *UsbMass;\r
852   EFI_USB_IO_PROTOCOL     *UsbIo;\r
853   EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
854   UINTN                   Index;\r
855   BOOLEAN                 AllChildrenStopped;\r
856 \r
857   //\r
858   // This a bus driver stop function since multi-lun supported. There are three \r
859   // kinds of device handle might be passed, 1st is a handle with devicepath/\r
860   // usbio/blockio installed(non-multi-lun), 2nd is a handle with devicepath/\r
861   // usbio installed(multi-lun root), 3rd is a handle with devicepath/blockio\r
862   // installed(multi-lun).\r
863   //\r
864   if (NumberOfChildren == 0) {\r
865     //\r
866     // A handle without any children, might be 1st and 2nd type.\r
867     //\r
868     Status = gBS->OpenProtocol (\r
869                     Controller,\r
870                     &gEfiBlockIoProtocolGuid,\r
871                     &BlockIo,\r
872                     This->DriverBindingHandle,\r
873                     Controller,\r
874                     EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
875                     );\r
876   \r
877     if (EFI_ERROR(Status)) {\r
878       //\r
879       // This is a 2nd type handle(multi-lun root), which only needs close \r
880       // devicepath protocol.\r
881       //\r
882       gBS->CloseProtocol (\r
883             Controller,\r
884             &gEfiDevicePathProtocolGuid,\r
885             This->DriverBindingHandle,\r
886             Controller\r
887             );\r
888       DEBUG ((EFI_D_INFO, "Success to stop multi-lun root handle\n"));\r
889       return EFI_SUCCESS;\r
890     }\r
891     \r
892     //\r
893     // This is a 1st type handle(non-multi-lun), which only needs uninstall\r
894     // blockio protocol, close usbio protocol and free mass device.\r
895     //\r
896     UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (BlockIo);\r
897   \r
898     //\r
899     // Uninstall Block I/O protocol from the device handle,\r
900     // then call the transport protocol to stop itself.\r
901     //\r
902     Status = gBS->UninstallProtocolInterface (\r
903                     Controller,\r
904                     &gEfiBlockIoProtocolGuid,\r
905                     &UsbMass->BlockIo\r
906                     );\r
907     if (EFI_ERROR (Status)) {\r
908       return Status;\r
909     }\r
910   \r
911     gBS->CloseProtocol (\r
912           Controller,\r
913           &gEfiUsbIoProtocolGuid,\r
914           This->DriverBindingHandle,\r
915           Controller\r
916           );\r
917   \r
918     UsbMass->Transport->Fini (UsbMass->Context);\r
919     gBS->FreePool (UsbMass);\r
920     \r
921     DEBUG ((EFI_D_INFO, "Success to stop non-multi-lun root handle\n"));\r
922     return EFI_SUCCESS;\r
923   } \r
924 \r
925   //\r
926   // This is a 3rd type handle(multi-lun), which needs uninstall\r
927   // blockio and devicepath protocol, close usbio protocol and \r
928   // free mass device.\r
929   //\r
930   AllChildrenStopped = TRUE;\r
931 \r
932   for (Index = 0; Index < NumberOfChildren; Index++) {\r
933 \r
934     Status = gBS->OpenProtocol (\r
935                     ChildHandleBuffer[Index],\r
936                     &gEfiBlockIoProtocolGuid,\r
937                     (VOID **) &BlockIo,\r
938                     This->DriverBindingHandle,\r
939                     Controller,\r
940                     EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
941                     );\r
942     if (EFI_ERROR (Status)) {\r
943       AllChildrenStopped = FALSE;\r
944       DEBUG ((EFI_D_ERROR, "Fail to stop No.%d multi-lun child handle when opening blockio\n", Index));\r
945       continue;\r
946     }\r
947 \r
948     UsbMass = USB_MASS_DEVICE_FROM_BLOCKIO (BlockIo);\r
949 \r
950     gBS->CloseProtocol (\r
951           Controller,\r
952           &gEfiUsbIoProtocolGuid,\r
953           This->DriverBindingHandle,\r
954           ChildHandleBuffer[Index]\r
955           );\r
956   \r
957     Status = gBS->UninstallMultipleProtocolInterfaces (\r
958                     ChildHandleBuffer[Index],\r
959                     &gEfiDevicePathProtocolGuid,\r
960                     UsbMass->DevicePath,\r
961                     &gEfiBlockIoProtocolGuid,\r
962                     &UsbMass->BlockIo,\r
963                     NULL\r
964                     );\r
965     \r
966     if (EFI_ERROR (Status)) {\r
967       //\r
968       // Fail to uninstall blockio and devicepath protocol, so re-open usbio by child.\r
969       //\r
970       AllChildrenStopped = FALSE;\r
971       DEBUG ((EFI_D_ERROR, "Fail to stop No.%d multi-lun child handle when uninstalling blockio and devicepath\n", Index));\r
972       \r
973       gBS->OpenProtocol (\r
974              Controller,\r
975              &gEfiUsbIoProtocolGuid,\r
976              &UsbIo,\r
977              This->DriverBindingHandle,\r
978              ChildHandleBuffer[Index],\r
979              EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
980              );\r
981     } else {\r
982       //\r
983       // Success to stop this multi-lun handle, so go on next child.\r
984       //\r
985       if (((Index + 1) == NumberOfChildren) && AllChildrenStopped) {\r
986         UsbMass->Transport->Fini (UsbMass->Context);\r
987       }\r
988       gBS->FreePool (UsbMass);\r
989     }\r
990   }\r
991 \r
992   if (!AllChildrenStopped) {\r
993     return EFI_DEVICE_ERROR;\r
994   }\r
995   \r
996   DEBUG ((EFI_D_INFO, "Success to stop all %d multi-lun children handles\n", NumberOfChildren));\r
997   return EFI_SUCCESS;\r
998 }\r
999 \r
1000 EFI_DRIVER_BINDING_PROTOCOL gUSBMassDriverBinding = {\r
1001   USBMassDriverBindingSupported,\r
1002   USBMassDriverBindingStart,\r
1003   USBMassDriverBindingStop,\r
1004   0x11,\r
1005   NULL,\r
1006   NULL\r
1007 };\r
1008 \r
1009 EFI_STATUS\r
1010 EFIAPI\r
1011 USBMassStorageEntryPoint (\r
1012   IN EFI_HANDLE               ImageHandle,\r
1013   IN EFI_SYSTEM_TABLE         *SystemTable\r
1014   )\r
1015 /*++\r
1016 \r
1017 Routine Description:\r
1018 \r
1019   The entry point for the driver, which will install the driver binding and\r
1020   component name protocol\r
1021 \r
1022 Arguments:\r
1023 \r
1024   ImageHandle - The image handle of this driver\r
1025   SystemTable - The system table\r
1026 \r
1027 Returns:\r
1028 \r
1029   EFI_SUCCESS - the protocols are installed OK\r
1030   Others      - Failed to install protocols.\r
1031 \r
1032 --*/\r
1033 {\r
1034   EFI_STATUS  Status;\r
1035 \r
1036   //\r
1037   // Install driver binding protocol\r
1038   //\r
1039   Status = EfiLibInstallDriverBindingComponentName2 (\r
1040              ImageHandle,\r
1041              SystemTable,\r
1042              &gUSBMassDriverBinding,\r
1043              ImageHandle,\r
1044              &gUsbMassStorageComponentName,\r
1045              &gUsbMassStorageComponentName2\r
1046              );\r
1047 \r
1048   return Status;\r
1049 }\r