Performing a Read Operation

25 Mar

Performing a Read Operation
The fread function reads data from a file into a buffer. The function accepts a FILEOBJ pointer to a FILE structure (fo), a pointer to a buffer to store the data to be read (dest), and the number of bytes to read (count). The function returns a status code. In the FILE structure, pos is the offset to begin reading from within the sector, seek is the offset to begin reading from within the file, ccls is the number of the cluster to read from, and sec is the number of the sector to read within the cluster. The function returns a status code.

The RAMread macro reads a byte at an address (a) plus an offset (f ) in RAM:

#define RAMread(a, f) *(a + f)

The fread function calls the SectorRead function from Chapter 5 and the Cluster2Sector and FILEget_next_cluster functions from Chapter 8.

CETYPE fread (FILEOBJ fo, void *dest, word count)
{
DISK *dsk;
CETYPE error = CE_GOOD;
dword l;
word pos;
dword seek;
dword size;
dword temp;
dsk = (DISK *)fo -> dsk;
temp = count;

// Save the offset to begin reading from within the current sector,
// the offset to read from within the file, and the file’s size.
pos = fo -> pos;
seek = fo -> seek;
size = fo -> size;
// Get the sector number of the file’s current cluster.
l = Cluster2Sector(dsk, fo -> ccls);
// Add the number of the current sector within the cluster.
l += (word)fo -> sec;
// Read the sector’s data.
if ( SectorRead( l, dsk->buffer) != sdcValid)
error = CE_BAD_SECTOR_READ;
// Read from the file until finished or an error.
while (error == CE_GOOD && temp > 0)
{
if (seek == size)
// It’s the end of the file.
error = CE_EOF;
else
{
// If we’ve reached the end of a sector, load another sector.
if (pos == SDC_SECTOR_SIZE)
{
// Reset the offset within the sector.
pos = 0;

// Increment the sector number.
fo -> sec++;
// The sector number (sec) should be a value between 0 and SecPerClus – 1.
// If sec = SecPerClus, the sector is the first sector in a new cluster.
if (fo -> sec == dsk -> SecPerClus)
{
// Get the next cluster in the file and start in the cluster’s first sector.
fo -> sec = 0;
error = FILEget_next_cluster( fo, 1);
}
if (error == CE_GOOD)
{
// Get the sector number of the current cluster, which may have changed.
l = Cluster2Sector(dsk,fo -> ccls);
// Add the number of the current sector within the cluster.
l += (word)fo -> sec;
// Read the sector’s data.
if (SectorRead( l, dsk -> buffer) != sdcValid)
error = CE_BAD_SECTOR_READ;
}
} // End: load new sector
if (error == CE_GOOD)

{
// A sector’s data is in the DISK structure’s buffer member.
// Copy a byte from the specified offset (pos) in the DISK structure’s buffer
// to the dest buffer.
*(char *)dest = RAMread(dsk -> buffer, pos++);

dest = dest + 1; // Increment the dest buffer offset.
seek++; // Increment the number of the byte to copy.
(temp)–; // Decrement the number of bytes remaining to copy.
}
} // End: if not end of file
} // while no error and more bytes to copy
// Save the offset within the sector.
fo->pos = pos;
// Save the offset within the file.
fo->seek = seek;
return(error);
}

Writing to a File
The FILEopen function in this chapter can prepare to write to a file. Because storage media typically requires writing complete sectors, each write to the media writes a sector’s worth of data. To write to a portion of a sector, firmware reads the sector’s contents into a buffer, changes the data in the desired location(s), and writes the entire buffer back to the storage media. As Chapter 1 explained, to write to the media, a MultiMediaCard’s controller may need to erase an entire erase block that contains multiple sectors and write the data back to the erased sectors, including the new data.

Tasks
To write to an empty file, firmware must perform the following actions (shown in Figure 10-2):

1. Allocate a cluster in the FAT, store the cluster number in the file’s directory entry, and convert the cluster number to an LBA sector number.
2. Write data to the cluster’s sector(s).
3. To write more data, search the FAT for an available cluster, store the cluster number in the FAT entry for the file’s current cluster, store an EOC

Figure 10-2: A mass-storage master performs these actions to write to an empty file.

marker in the new cluster’s FAT entry, convert the cluster number to an
LBA sector number, and store the additional data in the cluster’s sector(s).
4. Repeat step 3 as needed until all of the file’s data has been written.
5. Update the file’s directory entry.

To append data to an existing file, before writing to the file, firmware can get the file’s size from the directory entry and use the value to calculate the sector and offset to begin writing to the file. To overwrite a file that uses multiple clusters, firmware can use the FAT to find the clusters already allocated to the file.

Performing a Write Operation
The fwrite function writes a specified number of bytes beginning at a specified location in a file. The function accepts a FILEOBJ pointer to a FILE structure (fo), a pointer to a buffer containing the data to write (src), and the number of bytes to write (count). In the FILE structure, pos is the offset to begin writing to within the sector, seek is the offset to begin writing to within the file, ccls is the number of the cluster to write to, and sec is the number of the sector to write to within the cluster. The function returns a status code.

The function calls the IsWriteProtected, SectorRead, and SectorWrite functions from Chapter 5, the FILEget_next_cluster and Cluster2Sector functions and RAMWrite macro from Chapter 8, and the FILEallocate_new_cluster function from this chapter. To enable writing to the file, Flags.write must be true. The File_Open function in this chapter sets this value.

CETYPE fwrite( FILEOBJ fo, void * src, word count)
{
DISK * dsk;
CETYPE error = CE_GOOD;
dword l;
word pos;
byte sectorloaded = FALSE;
dword seek;
dword size;
word tempo;
// To enable writing, Flags.write must be true and IsWriteProtected must return false.
if (fo->Flags.write)
{
if (!IsWriteProtected())
{
// It’s OK to write to the media.
tempo = count;
// Save the file structure’s dsk structure, the offset within the current sector,
// and the absolute offset in the file.
dsk = fo -> dsk;
pos = fo -> pos;
seek = fo -> seek;
// Get the sector number of the file’s current cluster.
l = Cluster2Sector(dsk,fo -> ccls);
// Add the number of the current sector within the cluster.
l += (word)fo -> sec;
// Read the sector.
if (SectorRead(l, dsk->buffer) != sdcValid)
error = CE_BAD_SECTOR_READ;
sectorloaded = TRUE;
// Save the file’s size.
size = fo -> size;
// Write to the file until finished or an error.
while (error == CE_GOOD && tempo > 0)
{
if (seek == size)
{
// It’s the end of the file. Set the flag.
fo -> Flags.FileWriteEOF = TRUE;
}

// If we’ve reached the end of a sector, write the data to the media and
// load another sector.
if (pos == SDC_SECTOR_SIZE)
{
if (sectorloaded)
{
// The DISK structure’s buffer member contains data to be written.
// Copy the data to the storage media.
if (SectorWrite( l, dsk -> buffer) != sdcValid)
error = CE_WRITE_ERROR;
}
// Reset the offset within the sector.
pos = 0;
// Increment the sector number.
fo -> sec++;
// The sector number (sec) must be a value between 0 and SecPerClus – 1.
// If sec = SecPerClus, the sector is the first sector in a new cluster.
if (fo -> sec == dsk -> SecPerClus)
{
// Reset the sector number for the new cluster.
fo -> sec = 0;
if (fo -> Flags.FileWriteEOF)

// It’s the end of the file. Allocate a new cluster for additional data.
error = FILEallocate_new_cluster(fo);
else
// Not the end of the file. Get the next cluster allocated to the file.
error = FILEget_next_cluster( fo, 1);
}

if (error == CE_DISK_FULL)
{
return error;
}
if (error == CE_GOOD)
{
// Read the next sector from the media.
// Get the sector number of the file’s current cluster.
l = Cluster2Sector(dsk, fo -> ccls);
// Add the number of the current sector within the cluster.
l += (word)fo -> sec;
// Read the new sector’s data.
if (SectorRead( l, dsk -> buffer) != sdcValid)
error = CE_BAD_SECTOR_READ;
sectorloaded = TRUE;
}
} // End: write a sector to the media and read the next sector.
if (error == CE_GOOD)
{
// A sector’s data is in the DISK structure’s buffer member.
// Copy a byte from the passed buffer (src) to the
// specified offset (pos) in the DISK structure’s buffer.
RAMwrite (dsk -> buffer, pos++, *(char *)src);
// Increment the offset of the byte to write.
src = src + 1;
// Increment the offset of the byte within the file.
seek++;

// Decrement the number of bytes remaining to write.
tempo–;
}
if (fo -> Flags.FileWriteEOF)
// The data was appended to the file, so increment the file size.
size++;
} // End: write to the file (except for the last sector).
// If no error, write the final sector’s data to the media.
if (error == CE_GOOD)
{
// Get the sector number of the current cluster.
l = Cluster2Sector(dsk, fo -> ccls);
// Add the number of the current sector within the cluster.
l += (word)fo -> sec;
// Copy data from the DISK structure’s buffer item to the storage media.
if (SectorWrite(l, dsk->buffer) != sdcValid)
error = CE_WRITE_ERROR;
}
// Save the position within the current sector, the byte number within the file,
// and the file size.
fo->pos = pos;
fo->seek = seek;
fo->size = size;
}
else

error = CE_WRITE_PROTECTED;
}

else
error = CE_WRITE_ERROR;
return(error);
}

Closing a File
When finished writing to a file, firmware must update the file’s directory entry. The fclose function handles this task. The function accepts a FILEOBJ pointer to a FILE structure for the file and returns a status code.

The function calls the LoadDirAttrib, IncrementTimeStamp, and Write_File_Entry functions from Chapter 9.

CETYPE fclose(FILEOBJ fo)
{
CETYPE error = CE_GOOD;
DIRENTRY dir;
word fHandle;
// Set fHandle to the number of the file’s entry in its directory.
fHandle = fo -> entry;
// Nothing to do if the file wasn’t opened for writing.
if (fo -> Flags.write)
{
// Get the file’s attributes.
dir = LoadDirAttrib(fo, &fHandle);
// Update the time and date.
IncrementTimeStamp(dir);
// Set the DIRENTRY structure’s DIR_FileSize member to the file’s size.
dir -> DIR_FileSize = fo -> size;

// Write the file’s entry in its directory.
if (Write_File_Entry(fo, &fHandle))
error = CE_GOOD;
else
error = CE_WRITE_ERROR;
// The file is no longer open for writing.
fo -> Flags.write = FALSE;
}
return(error);
}

Random Posts

No comments yet

Leave a Reply

You must be logged in to post a comment.