# HG changeset patch # User nickg # Date 1074522901 0 # Node ID f2453fae08d493eb8df52081ec71d038fd36ab5d # Parent c9e09b1c18cbe4c5f366386561f5cc5049a5f833 Added support for FAT filesystem. Several packages, see the individual ChangeLogs for details. diff --git a/packages/ChangeLog b/packages/ChangeLog --- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,9 @@ +2004-01-19 Nick Garnett + + * ecos.db: Added FAT12/16 filesystem packages. + + * NEWS: Added line about FAT filesystem support. + 2003-11-22 Andrew Lunn * ecos.db: Added libipsec package. diff --git a/packages/NEWS b/packages/NEWS --- a/packages/NEWS +++ b/packages/NEWS @@ -1,3 +1,5 @@ +* Added support for FAT12/16 filesystems. Contributed by + Savin Zlobec of Elatec Engineering. * Support for Spanning Tree Protocol in the old OpenBSD stack * Add support for GPS-4020 (embedded ARM7 GPS receiver/controller) * Basic IPSEC and libipsec support. diff --git a/packages/devs/disk/synth/current/ChangeLog b/packages/devs/disk/synth/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/devs/disk/synth/current/ChangeLog @@ -0,0 +1,56 @@ +2004-01-15 Nick Garnett + + * cdl/synthdisk.cdl: + * src/synthdisk.c: + Added _FILENAME option for disk instances to map device to an + arbitrary cinfigured file. + + * src/synthdisk.c: Removed block_pos arguments from read + and write calls: it is not necessary. + +2003-09-01 Savin Zlobec + + * cdl/synthdisk.cdl: + * src/synthdisk.c: + Updated to work with the current disk interface. + +2003-08-19 Savin Zlobec + + * cdl/synthdisk.cdl: + * src/synthdisk.c: + A synthetic block device driver. + + + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/packages/devs/disk/synth/current/cdl/synthdisk.cdl b/packages/devs/disk/synth/current/cdl/synthdisk.cdl new file mode 100644 --- /dev/null +++ b/packages/devs/disk/synth/current/cdl/synthdisk.cdl @@ -0,0 +1,166 @@ +# ==================================================================== +# +# synthdisk.cdl +# +# Synthetic target disk package. +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2003 Savin Zlobec +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): savin +# Contributors: +# Date: 2003-06-18 +# +#####DESCRIPTIONEND#### +# ==================================================================== + +cdl_package CYGPKG_DEVS_DISK_ECOSYNTH { + display "Synthetic target disk driver" + + parent CYGPKG_IO_DISK_DEVICES + active_if CYGPKG_IO_DISK + active_if CYGPKG_HAL_SYNTH + + cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 { + display "Provide disk 0 device" + flavor bool + default_value 0 + description "Synthetic disk 0 driver." + + cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_NAME { + display "Device name for ecosynth disk 0 device" + flavor data + default_value {"\"/dev/disk0/\""} + description "This is the device name used to access this + device in eCos. Note that the trailing slash + character must be present." + } + + cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME { + display "Linux file name for ecosynth disk 0 device" + flavor data + default_value {"\"disk0.img\""} + description "This is the name of the file in the Linux filesystem that + the driver will use to simulate a real disk. It should have + been formatted with a DOS filesystem image before being used. + The driver may also be pointed at real devices. For example + setting this option to \"/dev/fd\" will cause the driver to access + any real floppy disk inserted in the host's floppy drive." + } + + cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE { + display "Size in bytes for ecosynth disk 0" + flavor data + default_value 10240000 + legal_values 512 to 99999999 + requires { (CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE % 512) == 0 } + description " + This option specifies the size in bytes used + for the ecosynth disk 0 device." + } + + cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR { + display "Expect MBR in synth disk 0 image" + flavor bool + default_value 0 + description " + This option controls the scanning of MBR in synthetic + disk 0 image." + } + + cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_CYLINDERS { + display "Number of cylinders on synth disk 0" + flavor data + default_value 0 + active_if CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR + description " + This option controls the number of cylinders on + synthetic disk 0. It is only needed if + CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR is set." + } + + cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_HEADS { + display "Number of heads on synth disk 0" + flavor data + default_value 0 + active_if CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR + description " + This option controls the number of heads on + synthetic disk 0. It is only needed if + CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR is set." + } + + cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_SECTORS { + display "Number of sectors per track on synth disk 0" + flavor data + default_value 0 + active_if CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR + description " + This option controls the number of sectors per track on + synthetic disk 0.It is only needed if + CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR is set." + } + } + + cdl_component CYGPKG_DEVS_DISK_ECOSYNTH_OPTIONS { + display "Build options" + active_if { CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 } + flavor none + compile -library=libextras.a synthdisk.c + description " + Package-specific build options including control over compiler + flags used only in building this package." + + cdl_option CYGPKG_DEVS_DISK_ECOSYNTH_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building this package. These flags are used in addition + to the set of global flags." + } + cdl_option CYGPKG_DEVS_DISK_ECOSYNTH_CFLAGS_REMOVE { + display "Suppressed compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building this package. These flags are removed from + the set of global flags if present." + } + } +} diff --git a/packages/devs/disk/synth/current/src/synthdisk.c b/packages/devs/disk/synth/current/src/synthdisk.c new file mode 100644 --- /dev/null +++ b/packages/devs/disk/synth/current/src/synthdisk.c @@ -0,0 +1,299 @@ +//========================================================================== +// +// synthdisk.c +// +// Disk device driver for the synthetic target +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Contributors: +// Date: 2003-06-18 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // sprintf + +// ---------------------------------------------------------------------------- + +//#define DEBUG 1 + +// ---------------------------------------------------------------------------- + +typedef struct { + int num; + cyg_uint32 cylinders_num; + cyg_uint32 heads_num; + cyg_uint32 sectors_num; + cyg_uint32 size; + int filefd; + char *filename; +} synth_disk_info_t; + +// ---------------------------------------------------------------------------- + +static cyg_bool synth_disk_init(struct cyg_devtab_entry *tab); + +static Cyg_ErrNo synth_disk_read(disk_channel *chan, + void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + +static Cyg_ErrNo synth_disk_write(disk_channel *chan, + const void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + +static Cyg_ErrNo synth_disk_get_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); + +static Cyg_ErrNo synth_disk_set_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); + +static Cyg_ErrNo synth_disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); + +static DISK_FUNS(synth_disk_funs, + synth_disk_read, + synth_disk_write, + synth_disk_get_config, + synth_disk_set_config +); + +// ---------------------------------------------------------------------------- + +#define SYNTH_DISK_INSTANCE(_number_,_mbr_supp_, _cyl_,_hpt_,_spt_) \ +static synth_disk_info_t synth_disk_info##_number_ = { \ + num: _number_, \ + cylinders_num: _cyl_, \ + heads_num: _hpt_, \ + sectors_num: _spt_, \ + size: CYGNUM_IO_DISK_ECOSYNTH_DISK##_number_##_SIZE, \ + filefd: -1, \ + filename: CYGDAT_IO_DISK_ECOSYNTH_DISK##_number_##_FILENAME \ +}; \ +static DISK_CHANNEL(synth_disk_channel##_number_, \ + synth_disk_funs, \ + synth_disk_info##_number_, \ + _mbr_supp_ \ +); \ +BLOCK_DEVTAB_ENTRY(synth_disk_io##_number_, \ + CYGDAT_IO_DISK_ECOSYNTH_DISK##_number_##_NAME, \ + 0, \ + &cyg_io_disk_devio, \ + synth_disk_init, \ + synth_disk_lookup, \ + &synth_disk_channel##_number_ \ +); + +// ---------------------------------------------------------------------------- + +#ifdef CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 +# ifndef CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR +SYNTH_DISK_INSTANCE(0, false, 0, 0, 0); +# else +SYNTH_DISK_INSTANCE(0, true, CYGIMP_IO_DISK_ECOSYNTH_DISK0_CYLINDERS, + CYGIMP_IO_DISK_ECOSYNTH_DISK0_HEADS, + CYGIMP_IO_DISK_ECOSYNTH_DISK0_SECTORS); +# endif +#endif + +// ---------------------------------------------------------------------------- + +static cyg_bool +synth_disk_init(struct cyg_devtab_entry *tab) +{ + disk_channel *chan = (disk_channel *) tab->priv; + synth_disk_info_t *synth_info = (synth_disk_info_t *) chan->dev_priv; + bool result = true; + + if (chan->init) + return true; + +#ifdef DEBUG + diag_printf("synth disk %d init size=%d\n", + synth_info->num, synth_info->size); +#endif + + synth_info->filefd = cyg_hal_sys_open(synth_info->filename, + CYG_HAL_SYS_O_RDWR, + CYG_HAL_SYS_S_IRWXU|CYG_HAL_SYS_S_IRWXG|CYG_HAL_SYS_S_IRWXO); + + if (-ENOENT == synth_info->filefd) + { + synth_info->filefd = cyg_hal_sys_open(synth_info->filename, + CYG_HAL_SYS_O_RDWR|CYG_HAL_SYS_O_CREAT, 0644); + + if (synth_info->filefd >= 0) + { + unsigned char b = 0x00; + int i; + + for (i = 0; i < synth_info->size; i++) + cyg_hal_sys_write(synth_info->filefd, &b, 1); + } + } + + if (synth_info->filefd < 0) + { + CYG_ASSERT(false, "Can't open/create disk image file"); + return false; + } + + if (result) + { + cyg_disk_identify_t ident; + + ident.serial[0] = '\0'; + ident.firmware_rev[0] = '\0'; + ident.model_num[0] = '\0'; + ident.lba_sectors_num = synth_info->size / 512; + ident.cylinders_num = synth_info->cylinders_num; + ident.heads_num = synth_info->heads_num; + ident.sectors_num = synth_info->sectors_num; + + if (!(chan->callbacks->disk_init)(tab)) + return false; + if (ENOERR != (chan->callbacks->disk_connected)(tab, &ident)) + return false; + } + return result; +} + +static Cyg_ErrNo +synth_disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + disk_channel *chan = (disk_channel *) (*tab)->priv; + return (chan->callbacks->disk_lookup(tab, sub_tab, name)); +} + +static Cyg_ErrNo +synth_disk_read(disk_channel *chan, + void *buf, + cyg_uint32 len, + cyg_uint32 block_num) +{ + synth_disk_info_t *synth_info = (synth_disk_info_t *)chan->dev_priv; + +#ifdef DEBUG + diag_printf("synth disk read block %d\n", block_num); +#endif + + if (synth_info->filefd >= 0) + { + cyg_hal_sys_lseek(synth_info->filefd, + block_num * chan->info->block_size, + CYG_HAL_SYS_SEEK_SET); + cyg_hal_sys_read(synth_info->filefd, buf, len); + return ENOERR; + } + return -EIO; +} + +static Cyg_ErrNo +synth_disk_write(disk_channel *chan, + const void *buf, + cyg_uint32 len, + cyg_uint32 block_num) +{ + synth_disk_info_t *synth_info = (synth_disk_info_t *)chan->dev_priv; + +#ifdef DEBUG + diag_printf("synth disk write block %d\n", block_num); +#endif + + if (synth_info->filefd >= 0) + { + cyg_hal_sys_lseek(synth_info->filefd, + block_num * chan->info->block_size, + CYG_HAL_SYS_SEEK_SET); + cyg_hal_sys_write(synth_info->filefd, buf, len); +// cyg_hal_sys_fdatasync(synth_info->filefd); + return ENOERR; + } + return -EIO; +} + +static Cyg_ErrNo +synth_disk_get_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len) +{ + +#ifdef DEBUG + diag_printf("synth disk get config\n"); +#endif + + return -EINVAL; +} + +static Cyg_ErrNo +synth_disk_set_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len) +{ + +#ifdef DEBUG + diag_printf("synth disk set config\n"); +#endif + + return -EINVAL; +} + +// ---------------------------------------------------------------------------- +// EOF synthdisk.c diff --git a/packages/devs/disk/v85x/edb_v850/current/ChangeLog b/packages/devs/disk/v85x/edb_v850/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/devs/disk/v85x/edb_v850/current/ChangeLog @@ -0,0 +1,46 @@ +2004-01-15 Nick Garnett + + * src/v85x_edb_v850_disk.c: Removed block_pos arguments from read + and write calls: it is not necessary. + +2003-09-01 Savin Zlobec + + * cdl/v85x_edb_v850_disk.cdl: + * src/v85x_edb_v850_disk.c: + * src/cf_ata.h: + A Elatec development board CF disk device driver + + + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/packages/devs/disk/v85x/edb_v850/current/cdl/v85x_edb_v850_disk.cdl b/packages/devs/disk/v85x/edb_v850/current/cdl/v85x_edb_v850_disk.cdl new file mode 100644 --- /dev/null +++ b/packages/devs/disk/v85x/edb_v850/current/cdl/v85x_edb_v850_disk.cdl @@ -0,0 +1,71 @@ +# ==================================================================== +# +# v85x_edb_v850_disk.cdl +# +# Elatec v850 development board disk package. +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2003 Savin Zlobec +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): savin +# Contributors: +# Date: 2003-08-21 +# +#####DESCRIPTIONEND#### +# ==================================================================== + +cdl_package CYGPKG_DEVS_DISK_V85X_EDB_V850 { + display "Disk driver for Elatec v850 development board" + + parent CYGPKG_IO_DISK_DEVICES + active_if CYGPKG_IO_DISK + active_if CYGPKG_HAL_V85X_EDB_V850 + + compile -library=libextras.a v85x_edb_v850_disk.c + + cdl_component CYGVAR_DEVS_DISK_V85X_EDB_V850_DISK0 { + display "Provide disk 0 device" + flavor bool + default_value 0 + description "Elatec v850 development board disk 0 driver." + + cdl_option CYGDAT_IO_DISK_V85X_EDB_V850_DISK0_NAME { + display "Device name for disk 0 device" + flavor data + default_value {"\"/dev/disk0/\""} + } + } +} + +# EOF v85x_edb_v850_disk.cdl diff --git a/packages/devs/disk/v85x/edb_v850/current/src/cf_ata.h b/packages/devs/disk/v85x/edb_v850/current/src/cf_ata.h new file mode 100644 --- /dev/null +++ b/packages/devs/disk/v85x/edb_v850/current/src/cf_ata.h @@ -0,0 +1,183 @@ +#ifndef CYGONCE_CF_ATA_H +#define CYGONCE_CF_ATA_H +//========================================================================== +// +// cf_ata.h +// +// CompactFlash ATA interface definitions +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Contributors: +// Date: 2003-08-21 +// Purpose: +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// ------------------------------------------------------------------------- +// CF Error Register bits +// +#define CF_EREG_BBK (1<<7) // bad block detected +#define CF_EREG_UNC (1<<6) // uncorrectable error +#define CF_EREG_IDNF (1<<4) // req sector ID is in error or cannot be found +#define CF_EREG_ABORT (1<<2) // command aborted +#define CF_EREG_AMNF (1<<0) // general error + +// ------------------------------------------------------------------------- +// CF Status Register bits +// +#define CF_SREG_BUSY (1<<7) // busy (no other bits in this register are valid) +#define CF_SREG_RDY (1<<6) // ready to accept a command +#define CF_SREG_DWF (1<<5) // write fault has occurred +#define CF_SREG_DSC (1<<4) // card is ready +#define CF_SREG_DRQ (1<<3) // information req transfer to/from the host + // through the Data register +#define CF_SREG_CORR (1<<2) // correctable data error (data corrected) +#define CF_SREG_IDX (1<<1) // always 0 +#define CF_SREG_ERR (1<<0) // command has ended in error (see error reg) + +// ------------------------------------------------------------------------- +// CF Device Control Register bits +// +#define CF_DREG_SWRST (1<<2) // soft reset +#define CF_DREG_IEN (1<<1) // interrupt enable (0 - enabled, 1 - disabled) + +// ------------------------------------------------------------------------- +// CF ATA Command Set +// +// FR - Features register +// SC - Sector count register +// SN - Sector number register +// CY - Cylinder registers +// DH - Card/Drive/Head register +// LBA - Logical Block Address Mode Supported +// +// Y - The register contains a valid parameter for this command +// For the Drive/Head Register Y means both the CF Card and head +// parameters are used +// D - Only the CompactFlash Card parameter is valid and not the head parameter +// +// Note FR SC SN CY DH LBA +#define CF_ATA_CHECK_POWER_MODE_CMD 0xE5 // - - - - - D - +#define CF_ATA_EXE_DRIVE_DIAG_CMD 0x90 // - - - - - D - +#define CF_ATA_ERASE_SECTORS_CMD 0xC0 // 2 - Y Y Y Y Y +#define CF_ATA_FORMAT_TRACK_CMD 0x50 // - - Y - Y Y Y +#define CF_ATA_IDENTIFY_DRIVE_CMD 0xEC // - - - - - D - +#define CF_ATA_IDLE_CMD 0xE3 // - - Y - - D - +#define CF_ATA_IDLE_IMMEDIATE_CMD 0xE1 // - - - - - D - +#define CF_ATA_INIT_DRIVE_PARAMS_CMD 0x91 // - - Y - - Y - +#define CF_ATA_READ_BUFFER_CMD 0xE4 // - - - - - D - +#define CF_ATA_READ_MULTIPLE_CMD 0xC4 // - - Y Y Y Y Y +#define CF_ATA_READ_LONG_SECTOR_CMD 0x22 // - - - Y Y Y Y +#define CF_ATA_READ_SECTORS_CMD 0x20 // - - Y Y Y Y Y +#define CF_ATA_READ_VERIFY_SECTORS_CMD 0x40 // - - Y Y Y Y Y +#define CF_ATA_RECALIBRATE_CMD 0x10 // - - - - - D - +#define CF_ATA_REQUEST_SENSE_CMD 0x03 // 1 - - - - D - +#define CF_ATA_SEEK_CMD 0x70 // - - - Y Y Y Y +#define CF_ATA_SET_FEATURES_CMD 0xEF // - Y - - - D - +#define CF_ATA_SET_MULTIPLE_MODE_CMD 0xC6 // - - Y - - D - +#define CF_ATA_SET_SLEEP_MODE_CMD 0xE6 // - - - - - D - +#define CF_ATA_STAND_BY_CMD 0xE2 // - - - - - D - +#define CF_ATA_STAND_BY_IMMEDIATE_CMD 0xE0 // - - - - - D - +#define CF_ATA_TRANSLATE_SECTOR_CMD 0x87 // 1 - Y Y Y Y Y +#define CF_ATA_WEAR_LEVEL_CMD 0xF5 // 1 - - - - Y - +#define CF_ATA_WRITE_BUFFER_CMD 0xE8 // - - - - - D - +#define CF_ATA_WRITE_LONG_SECTOR_CMD 0x32 // - - - Y Y Y Y +#define CF_ATA_WRITE_MULTIPLE_CMD 0xC5 // - - Y Y Y Y Y +#define CF_ATA_WRITE_MULTIPLE_WO_ERASE_CMD 0xCD // 2 - Y Y Y Y Y +#define CF_ATA_WRITE_SECTORS_CMD 0x30 // - - Y Y Y Y Y +#define CF_ATA_WRITE_SECTORS_WO_ERASE_CMD 0x38 // 2 - Y Y Y Y Y +#define CF_ATA_WRITE_VERIFY_SECTORS_CMD 0x3C // - - Y Y Y Y Y +// +// Note 1 : These commands are not standard PC Card ATA commands but +// provide additional functionality +// Note 2 : These commands are not standard PC Card ATA commands and +// these features are no longer supported with the +// introduction of 256 Mbit flash technology. If one +// of these commands is issued, the sectors will be +// erased but there will be no net gain in write +// performance when using the Write Without Erase command. + +// ------------------------------------------------------------------------- +// CF ATA Identify drive command response +// +typedef struct cf_ata_identify_data_t +{ + cyg_uint16 general_conf; // 00 : general configuration + cyg_uint16 num_cylinders; // 01 : number of cylinders (default CHS trans) + cyg_uint16 reserved0; // 02 : reserved + cyg_uint16 num_heads; // 03 : number of heads (default CHS trans) + cyg_uint16 num_ub_per_track; // 04 : number of unformatted bytes per track + cyg_uint16 num_ub_per_sector; // 05 : number of unformatted bytes per sector + cyg_uint16 num_sectors; // 06 : number of sectors per track (default CHS trans) + cyg_uint16 num_card_sectors[2]; // 07-08 : number of sectors per card + cyg_uint16 reserved1; // 09 : reserved + cyg_uint16 serial[10]; // 10-19 : serial number (string) + cyg_uint16 buffer_type; // 20 : buffer type (dual ported) + cyg_uint16 buffer_size; // 21 : buffer size in 512 increments + cyg_uint16 num_ECC_bytes; // 22 : number of ECC bytes passed on R/W Long cmds + cyg_uint16 firmware_rev[4]; // 23-26 : firmware revision (string) + cyg_uint16 model_num[20]; // 27-46 : model number (string) + cyg_uint16 rw_mult_support; // 47 : max number of sectors on R/W multiple cmds + cyg_uint16 reserved2; // 48 : reserved + cyg_uint16 capabilities; // 49 : LBA, DMA, IORDY support indicator + cyg_uint16 reserved3; // 50 : reserved + cyg_uint16 pio_xferc_timing; // 51 : PIO data transfer cycle timing mode + cyg_uint16 dma_xferc_timing; // 52 : single word DMA data transfer cycle timing mode + cyg_uint16 cur_field_validity; // 53 : words 54-58 validity (0 == not valid) + cyg_uint16 cur_cylinders; // 54 : number of current cylinders + cyg_uint16 cur_heads; // 55 : number of current heads + cyg_uint16 cur_spt; // 56 : number of current sectors per track + cyg_uint16 cur_capacity[2]; // 57-58 : current capacity in sectors + cyg_uint16 mult_sectors; // 59 : multiple sector setting + cyg_uint16 lba_total_sectors[2]; // 60-61 : total sectors in LBA mode + cyg_uint16 sw_dma; // 62 : single word DMA support + cyg_uint16 mw_dma; // 63 : multi word DMA support + cyg_uint16 apio_modes; // 64 : advanced PIO transfer mode supported + cyg_uint16 min_dma_timing; // 65 : minimum multiword DMA transfer cycle + cyg_uint16 rec_dma_timing; // 66 : recommended multiword DMA cycle + cyg_uint16 min_pio_timing; // 67 : min PIO transfer time without flow control + cyg_uint16 min_pio_iordy_timing; // 68 : min PIO transfer time with IORDY flow control +// cyg_uint16 reserved4[187]; // 69-255: reserved +} cf_ata_identify_data_t; + +#endif // CYGONCE_CF_ATA_H + +// ------------------------------------------------------------------------- +// EOF cf_ata.h diff --git a/packages/devs/disk/v85x/edb_v850/current/src/v85x_edb_v850_disk.c b/packages/devs/disk/v85x/edb_v850/current/src/v85x_edb_v850_disk.c new file mode 100644 --- /dev/null +++ b/packages/devs/disk/v85x/edb_v850/current/src/v85x_edb_v850_disk.c @@ -0,0 +1,406 @@ +//========================================================================== +// +// v85x_edb_v850_disk.c +// +// Elatec v850 development board disk driver +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Contributors: +// Date: 2003-08-21 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// ---------------------------------------------------------------------------- + +//#define DEBUG 1 + +#ifdef DEBUG +# define D(_args_) diag_printf _args_ +#else +# define D(_args_) +#endif + +// ---------------------------------------------------------------------------- + +#include + +static volatile unsigned char* P10 = (volatile unsigned char*)V850_REG_P10; +static volatile unsigned char* PM10 = (volatile unsigned char*)V850_REG_PM10; +static volatile unsigned char* PU10 = (volatile unsigned char*)V850_REG_PU10; + +#define CF_BASE 0x00380000 + +#define CF_HW_INIT() \ + do { \ + *PU10 |= (1<<4); \ + *PM10 |= (1<<4); \ + *PM10 &= ~2; \ + } while (0) + +#define CF_HW_RESET() \ + do { \ + int i; \ + *P10 |= 2; \ + for (i = 0; i < 500000; i++); \ + *P10 &= ~2; \ + for (i = 0; i < 500000; i++); \ + } while (0) + +#define CF_HW_BUSY_WAIT() \ + do { \ + while (0 == (*P10 & (1<<4))); \ + } while (0) + +// ---------------------------------------------------------------------------- + +typedef struct { + volatile cyg_uint16 *base; +} cf_disk_info_t; + +// ---------------------------------------------------------------------------- + +static cyg_bool cf_disk_init(struct cyg_devtab_entry *tab); + +static Cyg_ErrNo cf_disk_read(disk_channel *chan, + void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + + +static Cyg_ErrNo cf_disk_write(disk_channel *chan, + const void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + +static Cyg_ErrNo cf_disk_get_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); + +static Cyg_ErrNo cf_disk_set_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); + +static Cyg_ErrNo cf_disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); + +static DISK_FUNS(cf_disk_funs, + cf_disk_read, + cf_disk_write, + cf_disk_get_config, + cf_disk_set_config +); + +// ---------------------------------------------------------------------------- + +#define CF_DISK_INSTANCE(_number_,_base_,_mbr_supp_,_name_) \ +static cf_disk_info_t cf_disk_info##_number_ = { \ + base: (volatile cyg_uint16 *)_base_, \ +}; \ +static DISK_CHANNEL(cf_disk_channel##_number_, \ + cf_disk_funs, \ + cf_disk_info##_number_, \ + _mbr_supp_ \ +); \ +BLOCK_DEVTAB_ENTRY(cf_disk_io##_number_, \ + _name_, \ + 0, \ + &cyg_io_disk_devio, \ + cf_disk_init, \ + cf_disk_lookup, \ + &cf_disk_channel##_number_ \ +); + +// ---------------------------------------------------------------------------- + +#ifdef CYGVAR_DEVS_DISK_V85X_EDB_V850_DISK0 +CF_DISK_INSTANCE(0, CF_BASE, true, CYGDAT_IO_DISK_V85X_EDB_V850_DISK0_NAME); +#endif + +// ---------------------------------------------------------------------------- + +static __inline__ cyg_uint8 +get_status(volatile cyg_uint16 *base) +{ + cyg_uint16 val; + HAL_READ_UINT16(base + 7, val); + return (val & 0x00FF); +} + +static __inline__ cyg_uint8 +get_error(volatile cyg_uint16 *base) +{ + cyg_uint16 val; + HAL_READ_UINT16(base + 6, val); + return ((val >> 8) & 0x00FF); +} + +static __inline__ void +set_dctrl(volatile cyg_uint16 *base, cyg_uint8 dbits) +{ + cyg_uint16 val = dbits; + HAL_WRITE_UINT16(base + 7, val); +} + +static cyg_bool +exe_cmd(volatile cyg_uint16 *base, + cyg_uint8 cmd, + cyg_uint8 drive, + cyg_uint32 lba_addr, + cyg_uint8 sec_cnt) +{ + cyg_uint8 lba0_7; + cyg_uint16 lba8_23; + cyg_uint8 lba24_27; + cyg_uint8 drv; + + lba0_7 = lba_addr & 0xFF; + lba8_23 = (lba_addr >> 8) & 0xFFFF; + lba24_27 = (lba_addr >> 24) & 0x0F; + + // drive and LBA addressing mode flag + if (0 == drive) drv = 0x40; + else drv = 0x50; + + CF_HW_BUSY_WAIT(); + + HAL_WRITE_UINT16(base + 1, sec_cnt | (lba0_7 << 8)); + HAL_WRITE_UINT16(base + 2, lba8_23); + HAL_WRITE_UINT16(base + 3, lba24_27 | drv | (cmd << 8)); + + CF_HW_BUSY_WAIT(); + + return (0 == (get_status(base) & CF_SREG_ERR)); +} + +static void +read_data(volatile cyg_uint16 *base, + void *buf, + cyg_uint16 len) +{ + cyg_uint16 *bufp = (cyg_uint16 *)buf; + int i; + + CF_HW_BUSY_WAIT(); + + for (i = 0; i < 512; i += 2) + { + cyg_uint16 data; + HAL_READ_UINT16(base + 4, data); + *bufp++ = data; + } + + CF_HW_BUSY_WAIT(); +} + +static void +write_data(volatile cyg_uint16 *base, + const void *buf, + cyg_uint16 len) +{ + cyg_uint16 *bufp = (cyg_uint16 * const)buf; + int i; + + CF_HW_BUSY_WAIT(); + + for (i = 0; i < 512; i += 2) + { + cyg_uint16 data; + + data = *bufp++; + + HAL_WRITE_UINT16(base + 4, data); + } + + CF_HW_BUSY_WAIT(); +} + +static void +id_strcpy(char *dest, cyg_uint16 *src, cyg_uint16 size) +{ + int i; + + for (i = 0; i < size; i+=2) + { + *dest++ = (char)(*src >> 8); + *dest++ = (char)(*src & 0x00FF); + src++; + } + *dest = '\0'; +} + +// ---------------------------------------------------------------------------- + +static cyg_bool +cf_disk_init(struct cyg_devtab_entry *tab) +{ + disk_channel *chan = (disk_channel *) tab->priv; + cf_disk_info_t *info = (cf_disk_info_t *) chan->dev_priv; + cf_ata_identify_data_t *ata_id; + cyg_uint8 id_buf[sizeof(cf_ata_identify_data_t)]; + cyg_disk_identify_t ident; + + if (chan->init) + return true; + + D(("CF(%p) hw init\n", info->base)); + + CF_HW_INIT(); + CF_HW_RESET(); + + D(("CF(%p) identify drive\n", info->base)); + + if (!exe_cmd(info->base, CF_ATA_IDENTIFY_DRIVE_CMD, 0, 0, 0)) + { + D(("CF(%p) error (%x)\n", info->base, get_error(info->base))); + return false; + } + read_data(info->base, id_buf, sizeof(cf_ata_identify_data_t), 0); + + ata_id = (cf_ata_identify_data_t *)id_buf; + + D(("CF(%p) general conf = %x\n", info->base, ata_id->general_conf)); + + if (0x848A != ata_id->general_conf) + return false; + + id_strcpy(ident.serial, ata_id->serial, 20); + id_strcpy(ident.firmware_rev, ata_id->firmware_rev, 8); + id_strcpy(ident.model_num, ata_id->model_num, 40); + + ident.cylinders_num = ata_id->num_cylinders; + ident.heads_num = ata_id->num_heads; + ident.sectors_num = ata_id->num_sectors; + ident.lba_sectors_num = ata_id->lba_total_sectors[1] << 16 | + ata_id->lba_total_sectors[0]; + + if (!(chan->callbacks->disk_init)(tab)) + return false; + + if (ENOERR != (chan->callbacks->disk_connected)(tab, &ident)) + return false; + + return true; +} + +static Cyg_ErrNo +cf_disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + disk_channel *chan = (disk_channel *) (*tab)->priv; + return (chan->callbacks->disk_lookup)(tab, sub_tab, name); +} + +static Cyg_ErrNo +cf_disk_read(disk_channel *chan, + void *buf, + cyg_uint32 len, + cyg_uint32 block_num) +{ + cf_disk_info_t *info = (cf_disk_info_t *)chan->dev_priv; + + D(("CF(%p) read block %d\n", info->base, block_num)); + + if (!exe_cmd(info->base, CF_ATA_READ_SECTORS_CMD, 0, block_num, 1)) + { + D(("CF(%p) error (%x)\n", info->base, get_error(info->base))); + return -EIO; + } + read_data(info->base, buf, len); + + return ENOERR; +} + +static Cyg_ErrNo +cf_disk_write(disk_channel *chan, + const void *buf, + cyg_uint32 len, + cyg_uint32 block_num) +{ + cf_disk_info_t *info = (cf_disk_info_t *)chan->dev_priv; + + D(("CF(%p) write block %d\n", info->base, block_num)); + + if (!exe_cmd(info->base, CF_ATA_WRITE_SECTORS_CMD, 0, block_num, 1)) + { + D(("CF(%p) error (%x)\n", info->base, get_error(info->base))); + return -EIO; + } + write_data(info->base, buf, len); + + return ENOERR; +} + +static Cyg_ErrNo +cf_disk_get_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len) +{ + return -EINVAL; +} + +static Cyg_ErrNo +cf_disk_set_config(disk_channel *chan, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len) +{ + return -EINVAL; +} + +// ---------------------------------------------------------------------------- +// EOF v85x_edb_v850_disk.c diff --git a/packages/ecos.db b/packages/ecos.db --- a/packages/ecos.db +++ b/packages/ecos.db @@ -5433,3 +5433,47 @@ package CYGPKG_VNC_SERVER { } # -------------------------------------------------------------------------- + +package CYGPKG_DEVS_DISK_V85X_EDB_V850 { + alias { "Elatec v850 development board disk driver" edb_v850_disk } + directory devs/disk/v85x/edb_v850 + script v85x_edb_v850_disk.cdl + hardware + description "Disk driver for the Elatec v850 development board." +} + +package CYGPKG_DEVS_DISK_ECOSYNTH { + alias { "Synthetic target disk driver" ecosynthdisk ecosynth_disk_driver } + directory devs/disk/synth + script synthdisk.cdl + hardware + description "Disk driver for the synthetic target." +} + +package CYGPKG_IO_DISK { + alias { "Disk device drivers" disk io_disk } + directory io/disk + script io_disk.cdl + description " + This package contains drivers for basic I/O services on + disk devices." +} + +package CYGPKG_BLOCK_LIB { + alias { "Block cache and access library" blib block_lib } + directory services/blib + script blib.cdl + description " + This package contains the block cache and access library." +} + +package CYGPKG_FS_FAT { + alias { "FAT Filesystem" fatfs fat_fs fs_fat } + directory fs/fat + script fatfs.cdl + description " + This package contains a FAT filesystem implementation." +} + + +# -------------------------------------------------------------------------- diff --git a/packages/fs/fat/current/ChangeLog b/packages/fs/fat/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/ChangeLog @@ -0,0 +1,95 @@ +2004-01-19 Nick Garnett + + + * doc/README.txt: + * doc/fatfs.ecm: + * doc/fat16.ecm: + * doc/mkdisk0: + Added some initial basic documentation, sample configurations and + shell script to manufacture test disk image. + +2003-10-18 Savin Zlobec + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs_ncache.c: + Fixed node allocation tresholds (there should be no + more unnecessary node memory allocation/deallocation). + Added more sanity checks and a cdl option to enable them. + +2003-10-17 Savin Zlobec + + * src/fatfs_supp.c: Fixed dos to unix date conversion. + +2003-10-16 Savin Zlobec + + * src/fatfs.c: Fixed dir entry searching. + * src/fatfs_ncache.c: Added more sanity checks. + +2003-10-13 Savin Zlobec + + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + Added FAT12 support and FAT type detection. + Fixed file deletion. + Fixed reusing of deleted directory entries. + Fixed free clusters search. + Fixed directory renaming. + Fixed out of space situation while writting. + +2003-09-01 Savin Zlobec + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + * src/fatfs_tcache.c: + * src/fatfs_ncache.c: + Implemented block IO with BLib, fixed FAT data access + problems and added FAT table cache memory constraints. + +2003-07-07 Savin Zlobec + + * cdl/fatfs.cdl: + * src/fatfs.h: + * src/fatfs.c: + * src/fatfs_supp.c: + * src/fatfs_ncache.c: + * src/fatfs_tcache.c: + A FAT16 filesystem implementation + + + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/packages/fs/fat/current/cdl/fatfs.cdl b/packages/fs/fat/current/cdl/fatfs.cdl new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/cdl/fatfs.cdl @@ -0,0 +1,138 @@ +# ==================================================================== +# +# fatfs.cdl +# +# FAT Filesystem configuration data +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2003 Savin Zlobec +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): savin +# Contributors: +# Date: 2003-06-25 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_FS_FAT { + display "FAT filesystem" + include_dir cyg/fatfs + + requires CYGPKG_IO_FILEIO + + requires CYGPKG_ISOINFRA + requires CYGINT_ISO_ERRNO + requires CYGINT_ISO_ERRNO_CODES + requires CYGPKG_MEMALLOC + requires CYGPKG_BLOCK_LIB +# requires CYGFUN_LIBC_STRING_BSD_FUNCS + + implements CYGINT_IO_FILEIO_FS + + compile -library=libextras.a fatfs.c \ + fatfs_supp.c \ + fatfs_ncache.c \ + fatfs_tcache.c + + cdl_option CYGNUM_FS_FAT_NODE_HASH_TABLE_SIZE { + display "Node hash table size" + flavor data + default_value 32 + legal_values 1 to 9999999999 + description "This option controls the number of slots in the + hash table used to store file nodes using filenames + as keys." + } + + cdl_option CYGNUM_FS_FAT_NODE_ALLOC_THRESHOLD { + display "Node allocation treshold" + flavor data + default_value 16 + legal_values 1 to 9999999999 + description "This option controls at which point the filesystem + starts reusing dead file nodes rather then allocating + memory for new nodes." + } + + cdl_option CYGNUM_FS_FAT_FAT_TABLE_CACHE_MEMSIZE { + display "FAT table cache memory size" + flavor data + default_value 10240 + legal_values 1 to 9999999999 + description "This option controls the amount of memory used + for the FAT table cache." + } + + cdl_option CYGNUM_FS_FAT_FAT_TABLE_CACHE_INCREMENT { + display "FAT table cache size increment" + flavor data + default_value 10 + legal_values 1 to 9999999999 + description "This option controls the amount of memory by which + the per-file FAT table cache will grow." + } + + cdl_option CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE { + display "FAT block cache memory size" + flavor data + default_value 10240 + legal_values 1 to 9999999999 + description "This option controls the amount of memory used for + the block cache." + } + + cdl_option CYGDBG_FS_FAT_NODE_CACHE_EXTRA_CHECKS { + display "Node cache extra checks" + flavor bool + default_value 1 + active_if CYGPKG_INFRA_DEBUG && CYGDBG_USE_ASSERTS + description "This option controls the inclusion of extra + sanity checks in node cache code." + } + + # -------------------------------------------------------------------- + + cdl_option CYGPKG_FS_FAT_TESTS { + display "FAT FS tests" + flavor data + no_define + calculated { "tests/fileio1.c" } + description "This option specifies the set of tests for the + FAT FS package." + } +} + +# ==================================================================== +# End of fatfs.cdl diff --git a/packages/fs/fat/current/doc/README.txt b/packages/fs/fat/current/doc/README.txt new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/doc/README.txt @@ -0,0 +1,128 @@ + +FAT Filesystem README ++++++++++++++++++++++ + +This file serves as documentation for the FAT filesystem until full +documentation can be generated. + +Configuring FAT +=============== + +The FAT filesystem support consists of several packages: + +CYGPKG_FS_FAT + The FAT filesystem itself. + +CYGPKG_BLOCK_LIB + Block library. This actually implements the block cache. It + depends on the Linux compatibility library + (CYGPKG_LINUX_COMPAT) for support. + +CYGPKG_IO_DISK + Disk device IO support. This provides the top level disk + driver functions. It also interprets partition tables and + provides a separate access channel for each partition. + +CYGPKG_DEVS_DISK_ECOSYNTH + This is the only low level device driver currently known to + work. This driver treats a file in the Linux filesystem as a + disk image. + +CYGPKG_DEVS_DISK_V85X_EDB_V850 + This is a device driver for an IDE compatible Compact Flash + device on a platform that is otherwise unsupported. It has not + been tested. + +In addition the above new packages, FAT support also relies on the +following existing packages: + +CYGPKG_IO_FILEIO + The File IO package. This provides the API by which the FAT + filesystem is accessed. + +CYGPKG_IO + Device IO package. This provides all the infrastructure for + the disk devices. + +CYGPKG_LIBC_STRING + Strings library. This provides the string and memory move and + compare routines used by the filesystem. + +CYGPKG_MEMALLOC + The FAT filesystem currently uses malloc() to allocate + its memory resources, so this package is required. + +The doc directory that contains this file contains a number of .ecm +files that will add the FAT filesystem to most configurations. These +should be used as a basis for creating new configurations. + +Device Drivers +============== + +The disk device interface is divided into a generic layer in the +CYGPKG_IO_DISK package and drivers for each device. + +Apart from providing the main API, the other thing that the DISK +package does is to provide support for disk partitions. It interprets +the on-disk data structures and provides an independent channel to +access each partition independently. Partitions are referenced using +an additional element to the device name. If the device is named +"/dev/disk0" then "/dev/disk0/1" refers to partition 1, "/dev/disk0/2" +to partition 2 and so on. If the disk is not partitioned, for example +a floppy disk, then "/dev/disk0/0" refers to the whole disk. + +There are two device drivers currently supported: + +Linux Synthetic Driver +---------------------- + +This driver treats a disk file on Linux as a disk image and read or +writes blocks from/to it to simulate disk IO. At present it supports +just a single disk device image. There are a number of configuration +options for this device, see the CDL file for details, and the .ecm +files for examples. + +EDB V850 Compact Flash Device +----------------------------- + +This uses an IDE interface to a Compact Flash device. This is actually +for a platform that is not supported in the current source tree, so +cannot be tested. However, it should be possible to adapt the code for +other devices. + +Testing +======= + +There is currently just one test available for the FAT filesystem, +fileio1. This test will currently only run on the Linux synthetic +target and needs a special disk image. + +To run the test you first need to configure the synthetic disk driver +to access the disk image. This is most easily done by importing the +fat16.ecm file. A complete build would look like this: + +$ mkdir linux.fat16 +$ cd linux.fat16 +$ ecosconfig new linux +$ ecosconfig import $ECOS_REPOSITORY/fs/fat/current/doc/fat16.ecm +$ ecosconfig tree +$ make tests + +To make the disk image, first ensure that there is at least 10MB of +space available in your /tmp directory. Add the following two lines to +your ~/.mtoolsrc file (you may need to create it): + +drive z: file="/tmp/disk0.img" partition=1 +drive y: file="/tmp/disk0.img" partition=2 + +Now run the mkdisk0 shell script from the fs/fat/doc directory: + +$ source $ECOS_REPOSITORY/fs/fat/current/doc/mkdisk0 +20000+0 records in +20000+0 records out +Warning: no active (bootable) partition present +$ + +Ignore the warning, we are not interested in booting this disk. + +It should now be possible to run the fileio1 test program. diff --git a/packages/fs/fat/current/doc/fat16.ecm b/packages/fs/fat/current/doc/fat16.ecm new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/doc/fat16.ecm @@ -0,0 +1,57 @@ +cdl_savefile_version 1; +cdl_savefile_command cdl_savefile_version {}; +cdl_savefile_command cdl_savefile_command {}; +cdl_savefile_command cdl_configuration { description hardware template package }; +cdl_savefile_command cdl_package { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_component { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_option { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_interface { value_source user_value wizard_value inferred_value }; + +cdl_configuration eCos { + + package CYGPKG_IO_FILEIO current ; + package CYGPKG_IO current ; + package CYGPKG_LIBC_STRING current ; + + package CYGPKG_DEVS_DISK_ECOSYNTH current ; + package CYGPKG_IO_DISK current ; + package CYGPKG_BLOCK_LIB current ; + package CYGPKG_LINUX_COMPAT current ; + package CYGPKG_FS_FAT current ; + + package CYGPKG_MEMALLOC current ; + +}; + + +cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 { + user_value 1 +}; + +cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_NAME { + user_value {"/dev/disk0/"} +}; + +cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME { + user_value {"/tmp/disk0.img"} +}; + +cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE { + user_value 10240000 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR { + user_value 1 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_CYLINDERS { + user_value 100 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_HEADS { + user_value 5 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_SECTORS { + user_value 40 +}; diff --git a/packages/fs/fat/current/doc/fatfd.ecm b/packages/fs/fat/current/doc/fatfd.ecm new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/doc/fatfd.ecm @@ -0,0 +1,45 @@ +cdl_savefile_version 1; +cdl_savefile_command cdl_savefile_version {}; +cdl_savefile_command cdl_savefile_command {}; +cdl_savefile_command cdl_configuration { description hardware template package }; +cdl_savefile_command cdl_package { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_component { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_option { value_source user_value wizard_value inferred_value }; +cdl_savefile_command cdl_interface { value_source user_value wizard_value inferred_value }; + +cdl_configuration eCos { + + package CYGPKG_IO_FILEIO current ; + package CYGPKG_IO current ; + package CYGPKG_LIBC_STRING current ; + + package CYGPKG_DEVS_DISK_ECOSYNTH current ; + package CYGPKG_IO_DISK current ; + package CYGPKG_BLOCK_LIB current ; + package CYGPKG_LINUX_COMPAT current ; + package CYGPKG_FS_FAT current ; + + package CYGPKG_MEMALLOC current ; + +}; + + +cdl_component CYGVAR_DEVS_DISK_ECOSYNTH_DISK0 { + user_value 1 +}; + +cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_NAME { + user_value {"/dev/disk0/"} +}; + +cdl_option CYGDAT_IO_DISK_ECOSYNTH_DISK0_FILENAME { + user_value {"/dev/fd0"} +}; + +cdl_option CYGNUM_IO_DISK_ECOSYNTH_DISK0_SIZE { + user_value 1474560 +}; + +cdl_option CYGIMP_IO_DISK_ECOSYNTH_DISK0_MBR { + user_value 0 +}; \ No newline at end of file diff --git a/packages/fs/fat/current/doc/mkdisk0 b/packages/fs/fat/current/doc/mkdisk0 new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/doc/mkdisk0 @@ -0,0 +1,18 @@ +#! /bin/bash +# +# Add the following lines to your ~/.mtoolsrc file before running this script +# +# drive z: file="/tmp/disk0.img" partition=1 +# drive y: file="/tmp/disk0.img" partition=2 +# +# +# + +dd if=/dev/zero of=/tmp/disk0.img bs=512 count=20000 +mpartition -I -t 100 -h 5 -s 40 Z: +mpartition -cfa -t 49 -h 5 -s 40 -b 200 Z: +mpartition -cf -t 50 -h 5 -s 40 -b 10000 Y: +mformat z: +mformat y: + +# end diff --git a/packages/fs/fat/current/src/fatfs.c b/packages/fs/fat/current/src/fatfs.c new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/src/fatfs.c @@ -0,0 +1,1278 @@ +//========================================================================== +// +// fatfs.c +// +// FAT file system +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin (based on ramfs.c) +// Original data: nickg +// Date: 2003-06-29 +// Purpose: FAT file system +// Description: This is a FAT filesystem for eCos. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include + +#include // base kernel types +#include // tracing macros +#include // assertion macros + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "fatfs.h" + +//========================================================================== +// Tracing support defines + +#ifdef FATFS_TRACE_FS_OP +# define TFS 1 +#else +# define TFS 0 +#endif + +#ifdef FATFS_TRACE_FILE_OP +# define TFO 1 +#else +# define TFO 0 +#endif + +//========================================================================== +// Forward definitions + +// Filesystem operations +static int fatfs_mount (cyg_fstab_entry *fste, cyg_mtab_entry *mte); +static int fatfs_umount (cyg_mtab_entry *mte); +static int fatfs_open (cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int mode, cyg_file *fte); +static int fatfs_unlink (cyg_mtab_entry *mte, cyg_dir dir, const char *name); +static int fatfs_mkdir (cyg_mtab_entry *mte, cyg_dir dir, const char *name); +static int fatfs_rmdir (cyg_mtab_entry *mte, cyg_dir dir, const char *name); +static int fatfs_rename (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2 ); +static int fatfs_link (cyg_mtab_entry *mte, cyg_dir dir1, const char *name1, + cyg_dir dir2, const char *name2, int type ); +static int fatfs_opendir(cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_file *fte ); +static int fatfs_chdir (cyg_mtab_entry *mte, cyg_dir dir, const char *name, + cyg_dir *dir_out ); +static int fatfs_stat (cyg_mtab_entry *mte, cyg_dir dir, const char *name, + struct stat *buf); +static int fatfs_getinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); +static int fatfs_setinfo(cyg_mtab_entry *mte, cyg_dir dir, const char *name, + int key, void *buf, int len ); + +// File operations +static int fatfs_fo_read (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int fatfs_fo_write (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int fatfs_fo_lseek (struct CYG_FILE_TAG *fp, off_t *pos, int whence ); +static int fatfs_fo_ioctl (struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, + CYG_ADDRWORD data); +static int fatfs_fo_fsync (struct CYG_FILE_TAG *fp, int mode ); +static int fatfs_fo_close (struct CYG_FILE_TAG *fp); +static int fatfs_fo_fstat (struct CYG_FILE_TAG *fp, struct stat *buf ); +static int fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, + void *buf, int len ); +static int fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, + void *buf, int len ); + +// Directory operations +static int fatfs_fo_dirread (struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio); +static int fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence); + +//========================================================================== +// Filesystem table entries + +// ------------------------------------------------------------------------- +// Fstab entry. + +FSTAB_ENTRY(fatfs_fste, "fatfs", 0, + CYG_SYNCMODE_FILE_FILESYSTEM|CYG_SYNCMODE_IO_FILESYSTEM, + fatfs_mount, + fatfs_umount, + fatfs_open, + fatfs_unlink, + fatfs_mkdir, + fatfs_rmdir, + fatfs_rename, + fatfs_link, + fatfs_opendir, + fatfs_chdir, + fatfs_stat, + fatfs_getinfo, + fatfs_setinfo); + +// ------------------------------------------------------------------------- +// File operations. + +static cyg_fileops fatfs_fileops = +{ + fatfs_fo_read, + fatfs_fo_write, + fatfs_fo_lseek, + fatfs_fo_ioctl, + cyg_fileio_seltrue, + fatfs_fo_fsync, + fatfs_fo_close, + fatfs_fo_fstat, + fatfs_fo_getinfo, + fatfs_fo_setinfo +}; + +// ------------------------------------------------------------------------- +// Directory file operations. + +static cyg_fileops fatfs_dirops = +{ + fatfs_fo_dirread, + (cyg_fileop_write *)cyg_fileio_enosys, + fatfs_fo_dirlseek, + (cyg_fileop_ioctl *)cyg_fileio_enosys, + cyg_fileio_seltrue, + (cyg_fileop_fsync *)cyg_fileio_enosys, + fatfs_fo_close, + (cyg_fileop_fstat *)cyg_fileio_enosys, + (cyg_fileop_getinfo *)cyg_fileio_enosys, + (cyg_fileop_setinfo *)cyg_fileio_enosys +}; + +// ------------------------------------------------------------------------- +// Directory search data +// Parameters for a directory search. The fields of this structure are +// updated as we follow a pathname through the directory tree. + +struct fatfs_dirsearch_t +{ + fatfs_disk_t *disk; // Disk info + fatfs_node_t *dir; // Directory to search + const char *path; // Path to follow + fatfs_node_t *node; // Node found + const char *name; // Last name fragment used + int namelen; // Name fragment length + cyg_bool last; // Last name in path? +}; + +typedef struct fatfs_dirsearch_t fatfs_dirsearch_t; + +//========================================================================== + +#if TFS +static void +print_disk_info(fatfs_disk_t *disk) +{ + diag_printf("FAT: sector size: %u\n", disk->sector_size); + diag_printf("FAT: cluster size: %u\n", disk->cluster_size); + diag_printf("FAT: FAT table position: %u\n", disk->fat_tbl_pos); + diag_printf("FAT: FAT table num ent: %u\n", disk->fat_tbl_nents); + diag_printf("FAT: FAT table size: %u\n", disk->fat_tbl_size); + diag_printf("FAT: FAT tables num: %u\n", disk->fat_tbls_num); + diag_printf("FAT: FAT root dir pos: %u\n", disk->fat_root_dir_pos); + diag_printf("FAT: FAT root dir nents: %u\n", disk->fat_root_dir_nents); + diag_printf("FAT: FAT root dir size: %u\n", disk->fat_root_dir_size); + diag_printf("FAT: FAT data position: %u\n", disk->fat_data_pos); +} +#endif + +static void +init_dirsearch(fatfs_dirsearch_t *ds, + fatfs_disk_t *disk, + fatfs_node_t *dir, + const char *name) +{ + ds->disk = disk; + if (NULL == dir) + ds->dir = disk->root; + else + ds->dir = dir; + ds->path = name; + ds->node = ds->dir; + ds->namelen = 0; + ds->last = false; +} + +static int +find_direntry(fatfs_dirsearch_t *ds) +{ + int err; + cyg_uint32 pos = 0; + fatfs_node_t node_data; + + CYG_TRACE1(TFS, "Finding dir entry '%s'", ds->name); + + ds->node = fatfs_node_find(ds->disk, ds->name, + ds->namelen, ds->dir->cluster); + + if (ds->node != NULL) + { + CYG_TRACE1(TFS, "Found dir entry '%s' in cache", ds->name); + fatfs_node_touch(ds->disk, ds->node); + return ENOERR; + } + + while (true) + { + err = fatfs_get_dir_entry_node(ds->disk, ds->dir, &pos, &node_data); + if (err != ENOERR) + return (err == EEOF ? ENOERR : err); + + if ('\0' == node_data.filename[ds->namelen] && + 0 == strncasecmp(node_data.filename, ds->name, ds->namelen)) + { + CYG_TRACE2(TFS, "Read dir entry '%s' at %d", + node_data.filename, pos); + + ds->node = fatfs_node_alloc(ds->disk, &node_data); + if (NULL == ds->node) + return ENOMEM; + return ENOERR; + } + pos++; + } +} + +static int +find_entry(fatfs_dirsearch_t *ds) +{ + int err; + const char *name = ds->path; + const char *n = name; + char namelen = 0; + + // check that we really have a directory + if( !S_ISDIR(ds->dir->mode) ) + { + CYG_TRACE1(TFS, "Entry '%s' not dir", ds->dir->filename); + return ENOTDIR; + } + + // Isolate the next element of the path name. + while (*n != '\0' && *n != '/') + n++, namelen++; + + // If we terminated on a NUL, set last flag. + if (*n == '\0') + ds->last = true; + + // update name in dirsearch object + ds->name = name; + ds->namelen = namelen; + + err = find_direntry(ds); + if (err != ENOERR) + return err; + + CYG_TRACE2(TFS, "Entry '%s' %s", name, (ds->node ? "found" : "not found")); + + if (ds->node != NULL) + return ENOERR; + else + return ENOENT; +} + +static int +fatfs_find(fatfs_dirsearch_t *ds) +{ + int err; + + CYG_TRACE1(TFS, "Find path='%s'", ds->path); + + // Short circuit empty paths + if (*(ds->path) == '\0') + return ENOERR; + + // Iterate down directory tree until we find the object + // we want. + for(;;) + { + err = find_entry(ds); + + if (err != ENOERR) + return err; + + if (ds->last) + { + CYG_TRACE0(TFS, "Entry found"); + return ENOERR; + } + + // Update dirsearch object to search next directory. + ds->dir = ds->node; + ds->path += ds->namelen; + + // Skip dirname separators + if (*(ds->path) == '/') ds->path++; + + CYG_TRACE1(TFS, "Find path to go='%s'", ds->path); + } +} + +//========================================================================== +// Filesystem operations + +// ------------------------------------------------------------------------- +// fatfs_mount() +// Process a mount request. This mainly creates a root for the +// filesystem. + +static int +fatfs_mount(cyg_fstab_entry *fste, cyg_mtab_entry *mte) +{ + cyg_io_handle_t dev_h; + Cyg_ErrNo err; + fatfs_disk_t *disk; + fatfs_node_t root_data; + + CYG_TRACE2(TFS, "Mount fste=%p mte=%p", fste, mte); + + CYG_TRACE1(TFS, "Looking up disk device '%s'", mte->devname); + + err = cyg_io_lookup(mte->devname, &dev_h); + if (err != ENOERR) + return err; + + disk = (fatfs_disk_t *)malloc(sizeof(fatfs_disk_t)); + if (NULL == disk) + return ENOMEM; + + CYG_TRACE0(TFS, "Initializing FAT table cache"); + + if (ENOERR != fatfs_tcache_create(disk, + CYGNUM_FS_FAT_FAT_TABLE_CACHE_MEMSIZE)) + { + free(disk); + return ENOMEM; + } + + CYG_TRACE0(TFS, "Initializing block cache"); + + disk->bcache_mem = (cyg_uint8 *)malloc(CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE); + if (NULL == disk->bcache_mem) + { + fatfs_tcache_delete(disk); + free(disk); + return ENOMEM; + } + // FIXME: get block size from disk device + err = cyg_blib_io_create(dev_h, disk->bcache_mem, + CYGNUM_FS_FAT_BLOCK_CACHE_MEMSIZE, 512, &disk->blib); + if (err != ENOERR) + { + fatfs_tcache_delete(disk); + free(disk->bcache_mem); + free(disk); + return err; + } + + disk->dev_h = dev_h; + + CYG_TRACE0(TFS, "Initializing disk"); + + err = fatfs_get_disk_info(disk); + if (err != ENOERR) + { + fatfs_tcache_delete(disk); + cyg_blib_delete(&disk->blib); + free(disk->bcache_mem); + free(disk); + return err; + } + +#if TFS + print_disk_info(disk); +#endif + + CYG_TRACE0(TFS, "Initializing node cache"); + + fatfs_node_cache_init(disk); + + CYG_TRACE0(TFS, "Initializing root node"); + + fatfs_get_root_node(disk, &root_data); + disk->root = fatfs_node_alloc(disk, &root_data); + fatfs_node_ref(disk, disk->root); + + mte->root = (cyg_dir)disk->root; + mte->data = (CYG_ADDRWORD)disk; + + CYG_TRACE0(TFS, "Disk mounted"); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_umount() +// Unmount the filesystem. This will currently only succeed if the +// filesystem is empty. + +static int +fatfs_umount(cyg_mtab_entry *mte) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_node_t *root = (fatfs_node_t *)mte->root; + + CYG_TRACE3(TFS, "Umount mte=%p %d live nodes %d dead nodes", + mte, fatfs_get_live_node_count(disk), + fatfs_get_dead_node_count(disk)); + + if (root->refcnt > 1) + return EBUSY; + + if (fatfs_get_live_node_count(disk) != 1) + return EBUSY; + + fatfs_node_unref(disk, root); + fatfs_node_cache_flush(disk); + fatfs_tcache_delete(disk); + // FIXME: cache delete can fail if cache can't be synced + cyg_blib_delete(&disk->blib); + free(disk->bcache_mem); + free(disk); + + mte->root = CYG_DIR_NULL; + mte->data = (CYG_ADDRWORD)NULL; + + CYG_TRACE0(TFS, "Disk umounted"); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_open() +// Open a file for reading or writing. + +static int +fatfs_open(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + int mode, + cyg_file *file) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_node_t *node = NULL; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE5(TFS, "Open mte=%p dir=%p name='%s' mode=%d file=%p", + mte, dir, name, mode, file); + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err == ENOENT) + { + if (ds.last && (mode & O_CREAT)) + { + fatfs_node_t node_data; + + // No node there, if the O_CREAT bit is set then we must + // create a new one. The dir and name fields of the dirsearch + // object will have been updated so we know where to put it. + + CYG_TRACE1(TFS, "Creating new file '%s'", name); + + err = fatfs_create_file(disk, ds.dir, ds.name, + ds.namelen, &node_data); + if (err != ENOERR) + return err; + + node = fatfs_node_alloc(disk, &node_data); + if (NULL == node) + return ENOMEM; + + // Update directory times + ds.dir->atime = + ds.dir->mtime = cyg_timestamp(); + + err = ENOERR; + } + } + else if (err == ENOERR) + { + // The node exists. If the O_CREAT and O_EXCL bits are set, we + // must fail the open. + + if ((mode & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) + err = EEXIST; + else + node = ds.node; + } + + if (err == ENOERR && (mode & O_TRUNC)) + { + // If the O_TRUNC bit is set we must clean out the file data. + CYG_TRACE0(TFS, "Truncating file"); + fatfs_trunc_file(disk, node); + } + + if (err != ENOERR) + return err; + + // Check that we actually have a file here + if (S_ISDIR(node->mode)) + return EISDIR; + + // Make a reference to this file node + fatfs_node_ref(disk, node); + + // Initialize the file object + + file->f_flag |= mode & CYG_FILE_MODE_MASK; + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &fatfs_fileops; + file->f_offset = (mode & O_APPEND) ? node->size : 0; + file->f_data = (CYG_ADDRWORD)node; + file->f_xops = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_unlink() +// Remove a file link from its directory. + +static int +fatfs_unlink(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE3(TFS, "Unlink mte=%p dir=%p name='%s'", mte, dir, name); + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err != ENOERR) + return err; + + if (ds.node->refcnt > 0) + return EBUSY; + + // Delete node + err = fatfs_delete_file(disk, ds.node); + if (err == ENOERR) + fatfs_node_free(disk, ds.node); + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_mkdir() +// Create a new directory. + +static int +fatfs_mkdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE3(TFS, "Mkdir mte=%p dir=%p name='%s'", mte, dir, name); + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err == ENOENT) + { + if (ds.last) + { + fatfs_node_t node_data; + + // The entry does not exist, and it is the last element in + // the pathname, so we can create it here. + + err = fatfs_create_dir(disk, ds.dir, ds.name, + ds.namelen, &node_data); + if (err != ENOERR) + return err; + + fatfs_node_alloc(disk, &node_data); + + return ENOERR; + } + } + else if (err == ENOERR) + { + return EEXIST; + } + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_rmdir() +// Remove a directory. + +static int +fatfs_rmdir(cyg_mtab_entry *mte, cyg_dir dir, const char *name) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE3(TFS, "Rmdir mte=%p dir=%p name='%s'", mte, dir, name); + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err != ENOERR) + return err; + + // Check that this is actually a directory. + if (!S_ISDIR(ds.node->mode)) + return EPERM; + + if (ds.node->refcnt > 0) + return EBUSY; + + err = fatfs_delete_file(disk, ds.node); + if (err == ENOERR) + fatfs_node_free(disk, ds.node); + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_rename() +// Rename a file/dir. + +static int +fatfs_rename(cyg_mtab_entry *mte, + cyg_dir dir1, + const char *name1, + cyg_dir dir2, + const char *name2) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_dirsearch_t ds1, ds2; + int err; + + CYG_TRACE5(TFS, "Rename mte=%p dir1=%p name1='%s' dir2=%p name2='%s'", + mte, dir1, name1, dir2, name2); + + init_dirsearch(&ds1, disk, (fatfs_node_t *)dir1, name1); + + err = fatfs_find(&ds1); + if (err != ENOERR) + return err; + + // Protect the found nodes from being reused by the + // following search + fatfs_node_ref(disk, ds1.dir); + fatfs_node_ref(disk, ds1.node); + + init_dirsearch(&ds2, disk, (fatfs_node_t *)dir2, name2); + + err = fatfs_find(&ds2); + + if (err == ENOERR && ds2.last) + { + err = EEXIST; + goto out; + } + + if (err == ENOENT && !ds2.last) + goto out; + + if (ds1.node == ds2.node) + { + err = ENOERR; + goto out; + } + + err = fatfs_rename_file(disk, ds1.dir, ds1.node, ds2.dir, + ds2.name, ds2.namelen); + fatfs_node_rehash(disk, ds1.node); + +out: + fatfs_node_unref(disk, ds1.dir); + fatfs_node_unref(disk, ds1.node); + + if (err == ENOERR) + { + ds1.dir->atime = + ds1.dir->mtime = + ds2.dir->atime = + ds2.dir->mtime = cyg_timestamp(); + } + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_link() +// Make a new directory entry for a file. + +static int +fatfs_link(cyg_mtab_entry *mte, + cyg_dir dir1, + const char *name1, + cyg_dir dir2, + const char *name2, + int type) +{ + CYG_TRACE6(TFS, "Link mte=%p dir1=%p name1='%s' dir2=%p name2='%s' type=%d", + mte, dir1, name1, dir2, name2, type); + + // Linking not supported + return EINVAL; +} + +// ------------------------------------------------------------------------- +// fatfs_opendir() +// Open a directory for reading. + +static int +fatfs_opendir(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + cyg_file *file) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE4(TFS, "Opendir mte=%p dir=%p name='%s' file=%p", + mte, dir, name, file); + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err != ENOERR) + return err; + + // Check it is really a directory. + if (!S_ISDIR(ds.node->mode)) + return ENOTDIR; + + // Make a reference to this dir node + fatfs_node_ref(disk, ds.node); + + // Initialize the file object + file->f_type = CYG_FILE_TYPE_FILE; + file->f_ops = &fatfs_dirops; + file->f_data = (CYG_ADDRWORD)ds.node; + file->f_xops = 0; + file->f_offset = 0; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_chdir() +// Change directory support. + +static int +fatfs_chdir(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + cyg_dir *dir_out) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + + CYG_TRACE4(TFS, "Chdir mte=%p dir=%p dir_out=%p name=%d", + mte, dir, dir_out, name); + + if (dir_out != NULL) + { + // This is a request to get a new directory pointer in + // *dir_out. + + fatfs_dirsearch_t ds; + int err; + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err != ENOERR) + return err; + + // Check it is a directory + if (!S_ISDIR(ds.node->mode)) + return ENOTDIR; + + if (ds.node != disk->root) + fatfs_node_ref(disk, ds.node); + *dir_out = (cyg_dir)ds.node; + } + else + { + // If no output dir is required, this means that the mte and + // dir arguments are the current cdir setting and we should + // forget this fact. + + fatfs_node_t *node = (fatfs_node_t *)dir; + + if (node != disk->root) + fatfs_node_unref(disk, node); + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_stat() +// Get struct stat info for named object. + +static int +fatfs_stat(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + struct stat *buf) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)mte->data; + fatfs_dirsearch_t ds; + int err; + + CYG_TRACE4(TFS, "Stat mte=%p dir=%p name='%s' buf=%p", + mte, dir, name, buf); + + init_dirsearch(&ds, disk, (fatfs_node_t *)dir, name); + + err = fatfs_find(&ds); + + if (err != ENOERR) + return err; + + // Fill in the status + buf->st_mode = ds.node->mode; + buf->st_ino = (ino_t)ds.node->cluster; + buf->st_dev = 0; + buf->st_nlink = 1; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = ds.node->size; + buf->st_atime = ds.node->atime; + buf->st_mtime = ds.node->mtime; + buf->st_ctime = ds.node->ctime; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_getinfo() +// Getinfo. Nothing to support here at present. + +static int +fatfs_getinfo(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + int key, + void *buf, + int len) +{ + CYG_TRACE6(TFS, "Getinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d", + mte, dir, name, key, buf, len); + return EINVAL; +} + +// ------------------------------------------------------------------------- +// fatfs_setinfo() +// Setinfo. Nothing to support here at present. + +static int +fatfs_setinfo(cyg_mtab_entry *mte, + cyg_dir dir, + const char *name, + int key, + void *buf, + int len) +{ + CYG_TRACE6(TFS, "Getinfo mte=%p dir=%p name='%s' key=%d buf=%p len=%d", + mte, dir, name, key, buf, len); + return EINVAL; +} + +//========================================================================== +// File operations + +// ------------------------------------------------------------------------- +// fatfs_fo_read() +// Read data from the file. + +static int +fatfs_fo_read(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data; + fatfs_node_t *node = (fatfs_node_t *)fp->f_data; + cyg_uint32 pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + int i; + + CYG_TRACE3(TFO, "Read fp=%p uio=%p pos=%d", fp, uio, pos); + + // Loop over the io vectors until there are none left + for (i = 0; i < uio->uio_iovcnt; i++) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + // Loop over each vector filling it with data from the file. + while (len > 0 && pos < node->size) + { + cyg_uint32 l = len; + int err; + + // Adjust size to end of file if necessary + if (l > node->size-pos) + l = node->size-pos; + + // Read data + err = fatfs_read_data(disk, node, buf, &l, pos); + + if (err != ENOERR) + return err; + + // Update working vars + len -= l; + buf += l; + pos += l; + resid -= l; + } + } + + // We successfully read some data, + // update the access time, file offset and transfer residue. + node->atime = cyg_timestamp(); + uio->uio_resid = resid; + fp->f_offset = (off_t)pos; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_write() +// Write data to file. + +static int +fatfs_fo_write(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data; + fatfs_node_t *node = (fatfs_node_t *)fp->f_data; + cyg_uint32 pos = fp->f_offset; + ssize_t resid = uio->uio_resid; + int err = ENOERR; + int i; + + CYG_TRACE3(TFO, "Write fp=%p uio=%p pos=%d", fp, uio, pos); + + // If the APPEND mode bit was supplied, force all writes to + // the end of the file. + if (fp->f_flag & CYG_FAPPEND) + pos = fp->f_offset = node->size; + + // Check that pos is within current file size, or at the very end. + if (pos < 0 || pos > node->size) + return EINVAL; + + // Now loop over the iovecs until they are all done, or + // we get an error. + for (i = 0; i < uio->uio_iovcnt; i++) + { + cyg_iovec *iov = &uio->uio_iov[i]; + char *buf = (char *)iov->iov_base; + off_t len = iov->iov_len; + + // Loop over the vector writing it to the file until it has + // all been done. + while (len > 0) + { + cyg_uint32 l = len; + + // Write data + err = fatfs_write_data(disk, node, buf, &l, pos); + + // Update working vars + len -= l; + buf += l; + pos += l; + resid -= l; + + // Stop writing if there is no more space in the file and + // indicate end of data. + if (err == ENOSPC) + break; + + if (err != ENOERR) + return err; + } + } + + // We wrote some data successfully, update the modified and access + // times of the node, increase its size appropriately, and update + // the file offset and transfer residue. + node->mtime = + node->atime = cyg_timestamp(); + if (pos > node->size) + node->size = pos; + + uio->uio_resid = resid; + fp->f_offset = (off_t)pos; + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_lseek() +// Seek to a new file position. + +static int +fatfs_fo_lseek(struct CYG_FILE_TAG *fp, off_t *apos, int whence) +{ + fatfs_node_t *node = (fatfs_node_t *)fp->f_data; + off_t pos = *apos; + + CYG_TRACE3(TFO, "Lseek fp=%p pos=%d whence=%d", fp, fp->f_offset, whence); + + switch (whence) + { + case SEEK_SET: + // Pos is already where we want to be. + break; + case SEEK_CUR: + // Add pos to current offset. + pos += fp->f_offset; + break; + case SEEK_END: + // Add pos to file size. + pos += node->size; + break; + default: + return EINVAL; + } + + // Check that pos is still within current file size, or at the + // very end. + if (pos < 0 || pos > node->size) + return EINVAL; + + // All OK, set fp offset and return new position. + *apos = fp->f_offset = pos; + + CYG_TRACE2(TFO, "Lseek fp=%p new pos=%d", fp, *apos); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_ioctl() +// Handle ioctls. Currently none are defined. + +static int +fatfs_fo_ioctl(struct CYG_FILE_TAG *fp, CYG_ADDRWORD com, CYG_ADDRWORD data) +{ + CYG_TRACE3(TFO, "Ioctl fp=%p com=%x data=%x", fp, com, data); + return EINVAL; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_fsync(). +// Force the file out to data storage. + +static int +fatfs_fo_fsync(struct CYG_FILE_TAG *fp, int mode) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data; + fatfs_node_t *node = (fatfs_node_t *)fp->f_data; + int err; + + CYG_TRACE2(TFO, "Fsync fp=%p mode=%d", fp, mode); + + err = fatfs_write_file_attr(disk, node); + if (ENOERR == err) + err = cyg_blib_sync(&disk->blib); + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_close() +// Close a file. + +static int +fatfs_fo_close(struct CYG_FILE_TAG *fp) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data; + fatfs_node_t *node = (fatfs_node_t *)fp->f_data; + int err = ENOERR; + + CYG_TRACE1(TFO, "Close fp=%p", fp); + + if (node != disk->root) + err = fatfs_write_file_attr(disk, node); + fatfs_node_unref(disk, node); + + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_fstat() +// Get file status. + +static int +fatfs_fo_fstat(struct CYG_FILE_TAG *fp, struct stat *buf) +{ + fatfs_node_t *node = (fatfs_node_t *)fp->f_data; + + CYG_TRACE2(TFO, "Fstat fp=%p buf=%p", fp, buf); + + // Fill in the status + buf->st_mode = node->mode; + buf->st_ino = (ino_t)node->cluster; + buf->st_dev = 0; + buf->st_nlink = 1; + buf->st_uid = 0; + buf->st_gid = 0; + buf->st_size = node->size; + buf->st_atime = node->atime; + buf->st_mtime = node->mtime; + buf->st_ctime = node->ctime; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_getinfo() +// Get info. + +static int +fatfs_fo_getinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len) +{ + CYG_TRACE4(TFO, "Getinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len); + return EINVAL; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_setinfo() +// Set info. + +static int +fatfs_fo_setinfo(struct CYG_FILE_TAG *fp, int key, void *buf, int len) +{ + CYG_TRACE4(TFO, "Setinfo fp=%p key=%d buf=%p len=%d", fp, key, buf, len); + return EINVAL; +} + +//========================================================================== +// Directory operations + +// ------------------------------------------------------------------------- +// fatfs_fo_dirread() +// Read a single directory entry from a file. + +static int +fatfs_fo_dirread(struct CYG_FILE_TAG *fp, struct CYG_UIO_TAG *uio) +{ + fatfs_disk_t *disk = (fatfs_disk_t *)fp->f_mte->data; + fatfs_node_t *dir = (fatfs_node_t *)fp->f_data; + struct dirent *ent = (struct dirent *)uio->uio_iov[0].iov_base; + cyg_uint32 pos = fp->f_offset; + char *nbuf = ent->d_name; + off_t len = uio->uio_iov[0].iov_len; + fatfs_node_t node; + int err; + + CYG_TRACE3(TFO, "Dirread fp=%p uio=%p pos=%d", fp, uio, pos); + + if (len < sizeof(struct dirent)) + return EINVAL; + + err = fatfs_get_dir_entry_node(disk, dir, &pos, &node); + + if (err != ENOERR) + return (err == EEOF ? ENOERR : err); + + strcpy(nbuf, node.filename); + + dir->atime = cyg_timestamp(); + uio->uio_resid -= sizeof(struct dirent); + fp->f_offset = (off_t)(pos + 1); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_fo_dirlseek() +// Seek directory to start. + +static int +fatfs_fo_dirlseek(struct CYG_FILE_TAG *fp, off_t *pos, int whence) +{ + CYG_TRACE2(TFO, "Dirlseek fp=%p whence=%d", fp, whence); + + // Only allow SEEK_SET to zero + + if (whence != SEEK_SET || *pos != 0) + return EINVAL; + + *pos = fp->f_offset = 0; + return ENOERR; +} + +// ------------------------------------------------------------------------- +// EOF fatfs.c diff --git a/packages/fs/fat/current/src/fatfs.h b/packages/fs/fat/current/src/fatfs.h new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/src/fatfs.h @@ -0,0 +1,324 @@ +#ifndef CYGONCE_FATFS_FATFS_H +#define CYGONCE_FATFS_FATFS_H +//========================================================================== +// +// fatfs.h +// +// FAT file system header +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-06-29 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +// -------------------------------------------------------------------------- + +#define FATFS_HASH_TABLE_SIZE CYGNUM_FS_FAT_NODE_HASH_TABLE_SIZE + +#define FATFS_NODE_ALLOC_THRESHOLD CYGNUM_FS_FAT_NODE_ALLOC_THRESHOLD + +#define FATFS_FAT_TABLE_CACHE_INCREMENT CYGNUM_FS_FAT_FAT_TABLE_CACHE_INCREMENT + +#ifdef CYGDBG_FS_FAT_NODE_CACHE_EXTRA_CHECKS +# define FATFS_NODE_CACHE_EXTRA_CHECKS 1 +#endif + +// -------------------------------------------------------------------------- + +// Node cache tracing support +//#define FATFS_TRACE_NODE_CACHE 1 + +// FAT table cache tracing support +//#define FATFS_TRACE_FAT_TABLE_CACHE 1 + +// FAT table operations tracing support +//#define FATFS_TRACE_FAT_TABLE 1 + +// FAT dir entry operations tracing support +//#define FATFS_TRACE_DIR_ENTRY 1 + +// FAT clusters operations tracing support +//#define FATFS_TRACE_CLUSTER 1 + +// FAT data tracing support +//#define FATFS_TRACE_DATA 1 + +// FAT file operations tracing support +//#define FATFS_TRACE_FILE_OP 1 + +// FAT filesystem operations tracing support +//#define FATFS_TRACE_FS_OP 1 + +// -------------------------------------------------------------------------- + +typedef enum fatfs_type_e +{ + FATFS_FAT12 = 0, + FATFS_FAT16 +} fatfs_type_t; + +typedef struct fatfs_data_pos_s +{ + cyg_uint32 cluster; // Cluster number + cyg_uint32 cluster_snum; // Cluster file seq number + // (0 - first cluster of file, + // 1 - second cluster of file, ...) + cyg_uint32 cluster_pos; // Position inside cluster +} fatfs_data_pos_t; + +typedef struct fatfs_tcache_s +{ + cyg_uint32 *clusters; // Cached clusters array + cyg_uint32 size; // Number of cached clusters in array + cyg_uint32 max_size; // Size of array (allocated space) +} fatfs_tcache_t; + +typedef struct fatfs_node_s +{ + char filename[12+1]; // File name + mode_t mode; // Node type + cyg_ucount32 refcnt; // Open file/current dir references + size_t size; // Size of file in bytes + time_t ctime; // Creation timestamp + time_t atime; // Last access timestamp + time_t mtime; // Last write timestamp + cyg_uint8 priv_data; // Private data + cyg_uint32 cluster; // First cluster number + cyg_uint32 parent_cluster; // First cluster of parent dentry + fatfs_data_pos_t dentry_pos; // Position of node's dir entry on disk + + struct fatfs_node_s *list_prev; // Next node in list + struct fatfs_node_s *list_next; // Prev node in list + struct fatfs_node_s *hash_next; // Next node in hash + + fatfs_tcache_t tcache; // Node FAT table clusters cache +} fatfs_node_t; + +typedef struct fatfs_hash_table_s +{ + cyg_uint32 size; // Number of slots + cyg_uint32 n; // Number of nodes + fatfs_node_t *nodes[FATFS_HASH_TABLE_SIZE]; // Nodes slots +} fatfs_hash_table_t; + +typedef struct fatfs_node_list_s +{ + cyg_uint32 size; // Number of nodes in list + fatfs_node_t *first; // First node in list + fatfs_node_t *last; // Last node in list +} fatfs_node_list_t; + +typedef struct fatfs_disk_s +{ + cyg_uint32 sector_size; // Sector size in bytes + cyg_uint32 sector_size_log2; // Sector size log2 + cyg_uint32 cluster_size; // Cluster size in bytes + cyg_uint32 cluster_size_log2; // Cluster size log2 + cyg_uint32 fat_tbl_pos; // Position of the first FAT table + cyg_uint32 fat_tbl_size; // FAT table size in bytes + cyg_uint32 fat_tbl_nents; // Number of entries in FAT table + cyg_uint32 fat_tbls_num; // Number of FAT tables + cyg_uint32 fat_root_dir_pos; // Position of the root dir + cyg_uint32 fat_root_dir_size; // Root dir size in bytes + cyg_uint32 fat_root_dir_nents; // Max number of entries in root dir + cyg_uint32 fat_data_pos; // Position of data area + fatfs_type_t fat_type; // Type of FAT - 12 or 16 + + cyg_io_handle_t dev_h; // Disk device handle + fatfs_node_t *root; // Root dir node + + cyg_uint8 *tcache_mem; // FAT table cache memory base + cyg_handle_t tcache_mpool_h; // FAT table cache memory pool handle + cyg_mempool_var tcache_mpool; // FAT table cache memory pool struct + + cyg_uint8 *bcache_mem; // Block cache memory base + cyg_blib_t blib; // Block cache and access library instance + + fatfs_node_list_t live_nlist; // List of nodes with refcnt > 0 + fatfs_node_list_t dead_nlist; // List of nodes with refcnt == 0 + fatfs_hash_table_t node_hash; // Hash of nodes in live and dead lists +} fatfs_disk_t; + +// -------------------------------------------------------------------------- + +int fatfs_get_disk_info(fatfs_disk_t *disk); + +void fatfs_get_root_node(fatfs_disk_t *disk, fatfs_node_t *root); + +bool fatfs_is_node_root_node(fatfs_node_t *node); + +int fatfs_get_dir_entry_node(fatfs_disk_t *disk, + fatfs_node_t *dir, + cyg_uint32 *pos, + fatfs_node_t *node); + +int fatfs_write_file_attr(fatfs_disk_t *disk, fatfs_node_t *node); + +int fatfs_delete_file(fatfs_disk_t *disk, fatfs_node_t *node); + +int fatfs_create_file(fatfs_disk_t *disk, + fatfs_node_t *dir, + const char *name, + int namelen, + fatfs_node_t *node); + +int fatfs_create_dir(fatfs_disk_t *disk, + fatfs_node_t *dir, + const char *name, + int namelen, + fatfs_node_t *node); + +int fatfs_trunc_file(fatfs_disk_t *disk, fatfs_node_t *node); + +int fatfs_rename_file(fatfs_disk_t *disk, + fatfs_node_t *dir1, + fatfs_node_t *node, + fatfs_node_t *dir2, + const char *name, + int namelen); + +int fatfs_read_data(fatfs_disk_t *disk, + fatfs_node_t *node, + void *data, + cyg_uint32 *len, + cyg_uint32 off); + +int fatfs_write_data(fatfs_disk_t *disk, + fatfs_node_t *node, + void *data, + cyg_uint32 *len, + cyg_uint32 off); + +// -------------------------------------------------------------------------- + +void fatfs_node_cache_init(fatfs_disk_t *disk); + +void fatfs_node_cache_flush(fatfs_disk_t *disk); + +fatfs_node_t *fatfs_node_alloc(fatfs_disk_t *disk, fatfs_node_t *node_data); + +void fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node); + +void fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node); + +fatfs_node_t* fatfs_node_find(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + cyg_uint32 parent_cluster); + +int fatfs_get_live_node_count(fatfs_disk_t *disk); + +int fatfs_get_dead_node_count(fatfs_disk_t *disk); + +void fatfs_node_flush_dead_tcache(fatfs_disk_t *disk); + +// -------------------------------------------------------------------------- + +int fatfs_tcache_create(fatfs_disk_t *disk, cyg_uint32 mem_size); + +void fatfs_tcache_delete(fatfs_disk_t *disk); + +void fatfs_tcache_init(fatfs_disk_t *disk, fatfs_tcache_t *tcache); + +void fatfs_tcache_flush(fatfs_disk_t *disk, fatfs_tcache_t *tcache); + +bool fatfs_tcache_get(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 num, + cyg_uint32 *cluster); + +bool fatfs_tcache_get_last(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 *num, + cyg_uint32 *cluster); + +bool fatfs_tcache_set(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 num, + cyg_uint32 cluster); + +// -------------------------------------------------------------------------- +// Support routines +// These enable the definition of local versions of certain routines +// if the given packages are not present. + +#ifndef CYGPKG_LIBC_I18N + +__externC int toupper( int c ); + +#endif + +#ifndef CYGFUN_LIBC_STRING_BSD_FUNCS + +__externC int strcasecmp( const char *s1, const char *s2 ); + +__externC int strncasecmp( const char *s1, const char *s2, size_t n ); + +#endif + +// -------------------------------------------------------------------------- + +#endif // CYGONCE_FATFS_FATFS_H + +// -------------------------------------------------------------------------- +// EOF fatfs.h diff --git a/packages/fs/fat/current/src/fatfs_ncache.c b/packages/fs/fat/current/src/fatfs_ncache.c new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/src/fatfs_ncache.c @@ -0,0 +1,926 @@ +//========================================================================== +// +// fatfs_ncache.c +// +// FAT file system node cache functions +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-06-26 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fatfs.h" + +//========================================================================== +// Defines & macros + +#ifdef CYGDBG_USE_ASSERTS +# define USE_ASSERTS 1 +#endif + +#ifdef FATFS_NODE_CACHE_EXTRA_CHECKS +# define USE_XCHECKS 1 +#endif + +#ifdef FATFS_TRACE_NODE_CACHE +# define TNC 1 +#else +# define TNC 0 +#endif + +// This defines how many nodes should always be kept in +// dead list regardless of node allocation treshold - it +// should always be >= 2 or the file finding code may not +// work correctly! +#define DLIST_KEEP_NUM 2 + +//========================================================================== +// Node list functions + +static void +node_list_init(fatfs_node_list_t *list) +{ + list->size = 0; + list->first = list->last = NULL; +} + +static __inline__ int +node_list_get_size(fatfs_node_list_t *list) +{ + return (list->size); +} + +static __inline__ fatfs_node_t* +node_list_get_head(fatfs_node_list_t *list) +{ + return (list->first); +} + +static __inline__ fatfs_node_t* +node_list_get_tail(fatfs_node_list_t *list) +{ + return (list->last); +} + +static __inline__ fatfs_node_t* +node_list_get_next(fatfs_node_t *node) +{ + return (node->list_next); +} + +static __inline__ fatfs_node_t* +node_list_get_prev(fatfs_node_t *node) +{ + return (node->list_prev); +} + +static __inline__ bool +node_list_is_last(fatfs_node_t *node) +{ + return (NULL == node->list_next); +} + +static __inline__ bool +node_list_is_first(fatfs_node_t *node) +{ + return (NULL == node->list_prev); +} + +static void +node_list_head_add(fatfs_node_list_t *list, fatfs_node_t *node) +{ + node->list_prev = NULL; + node->list_next = list->first; + + if (NULL == list->first) + { + CYG_ASSERTC(list->size == 0); + list->last = node; + } + else + { + list->first->list_prev = node; + } + + list->first = node; + list->size++; +} + +#if 0 +static __inline__ fatfs_node_t* +node_list_head_get(fatfs_node_list_t *list) +{ + return list->first; +} + +static void +node_list_tail_add(fatfs_node_list_t *list, fatfs_node_t *node) +{ + node->list_prev = list->last; + node->list_next = NULL; + + if (NULL == list->last) + { + CYG_ASSERTC(list->size == 0); + list->first = node; + } + else + { + list->last->list_next = node; + } + + list->last = node; + list->size++; +} +#endif + +static __inline__ fatfs_node_t* +node_list_tail_get(fatfs_node_list_t *list) +{ + return list->last; +} + +static void +node_list_remove(fatfs_node_list_t *list, fatfs_node_t *node) +{ + if (list->first == list->last) + { + if (list->first == node) + { + CYG_ASSERTC(list->size == 1); + list->first = list->last = NULL; + } + else + { + CYG_ASSERT(false, "chain node not in list!"); + } + } + else if (list->first == node) + { + CYG_ASSERTC(node->list_prev == NULL); + list->first = node->list_next; + list->first->list_prev = NULL; + } + else if (list->last == node) + { + CYG_ASSERTC(node->list_next == NULL); + list->last = node->list_prev; + list->last->list_next = NULL; + } + else + { + CYG_ASSERTC(node->list_prev != NULL && node->list_next != NULL); + node->list_prev->list_next = node->list_next; + node->list_next->list_prev = node->list_prev; + } + list->size--; +} + +#ifdef USE_XCHECKS +static void +node_list_check(fatfs_node_list_t *list, int min_ref, int max_ref) +{ + int i; + fatfs_node_t *node, *pnode; + + CYG_ASSERT((list->last == NULL && list->first == NULL) || + (list->last != NULL && list->first != NULL), + "list->first and list->last broken!"); + + if (list->first == NULL) + { + CYG_ASSERTC(list->size == 0); + return; + } + + CYG_ASSERTC(list->first->list_prev == NULL); + CYG_ASSERTC(list->last->list_next == NULL); + + CYG_ASSERTC(list->first->refcnt >= min_ref && + list->first->refcnt <= max_ref); + CYG_ASSERTC(list->last->refcnt >= min_ref && + list->last->refcnt <= max_ref); + + i = 1; + node = list->first; + pnode = NULL; + while (node->list_next != NULL) + { + i++; + CYG_ASSERTC(node->list_prev == pnode); + CYG_ASSERTC(node->refcnt >= min_ref && node->refcnt <= max_ref); + pnode = node; + node = node->list_next; + } + CYG_ASSERTC(node->list_prev == pnode); + CYG_ASSERTC(list->size == i); + CYG_ASSERTC(node == list->last); +} + +static void +node_lists_check(fatfs_disk_t* disk) +{ + node_list_check(&disk->live_nlist, 1, 99999); + node_list_check(&disk->dead_nlist, 0, 0); + + if ((disk->live_nlist.size + disk->dead_nlist.size) > + FATFS_NODE_ALLOC_THRESHOLD) + CYG_ASSERTC(disk->dead_nlist.size <= DLIST_KEEP_NUM); +} +#endif // USE_XCHECKS + +//========================================================================== +// Node hash functions + +static unsigned int +hash_fn(const char *str, unsigned int strlen) +{ + unsigned int i = 0, val = 0; + + while (i++ < strlen) + val = (val ^ (int)toupper(*str++)) << 1; + return(val); +} + +static void +node_hash_init(fatfs_hash_table_t *tbl) +{ + int i; + + // Set size and clear all slots + tbl->size = FATFS_HASH_TABLE_SIZE; + for (i = 0; i < tbl->size; i++) + tbl->nodes[i] = NULL; + tbl->n = 0; +} + +static bool +node_hash_add(fatfs_hash_table_t *tbl, fatfs_node_t *node) +{ + unsigned int hval; + + // Calculate hash of given node filename + hval = hash_fn(node->filename, strlen(node->filename)) % tbl->size; + + CYG_TRACE2(TNC, "name='%s' hval=%d", node->filename, hval); + + if (tbl->nodes[hval] == NULL) + { + // First node in this slot + node->hash_next = NULL; + tbl->nodes[hval] = node; + tbl->n++; + return true; + } + else + { + // More nodes in this slot + fatfs_node_t *lnode, *pnode; + + pnode = NULL; + lnode = tbl->nodes[hval]; + + // Insert node into list so that it is sorted by filename + while (lnode != NULL) + { + if (lnode == node) + return false; + + if (strcasecmp(lnode->filename, node->filename) > 0) + { + if (pnode != NULL) + pnode->hash_next = node; // Insert in the middle + else + tbl->nodes[hval] = node; // Insert at the beginning + node->hash_next = lnode; + tbl->n++; + return true; + } + + pnode = lnode; + lnode = lnode->hash_next; + } + // Insert at the end + pnode->hash_next = node; + node->hash_next = NULL; + tbl->n++; + return true; + } +} + +static fatfs_node_t* +node_hash_find(fatfs_hash_table_t *tbl, + const char *name, + unsigned int namelen, + unsigned int parent_cluster) +{ + unsigned int hval; + fatfs_node_t *node; + + // Calculate hash of name and get the first node in slot + hval = hash_fn(name, namelen) % tbl->size; + node = tbl->nodes[hval]; + + CYG_TRACE2(TNC, "name='%s' hval=%d\n", name, hval); + + // Find the node in list wich matches the + // given name and parent_cluster + while (node != NULL) + { + // First compare the parent cluster number and + // check filename length since it is faster than + // comparing filenames + if (parent_cluster == node->parent_cluster && + '\0' == node->filename[namelen]) + { + int i = strncasecmp(node->filename, name, namelen); + + if (i == 0) + return node; + else if (i > 0) + return NULL; // Stop searching - we have a + // sorted list so there can't be + // any matching filename further on + // if i > 0 - look at node_hash_add + } + node = node->hash_next; + } + + // No such node found + return NULL; +} + +static bool +node_hash_remove_keyless(fatfs_hash_table_t *tbl, fatfs_node_t *node) +{ + int i; + fatfs_node_t *lnode, *pnode; + + // Look in all slots, since we have no key + for (i = 0; i < tbl->size; i++) + { + // Look in list and remove it if there + lnode = tbl->nodes[i]; + pnode = NULL; + while (lnode != NULL) + { + if (lnode == node) + { + if (pnode == NULL) + tbl->nodes[i] = lnode->hash_next; + else + pnode->hash_next = lnode->hash_next; + node->hash_next = NULL; + tbl->n--; + + // All done + return true; + } + pnode = lnode; + lnode = lnode->hash_next; + } + } + return false; +} + +static bool +node_hash_remove(fatfs_hash_table_t *tbl, fatfs_node_t *node) +{ + unsigned int hval; + fatfs_node_t *lnode, *pnode; + + // Calculate hash of name and get the first node in slot + hval = hash_fn(node->filename, strlen(node->filename)) % tbl->size; + lnode = tbl->nodes[hval]; + + // Now find the node in list and remove it + pnode = NULL; + while (lnode != NULL) + { + if (lnode == node) + { + if (pnode == NULL) + tbl->nodes[hval] = lnode->hash_next; + else + pnode->hash_next = lnode->hash_next; + node->hash_next = NULL; + tbl->n--; + + // All done + return true; + } + pnode = lnode; + lnode = lnode->hash_next; + } + + // Node not in list + return false; +} + +#ifdef USE_XCHECKS +static void +node_hash_check(fatfs_hash_table_t *tbl) +{ + int i, n; + + n = 0; + for (i = 0; i < tbl->size; i++) + { + fatfs_node_t *lnode, *pnode; + + pnode = NULL; + lnode = tbl->nodes[i]; + + while (lnode != NULL) + { + if (pnode != NULL) + { + int c = strcasecmp(pnode->filename, lnode->filename); + CYG_ASSERT(c <= 0, "hash table not sorted"); + CYG_ASSERT(pnode->parent_cluster != lnode->parent_cluster || + 0 != c, "duplicated node in hash table"); + } + n++; + pnode = lnode; + lnode = lnode->hash_next; + } + } + CYG_ASSERTC(tbl->n == n); +} + +static void +node_hash_not_found_check(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + unsigned int parent_cluster) +{ + fatfs_node_t* node; + + node = node_list_get_head(&disk->live_nlist); + while (NULL != node) + { + if (node->parent_cluster == parent_cluster && + namelen == strlen(node->filename) && + 0 == strncasecmp(name, node->filename, namelen)) + CYG_ASSERT(false, "node not found in hash, " + "but exists in live list"); + node = node_list_get_next(node); + } + + node = node_list_get_head(&disk->dead_nlist); + while (NULL != node) + { + if (node->parent_cluster == parent_cluster && + namelen == strlen(node->filename) && + 0 == strncasecmp(name, node->filename, namelen)) + CYG_ASSERT(false, "node not found in hash, " + "but exists in dead list"); + node = node_list_get_next(node); + } +} + +static void +node_hash_found_check(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + unsigned int parent_cluster, + fatfs_node_t* node) +{ + fatfs_node_t* n; + + n = node_list_get_head(&disk->live_nlist); + while (NULL != n) + { + if (n == node) + { + if (node->parent_cluster != parent_cluster || + namelen != strlen(node->filename) || + 0 != strncasecmp(name, node->filename, namelen)) + CYG_ASSERT(false, "node_hash_find returned wrong node"); + return; + } + n = node_list_get_next(n); + } + + n = node_list_get_head(&disk->dead_nlist); + while (NULL != n) + { + if (n == node) + { + if (node->parent_cluster != parent_cluster || + namelen != strlen(node->filename) || + 0 != strncasecmp(name, node->filename, namelen)) + CYG_ASSERT(false, "node_hash_find returned wrong node"); + return; + } + n = node_list_get_next(n); + } + + CYG_ASSERT(false, "node not found in dead or live lists, " + "but exists in hash."); +} +#endif // USE_XCHECKS + +//-------------------------------------------------------------------------- + +#ifdef USE_XCHECKS + +# define SANITY_CHECK() \ + CYG_MACRO_START \ + node_lists_check(disk); \ + node_hash_check(&disk->node_hash); \ + CYG_ASSERTC((disk->live_nlist.size + disk->dead_nlist.size) == \ + disk->node_hash.n); \ + CYG_MACRO_END + +# define NODE_FIND_CHECK() \ + CYG_MACRO_START \ + if (NULL == node) \ + node_hash_not_found_check(disk, name, namelen, parent_cluster); \ + else \ + node_hash_found_check(disk, name, namelen, parent_cluster, node); \ + CYG_MACRO_END + +#else // USE_XCHECKS +# define SANITY_CHECK() CYG_EMPTY_STATEMENT +# define NODE_FIND_CHECK() CYG_EMPTY_STATEMENT +#endif // not USE_XCHECKS + +//========================================================================== +//========================================================================== +// Exported functions + +//-------------------------------------------------------------------------- +// fatfs_node_cache_init() +// Initializes node cache of given disk. + +void +fatfs_node_cache_init(fatfs_disk_t *disk) +{ + CYG_CHECK_DATA_PTRC(disk); + + node_list_init(&disk->live_nlist); + node_list_init(&disk->dead_nlist); + node_hash_init(&disk->node_hash); + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_cache_flush() +// Frees all node cache of given disk. + +void +fatfs_node_cache_flush(fatfs_disk_t *disk) +{ + fatfs_node_t *node, *next_node; + + node = node_list_get_head(&disk->live_nlist); + + while (NULL != node) + { + next_node = node_list_get_next(node); + node_list_remove(&disk->live_nlist, node); + if (!node_hash_remove(&disk->node_hash, node)) + CYG_ASSERT(false, "Node not in hash"); + fatfs_tcache_flush(disk, &node->tcache); + free(node); + node = next_node; + } + + node = node_list_get_head(&disk->dead_nlist); + + while (NULL != node) + { + next_node = node_list_get_next(node); + node_list_remove(&disk->dead_nlist, node); + if (!node_hash_remove(&disk->node_hash, node)) + CYG_ASSERT(false, "Node not in hash"); + fatfs_tcache_flush(disk, &node->tcache); + free(node); + node = next_node; + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_alloc() +// Allocates a new node. + +fatfs_node_t* +fatfs_node_alloc(fatfs_disk_t *disk, fatfs_node_t *node_data) +{ + int lsize, dsize; + fatfs_node_t *anode; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node_data); + + lsize = node_list_get_size(&disk->live_nlist); + dsize = node_list_get_size(&disk->dead_nlist); + + CYG_TRACE2(TNC, "lsize=%d dsize=%d", lsize, dsize); + + // Allocate space for a new node if we haven't reached the + // allocation treshold or if we can't reuse dead nodes space + if (dsize > DLIST_KEEP_NUM && (lsize + dsize) >= + (FATFS_NODE_ALLOC_THRESHOLD - 1)) + anode = NULL; + else + anode = (fatfs_node_t *)malloc(sizeof(fatfs_node_t)); + + if (NULL == anode) + { + CYG_TRACE0(TNC, "getting node from dead list"); + + if (dsize <= DLIST_KEEP_NUM) + return NULL; + + anode = node_list_tail_get(&disk->dead_nlist); + if (NULL == anode) + return NULL; + + CYG_TRACE1(TNC, "recycling node='%s'", anode->filename); + + // Flush FAT table cache + fatfs_tcache_flush(disk, &anode->tcache); + + node_list_remove(&disk->dead_nlist, anode); + if (!node_hash_remove(&disk->node_hash, anode)) + CYG_ASSERT(false, "Node not in hash"); + } + + // Init new node + *anode = *node_data; + anode->refcnt = 0; + + // Init FAT table cache + fatfs_tcache_init(disk, &anode->tcache); + + node_list_head_add(&disk->dead_nlist, anode); + if (!node_hash_add(&disk->node_hash, anode)) + CYG_ASSERT(false, "Node already in hash"); + + SANITY_CHECK(); + + return anode; +} + +//-------------------------------------------------------------------------- +// fatfs_node_touch() +// Moves given node to top of its list. +// (When reusing dead node it is always taken from the bottom of list) + +void +fatfs_node_touch(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt); + + if (node->refcnt == 0) + { + node_list_remove(&disk->dead_nlist, node); + node_list_head_add(&disk->dead_nlist, node); + } + else + { + node_list_remove(&disk->live_nlist, node); + node_list_head_add(&disk->live_nlist, node); + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_ref() +// Increases the given node reference count. +// If the reference goes from 0 to 1, than the node +// is moved from dead list to live list. + +void +fatfs_node_ref(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt); + + // Increase node reference counter + node->refcnt++; + if (1 == node->refcnt) + { + CYG_TRACE1(TNC, "node='%s' to live list", node->filename); + // First reference - move node from dead to live list + node_list_remove(&disk->dead_nlist, node); + node_list_head_add(&disk->live_nlist, node); + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_unref() +// Decreases the given node reference count. +// If the reference goes from 1 to 0, than the node +// is moved from live list to top of dead list. +// (When reusing dead node it is always taken from the bottom of list) +// If we are over the allocation treshold the bottom node of +// dead list if freed. + +void +fatfs_node_unref(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt); + CYG_ASSERT(node->refcnt > 0, "node->refcnt <= 0"); + + node->refcnt--; + if (node->refcnt == 0) + { + // No more references - move node from live to dead list + CYG_TRACE1(TNC, "node='%s' to dead list", node->filename); + node_list_remove(&disk->live_nlist, node); + node_list_head_add(&disk->dead_nlist, node); + + // Check the number of allocated nodes and free + // the last node in dead list if we are over + // the treshold and we have enough dead nodes + { + int lsize = node_list_get_size(&disk->live_nlist); + int dsize = node_list_get_size(&disk->dead_nlist); + + if (dsize > DLIST_KEEP_NUM && + (lsize + dsize) >= FATFS_NODE_ALLOC_THRESHOLD) + { + fatfs_node_t *n = node_list_get_tail(&disk->dead_nlist); + CYG_TRACE1(TNC, "freeing node='%s' - to satisfy " + "alloc treshold", n->filename); + fatfs_node_free(disk, n); + } + } + } + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_free() +// Frees node memory and removes it from its list and hash. +// This function must not be called on a referenced node. + +void +fatfs_node_free(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_TRACE2(TNC, "node='%s' refcnt=%d", node->filename, node->refcnt); + CYG_ASSERTC(node->refcnt == 0); + CYG_ASSERTC(node != disk->root); + + // Flush FAT table cache + fatfs_tcache_flush(disk, &node->tcache); + + // Remove from dead list and from hash and free ptr + node_list_remove(&disk->dead_nlist, node); + if (!node_hash_remove(&disk->node_hash, node)) + CYG_ASSERT(false, "node not in hash"); + free(node); + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_rehash() +// Recalculates given node hash key. + +void +fatfs_node_rehash(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + + if (!node_hash_remove_keyless(&disk->node_hash, node)) + CYG_ASSERT(false, "Node not in hash"); + + if (!node_hash_add(&disk->node_hash, node)) + CYG_ASSERT(false, "Node already in hash"); + + SANITY_CHECK(); +} + +//-------------------------------------------------------------------------- +// fatfs_node_find() +// Finds node in hash by its name and parent cluster. +// If no node found NULL is returned. + +fatfs_node_t* +fatfs_node_find(fatfs_disk_t *disk, + const char *name, + unsigned int namelen, + unsigned int parent_cluster) +{ + fatfs_node_t *node; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(name); + + node = node_hash_find(&disk->node_hash, name, namelen, parent_cluster); + + NODE_FIND_CHECK(); + + CYG_TRACE3(TNC, "node name=%s pcluster=%d %s found in cache\n", + name, parent_cluster, ((node != NULL) ? "" : "not")); + + return node; +} + +//-------------------------------------------------------------------------- +// fatfs_get_live_node_count() +// Gets the number of live nodes. + +int +fatfs_get_live_node_count(fatfs_disk_t *disk) +{ + return node_list_get_size(&disk->live_nlist); +} + +//-------------------------------------------------------------------------- +// fatfs_get_dead_node_count() +// Gets the number of dead nodes. + +int +fatfs_get_dead_node_count(fatfs_disk_t *disk) +{ + return node_list_get_size(&disk->dead_nlist); +} + +//-------------------------------------------------------------------------- +// fatfs_node_flush_dead_tcache() +// Flushes FAT table cache of dead nodes. + +void +fatfs_node_flush_dead_tcache(fatfs_disk_t *disk) +{ + fatfs_node_t *node; + + CYG_CHECK_DATA_PTRC(disk); + + node = node_list_get_tail(&disk->dead_nlist); + + while (NULL != node) + { + CYG_TRACE1(TNC, "node='%s'", node->filename); + fatfs_tcache_flush(disk, &node->tcache); + node = node_list_get_prev(node); + } +} + +// ------------------------------------------------------------------------- +// EOF fatfs_ncache.c diff --git a/packages/fs/fat/current/src/fatfs_supp.c b/packages/fs/fat/current/src/fatfs_supp.c new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/src/fatfs_supp.c @@ -0,0 +1,2481 @@ +//========================================================================== +// +// fatfs_supp.c +// +// FAT file system support functions +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-06-30 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fatfs.h" + +//========================================================================== +// FAT defines & macros + +// ------------------------------------------------------------------------- +// FAT dir entry attributes + +#define DENTRY_ATTR_RDONLY 0x01 // Read only +#define DENTRY_ATTR_HIDDEN 0x02 // Hidden +#define DENTRY_ATTR_SYSTEM 0x04 // System +#define DENTRY_ATTR_VOLUME 0x08 // Volume label +#define DENTRY_ATTR_DIR 0x10 // Subdirectory +#define DENTRY_ATTR_ARCHIVE 0x20 // Needs archiving + +// ------------------------------------------------------------------------- +// FAT dir entry attributes macros + +#define DENTRY_IS_RDONLY(_dentry_) ((_dentry_)->attr & DENTRY_ATTR_RDONLY) +#define DENTRY_IS_HIDDEN(_dentry_) ((_dentry_)->attr & DENTRY_ATTR_HIDDEN) +#define DENTRY_IS_SYSTEM(_dentry_) ((_dentry_)->attr & DENTRY_ATTR_SYSTEM) +#define DENTRY_IS_VOLUME(_dentry_) ((_dentry_)->attr & DENTRY_ATTR_VOLUME) +#define DENTRY_IS_DIR(_dentry_) ((_dentry_)->attr & DENTRY_ATTR_DIR) +#define DENTRY_IS_ARCHIVE(_dentry_) ((_dentry_)->attr & DENTRY_ATTR_ARCHIVE) + +#define DENTRY_IS_DELETED(_dentry_) \ + (0xE5 == (unsigned char)((_dentry_)->name[0])) + +#define DENTRY_IS_ZERO(_dentry_) \ + (0x00 == (unsigned char)((_dentry_)->name[0])) + +// ------------------------------------------------------------------------- +// FAT disk data access macros + +// FIXME: support big endian machines! + +#define GET_BYTE(_data_, _var_, _off_) \ + (_var_ = *( ((cyg_uint8 *)_data_) + (_off_) ) ) + +#define GET_WORD(_data_, _var_, _off_) \ + (_var_ = *( ((cyg_uint8 *)_data_) + (_off_) ) | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) << 8) + +#define GET_DWORD(_data_, _var_, _off_) \ + (_var_ = *( ((cyg_uint8 *)_data_) + (_off_)) | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) << 8 | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 2 ) << 16 | \ + *( ((cyg_uint8 *)_data_) + (_off_) + 3 ) << 24) + +#define GET_BYTES(_data_, _var_, _size_, _off_) \ + memcpy((void *)(_var_), (void*)(((cyg_uint8 *)_data_)+(_off_)),_size_) + +#define SET_BYTE(_data_, _val_, _off_) \ + (*( ((cyg_uint8 *)_data_) + (_off_) ) = _val_) + +#define SET_WORD(_data_, _val_, _off_) \ + do { \ + *( ((cyg_uint8 *)_data_) + (_off_) ) = _val_ & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) = (_val_ >> 8) & 0xFF; \ + } while (0) + +#define SET_DWORD(_data_, _val_, _off_) \ + do { \ + *( ((cyg_uint8 *)_data_) + (_off_) ) = _val_ & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 1 ) = (_val_ >> 8) & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 2 ) = (_val_ >> 16) & 0xFF; \ + *( ((cyg_uint8 *)_data_) + (_off_) + 3 ) = (_val_ >> 24) & 0xFF; \ + } while (0) + +#define SET_BYTES(_data_, _var_, _size_, _off_) \ + memcpy((void *)(((cyg_uint8 *)_data_)+(_off_)), (void *)(_var_), _size_) + +// ------------------------------------------------------------------------- +// FAT table entries types + +#define TENTRY_REGULAR 0 // Used when entry points to next file cluster +#define TENTRY_FREE 1 // Free cluster +#define TENTRY_LAST 2 // Last cluster of file +#define TENTRY_RESERVED 3 // Reserved cluster +#define TENTRY_BAD 4 // Bad cluster + +// ------------------------------------------------------------------------- +// FAT table structures size + +#define DENTRY_SIZE 0x20 // Dir entry size + +// ------------------------------------------------------------------------- +// Time & date defines + +#define JD_1_JAN_1970 2440588 // 1 Jan 1970 in Julian day number + +// ------------------------------------------------------------------------- +// Code tracing defines + +#ifdef FATFS_TRACE_FAT_TABLE +# define TFT 1 +#else +# define TFT 0 +#endif + +#ifdef FATFS_TRACE_DIR_ENTRY +# define TDE 1 +#else +# define TDE 0 +#endif + +#ifdef FATFS_TRACE_CLUSTER +# define TCL 1 +#else +# define TCL 0 +#endif + +#ifdef FATFS_TRACE_DATA +# define TDO 1 +#else +# define TDO 0 +#endif + +// ------------------------------------------------------------------------- +// FAT table entry type strings + +#if TFT +static const char *tentry_type_name[5] = { + "REGULAR", "FREE", "LAST", "RESERVED", "BAD" +}; +#endif + +//========================================================================== +// FAT structures + +// ------------------------------------------------------------------------- +// FAT table boot record structure + +typedef struct fat_boot_record_s +{ + cyg_uint16 jump; // 00h : Jump code +// cyg_uint8 jump0; // + NOP + char oem_name[8+1]; // 03h : OEM name + cyg_uint16 bytes_per_sec; // 0Bh : cyg_bytes per sector + cyg_uint8 sec_per_clust; // 0Dh : Sectors per cluster + cyg_uint16 res_sec_num; // 0Eh : Number of reserved sectors + cyg_uint8 fat_tbls_num; // 10h : Number of copies of fat + cyg_uint16 max_root_dents; // 11h : Maximum number of root dir entries + cyg_uint16 sec_num_32; // 13h : Number of sectors in partition < 32MB + cyg_uint8 media_desc; // 15h : Media descriptor + cyg_uint16 sec_per_fat; // 16h : Sectors per FAT + cyg_uint16 sec_per_track; // 18h : Sectors per track + cyg_uint16 heads_num; // 1Ah : Number of heads + cyg_uint32 hsec_num; // 1Ch : Number of hidden sectors + cyg_uint32 sec_num; // 20h : Number of sectors in partition + cyg_uint16 ldrv_num; // 24h : Logical drive number of partition + cyg_uint8 ext_sig; // 26h : Extended signature + cyg_uint32 ser_num; // 27h : Serial number of partition + char vol_name[11+1]; // 2Bh : Volume name of partition + char fat_name[8+1]; // 36h : FAT name +// unsigned char exe_code[448]; // 3Eh : Executable code + unsigned char exe_marker[2]; // 1FEh: Executable marker (55h AAh) +} fat_boot_record_t; + +// ------------------------------------------------------------------------- +// FAT dir entry structure + +typedef struct fat_dir_entry_s +{ + char name[8+1]; // 00h : Name + char ext[3+1]; // 08h : Extension + cyg_uint8 attr; // 0Bh : Attribute + cyg_uint8 nt_reserved; // 0Ch : Win NT Reserved field + cyg_uint8 crt_sec_100; // 0Dh : Creation time ms stamp 0 - 199 + cyg_uint16 crt_time; // 0Eh : Creation time + cyg_uint16 crt_date; // 10h : Creation date + cyg_uint16 acc_date; // 12h : Last access date + cyg_uint16 cluster_HI; // 14h : Starting cluster HI WORD (FAT32) + cyg_uint16 wrt_time; // 16h : Time + cyg_uint16 wrt_date; // 18h : Date + cyg_uint16 cluster; // 1Ah : Starting cluster + cyg_uint32 size; // 1Ch : Size of the file + + fatfs_data_pos_t pos; // Positon on disk +} fat_dir_entry_t; + +// ------------------------------------------------------------------------- +// FAT cluster opts + +typedef enum cluster_opts_e +{ + CO_NONE = 0x00, // NULL option + CO_EXTEND = 0x01, // Extend cluster chain if one cluster too short + CO_ERASE_NEW = 0x02, // Erase newly allocated cluster + CO_MARK_LAST = 0x04 // Mark newly allocated cluster as last +} cluster_opts_t; + +//========================================================================== +// Utility functions + +// ------------------------------------------------------------------------- +// get_val_log2() +// Gets the log2 of given value or returns 0 if value is +// not power of 2. + +static cyg_uint32 +get_val_log2(cyg_uint32 val) +{ + cyg_uint32 i, log2; + + i = val; + log2 = 0; + while (0 == (i & 1)) + { + i >>= 1; + log2++; + } + if (i != 1) + return 0; + else + return log2; +} + +// ------------------------------------------------------------------------- +// get_data_disk_apos() +// Gets the absolute data position on disk from cluster number and +// position inside given cluster. + +static __inline__ cyg_uint32 +get_data_disk_apos(fatfs_disk_t *disk, cyg_uint32 cluster, cyg_uint32 pos) +{ + return (disk->fat_data_pos + disk->cluster_size * (cluster - 2) + pos); +} + +// ------------------------------------------------------------------------- +// jdays_to_gdate() +// Converts juilan days into gregorian date. + +static void +jdays_to_gdate(cyg_uint32 jd, int *day, int *month, int *year) +{ + cyg_uint32 l, n, i, j; + + l = jd + 68569; + n = (4 * l) / 146097; + l = l - (146097 * n + 3) / 4; + i = (4000 * (l + 1)) / 1461001; + l = l - (1461 * i) / 4 + 31; + j = (80 * l) / 2447; + *day = l - (2447 * j) / 80; + + l = j / 11; + *month = j + 2 - (12 * l); + *year = 100 * (n - 49) + i + l; +} + +// ------------------------------------------------------------------------- +// gdate_to_jdays() +// Converts gregorian date to juilan days. + +static void +gdate_to_jdays(int day, int month, int year, cyg_uint32 *jd) +{ + *jd = day - 32075 + 1461*(year + 4800 + (month - 14)/12)/4 + + 367*(month - 2 - (month - 14)/12*12)/12 - + 3*((year + 4900 + (month - 14)/12)/100)/4; +} + +// ------------------------------------------------------------------------- +// date_unix2dos() +// Converts unix timestamp to dos time and date. + +static void +date_unix2dos(cyg_uint32 unix_timestamp, + cyg_uint16 *dos_time, + cyg_uint16 *dos_date) +{ + cyg_uint32 jd; + cyg_uint16 dtime, ddate; + int hour, min, sec; + int day, month, year; + + hour = (unix_timestamp / 3600) % 24; + min = (unix_timestamp / 60) % 60; + sec = unix_timestamp % 60; + + jd = JD_1_JAN_1970 + unix_timestamp / (3600 * 24); + jdays_to_gdate(jd, &day, &month, &year); + + CYG_TRACE7(TDE, "ts=%d date=%d:%d:%d %d-%d-%d", + unix_timestamp, hour, min, sec, year, month, day); + + if (year < 1980) + year = 1980; + + dtime = (hour << 11) | (min << 5) | (sec >> 1); + ddate = ((year - 1980) << 9) | (month << 5) | day; + + CYG_TRACE2(TDE, "dos time=%d date=%d", dtime, ddate); + + if (NULL != dos_time) *dos_time = dtime; + if (NULL != dos_date) *dos_date = ddate; +} + +// ------------------------------------------------------------------------- +// date_dos2unix() +// Converts dos time and date to unix timestamp. + +static void +date_dos2unix(cyg_uint16 dos_time, + cyg_uint16 dos_date, + cyg_uint32 *unix_timestamp) +{ + int hour, min, sec; + int day, month, year; + cyg_uint32 ts; + + sec = (dos_time & ((1<<5)-1)) * 2; + dos_time >>= 5; + min = (dos_time & ((1<<6)-1)); + dos_time >>= 6; + hour = dos_time; + + day = (dos_date & ((1<<5)-1)); + dos_date >>= 5; + month = (dos_date & ((1<<4)-1)); + dos_date >>= 4; + year = dos_date + 1980; + + gdate_to_jdays(day, month, year, &ts); + ts -= JD_1_JAN_1970; + ts = (ts * 24 * 3600) + (sec + min * 60 + hour * 3600); + + *unix_timestamp = ts; + + CYG_TRACE2(TDE, "dos time=%d date=%d", dos_time, dos_date); + CYG_TRACE7(TDE, "timestamp=%d date=%d:%d:%d %d-%d-%d", + ts, hour, min, sec, year, month, day); +} + +//========================================================================== +// FAT boot record functions + +// ------------------------------------------------------------------------- +// print_boot_record() +// Prints FAT boot record. + +#if TFT +static void +print_boot_record(fat_boot_record_t* fbr) +{ + diag_printf("FAT: FBR jump code: 0x%02X\n", fbr->jump); + diag_printf("FAT: FBR oem name: '%.8s'\n", fbr->oem_name); + diag_printf("FAT: FBR bytes per sec: %u\n", fbr->bytes_per_sec); + diag_printf("FAT: FBR sec per cluster: %u\n", fbr->sec_per_clust); + diag_printf("FAT: FBR reserved sec: %u\n", fbr->res_sec_num); + diag_printf("FAT: FBR fat tbls num: %u\n", fbr->fat_tbls_num); + diag_printf("FAT: FBR max root dents: %u\n", fbr->max_root_dents); + diag_printf("FAT: FBR sec num (32): %u\n", fbr->sec_num_32); + diag_printf("FAT: FBR media desc: 0x%02X\n", fbr->media_desc); + diag_printf("FAT: FBR sec per fat: %u\n", fbr->sec_per_fat); + diag_printf("FAT: FBR sec per track: %u\n", fbr->sec_per_track); + diag_printf("FAT: FBR heads num: %u\n", fbr->heads_num); + diag_printf("FAT: FBR hidden sec num: %u\n", fbr->hsec_num); + diag_printf("FAT: FBR sec num: %u\n", fbr->sec_num); + diag_printf("FAT: FBR log drv num: %u\n", fbr->ldrv_num); + diag_printf("FAT: FBR ext sig: 0x%02X\n", fbr->ext_sig); + diag_printf("FAT: FBR ser num: 0x%08X\n", fbr->ser_num); + diag_printf("FAT: FBR vol name: '%.11s'\n", fbr->vol_name); + diag_printf("FAT: FBR fat name: '%.8s'\n", fbr->fat_name); + diag_printf("FAT: FBR exe mark: 0x%02X 0x%02X\n", + fbr->exe_marker[0], fbr->exe_marker[1]); +} +#endif // TFT + +// ------------------------------------------------------------------------- +// read_boot_record() +// Reads FAT boot record from disk. + +static int +read_boot_record(fatfs_disk_t *disk, fat_boot_record_t *fbr) +{ + int len, err; + unsigned char data[0x3E]; + + len = 0x3E; + err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, 0); + if (err != ENOERR) + return err; + + GET_WORD(data, fbr->jump, 0x00); + GET_BYTES(data, fbr->oem_name, 8, 0x03); + GET_WORD(data, fbr->bytes_per_sec, 0x0B); + GET_BYTE(data, fbr->sec_per_clust, 0x0D); + GET_WORD(data, fbr->res_sec_num, 0x0E); + GET_BYTE(data, fbr->fat_tbls_num, 0x10); + GET_WORD(data, fbr->max_root_dents, 0x11); + GET_WORD(data, fbr->sec_num_32, 0x13); + GET_BYTE(data, fbr->media_desc, 0x15); + GET_WORD(data, fbr->sec_per_fat, 0x16); + GET_WORD(data, fbr->sec_per_track, 0x18); + GET_WORD(data, fbr->heads_num, 0x1A); + GET_DWORD(data, fbr->hsec_num, 0x1C); + GET_DWORD(data, fbr->sec_num, 0x20); + GET_WORD(data, fbr->ldrv_num, 0x24); + GET_BYTE(data, fbr->ext_sig, 0x26); + GET_DWORD(data, fbr->ser_num, 0x27); + GET_BYTES(data, fbr->vol_name, 11, 0x2B); + GET_BYTES(data, fbr->fat_name, 8, 0x36); + + // Skip the exe code and read the end marker + len = 0x02; + err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, 0x1FE); + if (err != ENOERR) + return err; + + GET_BYTES(data, fbr->exe_marker, 2, 0); + + // Zero terminate strings + fbr->oem_name[8] = '\0'; + fbr->vol_name[11] = '\0'; + fbr->fat_name[8] = '\0'; + +#if TFT + print_boot_record(fbr); +#endif + + return ENOERR; +} + +//========================================================================== +// FAT table entry functions + +// ------------------------------------------------------------------------- +// read_tentry() +// Reads FAT table entry from disk. + +static int +read_tentry_fat12(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + unsigned char data[2]; + cyg_uint32 pos, num3; + cyg_uint16 e; + int len, err; + + num3 = num * 3; + pos = disk->fat_tbl_pos + (num3 >> 1); + len = 2; + + err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, pos); + if (err != ENOERR) + return err; + + GET_WORD(data, e, 0x00); + + if (0 == (num3 & 1)) + *entry = e & 0x0FFF; + else + *entry = (e >> 4) & 0x0FFF; + + CYG_TRACE3(TFT, "tentry=%x num=%d at %d", *entry, num, pos); + + return ENOERR; +} + +static int +read_tentry_fat16(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + unsigned char data[2]; + cyg_uint32 pos; + cyg_uint16 e; + int len, err; + + pos = disk->fat_tbl_pos + (num << 1); + len = 2; + + err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, pos); + if (err != ENOERR) + return err; + + GET_WORD(data, e, 0x00); + *entry = e; + + CYG_TRACE3(TFT, "tentry=%x num=%d at %d", *entry, num, pos); + + return ENOERR; +} + +static int +read_tentry(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + switch (disk->fat_type) + { + case FATFS_FAT12: return read_tentry_fat12(disk, num, entry); + case FATFS_FAT16: return read_tentry_fat16(disk, num, entry); + default: return EINVAL; + } +} + +// ------------------------------------------------------------------------- +// write_tentry() +// Writes FAT table entry to disk (to all copies of FAT). + +static int +write_tentry_fat12(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + unsigned char data[2]; + cyg_uint32 pos, num3; + cyg_uint16 e; + int i, len, err; + + num3 = num * 3; + pos = disk->fat_tbl_pos + (num3 >> 1); + len = 2; + + err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, pos); + if (err != ENOERR) + return err; + + GET_WORD(data, e, 0x00); + + if (0 == (num3 & 1)) + e = (e & 0xF000) | (*entry & 0x0FFF); + else + e = (e & 0x000F) | ((*entry & 0x0FFF) << 4); + + SET_WORD(data, e, 0x00); + + for (i = 0; i < disk->fat_tbls_num; i++) + { + err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, pos); + if (err != ENOERR) + return err; + + CYG_TRACE4(TFT, "tentry=%x num=%d at %d tbl=%d", *entry, num, pos, i); + + pos += disk->fat_tbl_size; + } + + return ENOERR; +} + +static int +write_tentry_fat16(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + unsigned char data[2]; + cyg_uint32 pos; + cyg_uint16 e; + int i, len, err; + + pos = disk->fat_tbl_pos + (num << 1); + len = 2; + + e = *entry; + SET_WORD(data, e, 0x00); + + for (i = 0; i < disk->fat_tbls_num; i++) + { + err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, pos); + if (err != ENOERR) + return err; + + CYG_TRACE4(TFT, "tentry=%x num=%d at %d tbl=%d", *entry, num, pos, i); + + pos += disk->fat_tbl_size; + } + + return ENOERR; +} + +static int +write_tentry(fatfs_disk_t *disk, cyg_uint32 num, cyg_uint32 *entry) +{ + switch (disk->fat_type) + { + case FATFS_FAT12: return write_tentry_fat12(disk, num, entry); + case FATFS_FAT16: return write_tentry_fat16(disk, num, entry); + default: return EINVAL; + } +} + +// ------------------------------------------------------------------------- +// get_tentry_type() +// Gets the type of FAT table entry. + +static int +get_tentry_type_fat12(fatfs_disk_t *disk, cyg_uint32 entry) +{ + int type; + + if (entry < 0x0FF0) + { + if (0x0000 == entry) type = TENTRY_FREE; + else type = TENTRY_REGULAR; + } + else if (entry >= 0x0FF8) type = TENTRY_LAST; + else if (0x0FF7 == entry) type = TENTRY_BAD; + else type = TENTRY_RESERVED; + +#if TFT + CYG_TRACE2(TFT, "tentry=%04X type=%s", entry, tentry_type_name[type]); +#endif + + return type; +} + +static int +get_tentry_type_fat16(fatfs_disk_t *disk, cyg_uint32 entry) +{ + int type; + + if (entry < 0xFFF0) + { + if (0x0000 == entry) type = TENTRY_FREE; + else type = TENTRY_REGULAR; + } + else if (entry >= 0xFFF8) type = TENTRY_LAST; + else if (0xFFF7 == entry) type = TENTRY_BAD; + else type = TENTRY_RESERVED; + +#if TFT + CYG_TRACE2(TFT, "tentry=%04X type=%s", entry, tentry_type_name[type]); +#endif + + return type; +} + +static int +get_tentry_type(fatfs_disk_t *disk, cyg_uint32 entry) +{ + switch (disk->fat_type) + { + case FATFS_FAT12: return get_tentry_type_fat12(disk, entry); + case FATFS_FAT16: return get_tentry_type_fat16(disk, entry); + default: return TENTRY_BAD; + } +} + +// ------------------------------------------------------------------------- +// set_tentry_type() +// Sets the type of FAT table entry. + +static void +set_tentry_type_fat12(fatfs_disk_t *disk, cyg_uint32 *entry, cyg_uint32 type) +{ + switch (type) + { + case TENTRY_FREE: *entry = 0x0000; break; + case TENTRY_LAST: *entry = 0x0FF8; break; + case TENTRY_RESERVED: *entry = 0x0FF0; break; + case TENTRY_BAD: *entry = 0x0FF7; break; + default: + CYG_ASSERT(false, "Unknown tentry type"); + } +} + +static void +set_tentry_type_fat16(fatfs_disk_t *disk, cyg_uint32 *entry, cyg_uint32 type) +{ + switch (type) + { + case TENTRY_FREE: *entry = 0x0000; break; + case TENTRY_LAST: *entry = 0xFFF8; break; + case TENTRY_RESERVED: *entry = 0xFFF0; break; + case TENTRY_BAD: *entry = 0xFFF7; break; + default: + CYG_ASSERT(false, "Unknown tentry type"); + } +} + +static void +set_tentry_type(fatfs_disk_t *disk, cyg_uint32 *entry, cyg_uint32 type) +{ + switch (disk->fat_type) + { + case FATFS_FAT12: set_tentry_type_fat12(disk, entry, type); + case FATFS_FAT16: set_tentry_type_fat16(disk, entry, type); + } +} + +// ------------------------------------------------------------------------- +// get_tentry_next_cluster() +// Gets the the next file cluster number from FAT table entry. + +static __inline__ cyg_uint32 +get_tentry_next_cluster(fatfs_disk_t *disk, cyg_uint32 entry) +{ + return entry; +} + +// ------------------------------------------------------------------------- +// set_tentry_next_cluster() +// Sets the the next cluster number to FAT table entry. + +static __inline__ void +set_tentry_next_cluster(fatfs_disk_t *disk, + cyg_uint32 *entry, + cyg_uint32 next_cluster) +{ + *entry = next_cluster; +} + +//========================================================================== +// FAT cluster functions + +// ------------------------------------------------------------------------- +// is_pos_inside_cluster() +// Checks if the given position is inside cluster size. + +static __inline__ bool +is_pos_inside_cluster(fatfs_disk_t *disk, cyg_uint32 pos) +{ + return (pos < disk->cluster_size); +} + +// ------------------------------------------------------------------------- +// erase_cluster() +// Erases cluster (fills with 0x00). + +static int +erase_cluster(fatfs_disk_t *disk, cyg_uint32 cluster) +{ + unsigned char data[32]; + cyg_uint32 apos; + int err, len, i; + + len = 32; + memset((void*)data, 0x00, len); + apos = get_data_disk_apos(disk, cluster, 0); + + CYG_TRACE1(TCL, "cluster=%d", cluster); + + for (i = 0; i < (disk->cluster_size >> 5); i++) + { + err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, apos); + if (err != ENOERR) + return err; + apos += len; + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// mark_cluster() +// Marks cluster (sets the cluster's FAT table entry to given type). + +static int +mark_cluster(fatfs_disk_t *disk, cyg_uint32 cluster, cyg_uint32 type) +{ + cyg_uint32 tentry; + int err; + + set_tentry_type(disk, &tentry, type); + err = write_tentry(disk, cluster, &tentry); + + CYG_TRACE3(TCL, "cluster=%d type=%d tentry=%d", + cluster, type, tentry); + return err; +} + +// ------------------------------------------------------------------------- +// link_cluster() +// Links two clusters. + +static int +link_cluster(fatfs_disk_t *disk, cyg_uint32 cluster1, cyg_uint32 cluster2) +{ + cyg_uint32 tentry; + int err; + + set_tentry_next_cluster(disk, &tentry, cluster2); + err = write_tentry(disk, cluster1, &tentry); + + CYG_TRACE3(TCL, "cluster1=%d cluster2=%d tentry=%d", + cluster1, cluster2, tentry); + return err; +} + +// ------------------------------------------------------------------------- +// find_next_free_cluster() +// Finds first free cluster starting from given cluster. +// If none is available free_cluster is set to 0. +// If mark_as_last is set the found cluster is marked as LAST. + +static int +find_next_free_cluster(fatfs_disk_t *disk, + cyg_uint32 start_cluster, + cyg_uint32 *free_cluster, + cluster_opts_t opts) +{ + cyg_uint32 c, tentry; + int err; + + if (start_cluster < 2) + c = 2; + else + c = start_cluster + 1; + + *free_cluster = 0; + + CYG_TRACE1(TCL, "c=%d", c); + + // Search from the starting cluster to the end of FAT and + // from start of FAT to the starting cluster + while (c != start_cluster) + { + // End of FAT check + if (c >= disk->fat_tbl_nents) + { + c = 2; + if (c >= start_cluster) + break; + } + + err = read_tentry(disk, c, &tentry); + if (err != ENOERR) + return err; + + if (TENTRY_FREE == get_tentry_type(disk, tentry)) + { + CYG_TRACE1(TCL, "free_cluster=%d", c); + *free_cluster = c; + if (opts & CO_MARK_LAST) + err = mark_cluster(disk, c, TENTRY_LAST); + if ((err == ENOERR) && (opts & CO_ERASE_NEW)) + err = erase_cluster(disk, c); + return err; + } + c++; + } + + // No free clusters found + CYG_TRACE0(TCL, "!!! no free clusters found"); + + // Return out of space + return ENOSPC; +} + +// ------------------------------------------------------------------------- +// find_and_append_cluster() +// Finds a free cluster on disk and appends it to the given cluster. +// New cluster is marked as LAST. + +static int +find_and_append_cluster(fatfs_disk_t *disk, + cyg_uint32 cluster, + cyg_uint32 *new_cluster, + cluster_opts_t opts) +{ + cyg_uint32 free_cluster; + int err; + + CYG_TRACE1(TCL, "cluster=%d", cluster); + + err = find_next_free_cluster(disk, cluster, + &free_cluster, opts | CO_MARK_LAST); + if (err != ENOERR) + return err; + + CYG_TRACE1(TCL, "free_cluster=%d", free_cluster); + + // Link clusters + err = link_cluster(disk, cluster, free_cluster); + if (err != ENOERR) + return err; + + *new_cluster = free_cluster; + + CYG_TRACE1(TCL, "new_cluster=%d", free_cluster); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// find_nth_cluster0() +// Finds nth cluster in chain (ie nth cluster of file). +// Searching from given position. + +static int +find_nth_cluster0(fatfs_disk_t *disk, + fatfs_data_pos_t *dpos, + cyg_uint32 n, + fatfs_tcache_t *tcache) +{ + cyg_uint32 cluster, cluster_snum; + int err = ENOERR; + + // Trivial case check + if (dpos->cluster_snum == n) + return ENOERR; + + // First look in cache + if (NULL != tcache) + { + cyg_uint32 c, ln; + if (fatfs_tcache_get(disk, tcache, n, &c)) + { + // Cluster in cache + dpos->cluster = c; + dpos->cluster_snum = n; + CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d in cache", c, n); + return ENOERR; + } + else if (fatfs_tcache_get_last(disk, tcache, &ln, &c)) + { + // Cluster not in cache - get last + // in cache and search from there + dpos->cluster = c; + dpos->cluster_snum = ln; + CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d last in cache", c, ln); + } + else + { + // Empty cache - put first cluster in + fatfs_tcache_set(disk, tcache, dpos->cluster_snum, dpos->cluster); + } + } + + cluster = dpos->cluster; + cluster_snum = dpos->cluster_snum; + + CYG_TRACE4(TCL, "cluster=%d cluster_snum=%d n=%d n_to_search=%d", + cluster, cluster_snum, n, n-cluster_snum); + + // Adjust the number of clusters that should be + // walked according to the given position + n -= cluster_snum; + + // Walk the cluster chain for n clusters or + // until last cluster + while (n > 0) + { + cyg_uint32 tentry; + + err = read_tentry(disk, cluster, &tentry); + if (err != ENOERR) + return err; + + switch (get_tentry_type(disk, tentry)) + { + case TENTRY_REGULAR: + break; + case TENTRY_LAST: + // Oops early last cluster + CYG_TRACE1(TCL, "chain end n=%d", n); + err = EEOF; // File has less clusters than given n + // this err should be caught by the + // calling function + goto out; + default: + // Inconsistant FAT table state !!! + CYG_TRACE2(TCL, "!!! inconsistant FAT tentry=%x n=%d", + tentry, n); + err = EIO; + goto out; + } + cluster = get_tentry_next_cluster(disk, tentry); + cluster_snum++; + if (NULL != tcache) + fatfs_tcache_set(disk, tcache, cluster_snum, cluster); + n--; + } + +out: + dpos->cluster = cluster; + dpos->cluster_snum = cluster_snum; + + CYG_TRACE2(TCL, "nth cluster=%d cluster_snum=%d", cluster, cluster_snum); + return err; +} + +// ------------------------------------------------------------------------- +// find_nth_cluster() +// Finds nth cluster in chain (ie nth cluster of file) searching +// from given position. If the chain ends one cluster before the +// given nth cluster and the CO_EXTEND is specifide, than the +// chain is extended by one cluster. + +static int +find_nth_cluster(fatfs_disk_t *disk, + fatfs_data_pos_t *dpos, + cyg_uint32 n, + fatfs_tcache_t *tcache, + cluster_opts_t opts) +{ + int err; + + // Find nth cluster + err = find_nth_cluster0(disk, dpos, n, tcache); + + // EEOF meens that the cluster chain ended early + if ((err != EEOF) || !(opts & CO_EXTEND)) + return err; + + CYG_TRACE2(TCL, "cluster_snum=%d n=%d", dpos->cluster_snum, n); + + // Check if one cluster short + if (dpos->cluster_snum == (n - 1)) + { + // Extend the chain for one cluster + cyg_uint32 new_cluster; + + // Append new cluster to the end of chain + err = find_and_append_cluster(disk, dpos->cluster, + &new_cluster, opts); + if (err != ENOERR) + return err; + + // Update position + dpos->cluster = new_cluster; + dpos->cluster_snum++; + dpos->cluster_pos = 0; + + CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d", + dpos->cluster, dpos->cluster_snum); + + // Update cache + if (NULL != tcache) + fatfs_tcache_set(disk, tcache, dpos->cluster_snum, dpos->cluster); + } + + return err; +} + +// ------------------------------------------------------------------------- +// get_next_cluster() +// Gets next cluster in chain (ie next cluster of file). +// If CO_EXTEND is specified and the current cluster is last in +// chain then the chain is extended by one cluster. + +static int +get_next_cluster(fatfs_disk_t *disk, + fatfs_data_pos_t *dpos, + fatfs_tcache_t *tcache, + cluster_opts_t opts) +{ + int err; + + CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d", + dpos->cluster, dpos->cluster_snum); + + err = find_nth_cluster(disk, dpos, dpos->cluster_snum + 1, tcache, opts); + + if (err != ENOERR) + return err; + + // Update position + dpos->cluster_pos = 0; + + CYG_TRACE2(TCL, "cluster=%d cluster_snum=%d", + dpos->cluster, dpos->cluster_snum); + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// get_data_position_from_off() +// Gets data position from given file offset. +// If CO_EXTEND is specified the file is extended if +// one cluster too short. + +static int +get_data_position_from_off(fatfs_disk_t *disk, + cyg_uint32 first_cluster, + cyg_uint32 offset, + fatfs_data_pos_t *dpos, + fatfs_tcache_t *tcache, + cluster_opts_t opts) +{ + cyg_uint32 n; + int err; + + // Position inside the cluster + dpos->cluster_pos = offset & (disk->cluster_size - 1); + + // Cluster seq number to be searched for + n = offset >> disk->cluster_size_log2; + + // Start searching from first cluster + dpos->cluster = first_cluster; + dpos->cluster_snum = 0; + + CYG_TRACE4(TCL, "off=%d first_cluster=%d cluster_pos=%d n=%d\n", + offset, first_cluster, dpos->cluster_pos, n); + + err = find_nth_cluster(disk, dpos, n, tcache, opts); + + // Err could be EEOF wich means that the given + // offset if out of given file (cluster chain) + + return err; +} + +// ------------------------------------------------------------------------- +// free_cluster_chain() +// Marks all clusters FREE from given cluster to the last cluster in chain. + +static int +free_cluster_chain(fatfs_disk_t *disk, cyg_uint32 start_cluster) +{ + cyg_uint32 c, next_c, tentry; + bool last; + int err; + + CYG_TRACE1(TCL, "start_cluster=%d", start_cluster); + + c = next_c = start_cluster; + last = false; + while (!last) + { + err = read_tentry(disk, c, &tentry); + if (err != ENOERR) + return err; + + switch (get_tentry_type(disk, tentry)) + { + case TENTRY_LAST: + // Last cluster in chain + last = true; + break; + case TENTRY_REGULAR: + // Get next cluster in chain + next_c = get_tentry_next_cluster(disk, tentry); + break; + default: + CYG_TRACE2(TCL, "!!! inconsistant FAT tentry=%x c=%d", + tentry, c); + return EIO; + } + + // Set the current tentry to FREE + set_tentry_type(disk, &tentry, TENTRY_FREE); + err = write_tentry(disk, c, &tentry); + if (err != ENOERR) + return err; + + // Next cluster in chain + c = next_c; + } + + CYG_TRACE1(TCL, "last_cluster=%d", c); + + return ENOERR; +} + +//========================================================================== +// FAT dir entry functions + +// ------------------------------------------------------------------------- +// print_dentry() +// Prints FAT directory entry. + +#if TDE +static void +print_dentry(fat_dir_entry_t* fde) +{ + if (DENTRY_IS_DELETED(fde)) + diag_printf("FAT: FDE name: '?%.7s'\n", &fde->name[1]); + else + diag_printf("FAT: FDE name: '%.8s'\n", fde->name); + diag_printf("FAT: FDE ext: '%.3s'\n", fde->ext); + diag_printf("FAT: FDE attr: %c%c%c%c%c%c\n", + (DENTRY_IS_RDONLY(fde) ? 'R' : '-'), + (DENTRY_IS_HIDDEN(fde) ? 'H' : '-'), + (DENTRY_IS_SYSTEM(fde) ? 'S' : '-'), + (DENTRY_IS_VOLUME(fde) ? 'V' : '-'), + (DENTRY_IS_DIR(fde) ? 'D' : '-'), + (DENTRY_IS_ARCHIVE(fde) ? 'A' : '-')); + diag_printf("FAT: FDE crt time: %u\n", fde->crt_time); + diag_printf("FAT: FDE crt date: %u\n", fde->crt_date); + diag_printf("FAT: FDE acc date: %u\n", fde->acc_date); + diag_printf("FAT: FDE wrt time: %u\n", fde->wrt_time); + diag_printf("FAT: FDE wrt date: %u\n", fde->wrt_date); + diag_printf("FAT: FDE cluster: %u\n", fde->cluster); + diag_printf("FAT: FDE size: %u\n", fde->size); +} +#endif // TDE + +// ------------------------------------------------------------------------- +// read_dentry() +// Reads dir entry from disk. +// If cluster is 0 reads from root dir. + +static int +read_dentry(fatfs_disk_t *disk, + fatfs_data_pos_t *dpos, + fat_dir_entry_t *fde) +{ + unsigned char data[DENTRY_SIZE]; + cyg_uint32 apos; + int len, err; + + // Check if we are reading the root directory + if (0 == dpos->cluster) + apos = disk->fat_root_dir_pos + dpos->cluster_pos; + else + apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos); + + CYG_TRACE3(TDE, "cluster=%d pos=%d apos=%d", + dpos->cluster, dpos->cluster_pos, apos); + + len = DENTRY_SIZE; + err = cyg_blib_read(&disk->blib, (void*)data, &len, 0, apos); + if (err != ENOERR) + return err; + + GET_BYTES(data, fde->name, 8, 0x00); + GET_BYTES(data, fde->ext, 3, 0x08); + GET_BYTE(data, fde->attr, 0x0B); + GET_BYTE(data, fde->nt_reserved, 0x0C); + GET_BYTE(data, fde->crt_sec_100, 0x0D); + GET_WORD(data, fde->crt_time, 0x0E); + GET_WORD(data, fde->crt_date, 0x10); + GET_WORD(data, fde->acc_date, 0x12); + GET_WORD(data, fde->cluster_HI, 0x14); + GET_WORD(data, fde->wrt_time, 0x16); + GET_WORD(data, fde->wrt_date, 0x18); + GET_WORD(data, fde->cluster, 0x1A); + GET_DWORD(data, fde->size, 0x1C); + + // Zero terminate strings + fde->name[8] = '\0'; + fde->ext[3] = '\0'; + + // Store position + fde->pos = *dpos; + +#if TDE + print_dentry(fde); +#endif + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// write_dentry() +// Writes dir entry to disk. +// If cluster is 0 writes to root dir. + +static int +write_dentry(fatfs_disk_t *disk, + fatfs_data_pos_t *dpos, + fat_dir_entry_t *fde) +{ + unsigned char data[DENTRY_SIZE]; + cyg_uint32 apos; + int len, err; + + // Check if we are writting to the root directory + if (0 == dpos->cluster) + apos = disk->fat_root_dir_pos + dpos->cluster_pos; + else + apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos); + + CYG_TRACE3(TDE, "cluster=%d pos=%d apos=%d", + dpos->cluster, dpos->cluster_pos, apos); + + SET_BYTES(data, fde->name, 8, 0x00); + SET_BYTES(data, fde->ext, 3, 0x08); + SET_BYTE(data, fde->attr, 0x0B); + SET_BYTE(data, fde->nt_reserved, 0x0C); + SET_BYTE(data, fde->crt_sec_100, 0x0D); + SET_WORD(data, fde->crt_time, 0x0E); + SET_WORD(data, fde->crt_date, 0x10); + SET_WORD(data, fde->acc_date, 0x12); + SET_WORD(data, fde->cluster_HI, 0x14); + SET_WORD(data, fde->wrt_time, 0x16); + SET_WORD(data, fde->wrt_date, 0x18); + SET_WORD(data, fde->cluster, 0x1A); + SET_DWORD(data, fde->size, 0x1C); + + len = DENTRY_SIZE; + err = cyg_blib_write(&disk->blib, (void*)data, &len, 0, apos); + if (err != ENOERR) + return err; + +#if TDE + print_dentry(fde); +#endif + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// dentry_set_deleted() +// Sets the dentry filename first char to 0xE5 (ie deleted). + +static __inline__ void +dentry_set_deleted(fatfs_disk_t *disk, fat_dir_entry_t *dentry) +{ + dentry->name[0] = 0xE5; +} + +// ------------------------------------------------------------------------- +// get_dentry_filename() +// Gets the filename from given dir entry. + +static void +get_dentry_filename(fat_dir_entry_t *fde, char *name) +{ + int i = 0; + char *cptr = fde->name; + char *cname = name; + + while (*cptr != ' ' && i < 8) + { + *cname++ = *cptr++; i++; + } + cptr = fde->ext; + + if (*cptr != ' ') + { + *cname++ = '.'; i = 0; + while (*cptr != ' ' && i < 3) + { + *cname++ = *cptr++; i++; + } + } + *cname = '\0'; + + CYG_TRACE3(TDE, "dos_name='%s' dos_ext='%s' filename='%s'", + fde->name, fde->ext, name); +} + +// ------------------------------------------------------------------------- +// set_dentry_filename() +// Sets the filename to given dir entry. + +static void +set_dentry_filename(fat_dir_entry_t *fde, const char *name, int namelen) +{ + int i, nidx; + const char *cname; + char *cptr; + + // Special case check + if (name[0] == '.') + { + if (name[1] == '\0') + { + strcpy(fde->name, ". "); + strcpy(fde->ext, " "); + return; + } + else if (name[1] == '.' && name[2] == '\0') + { + strcpy(fde->name, ".. "); + strcpy(fde->ext, " "); + return; + } + } + + if (0 == namelen) + namelen = 9999; + + nidx = 0; + cname = name; + cptr = fde->name; + for (i = 0; i < 8; i++) + { + if (*cname != '.' && *cname != '\0' && nidx++ < namelen) + *cptr++ = toupper(*cname++); + else + *cptr++ = ' '; + } + *cptr = '\0'; + + while (*cname != '.' && *cname != '\0' && nidx++ < namelen) + cname++; + + if (*cname == '.' && nidx++ < namelen) + cname++; + + cptr = fde->ext; + for (i = 0; i < 3; i++) + { + if (*cname != '.' && *cname != '\0' && nidx++ < namelen) + *cptr++ = toupper(*cname++); + else + *cptr++ = ' '; + } + *cptr = '\0'; + + CYG_TRACE4(TDE, "filename='%s' namelen=%d dos_name='%s' dos_ext='%s'", + name, namelen, fde->name, fde->ext); +} + +// ------------------------------------------------------------------------- +// get_next_dentry() +// Gets next dir entry searching from given position to the end. +// Position is expected in DENTRY_SIZE units. +// If EEOF is returned there are no more extries in given dir. + +static int +get_next_dentry(fatfs_disk_t *disk, + fatfs_node_t *dir, + cyg_uint32 *pos, + fat_dir_entry_t *dentry) +{ + fatfs_data_pos_t dpos; + cyg_uint32 off; + int err = ENOERR; + + // Calculate dentry offset + off = *pos * DENTRY_SIZE; + + CYG_TRACE4(TDE, "pos=%d off=%d dir=%p cluster=%d", + off, *pos, dir, dir->cluster); + + // Root dir check + if (0 == dir->cluster) + { + dpos.cluster = 0; + dpos.cluster_snum = 0; + dpos.cluster_pos = off; + } + else + { + err = get_data_position_from_off(disk, dir->cluster, + off, &dpos, &dir->tcache, false); + if (err != ENOERR) + return err; + } + + while (true) + { + // Root dir check + if (0 != dir->cluster) + { + // Change cluster if needed + if (!is_pos_inside_cluster(disk, dpos.cluster_pos)) + err = get_next_cluster(disk, &dpos, &dir->tcache, CO_NONE); + } + else + { + if (*pos >= disk->fat_root_dir_nents) + err = EEOF; + } + + if (err != ENOERR) + break; + + err = read_dentry(disk, &dpos, dentry); + if (err != ENOERR) + return err; + + if (DENTRY_IS_ZERO(dentry)) + { + // If we get a ZERO dir entry, we assume that + // there are no more entries in current dir + CYG_TRACE0(TDE, "ZERO dentry"); + err = EEOF; + break; + } + else if (!DENTRY_IS_DELETED(dentry)) + { + // Dir entry found + CYG_TRACE3(TDE, "dentry_pos=%d cluster=%d pos=%d", + *pos, dpos.cluster, dpos.cluster_pos); + break; + } + + // Increment offset and position + dpos.cluster_pos += DENTRY_SIZE; + (*pos)++; + } + + if (EEOF == err) CYG_TRACE0(TDE, "end of dir"); + + // EEOF could be returned if there are no more entries in this + // dir - this should be cought by the calling function + return err; +} + +// ------------------------------------------------------------------------- +// get_free_dentry() +// Gets free dir entry slot searching from given position. +// If an deleated entry is found, its clusters are freed and the +// entry is reused. +// The directory is extended if needed. + +static int +get_free_dentry(fatfs_disk_t *disk, + fatfs_data_pos_t *dpos, + fatfs_tcache_t *tcache) +{ + fat_dir_entry_t dentry; + fatfs_data_pos_t cdpos; + int err = ENOERR; + + CYG_TRACE3(TDE, "cluster=%d cluster_snum=%d cluster_pos=%d", + dpos->cluster, dpos->cluster_snum, dpos->cluster_pos); + cdpos = *dpos; + + while (true) + { + // Root dir check + if (0 != cdpos.cluster) + { + // Change cluster if needed + if (!is_pos_inside_cluster(disk, cdpos.cluster_pos)) + err = get_next_cluster(disk, &cdpos, tcache, + CO_EXTEND | CO_ERASE_NEW); + } + else + { + if (cdpos.cluster_pos >= disk->fat_root_dir_size) + err = ENOSPC; + } + + if (err != ENOERR) + return err; + + err = read_dentry(disk, &cdpos, &dentry); + if (err != ENOERR) + return err; + + if (DENTRY_IS_DELETED(&dentry)) + { + CYG_TRACE3(TDE, "deleted dentry at cluster=%d cluster_snum=%d " + "cluster_pos=%d", cdpos.cluster, cdpos.cluster_snum, + cdpos.cluster_pos); + + // Retrun found dentry position + *dpos = cdpos; + return ENOERR; + } + else if (DENTRY_IS_ZERO(&dentry)) + { + CYG_TRACE3(TDE, "zero dentry at cluster=%d cluster_snum=%d " + "cluster_pos=%d", cdpos.cluster, cdpos.cluster_snum, + cdpos.cluster_pos); + + // Retrun found dentry position + *dpos = cdpos; + return ENOERR; + } + + // Increment current position + cdpos.cluster_pos += DENTRY_SIZE; + } +} + +// ------------------------------------------------------------------------- +// dentry_to_node() +// Converts FAT dir entry to node. + +static void +dentry_to_node(fat_dir_entry_t *dentry, + fatfs_node_t *node) +{ + get_dentry_filename(dentry, node->filename); + + if (DENTRY_IS_DIR(dentry)) + node->mode = __stat_mode_DIR; + else + node->mode = __stat_mode_REG; + + date_dos2unix(dentry->crt_time, dentry->crt_date, &node->ctime); + date_dos2unix(0, dentry->acc_date, &node->atime); + date_dos2unix(dentry->wrt_time, dentry->wrt_date, &node->mtime); + + node->size = dentry->size; + node->priv_data = dentry->nt_reserved; + node->cluster = dentry->cluster; + node->dentry_pos = dentry->pos; +} + +// ------------------------------------------------------------------------- +// node_to_dentry() +// Converts node to FAT dir entry. + +static void +node_to_dentry(fatfs_node_t *node, fat_dir_entry_t *dentry) +{ + set_dentry_filename(dentry, node->filename, 0); + + if (node->mode == __stat_mode_DIR) + dentry->attr = DENTRY_ATTR_DIR; + else + dentry->attr = DENTRY_ATTR_ARCHIVE; + + date_unix2dos(node->ctime, &dentry->crt_time, &dentry->crt_date); + date_unix2dos(node->atime, NULL, &dentry->acc_date); + date_unix2dos(node->mtime, &dentry->wrt_time, &dentry->wrt_date); + + dentry->crt_sec_100 = 0; //FIXME + dentry->size = node->size; + dentry->nt_reserved = node->priv_data; + dentry->cluster = node->cluster; + dentry->cluster_HI = 0; + dentry->pos = node->dentry_pos; +} + +//========================================================================== +// FAT data functions + +// ------------------------------------------------------------------------- +// read_data() +// Reads data from given position. + +static int +read_data(fatfs_disk_t *disk, + void *data, + cyg_uint32 *len, + fatfs_data_pos_t *dpos, + fatfs_tcache_t *tcache) +{ + unsigned char *buf; + cyg_uint32 apos; + cyg_uint32 size; + int err; + + // Initialize variables and get the absolute starting pos on disk + buf = (unsigned char *)data; + size = *len; + apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos); + err = ENOERR; + + CYG_TRACE5(TDO, "len=%d cluster=%d cluster_pos=%d " + "cluster_snum=%d apos=%d", *len, dpos->cluster, + dpos->cluster_pos, dpos->cluster_snum, apos); + + while (size > 0) + { + cyg_uint32 csize; + + // Check if we are still inside current cluster + if (!is_pos_inside_cluster(disk, dpos->cluster_pos)) + { + // Get next cluster of file and adjust absolute disk position + err = get_next_cluster(disk, dpos, tcache, CO_NONE); + if (err != ENOERR) + goto out; + apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos); + } + + // Adjust the data chunk size to be read to the cluster boundary + if (size > (disk->cluster_size - dpos->cluster_pos)) + csize = disk->cluster_size - dpos->cluster_pos; + else + csize = size; + + CYG_TRACE5(TDO, "-- len=%d cluster=%d cluster_pos=%d " + "cluster_snum=%d apos=%d", csize, dpos->cluster, + dpos->cluster_pos, dpos->cluster_snum, apos); + + // Read data chunk + err = cyg_blib_read(&disk->blib, (void*)buf, &csize, 0, apos); + + if (err != ENOERR) + goto out; + + // Adjust running variables + buf += csize; + dpos->cluster_pos += csize; + apos += csize; + size -= csize; + } + +out: + *len -= size; + + CYG_TRACE1(TDO, "total len=%d", *len); + + return err; +} + +// ------------------------------------------------------------------------- +// write_data() +// Writes data to given position. + +static int +write_data(fatfs_disk_t *disk, + void *data, + cyg_uint32 *len, + fatfs_data_pos_t *dpos, + fatfs_tcache_t *tcache) +{ + unsigned char *buf; + cyg_uint32 apos; + cyg_uint32 size; + int err; + + // Initialize variables and get the absolute starting pos on disk + buf = (unsigned char *)data; + size = *len; + apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos); + err = ENOERR; + + CYG_TRACE5(TDO, "len=%d cluster=%d cluster_pos=%d " + "cluster_snum=%d apos=%d", *len, dpos->cluster, + dpos->cluster_pos, dpos->cluster_snum, apos); + + while (size > 0) + { + cyg_uint32 csize; + + // Check if we are still inside current cluster + if (!is_pos_inside_cluster(disk, dpos->cluster_pos)) + { + // Get next cluster of file and adjust absolute disk position, + // if at the last cluster try to extend the cluster chain + err = get_next_cluster(disk, dpos, tcache, CO_EXTEND); + if (err != ENOERR) + goto out; + apos = get_data_disk_apos(disk, dpos->cluster, dpos->cluster_pos); + } + + // Adjust the data chunk size to be read to the cluster boundary + if (size > (disk->cluster_size - dpos->cluster_pos)) + csize = disk->cluster_size - dpos->cluster_pos; + else + csize = size; + + CYG_TRACE5(TDO, "-- len=%d cluster=%d cluster_pos=%d " + "cluster_snum=%d apos=%d", csize, dpos->cluster, + dpos->cluster_pos, dpos->cluster_snum, apos); + + // Write data chunk + err = cyg_blib_write(&disk->blib, (void*)buf, &csize, 0, apos); + + if (err != ENOERR) + goto out; + + // Adjust running variables + buf += csize; + dpos->cluster_pos += csize; + apos += csize; + size -= csize; + } + +out: + *len -= size; + + CYG_TRACE1(TDO, "total len=%d", *len); + + return err; +} + +//========================================================================== +// Misc functions + +// ------------------------------------------------------------------------- +// init_node() +// Initializes file attributes of a new node. + +static void +init_node_fattr(fatfs_node_t *node, + const char *name, + int namelen, + mode_t mode, + cyg_uint32 parent_cluster, + fatfs_data_pos_t *dentry_pos) +{ + if (namelen == 0) + namelen = 12; + + strncpy(node->filename, name, namelen); + node->filename[namelen] = '\0'; + + node->mode = mode; + node->ctime = + node->atime = + node->mtime = cyg_timestamp(); + + node->priv_data = 0; + node->size = 0; + node->cluster = 0; + node->parent_cluster = parent_cluster; + node->dentry_pos = *dentry_pos; +} + +// ------------------------------------------------------------------------- +// is_node_root_node() +// Check if the given node is the root dir node + +static __inline__ bool +is_node_root_node(fatfs_node_t *node) +{ + return (node->filename[0] == '\0' && node->cluster == 0); +} + +//========================================================================== +//========================================================================== +// Exported functions + +// ------------------------------------------------------------------------- +// fatfs_get_disk_info() +// Gets disk info. + +int +fatfs_get_disk_info(fatfs_disk_t *disk) +{ + int err; + cyg_uint32 sec_num, root_dir_sec_num; + cyg_uint32 data_sec_num, data_clu_num; + fat_boot_record_t boot_rec; + + CYG_CHECK_DATA_PTRC(disk); + + err = read_boot_record(disk, &boot_rec); + if (err != ENOERR) + return err; + + // Check some known boot record values + if (0x29 != boot_rec.ext_sig || + 0x55 != boot_rec.exe_marker[0] || + 0xAA != boot_rec.exe_marker[1]) + return EINVAL; + + // Determine number of sectors + if (boot_rec.sec_num_32 != 0) + sec_num = boot_rec.sec_num_32; + else + sec_num = boot_rec.sec_num; + + // Number of sectors used by root directory + root_dir_sec_num = ((boot_rec.max_root_dents * DENTRY_SIZE) + + (boot_rec.bytes_per_sec - 1)) / boot_rec.bytes_per_sec; + + // Number of data sectors + data_sec_num = sec_num - (boot_rec.res_sec_num + + (boot_rec.fat_tbls_num * boot_rec.sec_per_fat) + root_dir_sec_num); + + // Number of data clusters + data_clu_num = data_sec_num / boot_rec.sec_per_clust; + + // Determine the type of FAT based on number of data clusters + if (data_clu_num < 4085) + disk->fat_type = FATFS_FAT12; + else if (data_clu_num < 65525) + disk->fat_type = FATFS_FAT16; + else // FAT32 + return EINVAL; + + // Sector and cluster sizes + disk->sector_size = boot_rec.bytes_per_sec; + disk->sector_size_log2 = get_val_log2(disk->sector_size); + disk->cluster_size = boot_rec.bytes_per_sec * boot_rec.sec_per_clust; + disk->cluster_size_log2 = get_val_log2(disk->cluster_size); + + // Sector and cluster size should always be a power of 2 + if (0 == disk->sector_size_log2 || 0 == disk->cluster_size_log2) + return EINVAL; + + // FAT table and root dir sizes and position + disk->fat_tbl_pos = boot_rec.bytes_per_sec * boot_rec.res_sec_num; + disk->fat_tbl_size = boot_rec.bytes_per_sec * boot_rec.sec_per_fat; + disk->fat_tbl_nents = data_clu_num + 2; + disk->fat_tbls_num = boot_rec.fat_tbls_num; + disk->fat_root_dir_pos = disk->fat_tbl_pos + + disk->fat_tbls_num * disk->fat_tbl_size; + disk->fat_root_dir_size = boot_rec.max_root_dents * DENTRY_SIZE; + disk->fat_root_dir_nents = boot_rec.max_root_dents; + disk->fat_data_pos = disk->fat_root_dir_pos + disk->fat_root_dir_size; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_get_root_node() +// Gets root dir node. + +void +fatfs_get_root_node(fatfs_disk_t *disk, fatfs_node_t *node) +{ + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + + node->mode = __stat_mode_DIR; + node->size = disk->fat_root_dir_size; + node->ctime = 0; + node->atime = 0; + node->mtime = 0; + node->filename[0] = '\0'; + node->cluster = 0; + node->parent_cluster = 0; + + node->dentry_pos.cluster = 0; + node->dentry_pos.cluster_snum = 0; + node->dentry_pos.cluster_pos = 0; +} + +// ------------------------------------------------------------------------- +// fatfs_is_node_root_node() +// Check if the given node is root dir node. + +bool +fatfs_is_node_root_node(fatfs_node_t *node) +{ + return(is_node_root_node(node)); +} + +// ------------------------------------------------------------------------- +// fatfs_get_dir_entry_node() +// Gets dir entry node at given position. +// If there is no dir entry at given position the next closest +// dir entry and its position are returned. +// If EEOF error is returned, then there are no more dir +// entries in given dir. + +int +fatfs_get_dir_entry_node(fatfs_disk_t *disk, + fatfs_node_t *dir, + cyg_uint32 *pos, + fatfs_node_t *node) +{ + fat_dir_entry_t dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir); + CYG_CHECK_DATA_PTRC(pos); + CYG_CHECK_DATA_PTRC(node); + + err = get_next_dentry(disk, dir, pos, &dentry); + + if (err != ENOERR) + return err; + + dentry_to_node(&dentry, node); + node->parent_cluster = dir->cluster; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_write_node() +// Writes node attributes to its dir entry (to disk). + +int +fatfs_write_file_attr(fatfs_disk_t *disk, fatfs_node_t *node) +{ + fat_dir_entry_t dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + + node_to_dentry(node, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_delete_file() +// Marks file dir entry as deleted. + +int +fatfs_delete_file(fatfs_disk_t *disk, fatfs_node_t *node) +{ + fat_dir_entry_t dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + + // Can't delete root + if (is_node_root_node(node)) + return EINVAL; + + CYG_TRACE1(TDE, "filename='%s'", node->filename); + + node_to_dentry(node, &dentry); + + // Check if we are about to delete a directory + if (DENTRY_IS_DIR(&dentry)) + { + fat_dir_entry_t cdentry; + cyg_uint32 pos = 0; + int i = 0; + + CYG_TRACE0(TDE, "got directory"); + // Count number of entries in this dir + while (true) + { + err = get_next_dentry(disk, node, &pos, &cdentry); + + if (EEOF == err) + break; + else if (err != ENOERR) + return err; + i++; pos++; + } + CYG_TRACE1(TDE, "child count=%d", i); + + // Check if the dir is empty (except '.' and '..') + if (i > 2) + return EPERM; + } + + // Free file clusters + free_cluster_chain(disk, dentry.cluster); + dentry_set_deleted(disk, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_create_file() +// Creates a new file. + +int +fatfs_create_file(fatfs_disk_t *disk, + fatfs_node_t *dir, + const char *name, + int namelen, + fatfs_node_t *node) +{ + fat_dir_entry_t dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir); + CYG_CHECK_DATA_PTRC(name); + CYG_CHECK_DATA_PTRC(node); + + CYG_TRACE2(TDE, "filename='%s' parent='%s'", name, dir->filename); + + dentry.pos.cluster = dir->cluster; + dentry.pos.cluster_snum = 0; + dentry.pos.cluster_pos = 0; + + // Get free dir entry in parent dir + err = get_free_dentry(disk, &dentry.pos, &dir->tcache); + if (err != ENOERR) + return err; + + // Set new file attributes + init_node_fattr(node, name, namelen, __stat_mode_REG, + dir->cluster, &dentry.pos); + + // Write new dir dentry + node_to_dentry(node, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + if (err != ENOERR) + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_create_dir() +// Creates a new directory. + +int +fatfs_create_dir(fatfs_disk_t *disk, + fatfs_node_t *dir, + const char *name, + int namelen, + fatfs_node_t *node) +{ + fat_dir_entry_t dentry; + fatfs_node_t cnode; + cyg_uint32 free_cluster; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir); + CYG_CHECK_DATA_PTRC(name); + CYG_CHECK_DATA_PTRC(node); + + CYG_TRACE2(TDE, "filename='%s' parent='%s'", name, dir->filename); + + // Get free cluster + err = find_next_free_cluster(disk, 0, &free_cluster, + CO_MARK_LAST | CO_ERASE_NEW); + if (err != ENOERR) + return err; + + dentry.pos.cluster = dir->cluster; + dentry.pos.cluster_snum = 0; + dentry.pos.cluster_pos = 0; + + // Get free dir entry in parent dir + err = get_free_dentry(disk, &dentry.pos, &dir->tcache); + if (err != ENOERR) + return err; + + // Set new dir attributes + init_node_fattr(node, name, namelen, __stat_mode_DIR, + dir->cluster, &dentry.pos); + node->cluster = free_cluster; + + // Write new dir dentry + node_to_dentry(node, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + if (err != ENOERR) + return err; + + // Starting position of new dir entries + dentry.pos.cluster = node->cluster; + dentry.pos.cluster_snum = 0; + dentry.pos.cluster_pos = 0; + + CYG_TRACE0(TDE, "Creating '.' entry"); + // Set '.' dir attributes + init_node_fattr(&cnode, ".", 0, __stat_mode_DIR, + node->cluster, &dentry.pos); + cnode.cluster = node->cluster; + + // Write '.' dentry + node_to_dentry(&cnode, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + if (err != ENOERR) + return err; + + dentry.pos.cluster_pos += DENTRY_SIZE; + + CYG_TRACE0(TDE, "Creating '..' entry"); + // Set '..' dir attributes + init_node_fattr(&cnode, "..", 0, __stat_mode_DIR, + node->cluster, &dentry.pos); + cnode.cluster = dir->cluster; + + // Write '..' dentry + node_to_dentry(&cnode, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + if (err != ENOERR) + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_trunc_file() +// Truncates a file to zero length. + +int +fatfs_trunc_file(fatfs_disk_t *disk, fatfs_node_t *node) +{ + fat_dir_entry_t dentry; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + + CYG_TRACE1(TDE, "file='%s'", node->filename); + + // Check for dir + if (S_ISDIR(node->mode)) + return EINVAL; + + // Trivial case check + if (0 == node->size) + return ENOERR; + + // Free cluster chain + err = free_cluster_chain(disk, node->cluster); + if (err != ENOERR) + return err; + + // Flush tcache + fatfs_tcache_flush(disk, &node->tcache); + + // Update node attributes + node->cluster = 0; + node->size = 0; + node->mtime = + node->atime = cyg_timestamp(); + + // Write dentry + node_to_dentry(node, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + if (err != ENOERR) + return err; + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_rename_file() +// Renames a file. + +int +fatfs_rename_file(fatfs_disk_t *disk, + fatfs_node_t *dir1, + fatfs_node_t *node, + fatfs_node_t *dir2, + const char *name, + int namelen) +{ + fat_dir_entry_t dentry; + fatfs_data_pos_t dpos; + int err; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(dir1); + CYG_CHECK_DATA_PTRC(node); + CYG_CHECK_DATA_PTRC(dir2); + CYG_CHECK_DATA_PTRC(name); + + // Can't rename root + if (is_node_root_node(node)) + return EINVAL; + + CYG_TRACE5(TDE, "dir1=%p '%s' to dir2=%p '%s' (%d)", + dir1, node->filename, dir2, name, namelen); + + strncpy(node->filename, name, namelen); + node->filename[namelen] = '\0'; + + // Moving around in same dir + if (dir1 == dir2) + { + CYG_TRACE0(TDE, "same dir"); + node_to_dentry(node, &dentry); + err = write_dentry(disk, &dentry.pos, &dentry); + return err; + } + + CYG_TRACE0(TDE, "different dirs"); + + // Moveing around in different dirs + + dpos.cluster = dir2->cluster; + dpos.cluster_snum = 0; + dpos.cluster_pos = 0; + + CYG_TRACE0(TDE, "writting to new dir"); + + // Get free dir entry in target dir + err = get_free_dentry(disk, &dpos, &dir2->tcache); + if (err != ENOERR) + return err; + + // Write node dentry to new location + node_to_dentry(node, &dentry); + err = write_dentry(disk, &dpos, &dentry); + if (err != ENOERR) + return err; + + CYG_TRACE0(TDE, "deleting from old dir"); + + // Deleate dentry at old location + dentry_set_deleted(disk, &dentry); + dentry.size = 0; + dentry.cluster = 0; + err = write_dentry(disk, &dentry.pos, &dentry); + if (err != ENOERR) + return err; + + // Set node new position and parent cluster + node->dentry_pos = dpos; + node->parent_cluster = dir2->cluster; + + // If we moved a directory, we also have to correct the '..' entry + if (__stat_mode_DIR == node->mode) + { + fat_dir_entry_t cdentry; + cyg_uint32 pos = 0; + + CYG_TRACE0(TDE, "moving directory - correcting '..' entry"); + + while (true) + { + err = get_next_dentry(disk, node, &pos, &cdentry); + + if (EEOF == err) + return EIO; // This dir doesn't have the '..' entry, + // that means something is very wrong + else if (err != ENOERR) + return err; + + if (0 == strncmp("..", cdentry.name, 2)) + { + cdentry.cluster = dir2->cluster; + err = write_dentry(disk, &cdentry.pos, &cdentry); + if (err != ENOERR) + return err; + break; + } + pos++; + } + } + + return ENOERR; +} + +// ------------------------------------------------------------------------- +// fatfs_read_data() +// Reads data from disk. + +int +fatfs_read_data(fatfs_disk_t *disk, + fatfs_node_t *node, + void *data, + cyg_uint32 *len, + cyg_uint32 off) +{ + int err; + fatfs_data_pos_t dpos; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_CHECK_DATA_PTRC(data); + CYG_CHECK_DATA_PTRC(len); + + err = get_data_position_from_off(disk, node->cluster, off, + &dpos, &node->tcache, CO_NONE); + if (err != ENOERR) + return err; + + err = read_data(disk, data, len, &dpos, &node->tcache); + return err; +} + +// ------------------------------------------------------------------------- +// fatfs_write_data() +// Writes data to disk. + +int +fatfs_write_data(fatfs_disk_t *disk, + fatfs_node_t *node, + void *data, + cyg_uint32 *len, + cyg_uint32 off) +{ + int err; + fatfs_data_pos_t dpos; + + CYG_CHECK_DATA_PTRC(disk); + CYG_CHECK_DATA_PTRC(node); + CYG_CHECK_DATA_PTRC(data); + CYG_CHECK_DATA_PTRC(len); + + // Check if this file has a zero size and no first cluster + if (0 == node->size && 0 == node->cluster) + { + cyg_uint32 free_cluster; + + CYG_TRACE0(TDO, "new cluster for zero file"); + // Get free cluster + err = find_next_free_cluster(disk, 0, &free_cluster, CO_MARK_LAST); + if (err != ENOERR) + { + *len = 0; + return err; + } + node->cluster = free_cluster; + } + + err = get_data_position_from_off(disk, node->cluster, off, + &dpos, &node->tcache, + CO_MARK_LAST | CO_EXTEND); + if (err != ENOERR) + { + *len = 0; + return err; + } + + err = write_data(disk, data, len, &dpos, &node->tcache); + return err; +} + +// ------------------------------------------------------------------------- +// Support routines +// These enable the definition of local versions of certain routines +// if the given packages are not present. + +#ifndef CYGPKG_LIBC_I18N + +__externC int +toupper( int c ) +{ + return (('a' <= c) && (c <= 'z')) ? c - 'a' + 'A' : c ; +} + +#endif + +#ifndef CYGFUN_LIBC_STRING_BSD_FUNCS + +__externC int +strcasecmp( const char *s1, const char *s2 ) +{ + int ret; + CYG_REPORT_FUNCNAMETYPE( "strcasecmp", "returning %d" ); + CYG_REPORT_FUNCARG2( "s1=%08x, s2=%08x", s1, s2 ); + + CYG_CHECK_DATA_PTR( s1, "s1 is not a valid pointer!" ); + CYG_CHECK_DATA_PTR( s2, "s2 is not a valid pointer!" ); + + while (*s1 != '\0' && toupper(*s1) == toupper(*s2)) + { + s1++; + s2++; + } + + ret = toupper(*(unsigned char *) s1) - toupper(*(unsigned char *) s2); + CYG_REPORT_RETVAL( ret ); + return ret; +} + +__externC int +strncasecmp( const char *s1, const char *s2, size_t n ) +{ + int ret; + CYG_REPORT_FUNCNAMETYPE( "strncasecmp", "returning %d" ); + CYG_REPORT_FUNCARG3( "s1=%08x, s2=%08x, n=%d", s1, s2, n ); + + if (n == 0) + { + CYG_REPORT_RETVAL(0); + return 0; + } + + CYG_CHECK_DATA_PTR( s1, "s1 is not a valid pointer!" ); + CYG_CHECK_DATA_PTR( s2, "s2 is not a valid pointer!" ); + + while (n-- != 0 && toupper(*s1) == toupper(*s2)) + { + if (n == 0 || *s1 == '\0' || *s2 == '\0') + break; + s1++; + s2++; + } + + ret = toupper(*(unsigned char *) s1) - toupper(*(unsigned char *) s2); + CYG_REPORT_RETVAL( ret ); + return ret; +} + +#endif + +// ------------------------------------------------------------------------- +// EOF fatfs_supp.c diff --git a/packages/fs/fat/current/src/fatfs_tcache.c b/packages/fs/fat/current/src/fatfs_tcache.c new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/src/fatfs_tcache.c @@ -0,0 +1,284 @@ +//========================================================================== +// +// fatfs_tcache.c +// +// FAT file system FAT table cache functions +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-06-27 +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fatfs.h" + +//========================================================================== +// Defines & macros + +#define TC_INC FATFS_FAT_TABLE_CACHE_INCREMENT + +#ifdef CYGDBG_USE_ASSERTS +# define USE_ASSERTS 1 +#endif + +#ifdef FATFS_TRACE_FAT_TABLE_CACHE +# define TTC 1 +#else +# define TTC 0 +#endif + +//========================================================================== +// Private functions + +//-------------------------------------------------------------------------- +// tcache_increment() +// Allocates or reallocates memory for FAT table cache. +// Returns true is allocation succeded. + +static bool +tcache_increment(fatfs_disk_t *disk, fatfs_tcache_t *tcache) +{ + CYG_TRACE2(TTC, "max_size=%d size=%d", tcache->max_size, tcache->size); + + if (NULL == tcache->clusters) + { + tcache->clusters = + (cyg_uint32 *)cyg_mempool_var_try_alloc(disk->tcache_mpool_h, + TC_INC*sizeof(cyg_uint32)); + + if (NULL == tcache->clusters) + return false; + + tcache->max_size = TC_INC; + } + else + { + cyg_uint32 *newc; + + newc = (cyg_uint32 *)cyg_mempool_var_try_alloc(disk->tcache_mpool_h, + (tcache->max_size+TC_INC)*sizeof(cyg_uint32)); + + if (NULL == newc) + return false; + + memcpy(newc, tcache->clusters, (tcache->max_size*sizeof(cyg_uint32))); + cyg_mempool_var_free(disk->tcache_mpool_h, tcache->clusters); + + tcache->clusters = newc; + tcache->max_size += TC_INC; + } + + CYG_TRACE2(TTC, "max_size=%d size=%d", tcache->max_size, tcache->size); + + return true; +} + +//========================================================================== +// Exported functions + +//-------------------------------------------------------------------------- +// fatfs_tcache_create() +// Creates FAT table caches memory pool. + +int +fatfs_tcache_create(fatfs_disk_t *disk, cyg_uint32 mem_size) +{ + disk->tcache_mem = (cyg_uint8 *)malloc(mem_size); + if (NULL == disk->tcache_mem) + return ENOMEM; + + cyg_mempool_var_create(disk->tcache_mem, mem_size, + &disk->tcache_mpool_h, &disk->tcache_mpool); + return ENOERR; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_delete() +// Deletes FAT table caches memory pool. + +void +fatfs_tcache_delete(fatfs_disk_t *disk) +{ + cyg_mempool_var_delete(disk->tcache_mpool_h); + free(disk->tcache_mem); +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_init() +// Initializes FAT table cache structure. + +void +fatfs_tcache_init(fatfs_disk_t *disk, fatfs_tcache_t *tcache) +{ + CYG_CHECK_DATA_PTRC(tcache); + + tcache->clusters = NULL; + tcache->size = 0; + tcache->max_size = 0; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_flush() +// Frees given tcache. + +void +fatfs_tcache_flush(fatfs_disk_t *disk, fatfs_tcache_t *tcache) +{ + CYG_CHECK_DATA_PTRC(tcache); + + if (tcache->clusters != NULL) + { + cyg_mempool_var_free(disk->tcache_mpool_h, tcache->clusters); + tcache->clusters = NULL; + tcache->size = 0; + tcache->max_size = 0; + } +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_get() +// Gets the cluster from cache at given position. +// Returns true if cluster in cache. + +bool +fatfs_tcache_get(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 num, + cyg_uint32 *cluster) +{ + CYG_CHECK_DATA_PTRC(tcache); + CYG_TRACE2(TTC, "size=%d max_size=%d", tcache->size, tcache->max_size); + + // Check if requested cluster is cached + if (num >= tcache->size) + { + CYG_TRACE1(TTC, "cluster num=%d not in tcache", num); + return false; + } + + *cluster = tcache->clusters[num]; + CYG_TRACE2(TTC, "got cluster=%d num=%d from tcache", *cluster, num); + return true; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_get() +// Gets last cluster in cache. +// Returns false if cache is empty. + +bool +fatfs_tcache_get_last(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 *num, + cyg_uint32 *cluster) +{ + CYG_CHECK_DATA_PTRC(tcache); + CYG_TRACE2(TTC, "size=%d max_size=%d", tcache->size, tcache->max_size); + + // Check if we have something in cache + if (tcache->size == 0) + return false; + + *num = tcache->size - 1; + *cluster = tcache->clusters[*num]; + CYG_TRACE2(TTC, "got last cluster=%d num=%d from tcache", *cluster, *num); + return true; +} + +//-------------------------------------------------------------------------- +// fatfs_tcache_set() +// Sets given cluster in cache at given position. +// There can't be any holes in cache (ie in order to set cluster +// at position N all clusters from 0 to N-1 must be set before) +// Returns true if set succeded. + +bool +fatfs_tcache_set(fatfs_disk_t *disk, + fatfs_tcache_t *tcache, + cyg_uint32 num, + cyg_uint32 cluster) +{ + CYG_CHECK_DATA_PTRC(tcache); + CYG_TRACE4(TTC, "num=%d cluster=%d tcache size=%d max_size=%d", + num, cluster, tcache->size, tcache->max_size); + + if (num > tcache->size) + { + CYG_TRACE0(TTC, "won't make a hole in tcache"); + return false; + } + + if (num < tcache->size) + { + CYG_TRACE2(TTC, "set cluster=%d num=%d in tcache", cluster, num); + tcache->clusters[num] = cluster; + return true; + } + + // Here num is equal to size - check if we need to increment cache + if (tcache->size == tcache->max_size) + { + // If we can't get memory to increment the cache + // try to flush dead nodes fat table cache + if (!tcache_increment(disk, tcache)) + { + fatfs_node_flush_dead_tcache(disk); + if (!tcache_increment(disk, tcache)) + return false; + } + } + + tcache->size++; + tcache->clusters[num] = cluster; + + CYG_TRACE2(TTC, "added cluster=%d num=%d to tcache", cluster, num); + + return true; +} + +// ------------------------------------------------------------------------- +// EOF fatfs_tcache.c diff --git a/packages/fs/fat/current/tests/fileio1.c b/packages/fs/fat/current/tests/fileio1.c new file mode 100644 --- /dev/null +++ b/packages/fs/fat/current/tests/fileio1.c @@ -0,0 +1,686 @@ +//========================================================================== +// +// fileio1.c +// +// Test fileio system +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg +// Contributors: nickg +// Date: 2000-05-25 +// Purpose: Test fileio system +// Description: This test uses the testfs to check out the initialization +// and basic operation of the fileio system +// +// +// +// +// +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include + +#include // base kernel types +#include // tracing macros +#include // assertion macros + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include // HAL polled output + + + +//========================================================================== + +#define SHOW_RESULT( _fn, _res ) \ +diag_printf(": " #_fn "() returned %d %s\n", _res, _res<0?strerror(errno):""); + +//========================================================================== + +#define IOSIZE 100 + +#define LONGNAME1 "long_file_name_that_should_take_up_more_than_one_directory_entry_1" +#define LONGNAME2 "long_file_name_that_should_take_up_more_than_one_directory_entry_2" + + +//========================================================================== + +#ifndef CYGPKG_LIBC_STRING + +char *strcat( char *s1, const char *s2 ) +{ + char *s = s1; + while( *s1 ) s1++; + while( (*s1++ = *s2++) != 0); + return s; +} + +#endif + +//========================================================================== + +static void listdir( char *name, int statp, int numexpected, int *numgot ) +{ + int err; + DIR *dirp; + int num=0; + + diag_printf(": reading directory %s\n",name); + + dirp = opendir( name ); + if( dirp == NULL ) SHOW_RESULT( opendir, -1 ); + + for(;;) + { + struct dirent *entry = readdir( dirp ); + + if( entry == NULL ) + break; + num++; + diag_printf(": entry %14s",entry->d_name); + if( statp ) + { + char fullname[PATH_MAX]; + struct stat sbuf; + + if( name[0] ) + { + strcpy(fullname, name ); + if( !(name[0] == '/' && name[1] == 0 ) ) + strcat(fullname, "/" ); + } + else fullname[0] = 0; + + strcat(fullname, entry->d_name ); + + err = stat( fullname, &sbuf ); + if( err < 0 ) + { + if( errno == ENOSYS ) + diag_printf(" "); + else SHOW_RESULT( stat, err ); + } + else + { + diag_printf(" [mode %08x ino %08x nlink %d size %d]", + sbuf.st_mode,sbuf.st_ino,sbuf.st_nlink,sbuf.st_size); + } + } + + diag_printf("\n"); + } + + err = closedir( dirp ); + if( err < 0 ) SHOW_RESULT( stat, err ); + if (numexpected >= 0 && num != numexpected) + CYG_TEST_FAIL("Wrong number of dir entries\n"); + if ( numgot != NULL ) + *numgot = num; +} + +//========================================================================== + +static void createfile( char *name, size_t size ) +{ + char buf[IOSIZE]; + int fd; + ssize_t wrote; + int i; + int err; + + diag_printf(": create file %s size %d\n",name,size); + + err = access( name, F_OK ); + if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err ); + + for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256; + + fd = open( name, O_WRONLY|O_CREAT ); + if( fd < 0 ) SHOW_RESULT( open, fd ); + + while( size > 0 ) + { + ssize_t len = size; + if ( len > IOSIZE ) len = IOSIZE; + + wrote = write( fd, buf, len ); + if( wrote != len ) SHOW_RESULT( write, wrote ); + + size -= wrote; + } + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +static void maxfile( char *name ) +{ + char buf[IOSIZE]; + int fd; + ssize_t wrote; + int i; + int err; + size_t size = 0; + size_t prevsize = 0; + + diag_printf(": create maximal file %s\n",name); + diag_printf(": This may take a few minutes\n"); + + err = access( name, F_OK ); + if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err ); + + for( i = 0; i < IOSIZE; i++ ) buf[i] = i%256; + + fd = open( name, O_WRONLY|O_CREAT ); + if( fd < 0 ) SHOW_RESULT( open, fd ); + + do + { + wrote = write( fd, buf, IOSIZE ); + //if( wrote < 0 ) SHOW_RESULT( write, wrote ); + + if( wrote >= 0 ) + size += wrote; + + if( (size-prevsize) > 100000 ) + { + diag_printf(": size = %d \n", size); + prevsize = size; + } + + } while( wrote == IOSIZE ); + + diag_printf(": file size == %d\n",size); + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +static void checkfile( char *name ) +{ + char buf[IOSIZE]; + int fd; + ssize_t done; + int i; + int err; + off_t pos = 0; + + diag_printf(": check file %s\n",name); + + err = access( name, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd = open( name, O_RDONLY ); + if( fd < 0 ) SHOW_RESULT( open, fd ); + + for(;;) + { + done = read( fd, buf, IOSIZE ); + if( done < 0 ) SHOW_RESULT( read, done ); + + if( done == 0 ) break; + + for( i = 0; i < done; i++ ) + if( buf[i] != i%256 ) + { + diag_printf("buf[%d+%d](%02x) != %02x\n",pos,i,buf[i],i%256); + CYG_TEST_FAIL("Data read not equal to data written\n"); + } + + pos += done; + } + + err = close( fd ); + if( err < 0 ) SHOW_RESULT( close, err ); +} + +//========================================================================== + +static void copyfile( char *name2, char *name1 ) +{ + + int err; + char buf[IOSIZE]; + int fd1, fd2; + ssize_t done, wrote; + + diag_printf(": copy file %s -> %s\n",name2,name1); + + err = access( name1, F_OK ); + if( err < 0 && errno != EACCES ) SHOW_RESULT( access, err ); + + err = access( name2, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd1 = open( name1, O_WRONLY|O_CREAT ); + if( fd1 < 0 ) SHOW_RESULT( open, fd1 ); + + fd2 = open( name2, O_RDONLY ); + if( fd2 < 0 ) SHOW_RESULT( open, fd2 ); + + for(;;) + { + done = read( fd2, buf, IOSIZE ); + if( done < 0 ) SHOW_RESULT( read, done ); + + if( done == 0 ) break; + + wrote = write( fd1, buf, done ); + if( wrote != done ) SHOW_RESULT( write, wrote ); + + if( wrote != done ) break; + } + + err = close( fd1 ); + if( err < 0 ) SHOW_RESULT( close, err ); + + err = close( fd2 ); + if( err < 0 ) SHOW_RESULT( close, err ); + +} + +//========================================================================== + +static void comparefiles( char *name2, char *name1 ) +{ + int err; + char buf1[IOSIZE]; + char buf2[IOSIZE]; + int fd1, fd2; + ssize_t done1, done2; + int i; + + diag_printf(": compare files %s == %s\n",name2,name1); + + err = access( name1, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + err = access( name1, F_OK ); + if( err != 0 ) SHOW_RESULT( access, err ); + + fd1 = open( name1, O_RDONLY ); + if( fd1 < 0 ) SHOW_RESULT( open, fd1 ); + + fd2 = open( name2, O_RDONLY ); + if( fd2 < 0 ) SHOW_RESULT( open, fd2 ); + + for(;;) + { + done1 = read( fd1, buf1, IOSIZE ); + if( done1 < 0 ) SHOW_RESULT( read, done1 ); + + done2 = read( fd2, buf2, IOSIZE ); + if( done2 < 0 ) SHOW_RESULT( read, done2 ); + + if( done1 != done2 ) + diag_printf("Files different sizes\n"); + + if( done1 == 0 ) break; + + for( i = 0; i < done1; i++ ) + if( buf1[i] != buf2[i] ) + { + diag_printf("buf1[%d](%02x) != buf1[%d](%02x)\n",i,buf1[i],i,buf2[i]); + CYG_TEST_FAIL("Data in files not equal\n"); + } + } + + err = close( fd1 ); + if( err < 0 ) SHOW_RESULT( close, err ); + + err = close( fd2 ); + if( err < 0 ) SHOW_RESULT( close, err ); + +} + +//========================================================================== + +void checkcwd( const char *cwd ) +{ + static char cwdbuf[PATH_MAX]; + char *ret; + + ret = getcwd( cwdbuf, sizeof(cwdbuf)); + if( ret == NULL ) SHOW_RESULT( getcwd, ret ); + + if( strcmp( cwdbuf, cwd ) != 0 ) + { + diag_printf( "cwdbuf %s cwd %s\n",cwdbuf, cwd ); + CYG_TEST_FAIL( "Current directory mismatch"); + } +} + +//========================================================================== +// main + +void fileio1_main( CYG_ADDRESS id ) +{ + int err; + int existingdirents=-1; + + CYG_TEST_INIT(); + + // -------------------------------------------------------------- + + err = mount( "/dev/disk0/1", "/", "fatfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); + + err = chdir( "/" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/" ); + + listdir( "/", true, -1, &existingdirents ); + + // -------------------------------------------------------------- + + createfile( "/foo", 20257 ); + checkfile( "foo" ); + copyfile( "foo", "fee"); + checkfile( "fee" ); + comparefiles( "foo", "/fee" ); + diag_printf(": mkdir bar\n"); + err = mkdir( "/bar", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + listdir( "/" , true, existingdirents+3, NULL ); + + copyfile( "fee", "/bar/fum" ); + checkfile( "bar/fum" ); + comparefiles( "/fee", "bar/fum" ); + + diag_printf(": cd bar\n"); + err = chdir( "bar" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/bar" ); + + diag_printf(": rename /foo bundy\n"); + err = rename( "/foo", "bundy" ); + if( err < 0 ) SHOW_RESULT( rename, err ); + + listdir( "/", true, existingdirents+2, NULL ); + listdir( "" , true, 4, NULL ); + + checkfile( "/bar/bundy" ); + comparefiles("/fee", "bundy" ); + + // -------------------------------------------------------------- + + diag_printf(": unlink fee\n"); + err = unlink( "/fee" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": unlink fum\n"); + err = unlink( "fum" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": unlink /bar/bundy\n"); + err = unlink( "/bar/bundy" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": cd /\n"); + err = chdir( "/" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/" ); + + diag_printf(": rmdir /bar\n"); + err = rmdir( "/bar" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + listdir( "/", false, existingdirents, NULL ); + + // -------------------------------------------------------------- + +#if 0 + diag_printf(": mkdir disk2\n"); + err = mkdir( "/disk2", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); +#else + diag_printf(": mount /disk2\n"); + err = mount( "/dev/disk0/2", "/disk2", "fatfs" ); + if( err < 0 ) SHOW_RESULT( mount, err ); +#endif + + listdir( "/disk2" , true, -1, &existingdirents); + + createfile( "/disk2/tinky", 4567 ); + copyfile( "/disk2/tinky", "/disk2/laalaa" ); + checkfile( "/disk2/tinky"); + checkfile( "/disk2/laalaa"); + comparefiles( "/disk2/tinky", "/disk2/laalaa" ); + + diag_printf(": cd /disk2\n"); + err = chdir( "/disk2" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/disk2" ); + + diag_printf(": mkdir noonoo\n"); + err = mkdir( "noonoo", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + listdir( "/disk2" , true, existingdirents+3, NULL); + + diag_printf(": cd noonoo\n"); + err = chdir( "noonoo" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + + checkcwd( "/disk2/noonoo" ); + + createfile( "tinky", 6789 ); + checkfile( "tinky" ); + + createfile( "dipsy", 34567 ); + checkfile( "dipsy" ); + copyfile( "dipsy", "po" ); + checkfile( "po" ); + comparefiles( "dipsy", "po" ); + + listdir( ".", true, 5, NULL ); + listdir( "", true, 5, NULL ); + listdir( "..", true, existingdirents+3, NULL ); + + // -------------------------------------------------------------- + + diag_printf(": unlink tinky\n"); + err = unlink( "tinky" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": unlink dipsy\n"); + err = unlink( "dipsy" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": unlink po\n"); + err = unlink( "po" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": cd ..\n"); + err = chdir( ".." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2" ); + + diag_printf(": rmdir noonoo\n"); + err = rmdir( "noonoo" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + // -------------------------------------------------------------- + + err = mkdir( "x", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + err = mkdir( "x/y", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + err = mkdir( "x/y/z", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + err = mkdir( "x/y/z/w", 0 ); + if( err < 0 ) SHOW_RESULT( mkdir, err ); + + diag_printf(": cd /disk2/x/y/z/w\n"); + err = chdir( "/disk2/x/y/z/w" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y/z/w" ); + + diag_printf(": cd ..\n"); + err = chdir( ".." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y/z" ); + + diag_printf(": cd .\n"); + err = chdir( "." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y/z" ); + + diag_printf(": cd ../../y\n"); + err = chdir( "../../y" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2/x/y" ); + + diag_printf(": cd ../..\n"); + err = chdir( "../.." ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/disk2" ); + + diag_printf(": rmdir x/y/z/w\n"); + err = rmdir( "x/y/z/w" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + diag_printf(": rmdir x/y/z\n"); + err = rmdir( "x/y/z" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + diag_printf(": rmdir x/y\n"); + err = rmdir( "x/y" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + diag_printf(": rmdir x\n"); + err = rmdir( "x" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); + + // -------------------------------------------------------------- + + checkcwd( "/disk2" ); + + diag_printf(": unlink tinky\n"); + err = unlink( "tinky" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": unlink laalaa\n"); + err = unlink( "laalaa" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": cd /\n"); + err = chdir( "/" ); + if( err < 0 ) SHOW_RESULT( chdir, err ); + checkcwd( "/" ); + + listdir( "/disk2", true, -1, NULL ); + +#if 0 + diag_printf(": rmdir dir\n"); + err = rmdir( "disk2" ); + if( err < 0 ) SHOW_RESULT( rmdir, err ); +#else + diag_printf(": umount /disk2\n"); + err = umount( "/disk2" ); + if( err < 0 ) SHOW_RESULT( umount, err ); +#endif + + maxfile("file.max"); + + listdir( "/", true, -1, NULL ); + + diag_printf(": unlink file.max\n"); + err = unlink( "file.max" ); + if( err < 0 ) SHOW_RESULT( unlink, err ); + + diag_printf(": umount /\n"); + err = umount( "/" ); + if( err < 0 ) SHOW_RESULT( umount, err ); + + CYG_TEST_PASS_FINISH("fileio1"); +} + +// ------------------------------------------------------------------------- + +#include + +static char stack[CYGNUM_HAL_STACK_SIZE_TYPICAL]; +static cyg_handle_t thread_handle; +static cyg_thread thread; + +externC void +cyg_start( void ) +{ + cyg_thread_create(3, // Priority - just a number + fileio1_main, // entry + 0, // index + 0, // no name + &stack[0], // Stack + sizeof(stack), // Size + &thread_handle, // Handle + &thread // Thread data structure + ); + cyg_thread_resume(thread_handle); + + cyg_scheduler_start(); +} + +// ------------------------------------------------------------------------- +// EOF fileio1.c diff --git a/packages/io/common/current/ChangeLog b/packages/io/common/current/ChangeLog --- a/packages/io/common/current/ChangeLog +++ b/packages/io/common/current/ChangeLog @@ -1,3 +1,8 @@ +2004-01-19 Nick Garnett + + * include/config_keys.h (CYG_IO_GET_CONFIG_DISK_INFO): Added DISK + IO config key base definition. + 2003-11-27 David Woodhouse * include/config_keys.h: Add CYG_IO_SET_CONFIG_FLASH_FIS_NAME diff --git a/packages/io/common/current/include/config_keys.h b/packages/io/common/current/include/config_keys.h --- a/packages/io/common/current/include/config_keys.h +++ b/packages/io/common/current/include/config_keys.h @@ -123,6 +123,11 @@ #define CYG_IO_SET_CONFIG_FLASH_FIS_NAME 0x680 +// ======== 0x700 DISK ======================================================= +// Get/Set configuration 'key' values for DISK I/O + +#define CYG_IO_GET_CONFIG_DISK_INFO 0x700 + // ======== 0x1000 Generic =================================================== // Get/Set configuration 'key' values that can apply to more than one // class of device. diff --git a/packages/io/disk/current/ChangeLog b/packages/io/disk/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/io/disk/current/ChangeLog @@ -0,0 +1,58 @@ +2004-01-15 Nick Garnett + + * src/disk.c: + * include/disk.h: Removed block_pos arguments from + hardware driver read and write calls: it is not necessary. + + * src/disk.c (read_mbr): Modified to read a complete sector from + the disk rather than pieces. Drivers should only ever see + reads/writes in multiples of 512 bytes. + +2003-10-16 Savin Zlobec + + * src/disk.c: + Check return code from MBR scan and set + connected flag only if there was no error. + +2003-09-01 Savin Zlobec + + * cdl/io_disk.cdl: + * inc/disk.h: + * inc/diskio.h: + * src/disk.c: + A block device IO implementation + + + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/packages/io/disk/current/cdl/io_disk.cdl b/packages/io/disk/current/cdl/io_disk.cdl new file mode 100644 --- /dev/null +++ b/packages/io/disk/current/cdl/io_disk.cdl @@ -0,0 +1,116 @@ +# ==================================================================== +# +# io_disk.cdl +# +# eCos IO configuration data +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +## Copyright (C) 2003 Savin Zlobec +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## +## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +## at http://sources.redhat.com/ecos/ecos-license/ +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): savin +# Original data: jskov, gthomas +# Contributors: +# Date: 2003-06-09 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_IO_DISK { + display "Disk device drivers" + active_if CYGPKG_IO + requires CYGPKG_ERROR + include_dir cyg/io + description " + This option enables drivers for basic I/O services on + disk devices." + + compile -library=libextras.a disk.c + + define_proc { + puts $::cdl_header "/***** proc output start *****/" + puts $::cdl_header "#include " + puts $::cdl_header "#ifdef CYGDAT_IO_DISK_DEVICE_HEADER" + puts $::cdl_header "# include CYGDAT_IO_DISK_DEVICE_HEADER" + puts $::cdl_header "#endif " + puts $::cdl_header "/****** proc output end ******/" + } + + cdl_component CYGPKG_IO_DISK_DEVICES { + display "Hardware disk device drivers" + flavor bool + default_value 1 + description " + This option enables the hardware disk drivers + for the current platform." + } + + cdl_component CYGPKG_IO_DISK_OPTIONS { + display "Disk device driver build options" + flavor none + description " + Package specific build options including control over + compiler flags used only in building this package, + and details of which tests are built." + + + cdl_option CYGPKG_IO_DISK_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the disk device drivers. These flags are used + in addition to the set of global flags." + } + + cdl_option CYGPKG_IO_DISK_CFLAGS_REMOVE { + display "Suppressed compiler flags" + flavor data + no_define + default_value { "" } + description " + This option modifies the set of compiler flags for + building the disk device drivers. These flags are removed from + the set of global flags if present." + } + } +} + +# ==================================================================== +# EOF io_disk.cdl diff --git a/packages/io/disk/current/include/disk.h b/packages/io/disk/current/include/disk.h new file mode 100644 --- /dev/null +++ b/packages/io/disk/current/include/disk.h @@ -0,0 +1,169 @@ +#ifndef CYGONCE_DISK_H +#define CYGONCE_DISK_H +// ==================================================================== +// +// disk.h +// +// Device I/O +// +// ==================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +// ==================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Original data: gthomas +// Date: 2003-06-09 +// Purpose: Internal interfaces for disk I/O drivers +// Description: +// +//####DESCRIPTIONEND#### +// +// ==================================================================== + +// Disk I/O interfaces + +#include +#include + +#include +#include +#include +#include + +typedef struct disk_channel disk_channel; +typedef struct disk_funs disk_funs; + +// Pointers into upper-level driver +typedef struct { + + // Initialize the disk + cyg_bool (*disk_init)(struct cyg_devtab_entry *tab); + + // Disk device has been connected + Cyg_ErrNo (*disk_connected)(struct cyg_devtab_entry *tab, + cyg_disk_identify_t *ident); + + // Disk device has been disconnected + Cyg_ErrNo (*disk_disconnected)(struct cyg_devtab_entry *tab); + + // Lookup disk device + Cyg_ErrNo (*disk_lookup)(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); +} disk_callbacks_t; + +#define DISK_CALLBACKS(_l, \ + _init, \ + _connected, \ + _disconnected, \ + _lookup) \ +disk_callbacks_t _l = { \ + _init, \ + _connected, \ + _disconnected, \ + _lookup \ +}; + +extern disk_callbacks_t cyg_io_disk_callbacks; + +// Private data which describes this channel +struct disk_channel { + disk_funs *funs; + disk_callbacks_t *callbacks; + void *dev_priv; // device private data + cyg_disk_info_t *info; // disk info + cyg_disk_partition_t *partition; // partition data + cyg_bool mbr_support; // true if disk has MBR + cyg_bool valid; // true if device valid + cyg_bool init; // true if initialized +}; + +// Initialization macro for disk channel +#define DISK_CHANNEL(_l, \ + _funs, \ + _dev_priv, \ + _mbr_supp) \ +cyg_disk_info_t _l##_disk_info; \ +disk_channel _l = { \ + &(_funs), \ + &cyg_io_disk_callbacks, \ + &(_dev_priv), \ + &(_l##_disk_info), \ + NULL, \ + _mbr_supp, \ + false, \ + false \ +}; + +// Low level interface functions +struct disk_funs { + + // Read block data into buf + Cyg_ErrNo (*read)(disk_channel *priv, + void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + + // Write block data from buf + Cyg_ErrNo (*write)(disk_channel *priv, + const void *buf, + cyg_uint32 len, + cyg_uint32 block_num); + + // Get hardware configuration + Cyg_ErrNo (*get_config)(disk_channel *priv, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); + + // Set hardware configuration + Cyg_ErrNo (*set_config)(disk_channel *priv, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len); +}; + +#define DISK_FUNS(_l,_read,_write,_get_config,_set_config) \ +disk_funs _l = { \ + _read, \ + _write, \ + _get_config, \ + _set_config \ +}; + +extern cyg_devio_table_t cyg_io_disk_devio; + +#endif // CYGONCE_DISK_H diff --git a/packages/io/disk/current/include/diskio.h b/packages/io/disk/current/include/diskio.h new file mode 100644 --- /dev/null +++ b/packages/io/disk/current/include/diskio.h @@ -0,0 +1,94 @@ +#ifndef CYGONCE_DISKIO_H +#define CYGONCE_DISKIO_H +// ==================================================================== +// +// diskio.h +// +// Device I/O +// +// ==================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +// ==================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-06-09 +// Purpose: Special support for disk I/O devices +// Description: +// +//####DESCRIPTIONEND#### +// +// ==================================================================== + +// This file contains the user-level visible I/O interfaces + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char serial[20+1]; // serial number + char firmware_rev[8+1]; // firmware revision + char model_num[40+1]; // model number + cyg_uint32 cylinders_num; // number of cylinders (CHS) + cyg_uint32 heads_num; // number of heads (CHS) + cyg_uint32 sectors_num; // number of sectors per track (CHS) + cyg_uint32 lba_sectors_num; // total number of sectors in LBA mode +} cyg_disk_identify_t; + +typedef struct { + cyg_uint8 type; // partition type + cyg_uint8 state; // state 0x00 - inactive, 0x80 - active + cyg_uint32 start; // first sector number + cyg_uint32 end; // last sector number + cyg_uint32 size; // size in sectors +} cyg_disk_partition_t; + +typedef struct { + cyg_disk_identify_t ident; // identify data + cyg_disk_partition_t partitions[4]; // partitions + cyg_addrword_t devs[4]; // device instances + cyg_uint32 block_size; // block size + cyg_uint32 blocks_num; // number of blocks on disk + cyg_bool connected; // true if device connected +} cyg_disk_info_t; + +#ifdef __cplusplus +} +#endif + +#endif /* CYGONCE_DISKIO_H */ +/* EOF diskio.h */ diff --git a/packages/io/disk/current/src/disk.c b/packages/io/disk/current/src/disk.c new file mode 100644 --- /dev/null +++ b/packages/io/disk/current/src/disk.c @@ -0,0 +1,581 @@ +//========================================================================== +// +// io/disk/disk.c +// +// High level disk driver +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-06-10 +// Purpose: Top level disk driver +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include + +#include +#include +#include +#include // assertion support +#include // diagnostic output + +#include // malloc + +// --------------------------------------------------------------------------- + +//#define DEBUG 1 + +#ifdef DEBUG +# define D(_args_) diag_printf _args_ +#else +# define D(_args_) +#endif + +// --------------------------------------------------------------------------- + +// Master Boot Record defines +#define MBR_SIG_ADDR 0x1FE // signature address +#define MBR_SIG_BYTE0 0x55 // signature first byte value +#define MBR_SIG_BYTE1 0xAA // signature second byte value +#define MBR_PART_ADDR 0x1BE // first partition address +#define MBR_PART_SIZE 0x10 // partition size +#define MBR_PART_NUM 4 // number of partitions + +// Get cylinders, heads and sectors from data (MBR partition format) +#define READ_CHS(_data_, _c_, _h_, _s_) \ + do { \ + _h_ = (*((cyg_uint8 *)_data_)); \ + _s_ = (*(((cyg_uint8 *)_data_)+1) & 0x3F); \ + _c_ = (*(((cyg_uint8 *)_data_)+1) & ~0x3F) << 2 | \ + (*(((cyg_uint8 *)_data_)+2)); \ + } while (0) + +// Get double word from data (MBR partition format) +#define READ_DWORD(_data_, _val_) \ + do { \ + _val_ = *((cyg_uint8 *)_data_) | \ + *(((cyg_uint8 *)_data_)+1) << 8 | \ + *(((cyg_uint8 *)_data_)+2) << 16 | \ + *(((cyg_uint8 *)_data_)+3) << 24; \ + } while (0) + +// Convert cylinders, heads and sectors to LBA sectors +#define CHS_TO_LBA(_info_, _c_, _h_, _s_, _lba_) \ + (_lba_=(((_c_)*(_info_)->heads_num+(_h_))*(_info_)->sectors_num)+(_s_)-1) + +// --------------------------------------------------------------------------- + +static Cyg_ErrNo disk_bread(cyg_io_handle_t handle, + void *buf, + cyg_uint32 *len, + cyg_uint32 pos); + +static Cyg_ErrNo disk_bwrite(cyg_io_handle_t handle, + const void *buf, + cyg_uint32 *len, + cyg_uint32 pos); + +static Cyg_ErrNo disk_select(cyg_io_handle_t handle, + cyg_uint32 which, + CYG_ADDRWORD info); + +static Cyg_ErrNo disk_get_config(cyg_io_handle_t handle, + cyg_uint32 key, + void *buf, + cyg_uint32 *len); + +static Cyg_ErrNo disk_set_config(cyg_io_handle_t handle, + cyg_uint32 key, + const void *buf, + cyg_uint32 *len); + +BLOCK_DEVIO_TABLE(cyg_io_disk_devio, + disk_bwrite, + disk_bread, + disk_select, + disk_get_config, + disk_set_config +); + +static cyg_bool disk_init(struct cyg_devtab_entry *tab); + +static Cyg_ErrNo disk_connected(struct cyg_devtab_entry *tab, + cyg_disk_identify_t *ident); + +static Cyg_ErrNo disk_disconnected(struct cyg_devtab_entry *tab); + +static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name); + +DISK_CALLBACKS(cyg_io_disk_callbacks, + disk_init, + disk_connected, + disk_disconnected, + disk_lookup +); + +// --------------------------------------------------------------------------- + +// +// Read partition from data +// +static void +read_partition(cyg_uint8 *data, + cyg_disk_info_t *info, + cyg_disk_partition_t *part) +{ + cyg_uint16 c, h, s; + + part->type = data[4]; + part->state = data[0]; + + READ_CHS(&data[1], c, h, s); + CHS_TO_LBA(&info->ident, c, h, s, part->start); + + READ_CHS(&data[5], c, h, s); + CHS_TO_LBA(&info->ident, c, h, s, part->end); + + READ_DWORD(&data[12], part->size); +} + +// +// Read Master Boot Record (partitions) +// +static Cyg_ErrNo +read_mbr(disk_channel *chan) +{ + cyg_disk_info_t *info = chan->info; + disk_funs *funs = chan->funs; + cyg_uint8 buf[512]; + Cyg_ErrNo res = ENOERR; + int i; + + for (i = 0; i < MBR_PART_NUM; i++) + info->partitions[i].type = 0x00; + + res = (funs->read)(chan, (void *)buf, 512, 0); + if (ENOERR != res) + return res; + + if (MBR_SIG_BYTE0 == buf[MBR_SIG_ADDR+0] && MBR_SIG_BYTE1 == buf[MBR_SIG_ADDR+1]) + { + D(("disk MBR found\n")); + + for (i = 0; i < MBR_PART_NUM; i++) + { + cyg_disk_partition_t *part = &info->partitions[i]; + + read_partition(&buf[MBR_PART_ADDR+MBR_PART_SIZE*i], info, part); +#ifdef DEBUG + if (0x00 != part->type) + { + D(("\ndisk MBR partition %d:\n", i)); + D((" type = %02X\n", part->type)); + D((" state = %02X\n", part->state)); + D((" start = %d\n", part->start)); + D((" end = %d\n", part->end)); + D((" size = %d\n\n", part->size)); + } +#endif + } + } + return ENOERR; +} + +static cyg_bool +disk_init(struct cyg_devtab_entry *tab) +{ + disk_channel *chan = (disk_channel *) tab->priv; + cyg_disk_info_t *info = chan->info; + int i; + + if (!chan->init) + { + info->connected = false; + + // clear devices array (one per partition) + // and partition data + for (i = 0; i < MBR_PART_NUM; i++) + { + info->devs[i] = (cyg_addrword_t) 0; + info->partitions[i].type = 0x00; + } + + chan->init = true; + } + return true; +} + +static Cyg_ErrNo +disk_connected(struct cyg_devtab_entry *tab, + cyg_disk_identify_t *ident) +{ + disk_channel *chan = (disk_channel *) tab->priv; + cyg_disk_info_t *info = chan->info; + Cyg_ErrNo res = ENOERR; + + if (!chan->init) + return -EINVAL; + + info->ident = *ident; + info->block_size = 512; + info->blocks_num = ident->lba_sectors_num; + + D(("disk connected\n")); + D((" serial = '%s'\n", ident->serial)); + D((" firmware rev = '%s'\n", ident->firmware_rev)); + D((" model num = '%s'\n", ident->model_num)); + D((" block_size = %d\n", info->block_size)); + D((" blocks_num = %d\n", info->blocks_num)); + + if (chan->mbr_support) + { + // read disk master boot record + res = read_mbr(chan); + } + + if (ENOERR == res) + { + // now declare that we are connected + info->connected = true; + chan->valid = true; + } + return res; +} + +static Cyg_ErrNo +disk_disconnected(struct cyg_devtab_entry *tab) +{ + disk_channel *chan = (disk_channel *) tab->priv; + cyg_disk_info_t *info = chan->info; + int i; + + if (!chan->init) + return -EINVAL; + + info->connected = false; + chan->valid = false; + + // clear partition data and invalidate + // any allocated devices + for (i = 0; i < MBR_PART_NUM; i++) + { + info->partitions[i].type = 0x00; + + if (0 != info->devs[i]) + { + struct cyg_devtab_entry *dtab; + disk_channel *dchan; + + dtab = (struct cyg_devtab_entry *)info->devs[i]; + dchan = (disk_channel *) dtab->priv; + + dchan->valid = false; + } + } + + + D(("disk disconnected\n")); + + return ENOERR; +} + +static Cyg_ErrNo +disk_lookup(struct cyg_devtab_entry **tab, + struct cyg_devtab_entry *sub_tab, + const char *name) +{ + disk_channel *chan = (disk_channel *) (*tab)->priv; + cyg_disk_info_t *info = chan->info; + struct cyg_devtab_entry *new_tab; + disk_channel *new_chan; + int dev_num; + + if (!info->connected || name[0] < '0' || name[0] > '4' || '\0' != name[1]) + return -EINVAL; + + dev_num = name[0] - '0'; + + D(("disk lookup dev number = %d\n", dev_num)); + + if (0 == dev_num) + { + // 0 is the root device number + return ENOERR; + } + if (0x00 == info->partitions[dev_num-1].type) + { + D(("disk NO partition for dev\n")); + return -EINVAL; + } + + if (0 == info->devs[dev_num-1]) + { + D(("disk creating new devtab entry\n")); + + // alloc mem for new device + new_tab = (struct cyg_devtab_entry *) + malloc(sizeof(struct cyg_devtab_entry)); + if (NULL == new_tab) + return -ENOMEM; + + // alloc mem for new device private data + new_chan = (disk_channel *)malloc(sizeof(disk_channel)); + if (NULL == new_chan) + { + free(new_tab); + return -ENOMEM; + } + } + else + { + new_tab = (struct cyg_devtab_entry *) info->devs[dev_num-1]; + new_chan = (disk_channel *) new_tab->priv; + } + + // copy device data from parent + *new_tab = **tab; + *new_chan = *chan; + + new_tab->priv = (void *)new_chan; + + // set partition ptr and put this device into devices array + new_chan->partition = &info->partitions[dev_num-1]; + chan->info->devs[dev_num-1] = (cyg_addrword_t) new_tab; + + // return device tab + *tab = new_tab; + + return ENOERR; +} + +// --------------------------------------------------------------------------- + +static Cyg_ErrNo +disk_bread(cyg_io_handle_t handle, + void *buf, + cyg_uint32 *len, + cyg_uint32 pos) +{ + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; + disk_channel *chan = (disk_channel *) t->priv; + disk_funs *funs = chan->funs; + cyg_disk_info_t *info = chan->info; + cyg_uint32 size = *len; + cyg_uint8 *bbuf = (cyg_uint8 *)buf; + Cyg_ErrNo res = ENOERR; + cyg_uint32 last; + + if (!info->connected || !chan->valid) + return -EINVAL; + + if (NULL != chan->partition) + { + pos += chan->partition->start; + last = chan->partition->end; + } + else + { + last = info->blocks_num-1; + } + + D(("disk read block=%d len=%d buf=%p\n", pos, *len, buf)); + + while (size > 0) + { + if (pos > last) + { + res = -EIO; + break; + } + + res = (funs->read)(chan, (void*)bbuf, info->block_size, pos); + if (ENOERR != res) + break; + + if (!info->connected) + { + res = -EINVAL; + break; + } + + bbuf += info->block_size; + pos++; + size--; + } + *len -= size; + return res; +} + +// --------------------------------------------------------------------------- + +static Cyg_ErrNo +disk_bwrite(cyg_io_handle_t handle, + const void *buf, + cyg_uint32 *len, + cyg_uint32 pos) +{ + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; + disk_channel *chan = (disk_channel *) t->priv; + disk_funs *funs = chan->funs; + cyg_disk_info_t *info = chan->info; + cyg_uint32 size = *len; + cyg_uint8 *bbuf = (cyg_uint8 * const) buf; + Cyg_ErrNo res = ENOERR; + cyg_uint32 last; + + if (!info->connected || !chan->valid) + return -EINVAL; + + if (NULL != chan->partition) + { + pos += chan->partition->start; + last = chan->partition->end; + } + else + { + last = info->blocks_num-1; + } + + D(("disk write block=%d len=%d buf=%p\n", pos, *len, buf)); + + while (size > 0) + { + if (pos > last) + { + res = -EIO; + break; + } + + res = (funs->write)(chan, (void*)bbuf, info->block_size, pos); + if (ENOERR != res) + break; + + if (!info->connected) + { + res = -EINVAL; + break; + } + + bbuf += info->block_size; + pos++; + size--; + } + *len -= size; + return res; +} + +// --------------------------------------------------------------------------- + +static cyg_bool +disk_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info) +{ + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; + disk_channel *chan = (disk_channel *) t->priv; + cyg_disk_info_t *cinfo = chan->info; + + if (!cinfo->connected || !chan->valid) + return false; + else + return true; +} + +// --------------------------------------------------------------------------- + +static Cyg_ErrNo +disk_get_config(cyg_io_handle_t handle, + cyg_uint32 key, + void *xbuf, + cyg_uint32 *len) +{ + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; + disk_channel *chan = (disk_channel *) t->priv; + cyg_disk_info_t *info = chan->info; + cyg_disk_info_t *buf = (cyg_disk_info_t *) xbuf; + disk_funs *funs = chan->funs; + Cyg_ErrNo res = ENOERR; + + if (!info->connected || !chan->valid) + return -EINVAL; + + D(("disk get config key=%d\n", key)); + + switch (key) { + case CYG_IO_GET_CONFIG_DISK_INFO: + if (*len < sizeof(cyg_disk_info_t)) { + return -EINVAL; + } + *buf = *chan->info; + *len = sizeof(cyg_disk_info_t); + break; + + default: + // pass down to lower layers + res = (funs->get_config)(chan, key, xbuf, len); + } + + return res; +} + +// --------------------------------------------------------------------------- + +static Cyg_ErrNo +disk_set_config(cyg_io_handle_t handle, + cyg_uint32 key, + const void *xbuf, + cyg_uint32 *len) +{ + cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; + disk_channel *chan = (disk_channel *) t->priv; + cyg_disk_info_t *info = chan->info; + disk_funs *funs = chan->funs; + + if (!info->connected || !chan->valid) + return -EINVAL; + + D(("disk set config key=%d\n", key)); + + // pass down to lower layers + return (funs->set_config)(chan, key, xbuf, len); +} + +// --------------------------------------------------------------------------- +// EOF disk.c diff --git a/packages/services/blib/current/ChangeLog b/packages/services/blib/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/services/blib/current/ChangeLog @@ -0,0 +1,41 @@ +2003-09-01 Savin Zlobec + + * cdl/blib.cdl: + * inc/blib.h: + * src/disk.c: + A block access and cache library. + + + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/packages/services/blib/current/cdl/blib.cdl b/packages/services/blib/current/cdl/blib.cdl new file mode 100644 --- /dev/null +++ b/packages/services/blib/current/cdl/blib.cdl @@ -0,0 +1,70 @@ +# ==================================================================== +# +# blib.cdl +# +# Block cache and access library configuration data +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2003 Savin Zlobec +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos is distributed in the hope that it will be useful, but WITHOUT ANY +## WARRANTY; without even the implied warranty of MERCHANTABILITY or +## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): savin +# Contributors: +# Date: 2003-08-29 +# +#####DESCRIPTIONEND#### +# ==================================================================== + +cdl_package CYGPKG_BLOCK_LIB { + display "Block cache and access library" + define_header blib.h + include_dir blib + + requires CYGPKG_ISOINFRA + requires CYGPKG_MEMALLOC + requires CYGPKG_LINUX_COMPAT + requires CYGPKG_ERROR + + compile -library=libextras.a blib.c + + cdl_option CYGIMP_BLOCK_LIB_STATISTICS { + display "Block access statistics support" + flavor bool + default_value 1 + description "This option enables statistics for + block access operations." + } +} + +# ==================================================================== +# End of blib.cdl diff --git a/packages/services/blib/current/include/blib.h b/packages/services/blib/current/include/blib.h new file mode 100644 --- /dev/null +++ b/packages/services/blib/current/include/blib.h @@ -0,0 +1,285 @@ +#ifndef CYGONCE_BLIB_H +#define CYGONCE_BLIB_H +//========================================================================== +// +// blib.h +// +// Block cache and access library +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-08-29 +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include + +#include +#include + +// -------------------------------------------------------------------- + +typedef int (*cyg_blib_bread_fn) ( + void*, // private data + void*, // block buffer + cyg_uint32*, // number of blocks to read + cyg_uint32 // starting block number +); + +typedef int (*cyg_blib_bwrite_fn) ( + void*, // private data + const void*, // block buffer + cyg_uint32*, // number of blocks to write + cyg_uint32 // starting block number +); + +typedef struct { + cyg_uint32 n_gets; // number of block gets + cyg_uint32 n_reads; // number of block reads + cyg_uint32 n_writes; // number of block writes +} cyg_blib_stat_t; + +typedef struct { + void *priv; // private data + struct list_head list_head; // head of block list + struct rb_root rb_root; // block red-black tree root + cyg_uint32 block_size; // block size + cyg_uint32 block_size_log2; // block size log2 + cyg_uint8 *mem_base; // memory base + cyg_uint32 mem_size; // memory size + cyg_mempool_fix mem_pool; // memory pool struct + cyg_handle_t mem_pool_h; // memory pool handle + cyg_blib_bread_fn bread_fn; // block read function + cyg_blib_bwrite_fn bwrite_fn; // block write function +#ifdef CYGIMP_BLOCK_LIB_STATISTICS + cyg_blib_stat_t stat; // statistics +#endif +} cyg_blib_t; + +// -------------------------------------------------------------------- + +// -------------------------------------------------------------------- +// Creates a block lib instance +// +// priv_data - private data to pass to bread_fn and bwrite_fn +// mem_base - block cache memory base +// mem_size - block cache memory size +// block_size - block size +// bread_fn - function which reads blocks +// bwrite_fn - function which writes blocks +// bl - block lib instance space holder +// +// returns ENOERR if create succeded +// + +int cyg_blib_create(void *priv_data, + void *mem_base, + cyg_uint32 mem_size, + cyg_uint32 block_size, + cyg_blib_bread_fn bread_fn, + cyg_blib_bwrite_fn bwrite_fn, + cyg_blib_t *bl); + +// -------------------------------------------------------------------- +// Creates a block lib instance on top of IO system +// (cyg_io_bread and cyg_io_bwrite) +// +// handle - cyg_io_handle_t +// mem_base - block cache memory base +// mem_size - block cache memory size +// block_size - block size +// bl - block lib instance space holder +// +// returns ENOERR if create succeded +// + +int cyg_blib_io_create(cyg_io_handle_t handle, + void *mem_base, + cyg_uint32 mem_size, + cyg_uint32 block_size, + cyg_blib_t *bl); + +// -------------------------------------------------------------------- +// Deletes a block lib instance +// +// bl - block lib instance +// +// The block cache is synced before +// +// returns ENOERR if delete succeded +// + +int cyg_blib_delete(cyg_blib_t *bl); + +// -------------------------------------------------------------------- +// Reads a number of blocks +// +// bl - block lib instance +// buf - block buffer ptr +// len - number of blocks to read +// pos - starting block number +// +// returns ENOERR if read succeded +// + +int cyg_blib_bread(cyg_blib_t *bl, + void *buf, + cyg_uint32 *len, + cyg_uint32 pos); + +// -------------------------------------------------------------------- +// Writes a number of blocks +// +// bl - block lib instance +// buf - block buffer ptr +// len - number of blocks to write +// pos - starting block number +// +// returns ENOERR if write succeded +// + +int cyg_blib_bwrite(cyg_blib_t *bl, + const void *buf, + cyg_uint32 *len, + cyg_uint32 pos); + +// -------------------------------------------------------------------- +// Reads data +// +// bl - block lib instance +// buf - data buffer ptr +// len - number of bytes to read +// bnum - starting block number +// pos - starting position inside starting block +// +// returns ENOERR if read succeded +// +// The block number is automatically adjusted if +// position is greater than block size +// + +int cyg_blib_read(cyg_blib_t *bl, + void *buf, + cyg_uint32 *len, + cyg_uint32 bnum, + cyg_uint32 pos); + +// -------------------------------------------------------------------- +// Writes data +// +// bl - block lib instance +// buf - data buffer ptr +// len - number of bytes to write +// bnum - starting block number +// pos - starting position inside starting block +// +// returns ENOERR if write succeded +// +// The block number is automatically adjusted if +// position is greater than block size +// + +int cyg_blib_write(cyg_blib_t *bl, + const void *buf, + cyg_uint32 *len, + cyg_uint32 bnum, + cyg_uint32 pos); + +// -------------------------------------------------------------------- +// Syncs block cache - ie write modified blocks +// +// bl - block lib instance +// +// returns ENOERR if sync succeded +// + +int cyg_blib_sync(cyg_blib_t *bl); + +// -------------------------------------------------------------------- +// Syncs block - ie write if modified +// +// bl - block lib instance +// num - block number to sync +// +// returns ENOERR if sync succeded +// + +int cyg_blib_sync_block(cyg_blib_t *bl, cyg_uint32 num); + +// -------------------------------------------------------------------- +// Flushes block cache +// +// bl - block lib instance +// +// returns ENOERR if flush succeded +// +// The block cache is synced before +// + +int cyg_blib_flush(cyg_blib_t *bl); + +// -------------------------------------------------------------------- +// Sets block size +// +// bl - block lib instance +// block_size - new block size +// +// returns ENOERR if set succeded +// +// The block cache is synced before +// + +int cyg_blib_set_block_size(cyg_blib_t *bl, cyg_uint32 block_size); + +// -------------------------------------------------------------------- +// Gets block cache statistics +// +// bl - block lib instance +// +// returns ENOERR if get succeded +// + +int cyg_blib_get_stat(cyg_blib_t *bl, cyg_blib_stat_t *stat); + +#endif // CYGONCE_BLIB_H + +// -------------------------------------------------------------------- +// EOF blib.h diff --git a/packages/services/blib/current/src/blib.c b/packages/services/blib/current/src/blib.c new file mode 100644 --- /dev/null +++ b/packages/services/blib/current/src/blib.c @@ -0,0 +1,629 @@ +//========================================================================== +// +// blib.c +// +// Block cache and access library +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2003 Savin Zlobec +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): savin +// Date: 2003-08-29 +// Description: +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include // assertion support +#include // diagnostic output +#include +#include + +#include + +#include +#include + +// -------------------------------------------------------------------- + +//#define DEBUG 1 + +#ifdef DEBUG +# define D(_args_) diag_printf _args_ +#else +# define D(_args_) CYG_EMPTY_STATEMENT +#endif + +#ifdef CYGIMP_BLOCK_LIB_STATISTICS + +#define STAT_INIT(_bl_) do { \ + bl->stat.n_gets = 0; \ + bl->stat.n_reads = 0; \ + bl->stat.n_writes = 0; \ + } while (0) + +#define STAT(_bl_, _group_) \ + ((_bl_)->stat._group_++) + +#else // CYGIMP_BLOCK_LIB_STATISTICS + +#define STAT_INIT(_bl_) CYG_EMPTY_STATEMENT +#define STAT(_bl_, _group_) CYG_EMPTY_STATEMENT + +#endif // not CYGIMP_BLOCK_LIB_STATISTICS + +// -------------------------------------------------------------------- + +typedef struct { + struct list_head list_node; // list node + struct rb_node rb_node; // red-black tree node + cyg_uint32 num; // block number + cyg_bool modified; // is this block data modified (needs write) + cyg_uint8 data[0]; // block data +} blib_block_t; + +// -------------------------------------------------------------------- + +static blib_block_t * +rb_find_block(cyg_blib_t *bl, cyg_uint32 num) +{ + struct rb_node *node = bl->rb_root.rb_node; + blib_block_t *block = NULL; + + while (NULL != node) + { + block = rb_entry(node, blib_block_t, rb_node); + + if (block->num == num) + return block; + + node = (block->num > num) ? node->rb_left : node->rb_right; + } + return NULL; +} + +static void +rb_add_block(cyg_blib_t *bl, blib_block_t *block) +{ + struct rb_node *node = bl->rb_root.rb_node; + + if (NULL == node) + { + rb_link_node(&block->rb_node, NULL, &bl->rb_root.rb_node); + } + else + { + struct rb_node **link; + blib_block_t *b = NULL; + + while (NULL != node) + { + b = rb_entry(node, blib_block_t, rb_node); + + CYG_ASSERTC(b->num != block->num); + + link = (b->num > block->num) ? &node->rb_left : &node->rb_right; + node = *link; + } + rb_link_node(&block->rb_node, &b->rb_node, link); + } + rb_insert_color(&block->rb_node, &bl->rb_root); +} + +static __inline__ void +rb_del_block(cyg_blib_t *bl, blib_block_t *block) +{ + rb_erase(&block->rb_node, &bl->rb_root); +} + +// -------------------------------------------------------------------- + +static __inline__ void +list_add_block(cyg_blib_t *bl, blib_block_t *block) +{ + list_add(&block->list_node, &bl->list_head); +} + +static __inline__ void +list_del_block(cyg_blib_t *bl, blib_block_t *block) +{ + list_del(&block->list_node); +} + +static __inline__ blib_block_t * +list_get_first_block(cyg_blib_t *bl) +{ + return(list_entry(bl->list_head.next, blib_block_t, list_node)); +} + +static __inline__ blib_block_t * +list_get_last_block(cyg_blib_t *bl) +{ + return(list_entry(bl->list_head.prev, blib_block_t, list_node)); +} + +static void +list_move_block_to_head(cyg_blib_t *bl, blib_block_t *block) +{ + list_del(&block->list_node); + list_add(&block->list_node, &bl->list_head); +} + +// -------------------------------------------------------------------- + +static cyg_uint32 +get_val_log2(cyg_uint32 val) +{ + cyg_uint32 i, log2; + + i = val; + log2 = 0; + while (0 == (i & 1)) + { + i >>= 1; + log2++; + } + if (i != 1) + return 0; + else + return log2; +} + +static int +blib_sync_block(cyg_blib_t *bl, blib_block_t *block) +{ + int ret = ENOERR; + + if (block->modified) + { + int len = 1; + + D(("blib writting block=%d\n", block->num)); + + STAT(bl, n_writes); + + ret = bl->bwrite_fn(bl->priv, (void *)block->data, &len, block->num); + + if (ENOERR == ret) + block->modified = false; + } + return ret; +} + +static int +blib_sync(cyg_blib_t *bl) +{ + struct list_head *node = bl->list_head.next; + blib_block_t *block; + int ret = ENOERR; + + D(("blib cache sync\n")); + + while (node != &bl->list_head) + { + block = list_entry(node, blib_block_t, list_node); + + ret = blib_sync_block(bl, block); + if (ENOERR != ret) + break; + + node = node->next; + } + return ret; +} + +static int +blib_get_block(cyg_blib_t *bl, + cyg_uint32 num, + cyg_bool read_data, + blib_block_t **dblock) +{ + blib_block_t *block = NULL; + int ret = ENOERR; + int len; + + D(("blib get block=%d\n", num)); + + STAT(bl, n_gets); + + // first check if the most recently used block is the requested block, + // this can improve performance when using byte access functions + if (!list_empty(&bl->list_head)) + { + blib_block_t *first_block = list_get_first_block(bl); + if (first_block->num == num) + block = first_block; + else + block = rb_find_block(bl, num); + } + + if (NULL != block) + { + D(("blib block=%d found in cache\n", num)); + + list_move_block_to_head(bl, block); + *dblock = block; + return ret; + } + + D(("blib block=%d NOT found in cache\n", num)); + + block = (blib_block_t *)cyg_mempool_fix_try_alloc(bl->mem_pool_h); + + if (NULL == block) + { + CYG_ASSERTC(!list_empty(&bl->list_head)); + + block = list_get_last_block(bl); + + D(("blib reusing block=%d space\n", block->num)); + + ret = blib_sync_block(bl, block); + if (ENOERR != ret) + return ret; + + list_del_block(bl, block); + rb_del_block(bl, block); + } + + block->num = num; + block->modified = false; + + if (read_data) + { + D(("blib reading block=%d\n", block->num)); + + STAT(bl, n_reads); + + len = 1; + ret = bl->bread_fn(bl->priv, (void *)block->data, &len, block->num); + if (ENOERR != ret) + { + cyg_mempool_fix_free(bl->mem_pool_h, block); + return ret; + } + } + rb_add_block(bl, block); + list_add_block(bl, block); + + *dblock = block; + return ret; +} + +static int +blib_free_cache(cyg_blib_t *bl) +{ + int ret = ENOERR; + ret = blib_sync(bl); + if (ENOERR == ret) + cyg_mempool_fix_delete(bl->mem_pool_h); + return ret; +} + +static int +blib_init_cache(cyg_blib_t *bl, + void *mem_base, + cyg_uint32 mem_size, + cyg_uint32 block_size, + cyg_bool reinit) +{ + int ret = ENOERR; + cyg_uint32 mem_block_size; + + if (reinit) + { + ret = blib_free_cache(bl); + if (ENOERR != ret) + return ret; + } + + bl->rb_root = RB_ROOT; + INIT_LIST_HEAD(&bl->list_head); + + bl->mem_base = mem_base; + bl->mem_size = mem_size; + bl->block_size = block_size; + + bl->block_size_log2 = get_val_log2(block_size); + if (0 == bl->block_size_log2) + return -EINVAL; + + mem_block_size = bl->block_size + sizeof(blib_block_t); + + cyg_mempool_fix_create(bl->mem_base, bl->mem_size, mem_block_size, + &bl->mem_pool_h, &bl->mem_pool); + + STAT_INIT(bl); + + return ret; +} + +// -------------------------------------------------------------------- + +static int +io_bread(void *priv, void *buf, cyg_uint32 *len, cyg_uint32 pos) +{ + return cyg_io_bread((cyg_io_handle_t)priv, buf, len, pos); +} + +static int +io_bwrite(void *priv, const void *buf, cyg_uint32 *len, cyg_uint32 pos) +{ + return cyg_io_bwrite((cyg_io_handle_t)priv, buf, len, pos); +} + +// -------------------------------------------------------------------- + +int +cyg_blib_create(void *priv_data, + void *mem_base, + cyg_uint32 mem_size, + cyg_uint32 block_size, + cyg_blib_bread_fn bread_fn, + cyg_blib_bwrite_fn bwrite_fn, + cyg_blib_t *bl) +{ + int ret; + + bl->priv = priv_data; + bl->bread_fn = bread_fn; + bl->bwrite_fn = bwrite_fn; + + ret = blib_init_cache(bl, mem_base, mem_size, block_size, false); + return ret; +} + +int +cyg_blib_io_create(cyg_io_handle_t handle, + void *mem_base, + cyg_uint32 mem_size, + cyg_uint32 block_size, + cyg_blib_t *bl) +{ + return cyg_blib_create((void *)handle, mem_base, mem_size, block_size, + io_bread, io_bwrite, bl); +} + +int +cyg_blib_delete(cyg_blib_t *bl) +{ + return blib_free_cache(bl); +} + +int +cyg_blib_bread(cyg_blib_t *bl, + void *buf, + cyg_uint32 *len, + cyg_uint32 pos) +{ + blib_block_t *block; + cyg_int32 size = *len; + cyg_int32 bnum = pos; + cyg_uint8 *bbuf = buf; + Cyg_ErrNo ret = ENOERR; + + D(("blib bread block=%d len=%d buf=%p\n", pos, *len, buf)); + + while (size > 0) + { + ret = blib_get_block(bl, bnum, true, &block); + if (ENOERR != ret) + break; + + memcpy((void *)bbuf, (void *)block->data, bl->block_size); + + bbuf += bl->block_size; + bnum++; + size--; + } + *len -= size; + return ret; +} + +int +cyg_blib_bwrite(cyg_blib_t *bl, + const void *buf, + cyg_uint32 *len, + cyg_uint32 pos) +{ + blib_block_t *block; + cyg_int32 size = *len; + cyg_int32 bnum = pos; + cyg_uint8 *bbuf = (cyg_uint8 * const) buf; + Cyg_ErrNo ret = ENOERR; + + D(("blib bwrite block=%d len=%d buf=%p\n", pos, *len, buf)); + + while (size > 0) + { + ret = blib_get_block(bl, bnum, false, &block); + if (ENOERR != ret) + break; + + memcpy((void *)block->data, (void *)bbuf, bl->block_size); + block->modified = true; + + bbuf += bl->block_size; + bnum++; + size--; + } + *len -= size; + return ret; +} + +int +cyg_blib_read(cyg_blib_t *bl, + void *buf, + cyg_uint32 *len, + cyg_uint32 bnum, + cyg_uint32 pos) +{ + blib_block_t *block; + cyg_uint8 *bbuf = buf; + cyg_int32 size = *len; + Cyg_ErrNo ret = ENOERR; + + size = *len; + + if (pos >= bl->block_size) + { + bnum += pos >> bl->block_size_log2; + pos = pos & (bl->block_size - 1); + } + + D(("blib read len=%d pos=%d bnum=%d\n", *len, pos, bnum)); + + while (size > 0) + { + int csize; + + if ((size + pos) > bl->block_size) + csize = bl->block_size - pos; + else + csize = size; + + ret = blib_get_block(bl, bnum, true, &block); + if (ENOERR != ret) + break; + + memcpy((void *)bbuf, (void *)(block->data+pos), csize); + + bbuf += csize; + size -= csize; + pos = 0; + bnum++; + } + *len -= size; + return ret; +} + +int +cyg_blib_write(cyg_blib_t *bl, + const void *buf, + cyg_uint32 *len, + cyg_uint32 bnum, + cyg_uint32 pos) +{ + blib_block_t *block; + cyg_uint8 *bbuf = (cyg_uint8 * const) buf; + cyg_int32 size = *len; + Cyg_ErrNo ret = ENOERR; + + size = *len; + + if (pos >= bl->block_size) + { + bnum += pos >> bl->block_size_log2; + pos = pos & (bl->block_size - 1); + } + + D(("blib write len=%d pos=%d bnum=%d\n", *len, pos, bnum)); + + while (size > 0) + { + int csize; + + if ((size + pos) > bl->block_size) + csize = bl->block_size - pos; + else + csize = size; + + if (0 == pos && csize == bl->block_size) + ret = blib_get_block(bl, bnum, false, &block); + else + ret = blib_get_block(bl, bnum, true, &block); + if (ENOERR != ret) + break; + + memcpy((void *)(block->data+pos), (void *)bbuf, csize); + block->modified = true; + + bbuf += csize; + size -= csize; + pos = 0; + bnum++; + } + *len -= size; + return ret; +} + +int +cyg_blib_sync(cyg_blib_t *bl) +{ + return blib_sync(bl); +} + +int +cyg_blib_sync_block(cyg_blib_t *bl, cyg_uint32 num) +{ + blib_block_t *block = rb_find_block(bl, num); + + if (NULL != block) + return blib_sync_block(bl, block); + else + return -EINVAL; +} + +int +cyg_blib_flush(cyg_blib_t *bl) +{ + return blib_init_cache(bl, bl->mem_base, bl->mem_size, + bl->block_size, true); +} + +int +cyg_blib_set_block_size(cyg_blib_t *bl, cyg_uint32 block_size) +{ + return blib_init_cache(bl, bl->mem_base, bl->mem_size, + block_size, true); +} + +int +cyg_blib_get_stat(cyg_blib_t *bl, cyg_blib_stat_t *stat) +{ +#ifdef CYGIMP_BLOCK_LIB_STATISTICS + *stat = bl->stat; + return ENOERR; +#else + stat->n_gets = 0; + stat->n_reads = 0; + stat->n_writes = 0; + return -EINVAL; +#endif +} + +// -------------------------------------------------------------------- +// EOF blib.c