X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;ds=sidebyside;f=packages%2Fio%2Fdisk%2Fv2_0%2Fsrc%2Fdisk.c;h=05a2708b1a22ebd9a64988ea2e8d8e758ea07cf2;hb=7a4ea0a4d67744fd3f6b5f207d857005fc707b46;hp=f5d9c02f10389c627791f128e48ba9252a27ca62;hpb=f0c1bd5d9f8457be4a43912a28ca2df207a7f5a4;p=karo-tx-redboot.git diff --git a/packages/io/disk/v2_0/src/disk.c b/packages/io/disk/v2_0/src/disk.c index f5d9c02f..05a2708b 100644 --- a/packages/io/disk/v2_0/src/disk.c +++ b/packages/io/disk/v2_0/src/disk.c @@ -8,7 +8,7 @@ //####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. -// Copyright (C) 2003 Savin Zlobec +// 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 @@ -58,7 +58,9 @@ // --------------------------------------------------------------------------- -//#define DEBUG 1 +#ifdef CYGDBG_IO_DISK_DEBUG +#define DEBUG 1 +#endif #ifdef DEBUG # define D(_args_) diag_printf _args_ @@ -137,62 +139,150 @@ 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_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, 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_CHS(&data[1], c, h, s); - CHS_TO_LBA(&info->ident, c, h, s, part->start); + READ_DWORD(&data[8], start); + READ_DWORD(&data[12], size); - READ_CHS(&data[5], c, h, s); - CHS_TO_LBA(&info->ident, c, h, s, part->end); + // Use the LBA start and size fields if they are valid. Otherwise + // fall back to CHS. - READ_DWORD(&data[12], part->size); + 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; - - 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; @@ -223,6 +313,8 @@ read_mbr(disk_channel *chan) return ENOERR; } +// --------------------------------------------------------------------------- + static cyg_bool disk_init(struct cyg_devtab_entry *tab) { @@ -232,8 +324,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; @@ -243,6 +346,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) @@ -253,18 +358,31 @@ disk_connected(struct cyg_devtab_entry *tab, 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 @@ -280,16 +398,17 @@ disk_connected(struct cyg_devtab_entry *tab, 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; @@ -306,6 +425,8 @@ disk_disconnected(struct cyg_devtab_entry *tab) return ENOERR; } +// --------------------------------------------------------------------------- + static Cyg_ErrNo disk_lookup(struct cyg_devtab_entry **tab, struct cyg_devtab_entry *sub_tab, @@ -370,11 +491,12 @@ disk_lookup(struct cyg_devtab_entry **tab, static Cyg_ErrNo disk_bread(cyg_io_handle_t handle, void *buf, - cyg_uint32 *len, - cyg_uint32 pos) + cyg_uint32 *len, // In blocks + cyg_uint32 pos) // In blocks { 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; @@ -382,43 +504,88 @@ disk_bread(cyg_io_handle_t handle, 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) + cyg_drv_mutex_lock( &ctlr->lock ); + + while( ctlr->busy ) + cyg_drv_cond_wait( &ctlr->queue ); + + if (info->connected && chan->valid) { - if (pos > last) + ctlr->busy = true; + + if (NULL != chan->partition) { - res = -EIO; - break; + pos += chan->partition->start; + last = chan->partition->end; } - - res = (funs->read)(chan, (void*)bbuf, info->block_size, pos); - if (ENOERR != res) - break; - - if (!info->connected) + else { - res = -EINVAL; - break; + 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(); - bbuf += info->block_size; - pos++; - size--; + 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 + res = -EINVAL; + +done: + + cyg_drv_mutex_unlock( &ctlr->lock ); +#ifdef CYGPKG_KERNEL + cyg_thread_yield(); +#endif + *len -= size; return res; } @@ -428,11 +595,12 @@ disk_bread(cyg_io_handle_t handle, static Cyg_ErrNo disk_bwrite(cyg_io_handle_t handle, const void *buf, - cyg_uint32 *len, - cyg_uint32 pos) + cyg_uint32 *len, // In blocks + cyg_uint32 pos) // In blocks { 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; @@ -440,49 +608,106 @@ disk_bwrite(cyg_io_handle_t handle, 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) + cyg_drv_mutex_lock( &ctlr->lock ); + + while( ctlr->busy ) + cyg_drv_cond_wait( &ctlr->queue ); + + if (info->connected && chan->valid) { - if (pos > last) + ctlr->busy = true; + + if (NULL != chan->partition) { - res = -EIO; - break; + 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; - res = (funs->write)(chan, (void*)bbuf, info->block_size, pos); - if (ENOERR != res) - break; + 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 (!info->connected) - { - res = -EINVAL; - break; + if (!info->connected) + { + res = -EINVAL; + goto done; + } + + bbuf += tfr * info->block_size; + pos += tfr; + size -= tfr; + } - bbuf += info->block_size; - pos++; - size--; + ctlr->busy = false; + cyg_drv_cond_signal( &ctlr->queue ); } + else + res = -EINVAL; + +done: + + 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) { @@ -506,30 +731,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; - switch (key) { - case CYG_IO_GET_CONFIG_DISK_INFO: - if (*len < sizeof(cyg_disk_info_t)) { - 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)) { + 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; - - default: - // pass down to lower layers - res = (funs->get_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; } @@ -543,16 +787,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 ); - D(("disk set config key=%d\n", key)); - - // pass down to lower layers - return (funs->set_config)(chan, key, xbuf, len); + if (info->connected && chan->valid) + { + ctlr->busy = true; + + D(("disk set config key=%d\n", key)); + + 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; + } // ---------------------------------------------------------------------------