EmbeddedPkg/MmcDxe: Fix protocols uninstallation in Stop() function
[mirror_edk2.git] / ArmPkg / Universal / MmcDxe / Mmc.c
1 /** @file
2 Main file of the MMC Dxe driver. The driver entrypoint is defined into this file.
3
4 Copyright (c) 2011, ARM Limited. All rights reserved.
5
6 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 <Protocol/DevicePath.h>
17 #include <Protocol/MmcHost.h>
18
19 #include <Library/BaseLib.h>
20 #include <Library/BaseMemoryLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23 #include <Library/DevicePathLib.h>
24 #include <Library/DebugLib.h>
25
26 #include "Mmc.h"
27
28 EFI_BLOCK_IO_MEDIA mMmcMediaTemplate = {
29 SIGNATURE_32('m','m','c','o'), // MediaId
30 TRUE, // RemovableMedia
31 FALSE, // MediaPresent
32 FALSE, // LogicalPartition
33 FALSE, // ReadOnly
34 FALSE, // WriteCaching
35 512, // BlockSize
36 4, // IoAlign
37 0, // Pad
38 0 // LastBlock
39 };
40
41 //
42 // This device structure is serviced as a header.
43 // Its next field points to the first root bridge device node.
44 //
45 LIST_ENTRY mMmcHostPool;
46
47 /**
48 Initialize the MMC Host Pool to support multiple MMC devices
49 **/
50 VOID
51 InitializeMmcHostPool (
52 VOID
53 )
54 {
55 InitializeListHead (&mMmcHostPool);
56 }
57
58 /**
59 Insert a new Mmc Host controller to the pool
60 **/
61 VOID
62 InsertMmcHost (
63 IN MMC_HOST_INSTANCE *MmcHostInstance
64 )
65 {
66 InsertTailList (&mMmcHostPool, &(MmcHostInstance->Link));
67 }
68
69 /*
70 Remove a new Mmc Host controller to the pool
71 */
72 VOID
73 RemoveMmcHost (
74 IN MMC_HOST_INSTANCE *MmcHostInstance
75 )
76 {
77 RemoveEntryList (&(MmcHostInstance->Link));
78 }
79
80 MMC_HOST_INSTANCE* CreateMmcHostInstance(
81 IN EFI_MMC_HOST_PROTOCOL* MmcHost
82 )
83 {
84 EFI_STATUS Status;
85 MMC_HOST_INSTANCE* MmcHostInstance;
86 EFI_DEVICE_PATH_PROTOCOL *NewDevicePathNode;
87 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
88
89 MmcHostInstance = AllocateZeroPool (sizeof (MMC_HOST_INSTANCE));
90 if (MmcHostInstance == NULL) {
91 return NULL;
92 }
93
94 MmcHostInstance->Signature = MMC_HOST_INSTANCE_SIGNATURE;
95
96 MmcHostInstance->State = MmcHwInitializationState;
97
98 MmcHostInstance->BlockIo.Media = AllocateCopyPool (sizeof(EFI_BLOCK_IO_MEDIA), &mMmcMediaTemplate);
99 if (MmcHostInstance->BlockIo.Media == NULL) {
100 goto FREE_INSTANCE;
101 }
102
103 MmcHostInstance->BlockIo.Revision = EFI_BLOCK_IO_INTERFACE_REVISION;
104 MmcHostInstance->BlockIo.Reset = MmcReset;
105 MmcHostInstance->BlockIo.ReadBlocks = MmcReadBlocks;
106 MmcHostInstance->BlockIo.WriteBlocks = MmcWriteBlocks;
107 MmcHostInstance->BlockIo.FlushBlocks = MmcFlushBlocks;
108
109 MmcHostInstance->MmcHost = MmcHost;
110
111 // Create DevicePath for the new MMC Host
112 Status = MmcHost->BuildDevicePath(&NewDevicePathNode);
113 if (EFI_ERROR (Status)) {
114 goto FREE_MEDIA;
115 }
116
117 DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) AllocatePool (END_DEVICE_PATH_LENGTH);
118 if (DevicePath == NULL) {
119 goto FREE_MEDIA;
120 }
121
122 SetDevicePathEndNode (DevicePath);
123 MmcHostInstance->DevicePath = AppendDevicePathNode (DevicePath, NewDevicePathNode);
124
125 // Publish BlockIO protocol interface
126 Status = gBS->InstallMultipleProtocolInterfaces (
127 &MmcHostInstance->MmcHandle,
128 &gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo),
129 &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
130 NULL
131 );
132 if (EFI_ERROR(Status)) {
133 goto FREE_DEVICE_PATH;
134 }
135
136 return MmcHostInstance;
137
138 FREE_DEVICE_PATH:
139 FreePool(DevicePath);
140
141 FREE_MEDIA:
142 FreePool(MmcHostInstance->BlockIo.Media);
143
144 FREE_INSTANCE:
145 FreePool(MmcHostInstance);
146
147 return NULL;
148 }
149
150 EFI_STATUS DestroyMmcHostInstance(
151 IN MMC_HOST_INSTANCE* MmcHostInstance
152 )
153 {
154 EFI_STATUS Status;
155
156 // Uninstall Protocol Interfaces
157 Status = gBS->UninstallMultipleProtocolInterfaces(
158 MmcHostInstance->MmcHandle,
159 &gEfiBlockIoProtocolGuid,&(MmcHostInstance->BlockIo),
160 &gEfiDevicePathProtocolGuid,MmcHostInstance->DevicePath,
161 NULL
162 );
163 ASSERT_EFI_ERROR (Status);
164
165 // Free Memory allocated for the instance
166 if (MmcHostInstance->BlockIo.Media) {
167 FreePool(MmcHostInstance->BlockIo.Media);
168 }
169 FreePool (MmcHostInstance);
170
171 return Status;
172 }
173
174 /**
175 This function checks if the controller implement the Mmc Host and the Device Path Protocols
176 **/
177 EFI_STATUS
178 EFIAPI
179 MmcDriverBindingSupported (
180 IN EFI_DRIVER_BINDING_PROTOCOL *This,
181 IN EFI_HANDLE Controller,
182 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
183 )
184 {
185 EFI_STATUS Status;
186 //EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
187 EFI_MMC_HOST_PROTOCOL *MmcHost;
188 EFI_DEV_PATH_PTR Node;
189
190 //
191 // Check RemainingDevicePath validation
192 //
193 if (RemainingDevicePath != NULL) {
194 //
195 // Check if RemainingDevicePath is the End of Device Path Node,
196 // if yes, go on checking other conditions
197 //
198 if (!IsDevicePathEnd (RemainingDevicePath)) {
199 //
200 // If RemainingDevicePath isn't the End of Device Path Node,
201 // check its validation
202 //
203 Node.DevPath = RemainingDevicePath;
204 if (Node.DevPath->Type != HARDWARE_DEVICE_PATH ||
205 Node.DevPath->SubType != HW_VENDOR_DP ||
206 DevicePathNodeLength(Node.DevPath) != sizeof(VENDOR_DEVICE_PATH)) {
207 return EFI_UNSUPPORTED;
208 }
209 }
210 }
211
212 //
213 // Check if Mmc Host protocol is installed by platform
214 //
215 Status = gBS->OpenProtocol (
216 Controller,
217 &gEfiMmcHostProtocolGuid,
218 (VOID **) &MmcHost,
219 This->DriverBindingHandle,
220 Controller,
221 EFI_OPEN_PROTOCOL_BY_DRIVER
222 );
223 if (Status == EFI_ALREADY_STARTED) {
224 return EFI_SUCCESS;
225 }
226 if (EFI_ERROR (Status)) {
227 return Status;
228 }
229
230 //
231 // Close the Mmc Host used to perform the supported test
232 //
233 gBS->CloseProtocol (
234 Controller,
235 &gEfiMmcHostProtocolGuid,
236 This->DriverBindingHandle,
237 Controller
238 );
239
240 return EFI_SUCCESS;
241 }
242
243 /**
244
245 **/
246 EFI_STATUS
247 EFIAPI
248 MmcDriverBindingStart (
249 IN EFI_DRIVER_BINDING_PROTOCOL *This,
250 IN EFI_HANDLE Controller,
251 IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath
252 )
253 {
254 EFI_STATUS Status;
255 MMC_HOST_INSTANCE *MmcHostInstance;
256 EFI_MMC_HOST_PROTOCOL *MmcHost;
257
258 //
259 // Check RemainingDevicePath validation
260 //
261 if (RemainingDevicePath != NULL) {
262 //
263 // Check if RemainingDevicePath is the End of Device Path Node,
264 // if yes, return EFI_SUCCESS
265 //
266 if (IsDevicePathEnd (RemainingDevicePath)) {
267 return EFI_SUCCESS;
268 }
269 }
270
271 //
272 // Get the Mmc Host protocol
273 //
274 Status = gBS->OpenProtocol (
275 Controller,
276 &gEfiMmcHostProtocolGuid,
277 (VOID **) &MmcHost,
278 This->DriverBindingHandle,
279 Controller,
280 EFI_OPEN_PROTOCOL_BY_DRIVER
281 );
282 if (EFI_ERROR (Status)) {
283 if (Status == EFI_ALREADY_STARTED) {
284 return EFI_SUCCESS;
285 }
286 return Status;
287 }
288
289 MmcHostInstance = CreateMmcHostInstance(MmcHost);
290 if (MmcHostInstance != NULL) {
291 // Add the handle to the pool
292 InsertMmcHost (MmcHostInstance);
293 }
294
295 return EFI_SUCCESS;
296 }
297
298 /**
299
300 **/
301 EFI_STATUS
302 EFIAPI
303 MmcDriverBindingStop (
304 IN EFI_DRIVER_BINDING_PROTOCOL *This,
305 IN EFI_HANDLE Controller,
306 IN UINTN NumberOfChildren,
307 IN EFI_HANDLE *ChildHandleBuffer
308 )
309 {
310 EFI_STATUS Status = EFI_SUCCESS;
311 LIST_ENTRY *CurrentLink;
312 MMC_HOST_INSTANCE *MmcHostInstance;
313
314 MMC_TRACE("MmcDriverBindingStop()");
315
316 // For each MMC instance
317 CurrentLink = mMmcHostPool.ForwardLink;
318 while (CurrentLink != NULL && CurrentLink != &mMmcHostPool && (Status == EFI_SUCCESS)) {
319 MmcHostInstance = MMC_HOST_INSTANCE_FROM_LINK(CurrentLink);
320 ASSERT(MmcHostInstance != NULL);
321
322 // Close gEfiMmcHostProtocolGuid
323 Status = gBS->CloseProtocol (
324 Controller,
325 &gEfiMmcHostProtocolGuid,(VOID **) &MmcHostInstance->MmcHost,
326 This->DriverBindingHandle
327 );
328
329 // Remove MMC Host Instance from the pool
330 RemoveMmcHost (MmcHostInstance);
331
332 // Destroy MmcHostInstance
333 DestroyMmcHostInstance (MmcHostInstance);
334 }
335
336 return Status;
337 }
338
339 EFI_DRIVER_BINDING_PROTOCOL gMmcDriverBinding = {
340 MmcDriverBindingSupported,
341 MmcDriverBindingStart,
342 MmcDriverBindingStop,
343 0xa,
344 NULL,
345 NULL
346 };
347
348 /**
349
350 **/
351 EFI_STATUS
352 EFIAPI
353 MmcDxeInitialize (
354 IN EFI_HANDLE ImageHandle,
355 IN EFI_SYSTEM_TABLE *SystemTable
356 )
357 {
358 EFI_STATUS Status;
359
360 //
361 // Initializes MMC Host pool
362 //
363 InitializeMmcHostPool ();
364
365 //
366 // Install driver model protocol(s).
367 //
368 Status = EfiLibInstallDriverBindingComponentName2 (
369 ImageHandle,
370 SystemTable,
371 &gMmcDriverBinding,
372 ImageHandle,
373 &gMmcComponentName,
374 &gMmcComponentName2
375 );
376 ASSERT_EFI_ERROR (Status);
377
378 // Install driver diagnostics
379 Status = gBS->InstallMultipleProtocolInterfaces (
380 &ImageHandle,
381 &gEfiDriverDiagnostics2ProtocolGuid,&gMmcDriverDiagnostics2,
382 NULL
383 );
384 ASSERT_EFI_ERROR (Status);
385
386 return Status;
387 }