1 /** @file
2   Implementation of the EFI Block IO Protocol for ISA Floppy driver
3 
4 Copyright (c) 2006 - 2009, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "IsaFloppy.h"
16 
17 /**
18   Reset the Block Device.
19 
20   @param  This                 Indicates a pointer to the calling context.
21   @param  ExtendedVerification Driver may perform diagnostics on reset.
22 
23   @retval EFI_SUCCESS          The device was reset.
24   @retval EFI_DEVICE_ERROR     The device is not functioning properly and could
25                                not be reset.
26 **/
27 EFI_STATUS
28 EFIAPI
FdcReset(IN EFI_BLOCK_IO_PROTOCOL * This,IN BOOLEAN ExtendedVerification)29 FdcReset (
30   IN  EFI_BLOCK_IO_PROTOCOL  *This,
31   IN  BOOLEAN                ExtendedVerification
32   )
33 {
34   FDC_BLK_IO_DEV  *FdcDev;
35 
36   //
37   // Reset the Floppy Disk Controller
38   //
39   FdcDev = FDD_BLK_IO_FROM_THIS (This);
40 
41   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
42     EFI_PROGRESS_CODE,
43     EFI_P_PC_RESET | EFI_PERIPHERAL_REMOVABLE_MEDIA,
44     FdcDev->DevicePath
45     );
46 
47   return FddReset (FdcDev);
48 }
49 
50 /**
51   Flush the Block Device.
52 
53   @param  This              Indicates a pointer to the calling context.
54 
55   @retval EFI_SUCCESS       All outstanding data was written to the device
56   @retval EFI_DEVICE_ERROR  The device reported an error while writting back the data
57   @retval EFI_NO_MEDIA      There is no media in the device.
58 
59 **/
60 EFI_STATUS
61 EFIAPI
FddFlushBlocks(IN EFI_BLOCK_IO_PROTOCOL * This)62 FddFlushBlocks (
63   IN  EFI_BLOCK_IO_PROTOCOL  *This
64   )
65 {
66   //
67   // Not supported yet
68   //
69   return EFI_SUCCESS;
70 }
71 
72 /**
73   Common report status code interface.
74 
75   @param This  Pointer of FDC_BLK_IO_DEV instance
76   @param Read  Read or write operation when error occurrs
77 **/
78 VOID
FddReportStatus(IN EFI_BLOCK_IO_PROTOCOL * This,IN BOOLEAN Read)79 FddReportStatus (
80   IN  EFI_BLOCK_IO_PROTOCOL  *This,
81   IN  BOOLEAN                Read
82   )
83 {
84   FDC_BLK_IO_DEV  *FdcDev;
85 
86   FdcDev = FDD_BLK_IO_FROM_THIS (This);
87 
88   REPORT_STATUS_CODE_WITH_DEVICE_PATH (
89     EFI_ERROR_CODE,
90     ((Read) ? EFI_P_EC_INPUT_ERROR : EFI_P_EC_OUTPUT_ERROR) | EFI_PERIPHERAL_REMOVABLE_MEDIA,
91     FdcDev->DevicePath
92     );
93 }
94 
95 /**
96   Read BufferSize bytes from Lba into Buffer.
97 
98   @param  This       Indicates a pointer to the calling context.
99   @param  MediaId    Id of the media, changes every time the media is replaced.
100   @param  Lba        The starting Logical Block Address to read from
101   @param  BufferSize Size of Buffer, must be a multiple of device block size.
102   @param  Buffer     A pointer to the destination buffer for the data. The caller is
103                      responsible for either having implicit or explicit ownership of the buffer.
104 
105   @retval EFI_SUCCESS           The data was read correctly from the device.
106   @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
107   @retval EFI_NO_MEDIA          There is no media in the device.
108   @retval EFI_MEDIA_CHANGED     The MediaId does not matched the current device.
109   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
110   @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
111                                 or the buffer is not on proper alignment.
112 
113 **/
114 EFI_STATUS
115 EFIAPI
FddReadBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,OUT VOID * Buffer)116 FddReadBlocks (
117   IN  EFI_BLOCK_IO_PROTOCOL  *This,
118   IN  UINT32                 MediaId,
119   IN  EFI_LBA                Lba,
120   IN  UINTN                  BufferSize,
121   OUT VOID                   *Buffer
122   )
123 {
124   EFI_STATUS  Status;
125 
126   Status = FddReadWriteBlocks (This, MediaId, Lba, BufferSize, READ, Buffer);
127 
128   if (EFI_ERROR (Status)) {
129     FddReportStatus (This, TRUE);
130   }
131 
132   return Status;
133 }
134 
135 /**
136   Write BufferSize bytes from Lba into Buffer.
137 
138   @param  This       Indicates a pointer to the calling context.
139   @param  MediaId    The media ID that the write request is for.
140   @param  Lba        The starting logical block address to be written. The caller is
141                      responsible for writing to only legitimate locations.
142   @param  BufferSize Size of Buffer, must be a multiple of device block size.
143   @param  Buffer     A pointer to the source buffer for the data.
144 
145   @retval EFI_SUCCESS           The data was written correctly to the device.
146   @retval EFI_WRITE_PROTECTED   The device can not be written to.
147   @retval EFI_DEVICE_ERROR      The device reported an error while performing the write.
148   @retval EFI_NO_MEDIA          There is no media in the device.
149   @retval EFI_MEDIA_CHNAGED     The MediaId does not matched the current device.
150   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
151   @retval EFI_INVALID_PARAMETER The write request contains LBAs that are not valid,
152                                 or the buffer is not on proper alignment.
153 
154 **/
155 EFI_STATUS
156 EFIAPI
FddWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN VOID * Buffer)157 FddWriteBlocks (
158   IN EFI_BLOCK_IO_PROTOCOL  *This,
159   IN UINT32                 MediaId,
160   IN EFI_LBA                Lba,
161   IN UINTN                  BufferSize,
162   IN VOID                   *Buffer
163   )
164 {
165   EFI_STATUS  Status;
166 
167   Status = FddReadWriteBlocks (This, MediaId, Lba, BufferSize, WRITE, Buffer);
168 
169   if (EFI_ERROR (Status)) {
170     FddReportStatus (This, FALSE);
171   }
172 
173   return Status;
174 }
175 
176 /**
177   Read or Write a number of blocks to floppy disk
178 
179   @param  This       Indicates a pointer to the calling context.
180   @param  MediaId    Id of the media, changes every time the media is replaced.
181   @param  Lba        The starting Logical Block Address to read from
182   @param  BufferSize Size of Buffer, must be a multiple of device block size.
183   @param  Operation  Specifies the read or write operation.
184   @param  Buffer     A pointer to the destination buffer for the data. The caller is
185                      responsible for either having implicit or explicit ownership of the buffer.
186 
187   @retval EFI_SUCCESS           The data was read correctly from the device.
188   @retval EFI_DEVICE_ERROR      The device reported an error while performing the read.
189   @retval EFI_NO_MEDIA          There is no media in the device.
190   @retval EFI_MEDIA_CHANGED     The MediaId does not matched the current device.
191   @retval EFI_BAD_BUFFER_SIZE   The Buffer was not a multiple of the block size of the device.
192   @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not valid,
193                                 or the buffer is not on proper alignment.
194   @retval EFI_WRITE_PROTECTED   The device can not be written to.
195 
196 **/
197 EFI_STATUS
FddReadWriteBlocks(IN EFI_BLOCK_IO_PROTOCOL * This,IN UINT32 MediaId,IN EFI_LBA Lba,IN UINTN BufferSize,IN BOOLEAN Operation,OUT VOID * Buffer)198 FddReadWriteBlocks (
199   IN  EFI_BLOCK_IO_PROTOCOL  *This,
200   IN  UINT32                 MediaId,
201   IN  EFI_LBA                Lba,
202   IN  UINTN                  BufferSize,
203   IN  BOOLEAN                Operation,
204   OUT VOID                   *Buffer
205   )
206 {
207   EFI_BLOCK_IO_MEDIA  *Media;
208   FDC_BLK_IO_DEV      *FdcDev;
209   UINTN               BlockSize;
210   UINTN               NumberOfBlocks;
211   UINTN               BlockCount;
212   EFI_STATUS          Status;
213   EFI_LBA             Lba0;
214   UINT8               *Pointer;
215 
216   //
217   // Get the intrinsic block size
218   //
219   Media     = This->Media;
220   BlockSize = Media->BlockSize;
221   FdcDev    = FDD_BLK_IO_FROM_THIS (This);
222 
223   if (Operation == WRITE) {
224     if (Lba == 0) {
225       FdcFreeCache (FdcDev);
226     }
227   }
228 
229   //
230   // Set the drive motor on
231   //
232   Status = MotorOn (FdcDev);
233   if (EFI_ERROR (Status)) {
234     return EFI_DEVICE_ERROR;
235   }
236   //
237   // Check to see if media can be detected
238   //
239   Status = DetectMedia (FdcDev);
240   if (EFI_ERROR (Status)) {
241     MotorOff (FdcDev);
242     FdcFreeCache (FdcDev);
243     return EFI_DEVICE_ERROR;
244   }
245   //
246   // Check to see if media is present
247   //
248   if (!(Media->MediaPresent)) {
249     MotorOff (FdcDev);
250     FdcFreeCache (FdcDev);
251     return EFI_NO_MEDIA;
252   }
253   //
254   // Check to see if media has been changed
255   //
256   if (MediaId != Media->MediaId) {
257     MotorOff (FdcDev);
258     FdcFreeCache (FdcDev);
259     return EFI_MEDIA_CHANGED;
260   }
261 
262   if (BufferSize == 0) {
263     MotorOff (FdcDev);
264     return EFI_SUCCESS;
265   }
266 
267   if (Operation == WRITE) {
268     if (Media->ReadOnly) {
269       MotorOff (FdcDev);
270       return EFI_WRITE_PROTECTED;
271     }
272   }
273   //
274   // Check the parameters for this read/write operation
275   //
276   if (Buffer == NULL) {
277     MotorOff (FdcDev);
278     return EFI_INVALID_PARAMETER;
279   }
280 
281   if (BufferSize % BlockSize != 0) {
282     MotorOff (FdcDev);
283     return EFI_BAD_BUFFER_SIZE;
284   }
285 
286   if (Lba > Media->LastBlock) {
287     MotorOff (FdcDev);
288     return EFI_INVALID_PARAMETER;
289   }
290 
291   if (((BufferSize / BlockSize) + Lba - 1) > Media->LastBlock) {
292     MotorOff (FdcDev);
293     return EFI_INVALID_PARAMETER;
294   }
295 
296   if (Operation == READ) {
297     //
298     // See if the data that is being read is already in the cache
299     //
300     if (FdcDev->Cache != NULL) {
301       if (Lba == 0 && BufferSize == BlockSize) {
302         MotorOff (FdcDev);
303         CopyMem ((UINT8 *) Buffer, (UINT8 *) FdcDev->Cache, BlockSize);
304         return EFI_SUCCESS;
305       }
306     }
307   }
308   //
309   // Set up Floppy Disk Controller
310   //
311   Status = Setup (FdcDev);
312   if (EFI_ERROR (Status)) {
313     MotorOff (FdcDev);
314     return EFI_DEVICE_ERROR;
315   }
316 
317   NumberOfBlocks  = BufferSize / BlockSize;
318   Lba0            = Lba;
319   Pointer         = Buffer;
320 
321   //
322   // read blocks in the same cylinder.
323   // in a cylinder , there are 18 * 2 = 36 blocks
324   //
325   BlockCount = GetTransferBlockCount (FdcDev, Lba, NumberOfBlocks);
326   while ((BlockCount != 0) && !EFI_ERROR (Status)) {
327     Status = ReadWriteDataSector (FdcDev, Buffer, Lba, BlockCount, Operation);
328     if (EFI_ERROR (Status)) {
329       MotorOff (FdcDev);
330       FddReset (FdcDev);
331       return EFI_DEVICE_ERROR;
332     }
333 
334     Lba += BlockCount;
335     NumberOfBlocks -= BlockCount;
336     Buffer      = (VOID *) ((UINTN) Buffer + BlockCount * BlockSize);
337     BlockCount  = GetTransferBlockCount (FdcDev, Lba, NumberOfBlocks);
338   }
339 
340   Buffer = Pointer;
341 
342   //
343   // Turn the motor off
344   //
345   MotorOff (FdcDev);
346 
347   if (Operation == READ) {
348     //
349     // Cache the data read
350     //
351     if (Lba0 == 0 && FdcDev->Cache == NULL) {
352       FdcDev->Cache = AllocateCopyPool (BlockSize, Buffer);
353     }
354   }
355 
356   return EFI_SUCCESS;
357 
358 }
359 
360 /**
361   Free cache for a floppy disk.
362 
363   @param FdcDev  A Pointer to FDC_BLK_IO_DEV instance
364 
365 **/
366 VOID
FdcFreeCache(IN FDC_BLK_IO_DEV * FdcDev)367 FdcFreeCache (
368   IN FDC_BLK_IO_DEV  *FdcDev
369   )
370 {
371   if (FdcDev->Cache != NULL) {
372     FreePool (FdcDev->Cache);
373     FdcDev->Cache = NULL;
374   }
375 }
376