Mercurial > ecos
changeset 2300:654c837cc618
* include/disk.h (DISK_CHANNEL): No need for extra _part_dev
slot intended for entire disk's own devtab.
* src/disk.c (disk_lookup): Just access pdevs_dev directly
with no adjusted offset.
(disk_init): No longer set entry 0 from device devtab.
It's unnecessary.
* include/disk.h: Merge eCosCentric CVS with public eCos CVS.
This merges (and therefore changes) the API. ChangeLog
entries have been incorporated in the correct places below.
* include/diskio.h: Also merged.
* cdl/io_disk.cdl: Also merged.
* src/disk.c: Also merged.
* src/disk.c (read_partition): Switched to favour LBA partition
information in partition tables. This also means we don't now rely
on the driver reporting a correct CHS size for the disk.
(disk_bread, disk_bwrite): API changed to take length in sectors
rather than bytes. Also call hardware driver to transfer in
(up to) max_transfer sized chunks, rather than a sector at a
time. Call in to driver now made with DSR lock claimed, to avoid a
race condition between the DSR and the calling thread.
(disk_get_config): Added phys_block_size to channel info.
* include/diskio.h (struct cyg_disk_identify_t): Added
phys_block_size and max_transfer fields. These must be filled in
by the driver.
* include/disk.h (DISK_CHANNEL_INIT): New macro. Allows a
DISK_CHANNEL to be defined dynamically.
* src/disk.c (disk_bread, disk_bwrite): Made calls to
cyg_thread_yield() dependent on presence ok kernel.
* src/disk.c (disk_bwrite): Uncomment the cyg_thread_yield() at
the end to prevent thread starvation between threads at the same
priority.
(disk_bread): Ditto.
* src/disk.c (read_partition): Changed to account for very large
disks which report bogus CHS geometry. We can only use the LBA
partition parameters in such disks.
(disk_set_config): Added some extra debug output.
* src/disk.c:
Various changes to support disconnect/reconnect of changeable
media.
* include/diskio.h: Added mount counter to disk_info structure.
* include/disk.h: Changed disk_disconnected() function to take a
pointer to a struct disk_channel rather than a struct
cyg_devtab_entry. Added mount count to disk_channel structure.
Moved include of diskio.h to end to fix declaration problems.
* src/disk.c: Fixed bug in write routine where controller result
field was not being initialized. Added signals on controller queue
condition variable after setting controller busy flag to false.
* include/disk.h: Made controller result and busy fields
volatile. Just in case.
* include/disk.h:
* src/disk.c:
Many changes. Added additional disk_controller data structure to
better reflect hardware structure. Added support for serialization
of multithreaded accesses to a single controller. Added support
for asynchronous, interrupt driven, IO operations. Various
tidies.
| author | jlarmour |
|---|---|
| date | Thu, 21 Sep 2006 16:38:38 +0000 |
| parents | 04006a2b020d |
| children | 67647617a206 |
| files | packages/io/disk/current/ChangeLog packages/io/disk/current/include/disk.h packages/io/disk/current/include/diskio.h packages/io/disk/current/src/disk.c |
| diffstat | 4 files changed, 596 insertions(+), 153 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/io/disk/current/ChangeLog +++ b/packages/io/disk/current/ChangeLog @@ -1,14 +1,73 @@ +2006-09-21 Jonathan Larmour <jifl@eCosCentric.com> + + * include/disk.h (DISK_CHANNEL): No need for extra _part_dev + slot intended for entire disk's own devtab. + + * src/disk.c (disk_lookup): Just access pdevs_dev directly + with no adjusted offset. + (disk_init): No longer set entry 0 from device devtab. + It's unnecessary. + +2006-09-20 Jonathan Larmour <jifl@eCosCentric.com> + + * include/disk.h: Merge eCosCentric CVS with public eCos CVS. + This merges (and therefore changes) the API. ChangeLog + entries have been incorporated in the correct places below. + * include/diskio.h: Also merged. + * cdl/io_disk.cdl: Also merged. + * src/disk.c: Also merged. + 2006-08-18 Andy Jackson <andy@xylanta.com> * cdl/io_disk.cdl: Made debugging CDL controlled. * src/disk.c: Added support to allow non-CHS disk devices to use LBA information in MBR. +2006-02-03 Nick Garnett <nickg@ecoscentric.com> + + * src/disk.c (read_partition): Switched to favour LBA partition + information in partition tables. This also means we don't now rely + on the driver reporting a correct CHS size for the disk. + (disk_bread, disk_bwrite): API changed to take length in sectors + rather than bytes. Also call hardware driver to transfer in + (up to) max_transfer sized chunks, rather than a sector at a + time. Call in to driver now made with DSR lock claimed, to avoid a + race condition between the DSR and the calling thread. + (disk_get_config): Added phys_block_size to channel info. + + * include/diskio.h (struct cyg_disk_identify_t): Added + phys_block_size and max_transfer fields. These must be filled in + by the driver. + +2005-12-12 Jonathan Larmour <jifl@eCosCentric.com> + + * include/disk.h (DISK_CHANNEL_INIT): New macro. Allows a + DISK_CHANNEL to be defined dynamically. + 2005-12-02 Andrew Lunn <andrew.lunn@ascom.ch> * src/disk.c: Add comments that bread/bwrite take the position and len in terms of blocks, not bytes. +2005-10-11 Nick Garnett <nickg@ecoscentric.com> + + * src/disk.c (disk_bread, disk_bwrite): Made calls to + cyg_thread_yield() dependent on presence ok kernel. + +2005-06-01 Jonathan Larmour <jifl@eCosCentric.com> + + * src/disk.c (disk_bwrite): Uncomment the cyg_thread_yield() at + the end to prevent thread starvation between threads at the same + priority. + (disk_bread): Ditto. + +2004-07-21 Nick Garnett <nickg@ecoscentric.com> + + * src/disk.c (read_partition): Changed to account for very large + disks which report bogus CHS geometry. We can only use the LBA + partition parameters in such disks. + (disk_set_config): Added some extra debug output. + 2004-07-01 Savin Zlobec <savin@elatec.si> * src/disk.c: @@ -18,7 +77,39 @@ 2004-07-01 Savin Zlobec <savin@elatec. radher than malloc. Extended DISK_CHANNEL macro to support defining maximum number of partitions. -2004-01-15 Nick Garnett <nickg@calivar.com> +2004-04-15 Nick Garnett <nickg@ecoscentric.com> + + * src/disk.c: + Various changes to support disconnect/reconnect of changeable + media. + + * include/diskio.h: Added mount counter to disk_info structure. + + * include/disk.h: Changed disk_disconnected() function to take a + pointer to a struct disk_channel rather than a struct + cyg_devtab_entry. Added mount count to disk_channel structure. + Moved include of diskio.h to end to fix declaration problems. + +2004-02-04 Nick Garnett <nickg@ecoscentric.com> + + * src/disk.c: Fixed bug in write routine where controller result + field was not being initialized. Added signals on controller queue + condition variable after setting controller busy flag to false. + + * include/disk.h: Made controller result and busy fields + volatile. Just in case. + +2004-01-23 Nick Garnett <nickg@ecoscentric.com> + + * include/disk.h: + * src/disk.c: + Many changes. Added additional disk_controller data structure to + better reflect hardware structure. Added support for serialization + of multithreaded accesses to a single controller. Added support + for asynchronous, interrupt driven, IO operations. Various + tidies. + +2004-01-15 Nick Garnett <nickg@ecoscentric.com> * src/disk.c: * include/disk.h: Removed block_pos arguments from @@ -48,7 +139,8 @@ 2003-09-01 Savin Zlobec <savin@elatec.si //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2003 Savin Zlobec +// Copyright (C) 2003 Savin Zlobec +// Copyright (C) 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
--- a/packages/io/disk/current/include/disk.h +++ b/packages/io/disk/current/include/disk.h @@ -11,7 +11,8 @@ // ------------------------------------------- // 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 +// Copyright (C) 2003 Savin Zlobec +// Copyright (C) 2004, 2005 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 @@ -59,12 +60,22 @@ #include <pkgconf/io_disk.h> #include <cyg/infra/cyg_type.h> +#include <cyg/io/devtab.h> #include <cyg/io/io.h> -#include <cyg/io/diskio.h> #include <cyg/hal/drv_api.h> +#include <string.h> /* memset() */ -typedef struct disk_channel disk_channel; -typedef struct disk_funs disk_funs; +// --------------------------------------------------------------------------- + +typedef struct cyg_disk_partition_t cyg_disk_partition_t; +typedef struct cyg_disk_info_t cyg_disk_info_t; +typedef struct cyg_disk_identify_t cyg_disk_identify_t; + +typedef struct disk_channel disk_channel; +typedef struct disk_controller disk_controller; +typedef struct disk_funs disk_funs; + +// --------------------------------------------------------------------------- // Pointers into upper-level driver typedef struct { @@ -77,33 +88,62 @@ typedef struct { cyg_disk_identify_t *ident); // Disk device has been disconnected - Cyg_ErrNo (*disk_disconnected)(struct cyg_devtab_entry *tab); + Cyg_ErrNo (*disk_disconnected)(struct disk_channel *chan); // Lookup disk device Cyg_ErrNo (*disk_lookup)(struct cyg_devtab_entry **tab, struct cyg_devtab_entry *sub_tab, const char *name); + + // Asynchronous block transfer done + void (*disk_transfer_done)(struct disk_channel *chan, Cyg_ErrNo res); + } disk_callbacks_t; -#define DISK_CALLBACKS(_l, \ - _init, \ - _connected, \ - _disconnected, \ - _lookup) \ -disk_callbacks_t _l = { \ - _init, \ - _connected, \ - _disconnected, \ - _lookup \ +#define DISK_CALLBACKS(_l, \ + _init, \ + _connected, \ + _disconnected, \ + _lookup, \ + _transfer_done) \ +disk_callbacks_t _l = { \ + _init, \ + _connected, \ + _disconnected, \ + _lookup, \ + _transfer_done \ }; extern disk_callbacks_t cyg_io_disk_callbacks; +// --------------------------------------------------------------------------- +// Private data which describes a disk controller + +struct disk_controller { + cyg_drv_mutex_t lock; // Per-controller lock + cyg_drv_cond_t queue; // Access wait list + cyg_drv_cond_t async; // Async transfer waits here + void *priv; // Private data + volatile Cyg_ErrNo result; // Last operation result + cyg_bool init; // Initialized? + volatile cyg_bool busy; // Busy? +}; + +#define DISK_CONTROLLER(_l, _priv) \ +static disk_controller _l = { \ + priv: &_priv, \ + init: false, \ + busy: false \ +}; + +// --------------------------------------------------------------------------- // Private data which describes this channel + struct disk_channel { disk_funs *funs; disk_callbacks_t *callbacks; void *dev_priv; // device private data + disk_controller *controller; // pointer to controller cyg_disk_info_t *info; // disk info cyg_disk_partition_t *partition; // partition data struct cyg_devtab_entry *pdevs_dev; // partition devs devtab ents @@ -111,12 +151,14 @@ struct disk_channel { cyg_bool mbr_support; // true if disk has MBR cyg_bool valid; // true if device valid cyg_bool init; // true if initialized + cyg_ucount16 mounts; // count of number of mounts }; // Initialization macro for disk channel #define DISK_CHANNEL(_l, \ _funs, \ _dev_priv, \ + _controller, \ _mbr_supp, \ _max_part_num) \ static struct cyg_devtab_entry _l##_part_dev[_max_part_num]; \ @@ -130,16 +172,49 @@ static disk_channel _l = { &(_funs), \ &cyg_io_disk_callbacks, \ &(_dev_priv), \ + &(_controller), \ &(_l##_disk_info), \ NULL, \ _l##_part_dev, \ _l##_part_chan, \ _mbr_supp, \ false, \ - false \ + false, \ + 0 \ }; +// Initialization macro for disk channel allocated elsewhere. +#define DISK_CHANNEL_INIT(_dc, \ + _funs, \ + _dev_priv, \ + _controller, \ + _disk_info, \ + _part_dev, \ + _part_chan, \ + _part_tab, \ + _mbr_supp, \ + _max_part_num) \ + CYG_MACRO_START \ + memset((_disk_info), 0, sizeof(cyg_disk_info_t)); \ + (_dc).funs = &(_funs); \ + (_dc).callbacks = &cyg_io_disk_callbacks; \ + (_dc).dev_priv = (_dev_priv); \ + (_dc).controller = &(_controller); \ + (_dc).info = &(_disk_info); \ + (_dc).info->partitions = (_part_tab); \ + (_dc).pdevs_dev = (_part_dev); \ + (_dc).pdevs_chan = (_part_chan); \ + (_dc).partition = NULL; \ + (_dc).mbr_support = (_mbr_supp); \ + (_dc).valid = false; \ + (_dc).init = false; \ + (_dc).mounts = 0; \ + (_dc).info->partitions_num = (_max_part_num); \ + CYG_MACRO_END + +// --------------------------------------------------------------------------- // Low level interface functions + struct disk_funs { // Read block data into buf @@ -168,7 +243,7 @@ struct disk_funs { }; #define DISK_FUNS(_l,_read,_write,_get_config,_set_config) \ -disk_funs _l = { \ +static disk_funs _l = { \ _read, \ _write, \ _get_config, \ @@ -177,4 +252,9 @@ disk_funs _l = { extern cyg_devio_table_t cyg_io_disk_devio; +// --------------------------------------------------------------------------- + +#include <cyg/io/diskio.h> + +// --------------------------------------------------------------------------- #endif // CYGONCE_DISK_H
--- a/packages/io/disk/current/include/diskio.h +++ b/packages/io/disk/current/include/diskio.h @@ -10,7 +10,8 @@ //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2003 Savin Zlobec +// Copyright (C) 2003 Savin Zlobec +// Copyright (C) 2004, 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 @@ -55,11 +56,14 @@ #include <cyg/infra/cyg_type.h> #include <cyg/io/config_keys.h> +#include <cyg/io/devtab.h> + #ifdef __cplusplus extern "C" { #endif - -typedef struct { + +struct cyg_disk_identify_t +{ char serial[20+1]; // serial number char firmware_rev[8+1]; // firmware revision char model_num[40+1]; // model number @@ -67,24 +71,30 @@ typedef struct { 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; + cyg_uint32 phys_block_size; // physical block size in sectors + cyg_uint32 max_transfer; // Maximum transfer size in bytes +}; -typedef struct { +struct cyg_disk_partition_t +{ 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_partition_t *partitions; // partition table - int partitions_num;// partition table size - cyg_disk_identify_t ident; // identify data - 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; +struct cyg_disk_info_t +{ + cyg_disk_partition_t *partitions; // partition table + int partitions_num;// partition table size + cyg_disk_identify_t ident; // identify data + cyg_uint32 block_size; // logical block size + cyg_uint32 blocks_num; // number of blocks on disk + cyg_uint32 phys_block_size; // physical block size + cyg_bool connected; // true if device connected + cyg_ucount16 mounts; // total number of mounts for all partitions +}; #ifdef __cplusplus }
--- a/packages/io/disk/current/src/disk.c +++ b/packages/io/disk/current/src/disk.c @@ -8,7 +8,8 @@ //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2003 Savin Zlobec +// Copyright (C) 2003 Savin Zlobec +// Copyright (C) 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 @@ -139,24 +140,27 @@ static cyg_bool disk_init(struct cyg_dev 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_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_lookup, + disk_transfer_done ); // --------------------------------------------------------------------------- - // // Read partition from data // + static void read_partition(cyg_uint8 *data, cyg_disk_info_t *info, @@ -164,56 +168,122 @@ read_partition(cyg_uint8 *dat { 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); - // If disk doesn't have cylinders/heads, use LBA data rather than CHS - if (ident->cylinders_num == 0 && ident->heads_num == 0 && - ident->sectors_num== 0) + 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 ) { - // Use LBA data to determine disk size - READ_DWORD(&data[8], part->start); - part->end = (part->start + part->size) - 1; - D(("LBA partition data (%d,%d,%d)\n", - part->start, part->end, part->size)); + 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 { - // Use CHS data to determine disk size READ_CHS(&data[1], c, h, s); - D(("partition start CHS %d,%d,%d\n", c, h, s)); - CHS_TO_LBA(ident, c, h, s, part->start); - + 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); - D(("partition end CHS %d,%d,%d\n", c, h, s)); - CHS_TO_LBA(ident, c, h, s, part->end); - D(("CHS partition data (%d,%d,%d)\n", - part->start, part->end, part->size)); + 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; - - res = (funs->read)(chan, (void *)buf, 512, 0); + + + + 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; @@ -244,6 +314,8 @@ read_mbr(disk_channel *chan) return ENOERR; } +// --------------------------------------------------------------------------- + static cyg_bool disk_init(struct cyg_devtab_entry *tab) { @@ -253,8 +325,19 @@ disk_init(struct cyg_devtab_entry *tab) 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; @@ -264,6 +347,8 @@ disk_init(struct cyg_devtab_entry *tab) return true; } +// --------------------------------------------------------------------------- + static Cyg_ErrNo disk_connected(struct cyg_devtab_entry *tab, cyg_disk_identify_t *ident) @@ -274,18 +359,31 @@ disk_connected(struct cyg_devtab_entry * 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 = %d\n", info->blocks_num)); - + 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 @@ -301,16 +399,17 @@ disk_connected(struct cyg_devtab_entry * return res; } +// --------------------------------------------------------------------------- + static Cyg_ErrNo -disk_disconnected(struct cyg_devtab_entry *tab) +disk_disconnected(disk_channel *chan) { - 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; @@ -327,6 +426,8 @@ disk_disconnected(struct cyg_devtab_entr return ENOERR; } +// --------------------------------------------------------------------------- + static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab, struct cyg_devtab_entry *sub_tab, @@ -396,6 +497,7 @@ disk_bread(cyg_io_handle_t handle, { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; disk_channel *chan = (disk_channel *) t->priv; + disk_controller *ctlr = chan->controller; disk_funs *funs = chan->funs; cyg_disk_info_t *info = chan->info; cyg_uint32 size = *len; @@ -403,43 +505,88 @@ disk_bread(cyg_io_handle_t handle, Cyg_ErrNo res = ENOERR; cyg_uint32 last; - if (!info->connected || !chan->valid) - return -EINVAL; + cyg_drv_mutex_lock( &ctlr->lock ); + + while( ctlr->busy ) + cyg_drv_cond_wait( &ctlr->queue ); + + if (info->connected && chan->valid) + { + ctlr->busy = true; - if (NULL != chan->partition) - { - pos += chan->partition->start; - last = chan->partition->end; + 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 ) + { + cyg_uint32 tfr = size; + + if (pos > last) + { + res = -EIO; + break; + } + + if( tfr > info->ident.max_transfer ) + tfr = info->ident.max_transfer; + + ctlr->result = -EWOULDBLOCK; + + cyg_drv_dsr_lock(); + + res = (funs->read)(chan, (void*)bbuf, tfr, pos); + + 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; + } + + cyg_drv_dsr_unlock(); + + if (ENOERR != res) + goto done; + + if (!info->connected) + { + res = -EINVAL; + goto done; + } + + bbuf += tfr * info->block_size; + pos += tfr; + size -= tfr; + } + + ctlr->busy = false; + cyg_drv_cond_signal( &ctlr->queue ); } 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; + res = -EINVAL; - if (!info->connected) - { - res = -EINVAL; - break; - } - - bbuf += info->block_size; - pos++; - size--; - } +done: + + cyg_drv_mutex_unlock( &ctlr->lock ); +#ifdef CYGPKG_KERNEL + cyg_thread_yield(); +#endif + *len -= size; return res; } @@ -454,6 +601,7 @@ disk_bwrite(cyg_io_handle_t handle, { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; disk_channel *chan = (disk_channel *) t->priv; + disk_controller *ctlr = chan->controller; disk_funs *funs = chan->funs; cyg_disk_info_t *info = chan->info; cyg_uint32 size = *len; @@ -461,49 +609,106 @@ disk_bwrite(cyg_io_handle_t handle, Cyg_ErrNo res = ENOERR; cyg_uint32 last; - if (!info->connected || !chan->valid) - return -EINVAL; + cyg_drv_mutex_lock( &ctlr->lock ); + + while( ctlr->busy ) + cyg_drv_cond_wait( &ctlr->queue ); + + if (info->connected && chan->valid) + { + ctlr->busy = true; + + 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 ) + { + cyg_uint32 tfr = size; + + if (pos > last) + { + res = -EIO; + goto done; + } + + if( tfr > info->ident.max_transfer ) + tfr = info->ident.max_transfer; + + ctlr->result = -EWOULDBLOCK; + + cyg_drv_dsr_lock(); + + res = (funs->write)(chan, (void*)bbuf, tfr, pos); + + 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; + } + + cyg_drv_dsr_unlock(); + + if (ENOERR != res) + goto done; - if (NULL != chan->partition) - { - pos += chan->partition->start; - last = chan->partition->end; + if (!info->connected) + { + res = -EINVAL; + goto done; + } + + bbuf += tfr * info->block_size; + pos += tfr; + size -= tfr; + + } + + ctlr->busy = false; + cyg_drv_cond_signal( &ctlr->queue ); } else - { - last = info->blocks_num-1; - } + res = -EINVAL; + +done: - 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--; - } + cyg_drv_mutex_unlock( &ctlr->lock ); +#ifdef CYGPKG_KERNEL + cyg_thread_yield(); +#endif + *len -= size; return res; } // --------------------------------------------------------------------------- +static void disk_transfer_done(struct disk_channel *chan, Cyg_ErrNo res) +{ + disk_controller *ctlr = chan->controller; + + ctlr->result = res; + + cyg_drv_cond_signal( &ctlr->async ); +} + +// --------------------------------------------------------------------------- + static cyg_bool disk_select(cyg_io_handle_t handle, cyg_uint32 which, CYG_ADDRWORD info) { @@ -527,30 +732,49 @@ disk_get_config(cyg_io_handle_t handle, { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; disk_channel *chan = (disk_channel *) t->priv; + disk_controller *ctlr = chan->controller; 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; + cyg_drv_mutex_lock( &ctlr->lock ); + + while( ctlr->busy ) + cyg_drv_cond_wait( &ctlr->queue ); - D(("disk get config key=%d\n", key)); + if (info->connected && chan->valid) + { + ctlr->busy = true; + + 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; + switch (key) { + case CYG_IO_GET_CONFIG_DISK_INFO: + if (*len < sizeof(cyg_disk_info_t)) { + res = -EINVAL; + break; + } + D(("chan->info->block_size %u\n", chan->info->block_size )); + D(("chan->info->blocks_num %u\n", chan->info->blocks_num )); + D(("chan->info->phys_block_size %u\n", chan->info->phys_block_size )); + *buf = *chan->info; + *len = sizeof(cyg_disk_info_t); + break; + + default: + // pass down to lower layers + res = (funs->get_config)(chan, key, xbuf, len); } - *buf = *chan->info; - *len = sizeof(cyg_disk_info_t); - break; + + ctlr->busy = false; + cyg_drv_cond_signal( &ctlr->queue ); + } + else + res = -EINVAL; - default: - // pass down to lower layers - res = (funs->get_config)(chan, key, xbuf, len); - } - + cyg_drv_mutex_unlock( &ctlr->lock ); + return res; } @@ -564,16 +788,53 @@ disk_set_config(cyg_io_handle_t handle, { cyg_devtab_entry_t *t = (cyg_devtab_entry_t *) handle; disk_channel *chan = (disk_channel *) t->priv; + disk_controller *ctlr = chan->controller; cyg_disk_info_t *info = chan->info; disk_funs *funs = chan->funs; + Cyg_ErrNo res = ENOERR; + + cyg_drv_mutex_lock( &ctlr->lock ); - if (!info->connected || !chan->valid) - return -EINVAL; + while( ctlr->busy ) + cyg_drv_cond_wait( &ctlr->queue ); + + if (info->connected && chan->valid) + { + ctlr->busy = true; + + D(("disk set config key=%d\n", key)); - D(("disk set config key=%d\n", key)); - - // pass down to lower layers - return (funs->set_config)(chan, key, xbuf, len); + switch ( key ) + { + case CYG_IO_SET_CONFIG_DISK_MOUNT: + chan->mounts++; + info->mounts++; + D(("disk mount: chan %d disk %d\n",chan->mounts, info->mounts)); + break; + + case CYG_IO_SET_CONFIG_DISK_UMOUNT: + chan->mounts--; + info->mounts--; + D(("disk umount: chan %d disk %d\n",chan->mounts, info->mounts)); + break; + + default: + break; + } + + // pass down to lower layers + res = (funs->set_config)(chan, key, xbuf, len); + + ctlr->busy = false; + cyg_drv_cond_signal( &ctlr->queue ); + } + else + res = -EINVAL; + + cyg_drv_mutex_unlock( &ctlr->lock ); + + return res; + } // ---------------------------------------------------------------------------
