mmc_spi.c
字号:
}
return false;
}
// ----------------------------------------------------------------------------
// No hardware initialization is performed here. Even if a card is
// currently plugged in it may get removed before it gets mounted, so
// there is no point looking at the card here. It is still necessary
// to invoke the callback init function so that higher-level code gets
// a chance to do its bit.
static cyg_bool
mmc_spi_disk_init(struct cyg_devtab_entry* tab)
{
disk_channel* chan = (disk_channel*) tab->priv;
MMC_SPI_INIT_FF_DATA();
return (*chan->callbacks->disk_init)(tab);
}
// lookup() is called during a mount() operation, so this is the right
// place to check whether or not there is a card.
static char*
mmc_spi_disk_lookup_itoa(cyg_uint32 num, char* where)
{
if (0 == num) {
*where++ = '0';
} else {
char local[10]; // 2^32 just fits into 10 places
int index = 9;
while (num > 0) {
local[index--] = (num % 10) + '0';
num /= 10;
}
for (index += 1; index < 10; index++) {
*where++ = local[index];
}
}
return where;
}
static Cyg_ErrNo
mmc_spi_disk_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry *sub_tab, const char* name)
{
disk_channel* chan = (disk_channel*) (*tab)->priv;
cyg_mmc_spi_disk_info_t* disk = (cyg_mmc_spi_disk_info_t*) chan->dev_priv;
Cyg_ErrNo result;
DEBUG2("mmc_spi_disk_lookup(): target name=%s\n", name );
DEBUG2(" : device name=%s dep_name=%s\n", tab[0]->name, tab[0]->dep_name );
DEBUG2(" : sub name=%s dep_name=%s\n", sub_tab->name, sub_tab->dep_name );
if (disk->mmc_connected) {
// There was a card plugged in last time we looked. Is it still there?
if (mmc_spi_disk_changed(disk)) {
// The old card is gone. Either there is no card plugged in, or
// it has been replaced with a different one. If the latter the
// existing mounts must be removed before anything sensible
// can be done.
disk->mmc_connected = false;
(*chan->callbacks->disk_disconnected)(chan);
if (0 != chan->info->mounts) {
return -ENODEV;
}
}
}
if ((0 != chan->info->mounts) && !disk->mmc_connected) {
// There are still mount points to an old card. We cannot accept
// new mount requests until those have been cleaned out.
return -ENODEV;
}
if (!disk->mmc_connected) {
cyg_disk_identify_t ident;
cyg_uint32 id_data;
char* where;
int i;
// The world is consistent and the higher-level code does not
// know anything about the current card, if any. Is there a
// card?
result = mmc_spi_check_for_disk(disk);
if (ENOERR != result) {
return result;
}
// A card has been found. Tell the higher-level code about it.
// This requires an identify structure, although it is not
// entirely clear what purpose that serves.
disk->mmc_connected = true;
// Serial number, up to 20 characters; The CID register contains
// various fields which can be used for this.
where = &(ident.serial[0]);
id_data = disk->mmc_id.cid_data[0]; // 1-byte manufacturer id -> 3 chars, 17 left
where = mmc_spi_disk_lookup_itoa(id_data, where);
id_data = (disk->mmc_id.cid_data[1] << 8) + disk->mmc_id.cid_data[2]; // 2-byte OEM ID, 5 chars, 12 left
where = mmc_spi_disk_lookup_itoa(id_data, where);
id_data = (disk->mmc_id.cid_data[10] << 24) + (disk->mmc_id.cid_data[11] << 16) +
(disk->mmc_id.cid_data[12] << 8) + disk->mmc_id.cid_data[13];
where = mmc_spi_disk_lookup_itoa(id_data, where); // 4-byte OEM ID, 10 chars, 2 left
// And terminate the string with a couple of places to spare.
*where = '\0';
// Firmware revision number. There is a one-byte product
// revision number in the CID, BCD-encoded
id_data = disk->mmc_id.cid_data[9] >> 4;
if (id_data <= 9) {
ident.firmware_rev[0] = id_data + '0';
} else {
ident.firmware_rev[0] = id_data - 10 + 'A';
}
id_data = disk->mmc_id.cid_data[9] & 0x0F;
if (id_data <= 9) {
ident.firmware_rev[1] = id_data + '0';
} else {
ident.firmware_rev[1] = id_data - 10 + 'A';
}
ident.firmware_rev[2] = '\0';
// Model number. There is a six-byte product name in the CID.
for (i = 0; i < 6; i++) {
if ((disk->mmc_id.cid_data[i + 3] >= 0x20) && (disk->mmc_id.cid_data[i+3] <= 0x7E)) {
ident.model_num[i] = disk->mmc_id.cid_data[i + 3];
} else {
break;
}
}
ident.model_num[i] = '\0';
// We don't have no cylinders, heads, or sectors, but
// higher-level code may interpret partition data using C/H/S
// addressing rather than LBA. Hence values for some of these
// settings were calculated above.
ident.cylinders_num = 1;
ident.heads_num = disk->mmc_heads_per_cylinder;
ident.sectors_num = disk->mmc_sectors_per_head;
ident.lba_sectors_num = disk->mmc_block_count;
ident.phys_block_size = disk->mmc_write_block_length/512;
ident.max_transfer = disk->mmc_write_block_length;
DEBUG1("Calling disk_connected(): serial %s, firmware %s, model %s, heads %d, sectors %d, lba_sectors_num %d, phys_block_size %d\n", \
ident.serial, ident.firmware_rev, ident.model_num, ident.heads_num, ident.sectors_num,
ident.lba_sectors_num, ident.phys_block_size);
(*chan->callbacks->disk_connected)(*tab, &ident);
// We now have a valid card and higher-level code knows about it. Fall through.
}
// And leave it to higher-level code to finish the lookup, taking
// into accounts partitions etc.
return (*chan->callbacks->disk_lookup)(tab, sub_tab, name);
}
static Cyg_ErrNo
mmc_spi_disk_read(disk_channel* chan, void* buf_arg, cyg_uint32 blocks, cyg_uint32 first_block)
{
cyg_mmc_spi_disk_info_t* disk = (cyg_mmc_spi_disk_info_t*) chan->dev_priv;
cyg_uint32 i;
cyg_uint8* buf = (cyg_uint8*) buf_arg;
Cyg_ErrNo code = ENOERR;
DEBUG1("mmc_spi_disk_read(): first block %d, buf %p, len %lu blocks (%lu bytes)\n",
first_block, buf, (unsigned long)blocks, (unsigned long)blocks*512);
if (! disk->mmc_connected) {
return -ENODEV;
}
if ((first_block + blocks) >= disk->mmc_block_count) {
return -EINVAL;
}
for (i = 0; (i < blocks) && (ENOERR == code); i++) {
code = mmc_spi_read_disk_block(disk, buf, first_block + i, false);
buf += MMC_SPI_BLOCK_SIZE;
}
return code;
}
static Cyg_ErrNo
mmc_spi_disk_write(disk_channel* chan, const void* buf_arg, cyg_uint32 blocks, cyg_uint32 first_block)
{
cyg_mmc_spi_disk_info_t* disk = (cyg_mmc_spi_disk_info_t*) chan->dev_priv;
cyg_uint32 i;
const cyg_uint8* buf = (cyg_uint8*) buf_arg;
Cyg_ErrNo code = ENOERR;
DEBUG1("mmc_spi_disk_write(): first block %d, buf %p, len %lu blocks (%lu bytes)\n",
first_block, buf, (unsigned long)blocks, (unsigned long)blocks*512);
if (! disk->mmc_connected) {
return -ENODEV;
}
if (disk->mmc_read_only) {
return -EROFS;
}
if ((first_block + blocks) >= disk->mmc_block_count) {
return -EINVAL;
}
for (i = 0; (i < blocks) && (ENOERR == code); i++) {
code = mmc_spi_write_disk_block(disk, buf, first_block + i);
buf += MMC_SPI_BLOCK_SIZE;
}
return code;
}
// get_config() and set_config(). There are no supported get_config() operations
// at this time.
static Cyg_ErrNo
mmc_spi_disk_get_config(disk_channel* chan, cyg_uint32 key, const void* buf, cyg_uint32* len)
{
CYG_UNUSED_PARAM(disk_channel*, chan);
CYG_UNUSED_PARAM(cyg_uint32, key);
CYG_UNUSED_PARAM(const void*, buf);
CYG_UNUSED_PARAM(cyg_uint32*, len);
return -EINVAL;
}
static Cyg_ErrNo
mmc_spi_disk_set_config(disk_channel* chan, cyg_uint32 key, const void* buf, cyg_uint32* len)
{
Cyg_ErrNo result = ENOERR;
cyg_mmc_spi_disk_info_t* disk = (cyg_mmc_spi_disk_info_t*) chan->dev_priv;
switch(key) {
case CYG_IO_SET_CONFIG_DISK_MOUNT:
// There will have been a successful lookup(), so there's
// little point in checking the disk again.
break;
case CYG_IO_SET_CONFIG_DISK_UMOUNT:
if (0 == chan->info->mounts) {
// If this is the last unmount of the card, mark it as
// disconnected. If the user then removes the card and
// plugs in a new one everything works cleanly. Also
// reset the SPI device's clockrate.
disk->mmc_connected = false;
mmc_spi_restore_baud(disk);
result = (chan->callbacks->disk_disconnected)(chan);
}
break;
}
return result;
}
// ----------------------------------------------------------------------------
// And finally the data structures that define this disk. Some of this
// should be moved into an exported header file so that applications can
// define additional disks.
//
// It is not obvious why there are quite so many structures. Apart
// from the devtab entries there are no tables involved, so there is
// no need to keep everything the same size. The cyg_disk_info_t could
// be the common part of a h/w info_t. The channel structure is
// redundant and its fields could be merged into the cyg_disk_info_t
// structure. That would leave a devtab entry, a disk info structure
// (h/w specific but with a common base), and a disk controller
// structure (ditto).
DISK_FUNS(cyg_mmc_spi_disk_funs,
mmc_spi_disk_read,
mmc_spi_disk_write,
mmc_spi_disk_get_config,
mmc_spi_disk_set_config
);
static cyg_mmc_spi_disk_info_t cyg_mmc_spi_disk0_hwinfo = {
.mmc_spi_dev = &cyg_spi_mmc_dev0,
#ifdef MMC_SPI_BACKGROUND_WRITES
.mmc_writing = 0,
#endif
.mmc_connected = 0
};
// No h/w controller structure is needed, but the address of the
// second argument is taken anyway.
DISK_CONTROLLER(cyg_mmc_spi_disk_controller_0, cyg_mmc_spi_disk0_hwinfo);
DISK_CHANNEL(cyg_mmc_spi_disk0_channel,
cyg_mmc_spi_disk_funs,
cyg_mmc_spi_disk0_hwinfo,
cyg_mmc_spi_disk_controller_0,
true, /* MBR support */
1 /* Number of partitions supported */
);
BLOCK_DEVTAB_ENTRY(cyg_mmc_spi_disk0_devtab_entry,
CYGDAT_DEVS_DISK_MMC_SPI_DISK0_NAME,
0,
&cyg_io_disk_devio,
&mmc_spi_disk_init,
&mmc_spi_disk_lookup,
&cyg_mmc_spi_disk0_channel);
// EOF mmc_spi.c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -