/* * Copyright (C) 2005-2006 by egnite Software GmbH. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ /*! * \file fs/phatfs.c * \brief PHAT File System. * * \verbatim * * $Log$ * Revision 1.15 2009/02/13 14:52:05 haraldkipp * Include memdebug.h for heap management debugging support. * * Revision 1.14 2008/08/25 14:26:32 haraldkipp * Append to existing files failed when file size was larger than one cluster. * * Revision 1.13 2008/08/22 16:22:22 olereinhardt * Check if filesystem is mounted when opening a file. * Prevents a system lockup because of a nullpointer access * * Revision 1.12 2008/08/11 06:59:42 haraldkipp * BSD types replaced by stdint types (feature request #1282721). * * Revision 1.11 2008/04/02 09:39:29 haraldkipp * Fixed another PHAT file pointer bug. * * Revision 1.10 2008/04/01 10:14:58 haraldkipp * Fixes bug #1903303. Still not fully correct, because fopen(name, "a+") * should set the read pointer at position 0 while all writes should go * to the end of the file. But setting the write pointer at the end will * work for now. * * Revision 1.9 2006/10/08 16:42:56 haraldkipp * Not optimal, but simple and reliable exclusive access implemented. * Fixes bug #1486539. Furthermore, bug #1567790, which had been rejected, * had been reported correctly and is now fixed. * * Revision 1.8 2006/07/11 12:20:57 haraldkipp * Added mutual exclusion protection during flush. * Honor Nut/OS file flushing, which uses a NULL pointer on read/write. * * Revision 1.7 2006/07/05 16:02:23 haraldkipp * Typically Nut/OS doesn't check parameters, but this one is missed often, * closing a file which points to NUTFILE_EOF. * * Revision 1.6 2006/06/18 16:38:28 haraldkipp * No need to set errno after malloc failed. * Support for long filenames (VFAT) added. * New function PhatDirReleaseChain() simplifies code. * Fixed positioning bug, which caused several problems like limiting * directories to one cluster. * * Revision 1.5 2006/05/15 11:47:18 haraldkipp * Added support for file seek. * * Revision 1.4 2006/03/02 19:59:05 haraldkipp * Added implementation of dev_size makes _filelength() work, which in turn * enables the use of these file systems in pro/httpd.c. * * Revision 1.3 2006/02/23 15:45:22 haraldkipp * PHAT file system now supports configurable number of sector buffers. * This dramatically increased write rates of no-name cards. * AVR compile errors corrected. * * Revision 1.2 2006/01/22 17:42:08 haraldkipp * Now file delete requests fail if used on directory entries. * * Revision 1.1 2006/01/05 16:31:39 haraldkipp * First check-in. * * * \endverbatim */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(NUTDEBUG) #include #include #endif #ifndef SEEK_SET # define SEEK_SET 0 /* Seek from beginning of file. */ # define SEEK_CUR 1 /* Seek from current position. */ # define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ #endif /*! * \addtogroup xgPhatFs */ /*@{*/ /*! * \brief Search free cluster. * * \param dev Specifies the file system device. * \param first First cluster to check. Must not be lower than 2. * \param last Last cluster to check. * * \return Number of the free cluster found. If an error occured, the * returned value will be lower than the first cluster searched. */ static uint32_t SearchFreeCluster(NUTDEVICE * dev, uint32_t first, uint32_t last) { int rc = -1; uint32_t clust; uint32_t link = 1; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; if (vol->vol_type == 32) { for (clust = first; clust < last; clust++) { if ((rc = Phat32GetClusterLink(dev, clust, &link)) < 0 || link == 0) { break; } } } else if (vol->vol_type == 16) { for (clust = first; clust < last; clust++) { if ((rc = Phat16GetClusterLink(dev, clust, &link)) < 0 || link == 0) { break; } } } else { for (clust = first; clust < last; clust++) { if ((rc = Phat12GetClusterLink(dev, clust, &link)) < 0 || link == 0) { break; } } } if (rc || link) { return 0; } return clust; } /*! * \brief Allocate a new cluster. * * \param dev Specifies the file system device. * * \return Number of the allocated cluster, which is not lower than 2. * Any value lower than 2 indicates an error. */ static uint32_t AllocCluster(NUTDEVICE * dev) { uint32_t clust; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* * If the hint for the next free cluster is invalid, start from the beginning, */ if (vol->vol_nxtfree < 2 || vol->vol_nxtfree >= vol->vol_last_clust) { vol->vol_nxtfree = 2; } if ((clust = SearchFreeCluster(dev, vol->vol_nxtfree, vol->vol_last_clust)) < 2) { if ((clust = SearchFreeCluster(dev, 2, vol->vol_nxtfree)) < 2) { vol->vol_nxtfree = 0; errno = ENOSPC; return 0; } } vol->vol_nxtfree = clust; return clust; } /*! * \brief Allocate the first cluster of a file. * * \param nfp The file for which a cluster is allocated. * * \return Number of the allocated cluster, which is not lower than 2. * Any value lower than 2 indicates an error. */ uint32_t AllocFirstCluster(NUTFILE * nfp) { uint32_t clust; NUTDEVICE *dev = nfp->nf_dev; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; PHATFILE *fcb = nfp->nf_fcb; if ((clust = AllocCluster(dev)) < 2) { return 0; } /* Set the pointer to the first cluster in out directory entry. */ fcb->f_dirent.dent_clusthi = (uint16_t) (clust >> 16); fcb->f_dirent.dent_clust = (uint16_t) clust; fcb->f_de_dirty = 1; /* The first cluster entry will be set to EOC. */ if (vol->vol_type == 32) { if (Phat32SetClusterLink(dev, clust, PHAT32CMASK)) { return 0; } } else if (vol->vol_type == 16) { if (Phat16SetClusterLink(dev, clust, PHAT16CMASK)) { return 0; } } else if (Phat12SetClusterLink(dev, clust, PHAT12CMASK)) { return 0; } vol->vol_numfree--; return clust; } /*! * \brief Allocate the next cluster of a file. * * \param The file for which a cluster is allocated. * * \return Number of the allocated cluster, which is not lower than 2. * Any value lower than 2 indicates an error. */ static uint32_t AllocNextCluster(NUTFILE * nfp) { uint32_t clust; NUTDEVICE *dev = nfp->nf_dev; PHATFILE *fcb = nfp->nf_fcb; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Allocate a free cluster. */ if ((clust = AllocCluster(dev)) < 2) { return 0; } /* Link the previous cluster to the new one and set * the entry of the new one to EOC. */ if (vol->vol_type == 32) { if (Phat32SetClusterLink(dev, fcb->f_clust, clust)) { return 0; } if (Phat32SetClusterLink(dev, clust, PHAT32CMASK)) { return 0; } } else if (vol->vol_type == 16) { if (Phat16SetClusterLink(dev, fcb->f_clust, clust)) { return 0; } if (Phat16SetClusterLink(dev, clust, PHAT16CMASK)) { return 0; } } else if (Phat12SetClusterLink(dev, fcb->f_clust, clust)) { return 0; } else if (Phat12SetClusterLink(dev, clust, PHAT12CMASK)) { return 0; } vol->vol_numfree--; return clust; } /*! * \brief Flush file buffers. * * \param nfp Specifies the file. * * \return 0 on success, -1 otherwise. */ static int PhatFileFlush(NUTFILE * nfp) { int rc; NUTDEVICE *dev = nfp->nf_dev; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Update the file's directory entry. */ if ((rc = PhatDirEntryUpdate(nfp)) == 0) { /* Gain mutex access. */ NutEventWait(&vol->vol_iomutex, 0); /* Flush sector buffers. */ rc = PhatSectorFlush(nfp->nf_dev, -1); /* Release mutex access. */ NutEventPost(&vol->vol_iomutex); } return rc; } /*! * \brief Close a file. * * \param nfp File descriptor. * * \return 0 on success, -1 otherwise. */ int PhatFileClose(NUTFILE * nfp) { int rc; if (nfp == NULL || nfp == NUTFILE_EOF) { errno = EBADF; return -1; } #ifdef NUTDEBUG PhatDbgFileInfo(stdout, "Close file", (PHATFILE *) nfp->nf_fcb); #endif rc = PhatFileFlush(nfp); if (nfp->nf_fcb) { free(nfp->nf_fcb); } free(nfp); return rc; } /*! * \brief Open a file. * * This function is called by the low level open routine of the C runtime * library, using the _NUTDEVICE::dev_open entry. * * \param dev Specifies the file system device. * \param path Pathname of the file to open. If the last character is a * slash, then a directory will be opened. * \param mode Operation mode. * \param acc File attribute. * * \return Pointer to a NUTFILE structure if successful or NUTFILE_EOF otherwise. * * \bug Append mode not working as expected. */ NUTFILE *PhatFileOpen(NUTDEVICE * dev, const char *path, int mode, int acc) { NUTFILE *nfp = NUTFILE_EOF; NUTFILE *ndp = NUTFILE_EOF; PHATFILE *ffcb; PHATFILE *dfcb; PHATFIND *srch; const char *fname; /* Open the parent directory and return the basename. */ if ((ndp = PhatDirOpenParent(dev, path, &fname)) == NUTFILE_EOF) { return NUTFILE_EOF; } /* * We successfully opened the directory. If no file name had been specified, * then the caller wants to open the directory itself. In this case, simply * return the NUTFILE for it. */ dfcb = ndp->nf_fcb; if (*fname == 0) { dfcb->f_mode = mode; return ndp; } /* * Allocate a file information and a temporary search structure. */ nfp = malloc(sizeof(NUTFILE)); ffcb = malloc(sizeof(PHATFILE)); srch = malloc(sizeof(PHATFIND)); if (nfp == NULL || ffcb == NULL || srch == NULL) { PhatFileClose(ndp); if (nfp) { free(nfp); } if (ffcb) { free(ffcb); } if (srch) { free(srch); } return NUTFILE_EOF; } memset(ffcb, 0, sizeof(PHATFILE)); nfp->nf_dev = dev; nfp->nf_fcb = ffcb; /* * Check if the specified file already exists. * * Note, that directories are handled differently in PhatDirCreate(), * where the first cluster is initialized with zeros. */ if (PhatDirEntryFind(ndp, fname, PHAT_FATTR_FILEMASK, srch)) { /* * File doesn't exist. Does the opening mode allow to create * a new file? */ if ((mode & _O_CREAT) == 0) { free(srch); PhatFileClose(ndp); PhatFileClose(nfp); errno = ENOENT; return NUTFILE_EOF; } /* Create a new directory entry. */ if (PhatDirEntryCreate(ndp, fname, acc, &ffcb->f_dirent)) { free(srch); PhatFileClose(ndp); PhatFileClose(nfp); return NUTFILE_EOF; } ffcb->f_de_dirty = 1; #ifdef NUTDEBUG PhatDbgFileInfo(stdout, "New entry", ffcb); #endif } else { /* * We return an error, if the file exists and _O_EXCL has been * set with _O_CREAT. */ if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) { free(srch); PhatFileClose(ndp); PhatFileClose(nfp); errno = EEXIST; return NUTFILE_EOF; } #ifdef NUTDEBUG PhatDbgFileInfo(stdout, "Existing entry", ffcb); #endif /* * Truncate an existing file. */ if (mode & _O_TRUNC) { /* * Relase all clusters allocated by this entry. */ if (PhatDirReleaseChain(dev, &srch->phfind_ent)) { PhatFileClose(ndp); PhatFileClose(nfp); free(srch); return NUTFILE_EOF; } memset(ffcb, 0, sizeof(PHATFILE)); memcpy(ffcb->f_dirent.dent_name, srch->phfind_ent.dent_name, sizeof(ffcb->f_dirent.dent_name)); ffcb->f_dirent.dent_attr = srch->phfind_ent.dent_attr; ffcb->f_dirent.dent_rsvdnt = srch->phfind_ent.dent_rsvdnt; ffcb->f_dirent.dent_ctsecs = srch->phfind_ent.dent_ctsecs; ffcb->f_dirent.dent_ctime = srch->phfind_ent.dent_ctime; ffcb->f_dirent.dent_cdate = srch->phfind_ent.dent_cdate; ffcb->f_de_dirty = 1; } else { ffcb->f_dirent = srch->phfind_ent; } } free(srch); /* Store position of our directory entry. */ ffcb->f_de_sect = PhatClusterSector(ndp, dfcb->f_clust) + dfcb->f_clust_pos; ffcb->f_de_offs = dfcb->f_sect_pos - 32; /* Store first cluster of parent. */ ffcb->f_pde_clusthi = dfcb->f_dirent.dent_clusthi; ffcb->f_pde_clust = dfcb->f_dirent.dent_clust; /* Set the current cluster. */ ffcb->f_clust = ffcb->f_dirent.dent_clusthi; ffcb->f_clust <<= 16; ffcb->f_clust += ffcb->f_dirent.dent_clust; /* Store the opening mode. */ ffcb->f_mode = mode; /* Close the directory. */ PhatFileClose(ndp); /* * Append to an existing file. */ if ((mode & _O_APPEND) != 0 && ffcb->f_dirent.dent_fsize) { if (PhatFilePosSet(nfp, ffcb->f_dirent.dent_fsize)) { PhatFileClose(nfp); return NUTFILE_EOF; } } #ifdef NUTDEBUG PhatDbgFileInfo(stdout, "File opened", ffcb); #endif return nfp; } /*! * \brief Write data to a file. * * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous * call to PnutFileOpen(). * \param buffer Pointer to the data to be written. If zero, then the * output buffer will be flushed. * \param len Number of bytes to write. * * \return The number of bytes written. A return value of -1 indicates an * error. */ int PhatFileWrite(NUTFILE * nfp, const void *buffer, int len) { int rc; int step; uint32_t clust; int sbn; uint8_t *buf = (uint8_t *) buffer; NUTDEVICE *dev = nfp->nf_dev; PHATFILE *fcb = nfp->nf_fcb; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* * Refuse to write to files with RDONLY attribute set. */ if (fcb->f_dirent.dent_attr & PHAT_FATTR_RDONLY) { errno = EACCES; return -1; } /* * Flush file if buffer is a NULL pointer. */ if (buf == NULL || len == 0) { return PhatFileFlush(nfp); } /* * In case of normal files, check for sufficient space. */ if ((fcb->f_dirent.dent_attr & PHAT_FATTR_DIR) == 0) { /* Bytes per cluster. */ uint32_t num = vol->vol_sectsz * vol->vol_clustsz; /* Number of clusters already used. */ uint32_t cur = (fcb->f_dirent.dent_fsize + num - 1) / num; /* Number of clusters used after writing. */ num = (fcb->f_pos + len + num - 1) / num; /* If additional clusters are required, are they available? */ if (num > cur && num - cur > vol->vol_numfree) { errno = ENOSPC; return -1; } /* If the file is empty, allocate the first cluster. */ if (fcb->f_dirent.dent_fsize == 0) { if ((clust = AllocFirstCluster(nfp)) < 2) { return -1; } fcb->f_clust_prv = clust; fcb->f_clust = clust; } } /* * Write the data. */ for (rc = 0, step = 0; rc < len; rc += step) { /* Did we reach the end of a sector? */ if (fcb->f_sect_pos >= vol->vol_sectsz) { /* Move to the next sector within the cluster. */ if (IsFixedRootDir(nfp)) { if (fcb->f_clust_pos + 1 >= vol->vol_rootsz) { /* End of root directory, abort writing. */ break; } fcb->f_clust_pos++; } else { /* Did we reach the last sector of this cluster? */ if (fcb->f_clust_pos + 1 >= vol->vol_clustsz) { /* Move to the next cluster. */ if (vol->vol_type == 32) { if (Phat32GetClusterLink(dev, fcb->f_clust, &clust)) { rc = -1; break; } if (clust >= (PHATEOC & PHAT32CMASK)) { if ((clust = AllocNextCluster(nfp)) < 2) { rc = -1; break; } } } else if (vol->vol_type == 16) { if (Phat16GetClusterLink(dev, fcb->f_clust, &clust)) { rc = -1; break; } if (clust >= (PHATEOC & PHAT16CMASK)) { if ((clust = AllocNextCluster(nfp)) < 2) { rc = -1; break; } } } else if (Phat12GetClusterLink(dev, fcb->f_clust, &clust)) { rc = -1; break; } else if (clust >= (PHATEOC & PHAT12CMASK)) { if ((clust = AllocNextCluster(nfp)) < 2) { rc = -1; break; } } fcb->f_clust_pos = 0; fcb->f_clust_prv = fcb->f_clust; fcb->f_clust = clust; } else { fcb->f_clust_pos++; } } fcb->f_sect_pos = 0; } /* Write full sectors. */ if (fcb->f_sect_pos == 0 && len - rc >= vol->vol_sectsz) { uint32_t sect; int cnt; step = (int) (vol->vol_clustsz - fcb->f_clust_pos) * vol->vol_sectsz; if (step > len - rc) { step = len - rc; } sect = PhatClusterSector(nfp, fcb->f_clust) + fcb->f_clust_pos; /* Calculate number of sectors. */ cnt = step / vol->vol_sectsz; /* Update step size to sector size multiple. */ step = cnt * vol->vol_sectsz; if (PhatSectorWrite(nfp->nf_dev, sect, &buf[rc], cnt)) { rc = -1; break; } /* Advance file pointers. */ fcb->f_clust_pos += cnt - 1; fcb->f_pos += step; fcb->f_sect_pos = vol->vol_sectsz; } else { /* Load the sector we want to write to. */ if ((sbn = PhatSectorLoad(nfp->nf_dev, PhatClusterSector(nfp, fcb->f_clust) + fcb->f_clust_pos)) < 0) { rc = -1; break; } /* The number of bytes we write to this sector. */ step = (int) (vol->vol_sectsz - fcb->f_sect_pos); if (step > len - rc) { step = len - rc; } /* Copy data to this sector. */ memcpy(&vol->vol_buf[sbn].sect_data[fcb->f_sect_pos], &buf[rc], step); vol->vol_buf[sbn].sect_dirty = 1; PhatSectorBufferRelease(dev, sbn); /* Advance file pointers. */ fcb->f_pos += step; fcb->f_sect_pos += step; } } if (rc > 0) { /* * Update directory entry. Note that directory entries of directories * are never updated in a PHAT file system. */ if ((fcb->f_dirent.dent_attr & PHAT_FATTR_DIR) == 0) { GetDosTimeStamp(&fcb->f_dirent.dent_mtime, &fcb->f_dirent.dent_mdate); fcb->f_dirent.dent_adate = fcb->f_dirent.dent_mdate; fcb->f_dirent.dent_attr |= PHAT_FATTR_ARCHIV; if(fcb->f_dirent.dent_fsize < fcb->f_pos) { fcb->f_dirent.dent_fsize = fcb->f_pos; } fcb->f_de_dirty = 1; } } return rc; } #ifdef __HARVARD_ARCH__ /*! * \brief Write data from program space to a file. * * This function is not yet implemented and will always return -1. * * Similar to PhatFileWrite() except that the data is located in * program memory. * * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous * call to PnutFileOpen(). * \param buffer Pointer to the data in program space. If zero, then the * output buffer will be flushed. * \param len Number of bytes to write. * * \return The number of bytes written. A return value of -1 indicates an * error. */ int PhatFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len) { return -1; } #endif /*! * \brief Read data from a file. * * \param nfp Pointer to a ::NUTFILE structure, obtained by a previous * call to PnutFileOpen(). * \param buffer Pointer to the data buffer to fill. * \param size Maximum number of bytes to read. * * \return The number of bytes actually read. A return value of -1 indicates * an error. */ int PhatFileRead(NUTFILE * nfp, void *buffer, int size) { int rc; int step; int sbn; uint8_t *buf = (uint8_t *) buffer; NUTDEVICE *dev = nfp->nf_dev; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; PHATFILE *fcb = nfp->nf_fcb; /* * Ignore input flush. */ if (buf == NULL || size == 0) { return 0; } /* Respect the end of normal files. */ if ((fcb->f_dirent.dent_attr & PHAT_FATTR_DIR) == 0) { if (fcb->f_pos + size >= fcb->f_dirent.dent_fsize) { size = fcb->f_dirent.dent_fsize - fcb->f_pos; } } for (rc = 0, step = 0; rc < size; rc += step) { /* Did we reach the end of a sector? */ if (fcb->f_sect_pos >= vol->vol_sectsz) { /* Move to the next sector. */ if (IsFixedRootDir(nfp)) { if (fcb->f_clust_pos + 1 >= vol->vol_rootsz) { /* End of root directory, abort reading. */ break; } fcb->f_clust_pos++; } else { /* Did we reach the last sector of this cluster? */ if (fcb->f_clust_pos + 1 >= vol->vol_clustsz) { /* Move to the next cluster. */ uint32_t clust; if (vol->vol_type == 32) { if (Phat32GetClusterLink(dev, fcb->f_clust, &clust)) { break; } if (clust >= (PHATEOC & PHAT32CMASK)) { break; } } else if (vol->vol_type == 16) { if (Phat16GetClusterLink(dev, fcb->f_clust, &clust)) { break; } if (clust >= (PHATEOC & PHAT16CMASK)) { break; } } else if (Phat12GetClusterLink(dev, fcb->f_clust, &clust)) { break; } else if (clust >= (PHATEOC & PHAT12CMASK)) { break; } fcb->f_clust_pos = 0; fcb->f_clust_prv = fcb->f_clust; fcb->f_clust = clust; } else { fcb->f_clust_pos++; } } fcb->f_sect_pos = 0; } /* Make sure that the required sector is loaded. */ if ((sbn = PhatSectorLoad(nfp->nf_dev, PhatClusterSector(nfp, fcb->f_clust) + fcb->f_clust_pos)) < 0) { rc = -1; break; } step = (int) (vol->vol_sectsz - fcb->f_sect_pos); if (step > size - rc) { step = size - rc; } memcpy(&buf[rc], &vol->vol_buf[sbn].sect_data[fcb->f_sect_pos], step); PhatSectorBufferRelease(dev, sbn); fcb->f_pos += step; fcb->f_sect_pos += step; } return rc; } /*! * \brief Retrieve the size of a previously opened file. * * This function is called by the low level size routine of the C runtime * library, using the _NUTDEVICE::dev_size entry. * * \param nfp Pointer to a \ref _NUTFILE structure, obtained by a * previous call to PhatFileOpen(). * * \return Size of the file. */ static long PhatFileSize(NUTFILE *nfp) { PHATFILE *fcb = nfp->nf_fcb; return fcb->f_dirent.dent_fsize; } static int PhatFileSeek(NUTFILE * nfp, long *pos, int whence) { int rc = 0; long npos = *pos; PHATFILE *fcb = nfp->nf_fcb; switch (whence) { case SEEK_CUR: npos += fcb->f_pos; break; case SEEK_END: npos += PhatFileSize(nfp); break; } if (npos < 0 || npos > PhatFileSize(nfp)) { rc = EINVAL; } else { rc = PhatFilePosSet(nfp, npos); *pos = fcb->f_pos; } return rc; } /*! * \brief File system specific functions. * \param dev Specifies the file system device. */ static int PhatIOCtl(NUTDEVICE * dev, int req, void *conf) { int rc = -1; switch (req) { case FS_STATUS: { FSCP_STATUS *par = (FSCP_STATUS *) conf; rc = PhatDirEntryStatus(dev, par->par_path, par->par_stp); } break; case FS_DIR_CREATE: rc = PhatDirCreate(dev, (char *) conf); break; case FS_DIR_REMOVE: rc = PhatDirRemove(dev, (char *) conf); break; case FS_DIR_OPEN: /* Open a directory for reading entries. */ { DIR *dir = (DIR *) conf; if ((dir->dd_fd = PhatDirOpen(dev, dir->dd_buf)) != NUTFILE_EOF) { rc = 0; } } break; case FS_DIR_CLOSE: rc = PhatFileClose(((DIR *) conf)->dd_fd); break; case FS_DIR_READ: rc = PhatDirRead((DIR *) conf); break; case FS_FILE_STATUS: /* TODO */ break; case FS_FILE_DELETE: rc = PhatDirDelEntry(dev, (char *) conf, PHAT_FATTR_FILEMASK & ~PHAT_FATTR_DIR); break; case FS_FILE_SEEK: PhatFileSeek((NUTFILE *) ((IOCTL_ARG3 *) conf)->arg1, /* */ (long *) ((IOCTL_ARG3 *) conf)->arg2, /* */ (int) ((IOCTL_ARG3 *) conf)->arg3); break; case FS_RENAME: /* Rename an existing file or directory. */ { FSCP_RENAME *par = (FSCP_RENAME *) conf; rc = PhatDirRenameEntry(dev, par->par_old, par->par_new); } break; case FS_VOL_MOUNT: { /* Mount a volume. */ FSCP_VOL_MOUNT *par = (FSCP_VOL_MOUNT *) conf; rc = PhatVolMount(dev, par->fscp_bmnt, par->fscp_part_type); if (rc) { /* Release resources on failures. */ PhatVolUnmount(dev); } } break; case FS_VOL_UNMOUNT: /* Unmount a volume. */ rc = PhatVolUnmount(dev); break; } return rc; } /*! * \brief Initialize the PHAT file system driver. * * This routine is called during device registration. * * \param dev Specifies the file system device. * * \return Zero on success. Otherwise an error code is returned. */ static int PhatInit(NUTDEVICE * dev) { /* Nothing to do. */ return 0; } /*! * \brief Reentrant variant of PhatFileOpen(). */ static NUTFILE *PhatApiFileOpen(NUTDEVICE * dev, const char *path, int mode, int acc) { NUTFILE *rc; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Make sure the volume is mounted. */ if (vol == NULL) { errno = ENOENT; return NUTFILE_EOF; } /* Lock filesystem access. */ NutEventWait(&vol->vol_fsmutex, 0); /* Call worker routine. */ rc = PhatFileOpen(dev, path, mode, acc); /* Release filesystem lock. */ NutEventPost(&vol->vol_fsmutex); return rc; } /*! * \brief Reentrant variant of PhatFileClose(). */ static int PhatApiFileClose(NUTFILE * nfp) { int rc; NUTDEVICE *dev = nfp->nf_dev; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Lock filesystem access. */ NutEventWait(&vol->vol_fsmutex, 0); /* Call worker routine. */ rc = PhatFileClose(nfp); /* Release filesystem lock. */ NutEventPost(&vol->vol_fsmutex); return rc; } /*! * \brief Reentrant variant of PhatFileWrite(). */ static int PhatApiFileWrite(NUTFILE * nfp, const void *buffer, int len) { int rc; NUTDEVICE *dev = nfp->nf_dev; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Lock filesystem access. */ NutEventWait(&vol->vol_fsmutex, 0); /* Call worker routine. */ rc = PhatFileWrite(nfp, buffer, len); /* Release filesystem lock. */ NutEventPost(&vol->vol_fsmutex); return rc; } #ifdef __HARVARD_ARCH__ /*! * \brief Reentrant variant of PhatFileWrite_P(). */ static int PhatApiFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len) { int rc; NUTDEVICE *dev = nfp->nf_dev; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Lock filesystem access. */ NutEventWait(&vol->vol_fsmutex, 0); /* Call worker routine. */ rc = PhatFileWrite_P(nfp, buffer, len); /* Release filesystem lock. */ NutEventPost(&vol->vol_fsmutex); return rc; } #endif /*! * \brief Reentrant variant of PhatFileRead(). */ static int PhatApiFileRead(NUTFILE * nfp, void *buffer, int size) { int rc; NUTDEVICE *dev = nfp->nf_dev; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Lock filesystem access. */ NutEventWait(&vol->vol_fsmutex, 0); /* Call worker routine. */ rc = PhatFileRead(nfp, buffer, size); /* Release filesystem lock. */ NutEventPost(&vol->vol_fsmutex); return rc; } /*! * \brief Reentrant variant of PhatIOCtl(). */ static int PhatApiIOCtl(NUTDEVICE * dev, int req, void *conf) { int rc; PHATVOL *vol = (PHATVOL *) dev->dev_dcb; /* Lock filesystem access. */ if (req != FS_VOL_MOUNT && vol) { NutEventWait(&vol->vol_fsmutex, 0); } /* Call worker routine. */ rc = PhatIOCtl(dev, req, conf); /* Release filesystem lock. */ if (req != FS_VOL_MOUNT && req != FS_VOL_UNMOUNT && vol) { NutEventPost(&vol->vol_fsmutex); } return rc; } /*! * \brief PHAT file system driver information structure. * * A pointer to this structure must be passed to NutRegisterDevice() * to bind this file system driver to the Nut/OS kernel. * An application may then call * /verbatim * _open("MMC0:1/PHAT0", _O_RDWR | _O_BINARY); * /endverbatim * to mount partition 1 on the previously registered block * device (devMmc0 in this example). */ NUTDEVICE devPhat0 = { 0, /*!< Pointer to next device, dev_next. */ {'P', 'H', 'A', 'T', '0', 0, 0, 0, 0} , /*!< Unique device name, dev_name. */ IFTYP_FS, /*!< Type of device, dev_type. Obsolete. */ 0, /*!< Base address, dev_base. Unused. */ 0, /*!< First interrupt number, dev_irq. Unused. */ 0, /*!< Mounted block device partition, dev_icb. */ 0, /*!< Volume information, dev_dcb. */ PhatInit, /*!< Driver initialization routine, dev_init. */ PhatApiIOCtl, /*!< Driver specific control function, dev_ioctl. */ PhatApiFileRead, /*!< Read data from a file, dev_read. */ PhatApiFileWrite, /*!< Write data to a file, dev_write. */ #ifdef __HARVARD_ARCH__ PhatApiFileWrite_P, /*!< Write data from program space to a file, dev_write_P. */ #endif PhatApiFileOpen, /*!< Open a file, dev_open. */ PhatApiFileClose, /*!< Close a file, dev_close. */ PhatFileSize, /*!< Return file size, dev_size. */ NULL, /*!< Select function, optional, not yet implemented */ }; NUTDEVICE devPhat1 = { 0, /*!< Pointer to next device, dev_next. */ {'P', 'H', 'A', 'T', '1', 0, 0, 0, 0} , /*!< Unique device name, dev_name. */ IFTYP_FS, /*!< Type of device, dev_type. Obsolete. */ 0, /*!< Base address, dev_base. Unused. */ 0, /*!< First interrupt number, dev_irq. Unused. */ 0, /*!< Mounted block device partition, dev_icb. */ 0, /*!< Volume information, dev_dcb. */ PhatInit, /*!< Driver initialization routine, dev_init. */ PhatApiIOCtl, /*!< Driver specific control function, dev_ioctl. */ PhatApiFileRead, /*!< Read data from a file, dev_read. */ PhatApiFileWrite, /*!< Write data to a file, dev_write. */ #ifdef __HARVARD_ARCH__ PhatApiFileWrite_P, /*!< Write data from program space to a file, dev_write_P. */ #endif PhatApiFileOpen, /*!< Open a file, dev_open. */ PhatApiFileClose, /*!< Close a file, dev_close. */ PhatFileSize, /*!< Return file size, dev_size. */ NULL, /*!< Select function, optional, not yet implemented */ }; /*@}*/