| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086 |
- /*
- * Copyright (C) 2010 by egnite 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/
- */
- #include <fs/fs.h>
- #include <sys/nutdebug.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <fcntl.h>
- #include <sys/stat.h>
- #include <string.h>
- #include <errno.h>
- #include <dirent.h>
- #include <dev/spi_at45dib.h>
- #include <fs/uflashfs.h>
- #if 0
- /* Use for local debugging. */
- #define NUTDEBUG
- #include <stdio.h>
- #endif
- /*!
- * \file fs/uflashfs.c
- * \brief UFLASH File System.
- *
- * The UFLASH file system has been specifically designed for serial
- * flash memory chips used on tiny embedded systems.
- *
- * Such memory chips do not allow to program single bytes. Instead,
- * only full pages can be programmed. On the other hand, these chips
- * provide one or two integrated RAM buffers. They allow to modify
- * single bytes without transfering full pages between the memory
- * chip and the microcontroller's RAM.
- *
- * Although no mechanism has been implemented to replace worn out pages,
- * the file system maintains a very well balanced wear levelling. This
- * includes regular movement of static files.
- *
- * Furthermore, previously unfinished write accesses are automatically
- * detected and reverted when mounting a volume. However, this features
- * still needs to be tested more thoroughly.
- *
- * Note, that directories do not really exist in the UFLASH file system.
- * They are mimicked in a limited way. When scanning a directory, all
- * subdirectories will appear again for each file they contain.
- *
- * \verbatim
- * $Id$
- * \endverbatim
- */
- /*!
- * \addtogroup xgUFlash
- */
- /*@{*/
- /* Wheter to use time stamps. Requires, that the system time had been set. */
- //#define UFLASH_USE_TIMESTAMP
- /* Wheter to use IDs instead of file names. Not implemented. */
- //#define UFLASH_ID_NAMES
- /*
- * File system configuration.
- */
- #ifndef UFLASH_MAX_BLOCKS
- /*! \brief Max. number of blocks per file system. */
- #define UFLASH_MAX_BLOCKS 8192
- #endif
- #ifndef UFLASH_BLOCK_UNITS
- /*! \brief Number of units per block. */
- #define UFLASH_BLOCK_UNITS 4
- #endif
- #ifndef UFLASH_ENTRIES
- /*! \brief Number of file system entries. */
- #define UFLASH_ENTRIES 128
- #endif
- #ifndef UFLASH_USAGE_CACHE
- /*! \brief Size of the least used buffer. */
- #define UFLASH_USAGE_CACHE 32
- #endif
- #if UFLASH_MAX_BLOCKS <= 255
- typedef uint8_t blknum_t;
- typedef uint_fast8_t blknum_fast_t;
- #elif UFLASH_MAX_BLOCKS <= 65535
- typedef uint16_t blknum_t;
- typedef uint_fast16_t blknum_fast_t;
- #else
- typedef uint32_t blknum_t;
- typedef uint_fast32_t blknum_fast_t;
- #endif
- #ifndef UFLASH_MAX_PATH
- /*! \brief Maximum lenght of a full absolute path.
- *
- * If not defined, a variable path name length is used. Note, that
- * the total size of the name is still limited, because the block
- * header and the entry header must fit into the first unit of the
- * file system block.
- */
- #define UFLASH_MAX_PATH 0
- #endif
- #define UFLASH_BLOCK_INVALID ((blknum_t)-1)
- /*!
- * \brief Internal block usage cache structure.
- */
- typedef struct _BLOCK_USAGE {
- uint8_t use_cnt;
- blknum_t use_phyblk;
- } BLOCK_USAGE;
- /*!
- * \brief Internal block usage cache.
- */
- static BLOCK_USAGE min_used[UFLASH_USAGE_CACHE];
- /*!
- * \brief UFLASH block header.
- *
- * Each used block starts with a BLOCKHEAD structure.
- */
- typedef struct NUT_PACKED_TYPE _BLOCKHEAD {
- blknum_t bh_logblk; /*!< \brief Logical block number. */
- blknum_t bh_version; /*!< \brief Logical block version. */
- blknum_t bh_entblk; /*!< \brief Logical entry block number. */
- blknum_t bh_entseq; /*!< \brief Block sequence. */
- } BLOCKHEAD;
- /*!
- * \brief UFLASH block footer.
- *
- * Each block ends with a BLOCKFOOT structure.
- */
- typedef struct NUT_PACKED_TYPE _BLOCKFOOT {
- #ifdef UFLASH_USE_TIMESTAMP
- time_t bf_time; /*!< \brief Last update time. */
- #endif
- uint8_t bf_wear; /*!< \brief Erase counter. */
- } BLOCKFOOT;
- /*
- * UFLASH entry header.
- *
- * On each entry block an ENTRYHEAD structure follows the BLOCKHEAD.
- * The ENTRYHEAD is followed by the variable length name of this entry.
- * After the name comes the data, immediatly followed by an optional
- * timestamp.
- */
- typedef struct NUT_PACKED_TYPE _ENTRYHEAD {
- /* Entry attributes. */
- uint8_t eh_attr;
- /* Name length. */
- uint8_t eh_nlen;
- } ENTRYHEAD;
- /*!
- * \brief File system entry descriptor.
- */
- typedef struct _UFLASHENTRY {
- /*! \brief Logical block number of the entry. */
- blknum_t ent_id;
- /*! \brief Current data pointer position. */
- uint32_t ent_pos;
- /*! \brief Current logical block number. */
- blknum_t ent_bidx;
- /*! \brief Current sequence. */
- uint16_t ent_sidx;
- /*! \brief Current unit index within the current block. */
- uint16_t ent_uidx;
- /*! \brief Current position within the current unit. */
- uint16_t ent_upos;
- /*! \brief File open mode flags. */
- uint32_t ent_mode;
- /*! \brief Entry attributes. */
- uint8_t ent_attr;
- /*! \brief Name length. */
- uint8_t ent_namlen;
- /*! \brief Total data size. */
- uint32_t ent_size;
- /*! \brief Total file data offset of the entry block. */
- uint16_t ent_eoff;
- } UFLASHENTRY;
- #define UFLASH_VOLF_FIXED 0x80
- /*! \brief Volume info structure type. */
- typedef struct _UFLASHVOLUME UFLASHVOLUME;
- /*!
- * \brief Volume info structure.
- */
- struct _UFLASHVOLUME {
- /*! \brief I/O interface. */
- NUTSERIALFLASH *vol_ifc;
- /*! \brief Exclusive access queue. */
- HANDLE vol_mutex;
- /*! \brief Attribute flags. */
- uint8_t vol_attrib;
- /*! \brief Number of blocks in this volume. */
- blknum_t vol_blocks;
- /*! \brief Block translation table. */
- blknum_t *vol_l2p;
- };
- /*!
- * \brief Entry search structure.
- */
- typedef struct _UFLASHFIND {
- blknum_t uff_lbe;
- char *uff_path;
- } UFLASHFIND;
- /*!
- * \brief Copy a given unit from one block to another.
- */
- static int FlashUnitCopy(NUTSERIALFLASH * ifc, blknum_t b_src, blknum_t b_dst, sf_unit_t unit)
- {
- unit += ifc->sf_rsvbot;
- return (*ifc->sf_copy) (ifc, b_src * UFLASH_BLOCK_UNITS + unit, b_dst * UFLASH_BLOCK_UNITS + unit);
- }
- /*!
- * \brief Commit changes of a given unit.
- */
- static void FlashUnitCommit(NUTSERIALFLASH * ifc, blknum_t b, sf_unit_t unit)
- {
- (*ifc->sf_commit) (ifc, b * UFLASH_BLOCK_UNITS + unit + ifc->sf_rsvbot);
- }
- /*!
- * \brief Read unit.
- */
- static int FlashUnitRead(NUTSERIALFLASH * ifc, blknum_t b, sf_unit_t unit, int upos, void *data, int len)
- {
- int rc = ifc->sf_unit_size - upos;
- if (unit == UFLASH_BLOCK_UNITS - 1) {
- rc -= sizeof(BLOCKFOOT);
- }
- if (rc > len) {
- rc = len;
- }
- (*ifc->sf_read) (ifc, b * UFLASH_BLOCK_UNITS + unit + ifc->sf_rsvbot, upos, data, rc);
- return rc;
- }
- /*!
- * \brief Write unit.
- */
- static int FlashUnitWrite(NUTSERIALFLASH * ifc, blknum_t b, sf_unit_t unit, int upos, const void *data, int len)
- {
- int rc = ifc->sf_unit_size - upos;
- if (unit == UFLASH_BLOCK_UNITS - 1) {
- rc -= sizeof(BLOCKFOOT);
- }
- if (rc > len) {
- rc = len;
- }
- (*ifc->sf_write) (ifc, b * UFLASH_BLOCK_UNITS + unit + ifc->sf_rsvbot, upos, data, rc);
- return rc;
- }
- /*!
- * \brief Verify CRC of a given block.
- */
- static int FlashCheckBlock(NUTSERIALFLASH * ifc, blknum_t b)
- {
- return (*ifc->sf_check) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, UFLASH_BLOCK_UNITS);
- }
- /*!
- * \brief Read header of a given block.
- */
- static int FlashReadBlockHead(NUTSERIALFLASH * ifc, blknum_t b, BLOCKHEAD * bh)
- {
- return (*ifc->sf_read) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, 0, bh, sizeof(BLOCKHEAD));
- }
- /*!
- * \brief Write header of a given block.
- */
- static int FlashWriteBlockHead(NUTSERIALFLASH * ifc, blknum_t b, BLOCKHEAD * bh)
- {
- /* Update the block's version number. */
- bh->bh_version++;
- return (*ifc->sf_write) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, 0, bh, sizeof(BLOCKHEAD));
- }
- /*!
- * \brief Read entry of a given block.
- */
- static int FlashReadEntry(NUTSERIALFLASH * ifc, blknum_t b, ENTRYHEAD * eh, char **name)
- {
- int rc;
- /* Read the entry header, which contains the name length. */
- rc = (*ifc->sf_read) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, sizeof(BLOCKHEAD), eh, sizeof(ENTRYHEAD));
- if (rc == 0 && name) {
- *name = malloc(eh->eh_nlen + 1);
- if (*name == NULL) {
- rc = -1;
- } else {
- *(*name + eh->eh_nlen) = '\0';
- rc = (*ifc->sf_read) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, sizeof(BLOCKHEAD) + sizeof(ENTRYHEAD), *name,
- eh->eh_nlen);
- }
- }
- return rc;
- }
- /*!
- * \brief Compare entry name of a given block.
- */
- static int FlashBlockCmpEntryName(NUTSERIALFLASH * ifc, blknum_t b, const char *name, uint8_t len)
- {
- return (*ifc->sf_compare) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, sizeof(BLOCKHEAD) + sizeof(ENTRYHEAD), name, len);
- }
- /*!
- * \brief Write entry of a given block.
- */
- static int FlashWriteEntry(NUTSERIALFLASH * ifc, blknum_t b, const ENTRYHEAD * eh, const char *name)
- {
- int rc;
- rc = (*ifc->sf_write) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, sizeof(BLOCKHEAD), eh, sizeof(ENTRYHEAD));
- if (rc == 0) {
- rc = (*ifc->sf_write) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, sizeof(BLOCKHEAD) + sizeof(ENTRYHEAD), name,
- eh->eh_nlen);
- }
- return rc;
- }
- /*!
- * \brief Read footer of a given block.
- */
- static int FlashReadBlockFoot(NUTSERIALFLASH * ifc, blknum_t b, BLOCKFOOT * bf)
- {
- return (*ifc->sf_read) (ifc, (b + 1) * UFLASH_BLOCK_UNITS - 1 + ifc->sf_rsvbot, -(int) sizeof(BLOCKFOOT), bf,
- sizeof(BLOCKFOOT));
- }
- /*!
- * \brief Write footer of a given block.
- */
- static int FlashWriteBlockFoot(NUTSERIALFLASH * ifc, blknum_t b, BLOCKFOOT * bf)
- {
- bf->bf_wear++;
- #ifdef UFLASH_USE_TIMESTAMP
- bf->bf_time = time(NULL);
- #endif
- return (*ifc->sf_write) (ifc, (b + 1) * UFLASH_BLOCK_UNITS - 1 + ifc->sf_rsvbot, -(int) sizeof(BLOCKFOOT), bf,
- sizeof(BLOCKFOOT));
- }
- /*!
- * \brief Erase first unit of a given block.
- */
- static int FlashEraseEntry(NUTSERIALFLASH * ifc, blknum_t b)
- {
- return (*ifc->sf_erase) (ifc, b * UFLASH_BLOCK_UNITS + ifc->sf_rsvbot, 1);
- }
- /*!
- * \brief Erase data units of a given block.
- */
- static int FlashEraseBlockData(NUTSERIALFLASH * ifc, blknum_t b)
- {
- return (*ifc->sf_erase) (ifc, b * UFLASH_BLOCK_UNITS + 1 + ifc->sf_rsvbot, UFLASH_BLOCK_UNITS - 1);
- }
- /*!
- * \brief Erase a given number of blocks.
- */
- static int FlashEraseBlocks(NUTSERIALFLASH * ifc, int n)
- {
- return (*ifc->sf_erase) (ifc, ifc->sf_rsvbot, n * UFLASH_BLOCK_UNITS);
- }
- /*!
- * \brief Collect least used blocks.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- *
- * TODO: We may optionally exclude static files from recycling.
- */
- static void CollectLeastUsed(UFLASHVOLUME * vol, uint8_t * usage)
- {
- uint8_t rmin;
- uint8_t rmax;
- blknum_t b;
- /* Determine the range of values. */
- rmin = usage[0];
- rmax = rmin;
- for (b = 1; b < vol->vol_blocks; b++) {
- if (usage[b] < rmin) {
- rmin = usage[b];
- if (rmax - rmin >= 128) {
- break;
- }
- } else if (usage[b] > rmax) {
- rmax = usage[b];
- if (rmax - rmin >= 128) {
- break;
- }
- }
- }
- /*
- ** If the range of values is equal or greater than half of the full
- ** range, then some values overflowed. In this case we shift all
- ** values by half of the full range to get a consecutive sequence.
- */
- if (b < vol->vol_blocks) {
- rmax = 0;
- for (b = 0; b < vol->vol_blocks; b++) {
- usage[b] += 128;
- if (usage[b] > rmax) {
- rmax = usage[b];
- }
- }
- }
- /*
- ** Collect a sorted table of minimums.
- */
- memset(min_used, 0xff, sizeof(min_used));
- for (b = 0; b < vol->vol_blocks; b++) {
- int j = UFLASH_USAGE_CACHE - 1;
- if (min_used[j].use_phyblk > vol->vol_blocks || min_used[j].use_cnt > usage[b]) {
- while (--j >= 0) {
- if (usage[b] >= min_used[j].use_cnt && min_used[j].use_phyblk < vol->vol_blocks) {
- break;
- }
- min_used[j + 1] = min_used[j];
- }
- min_used[j + 1].use_cnt = usage[b];
- min_used[j + 1].use_phyblk = b;
- }
- }
- }
- static blknum_t PhysBlkAllocate(UFLASHVOLUME * vol, int level);
- /*!
- * \brief Move a physical block.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- * \param src Physical source block number. On return, this block will
- * be available again.
- * \param dst Physical destination block number.
- */
- static int PhysBlkMove(UFLASHVOLUME * vol, blknum_t src, blknum_t dst)
- {
- int rc = 0;
- BLOCKHEAD bh;
- BLOCKFOOT bf;
- uint_fast8_t u;
- /* Read the header of the source block. */
- FlashReadBlockHead(vol->vol_ifc, src, &bh);
- /* Read the footer of the destination block. */
- FlashReadBlockFoot(vol->vol_ifc, dst, &bf);
- /* Erase the tail of the destination. */
- FlashEraseBlockData(vol->vol_ifc, dst);
- /* Copy unit by unit. */
- for (u = 0; rc == 0 && u < UFLASH_BLOCK_UNITS; u++) {
- FlashUnitCopy(vol->vol_ifc, src, dst, u);
- /* If this is the first unit, write a new version of the header. */
- if (u == 0) {
- FlashWriteBlockHead(vol->vol_ifc, dst, &bh);
- }
- /* If this is the last unit, write back an updated footer. */
- else if (u == UFLASH_BLOCK_UNITS - 1) {
- FlashWriteBlockFoot(vol->vol_ifc, dst, &bf);
- }
- /* Commit the unit. */
- FlashUnitCommit(vol->vol_ifc, dst, u);
- }
- /* If all done, erase the first unit of the source block. This will
- ** make the block available again. */
- FlashEraseEntry(vol->vol_ifc, src);
- return 0;
- }
- /*!
- * \brief Get the first unused block.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static blknum_t PhysBlkUnused(UFLASHVOLUME * vol)
- {
- BLOCKHEAD bh;
- blknum_t b;
- for (b = 0; b < vol->vol_blocks; b++) {
- FlashReadBlockHead(vol->vol_ifc, b, &bh);
- if (bh.bh_logblk >= vol->vol_blocks) {
- break;
- }
- }
- return b;
- }
- /*!
- * \brief Reallocate an unused physical block.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static int PhysBlkRequest(UFLASHVOLUME * vol, int level, blknum_t n)
- {
- blknum_t b;
- /* Check if this block is in use. */
- for (b = 0; b < vol->vol_blocks; b++) {
- if (vol->vol_l2p[b] == n) {
- blknum_t nn;
- nn = PhysBlkAllocate(vol, level + 1);
- if (nn >= vol->vol_blocks) {
- return -1;
- }
- vol->vol_l2p[b] = nn;
- PhysBlkMove(vol, n, nn);
- break;
- }
- }
- return 0;
- }
- /*!
- * \brief Allocate an unused physical block.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static blknum_t PhysBlkAllocate(UFLASHVOLUME * vol, int level)
- {
- uint8_t *phy_use;
- int i;
- for (;;) {
- if (level || min_used[UFLASH_USAGE_CACHE - (UFLASH_USAGE_CACHE / 4)].use_phyblk < vol->vol_blocks) {
- for (i = 0; i < UFLASH_USAGE_CACHE; i++) {
- blknum_t n = min_used[i].use_phyblk;
- if (n < vol->vol_blocks) {
- min_used[i].use_phyblk = UFLASH_BLOCK_INVALID;
- if (PhysBlkRequest(vol, level, n)) {
- return UFLASH_BLOCK_INVALID;
- }
- return n;
- }
- }
- }
- /* Cache exhausted. */
- phy_use = calloc(vol->vol_blocks, 1);
- if (phy_use == NULL) {
- break;
- }
- for (i = 0; i < vol->vol_blocks; i++) {
- BLOCKFOOT bf;
- FlashReadBlockFoot(vol->vol_ifc, i, &bf);
- phy_use[i] = bf.bf_wear;
- }
- for (i = 0; i < vol->vol_blocks; i++) {
- if (vol->vol_l2p[i] < vol->vol_blocks) {
- phy_use[vol->vol_l2p[i]] += UFLASH_USAGE_CACHE - 1;
- }
- }
- CollectLeastUsed(vol, phy_use);
- free(phy_use);
- if (level) {
- break;
- }
- }
- return PhysBlkUnused(vol);
- }
- /*!
- * \brief Allocate a new logical block.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static blknum_t LogBlkAllocate(UFLASHVOLUME * vol, blknum_t lb)
- {
- int lb_last = lb ? vol->vol_blocks : UFLASH_ENTRIES;
- /* Find an unused logical block. */
- for (; lb < lb_last; lb++) {
- if (vol->vol_l2p[lb] >= vol->vol_blocks) {
- /* Found, assign a physical block. */
- vol->vol_l2p[lb] = PhysBlkAllocate(vol, 0);
- break;
- }
- }
- return lb;
- }
- /*!
- * \brief Reallocate a logical block.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static blknum_t LogBlkReallocate(UFLASHVOLUME * vol, blknum_t lb)
- {
- blknum_t b_old;
- blknum_t b_new;
- /* Allocate a new physical block. Note, that the currently assigned
- ** physical block may move during this allocation. */
- b_new = PhysBlkAllocate(vol, 0);
- if (b_new >= vol->vol_blocks) {
- b_old = b_new;
- } else {
- b_old = vol->vol_l2p[lb];
- vol->vol_l2p[lb] = b_new;
- }
- return b_old;
- }
- /*!
- * \brief Find existing data block by sequence.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static int LogBlkRelease(UFLASHVOLUME * vol, blknum_t lb)
- {
- blknum_t b;
- b = vol->vol_l2p[lb];
- vol->vol_l2p[lb] = UFLASH_BLOCK_INVALID;
- return FlashEraseEntry(vol->vol_ifc, b);
- }
- /*!
- * \brief Find existing data block by sequence.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- * \param lbe Logical block of the entry.
- * \param seq Sequence number to search.
- *
- * \return Logical block number of the requested data block. If this is
- * equal to the number of blocks available, then the sequence
- * doesn't exist.
- */
- static blknum_t EntrySeek(UFLASHVOLUME * vol, blknum_t lbe, blknum_t seq)
- {
- blknum_t lb = lbe;
- /* Data block sequence 0 is the same as the entry block. No need to
- ** search for it. */
- if (seq) {
- /* Walking through the translation table requires exclusive access. */
- NutEventWait(&vol->vol_mutex, 0);
- /* No need to search the entry blocks. */
- for (lb = UFLASH_ENTRIES; lb < vol->vol_blocks; lb++) {
- blknum_t b = vol->vol_l2p[lb];
- /* If this block is allocated, read its header. */
- if (b < vol->vol_blocks) {
- BLOCKHEAD bh;
- FlashReadBlockHead(vol->vol_ifc, b, &bh);
- if (bh.bh_entblk == lbe && bh.bh_entseq == seq) {
- /* Block found. */
- break;
- }
- }
- }
- /* Release the exclusive access. */
- NutEventPost(&vol->vol_mutex);
- }
- return lb;
- }
- /*!
- * \brief Update the position indices.
- */
- static void EntryPosSet(NUTFILE * nfp, long pos)
- {
- uint16_t seq;
- uint32_t off;
- UFLASHENTRY *ent;
- UFLASHVOLUME *vol;
- ent = (UFLASHENTRY *) nfp->nf_fcb;
- vol = (UFLASHVOLUME *) nfp->nf_dev->dev_dcb;
- /* Current position within this entry. */
- ent->ent_pos = pos;
- off = ent->ent_eoff + ent->ent_pos;
- /* Current block sequence within this entry. */
- seq = (uint16_t) (off / (vol->vol_ifc->sf_unit_size * UFLASH_BLOCK_UNITS - (sizeof(BLOCKHEAD) + sizeof(BLOCKFOOT))));
- if (seq != ent->ent_sidx) {
- ent->ent_sidx = seq;
- ent->ent_bidx = UFLASH_BLOCK_INVALID;
- }
- off -= ent->ent_sidx * (vol->vol_ifc->sf_unit_size * UFLASH_BLOCK_UNITS - (sizeof(BLOCKHEAD) + sizeof(BLOCKFOOT)));
- /* Page number within the current block. */
- ent->ent_uidx = (uint16_t) ((off + sizeof(BLOCKHEAD)) / vol->vol_ifc->sf_unit_size);
- off -= ent->ent_uidx * vol->vol_ifc->sf_unit_size;
- /* Position with the current page. */
- ent->ent_upos = (uint16_t) off + sizeof(BLOCKHEAD);
- }
- static void EntryPosInc(NUTFILE * nfp, int step)
- {
- UFLASHENTRY *ent;
- UFLASHVOLUME *vol;
- ent = (UFLASHENTRY *) nfp->nf_fcb;
- vol = (UFLASHVOLUME *) nfp->nf_dev->dev_dcb;
- ent->ent_pos += step;
- ent->ent_upos += step;
- if (ent->ent_uidx < UFLASH_BLOCK_UNITS - 1) {
- step = vol->vol_ifc->sf_unit_size - ent->ent_upos;
- } else {
- step = vol->vol_ifc->sf_unit_size - sizeof(BLOCKFOOT) - ent->ent_upos;
- }
- if (step == 0) {
- ent->ent_uidx++;
- if (ent->ent_uidx >= UFLASH_BLOCK_UNITS) {
- ent->ent_upos = sizeof(BLOCKHEAD);
- ent->ent_uidx = 0;
- ent->ent_sidx++;
- ent->ent_bidx = UFLASH_BLOCK_INVALID;
- } else {
- ent->ent_upos = 0;
- }
- }
- }
- /*!
- * \brief Scan for all blocks associated to a given entry.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static long EntryScan(UFLASHVOLUME * vol, blknum_t lbe, time_t * mod)
- {
- long siz;
- int p;
- int i;
- int off;
- BLOCKHEAD bh;
- blknum_t lb;
- blknum_t lbs = lbe;
- blknum_t seq = 0;
- #ifdef UFLASH_USE_TIMESTAMP
- if (mod) {
- BLOCKFOOT bf;
- FlashReadBlockFoot(vol->vol_ifc, vol->vol_l2p[lbe] / UFLASH_BLOCK_UNITS, &bf);
- *mod = bf.bf_time;
- }
- #endif
- /* Find the highest sequence block. */
- for (lb = UFLASH_ENTRIES; lb < vol->vol_blocks; lb++) {
- p = vol->vol_l2p[lb];
- if (p < vol->vol_blocks) {
- p *= UFLASH_BLOCK_UNITS;
- FlashReadBlockHead(vol->vol_ifc, p / UFLASH_BLOCK_UNITS, &bh);
- if (bh.bh_entblk == lbe) {
- if (seq < bh.bh_entseq) {
- seq = bh.bh_entseq;
- lbs = lb;
- }
- #ifdef UFLASH_USE_TIMESTAMP
- if (mod) {
- BLOCKFOOT bf;
- FlashReadBlockFoot(vol->vol_ifc, p / UFLASH_BLOCK_UNITS, &bf);
- if (*mod < bf.bf_time) {
- *mod = bf.bf_time;
- }
- }
- #endif
- }
- }
- }
- /* Size of full blocks. */
- siz = seq * (vol->vol_ifc->sf_unit_size * UFLASH_BLOCK_UNITS - (sizeof(BLOCKHEAD) + sizeof(BLOCKFOOT)));
- /* Number of bytes in the highest sequence block. */
- p = vol->vol_l2p[lbs] * UFLASH_BLOCK_UNITS;
- off = -(int) sizeof(BLOCKFOOT);
- for (i = UFLASH_BLOCK_UNITS; --i >= 0;) {
- int s = (*vol->vol_ifc->sf_used) (vol->vol_ifc, p + i + vol->vol_ifc->sf_rsvbot, off);
- off = 0;
- if (s) {
- siz += s + i * vol->vol_ifc->sf_unit_size - sizeof(BLOCKHEAD);
- break;
- }
- }
- return siz;
- }
- /*!
- * \brief Find entry by name.
- *
- * This routine can be either used to find an exact match or to find
- * the next entry that starts with the given name.
- *
- * \param vol Pointer to the volume information structure.
- * \param name Full path name of the requested entry. A leading slash
- * will be ignored.
- * \param eh Pointer to the entry header.
- * \param lbs Logical block to start searching. If set to UFLASH_ENTRIES,
- * all entry blocks will be searched for an exact match.
- * Otherwise the search starts at the specified block for the
- * next entry that starts with the given name.
- */
- static int EntrySearchNext(UFLASHVOLUME * vol, const char *name, ENTRYHEAD * eh, blknum_t lbs)
- {
- blknum_t lbe;
- blknum_t b;
- int nlen;
- nlen = strlen(name);
- /* Walking through the translation table requires exclusive access. */
- NutEventWait(&vol->vol_mutex, 0);
- for (lbe = lbs == UFLASH_ENTRIES ? 0 : lbs; lbe < UFLASH_ENTRIES; lbe++) {
- b = vol->vol_l2p[lbe];
- if (b < vol->vol_blocks) {
- /* Read the entry header, which contains the path length. */
- if (FlashReadEntry(vol->vol_ifc, b, eh, NULL) == 0) {
- /* If the path lengths are equal, then compare the names. */
- if (eh->eh_nlen == nlen || (lbs != UFLASH_ENTRIES && eh->eh_nlen >= nlen)) {
- if (FlashBlockCmpEntryName(vol->vol_ifc, b, name, nlen) == 0) {
- NutEventPost(&vol->vol_mutex);
- return lbe;
- }
- }
- }
- }
- }
- NutEventPost(&vol->vol_mutex);
- return UFLASH_ENTRIES;
- }
- /*!
- * \brief Find existing entry by name.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static int EntrySearch(UFLASHVOLUME * vol, const char *name, ENTRYHEAD * eh)
- {
- return EntrySearchNext(vol, name, eh, UFLASH_ENTRIES);
- }
- /*!
- * \brief Erase all data blocks of a given entry.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static int EntryTruncateSeq(UFLASHVOLUME * vol, int lbe)
- {
- blknum_t lb;
- BLOCKHEAD bh;
- for (lb = UFLASH_ENTRIES; lb < vol->vol_blocks; lb++) {
- if (vol->vol_l2p[lb] < vol->vol_blocks) {
- FlashReadBlockHead(vol->vol_ifc, vol->vol_l2p[lb], &bh);
- if (bh.bh_entblk == lbe) {
- LogBlkRelease(vol, lb);
- }
- }
- }
- return 0;
- }
- /*!
- * \brief Truncate an entry.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static int EntryTruncate(UFLASHVOLUME * vol, blknum_t lbe)
- {
- BLOCKHEAD bh;
- ENTRYHEAD eh;
- BLOCKFOOT bf;
- blknum_t b;
- blknum_t b_old;
- char *name = NULL;
- EntryTruncateSeq(vol, lbe);
- /* Allocate a new entry block. */
- b = PhysBlkAllocate(vol, 0);
- if (b >= vol->vol_blocks) {
- return -1;
- }
- b_old = vol->vol_l2p[lbe];
- vol->vol_l2p[lbe] = b;
- /* Read the block footer and erase the remaining units. */
- FlashReadBlockFoot(vol->vol_ifc, b, &bf);
- FlashEraseBlockData(vol->vol_ifc, b);
- /* Copy block and entry headers, incrementing the version.
- * Note, that we cannot use a unit copy, because we also must
- * get rid of the data in the first block. */
- FlashReadBlockHead(vol->vol_ifc, b_old, &bh);
- FlashReadEntry(vol->vol_ifc, b_old, &eh, &name);
- FlashWriteBlockHead(vol->vol_ifc, b, &bh);
- FlashWriteEntry(vol->vol_ifc, b, &eh, name);
- free(name);
- FlashUnitCommit(vol->vol_ifc, b, 0);
- /* Update block footer in the last unit. */
- FlashWriteBlockFoot(vol->vol_ifc, b, &bf);
- FlashUnitCommit(vol->vol_ifc, b, UFLASH_BLOCK_UNITS - 1);
- /* Remove the old entry. */
- FlashEraseEntry(vol->vol_ifc, b_old);
- return 0;
- }
- /*!
- * \brief Create a new entry.
- *
- * \param vol Pointer to the information structure of a mounted volume.
- */
- static int EntryCreate(UFLASHVOLUME * vol, blknum_t lbe, ENTRYHEAD * eh, const char *name)
- {
- BLOCKHEAD bh;
- BLOCKFOOT bf;
- blknum_t b;
- b = vol->vol_l2p[lbe];
- /* Read the footer before erasing the block. */
- FlashReadBlockFoot(vol->vol_ifc, b, &bf);
- FlashEraseBlockData(vol->vol_ifc, b);
- /* Write block header. */
- bh.bh_logblk = lbe;
- bh.bh_version = UFLASH_BLOCK_INVALID;
- bh.bh_entblk = lbe;
- bh.bh_entseq = 0;
- FlashWriteBlockHead(vol->vol_ifc, b, &bh);
- /* Write the entry header and name. */
- FlashWriteEntry(vol->vol_ifc, b, eh, name);
- FlashUnitCommit(vol->vol_ifc, b, 0);
- /* Update block footer. */
- FlashWriteBlockFoot(vol->vol_ifc, b, &bf);
- FlashUnitCommit(vol->vol_ifc, b, UFLASH_BLOCK_UNITS - 1);
- return 0;
- }
- /*!
- * \brief Remove an entry.
- */
- static int UFlashFileRemove(NUTDEVICE * dev, const char *name)
- {
- blknum_t lbe;
- ENTRYHEAD eh;
- UFLASHVOLUME *vol;
- vol = (UFLASHVOLUME *) dev->dev_dcb;
- while (*name == '/') {
- name++;
- }
- lbe = EntrySearch(vol, name, &eh);
- if (lbe >= UFLASH_ENTRIES || vol->vol_l2p[lbe] > vol->vol_blocks) {
- return -1;
- }
- NutEventWait(&vol->vol_mutex, 0);
- EntryTruncateSeq(vol, lbe);
- LogBlkRelease(vol, lbe);
- NutEventPost(&vol->vol_mutex);
- return 0;
- }
- /*!
- * \brief Retrieve the status of an entry.
- */
- static int UFlashFileStatus(NUTDEVICE * dev, const char *name, struct stat *st)
- {
- blknum_t lbe;
- ENTRYHEAD eh;
- UFLASHVOLUME *vol;
- uint_fast8_t partial = 0;
- vol = (UFLASHVOLUME *) dev->dev_dcb;
- while (*name == '/') {
- name++;
- }
- lbe = EntrySearch(vol, name, &eh);
- if (lbe >= UFLASH_ENTRIES || vol->vol_l2p[lbe] > vol->vol_blocks) {
- lbe = EntrySearchNext(vol, name, &eh, 0);
- if (lbe >= UFLASH_ENTRIES || vol->vol_l2p[lbe] > vol->vol_blocks) {
- return -1;
- }
- partial = 1;
- }
- memset(st, 0, sizeof(struct stat));
- if (partial) {
- st->st_mode = 1;
- #ifdef UFLASH_USE_TIMESTAMP
- EntryScan(vol, lbe, &st->st_mtime);
- #endif
- } else {
- NutEventWait(&vol->vol_mutex, 0);
- #ifdef UFLASH_USE_TIMESTAMP
- st->st_size = EntryScan(vol, lbe, &st->st_mtime);
- #else
- st->st_size = EntryScan(vol, lbe, NULL);
- #endif
- NutEventPost(&vol->vol_mutex);
- #if UFLASH_MAX_PATH
- st->st_size -= sizeof(ENTRYHEAD) + UFLASH_MAX_PATH;
- #else
- st->st_size -= sizeof(ENTRYHEAD) + eh.eh_nlen;
- #endif
- }
- return 0;
- }
- /*!
- * \brief Open a directory.
- *
- * \param dev Specifies the file system device.
- * \param dpath Full absolute pathname of the directory to open.
- *
- * \return Pointer to a NUTFILE structure if successful or NUTFILE_EOF otherwise.
- */
- static NUTFILE *UFlashDirOpen(NUTDEVICE * dev, const char *dpath)
- {
- NUTFILE *ndp;
- UFLASHFIND *uff;
- ndp = malloc(sizeof(NUTFILE));
- if (ndp) {
- uff = malloc(sizeof(UFLASHFIND));
- if (uff) {
- uff->uff_lbe = 0;
- uff->uff_path = strdup(dpath + (*dpath == '/'));
- if (uff->uff_path) {
- ndp->nf_dev = dev;
- ndp->nf_fcb = uff;
- return ndp;
- }
- free(uff);
- }
- free(ndp);
- }
- return NUTFILE_EOF;
- }
- /*!
- * \brief Close a directory.
- */
- static int UFlashDirClose(NUTFILE * ndp)
- {
- UFLASHFIND *uff;
- uff = (UFLASHFIND *) ndp->nf_fcb;
- free(uff->uff_path);
- free(uff);
- free(ndp);
- return 0;
- }
- /*!
- * \brief Read next entry of a directory.
- */
- static int UFlashDirRead(DIR * dir)
- {
- struct dirent *ent;
- NUTFILE *ndp;
- NUTDEVICE *dev;
- UFLASHFIND *uff;
- UFLASHVOLUME *vol;
- ENTRYHEAD eh;
- char *name = NULL;
- char *cp;
- ndp = dir->dd_fd;
- uff = (UFLASHFIND *) ndp->nf_fcb;
- dev = ndp->nf_dev;
- vol = (UFLASHVOLUME *) dev->dev_dcb;
- uff->uff_lbe = EntrySearchNext(vol, uff->uff_path, &eh, uff->uff_lbe);
- if (uff->uff_lbe >= UFLASH_ENTRIES) {
- return -1;
- }
- ent = (struct dirent *) dir->dd_buf;
- memset(dir->dd_buf, 0, sizeof(struct dirent));
- FlashReadEntry(vol->vol_ifc, vol->vol_l2p[uff->uff_lbe], &eh, &name);
- cp = strchr(name + strlen(uff->uff_path), '/');
- if (cp) {
- *cp = '\0';
- ent->d_type = 1;
- } else {
- ent->d_type = 0;
- }
- strcpy(ent->d_name, name + strlen(uff->uff_path));
- ent->d_namlen = strlen(name + strlen(uff->uff_path));
- free(name);
- uff->uff_lbe++;
- return 0;
- }
- /*!
- * \brief Rename an entry.
- *
- * \param dev Specifies the file system device.
- * \param old_path Old name and path of the file.
- * \param new_path New name and path of the file.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int UFlashFileRename(NUTDEVICE * dev, const char *old_path, const char *new_path)
- {
- blknum_t lbe;
- blknum_t b;
- blknum_t b_old;
- uint_fast8_t u;
- ENTRYHEAD eh;
- UFLASHVOLUME *vol;
- BLOCKHEAD bh;
- BLOCKFOOT bf;
- NUTASSERT(dev != NULL);
- NUTASSERT(old_path != NULL);
- #if UFLASH_MAX_PATH
- NUTASSERT(strlen(old_path) <= UFLASH_MAX_PATH);
- #endif
- NUTASSERT(new_path != NULL);
- #if UFLASH_MAX_PATH
- NUTASSERT(strlen(new_path) <= UFLASH_MAX_PATH);
- #endif
- vol = (UFLASHVOLUME *) dev->dev_dcb;
- NUTASSERT(vol != NULL);
- vol = (UFLASHVOLUME *) dev->dev_dcb;
- while (*old_path == '/') {
- old_path++;
- }
- while (*new_path == '/') {
- new_path++;
- }
- lbe = EntrySearch(vol, old_path, &eh);
- if (lbe >= UFLASH_ENTRIES || vol->vol_l2p[lbe] > vol->vol_blocks) {
- return -1;
- }
- #if UFLASH_MAX_PATH == 0
- if (eh.eh_nlen < strlen(new_path)) {
- return -1;
- }
- #endif
- eh.eh_nlen = strlen(new_path);
- /* Exclusive access from here. */
- NutEventWait(&vol->vol_mutex, 0);
- /* Allocate a new entry block. */
- b = PhysBlkAllocate(vol, 0);
- if (b >= vol->vol_blocks) {
- /* No more blocks available. */
- NutEventPost(&vol->vol_mutex);
- return -1;
- }
- /* Replace the physical block in the translation table. */
- b_old = vol->vol_l2p[lbe];
- vol->vol_l2p[lbe] = b;
- /* Keep the footer and erase the pages of the new block, except the
- first. The first page of unallocated blocks is already erased. */
- FlashReadBlockFoot(vol->vol_ifc, b, &bf);
- FlashEraseBlockData(vol->vol_ifc, b);
- /* Copy all pages of the entry block. */
- for (u = 0; u < UFLASH_BLOCK_UNITS; u++) {
- FlashUnitCopy(vol->vol_ifc, b_old, b, u);
- /* If this is the first unit, write a new version of the header. */
- if (u == 0) {
- /* Separately copy the block header, because this will
- increment the block's version number. */
- FlashReadBlockHead(vol->vol_ifc, b, &bh);
- FlashWriteBlockHead(vol->vol_ifc, b, &bh);
- /* Fill in the new path name. */
- FlashWriteEntry(vol->vol_ifc, b, &eh, new_path);
- }
- /* If this is the last unit, write back an updated footer. */
- else if (u == UFLASH_BLOCK_UNITS - 1) {
- FlashWriteBlockFoot(vol->vol_ifc, b, &bf);
- }
- /* Commit the unit. */
- FlashUnitCommit(vol->vol_ifc, b, u);
- }
- /* Finally remove the old entry. */
- FlashEraseEntry(vol->vol_ifc, b_old);
- /* Release exclusive access. */
- NutEventPost(&vol->vol_mutex);
- return 0;
- }
- /*!
- * \brief Retrieve the size of a previously opened UFLASH 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 UFlashFileOpen().
- *
- * \return Size of the file.
- */
- static long UFlashFileSize(NUTFILE * nfp)
- {
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_fcb != NULL);
- return ((UFLASHENTRY *) nfp->nf_fcb)->ent_size;
- }
- /*!
- * \brief Open a UFLASH 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.
- * \param mode Operation mode.
- * \param acc File attribute, currently ignored.
- *
- * \return Pointer to a NUTFILE structure if successful or NUTFILE_EOF otherwise.
- */
- static NUTFILE *UFlashFileOpen(NUTDEVICE * dev, const char *path, int mode, int acc)
- {
- int rc = -1;
- NUTFILE *nfp = NUTFILE_EOF;
- UFLASHENTRY *ent;
- UFLASHVOLUME *vol;
- blknum_t lbe;
- ENTRYHEAD eh;
- long datsiz = 0;
- NUTASSERT(dev != NULL);
- NUTASSERT(path != NULL);
- vol = (UFLASHVOLUME *) dev->dev_dcb;
- NUTASSERT(vol != NULL);
- while (*path == '/') {
- path++;
- }
- /* Allocate a private entry information structure. */
- ent = calloc(1, sizeof(UFLASHENTRY));
- if (ent == NULL) {
- return NUTFILE_EOF;
- }
- ent->ent_mode = mode;
- lbe = EntrySearch(vol, path, &eh);
- NutEventWait(&vol->vol_mutex, 0);
- /*
- ** Entry exists.
- */
- if (lbe < UFLASH_ENTRIES) {
- if ((mode & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) {
- errno = EEXIST;
- } else {
- /* Truncate. */
- if ((mode & _O_TRUNC) == _O_TRUNC) {
- rc = EntryTruncate(vol, lbe);
- }
- /* Do not truncate. */
- else {
- rc = 0;
- datsiz = EntryScan(vol, lbe, NULL);
- #if UFLASH_MAX_PATH
- datsiz -= sizeof(ENTRYHEAD) + UFLASH_MAX_PATH;
- #else
- datsiz -= sizeof(ENTRYHEAD) + eh.eh_nlen;
- #endif
- }
- }
- }
- /*
- ** Entry doesn't exist.
- */
- else {
- if ((mode & _O_CREAT) == _O_CREAT) {
- lbe = LogBlkAllocate(vol, 0);
- if (lbe < UFLASH_ENTRIES) {
- eh.eh_nlen = strlen(path);
- eh.eh_attr = 0;
- rc = EntryCreate(vol, lbe, &eh, path);
- }
- } else {
- errno = ENOENT;
- }
- }
- if (rc) {
- free(ent);
- } else {
- ent->ent_attr = eh.eh_attr;
- ent->ent_namlen = eh.eh_nlen;
- #if UFLASH_MAX_PATH
- ent->ent_eoff = sizeof(ENTRYHEAD) + UFLASH_MAX_PATH;
- #else
- ent->ent_eoff = sizeof(ENTRYHEAD) + eh.eh_nlen;
- #endif
- ent->ent_id = lbe;
- ent->ent_size = datsiz;
- /* Allocate a file information structure. */
- nfp = malloc(sizeof(NUTFILE));
- if (nfp == NULL) {
- free(ent);
- NutEventPost(&vol->vol_mutex);
- return NUTFILE_EOF;
- }
- nfp->nf_dev = dev;
- nfp->nf_fcb = ent;
- if ((mode & _O_APPEND) == _O_APPEND) {
- ent->ent_bidx = UFLASH_BLOCK_INVALID;
- EntryPosSet(nfp, datsiz);
- } else {
- ent->ent_bidx = lbe;
- EntryPosSet(nfp, 0);
- }
- }
- NutEventPost(&vol->vol_mutex);
- return nfp;
- }
- /*!
- * \brief Close a UFLASH file.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to UFlashFileOpen().
- *
- * \return Always 0.
- */
- static int UFlashFileClose(NUTFILE * nfp)
- {
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_dev != NULL);
- NUTASSERT(nfp->nf_fcb != NULL);
- free(nfp->nf_fcb);
- free(nfp);
- return 0;
- }
- /*!
- * \brief Read data from a UFLASH file.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to UFlashFileOpen().
- * \param data 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.
- */
- static int UFlashFileRead(NUTFILE * nfp, void *data, int size)
- {
- int u;
- int step;
- int rc;
- uint8_t *dptr;
- UFLASHENTRY *ent;
- UFLASHVOLUME *vol;
- /* Ignore input flush. */
- if (data == NULL || size == 0) {
- return 0;
- }
- dptr = (uint8_t *) data;
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_dev != NULL);
- ent = (UFLASHENTRY *) nfp->nf_fcb;
- NUTASSERT(ent != NULL);
- vol = (UFLASHVOLUME *) nfp->nf_dev->dev_dcb;
- NUTASSERT(vol != NULL);
- /* Do not read beyond the file end. */
- if (size > (int) (ent->ent_size - ent->ent_pos)) {
- size = (int) (ent->ent_size - ent->ent_pos);
- }
- /*
- ** Loop until we read the requested size.
- */
- for (rc = 0; rc < size;) {
- /* Is the current logical block number still valid? */
- if (ent->ent_bidx >= vol->vol_blocks) {
- /* If not, seek for the right block. */
- ent->ent_bidx = EntrySeek(vol, ent->ent_id, ent->ent_sidx);
- if (ent->ent_bidx >= vol->vol_blocks) {
- /* Seek failed. */
- errno = EIO;
- return -1;
- }
- }
- /* Read unit by unit. */
- for (u = ent->ent_uidx; rc < size && u < UFLASH_BLOCK_UNITS; u++) {
- NutEventWait(&vol->vol_mutex, 0);
- step = FlashUnitRead(vol->vol_ifc, vol->vol_l2p[ent->ent_bidx], u, ent->ent_upos, dptr, size - rc);
- NutEventPost(&vol->vol_mutex);
- if (step < 0) {
- /* Read failed. */
- errno = EIO;
- return -1;
- }
- /* Increment pointers and indices. */
- rc += step;
- dptr += step;
- EntryPosInc(nfp, step);
- }
- }
- return rc;
- }
- /*!
- * \brief Write data to a UFLASH file.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to UFlashFileOpen().
- * \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.
- */
- static int UFlashFileWrite(NUTFILE * nfp, const void *data, int len)
- {
- int u;
- blknum_t b;
- blknum_t b_old;
- int step;
- int siz = len;
- uint8_t *dptr;
- BLOCKHEAD bh;
- BLOCKFOOT bf;
- UFLASHENTRY *ent;
- UFLASHVOLUME *vol;
- /* Ignore output flush. */
- if (data == NULL || len == 0) {
- return 0;
- }
- dptr = (uint8_t *) data;
- /* Sanity check. */
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_fcb != NULL);
- NUTASSERT(nfp->nf_dev != NULL);
- NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
- ent = (UFLASHENTRY *) nfp->nf_fcb;
- vol = (UFLASHVOLUME *) nfp->nf_dev->dev_dcb;
- /*
- ** Loop until all data has been written.
- */
- while (len) {
- /* Is the current logical block number still valid? */
- if (ent->ent_bidx >= vol->vol_blocks) {
- /* If not, seek for the right block. The routine will return an
- ** invalid block number, if we need a new block. */
- ent->ent_bidx = EntrySeek(vol, ent->ent_id, ent->ent_sidx);
- }
- /*
- ** If the current logical block exists, then we reallocate a
- ** physical block.
- */
- NutEventWait(&vol->vol_mutex, 0);
- if (ent->ent_bidx < vol->vol_blocks) {
- b_old = LogBlkReallocate(vol, ent->ent_bidx);
- if (b_old >= vol->vol_blocks) {
- /* No more blocks available. */
- NutEventPost(&vol->vol_mutex);
- return -1;
- }
- /* Read header of old block and increment the version number. */
- FlashReadBlockHead(vol->vol_ifc, b_old, &bh);
- }
- /*
- ** If the current logical block doesn't exists, then we allocate
- ** a new one.
- */
- else {
- ent->ent_bidx = LogBlkAllocate(vol, UFLASH_ENTRIES);
- if (ent->ent_bidx >= vol->vol_blocks) {
- /* No more blocks available. */
- NutEventPost(&vol->vol_mutex);
- return -1;
- }
- /* No reallocation, old and new are the same block. */
- b_old = vol->vol_l2p[ent->ent_bidx];
- /* Initial block header. */
- bh.bh_logblk = ent->ent_bidx;
- bh.bh_entblk = ent->ent_id;
- bh.bh_entseq = ent->ent_sidx;
- bh.bh_version = UFLASH_BLOCK_INVALID;
- }
- /* Current physical block. */
- b = vol->vol_l2p[ent->ent_bidx];
- /* Read the block footer. */
- FlashReadBlockFoot(vol->vol_ifc, b, &bf);
- /* The first unit had been erased when the block was released.
- ** Now erase the remaining units. */
- FlashEraseBlockData(vol->vol_ifc, b);
- /*
- ** Write unit by unit.
- */
- for (u = 0; u < UFLASH_BLOCK_UNITS; u++) {
- /* Make a copy of the old unit. If old and new are
- ** the same, this won't hurt. */
- FlashUnitCopy(vol->vol_ifc, b_old, b, u);
- /* Write the block header to the first unit. */
- if (u == 0) {
- FlashWriteBlockHead(vol->vol_ifc, b, &bh);
- }
- /* If this is the current unit to write to, fill it with data. */
- if (len && u == ent->ent_uidx) {
- step = FlashUnitWrite(vol->vol_ifc, b, u, ent->ent_upos, dptr, len);
- /* Increment pointers and indices. */
- len -= step;
- dptr += step;
- EntryPosInc(nfp, step);
- if (ent->ent_size < ent->ent_pos) {
- ent->ent_size = ent->ent_pos;
- }
- }
- /* Write the block footer to the last unit. */
- if (u == UFLASH_BLOCK_UNITS - 1) {
- FlashWriteBlockFoot(vol->vol_ifc, b, &bf);
- }
- FlashUnitCommit(vol->vol_ifc, b, u);
- }
- /* If we reallocated the block, then release the old one now.
- ** It is essential to do this after having successfully written
- ** the new block. If the new block with a higher version number
- ** remains incomplete, then the old version still exists and the
- ** incomplete block will be removed during mounting. */
- if (b != b_old) {
- FlashEraseEntry(vol->vol_ifc, b_old);
- }
- NutEventPost(&vol->vol_mutex);
- }
- return siz;
- }
- #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 UFlashFileWrite() except that the data is located in
- * program memory.
- *
- * \param nfp Pointer to a \ref NUTFILE structure, obtained by a previous
- * call to RawFsFileOpen().
- * \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.
- */
- static int UFlashFileWrite_P(NUTFILE * nfp, PGM_P buffer, int len)
- {
- return -1;
- }
- #endif
- /*!
- * \brief Move file pointer to a specified position.
- *
- * Moving beyond the current file size is not supported.
- *
- * \param nfp File descriptor.
- * \param pos Requested file position.
- * \param whence Positioning directive.
- *
- * \return 0 on success, -1 otherwise. In the latter case the position
- * is unspecified.
- */
- static int UFlashFileSeek(NUTFILE * nfp, long *pos, int whence)
- {
- int rc = 0;
- long npos;
- UFLASHENTRY *ent;
- NUTASSERT(nfp != NULL);
- NUTASSERT(nfp->nf_fcb != NULL);
- ent = (UFLASHENTRY *) nfp->nf_fcb;
- NUTASSERT(pos != NULL);
- npos = *pos;
- switch (whence) {
- case SEEK_CUR:
- /* Relative to current position. */
- npos += ent->ent_pos;
- break;
- case SEEK_END:
- /* Relative to file end. */
- npos += ent->ent_size;
- break;
- }
- /* Make sure that we are within limits. */
- if (npos < 0 || npos > (long) ent->ent_size) {
- errno = EINVAL;
- rc = -1;
- } else {
- EntryPosSet(nfp, npos);
- *pos = npos;
- }
- return rc;
- }
- /*!
- * \brief Mount a UFLASH volume.
- *
- * \param dev Specifies the file system device.
- *
- * \return 0 on success or -1 in case of an error.
- */
- static int UFlashMount(NUTDEVICE * dev)
- {
- int b;
- BLOCKHEAD bh;
- UFLASHVOLUME *vol;
- NUTASSERT(dev != NULL);
- NUTASSERT(dev->dev_icb != NULL);
- NUTASSERT(dev->dev_dcb == NULL);
- /* Allocate the volume information structure */
- vol = calloc(1, sizeof(UFLASHVOLUME));
- if (vol == NULL) {
- return -1;
- }
- dev->dev_dcb = vol;
- vol->vol_ifc = (NUTSERIALFLASH *) dev->dev_icb;
- /* Determine the total number of blocks. Honor any reserved units. */
- vol->vol_blocks =
- (blknum_t) ((vol->vol_ifc->sf_units - vol->vol_ifc->sf_rsvbot - vol->vol_ifc->sf_rsvtop) / UFLASH_BLOCK_UNITS);
- if (vol->vol_blocks > UFLASH_MAX_BLOCKS) {
- vol->vol_blocks = UFLASH_MAX_BLOCKS;
- }
- /* Allocate the block translation table. */
- vol->vol_l2p = malloc(vol->vol_blocks * sizeof(blknum_t));
- if (vol->vol_l2p == NULL) {
- free(vol);
- return -1;
- }
- memset(vol->vol_l2p, 0xff, vol->vol_blocks * sizeof(blknum_t));
- memset(min_used, 0xff, sizeof(min_used));
- /*
- ** Fill the block translation table.
- */
- for (b = 0; b < vol->vol_blocks; b++) {
- FlashReadBlockHead(vol->vol_ifc, b, &bh);
- /* Check if this is a logical block. */
- if (bh.bh_logblk < vol->vol_blocks) {
- /* Verify the CRC. */
- if (FlashCheckBlock(vol->vol_ifc, b)) {
- /* Bad block, remove it. */
- FlashEraseEntry(vol->vol_ifc, b);
- /* Mark the volume fixed. */
- vol->vol_attrib |= UFLASH_VOLF_FIXED;
- } else {
- /* Check if we already found another version. */
- if (vol->vol_l2p[bh.bh_logblk] < vol->vol_blocks) {
- BLOCKHEAD bho;
- /* Read the previous entry. */
- FlashReadBlockHead(vol->vol_ifc, vol->vol_l2p[bh.bh_logblk], &bho);
- /* Compare versions. */
- if (bho.bh_version > bh.bh_version) {
- if (bho.bh_version - bh.bh_version > vol->vol_blocks) {
- /* The current version overflowed. */
- FlashEraseEntry(vol->vol_ifc, b);
- } else {
- FlashEraseEntry(vol->vol_ifc, vol->vol_l2p[bh.bh_logblk]);
- vol->vol_l2p[bh.bh_logblk] = b;
- }
- } else if (bh.bh_version - bho.bh_version < vol->vol_blocks) {
- /* The current version is higher. */
- FlashEraseEntry(vol->vol_ifc, b);
- } else {
- FlashEraseEntry(vol->vol_ifc, vol->vol_l2p[bh.bh_logblk]);
- vol->vol_l2p[bh.bh_logblk] = b;
- }
- /* Mark the volume fixed. */
- vol->vol_attrib |= UFLASH_VOLF_FIXED;
- } else {
- /* First version of this block. */
- vol->vol_l2p[bh.bh_logblk] = b;
- }
- }
- }
- }
- /* Initialize the mutex. */
- NutEventPost(&vol->vol_mutex);
- return 0;
- }
- /*!
- * \brief Unmount a UFLASH volume.
- *
- * \param dev Specifies the file system device.
- *
- * \return Always 0.
- */
- static int UFlashUnmount(NUTDEVICE * dev)
- {
- UFLASHVOLUME *vol;
- /* Sanity check. */
- NUTASSERT(dev != NULL);
- /* Get the volume information structure. */
- NUTASSERT(dev->dev_dcb != NULL);
- vol = (UFLASHVOLUME *) dev->dev_dcb;
- /* Release allocated heap space. */
- NUTASSERT(vol->vol_l2p != NULL);
- free(vol->vol_l2p);
- free(vol);
- /* Mark the file system driver unmounted. */
- dev->dev_dcb = NULL;
- return 0;
- }
- /*!
- * \brief Attach an UFLASH file system to a serial flash device.
- *
- * This function will initialize the hardware and mount the
- * volume.
- *
- * \param dev Specifies the file system device.
- * \param sfi Specifies the serial flash interface.
- * \param bus Specifies the SPI bus driver.
- *
- * \return 0 on success, -1 on errors.
- */
- int UFlashAttach(NUTDEVICE * dev, NUTSERIALFLASH * sfi, NUTSPIBUS * bus)
- {
- /* Sanity checks. */
- NUTASSERT(dev != NULL);
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_node != NULL);
- NUTASSERT(sfi->sf_init != NULL);
- NUTASSERT(bus != NULL);
- NUTASSERT(bus->bus_initnode != NULL);
- sfi->sf_node->node_bus = bus;
- if ((*bus->bus_initnode) (sfi->sf_node)) {
- return -1;
- }
- NutEventPost(&sfi->sf_node->node_bus->bus_mutex);
- if ((*sfi->sf_init) (sfi) == 0) {
- dev->dev_icb = (void *) sfi;
- if (UFlashMount(dev) == 0) {
- return 0;
- }
- dev->dev_icb = NULL;
- }
- return -1;
- }
- /*!
- * \brief Detach an UFLASH file system from a serial flash device.
- *
- * This function will unmount the volume and release all occupied resources.
- *
- * \param dev Specifies the file system device.
- *
- * \return 0 on success, -1 on errors.
- */
- void UFlashDetach(NUTDEVICE * dev)
- {
- NUTSERIALFLASH *sfi;
- UFlashUnmount(dev);
- /* Release and detach the serial flash interface. */
- sfi = (NUTSERIALFLASH *) dev->dev_icb;
- NUTASSERT(sfi != NULL);
- NUTASSERT(sfi->sf_exit != NULL);
- (*sfi->sf_exit) (sfi);
- dev->dev_icb = NULL;
- }
- /*!
- * \brief Format an UFLASH volume.
- *
- * Formatting an UFLASH volume is easy: All blocks are simply erased.
- * Therefore, formatting is required only to remove existing contents,
- * which is incompatible with the current format options. The caller
- * must make sure, that the volume is not mounted.
- *
- * \param dev Specifies the file system device.
- * \param sfi Specifies the serial flash interface.
- * \param bus Specifies the SPI bus driver.
- *
- * \return 0 on success, -1 on errors.
- */
- int UFlashFormat(NUTDEVICE * dev, NUTSERIALFLASH * sfi, NUTSPIBUS * bus)
- {
- int rc;
- blknum_t n;
- /* Sanity checks. */
- NUTASSERT(dev != NULL);
- NUTASSERT(sfi != NULL);
- NUTASSERT(dev->dev_dcb == NULL);
- sfi->sf_node->node_bus = bus;
- if ((*sfi->sf_node->node_bus->bus_initnode) (sfi->sf_node)) {
- return -1;
- }
- NutEventPost(&sfi->sf_node->node_bus->bus_mutex);
- rc = (*sfi->sf_init) (sfi);
- if (rc == 0) {
- n = (blknum_t) ((sfi->sf_units - sfi->sf_rsvbot - sfi->sf_rsvtop) / UFLASH_BLOCK_UNITS);
- if (n > UFLASH_MAX_BLOCKS) {
- n = UFLASH_MAX_BLOCKS;
- }
- FlashEraseBlocks(sfi, n);
- (*sfi->sf_exit) (sfi);
- }
- return 0;
- }
- /*!
- * \brief UFLASH specific functions.
- *
- * \param dev Identifies the file system device that receives the
- * control function.
- * \param req Requested control function. May be set to one of the
- * following constants:
- * - FS_STATUS
- * - FS_DIR_CREATE, ignored
- * - FS_DIR_REMOVE, ignored
- * - FS_DIR_OPEN
- * - FS_DIR_CLOSE
- * - FS_DIR_READ
- * - FS_FILE_DELETE
- * - FS_FILE_SEEK
- * - FS_RENAME
- * - FS_VOL_MOUNT
- * - FS_VOL_UNMOUNT
- * \param conf Points to a buffer that contains any data required for
- * the given control function or receives data from that
- * function.
- *
- * \return 0 on success, -1 otherwise.
- */
- static int UFlashIOCtl(NUTDEVICE * dev, int req, void *conf)
- {
- int rc = -1;
- switch (req) {
- case FS_STATUS:
- NUTASSERT(conf != NULL);
- {
- FSCP_STATUS *par = (FSCP_STATUS *) conf;
- rc = UFlashFileStatus(dev, par->par_path, par->par_stp);
- }
- break;
- case FS_DIR_CREATE:
- rc = 0;
- break;
- case FS_DIR_REMOVE:
- rc = 0;
- break;
- case FS_DIR_OPEN:
- /* Open a directory for reading entries. */
- NUTASSERT(conf != NULL);
- {
- DIR *dir = (DIR *) conf;
- if ((dir->dd_fd = UFlashDirOpen(dev, dir->dd_buf)) != NUTFILE_EOF) {
- rc = 0;
- }
- }
- break;
- case FS_DIR_CLOSE:
- NUTASSERT(conf != NULL);
- rc = UFlashDirClose(((DIR *) conf)->dd_fd);
- break;
- case FS_DIR_READ:
- rc = UFlashDirRead((DIR *) conf);
- break;
- case FS_FILE_DELETE:
- rc = UFlashFileRemove(dev, (char *) conf);
- break;
- case FS_FILE_SEEK:
- NUTASSERT(conf != NULL);
- UFlashFileSeek((NUTFILE *) ((IOCTL_ARG3 *) conf)->arg1, /* */
- (long *) ((IOCTL_ARG3 *) conf)->arg2, /* */
- (int) ((IOCTL_ARG3 *) conf)->arg3);
- break;
- case FS_RENAME:
- /* Rename an existing file. */
- NUTASSERT(conf != NULL);
- {
- FSCP_RENAME *par = (FSCP_RENAME *) conf;
- rc = UFlashFileRename(dev, par->par_old, par->par_new);
- }
- break;
- case FS_VOL_MOUNT:
- /* Mount a volume. */
- rc = UFlashMount(dev);
- break;
- case FS_VOL_UNMOUNT:
- /* Unmount a volume. */
- rc = UFlashUnmount(dev);
- break;
- }
- return rc;
- }
- /*!
- * \brief Initialize the UFLASH device 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 UFlashInit(NUTDEVICE * dev)
- {
- return 0;
- }
- /*!
- * \brief UFLASH0 file system device.
- */
- NUTDEVICE devUFlash0 = {
- 0, /*!< \brief Pointer to next device, dev_next. */
- {'U', 'F', 'L', 'A', 'S', 'H', '0', 0, 0}
- , /*!< \brief Unique device name, dev_name. */
- IFTYP_FS, /*!< \brief Type of device, dev_type. Obsolete. */
- 0, /*!< \brief Chip select, dev_base. */
- 0, /*!< \brief First interrupt number, dev_irq. Unused. */
- NULL, /*!< \brief Attached serial flash interface, dev_icb. */
- NULL, /*!< \brief Mounted volume information, dev_dcb. */
- UFlashInit, /*!< \brief Driver initialization routine, dev_init. */
- UFlashIOCtl, /*!< \brief Driver specific control function, dev_ioctl. */
- UFlashFileRead, /*!< \brief Read data from a file, dev_read. */
- UFlashFileWrite, /*!< \brief Write data to a file, dev_write. */
- #ifdef __HARVARD_ARCH__
- UFlashFileWrite_P, /*!< \brief Write data from program space to a file, dev_write_P. */
- #endif
- UFlashFileOpen, /*!< \brief Open a file, dev_open. */
- UFlashFileClose, /*!< \brief Close a file, dev_close. */
- UFlashFileSize, /*!< \brief Return file size, dev_size. */
- NULL, /*!< \brief Select function, optional, not yet implemented */
- };
- /*!
- * \brief UFLASH1 file system device.
- */
- NUTDEVICE devUFlash1 = {
- 0, /*!< \brief Pointer to next device, dev_next. */
- {'U', 'F', 'L', 'A', 'S', 'H', '1', 0, 0}
- , /*!< \brief Unique device name, dev_name. */
- IFTYP_FS, /*!< \brief Type of device, dev_type. Obsolete. */
- 1, /*!< \brief Chip select, dev_base. */
- 0, /*!< \brief First interrupt number, dev_irq. Unused. */
- NULL, /*!< \brief Attached serial flash interface, dev_icb. */
- NULL, /*!< \brief Mounted volume information, dev_dcb. */
- UFlashInit, /*!< \brief Driver initialization routine, dev_init. */
- UFlashIOCtl, /*!< \brief Driver specific control function, dev_ioctl. */
- UFlashFileRead, /*!< \brief Read data from a file, dev_read. */
- UFlashFileWrite, /*!< \brief Write data to a file, dev_write. */
- #ifdef __HARVARD_ARCH__
- UFlashFileWrite_P, /*!< \brief Write data from program space to a file, dev_write_P. */
- #endif
- UFlashFileOpen, /*!< \brief Open a file, dev_open. */
- UFlashFileClose, /*!< \brief Close a file, dev_close. */
- UFlashFileSize, /*!< \brief Return file size, dev_size. */
- NULL, /*!< \brief Select function, optional, not yet implemented */
- };
- /*@}*/
|