diff packages/io/disk/current/src/disk.c @ 1476:f2453fae08d4

Added support for FAT filesystem. Several packages, see the individual ChangeLogs for details.
author nickg
date Mon, 19 Jan 2004 14:35:01 +0000
parents
children b1d2762b2eec
line wrap: on
line diff
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 <pkgconf/io.h>
+#include <pkgconf/io_disk.h>
+
+#include <cyg/io/io.h>
+#include <cyg/io/devtab.h>
+#include <cyg/io/disk.h>
+#include <cyg/infra/cyg_ass.h>      // assertion support
+#include <cyg/infra/diag.h>         // diagnostic output
+
+#include <stdlib.h>  // 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