stream-like access added
This commit is contained in:
parent
de52ac5d35
commit
8b8dcbe170
179
fs/fat32/fat32.c
179
fs/fat32/fat32.c
@ -236,11 +236,12 @@ static inline uint32_t fat32_get_first_cluster(const Fat32_FileEntry *entry) {
|
|||||||
* The function locates a file based on its full path.
|
* The function locates a file based on its full path.
|
||||||
* @param ctrl pointer to a FAT32 control block
|
* @param ctrl pointer to a FAT32 control block
|
||||||
* @param path full path of the file to be located
|
* @param path full path of the file to be located
|
||||||
|
* @param file_entry_ba if not NULL, then the function returns here the file entry's byte address (on the mass storage); untouched if file not found
|
||||||
* @return pointer to the file entry. Caution! This pointer is only valid until no further
|
* @return pointer to the file entry. Caution! This pointer is only valid until no further
|
||||||
* call is made to fat32_* functions, since all functions involve the same buffer.
|
* call is made to fat32_* functions, since all functions involve the same buffer.
|
||||||
* Functions invalidating buffer content are marked with (I).
|
* Functions invalidating buffer content are marked with (I).
|
||||||
*/
|
*/
|
||||||
const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path) {
|
const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path, uint32_t *file_entry_ba) {
|
||||||
FETCH_MSTG;
|
FETCH_MSTG;
|
||||||
|
|
||||||
// start search in the root directory
|
// start search in the root directory
|
||||||
@ -256,7 +257,7 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
|
|||||||
char entry_name[FAT32_MAX_BASENAME_LENGTH + 1]; // entry name
|
char entry_name[FAT32_MAX_BASENAME_LENGTH + 1]; // entry name
|
||||||
Fat32_FileEntry *entry = NULL; // entry iterator
|
Fat32_FileEntry *entry = NULL; // entry iterator
|
||||||
|
|
||||||
while ((!file_found) && (!no_such_file)) {
|
while ((!file_found) && (!no_such_file)) { // traversing tree levels
|
||||||
// extract first basename
|
// extract first basename
|
||||||
uint16_t basename_len = fat32_extract_next_basename_length(path_iter); // get basename length
|
uint16_t basename_len = fat32_extract_next_basename_length(path_iter); // get basename length
|
||||||
uint16_t copy_len = MIN(basename_len, FAT32_MAX_BASENAME_LENGTH);
|
uint16_t copy_len = MIN(basename_len, FAT32_MAX_BASENAME_LENGTH);
|
||||||
@ -271,7 +272,7 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
|
|||||||
// acquire pointer to the beginning of the file entry table
|
// acquire pointer to the beginning of the file entry table
|
||||||
entry = (Fat32_FileEntry *) buffer;
|
entry = (Fat32_FileEntry *) buffer;
|
||||||
bool basename_entry_found = false;
|
bool basename_entry_found = false;
|
||||||
while ((entry->short_fname[0] != 0x00) && (!basename_entry_found)) {
|
while ((entry->short_fname[0] != 0x00) && (!basename_entry_found)) { // traversing siblings
|
||||||
if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) {
|
if (entry->short_fname[0] != FAT32_UNUSED_FILE_ENTRY) {
|
||||||
fat32_get_file_entry(&entry, entry_name);
|
fat32_get_file_entry(&entry, entry_name);
|
||||||
|
|
||||||
@ -304,6 +305,9 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
|
|||||||
if (basename_entry_found) {
|
if (basename_entry_found) {
|
||||||
if (path_iter[0] == '\0') { // ...and it's the end of the path
|
if (path_iter[0] == '\0') { // ...and it's the end of the path
|
||||||
file_found = true; // ...then the file is found
|
file_found = true; // ...then the file is found
|
||||||
|
if (file_entry_ba != NULL) { // if file entry writeback address if provided
|
||||||
|
*file_entry_ba = sector * ctrl->bytes_per_sector + (((uint8_t *) entry) - buffer);
|
||||||
|
}
|
||||||
} else { // ...if it's not the end of the path, then load the cluster corresponding to the entry
|
} else { // ...if it's not the end of the path, then load the cluster corresponding to the entry
|
||||||
// determine sector number
|
// determine sector number
|
||||||
uint32_t next_cluster = fat32_get_first_cluster(entry);
|
uint32_t next_cluster = fat32_get_first_cluster(entry);
|
||||||
@ -312,6 +316,8 @@ const Fat32_FileEntry *fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char
|
|||||||
// load sectors
|
// load sectors
|
||||||
READ_MSTG_TO_FIRST_BUFFER(next_sector);
|
READ_MSTG_TO_FIRST_BUFFER(next_sector);
|
||||||
READ_MSTG_TO_SECOND_BUFFER(next_sector + 1);
|
READ_MSTG_TO_SECOND_BUFFER(next_sector + 1);
|
||||||
|
|
||||||
|
sector = next_sector; // advance sector
|
||||||
}
|
}
|
||||||
} else { // if not found...
|
} else { // if not found...
|
||||||
no_such_file = true; // ...then the file could not be located
|
no_such_file = true; // ...then the file could not be located
|
||||||
@ -362,6 +368,30 @@ int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char *dir) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sequence number of the sector holding the byte pointed by address.
|
||||||
|
* @param ctrl pointer to FAT32 control block
|
||||||
|
* @param address byte address
|
||||||
|
* @return sequence number of the requested sector
|
||||||
|
*/
|
||||||
|
static inline uint32_t fat32_get_sector_by_byte_address(const Fat32_CtrlBlock *ctrl, uint32_t ba) {
|
||||||
|
return ba / ctrl->bytes_per_sector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get file entry based on its byte address. (I)
|
||||||
|
* @param ctrl pointer to FAT32 control block
|
||||||
|
* @param ba byte address of the file entry
|
||||||
|
* @return pointer to file entry
|
||||||
|
*/
|
||||||
|
static inline const Fat32_FileEntry * fat32_get_file_entry_by_byte_address(const Fat32_CtrlBlock * ctrl, uint32_t ba) {
|
||||||
|
uint32_t sector = fat32_get_sector_by_byte_address(ctrl, ba);
|
||||||
|
uint32_t offset = ba % ctrl->bytes_per_sector;
|
||||||
|
FETCH_MSTG;
|
||||||
|
READ_MSTG_TO_FIRST_BUFFER(sector);
|
||||||
|
return (Fat32_FileEntry *)(buffer + offset);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the sector's sequence number corresponding to the specific cluster identified by its sequence number.
|
* Get the sector's sequence number corresponding to the specific cluster identified by its sequence number.
|
||||||
* @param ctrl pointer to FAT32 control block
|
* @param ctrl pointer to FAT32 control block
|
||||||
@ -373,7 +403,7 @@ static inline uint32_t fat32_get_fat_entry_sector_by_cluster_index(const Fat32_C
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the sequence number of the next chained cluster. (I)
|
* Get the sequence number of the next chained cluster. (I)
|
||||||
* @param ctrl pointer to FAT32 control block
|
* @param ctrl pointer to FAT32 control block
|
||||||
* @param cluster sequence number of a cluster
|
* @param cluster sequence number of a cluster
|
||||||
* @return sequence number of the cluster following the passed one in the chain
|
* @return sequence number of the cluster following the passed one in the chain
|
||||||
@ -388,7 +418,7 @@ static inline uint32_t fat32_get_next_chained_cluster(const Fat32_CtrlBlock *ctr
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get sequence number of the cluster storing a specific byte position (pos) if the file's first cluster is given.
|
* Get sequence number of the cluster storing a specific byte position (pos) if the file's first cluster is given. (I)
|
||||||
* @param ctrl pointer to FAT32 control block
|
* @param ctrl pointer to FAT32 control block
|
||||||
* @param first_cluster sequence number of the first cluster of the file
|
* @param first_cluster sequence number of the first cluster of the file
|
||||||
* @param pos byte position
|
* @param pos byte position
|
||||||
@ -426,7 +456,8 @@ static inline uint32_t fat32_get_bytes_per_cluster(const Fat32_CtrlBlock *ctrl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a file using "random access".
|
* Read a file using "random access". (I)
|
||||||
|
* This function holds a load of redundant code, but this is with intent.
|
||||||
* @param ctrl pointer to FAT32 control block
|
* @param ctrl pointer to FAT32 control block
|
||||||
* @param entry pointer to the corresponding file entry
|
* @param entry pointer to the corresponding file entry
|
||||||
* @param pos read start position
|
* @param pos read start position
|
||||||
@ -434,7 +465,7 @@ static inline uint32_t fat32_get_bytes_per_cluster(const Fat32_CtrlBlock *ctrl)
|
|||||||
* @param p pointer to output buffer
|
* @param p pointer to output buffer
|
||||||
* @return the number of bytes read
|
* @return the number of bytes read
|
||||||
*/
|
*/
|
||||||
uint32_t fat32_read_file(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *entry, uint32_t pos, uint32_t len, uint8_t *p) {
|
uint32_t fat32_read_file_random_access(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *entry, uint32_t pos, uint32_t len, uint8_t *p) {
|
||||||
// check position validity and check that the entry corresponds to a file, not to a directory or volume
|
// check position validity and check that the entry corresponds to a file, not to a directory or volume
|
||||||
uint8_t attr = entry->attributes;
|
uint8_t attr = entry->attributes;
|
||||||
if ((pos >= entry->size) || (attr & FAT32_FATT_DIRECTORY) || (attr & FAT32_FATT_VOLUME)) {
|
if ((pos >= entry->size) || (attr & FAT32_FATT_DIRECTORY) || (attr & FAT32_FATT_VOLUME)) {
|
||||||
@ -490,3 +521,137 @@ uint32_t fat32_read_file(const Fat32_CtrlBlock *ctrl, const Fat32_FileEntry *ent
|
|||||||
|
|
||||||
return read_len;
|
return read_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------
|
||||||
|
|
||||||
|
int fat32_open_file(const Fat32_CtrlBlock *ctrl, const char *path, Fat32_FileHelper *file_helper) {
|
||||||
|
uint32_t file_entry_ba;
|
||||||
|
const Fat32_FileEntry *entry = fat32_locate_file(ctrl, path, &file_entry_ba);
|
||||||
|
uint32_t first_cluster = fat32_get_first_cluster(entry);
|
||||||
|
|
||||||
|
// store data usable for identifying file on the storage
|
||||||
|
file_helper->ctrl = ctrl;
|
||||||
|
file_helper->entry_ba = file_entry_ba;
|
||||||
|
|
||||||
|
// store file information
|
||||||
|
file_helper->size = entry->size;
|
||||||
|
|
||||||
|
// seek to zero
|
||||||
|
file_helper->last_pos.pos = 0;
|
||||||
|
file_helper->last_pos.sector_begin_pos = 0;
|
||||||
|
file_helper->last_pos.cluster = first_cluster;
|
||||||
|
file_helper->last_pos.sector = fat32_get_first_sector_of_cluster(ctrl, first_cluster);
|
||||||
|
file_helper->last_pos.sector_of_cluster = 0;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seek file stream, set reader to pos.
|
||||||
|
* @param ctrl pointer to FAT32 control block
|
||||||
|
* @param pos stream position
|
||||||
|
* @param file_helper pointer to file helper, which gets updated
|
||||||
|
* @return 0 on success
|
||||||
|
*/
|
||||||
|
uint32_t fat32_seek_stream(const Fat32_CtrlBlock *ctrl, uint32_t pos, Fat32_FileHelper *file_helper) {
|
||||||
|
// check that we are not attempting to seek out of the file
|
||||||
|
if (pos >= file_helper->size) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual seeking
|
||||||
|
|
||||||
|
Fat32_SeekHint *seek_hint = &file_helper->last_pos; // fetch seek hint
|
||||||
|
if (pos > file_helper->last_pos.pos) { // if seeking forward, then seeking can be accelerated using the seek hints
|
||||||
|
uint32_t delta = pos - file_helper->last_pos.pos;
|
||||||
|
uint32_t bytes_to_sector_end = ctrl->bytes_per_sector - (file_helper->last_pos.pos % ctrl->bytes_per_sector); // determine byte count to sector end
|
||||||
|
if (delta < bytes_to_sector_end) { // if seeking does not cross sector boundary
|
||||||
|
seek_hint->pos = pos; // just change position
|
||||||
|
} else { // ...if crosses sector boundary
|
||||||
|
uint32_t sector_boundaries_crossed = 1 + (delta - bytes_to_sector_end) / ctrl->bytes_per_sector; // 1: boundary crossed; further boundaries crossed based on delta
|
||||||
|
uint32_t delta_sector_from_clusters_first_sector = seek_hint->sector_of_cluster + sector_boundaries_crossed;
|
||||||
|
uint32_t cluster_boundaries_crossed = delta_sector_from_clusters_first_sector / ctrl->sectors_per_cluster; // number of cluster boundary crossing
|
||||||
|
if (cluster_boundaries_crossed > 0) { // if we crossed a cluster boundary, then load the next cluster's first sector in chain
|
||||||
|
uint32_t cluster = seek_hint->cluster;
|
||||||
|
for (uint32_t i = 0; i < cluster_boundaries_crossed; i++) { // do the cluster crossings
|
||||||
|
cluster = fat32_get_next_chained_cluster(ctrl, cluster); // get next cluster
|
||||||
|
}
|
||||||
|
|
||||||
|
// update position fields
|
||||||
|
seek_hint->cluster = cluster;
|
||||||
|
seek_hint->sector_of_cluster = delta_sector_from_clusters_first_sector % ctrl->sectors_per_cluster;
|
||||||
|
seek_hint->sector += sector_boundaries_crossed;
|
||||||
|
seek_hint->pos = pos;
|
||||||
|
seek_hint->sector_begin_pos += sector_boundaries_crossed * ctrl->bytes_per_sector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { // if seeking backwards, then no acceleration is available
|
||||||
|
const Fat32_FileEntry * entry = fat32_get_file_entry_by_byte_address(ctrl, file_helper->entry_ba); // fetch file entry
|
||||||
|
uint32_t first_cluster = fat32_get_first_cluster(entry); // get first cluster of the entry
|
||||||
|
uint32_t cluster = fat32_get_cluster_from_pos(ctrl, first_cluster, pos); // get cluster sequence number of the file using the read position
|
||||||
|
uint32_t first_sector = fat32_get_first_sector_of_cluster(ctrl, cluster); // get the first sector of the cluster
|
||||||
|
uint32_t bytes_per_cluster = fat32_get_bytes_per_cluster(ctrl); // get number of bytes per cluster
|
||||||
|
uint32_t pos_in_cluster = pos % bytes_per_cluster; // transform read position to a relative read position from the cluster beginning
|
||||||
|
uint32_t sector_of_cluster = pos_in_cluster / ctrl->bytes_per_sector; // get the sequence number of sector in the cluster that will be read
|
||||||
|
uint32_t sector = first_sector + sector_of_cluster; // offset the sectors based on relative read position
|
||||||
|
|
||||||
|
// update position fields
|
||||||
|
seek_hint->pos = pos;
|
||||||
|
seek_hint->sector_of_cluster = sector_of_cluster;
|
||||||
|
seek_hint->cluster = cluster;
|
||||||
|
seek_hint->sector = first_sector + sector_of_cluster;
|
||||||
|
seek_hint->sector_begin_pos = pos - (pos % ctrl->bytes_per_sector);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *file_helper, uint32_t len, uint8_t *p) {
|
||||||
|
FETCH_MSTG;
|
||||||
|
|
||||||
|
Fat32_SeekHint * seek_hint = &file_helper->last_pos;
|
||||||
|
|
||||||
|
uint32_t pos_in_sector = seek_hint->pos - seek_hint->sector_begin_pos; // determine position in current sector
|
||||||
|
|
||||||
|
READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector);
|
||||||
|
|
||||||
|
// cap length, don't read after the end of the file
|
||||||
|
uint32_t read_len = MIN(len, file_helper->size - seek_hint->pos);
|
||||||
|
uint32_t offset = 0;
|
||||||
|
|
||||||
|
// reading from a file may involve reading across cluster boundaries
|
||||||
|
|
||||||
|
// 1. read the beginning from the first cluster
|
||||||
|
uint32_t first_read_len = MIN(read_len, ctrl->bytes_per_sector - seek_hint->pos);
|
||||||
|
memcpy(p, buffer + pos_in_sector, first_read_len);
|
||||||
|
read_len -= first_read_len; // decrease the total read len with the number of bytes read from the first sector of the file
|
||||||
|
offset += first_read_len; // advance offset
|
||||||
|
seek_hint->pos += first_read_len; // advance seek pos
|
||||||
|
|
||||||
|
// 2. read from intermediate sectors and read from the final sector
|
||||||
|
|
||||||
|
// if the remaining read length is non-zero, then maintain buffered sectors
|
||||||
|
while (read_len > 0) {
|
||||||
|
seek_hint->sector_of_cluster++; // increase sector index
|
||||||
|
if (seek_hint->sector_of_cluster >= ctrl->sectors_per_cluster) { // if we crossed a cluster boundary, then load the next cluster's first sector in chain
|
||||||
|
seek_hint->cluster = fat32_get_next_chained_cluster(ctrl, seek_hint->cluster); // get next cluster
|
||||||
|
seek_hint->sector = fat32_get_first_sector_of_cluster(ctrl, seek_hint->cluster);
|
||||||
|
seek_hint->sector_of_cluster = 0; // it's the zeroth sector of the cluster
|
||||||
|
READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector); // load the newly determined cluster's first sector
|
||||||
|
} else { // if we didn't cross a cluster boundary, then...
|
||||||
|
seek_hint->sector++; //...just increase the sector count
|
||||||
|
READ_MSTG_TO_FIRST_BUFFER(seek_hint->sector); // ...and load that sector
|
||||||
|
}
|
||||||
|
|
||||||
|
seek_hint->sector_begin_pos += ctrl->bytes_per_sector; // advance begin position, we have stepped one sector forward
|
||||||
|
|
||||||
|
// copy the whole sector or some portion of the sector counting from the sector's begin
|
||||||
|
uint32_t copy_size = MIN(ctrl->bytes_per_sector, read_len);
|
||||||
|
memcpy(p + offset, buffer, copy_size);
|
||||||
|
read_len -= copy_size;
|
||||||
|
offset += copy_size;
|
||||||
|
seek_hint->pos += copy_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return read_len;
|
||||||
|
}
|
@ -95,21 +95,29 @@ typedef struct {
|
|||||||
uint32_t pos; // byte position
|
uint32_t pos; // byte position
|
||||||
uint32_t cluster; // cluster corresponding to the position
|
uint32_t cluster; // cluster corresponding to the position
|
||||||
uint32_t sector; // sector of the last read
|
uint32_t sector; // sector of the last read
|
||||||
|
uint32_t sector_of_cluster; // sequence number of sector in the current cluster
|
||||||
uint32_t sector_begin_pos; // byte position of the sector's begin
|
uint32_t sector_begin_pos; // byte position of the sector's begin
|
||||||
} Fat32_SeekHint;
|
} Fat32_SeekHint;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Fat32_CtrlBlock * ctrl; // pointer FAT32 control block
|
const Fat32_CtrlBlock * ctrl; // pointer FAT32 control block
|
||||||
Fat32_SeekHint last_pos; // last read position
|
Fat32_SeekHint last_pos; // last read position
|
||||||
uint32_t entry_pos; // FileEntry byte position
|
uint32_t entry_ba; // FileEntry byte address on the mass storage
|
||||||
|
uint32_t size; // file size
|
||||||
} Fat32_FileHelper;
|
} Fat32_FileHelper;
|
||||||
|
|
||||||
int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage * mstg);
|
int fat32_load(Fat32_CtrlBlock *ctrl, uint32_t bpb_s, const MassStorage * mstg);
|
||||||
|
|
||||||
int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char * dir);
|
int fat32_list_dir(const Fat32_CtrlBlock *ctrl, const char * dir);
|
||||||
|
|
||||||
uint32_t fat32_read_file(const Fat32_CtrlBlock * ctrl, const Fat32_FileEntry * entry, uint32_t pos, uint32_t len, uint8_t * p);
|
uint32_t fat32_read_file_random_access(const Fat32_CtrlBlock * ctrl, const Fat32_FileEntry * entry, uint32_t pos, uint32_t len, uint8_t * p);
|
||||||
|
|
||||||
const Fat32_FileEntry * fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path);
|
const Fat32_FileEntry * fat32_locate_file(const Fat32_CtrlBlock *ctrl, const char *path, uint32_t * file_entry_ba);
|
||||||
|
|
||||||
|
int fat32_open_file(const Fat32_CtrlBlock *ctrl, const char *path, Fat32_FileHelper *file_helper);
|
||||||
|
|
||||||
|
uint32_t fat32_seek_stream(const Fat32_CtrlBlock *ctrl, uint32_t pos, Fat32_FileHelper *file_helper);
|
||||||
|
|
||||||
|
uint32_t fat32_read_file_stream(const Fat32_CtrlBlock *ctrl, Fat32_FileHelper *file_helper, uint32_t len, uint8_t *p);
|
||||||
|
|
||||||
#endif //EMBPART_FAT32_H
|
#endif //EMBPART_FAT32_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user