📄 disk.c
字号:
//==========================================================================
//
// io/disk/disk.c
//
// High level disk driver
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 2003. 2004, 2005, 2006 eCosCentric Limited
//
// 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
// ---------------------------------------------------------------------------
#ifdef CYGDBG_IO_DISK_DEBUG
#define DEBUG 1
#endif
#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 disk_channel *chan);
static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab,
struct cyg_devtab_entry *sub_tab,
const char *name);
static void disk_transfer_done(struct disk_channel *chan, Cyg_ErrNo res);
DISK_CALLBACKS(cyg_io_disk_callbacks,
disk_init,
disk_connected,
disk_disconnected,
disk_lookup,
disk_transfer_done
);
// ---------------------------------------------------------------------------
//
// Read partition from data
//
static void
read_partition(cyg_uint8 *data,
cyg_disk_info_t *info,
cyg_disk_partition_t *part)
{
cyg_disk_identify_t *ident = &info->ident;
cyg_uint16 c, h, s;
cyg_uint32 start, end, size;
#ifdef DEBUG
diag_printf("Partition data:\n");
diag_dump_buf( data, 16 );
diag_printf("Disk geometry: %d/%d/%d\n",info->ident.cylinders_num,
info->ident.heads_num, info->ident.sectors_num );
#endif
// Retrieve basic information
part->type = data[4];
part->state = data[0];
READ_DWORD(&data[12], part->size);
READ_DWORD(&data[8], start);
READ_DWORD(&data[12], size);
// Use the LBA start and size fields if they are valid. Otherwise
// fall back to CHS.
if( start > 0 && size > 0 )
{
READ_DWORD(&data[8], start);
end = start + size - 1;
#ifdef DEBUG
diag_printf("Using LBA partition parameters\n");
diag_printf(" LBA start %d\n",start);
diag_printf(" LBA size %d\n",size);
diag_printf(" LBA end %d\n",end);
#endif
}
else
{
READ_CHS(&data[1], c, h, s);
CHS_TO_LBA(ident, c, h, s, start);
#ifdef DEBUG
diag_printf("Using CHS partition parameters\n");
diag_printf(" CHS start %d/%d/%d => %d\n",c,h,s,start);
#endif
READ_CHS(&data[5], c, h, s);
CHS_TO_LBA(ident, c, h, s, end);
#ifdef DEBUG
diag_printf(" CHS end %d/%d/%d => %d\n",c,h,s,end);
diag_printf(" CHS size %d\n",size);
#endif
}
part->size = size;
part->start = start;
part->end = end;
}
// ---------------------------------------------------------------------------
//
// 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;
disk_controller *ctlr = chan->controller;
cyg_uint8 buf[512];
Cyg_ErrNo res = ENOERR;
int i;
D(("read MBR\n"));
for (i = 0; i < info->partitions_num; i++)
info->partitions[i].type = 0x00;
cyg_drv_mutex_lock( &ctlr->lock );
while( ctlr->busy )
cyg_drv_cond_wait( &ctlr->queue );
ctlr->busy = true;
ctlr->result = -EWOULDBLOCK;
for( i = 0; i < sizeof(buf); i++ )
buf[i] = 0;
res = (funs->read)(chan, (void *)buf, 1, 0);
if( res == -EWOULDBLOCK )
{
// If the driver replys EWOULDBLOCK, then the transfer is
// being handled asynchronously and when it is finished it
// will call disk_transfer_done(). This will wake us up here
// to continue.
while( ctlr->result == -EWOULDBLOCK )
cyg_drv_cond_wait( &ctlr->async );
res = ctlr->result;
}
ctlr->busy = false;
cyg_drv_mutex_unlock( &ctlr->lock );
if (ENOERR != res)
return res;
#ifdef DEBUG
diag_dump_buf_with_offset( buf, 512, buf );
#endif
if (MBR_SIG_BYTE0 == buf[MBR_SIG_ADDR+0] && MBR_SIG_BYTE1 == buf[MBR_SIG_ADDR+1])
{
int npart;
D(("disk MBR found\n"));
npart = info->partitions_num < MBR_PART_NUM ?
info->partitions_num : MBR_PART_NUM;
for (i = 0; i < npart; 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)
{
disk_controller *controller = chan->controller;
if( !controller->init )
{
cyg_drv_mutex_init( &controller->lock );
cyg_drv_cond_init( &controller->queue, &controller->lock );
cyg_drv_cond_init( &controller->async, &controller->lock );
controller->init = true;
}
info->connected = false;
// clear partition data
for (i = 0; i < info->partitions_num; i++)
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;
// If the device is already connected, nothing more to do
if( info->connected )
return ENOERR;
// If any of these assertions fire, it is probable that the
// hardware driver has not been updated to match the current disk
// API.
CYG_ASSERT( ident->lba_sectors_num > 0, "Bad LBA sector count" );
CYG_ASSERT( ident->phys_block_size > 0, "Bad physical block size");
CYG_ASSERT( ident->max_transfer > 0, "Bad max transfer size");
info->ident = *ident;
info->block_size = 512;
info->blocks_num = ident->lba_sectors_num;
info->phys_block_size = ident->phys_block_size;
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 = %u\n", info->blocks_num));
D((" phys_block_size = %d\n", info->phys_block_size));
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(disk_channel *chan)
{
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 partition devices
for (i = 0; i < info->partitions_num; i++)
{
info->partitions[i].type = 0x00;
chan->pdevs_chan[i].valid = false;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -