mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-10-14 02:27:39 -04:00
storage: 64-bit sector offsets
* Create new 'sector_t' type alias: * uint64_t for all targets with HAVE_LBA48 or HAVE_SDUC * unsigned long for the everything else * Alter all storage APIs to use sector_t instead of 'unsigned long' * Alter Volume/Partition/storage info structures to use sector_t * Disk cache converted to sector_t * ATA Core: * convert to using sector_t for sector addresses and drive sizes * Always fill out upper 16 bits of LBA48 addresses * IDENTIFY INFO is fixed at 512 bytes, not SECTOR_SIZE * USB mass storage: * convert to using sector_t for sector addesses and drive sizes * Implement READ_16/WRITE_16 for LBA48 addresses * Convert FAT code to use sector_t for all sector references * output_dyn_value() now accepts int64_t instead of 'int' * Corrected "rockbox info" to work for (MULTIVOLUME & !MULTIDRIVE) * Better reporting of disk and (logical+physical) sector sizes in debug info * Detect SDUC cards and report on storage debug_info screen To-do: SDUC * Refactor SD core to remove duplicate code in every driver * Card probe and init state machine * Implement core SDUC support * SD2.0 needs to be 2.0+ (fixed for jz47xx and x1000) * Host and Card ID (ACMD41) * 32-bit addressing for all read/write/erase operations (CMD22) * ADD SDUC to target device drivers, defining HAVE_SDUC as appropriate Change-Id: Ib0138781a0081664d11511037685503df1b93608
This commit is contained in:
parent
9ff308a589
commit
15e5237469
49 changed files with 629 additions and 435 deletions
|
@ -557,14 +557,16 @@ static const char* dbg_partitions_getname(int selected_item, void *data,
|
||||||
if (!disk_partinfo(partition, &p))
|
if (!disk_partinfo(partition, &p))
|
||||||
return buffer;
|
return buffer;
|
||||||
|
|
||||||
|
// XXX fix this up to use logical sector size
|
||||||
|
// XXX and if mounted, show free info...
|
||||||
if (selected_item%2)
|
if (selected_item%2)
|
||||||
{
|
{
|
||||||
snprintf(buffer, buffer_len, " T:%x %ld MB", p.type,
|
snprintf(buffer, buffer_len, " T:%x %llu MB", p.type,
|
||||||
p.size / ( 2048 / ( SECTOR_SIZE / 512 )));
|
(uint64_t)(p.size / ( 2048 / ( SECTOR_SIZE / 512 ))));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
snprintf(buffer, buffer_len, "P%d: S:%lx", partition, p.start);
|
snprintf(buffer, buffer_len, "P%d: S:%llx", partition, (uint64_t)p.start);
|
||||||
}
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
@ -572,7 +574,7 @@ static const char* dbg_partitions_getname(int selected_item, void *data,
|
||||||
static bool dbg_partitions(void)
|
static bool dbg_partitions(void)
|
||||||
{
|
{
|
||||||
struct simplelist_info info;
|
struct simplelist_info info;
|
||||||
simplelist_info_init(&info, "Partition Info", NUM_DRIVES * 4, NULL);
|
simplelist_info_init(&info, "Partition Info", NUM_DRIVES * MAX_PARTITIONS_PER_DRIVE, NULL);
|
||||||
info.selection_size = 2;
|
info.selection_size = 2;
|
||||||
info.scroll_all = true;
|
info.scroll_all = true;
|
||||||
info.get_name = dbg_partitions_getname;
|
info.get_name = dbg_partitions_getname;
|
||||||
|
@ -1343,6 +1345,22 @@ static int disk_callback(int btn, struct gui_synclist *lists)
|
||||||
"R2W: *%d", card->r2w_factor);
|
"R2W: *%d", card->r2w_factor);
|
||||||
#if (CONFIG_STORAGE & STORAGE_SD)
|
#if (CONFIG_STORAGE & STORAGE_SD)
|
||||||
int csd_structure = card_extract_bits(card->csd, 127, 2);
|
int csd_structure = card_extract_bits(card->csd, 127, 2);
|
||||||
|
const char *ver;
|
||||||
|
switch(csd_structure) {
|
||||||
|
case 0:
|
||||||
|
ver = "1 (SD)";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ver = "2 (SDHC/SDXC)";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ver = "3 (SDUC)";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ver = "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
simplelist_addline("SDVer: %s\n", ver);
|
||||||
if (csd_structure == 0) /* CSD version 1.0 */
|
if (csd_structure == 0) /* CSD version 1.0 */
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
|
@ -1407,15 +1425,41 @@ static int disk_callback(int btn, struct gui_synclist *lists)
|
||||||
buf[8]=0;
|
buf[8]=0;
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
"Firmware: %s", buf);
|
"Firmware: %s", buf);
|
||||||
snprintf(buf, sizeof buf, "%ld MB",
|
|
||||||
((unsigned long)identify_info[61] << 16 |
|
uint64_t total_sectors = identify_info[60] | (identify_info[61] << 16);
|
||||||
(unsigned long)identify_info[60]) / 2048 );
|
#ifdef HAVE_LBA48
|
||||||
|
if (identify_info[83] & 0x0400
|
||||||
|
&& total_sectors == 0x0FFFFFFF)
|
||||||
|
total_sectors = identify_info[100] | (identify_info[101] << 16) | ((uint64_t)identify_info[102] << 32) | ((uint64_t)identify_info[103] << 48);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t sector_size;
|
||||||
|
|
||||||
|
/* Logical sector size > 512B ? */
|
||||||
|
if ((identify_info[106] & 0xd000) == 0x5000)
|
||||||
|
sector_size = identify_info[117] | (identify_info[118] << 16);
|
||||||
|
else
|
||||||
|
sector_size = SECTOR_SIZE;
|
||||||
|
|
||||||
|
total_sectors *= sector_size; /* Convert to bytes */
|
||||||
|
total_sectors /= (1024 * 1024); /* Convert to MB */
|
||||||
|
|
||||||
|
simplelist_addline("Size: %llu MB", total_sectors);
|
||||||
|
simplelist_addline("Logical sector size: %u B", sector_size);
|
||||||
|
|
||||||
|
if((identify_info[106] & 0xe000) == 0x6000)
|
||||||
|
sector_size *= BIT_N(identify_info[106] & 0x000f);
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
"Size: %s", buf);
|
"Physical sector size: %d B", sector_size);
|
||||||
unsigned long free;
|
|
||||||
|
#ifndef HAVE_MULTIVOLUME
|
||||||
|
// XXX this needs to be fixed for multi-volume setups
|
||||||
|
sector_t free;
|
||||||
volume_size( IF_MV(0,) NULL, &free );
|
volume_size( IF_MV(0,) NULL, &free );
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
"Free: %ld MB", free / 1024);
|
"Free: %llu MB", free / 1024);
|
||||||
|
#endif
|
||||||
|
|
||||||
simplelist_addline("SSD detected: %s", ata_disk_isssd() ? "yes" : "no");
|
simplelist_addline("SSD detected: %s", ata_disk_isssd() ? "yes" : "no");
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
"Spinup time: %d ms", storage_spinup_time() * (1000/HZ));
|
"Spinup time: %d ms", storage_spinup_time() * (1000/HZ));
|
||||||
|
@ -1452,11 +1496,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
"No timing info");
|
"No timing info");
|
||||||
}
|
}
|
||||||
int sector_size = 512;
|
|
||||||
if((identify_info[106] & 0xe000) == 0x6000)
|
|
||||||
sector_size *= BIT_N(identify_info[106] & 0x000f);
|
|
||||||
simplelist_addline(
|
|
||||||
"Physical sector size: %d", sector_size);
|
|
||||||
#ifdef HAVE_ATA_DMA
|
#ifdef HAVE_ATA_DMA
|
||||||
if (identify_info[63] & (1<<0)) {
|
if (identify_info[63] & (1<<0)) {
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
|
@ -1751,8 +1791,8 @@ static int disk_callback(int btn, struct gui_synclist *lists)
|
||||||
simplelist_addline("Model: %s", info.product);
|
simplelist_addline("Model: %s", info.product);
|
||||||
simplelist_addline("Firmware: %s", info.revision);
|
simplelist_addline("Firmware: %s", info.revision);
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
"Size: %ld MB", info.num_sectors*(info.sector_size/512)/2024);
|
"Size: %lld MB", (uint64_t)(info.num_sectors*(info.sector_size/512)/2048));
|
||||||
unsigned long free;
|
storage_t free;
|
||||||
volume_size( IF_MV(0,) NULL, &free );
|
volume_size( IF_MV(0,) NULL, &free );
|
||||||
simplelist_addline(
|
simplelist_addline(
|
||||||
"Free: %ld MB", free / 1024);
|
"Free: %ld MB", free / 1024);
|
||||||
|
@ -1771,13 +1811,13 @@ static bool dbg_identify_info(void)
|
||||||
const unsigned short *identify_info = ata_get_identify();
|
const unsigned short *identify_info = ata_get_identify();
|
||||||
#ifdef ROCKBOX_LITTLE_ENDIAN
|
#ifdef ROCKBOX_LITTLE_ENDIAN
|
||||||
/* this is a pointer to a driver buffer so we can't modify it */
|
/* this is a pointer to a driver buffer so we can't modify it */
|
||||||
for (int i = 0; i < SECTOR_SIZE/2; ++i)
|
for (int i = 0; i < ATA_IDENTIFY_WORDS; ++i)
|
||||||
{
|
{
|
||||||
unsigned short word = swap16(identify_info[i]);
|
unsigned short word = swap16(identify_info[i]);
|
||||||
write(fd, &word, 2);
|
write(fd, &word, 2);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
write(fd, identify_info, SECTOR_SIZE);
|
write(fd, identify_info, ATA_IDENTIFY_WORDS*2);
|
||||||
#endif
|
#endif
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,8 +134,8 @@ static int show_legal(void)
|
||||||
struct info_data
|
struct info_data
|
||||||
|
|
||||||
{
|
{
|
||||||
unsigned long size[NUM_VOLUMES];
|
sector_t size[NUM_VOLUMES];
|
||||||
unsigned long free[NUM_VOLUMES];
|
sector_t free[NUM_VOLUMES];
|
||||||
unsigned long name[NUM_VOLUMES];
|
unsigned long name[NUM_VOLUMES];
|
||||||
bool new_data;
|
bool new_data;
|
||||||
};
|
};
|
||||||
|
@ -162,16 +162,19 @@ enum infoscreenorder
|
||||||
*/
|
*/
|
||||||
static int refresh_data(struct info_data *info)
|
static int refresh_data(struct info_data *info)
|
||||||
{
|
{
|
||||||
int i = 0;
|
#ifdef HAVE_MULTIVOLUME
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
int drive;
|
|
||||||
int max = -1;
|
int max = -1;
|
||||||
|
#endif
|
||||||
|
int drive = 0;
|
||||||
|
int i = 0;
|
||||||
for (i = 0 ; CHECK_VOL(i) ; i++) {
|
for (i = 0 ; CHECK_VOL(i) ; i++) {
|
||||||
#endif
|
#endif
|
||||||
volume_size(IF_MV(i,) &info->size[i], &info->free[i]);
|
volume_size(IF_MV(i,) &info->size[i], &info->free[i]);
|
||||||
|
#ifdef HAVE_MULTIVOLUME
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
drive = volume_drive(i);
|
drive = volume_drive(i);
|
||||||
|
#endif
|
||||||
if (drive > 0 || info->size[i] == 0)
|
if (drive > 0 || info->size[i] == 0)
|
||||||
info->name[i] = LANG_DISK_NAME_MMC;
|
info->name[i] = LANG_DISK_NAME_MMC;
|
||||||
else
|
else
|
||||||
|
@ -182,9 +185,12 @@ static int refresh_data(struct info_data *info)
|
||||||
max = drive;
|
max = drive;
|
||||||
else if (drive < max)
|
else if (drive < max)
|
||||||
break;
|
break;
|
||||||
|
#elif defined(HAVE_MULTIVOLUME)
|
||||||
|
if (volume_partition(i) == -1)
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_MULTIVOLUME
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
i++;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
info->new_data = false;
|
info->new_data = false;
|
||||||
|
|
11
apps/misc.c
11
apps/misc.c
|
@ -139,7 +139,7 @@ const unsigned char * const unit_strings_core[] =
|
||||||
* voiced.*/
|
* voiced.*/
|
||||||
char *output_dyn_value(char *buf,
|
char *output_dyn_value(char *buf,
|
||||||
int buf_size,
|
int buf_size,
|
||||||
int value,
|
int64_t value,
|
||||||
const unsigned char * const *units,
|
const unsigned char * const *units,
|
||||||
unsigned int unit_count,
|
unsigned int unit_count,
|
||||||
bool binary_scale)
|
bool binary_scale)
|
||||||
|
@ -147,8 +147,9 @@ char *output_dyn_value(char *buf,
|
||||||
unsigned int scale = binary_scale ? 1024 : 1000;
|
unsigned int scale = binary_scale ? 1024 : 1000;
|
||||||
unsigned int fraction = 0;
|
unsigned int fraction = 0;
|
||||||
unsigned int unit_no = 0;
|
unsigned int unit_no = 0;
|
||||||
unsigned int value_abs = (value < 0) ? -value : value;
|
uint64_t value_abs = (value < 0) ? -value : value;
|
||||||
char tbuf[5];
|
char tbuf[5];
|
||||||
|
int value2;
|
||||||
|
|
||||||
while (value_abs >= scale && unit_no < (unit_count - 1))
|
while (value_abs >= scale && unit_no < (unit_count - 1))
|
||||||
{
|
{
|
||||||
|
@ -157,7 +158,7 @@ char *output_dyn_value(char *buf,
|
||||||
unit_no++;
|
unit_no++;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = (value < 0) ? -value_abs : value_abs; /* preserve sign */
|
value2 = (value < 0) ? -value_abs : value_abs; /* preserve sign */
|
||||||
fraction = (fraction * 1000 / scale) / 10;
|
fraction = (fraction * 1000 / scale) / 10;
|
||||||
|
|
||||||
if (value_abs >= 100 || fraction >= 100 || !unit_no)
|
if (value_abs >= 100 || fraction >= 100 || !unit_no)
|
||||||
|
@ -170,10 +171,10 @@ char *output_dyn_value(char *buf,
|
||||||
if (buf)
|
if (buf)
|
||||||
{
|
{
|
||||||
if (*tbuf)
|
if (*tbuf)
|
||||||
snprintf(buf, buf_size, "%d%s%s%s", value, str(LANG_POINT),
|
snprintf(buf, buf_size, "%d%s%s%s", value2, str(LANG_POINT),
|
||||||
tbuf, P2STR(units[unit_no]));
|
tbuf, P2STR(units[unit_no]));
|
||||||
else
|
else
|
||||||
snprintf(buf, buf_size, "%d%s", value, P2STR(units[unit_no]));
|
snprintf(buf, buf_size, "%d%s", value2, P2STR(units[unit_no]));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#define MISC_H
|
#define MISC_H
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "screen_access.h"
|
#include "screen_access.h"
|
||||||
|
@ -36,7 +37,7 @@ extern const unsigned char * const unit_strings_core[];
|
||||||
* voiced.*/
|
* voiced.*/
|
||||||
char *output_dyn_value(char *buf,
|
char *output_dyn_value(char *buf,
|
||||||
int buf_size,
|
int buf_size,
|
||||||
int value,
|
int64_t value,
|
||||||
const unsigned char * const *units,
|
const unsigned char * const *units,
|
||||||
unsigned int unit_count,
|
unsigned int unit_count,
|
||||||
bool binary_scale);
|
bool binary_scale);
|
||||||
|
|
|
@ -649,7 +649,7 @@ struct plugin_api {
|
||||||
int (*memcmp)(const void *s1, const void *s2, size_t n);
|
int (*memcmp)(const void *s1, const void *s2, size_t n);
|
||||||
char *(*strcasestr) (const char* phaystack, const char* pneedle);
|
char *(*strcasestr) (const char* phaystack, const char* pneedle);
|
||||||
char* (*strtok_r)(char *ptr, const char *sep, char **end);
|
char* (*strtok_r)(char *ptr, const char *sep, char **end);
|
||||||
char* (*output_dyn_value)(char *buf, int buf_size, int value,
|
char* (*output_dyn_value)(char *buf, int buf_size, int64_t value,
|
||||||
const unsigned char * const *units,
|
const unsigned char * const *units,
|
||||||
unsigned int unit_count, bool binary_scale);
|
unsigned int unit_count, bool binary_scale);
|
||||||
/* unicode stuff */
|
/* unicode stuff */
|
||||||
|
|
|
@ -172,6 +172,8 @@ bool disk_init(IF_MD_NONVOID(int drive))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX backup GPT header at final LBA of drive...
|
||||||
|
|
||||||
while (is_gpt) {
|
while (is_gpt) {
|
||||||
/* Re-start partition parsing using GPT */
|
/* Re-start partition parsing using GPT */
|
||||||
uint64_t part_lba;
|
uint64_t part_lba;
|
||||||
|
@ -243,20 +245,24 @@ reload:
|
||||||
goto skip; /* Any flag makes us ignore this */
|
goto skip; /* Any flag makes us ignore this */
|
||||||
}
|
}
|
||||||
tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
|
tmp = BYTES2INT64(pptr, 32); /* FIRST LBA */
|
||||||
if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
|
#ifndef STORAGE_64BIT_SECTOR
|
||||||
DEBUGF("GPT: partition starts after 2GiB mark\n");
|
if (tmp > UINT32_MAX) {
|
||||||
|
DEBUGF("GPT: partition starts after 2TiB mark\n");
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (tmp < 34) {
|
if (tmp < 34) {
|
||||||
DEBUGF("GPT: Invalid start LBA\n");
|
DEBUGF("GPT: Invalid start LBA\n");
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
pinfo[part].start = tmp;
|
pinfo[part].start = tmp;
|
||||||
tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
|
tmp = BYTES2INT64(pptr, 40); /* LAST LBA */
|
||||||
if (tmp > UINT32_MAX) { // XXX revisit when we resize struct partinfo!
|
#ifndef STORAGE_64BIT_SECTOR
|
||||||
DEBUGF("GPT: partition ends after 2GiB mark\n");
|
if (tmp > UINT32_MAX) {
|
||||||
|
DEBUGF("GPT: partition ends after 2TiB mark\n");
|
||||||
goto skip;
|
goto skip;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (tmp <= pinfo[part].start) {
|
if (tmp <= pinfo[part].start) {
|
||||||
DEBUGF("GPT: Invalid end LBA\n");
|
DEBUGF("GPT: Invalid end LBA\n");
|
||||||
goto skip;
|
goto skip;
|
||||||
|
@ -264,7 +270,7 @@ reload:
|
||||||
pinfo[part].size = tmp - pinfo[part].start + 1;
|
pinfo[part].size = tmp - pinfo[part].start + 1;
|
||||||
pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
|
pinfo[part].type = PARTITION_TYPE_FAT32_LBA;
|
||||||
|
|
||||||
DEBUGF("GPart%d: start: %08lx size: %08lx\n",
|
DEBUGF("GPart%d: start: %016lx size: %016lx\n",
|
||||||
part,pinfo[part].start,pinfo[part].size);
|
part,pinfo[part].start,pinfo[part].size);
|
||||||
part++;
|
part++;
|
||||||
|
|
||||||
|
@ -499,13 +505,13 @@ unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume))
|
||||||
return clustersize;
|
return clustersize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep)
|
void volume_size(IF_MV(int volume,) sector_t *sizep, sector_t *freep)
|
||||||
{
|
{
|
||||||
disk_reader_lock();
|
disk_reader_lock();
|
||||||
|
|
||||||
if (!CHECK_VOL(volume) || !fat_size(IF_MV(volume,) sizep, freep))
|
if (!CHECK_VOL(volume) || !fat_size(IF_MV(volume,) sizep, freep))
|
||||||
{
|
{
|
||||||
if (freep) *sizep = 0;
|
if (sizep) *sizep = 0;
|
||||||
if (freep) *freep = 0;
|
if (freep) *freep = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,12 +71,12 @@ struct disk_cache_entry
|
||||||
#ifdef HAVE_MULTIVOLUME
|
#ifdef HAVE_MULTIVOLUME
|
||||||
unsigned char volume; /* volume of sector */
|
unsigned char volume; /* volume of sector */
|
||||||
#endif
|
#endif
|
||||||
unsigned long sector; /* cached disk sector number */
|
sector_t sector; /* cached disk sector number */
|
||||||
};
|
};
|
||||||
|
|
||||||
BITARRAY_TYPE_DECLARE(cache_map_entry_t, cache_map, DC_NUM_ENTRIES)
|
BITARRAY_TYPE_DECLARE(cache_map_entry_t, cache_map, DC_NUM_ENTRIES)
|
||||||
|
|
||||||
static inline unsigned int map_sector(unsigned long sector)
|
static inline unsigned int map_sector(sector_t sector)
|
||||||
{
|
{
|
||||||
/* keep sector hash simple for now */
|
/* keep sector hash simple for now */
|
||||||
return sector % DC_MAP_NUM_ENTRIES;
|
return sector % DC_MAP_NUM_ENTRIES;
|
||||||
|
@ -172,7 +172,7 @@ static inline void cache_discard_entry(struct disk_cache_entry *dce,
|
||||||
/* search the cache for the specified sector, returning a buffer, either
|
/* search the cache for the specified sector, returning a buffer, either
|
||||||
to the specified sector, if it exists, or a new/evicted entry that must
|
to the specified sector, if it exists, or a new/evicted entry that must
|
||||||
be filled */
|
be filled */
|
||||||
void * dc_cache_probe(IF_MV(int volume,) unsigned long sector,
|
void * dc_cache_probe(IF_MV(int volume,) sector_t sector,
|
||||||
unsigned int *flagsp)
|
unsigned int *flagsp)
|
||||||
{
|
{
|
||||||
unsigned int mapnum = map_sector(sector);
|
unsigned int mapnum = map_sector(sector);
|
||||||
|
@ -200,7 +200,7 @@ void * dc_cache_probe(IF_MV(int volume,) unsigned long sector,
|
||||||
if (old_flags)
|
if (old_flags)
|
||||||
{
|
{
|
||||||
int old_volume = IF_MV_VOL(dce->volume);
|
int old_volume = IF_MV_VOL(dce->volume);
|
||||||
unsigned long sector = dce->sector;
|
sector_t sector = dce->sector;
|
||||||
unsigned int old_mapnum = map_sector(sector);
|
unsigned int old_mapnum = map_sector(sector);
|
||||||
|
|
||||||
if (old_flags & DCE_DIRTY)
|
if (old_flags & DCE_DIRTY)
|
||||||
|
|
|
@ -109,15 +109,15 @@ static long last_disk_activity = -1;
|
||||||
static long power_off_tick = 0;
|
static long power_off_tick = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static unsigned long total_sectors;
|
static sector_t total_sectors;
|
||||||
static int multisectors; /* number of supported multisectors */
|
static int multisectors; /* number of supported multisectors */
|
||||||
static unsigned short identify_info[SECTOR_SIZE/2];
|
static unsigned short identify_info[ATA_IDENTIFY_WORDS];
|
||||||
|
|
||||||
#ifdef MAX_PHYS_SECTOR_SIZE
|
#ifdef MAX_PHYS_SECTOR_SIZE
|
||||||
|
|
||||||
struct sector_cache_entry {
|
struct sector_cache_entry {
|
||||||
bool inuse;
|
bool inuse;
|
||||||
unsigned long sectornum; /* logical sector */
|
sector_t sectornum; /* logical sector */
|
||||||
unsigned char data[MAX_PHYS_SECTOR_SIZE];
|
unsigned char data[MAX_PHYS_SECTOR_SIZE];
|
||||||
};
|
};
|
||||||
/* buffer for reading and writing large physical sectors */
|
/* buffer for reading and writing large physical sectors */
|
||||||
|
@ -381,7 +381,7 @@ static ICODE_ATTR void copy_write_sectors(const unsigned char* buf,
|
||||||
}
|
}
|
||||||
#endif /* !ATA_OPTIMIZED_WRITING */
|
#endif /* !ATA_OPTIMIZED_WRITING */
|
||||||
|
|
||||||
static int ata_transfer_sectors(unsigned long start,
|
static int ata_transfer_sectors(uint64_t start,
|
||||||
int incount,
|
int incount,
|
||||||
void* inbuf,
|
void* inbuf,
|
||||||
int write)
|
int write)
|
||||||
|
@ -443,9 +443,9 @@ static int ata_transfer_sectors(unsigned long start,
|
||||||
ATA_OUT8(ATA_NSECTOR, count & 0xff);
|
ATA_OUT8(ATA_NSECTOR, count & 0xff);
|
||||||
ATA_OUT8(ATA_SECTOR, (start >> 24) & 0xff); /* 31:24 */
|
ATA_OUT8(ATA_SECTOR, (start >> 24) & 0xff); /* 31:24 */
|
||||||
ATA_OUT8(ATA_SECTOR, start & 0xff); /* 7:0 */
|
ATA_OUT8(ATA_SECTOR, start & 0xff); /* 7:0 */
|
||||||
ATA_OUT8(ATA_LCYL, 0); /* 39:32 */
|
ATA_OUT8(ATA_LCYL, (start >> 32) & 0xff); /* 39:32 */
|
||||||
ATA_OUT8(ATA_LCYL, (start >> 8) & 0xff); /* 15:8 */
|
ATA_OUT8(ATA_LCYL, (start >> 8) & 0xff); /* 15:8 */
|
||||||
ATA_OUT8(ATA_HCYL, 0); /* 47:40 */
|
ATA_OUT8(ATA_HCYL, (start >> 40) & 0xff); /* 47:40 */
|
||||||
ATA_OUT8(ATA_HCYL, (start >> 16) & 0xff); /* 23:16 */
|
ATA_OUT8(ATA_HCYL, (start >> 16) & 0xff); /* 23:16 */
|
||||||
ATA_OUT8(ATA_SELECT, SELECT_LBA | ata_device);
|
ATA_OUT8(ATA_SELECT, SELECT_LBA | ata_device);
|
||||||
#ifdef HAVE_ATA_DMA
|
#ifdef HAVE_ATA_DMA
|
||||||
|
@ -592,7 +592,7 @@ static int ata_transfer_sectors(unsigned long start,
|
||||||
|
|
||||||
#ifndef MAX_PHYS_SECTOR_SIZE
|
#ifndef MAX_PHYS_SECTOR_SIZE
|
||||||
int ata_read_sectors(IF_MD(int drive,)
|
int ata_read_sectors(IF_MD(int drive,)
|
||||||
unsigned long start,
|
sector_t start,
|
||||||
int incount,
|
int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
|
@ -607,7 +607,7 @@ int ata_read_sectors(IF_MD(int drive,)
|
||||||
}
|
}
|
||||||
|
|
||||||
int ata_write_sectors(IF_MD(int drive,)
|
int ata_write_sectors(IF_MD(int drive,)
|
||||||
unsigned long start,
|
sector_t start,
|
||||||
int count,
|
int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
|
@ -623,7 +623,7 @@ int ata_write_sectors(IF_MD(int drive,)
|
||||||
#endif /* ndef MAX_PHYS_SECTOR_SIZE */
|
#endif /* ndef MAX_PHYS_SECTOR_SIZE */
|
||||||
|
|
||||||
#ifdef MAX_PHYS_SECTOR_SIZE
|
#ifdef MAX_PHYS_SECTOR_SIZE
|
||||||
static int cache_sector(unsigned long sector)
|
static int cache_sector(sector_t sector)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
@ -652,7 +652,7 @@ static inline int flush_current_sector(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
int ata_read_sectors(IF_MD(int drive,)
|
int ata_read_sectors(IF_MD(int drive,)
|
||||||
unsigned long start,
|
sector_t start,
|
||||||
int incount,
|
int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
|
@ -718,7 +718,7 @@ int ata_read_sectors(IF_MD(int drive,)
|
||||||
}
|
}
|
||||||
|
|
||||||
int ata_write_sectors(IF_MD(int drive,)
|
int ata_write_sectors(IF_MD(int drive,)
|
||||||
unsigned long start,
|
sector_t start,
|
||||||
int count,
|
int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
|
@ -916,7 +916,7 @@ static int identify(void)
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i=0; i<SECTOR_SIZE/2; i++) {
|
for (i=0; i<ATA_IDENTIFY_WORDS; i++) {
|
||||||
/* the IDENTIFY words are already swapped, so we need to treat
|
/* the IDENTIFY words are already swapped, so we need to treat
|
||||||
this info differently that normal sector data */
|
this info differently that normal sector data */
|
||||||
identify_info[i] = ATA_SWAP_IDENTIFY(ATA_IN16(ATA_DATA));
|
identify_info[i] = ATA_SWAP_IDENTIFY(ATA_IN16(ATA_DATA));
|
||||||
|
@ -1269,10 +1269,8 @@ int STORAGE_INIT_ATTR ata_init(void)
|
||||||
if (identify_info[83] & 0x0400 /* 48 bit address support */
|
if (identify_info[83] & 0x0400 /* 48 bit address support */
|
||||||
&& total_sectors == 0x0FFFFFFF) /* and disk size >= 128 GiB */
|
&& total_sectors == 0x0FFFFFFF) /* and disk size >= 128 GiB */
|
||||||
{ /* (needs BigLBA addressing) */
|
{ /* (needs BigLBA addressing) */
|
||||||
if (identify_info[102] || identify_info[103])
|
total_sectors = identify_info[100] | (identify_info[101] << 16) | ((uint64_t)identify_info[102] << 32) | ((uint64_t)identify_info[103] << 48);
|
||||||
panicf("Unsupported disk size: >= 2^32 sectors");
|
|
||||||
|
|
||||||
total_sectors = identify_info[100] | (identify_info[101] << 16);
|
|
||||||
lba48 = true; /* use BigLBA */
|
lba48 = true; /* use BigLBA */
|
||||||
}
|
}
|
||||||
#endif /* HAVE_LBA48 */
|
#endif /* HAVE_LBA48 */
|
||||||
|
@ -1360,7 +1358,13 @@ void ata_get_info(IF_MD(int drive,)struct storage_info *info)
|
||||||
(void)drive; /* unused for now */
|
(void)drive; /* unused for now */
|
||||||
#endif
|
#endif
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
/* Logical sector size */
|
||||||
|
if ((identify_info[106] & 0xd000) == 0x5000)
|
||||||
|
info->sector_size = identify_info[117] | (identify_info[118] << 16);
|
||||||
|
else
|
||||||
info->sector_size = SECTOR_SIZE;
|
info->sector_size = SECTOR_SIZE;
|
||||||
|
|
||||||
info->num_sectors = total_sectors;
|
info->num_sectors = total_sectors;
|
||||||
|
|
||||||
src = (unsigned short*)&identify_info[27];
|
src = (unsigned short*)&identify_info[27];
|
||||||
|
|
|
@ -173,8 +173,8 @@ union raw_dirent
|
||||||
|
|
||||||
struct fsinfo
|
struct fsinfo
|
||||||
{
|
{
|
||||||
unsigned long freecount; /* last known free cluster count */
|
sector_t freecount; /* last known free cluster count */
|
||||||
unsigned long nextfree; /* first cluster to start looking for free
|
sector_t nextfree; /* first cluster to start looking for free
|
||||||
clusters, or 0xffffffff for no hint */
|
clusters, or 0xffffffff for no hint */
|
||||||
};
|
};
|
||||||
/* fsinfo offsets */
|
/* fsinfo offsets */
|
||||||
|
@ -233,7 +233,7 @@ static struct bpb
|
||||||
unsigned long totalsectors;
|
unsigned long totalsectors;
|
||||||
unsigned long rootdirsector;
|
unsigned long rootdirsector;
|
||||||
unsigned long firstdatasector;
|
unsigned long firstdatasector;
|
||||||
unsigned long startsector;
|
sector_t startsector;
|
||||||
unsigned long dataclusters;
|
unsigned long dataclusters;
|
||||||
unsigned long fatrgnstart;
|
unsigned long fatrgnstart;
|
||||||
unsigned long fatrgnend;
|
unsigned long fatrgnend;
|
||||||
|
@ -241,7 +241,7 @@ static struct bpb
|
||||||
#ifdef HAVE_FAT16SUPPORT
|
#ifdef HAVE_FAT16SUPPORT
|
||||||
unsigned int bpb_rootentcnt; /* Number of dir entries in the root */
|
unsigned int bpb_rootentcnt; /* Number of dir entries in the root */
|
||||||
/* internals for FAT16 support */
|
/* internals for FAT16 support */
|
||||||
unsigned long rootdirsectornum; /* sector offset of root dir relative to start
|
sector_t rootdirsectornum; /* sector offset of root dir relative to start
|
||||||
* of first pseudo cluster */
|
* of first pseudo cluster */
|
||||||
#endif /* HAVE_FAT16SUPPORT */
|
#endif /* HAVE_FAT16SUPPORT */
|
||||||
|
|
||||||
|
@ -329,7 +329,7 @@ static void cache_discard(IF_MV_NONVOID(struct bpb *fat_bpb))
|
||||||
}
|
}
|
||||||
|
|
||||||
/* caches a FAT or data area sector */
|
/* caches a FAT or data area sector */
|
||||||
static void * cache_sector(struct bpb *fat_bpb, unsigned long secnum)
|
static void * cache_sector(struct bpb *fat_bpb, sector_t secnum)
|
||||||
{
|
{
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
void *buf = dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags);
|
void *buf = dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags);
|
||||||
|
@ -340,8 +340,8 @@ static void * cache_sector(struct bpb *fat_bpb, unsigned long secnum)
|
||||||
secnum + fat_bpb->startsector, 1, buf);
|
secnum + fat_bpb->startsector, 1, buf);
|
||||||
if (UNLIKELY(rc < 0))
|
if (UNLIKELY(rc < 0))
|
||||||
{
|
{
|
||||||
DEBUGF("%s() - Could not read sector %ld"
|
DEBUGF("%s() - Could not read sector %llu"
|
||||||
" (error %d)\n", __func__, secnum, rc);
|
" (error %d)\n", __func__, (uint64_t)secnum, rc);
|
||||||
dc_discard_buf(buf);
|
dc_discard_buf(buf);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -354,14 +354,14 @@ static void * cache_sector(struct bpb *fat_bpb, unsigned long secnum)
|
||||||
* contents are NOT loaded before returning - use when completely overwriting
|
* contents are NOT loaded before returning - use when completely overwriting
|
||||||
* a sector's contents in order to avoid a fill */
|
* a sector's contents in order to avoid a fill */
|
||||||
static void * cache_sector_buffer(IF_MV(struct bpb *fat_bpb,)
|
static void * cache_sector_buffer(IF_MV(struct bpb *fat_bpb,)
|
||||||
unsigned long secnum)
|
sector_t secnum)
|
||||||
{
|
{
|
||||||
unsigned int flags;
|
unsigned int flags;
|
||||||
return dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags);
|
return dc_cache_probe(IF_MV(fat_bpb->volume,) secnum, &flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* flush a cache buffer to storage */
|
/* flush a cache buffer to storage */
|
||||||
void dc_writeback_callback(IF_MV(int volume,) unsigned long sector, void *buf)
|
void dc_writeback_callback(IF_MV(int volume,) sector_t sector, void *buf)
|
||||||
{
|
{
|
||||||
struct bpb * const fat_bpb = &fat_bpbs[IF_MV_VOL(volume)];
|
struct bpb * const fat_bpb = &fat_bpbs[IF_MV_VOL(volume)];
|
||||||
unsigned int copies = !IS_FAT_SECTOR(fat_bpb, sector) ?
|
unsigned int copies = !IS_FAT_SECTOR(fat_bpb, sector) ?
|
||||||
|
@ -374,8 +374,8 @@ void dc_writeback_callback(IF_MV(int volume,) unsigned long sector, void *buf)
|
||||||
int rc = storage_write_sectors(IF_MD(fat_bpb->drive,) sector, 1, buf);
|
int rc = storage_write_sectors(IF_MD(fat_bpb->drive,) sector, 1, buf);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
panicf("%s() - Could not write sector %ld"
|
panicf("%s() - Could not write sector %llu"
|
||||||
" (error %d)\n", __func__, sector, rc);
|
" (error %d)\n", __func__, (uint64_t)sector, rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (--copies == 0)
|
if (--copies == 0)
|
||||||
|
@ -2397,12 +2397,12 @@ unsigned long fat_query_sectornum(const struct fat_filestr *filestr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* helper for fat_readwrite */
|
/* helper for fat_readwrite */
|
||||||
static long transfer(struct bpb *fat_bpb, unsigned long start, long count,
|
static long transfer(struct bpb *fat_bpb, sector_t start, long count,
|
||||||
char *buf, bool write)
|
char *buf, bool write)
|
||||||
{
|
{
|
||||||
long rc = 0;
|
long rc = 0;
|
||||||
|
|
||||||
DEBUGF("%s(s=%lx, c=%lx, wr=%u)\n", __func__,
|
DEBUGF("%s(s=%llx, c=%lx, wr=%u)\n", __func__,
|
||||||
start + fat_bpb->startsector, count, write ? 1 : 0);
|
start + fat_bpb->startsector, count, write ? 1 : 0);
|
||||||
|
|
||||||
if (write)
|
if (write)
|
||||||
|
@ -2416,12 +2416,12 @@ static long transfer(struct bpb *fat_bpb, unsigned long start, long count,
|
||||||
firstallowed = fat_bpb->firstdatasector;
|
firstallowed = fat_bpb->firstdatasector;
|
||||||
|
|
||||||
if (start < firstallowed)
|
if (start < firstallowed)
|
||||||
panicf("Write %ld before data\n", firstallowed - start);
|
panicf("Write %llu before data\n", (uint64_t)(firstallowed - start));
|
||||||
|
|
||||||
if (start + count > fat_bpb->totalsectors)
|
if (start + count > fat_bpb->totalsectors)
|
||||||
{
|
{
|
||||||
panicf("Write %ld after data\n",
|
panicf("Write %llu after data\n",
|
||||||
start + count - fat_bpb->totalsectors);
|
(uint64_t)(start + count - fat_bpb->totalsectors));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2487,14 +2487,14 @@ long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount,
|
||||||
long rc;
|
long rc;
|
||||||
|
|
||||||
long cluster = filestr->lastcluster;
|
long cluster = filestr->lastcluster;
|
||||||
unsigned long sector = filestr->lastsector;
|
sector_t sector = filestr->lastsector;
|
||||||
long clusternum = filestr->clusternum;
|
long clusternum = filestr->clusternum;
|
||||||
unsigned long sectornum = filestr->sectornum;
|
unsigned long sectornum = filestr->sectornum;
|
||||||
|
|
||||||
DEBUGF("%s(file:%lx,count:0x%lx,buf:%lx,%s)\n", __func__,
|
DEBUGF("%s(file:%lx,count:0x%lx,buf:%lx,%s)\n", __func__,
|
||||||
file->firstcluster, sectorcount, (long)buf,
|
file->firstcluster, sectorcount, (long)buf,
|
||||||
write ? "write":"read");
|
write ? "write":"read");
|
||||||
DEBUGF("%s: sec:%lx numsec:%ld eof:%d\n", __func__,
|
DEBUGF("%s: sec:%llx numsec:%ld eof:%d\n", __func__,
|
||||||
sector, (long)sectornum, eof ? 1 : 0);
|
sector, (long)sectornum, eof ? 1 : 0);
|
||||||
|
|
||||||
eof = false;
|
eof = false;
|
||||||
|
@ -2534,7 +2534,7 @@ long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount,
|
||||||
|
|
||||||
unsigned long transferred = 0;
|
unsigned long transferred = 0;
|
||||||
unsigned long count = 0;
|
unsigned long count = 0;
|
||||||
unsigned long last = sector;
|
sector_t last = sector;
|
||||||
|
|
||||||
while (transferred + count < sectorcount)
|
while (transferred + count < sectorcount)
|
||||||
{
|
{
|
||||||
|
@ -2961,7 +2961,7 @@ void fat_recalc_free(IF_MV_NONVOID(int volume))
|
||||||
dc_unlock_cache();
|
dc_unlock_cache();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free)
|
bool fat_size(IF_MV(int volume,) sector_t *size, sector_t *free)
|
||||||
{
|
{
|
||||||
struct bpb * const fat_bpb = FAT_BPB(volume);
|
struct bpb * const fat_bpb = FAT_BPB(volume);
|
||||||
if (!fat_bpb)
|
if (!fat_bpb)
|
||||||
|
|
|
@ -32,7 +32,7 @@ static unsigned char ramdisk[SECTOR_SIZE * NUM_SECTORS];
|
||||||
static long last_disk_activity = -1;
|
static long last_disk_activity = -1;
|
||||||
|
|
||||||
int ramdisk_read_sectors(IF_MD(int drive,)
|
int ramdisk_read_sectors(IF_MD(int drive,)
|
||||||
unsigned long start,
|
sector_t start,
|
||||||
int count,
|
int count,
|
||||||
void* buf)
|
void* buf)
|
||||||
{
|
{
|
||||||
|
@ -48,7 +48,7 @@ int ramdisk_read_sectors(IF_MD(int drive,)
|
||||||
}
|
}
|
||||||
|
|
||||||
int ramdisk_write_sectors(IF_MD(int drive,)
|
int ramdisk_write_sectors(IF_MD(int drive,)
|
||||||
unsigned long start,
|
sector_t start,
|
||||||
int count,
|
int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
|
|
|
@ -49,6 +49,13 @@ void sd_parse_csd(tCardInfo *card)
|
||||||
c_size = card_extract_bits(card->csd, 69, 22) + 1;
|
c_size = card_extract_bits(card->csd, 69, 22) + 1;
|
||||||
card->numblocks = c_size << 10;
|
card->numblocks = c_size << 10;
|
||||||
}
|
}
|
||||||
|
else if(csd_version == 2)
|
||||||
|
{
|
||||||
|
/* CSD version 3.0 */
|
||||||
|
c_size = card_extract_bits(card->csd, 75, 28) + 1;
|
||||||
|
card->numblocks = c_size << 10;
|
||||||
|
}
|
||||||
|
card->sd2plus = csd_version >= 1;
|
||||||
|
|
||||||
card->blocksize = 512; /* Always use 512 byte blocks */
|
card->blocksize = 512; /* Always use 512 byte blocks */
|
||||||
|
|
||||||
|
@ -62,7 +69,9 @@ void sd_parse_csd(tCardInfo *card)
|
||||||
|
|
||||||
card->r2w_factor = card_extract_bits(card->csd, 28, 3);
|
card->r2w_factor = card_extract_bits(card->csd, 28, 3);
|
||||||
|
|
||||||
logf("CSD%d.0 numblocks:%ld speed:%ld", csd_version+1, card->numblocks, card->speed);
|
|
||||||
|
|
||||||
|
logf("CSD%d.0 numblocks:%lld speed:%ld", csd_version+1, card->numblocks, card->speed);
|
||||||
logf("nsac: %d taac: %ld r2w: %d", card->nsac, card->taac, card->r2w_factor);
|
logf("nsac: %d taac: %ld r2w: %d", card->nsac, card->taac, card->r2w_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,4 +108,3 @@ void sd_get_info(IF_MD(int drive,) struct storage_info *info)
|
||||||
info->revision="0.00";
|
info->revision="0.00";
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -140,8 +140,8 @@ bool ata_disk_is_active(void);
|
||||||
int ata_soft_reset(void);
|
int ata_soft_reset(void);
|
||||||
int ata_init(void) STORAGE_INIT_ATTR;
|
int ata_init(void) STORAGE_INIT_ATTR;
|
||||||
void ata_close(void);
|
void ata_close(void);
|
||||||
int ata_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
int ata_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
int ata_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
int ata_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
void ata_spin(void);
|
void ata_spin(void);
|
||||||
#if (CONFIG_LED == LED_REAL)
|
#if (CONFIG_LED == LED_REAL)
|
||||||
void ata_set_led_enabled(bool enabled);
|
void ata_set_led_enabled(bool enabled);
|
||||||
|
@ -233,4 +233,6 @@ int ata_read_smart(struct ata_smart_values*);
|
||||||
#define STORAGE_CLOSE
|
#define STORAGE_CLOSE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ATA_IDENTIFY_WORDS 256
|
||||||
|
|
||||||
#endif /* __ATA_H__ */
|
#endif /* __ATA_H__ */
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
|
|
||||||
struct partinfo
|
struct partinfo
|
||||||
{
|
{
|
||||||
unsigned long start; /* first sector (LBA) */
|
sector_t start; /* first sector (LBA) */
|
||||||
unsigned long size; /* number of sectors */
|
sector_t size; /* number of sectors */
|
||||||
unsigned char type;
|
unsigned char type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,7 @@ struct fat_filestr
|
||||||
{
|
{
|
||||||
struct fat_file *fatfilep; /* common file information */
|
struct fat_file *fatfilep; /* common file information */
|
||||||
long lastcluster; /* cluster of last access */
|
long lastcluster; /* cluster of last access */
|
||||||
unsigned long lastsector; /* sector of last access */
|
sector_t lastsector; /* sector of last access */
|
||||||
long clusternum; /* cluster number of last access */
|
long clusternum; /* cluster number of last access */
|
||||||
unsigned long sectornum; /* sector number within current cluster */
|
unsigned long sectornum; /* sector number within current cluster */
|
||||||
bool eof; /* end-of-file reached */
|
bool eof; /* end-of-file reached */
|
||||||
|
@ -173,7 +173,7 @@ int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume));
|
||||||
#endif /* MAX_LOG_SECTOR_SIZE */
|
#endif /* MAX_LOG_SECTOR_SIZE */
|
||||||
unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume));
|
unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume));
|
||||||
void fat_recalc_free(IF_MV_NONVOID(int volume));
|
void fat_recalc_free(IF_MV_NONVOID(int volume));
|
||||||
bool fat_size(IF_MV(int volume,) unsigned long *size, unsigned long *free);
|
bool fat_size(IF_MV(int volume,) sector_t *size, sector_t *free);
|
||||||
|
|
||||||
/** Misc. **/
|
/** Misc. **/
|
||||||
void fat_empty_fat_direntry(struct fat_direntry *entry);
|
void fat_empty_fat_direntry(struct fat_direntry *entry);
|
||||||
|
|
|
@ -36,8 +36,8 @@ bool mmc_disk_is_active(void);
|
||||||
int mmc_soft_reset(void);
|
int mmc_soft_reset(void);
|
||||||
int mmc_init(void) STORAGE_INIT_ATTR;
|
int mmc_init(void) STORAGE_INIT_ATTR;
|
||||||
void mmc_close(void);
|
void mmc_close(void);
|
||||||
int mmc_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
int mmc_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
int mmc_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
int mmc_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
void mmc_spin(void);
|
void mmc_spin(void);
|
||||||
int mmc_spinup_time(void);
|
int mmc_spinup_time(void);
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#define __MV_H__
|
#define __MV_H__
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
/* FixMe: These macros are a bit nasty and perhaps misplaced here.
|
/* FixMe: These macros are a bit nasty and perhaps misplaced here.
|
||||||
|
@ -40,6 +41,19 @@
|
||||||
#define IF_MD_DRV(d) 0
|
#define IF_MD_DRV(d) 0
|
||||||
#endif /* HAVE_MULTIDRIVE */
|
#endif /* HAVE_MULTIDRIVE */
|
||||||
|
|
||||||
|
/* Storage size */
|
||||||
|
#if (CONFIG_STORAGE & STORAGE_ATA) && defined(HAVE_LBA48)
|
||||||
|
typedef uint64_t sector_t;
|
||||||
|
#define STORAGE_64BIT_SECTOR
|
||||||
|
#elif (CONFIG_STORAGE & STORAGE_SD) && defined(HAVE_SDUC)
|
||||||
|
typedef uint64_t sector_t;
|
||||||
|
#define STORAGE_64BIT_SECTOR
|
||||||
|
#else
|
||||||
|
typedef unsigned long sector_t;
|
||||||
|
#undef STORAGE_64BIT_SECTOR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Volumes mean things that have filesystems on them, like partitions */
|
/* Volumes mean things that have filesystems on them, like partitions */
|
||||||
#ifdef HAVE_MULTIVOLUME
|
#ifdef HAVE_MULTIVOLUME
|
||||||
#define IF_MV(x...) x /* valist contents or empty */
|
#define IF_MV(x...) x /* valist contents or empty */
|
||||||
|
@ -113,7 +127,7 @@ struct volumeinfo
|
||||||
/* Volume-centric functions (in disk.c) */
|
/* Volume-centric functions (in disk.c) */
|
||||||
void volume_recalc_free(IF_MV_NONVOID(int volume));
|
void volume_recalc_free(IF_MV_NONVOID(int volume));
|
||||||
unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume));
|
unsigned int volume_get_cluster_size(IF_MV_NONVOID(int volume));
|
||||||
void volume_size(IF_MV(int volume,) unsigned long *size, unsigned long *free);
|
void volume_size(IF_MV(int volume,) sector_t *size, sector_t *free);
|
||||||
#ifdef HAVE_DIRCACHE
|
#ifdef HAVE_DIRCACHE
|
||||||
bool volume_ismounted(IF_MV_NONVOID(int volume));
|
bool volume_ismounted(IF_MV_NONVOID(int volume));
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,8 +36,8 @@ bool nand_disk_is_active(void);
|
||||||
int nand_soft_reset(void);
|
int nand_soft_reset(void);
|
||||||
int nand_init(void) STORAGE_INIT_ATTR;
|
int nand_init(void) STORAGE_INIT_ATTR;
|
||||||
void nand_close(void);
|
void nand_close(void);
|
||||||
int nand_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
int nand_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
int nand_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
#ifdef HAVE_STORAGE_FLUSH
|
#ifdef HAVE_STORAGE_FLUSH
|
||||||
int nand_flush(void);
|
int nand_flush(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -35,8 +35,8 @@ bool ramdisk_disk_is_active(void);
|
||||||
int ramdisk_soft_reset(void);
|
int ramdisk_soft_reset(void);
|
||||||
int ramdisk_init(void) STORAGE_INIT_ATTR;
|
int ramdisk_init(void) STORAGE_INIT_ATTR;
|
||||||
void ramdisk_close(void);
|
void ramdisk_close(void);
|
||||||
int ramdisk_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
int ramdisk_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
int ramdisk_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
int ramdisk_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
void ramdisk_spin(void);
|
void ramdisk_spin(void);
|
||||||
void ramdisk_sleepnow(void);
|
void ramdisk_sleepnow(void);
|
||||||
int ramdisk_spinup_time(void);
|
int ramdisk_spinup_time(void);
|
||||||
|
|
|
@ -42,8 +42,8 @@ bool sd_disk_is_active(void);
|
||||||
int sd_soft_reset(void);
|
int sd_soft_reset(void);
|
||||||
int sd_init(void) STORAGE_INIT_ATTR;
|
int sd_init(void) STORAGE_INIT_ATTR;
|
||||||
void sd_close(void);
|
void sd_close(void);
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
int sd_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
void sd_spin(void);
|
void sd_spin(void);
|
||||||
int sd_spinup_time(void); /* ticks */
|
int sd_spinup_time(void); /* ticks */
|
||||||
|
|
||||||
|
@ -93,6 +93,7 @@ int sd_num_drives(int first_drive);
|
||||||
#define SD_READ_SINGLE_BLOCK 17
|
#define SD_READ_SINGLE_BLOCK 17
|
||||||
#define SD_READ_MULTIPLE_BLOCK 18
|
#define SD_READ_MULTIPLE_BLOCK 18
|
||||||
#define SD_SEND_NUM_WR_BLOCKS 22 /* acmd22 */
|
#define SD_SEND_NUM_WR_BLOCKS 22 /* acmd22 */
|
||||||
|
#define SD_UC_ADDRESS_EXTENSION 22
|
||||||
#define SD_SET_WR_BLK_ERASE_COUNT 23 /* acmd23 */
|
#define SD_SET_WR_BLK_ERASE_COUNT 23 /* acmd23 */
|
||||||
#define SD_WRITE_BLOCK 24
|
#define SD_WRITE_BLOCK 24
|
||||||
#define SD_WRITE_MULTIPLE_BLOCK 25
|
#define SD_WRITE_MULTIPLE_BLOCK 25
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#define __SDMMC_H__
|
#define __SDMMC_H__
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <mv.h> /* for sector_t */
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -37,7 +39,7 @@ typedef struct
|
||||||
unsigned int nsac; /* clock cycles */
|
unsigned int nsac; /* clock cycles */
|
||||||
unsigned long taac; /* n * 0.1 ns */
|
unsigned long taac; /* n * 0.1 ns */
|
||||||
unsigned int r2w_factor;
|
unsigned int r2w_factor;
|
||||||
unsigned long numblocks; /* size in flash blocks */
|
sector_t numblocks; /* size in flash blocks */
|
||||||
unsigned int blocksize; /* block size in bytes */
|
unsigned int blocksize; /* block size in bytes */
|
||||||
unsigned long rca; /* RCA register */
|
unsigned long rca; /* RCA register */
|
||||||
|
|
||||||
|
@ -48,6 +50,8 @@ typedef struct
|
||||||
#if (CONFIG_STORAGE & STORAGE_SD)
|
#if (CONFIG_STORAGE & STORAGE_SD)
|
||||||
unsigned int current_bank;
|
unsigned int current_bank;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
unsigned int sd2plus; /* SD 2.0 or better */
|
||||||
} tCardInfo;
|
} tCardInfo;
|
||||||
|
|
||||||
#if (CONFIG_STORAGE & STORAGE_SD)
|
#if (CONFIG_STORAGE & STORAGE_SD)
|
||||||
|
|
|
@ -107,7 +107,7 @@ int ramdisk_event(long id, intptr_t data);
|
||||||
struct storage_info
|
struct storage_info
|
||||||
{
|
{
|
||||||
unsigned int sector_size;
|
unsigned int sector_size;
|
||||||
unsigned int num_sectors;
|
sector_t num_sectors;
|
||||||
char *vendor;
|
char *vendor;
|
||||||
char *product;
|
char *product;
|
||||||
char *revision;
|
char *revision;
|
||||||
|
@ -318,6 +318,6 @@ int storage_driver_type(int drive);
|
||||||
|
|
||||||
#endif /* NOT CONFIG_STORAGE_MULTI and NOT SIMULATOR*/
|
#endif /* NOT CONFIG_STORAGE_MULTI and NOT SIMULATOR*/
|
||||||
|
|
||||||
int storage_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
int storage_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
int storage_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
int storage_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,7 +36,7 @@ static inline void dc_unlock_cache(void)
|
||||||
mutex_unlock(&disk_cache_mutex);
|
mutex_unlock(&disk_cache_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void * dc_cache_probe(IF_MV(int volume,) unsigned long secnum,
|
void * dc_cache_probe(IF_MV(int volume,) sector_t secnum,
|
||||||
unsigned int *flags);
|
unsigned int *flags);
|
||||||
void dc_dirty_buf(void *buf);
|
void dc_dirty_buf(void *buf);
|
||||||
void dc_discard_buf(void *buf);
|
void dc_discard_buf(void *buf);
|
||||||
|
@ -46,7 +46,7 @@ void dc_discard_all(IF_MV_NONVOID(int volume));
|
||||||
void dc_init(void) INIT_ATTR;
|
void dc_init(void) INIT_ATTR;
|
||||||
|
|
||||||
/* in addition to filling, writeback is implemented by the client */
|
/* in addition to filling, writeback is implemented by the client */
|
||||||
extern void dc_writeback_callback(IF_MV(int volume, ) unsigned long sector,
|
extern void dc_writeback_callback(IF_MV(int volume, ) sector_t sector,
|
||||||
void *buf);
|
void *buf);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -343,7 +343,7 @@ int storage_init(void)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int storage_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int storage_read_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
void* buf)
|
void* buf)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_STORAGE_MULTI
|
#ifdef CONFIG_STORAGE_MULTI
|
||||||
|
@ -385,7 +385,7 @@ int storage_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int storage_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int storage_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_STORAGE_MULTI
|
#ifdef CONFIG_STORAGE_MULTI
|
||||||
|
|
|
@ -446,7 +446,7 @@ static int sd_init_card(const int drive)
|
||||||
sd_parse_csd(&card_info[drive]);
|
sd_parse_csd(&card_info[drive]);
|
||||||
|
|
||||||
#if defined(HAVE_MULTIDRIVE)
|
#if defined(HAVE_MULTIDRIVE)
|
||||||
hs_card = (card_info[drive].speed == 50000000);
|
hs_card = (card_info[drive].speed >= 50000000);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Boost MCICLK to operating speed */
|
/* Boost MCICLK to operating speed */
|
||||||
|
@ -680,7 +680,7 @@ static int sd_select_bank(signed char bank)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_transfer_sectors(IF_MD(int drive,) unsigned long start,
|
static int sd_transfer_sectors(IF_MD(int drive,) sector_t start,
|
||||||
int count, void* buf, const bool write)
|
int count, void* buf, const bool write)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
|
@ -735,7 +735,8 @@ static int sd_transfer_sectors(IF_MD(int drive,) unsigned long start,
|
||||||
unsigned int transfer = (count >= 128) ? 127 : count; /* sectors */
|
unsigned int transfer = (count >= 128) ? 127 : count; /* sectors */
|
||||||
void *dma_buf;
|
void *dma_buf;
|
||||||
|
|
||||||
unsigned long bank_start = start;
|
sector_t bank_start = start;
|
||||||
|
// XXX 64-bit sectors?
|
||||||
|
|
||||||
/* Only switch banks for internal storage */
|
/* Only switch banks for internal storage */
|
||||||
if(drive == INTERNAL_AS3525)
|
if(drive == INTERNAL_AS3525)
|
||||||
|
@ -869,7 +870,7 @@ sd_transfer_error_nodma:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_read_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
void* buf)
|
void* buf)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -881,11 +882,11 @@ int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
#ifdef VERIFY_WRITE
|
#ifdef VERIFY_WRITE
|
||||||
unsigned long saved_start = start;
|
sector_t saved_start = start;
|
||||||
int saved_count = count;
|
int saved_count = count;
|
||||||
void *saved_buf = (void*)buf;
|
void *saved_buf = (void*)buf;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -677,7 +677,7 @@ int sd_init(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_transfer_sectors(IF_MD(int drive,) unsigned long start,
|
static int sd_transfer_sectors(IF_MD(int drive,) sector_t start,
|
||||||
int count, void* buf, bool write)
|
int count, void* buf, bool write)
|
||||||
{
|
{
|
||||||
unsigned long response;
|
unsigned long response;
|
||||||
|
@ -776,7 +776,7 @@ retry_with_reinit:
|
||||||
|
|
||||||
MCI_BYTCNT = transfer * SD_BLOCK_SIZE;
|
MCI_BYTCNT = transfer * SD_BLOCK_SIZE;
|
||||||
|
|
||||||
int arg = start;
|
sector_t arg = start; // XXX 64-bit
|
||||||
if(!(card_info[drive].ocr & (1<<30))) /* not SDHC */
|
if(!(card_info[drive].ocr & (1<<30))) /* not SDHC */
|
||||||
arg *= SD_BLOCK_SIZE;
|
arg *= SD_BLOCK_SIZE;
|
||||||
|
|
||||||
|
@ -858,13 +858,13 @@ exit:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_read_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
void* buf)
|
void* buf)
|
||||||
{
|
{
|
||||||
return sd_transfer_sectors(IF_MD(drive,) start, count, buf, false);
|
return sd_transfer_sectors(IF_MD(drive,) start, count, buf, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
return sd_transfer_sectors(IF_MD(drive,) start, count, (void*)buf, true);
|
return sd_transfer_sectors(IF_MD(drive,) start, count, (void*)buf, true);
|
||||||
|
|
|
@ -801,7 +801,7 @@ static void read_inplace_writes_cache(int bank, int phys_segment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int nand_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
int nand_read_sectors(IF_MD(int drive,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
|
@ -817,7 +817,7 @@ int nand_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
||||||
while (incount > 0)
|
while (incount > 0)
|
||||||
{
|
{
|
||||||
int done = 0;
|
int done = 0;
|
||||||
int segment = start / sectors_per_segment;
|
sector_t segment = start / sectors_per_segment;
|
||||||
int secmod = start % sectors_per_segment;
|
int secmod = start % sectors_per_segment;
|
||||||
|
|
||||||
while (incount > 0 && secmod < sectors_per_segment)
|
while (incount > 0 && secmod < sectors_per_segment)
|
||||||
|
@ -856,7 +856,7 @@ nand_read_error:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nand_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
|
|
|
@ -36,13 +36,13 @@ int nand_init(void)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
int nand_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int nand_read_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
void* buf)
|
void* buf)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nand_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -71,7 +71,7 @@ static const char *creative_part_name(enum imx233_part_t part)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int compute_window_creative(intptr_t user, part_read_fn_t read_fn,
|
static int compute_window_creative(intptr_t user, part_read_fn_t read_fn,
|
||||||
enum imx233_part_t part, unsigned *start, unsigned *end)
|
enum imx233_part_t part, sector_t *start, unsigned *end)
|
||||||
{
|
{
|
||||||
uint8_t mblk[512];
|
uint8_t mblk[512];
|
||||||
int ret = read_fn(user, MBLK_ADDR / 512, 1, mblk);
|
int ret = read_fn(user, MBLK_ADDR / 512, 1, mblk);
|
||||||
|
|
|
@ -45,7 +45,7 @@ enum imx233_part_t
|
||||||
/** The computation function can be called very early in the boot, at which point
|
/** The computation function can be called very early in the boot, at which point
|
||||||
* usual storage read/write function may not be available. To workaround this
|
* usual storage read/write function may not be available. To workaround this
|
||||||
* issue, one must provide a read function. */
|
* issue, one must provide a read function. */
|
||||||
typedef int (*part_read_fn_t)(intptr_t user, unsigned long start, int count, void* buf);
|
typedef int (*part_read_fn_t)(intptr_t user, sector_t start, int count, void* buf);
|
||||||
/* Enable/Disable window computations for internal storage following the
|
/* Enable/Disable window computations for internal storage following the
|
||||||
* Freescale/Creative convention */
|
* Freescale/Creative convention */
|
||||||
void imx233_partitions_enable_window(bool enable);
|
void imx233_partitions_enable_window(bool enable);
|
||||||
|
@ -55,6 +55,6 @@ bool imx233_partitions_is_window_enabled(void);
|
||||||
* for a whole disk, *end should be the size of the disk when the function is
|
* for a whole disk, *end should be the size of the disk when the function is
|
||||||
* called */
|
* called */
|
||||||
int imx233_partitions_compute_window(intptr_t user, part_read_fn_t read_fn,
|
int imx233_partitions_compute_window(intptr_t user, part_read_fn_t read_fn,
|
||||||
enum imx233_part_t part, unsigned *start, unsigned *end);
|
enum imx233_part_t part, sector_t *start, unsigned *end);
|
||||||
|
|
||||||
#endif /* __PARTITIONS_IMX233__ */
|
#endif /* __PARTITIONS_IMX233__ */
|
|
@ -648,7 +648,7 @@ int mmc_event(long id, intptr_t data)
|
||||||
#endif /* CONFIG_STORAGE & STORAGE_MMC */
|
#endif /* CONFIG_STORAGE & STORAGE_MMC */
|
||||||
|
|
||||||
/* low-level function, don't call directly! */
|
/* low-level function, don't call directly! */
|
||||||
static int __xfer_sectors(int drive, unsigned long start, int count, void *buf, bool read)
|
static int __xfer_sectors(int drive, sector_t start, int count, void *buf, bool read)
|
||||||
{
|
{
|
||||||
uint32_t resp;
|
uint32_t resp;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -660,7 +660,7 @@ static int __xfer_sectors(int drive, unsigned long start, int count, void *buf,
|
||||||
need_stop = false;
|
need_stop = false;
|
||||||
/* Set bank_start to the correct unit (blocks or bytes).
|
/* Set bank_start to the correct unit (blocks or bytes).
|
||||||
* MMC drives use block addressing, SD cards bytes or blocks */
|
* MMC drives use block addressing, SD cards bytes or blocks */
|
||||||
int bank_start = start;
|
sector_t bank_start = start;
|
||||||
if(SDMMC_MODE(drive) == SD_MODE && !(SDMMC_INFO(drive).ocr & (1<<30))) /* not SDHC */
|
if(SDMMC_MODE(drive) == SD_MODE && !(SDMMC_INFO(drive).ocr & (1<<30))) /* not SDHC */
|
||||||
bank_start *= SD_BLOCK_SIZE;
|
bank_start *= SD_BLOCK_SIZE;
|
||||||
/* issue read/write
|
/* issue read/write
|
||||||
|
@ -686,7 +686,7 @@ static int __xfer_sectors(int drive, unsigned long start, int count, void *buf,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int transfer_sectors(int drive, unsigned long start, int count, void *buf, bool read)
|
static int transfer_sectors(int drive, sector_t start, int count, void *buf, bool read)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
/* the function doesn't work when count is 0 */
|
/* the function doesn't work when count is 0 */
|
||||||
|
@ -806,7 +806,7 @@ static int transfer_sectors(int drive, unsigned long start, int count, void *buf
|
||||||
}
|
}
|
||||||
|
|
||||||
/* user specifies the sdmmc drive */
|
/* user specifies the sdmmc drive */
|
||||||
static int part_read_fn(intptr_t user, unsigned long start, int count, void* buf)
|
static int part_read_fn(intptr_t user, sector_t start, int count, void* buf)
|
||||||
{
|
{
|
||||||
return transfer_sectors(user, start, count, buf, true);
|
return transfer_sectors(user, start, count, buf, true);
|
||||||
}
|
}
|
||||||
|
@ -917,7 +917,7 @@ void sd_enable(bool on)
|
||||||
(void) on;
|
(void) on;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int sd_drive,) unsigned long start, int count, void *buf)
|
int sd_read_sectors(IF_MD(int sd_drive,) sector_t start, int count, void *buf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
int sd_drive = 0;
|
int sd_drive = 0;
|
||||||
|
@ -925,7 +925,7 @@ int sd_read_sectors(IF_MD(int sd_drive,) unsigned long start, int count, void *b
|
||||||
return transfer_sectors(sd_map[sd_drive], start, count, buf, true);
|
return transfer_sectors(sd_map[sd_drive], start, count, buf, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int sd_drive,) unsigned long start, int count, const void* buf)
|
int sd_write_sectors(IF_MD(int sd_drive,) sector_t start, int count, const void* buf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
int sd_drive = 0;
|
int sd_drive = 0;
|
||||||
|
@ -1039,7 +1039,7 @@ int mmc_spinup_time(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mmc_read_sectors(IF_MD(int mmc_drive,) unsigned long start, int count, void *buf)
|
int mmc_read_sectors(IF_MD(int mmc_drive,) sector_t start, int count, void *buf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
int mmc_drive = 0;
|
int mmc_drive = 0;
|
||||||
|
@ -1047,7 +1047,7 @@ int mmc_read_sectors(IF_MD(int mmc_drive,) unsigned long start, int count, void
|
||||||
return transfer_sectors(mmc_map[mmc_drive], start, count, buf, true);
|
return transfer_sectors(mmc_map[mmc_drive], start, count, buf, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mmc_write_sectors(IF_MD(int mmc_drive,) unsigned long start, int count, const void* buf)
|
int mmc_write_sectors(IF_MD(int mmc_drive,) sector_t start, int count, const void* buf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
int mmc_drive = 0;
|
int mmc_drive = 0;
|
||||||
|
|
|
@ -849,7 +849,7 @@ static void sd_select_device(int card_no)
|
||||||
|
|
||||||
/* API Functions */
|
/* API Functions */
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
int sd_read_sectors(IF_MD(int drive,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
|
@ -857,7 +857,7 @@ int sd_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
||||||
#endif
|
#endif
|
||||||
int ret;
|
int ret;
|
||||||
unsigned char *buf, *buf_end;
|
unsigned char *buf, *buf_end;
|
||||||
unsigned int bank;
|
sector_t bank;
|
||||||
|
|
||||||
/* TODO: Add DMA support. */
|
/* TODO: Add DMA support. */
|
||||||
|
|
||||||
|
@ -904,6 +904,8 @@ sd_read_retry:
|
||||||
|
|
||||||
MMC_NUMBLK = incount;
|
MMC_NUMBLK = incount;
|
||||||
|
|
||||||
|
// XXX 64-bit addresses..
|
||||||
|
|
||||||
#ifdef HAVE_HOTSWAP
|
#ifdef HAVE_HOTSWAP
|
||||||
if(currcard->ocr & (1<<30) )
|
if(currcard->ocr & (1<<30) )
|
||||||
{
|
{
|
||||||
|
@ -966,7 +968,7 @@ sd_read_error:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
/* Write support is not finished yet */
|
/* Write support is not finished yet */
|
||||||
|
|
|
@ -35,14 +35,14 @@
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
|
|
||||||
/* API Functions */
|
/* API Functions */
|
||||||
int nand_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
int nand_read_sectors(IF_MD(int drive,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
(void)drive;
|
(void)drive;
|
||||||
return ftl_read(start, incount, inbuf);
|
return ftl_read(start, incount, inbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nand_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
(void)drive;
|
(void)drive;
|
||||||
|
|
|
@ -453,7 +453,7 @@ static inline void write_sd_data(unsigned char **src)
|
||||||
*src += 512;
|
*src += 512;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_read_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
void* buf)
|
void* buf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
|
@ -498,6 +498,8 @@ int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
||||||
DATA_XFER_MULTI;
|
DATA_XFER_MULTI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX 64-bit
|
||||||
|
|
||||||
/* issue read command to the card */
|
/* issue read command to the card */
|
||||||
if (!send_cmd(SD_READ_MULTIPLE_BLOCK, start, RES_R1, &response))
|
if (!send_cmd(SD_READ_MULTIPLE_BLOCK, start, RES_R1, &response))
|
||||||
{
|
{
|
||||||
|
@ -576,7 +578,7 @@ int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Not tested */
|
/* Not tested */
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* buf)
|
const void* buf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
|
@ -620,6 +622,7 @@ int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
||||||
|
|
||||||
write_sd_data(&src); /* put data into transfer buffer */
|
write_sd_data(&src); /* put data into transfer buffer */
|
||||||
|
|
||||||
|
// XXX 64-bit
|
||||||
if (!send_cmd(SD_WRITE_MULTIPLE_BLOCK, start, RES_R1, &response))
|
if (!send_cmd(SD_WRITE_MULTIPLE_BLOCK, start, RES_R1, &response))
|
||||||
{
|
{
|
||||||
ret = -3;
|
ret = -3;
|
||||||
|
|
|
@ -596,7 +596,7 @@ static int sd_wait_for_state(const int card_no, unsigned int state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_transfer_sectors(int card_no, unsigned long start,
|
static int sd_transfer_sectors(int card_no, sector_t start,
|
||||||
int count, void* buf, const bool write)
|
int count, void* buf, const bool write)
|
||||||
{
|
{
|
||||||
int ret = EC_OK;
|
int ret = EC_OK;
|
||||||
|
@ -636,7 +636,7 @@ static int sd_transfer_sectors(int card_no, unsigned long start,
|
||||||
void *dma_buf;
|
void *dma_buf;
|
||||||
const int cmd =
|
const int cmd =
|
||||||
write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK;
|
write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK;
|
||||||
unsigned long start_addr = start;
|
sector_t start_addr = start;
|
||||||
|
|
||||||
dma_buf = aligned_buffer;
|
dma_buf = aligned_buffer;
|
||||||
if(transfer > UNALIGNED_NUM_SECTORS)
|
if(transfer > UNALIGNED_NUM_SECTORS)
|
||||||
|
@ -665,6 +665,7 @@ static int sd_transfer_sectors(int card_no, unsigned long start,
|
||||||
INTPND = SDI_MASK;
|
INTPND = SDI_MASK;
|
||||||
|
|
||||||
/* Initiate read/write command */
|
/* Initiate read/write command */
|
||||||
|
// XXX 64-bit
|
||||||
if(!send_cmd(card_no, cmd, start_addr, MCI_ARG | MCI_RESP, NULL))
|
if(!send_cmd(card_no, cmd, start_addr, MCI_ARG | MCI_RESP, NULL))
|
||||||
{
|
{
|
||||||
ret -= 3*20;
|
ret -= 3*20;
|
||||||
|
@ -783,7 +784,7 @@ sd_transfer_error:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int card_no,) unsigned long start, int incount,
|
int sd_read_sectors(IF_MD(int card_no,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -804,7 +805,7 @@ int sd_read_sectors(IF_MD(int card_no,) unsigned long start, int incount,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
#ifdef BOOTLOADER /* we don't need write support in bootloader */
|
#ifdef BOOTLOADER /* we don't need write support in bootloader */
|
||||||
|
|
|
@ -33,13 +33,13 @@
|
||||||
static bool initialized = false;
|
static bool initialized = false;
|
||||||
|
|
||||||
/* API Functions */
|
/* API Functions */
|
||||||
int nand_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
int nand_read_sectors(IF_MD(int drive,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
return ftl_read(start, incount, inbuf);
|
return ftl_read(start, incount, inbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int nand_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
return ftl_write(start, count, outbuf);
|
return ftl_write(start, count, outbuf);
|
||||||
|
|
|
@ -58,10 +58,9 @@
|
||||||
#define CEATA_DAT_NONBUSY_TIMEOUT 5000000
|
#define CEATA_DAT_NONBUSY_TIMEOUT 5000000
|
||||||
#define CEATA_MMC_RCA 1
|
#define CEATA_MMC_RCA 1
|
||||||
|
|
||||||
|
|
||||||
/** static, private data **/
|
/** static, private data **/
|
||||||
static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR;
|
static uint8_t ceata_taskfile[16] STORAGE_ALIGN_ATTR;
|
||||||
static uint16_t ata_identify_data[0x100] STORAGE_ALIGN_ATTR;
|
static uint16_t ata_identify_data[ATA_IDENTIFY_WORDS] STORAGE_ALIGN_ATTR;
|
||||||
static bool ceata;
|
static bool ceata;
|
||||||
static bool ata_lba48;
|
static bool ata_lba48;
|
||||||
static bool ata_dma;
|
static bool ata_dma;
|
||||||
|
@ -510,7 +509,7 @@ static int ata_identify(uint16_t* buf)
|
||||||
ata_write_cbr(&ATA_PIO_DVR, 0);
|
ata_write_cbr(&ATA_PIO_DVR, 0);
|
||||||
ata_write_cbr(&ATA_PIO_CSD, CMD_IDENTIFY);
|
ata_write_cbr(&ATA_PIO_CSD, CMD_IDENTIFY);
|
||||||
PASS_RC(ata_wait_for_start_of_transfer(10000000), 1, 1);
|
PASS_RC(ata_wait_for_start_of_transfer(10000000), 1, 1);
|
||||||
for (i = 0; i < 0x100; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR);
|
for (i = 0; i < ATA_IDENTIFY_WORDS; i++) buf[i] = ata_read_cbr(&ATA_PIO_DTR);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -701,7 +700,7 @@ static int ata_power_up(void)
|
||||||
| (((uint64_t)ata_identify_data[103]) << 48);
|
| (((uint64_t)ata_identify_data[103]) << 48);
|
||||||
else
|
else
|
||||||
ata_total_sectors = ata_identify_data[60] | (((uint32_t)ata_identify_data[61]) << 16);
|
ata_total_sectors = ata_identify_data[60] | (((uint32_t)ata_identify_data[61]) << 16);
|
||||||
ata_total_sectors >>= 3;
|
ata_total_sectors >>= 3; /* ie SECTOR_SIZE/512. */
|
||||||
ata_powered = true;
|
ata_powered = true;
|
||||||
ata_set_active();
|
ata_set_active();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -966,7 +965,7 @@ static int ata_reset(void)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ata_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
int ata_read_sectors(IF_MD(int drive,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
mutex_lock(&ata_mutex);
|
mutex_lock(&ata_mutex);
|
||||||
|
@ -975,7 +974,7 @@ int ata_read_sectors(IF_MD(int drive,) unsigned long start, int incount,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ata_write_sectors(IF_MD(int drive,) unsigned long start, int count,
|
int ata_write_sectors(IF_MD(int drive,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
mutex_lock(&ata_mutex);
|
mutex_lock(&ata_mutex);
|
||||||
|
|
|
@ -386,7 +386,7 @@ static void sd_select_device(int card_no)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int card_no,) unsigned long start, int incount,
|
int sd_read_sectors(IF_MD(int card_no,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
|
@ -433,6 +433,7 @@ sd_read_retry:
|
||||||
|
|
||||||
SDIDCTRL2 = (1<<2); /* multi block, read */
|
SDIDCTRL2 = (1<<2); /* multi block, read */
|
||||||
|
|
||||||
|
// XXX 64-bit
|
||||||
if (currcard->ocr & (1<<30))
|
if (currcard->ocr & (1<<30))
|
||||||
ret = sd_command(SD_READ_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
|
ret = sd_command(SD_READ_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
|
||||||
else
|
else
|
||||||
|
@ -500,7 +501,7 @@ sd_read_error:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int card_no,) unsigned long start, int count,
|
int sd_write_sectors(IF_MD(int card_no,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
/* Write support is not finished yet */
|
/* Write support is not finished yet */
|
||||||
|
@ -553,6 +554,7 @@ sd_write_retry:
|
||||||
|
|
||||||
SDIDCTRL2 = (1<<2) | (1<<1); /* multi block, write */
|
SDIDCTRL2 = (1<<2) | (1<<1); /* multi block, write */
|
||||||
|
|
||||||
|
// XXX 64-bit
|
||||||
if (currcard->ocr & (1<<30))
|
if (currcard->ocr & (1<<30))
|
||||||
ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
|
ret = sd_command(SD_WRITE_MULTIPLE_BLOCK, start, NULL, SDICMD_RES_TYPE1);
|
||||||
else
|
else
|
||||||
|
|
|
@ -126,8 +126,10 @@ void GIO2(void)
|
||||||
|
|
||||||
#define VFAT_SECTOR_SIZE(x) ( (x)/0x8000 ) /* 1GB array requires 80kB of RAM */
|
#define VFAT_SECTOR_SIZE(x) ( (x)/0x8000 ) /* 1GB array requires 80kB of RAM */
|
||||||
|
|
||||||
extern int ata_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
extern int ata_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
extern int ata_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
extern int ata_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
|
|
||||||
|
// XXX 64-bit: Due to this it's not likely that this target will ever handle 64-bit storage.
|
||||||
|
|
||||||
struct main_header
|
struct main_header
|
||||||
{
|
{
|
||||||
|
@ -384,19 +386,19 @@ static void cfs_init(void)
|
||||||
cfs_inited = true;
|
cfs_inited = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned long map_sector(unsigned long sector)
|
static inline sector_t map_sector(sector_t sector)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Sector mapping: start of CFS + FAT_SECTOR2CFS_SECTOR(sector) + missing part
|
* Sector mapping: start of CFS + FAT_SECTOR2CFS_SECTOR(sector) + missing part
|
||||||
* FAT works with sectors of 0x200 bytes, CFS with sectors of 0x8000 bytes.
|
* FAT works with sectors of 0x200 bytes, CFS with sectors of 0x8000 bytes.
|
||||||
*/
|
*/
|
||||||
#ifndef BOOTLOADER
|
#ifndef BOOTLOADER
|
||||||
unsigned long *sectors = core_get_data(sectors_handle);
|
sector_t *sectors = core_get_data(sectors_handle);
|
||||||
#endif
|
#endif
|
||||||
return cfs_start+sectors[sector/64]*64+sector%64;
|
return cfs_start+sectors[sector/64]*64+sector%64;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ata_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf)
|
int ata_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf)
|
||||||
{
|
{
|
||||||
if(!cfs_inited)
|
if(!cfs_inited)
|
||||||
cfs_init();
|
cfs_init();
|
||||||
|
@ -423,7 +425,7 @@ int ata_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ata_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf)
|
int ata_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf)
|
||||||
{
|
{
|
||||||
if(!cfs_inited)
|
if(!cfs_inited)
|
||||||
cfs_init();
|
cfs_init();
|
||||||
|
|
|
@ -36,8 +36,8 @@
|
||||||
/* Nasty hack, but Creative is nasty... */
|
/* Nasty hack, but Creative is nasty... */
|
||||||
#define ata_read_sectors _ata_read_sectors
|
#define ata_read_sectors _ata_read_sectors
|
||||||
#define ata_write_sectors _ata_write_sectors
|
#define ata_write_sectors _ata_write_sectors
|
||||||
extern int _ata_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf);
|
extern int _ata_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf);
|
||||||
extern int _ata_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf);
|
extern int _ata_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf);
|
||||||
|
|
||||||
/* General purpose memory region #1 */
|
/* General purpose memory region #1 */
|
||||||
#define ATA_IOBASE 0x50FEE000
|
#define ATA_IOBASE 0x50FEE000
|
||||||
|
|
|
@ -597,17 +597,17 @@ static int sd_wait_for_state(unsigned int state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_transfer_sectors(int card_no, unsigned long start,
|
static int sd_transfer_sectors(int card_no, sector_t start,
|
||||||
int count, void *buffer, bool write)
|
int count, void *buffer, bool write)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long start_addr;
|
sector_t start_addr;
|
||||||
int dma_channel = -1;
|
int dma_channel = -1;
|
||||||
bool use_direct_dma;
|
bool use_direct_dma;
|
||||||
int count_per_dma;
|
int count_per_dma;
|
||||||
unsigned long rel_addr;
|
unsigned long rel_addr;
|
||||||
|
|
||||||
dbgprintf("transfer %d %d %d", card_no, start, count);
|
dbgprintf("transfer %d %lu %d", card_no, start, count);
|
||||||
mutex_lock(&sd_mtx);
|
mutex_lock(&sd_mtx);
|
||||||
enable_controller(true);
|
enable_controller(true);
|
||||||
|
|
||||||
|
@ -673,6 +673,7 @@ sd_transfer_retry:
|
||||||
if (!(card_info[card_no].ocr & SD_OCR_CARD_CAPACITY_STATUS))
|
if (!(card_info[card_no].ocr & SD_OCR_CARD_CAPACITY_STATUS))
|
||||||
start_addr *= SD_BLOCK_SIZE; /* not SDHC */
|
start_addr *= SD_BLOCK_SIZE; /* not SDHC */
|
||||||
|
|
||||||
|
// XXX 64-bit
|
||||||
ret = sd_command(write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK,
|
ret = sd_command(write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK,
|
||||||
start_addr, MMC_CMD_DCLR | MMC_CMD_DATA |
|
start_addr, MMC_CMD_DCLR | MMC_CMD_DATA |
|
||||||
SDHC_RESP_FMT_1 | (write ? MMC_CMD_WRITE : 0),
|
SDHC_RESP_FMT_1 | (write ? MMC_CMD_WRITE : 0),
|
||||||
|
@ -765,7 +766,7 @@ sd_transfer_error:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int card_no,) unsigned long start, int incount,
|
int sd_read_sectors(IF_MD(int card_no,) sector_t start, int incount,
|
||||||
void* inbuf)
|
void* inbuf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
|
@ -774,7 +775,7 @@ int sd_read_sectors(IF_MD(int card_no,) unsigned long start, int incount,
|
||||||
return sd_transfer_sectors(card_no, start, incount, inbuf, false);
|
return sd_transfer_sectors(card_no, start, incount, inbuf, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int card_no,) unsigned long start, int count,
|
int sd_write_sectors(IF_MD(int card_no,) sector_t start, int count,
|
||||||
const void* outbuf)
|
const void* outbuf)
|
||||||
{
|
{
|
||||||
#ifndef HAVE_MULTIDRIVE
|
#ifndef HAVE_MULTIDRIVE
|
||||||
|
|
|
@ -209,9 +209,9 @@ int os_opendir_and_fd(const char *osdirname, DIR **osdirpp, int *osfdp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* do we really need this in the app? (in the sim, yes) */
|
/* do we really need this in the app? (in the sim, yes) */
|
||||||
void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep)
|
void volume_size(IF_MV(int volume,) sector_t *sizep, sector_t *freep)
|
||||||
{
|
{
|
||||||
unsigned long size = 0, free = 0;
|
sector_t size = 0, free = 0;
|
||||||
char volpath[MAX_PATH];
|
char volpath[MAX_PATH];
|
||||||
struct statfs fs;
|
struct statfs fs;
|
||||||
|
|
||||||
|
|
|
@ -472,7 +472,7 @@ int os_modtime(const char *path, time_t modtime)
|
||||||
|
|
||||||
int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize);
|
int os_volume_path(IF_MV(int volume, ) char *buffer, size_t bufsize);
|
||||||
|
|
||||||
void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep)
|
void volume_size(IF_MV(int volume,) sector_t *sizep, sector_t *freep)
|
||||||
{
|
{
|
||||||
ULARGE_INTEGER free = { .QuadPart = 0 },
|
ULARGE_INTEGER free = { .QuadPart = 0 },
|
||||||
size = { .QuadPart = 0 };
|
size = { .QuadPart = 0 };
|
||||||
|
|
|
@ -84,7 +84,7 @@ struct nand_param
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static volatile unsigned long nand_address;
|
static volatile sector_t nand_address;
|
||||||
#define NAND_DATAPORT (nand_address)
|
#define NAND_DATAPORT (nand_address)
|
||||||
#define NAND_ADDRPORT (nand_address+0x10000)
|
#define NAND_ADDRPORT (nand_address+0x10000)
|
||||||
#define NAND_COMMPORT (nand_address+0x08000)
|
#define NAND_COMMPORT (nand_address+0x08000)
|
||||||
|
@ -111,7 +111,7 @@ static volatile unsigned long nand_address;
|
||||||
static struct nand_info* chip_info = NULL;
|
static struct nand_info* chip_info = NULL;
|
||||||
static struct nand_info* banks[4];
|
static struct nand_info* banks[4];
|
||||||
static unsigned int nr_banks = 1;
|
static unsigned int nr_banks = 1;
|
||||||
static unsigned long bank_size;
|
static sector_t bank_size;
|
||||||
static struct nand_param internal_param;
|
static struct nand_param internal_param;
|
||||||
static struct mutex nand_mtx;
|
static struct mutex nand_mtx;
|
||||||
#ifdef USE_DMA
|
#ifdef USE_DMA
|
||||||
|
@ -282,7 +282,7 @@ static void jz_rs_correct(unsigned char *dat, int idx, int mask)
|
||||||
/*
|
/*
|
||||||
* Read oob
|
* Read oob
|
||||||
*/
|
*/
|
||||||
static int jz_nand_read_oob(unsigned long page_addr, unsigned char *buf, int size)
|
static int jz_nand_read_oob(sector_t page_addr, unsigned char *buf, int size)
|
||||||
{
|
{
|
||||||
struct nand_param *nandp = &internal_param;
|
struct nand_param *nandp = &internal_param;
|
||||||
int page_size, row_cycle, bus_width;
|
int page_size, row_cycle, bus_width;
|
||||||
|
@ -338,7 +338,7 @@ static int jz_nand_read_oob(unsigned long page_addr, unsigned char *buf, int siz
|
||||||
* page - page number within a block: 0, 1, 2, ...
|
* page - page number within a block: 0, 1, 2, ...
|
||||||
* dst - pointer to target buffer
|
* dst - pointer to target buffer
|
||||||
*/
|
*/
|
||||||
static int jz_nand_read_page(unsigned long page_addr, unsigned char *dst)
|
static int jz_nand_read_page(sector_t page_addr, unsigned char *dst)
|
||||||
{
|
{
|
||||||
struct nand_param *nandp = &internal_param;
|
struct nand_param *nandp = &internal_param;
|
||||||
int page_size, oob_size;
|
int page_size, oob_size;
|
||||||
|
@ -611,7 +611,7 @@ int nand_init(void)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int read_sector(unsigned long start, unsigned int count,
|
static inline int read_sector(sector_t start, unsigned int count,
|
||||||
void* buf, unsigned int chip_size)
|
void* buf, unsigned int chip_size)
|
||||||
{
|
{
|
||||||
register int ret;
|
register int ret;
|
||||||
|
@ -627,14 +627,14 @@ static inline int read_sector(unsigned long start, unsigned int count,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nand_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf)
|
int nand_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
(void)drive;
|
(void)drive;
|
||||||
#endif
|
#endif
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
unsigned int i, _count, chip_size = chip_info->page_size;
|
unsigned int i, _count, chip_size = chip_info->page_size;
|
||||||
unsigned long _start;
|
sector_t _start;
|
||||||
|
|
||||||
logf("start");
|
logf("start");
|
||||||
mutex_lock(&nand_mtx);
|
mutex_lock(&nand_mtx);
|
||||||
|
@ -670,7 +670,7 @@ int nand_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* bu
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO */
|
/* TODO */
|
||||||
int nand_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf)
|
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf)
|
||||||
{
|
{
|
||||||
(void)start;
|
(void)start;
|
||||||
(void)count;
|
(void)count;
|
||||||
|
|
|
@ -110,7 +110,7 @@ struct nand_param {
|
||||||
|
|
||||||
static struct nand_info* chip_info = NULL;
|
static struct nand_info* chip_info = NULL;
|
||||||
static struct nand_info* bank;
|
static struct nand_info* bank;
|
||||||
static unsigned long nand_size;
|
static sector_t nand_size;
|
||||||
static struct nand_param internal_param;
|
static struct nand_param internal_param;
|
||||||
static struct mutex nand_mtx;
|
static struct mutex nand_mtx;
|
||||||
#ifdef USE_DMA
|
#ifdef USE_DMA
|
||||||
|
@ -281,7 +281,7 @@ static void jz_rs_correct(unsigned char *dat, int idx, int mask)
|
||||||
/*
|
/*
|
||||||
* Read oob
|
* Read oob
|
||||||
*/
|
*/
|
||||||
static int jz_nand_read_oob(unsigned long page_addr, unsigned char *buf, int size)
|
static int jz_nand_read_oob(sector_t page_addr, unsigned char *buf, int size)
|
||||||
{
|
{
|
||||||
struct nand_param *nandp = &internal_param;
|
struct nand_param *nandp = &internal_param;
|
||||||
int page_size, row_cycle, bus_width;
|
int page_size, row_cycle, bus_width;
|
||||||
|
@ -337,7 +337,7 @@ static int jz_nand_read_oob(unsigned long page_addr, unsigned char *buf, int siz
|
||||||
* page - page number within a block: 0, 1, 2, ...
|
* page - page number within a block: 0, 1, 2, ...
|
||||||
* dst - pointer to target buffer
|
* dst - pointer to target buffer
|
||||||
*/
|
*/
|
||||||
static int jz_nand_read_page(unsigned long page_addr, unsigned char *dst)
|
static int jz_nand_read_page(sector_t page_addr, unsigned char *dst)
|
||||||
{
|
{
|
||||||
struct nand_param *nandp = &internal_param;
|
struct nand_param *nandp = &internal_param;
|
||||||
int page_size, oob_size;
|
int page_size, oob_size;
|
||||||
|
@ -532,7 +532,7 @@ int nand_init(void)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int read_sector(unsigned long start, unsigned int count,
|
static inline int read_sector(sector_t start, unsigned int count,
|
||||||
void* buf, unsigned int chip_size)
|
void* buf, unsigned int chip_size)
|
||||||
{
|
{
|
||||||
register int ret;
|
register int ret;
|
||||||
|
@ -548,7 +548,7 @@ static inline int read_sector(unsigned long start, unsigned int count,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int write_sector(unsigned long start, unsigned int count,
|
static inline int write_sector(sector_t start, unsigned int count,
|
||||||
const void* buf, unsigned int chip_size)
|
const void* buf, unsigned int chip_size)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
@ -563,14 +563,14 @@ static inline int write_sector(unsigned long start, unsigned int count,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nand_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf)
|
int nand_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
(void)drive;
|
(void)drive;
|
||||||
#endif
|
#endif
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
unsigned int _count, chip_size = chip_info->page_size;
|
unsigned int _count, chip_size = chip_info->page_size;
|
||||||
unsigned long _start;
|
sector_t _start;
|
||||||
|
|
||||||
logf("start");
|
logf("start");
|
||||||
mutex_lock(&nand_mtx);
|
mutex_lock(&nand_mtx);
|
||||||
|
@ -590,14 +590,14 @@ int nand_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* bu
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nand_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf)
|
int nand_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
(void)drive;
|
(void)drive;
|
||||||
#endif
|
#endif
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
unsigned int _count, chip_size = chip_info->page_size;
|
unsigned int _count, chip_size = chip_info->page_size;
|
||||||
unsigned long _start;
|
sector_t _start;
|
||||||
|
|
||||||
logf("start");
|
logf("start");
|
||||||
mutex_lock(&nand_mtx);
|
mutex_lock(&nand_mtx);
|
||||||
|
|
|
@ -42,7 +42,6 @@ static struct mutex sd_mtx;
|
||||||
|
|
||||||
static int use_4bit;
|
static int use_4bit;
|
||||||
static int num_6;
|
static int num_6;
|
||||||
static int sd2_0;
|
|
||||||
|
|
||||||
//#define SD_DMA_ENABLE
|
//#define SD_DMA_ENABLE
|
||||||
#define SD_DMA_INTERRUPT 0
|
#define SD_DMA_INTERRUPT 0
|
||||||
|
@ -598,7 +597,7 @@ static int jz_sd_transmit_data(struct sd_request *req)
|
||||||
static inline unsigned int jz_sd_calc_clkrt(unsigned int rate)
|
static inline unsigned int jz_sd_calc_clkrt(unsigned int rate)
|
||||||
{
|
{
|
||||||
unsigned int clkrt;
|
unsigned int clkrt;
|
||||||
unsigned int clk_src = sd2_0 ? SD_CLOCK_HIGH : SD_CLOCK_FAST;
|
unsigned int clk_src = card.sd2plus ? SD_CLOCK_HIGH : SD_CLOCK_FAST;
|
||||||
|
|
||||||
clkrt = 0;
|
clkrt = 0;
|
||||||
while (rate < clk_src)
|
while (rate < clk_src)
|
||||||
|
@ -716,7 +715,7 @@ static int jz_sd_exec_cmd(struct sd_request *request)
|
||||||
events = SD_EVENT_RX_DATA_DONE;
|
events = SD_EVENT_RX_DATA_DONE;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 6:
|
case SD_SWITCH_FUNC:
|
||||||
if (num_6 < 2)
|
if (num_6 < 2)
|
||||||
{
|
{
|
||||||
#if defined(SD_DMA_ENABLE)
|
#if defined(SD_DMA_ENABLE)
|
||||||
|
@ -1086,7 +1085,6 @@ static int sd_init_card_state(struct sd_request *request)
|
||||||
(request->response[3+i*4]<< 8) | request->response[4+i*4]);
|
(request->response[3+i*4]<< 8) | request->response[4+i*4]);
|
||||||
|
|
||||||
sd_parse_csd(&card);
|
sd_parse_csd(&card);
|
||||||
sd2_0 = (card_extract_bits(card.csd, 127, 2) == 1);
|
|
||||||
|
|
||||||
logf("CSD: %08lx%08lx%08lx%08lx", card.csd[0], card.csd[1], card.csd[2], card.csd[3]);
|
logf("CSD: %08lx%08lx%08lx%08lx", card.csd[0], card.csd[1], card.csd[2], card.csd[3]);
|
||||||
DEBUG("SD card is ready");
|
DEBUG("SD card is ready");
|
||||||
|
@ -1155,7 +1153,7 @@ static int sd_select_card(void)
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
if (sd2_0)
|
if (card.sd2plus)
|
||||||
{
|
{
|
||||||
retval = sd_read_switch(&request);
|
retval = sd_read_switch(&request);
|
||||||
if (!retval)
|
if (!retval)
|
||||||
|
@ -1188,7 +1186,6 @@ static int __sd_init_device(void)
|
||||||
/* Initialise card data as blank */
|
/* Initialise card data as blank */
|
||||||
memset(&card, 0, sizeof(tCardInfo));
|
memset(&card, 0, sizeof(tCardInfo));
|
||||||
|
|
||||||
sd2_0 = 0;
|
|
||||||
num_6 = 0;
|
num_6 = 0;
|
||||||
use_4bit = 0;
|
use_4bit = 0;
|
||||||
|
|
||||||
|
@ -1250,7 +1247,7 @@ static inline void sd_stop_transfer(void)
|
||||||
mutex_unlock(&sd_mtx);
|
mutex_unlock(&sd_mtx);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf)
|
int sd_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
(void)drive;
|
(void)drive;
|
||||||
|
@ -1276,7 +1273,8 @@ int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf)
|
||||||
if ((retval = sd_unpack_r1(&request, &r1)))
|
if ((retval = sd_unpack_r1(&request, &r1)))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (sd2_0)
|
// XXX 64-bit
|
||||||
|
if (card.sd2plus)
|
||||||
{
|
{
|
||||||
sd_send_cmd(&request, SD_READ_MULTIPLE_BLOCK, start,
|
sd_send_cmd(&request, SD_READ_MULTIPLE_BLOCK, start,
|
||||||
count, SD_BLOCK_SIZE, RESPONSE_R1, buf);
|
count, SD_BLOCK_SIZE, RESPONSE_R1, buf);
|
||||||
|
@ -1292,19 +1290,18 @@ int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_disk_activity = current_tick;
|
|
||||||
|
|
||||||
sd_simple_cmd(&request, SD_STOP_TRANSMISSION, 0, RESPONSE_R1B);
|
sd_simple_cmd(&request, SD_STOP_TRANSMISSION, 0, RESPONSE_R1B);
|
||||||
if ((retval = sd_unpack_r1(&request, &r1)))
|
if ((retval = sd_unpack_r1(&request, &r1)))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
last_disk_activity = current_tick;
|
||||||
sd_stop_transfer();
|
sd_stop_transfer();
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf)
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf)
|
||||||
{
|
{
|
||||||
#ifdef HAVE_MULTIDRIVE
|
#ifdef HAVE_MULTIDRIVE
|
||||||
(void)drive;
|
(void)drive;
|
||||||
|
@ -1330,7 +1327,8 @@ int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count, const voi
|
||||||
if ((retval = sd_unpack_r1(&request, &r1)))
|
if ((retval = sd_unpack_r1(&request, &r1)))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
if (sd2_0)
|
// XXX 64-bit
|
||||||
|
if (card.sd2plus)
|
||||||
{
|
{
|
||||||
sd_send_cmd(&request, SD_WRITE_MULTIPLE_BLOCK, start,
|
sd_send_cmd(&request, SD_WRITE_MULTIPLE_BLOCK, start,
|
||||||
count, SD_BLOCK_SIZE, RESPONSE_R1,
|
count, SD_BLOCK_SIZE, RESPONSE_R1,
|
||||||
|
|
|
@ -57,8 +57,6 @@ static struct semaphore sd_wakeup[NUM_DRIVES];
|
||||||
|
|
||||||
static int use_4bit[NUM_DRIVES];
|
static int use_4bit[NUM_DRIVES];
|
||||||
static int num_6[NUM_DRIVES];
|
static int num_6[NUM_DRIVES];
|
||||||
static int sd2_0[NUM_DRIVES];
|
|
||||||
|
|
||||||
|
|
||||||
//#define DEBUG(x...) logf(x)
|
//#define DEBUG(x...) logf(x)
|
||||||
#define DEBUG(x, ...)
|
#define DEBUG(x, ...)
|
||||||
|
@ -698,7 +696,7 @@ static inline unsigned int jz_sd_calc_clkrt(const int drive, unsigned int rate)
|
||||||
unsigned int clkrt = 0;
|
unsigned int clkrt = 0;
|
||||||
unsigned int clk_src = cpu_frequency / __cpm_get_mscdiv(); /* MSC_CLK */
|
unsigned int clk_src = cpu_frequency / __cpm_get_mscdiv(); /* MSC_CLK */
|
||||||
|
|
||||||
if (!sd2_0[drive] && rate > SD_CLOCK_FAST)
|
if (!card[drive].sd2plus && rate > SD_CLOCK_FAST)
|
||||||
rate = SD_CLOCK_FAST;
|
rate = SD_CLOCK_FAST;
|
||||||
|
|
||||||
while (rate < clk_src)
|
while (rate < clk_src)
|
||||||
|
@ -1192,7 +1190,6 @@ static int sd_init_card_state(const int drive, struct sd_request *request)
|
||||||
(request->response[3+i*4]<< 8) | request->response[4+i*4]);
|
(request->response[3+i*4]<< 8) | request->response[4+i*4]);
|
||||||
|
|
||||||
sd_parse_csd(&card[drive]);
|
sd_parse_csd(&card[drive]);
|
||||||
sd2_0[drive] = (card_extract_bits(card[drive].csd, 127, 2) == 1);
|
|
||||||
|
|
||||||
logf("CSD: %08lx%08lx%08lx%08lx", card[drive].csd[0], card[drive].csd[1], card[drive].csd[2], card[drive].csd[3]);
|
logf("CSD: %08lx%08lx%08lx%08lx", card[drive].csd[0], card[drive].csd[1], card[drive].csd[2], card[drive].csd[3]);
|
||||||
DEBUG("SD card is ready");
|
DEBUG("SD card is ready");
|
||||||
|
@ -1261,7 +1258,7 @@ static int sd_select_card(const int drive)
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
if (sd2_0[drive])
|
if (card[drive].sd2plus)
|
||||||
{
|
{
|
||||||
retval = sd_read_switch(drive, &request);
|
retval = sd_read_switch(drive, &request);
|
||||||
if (!retval)
|
if (!retval)
|
||||||
|
@ -1292,7 +1289,6 @@ static int __sd_init_device(const int drive)
|
||||||
/* Initialise card data as blank */
|
/* Initialise card data as blank */
|
||||||
memset(&card[drive], 0, sizeof(tCardInfo));
|
memset(&card[drive], 0, sizeof(tCardInfo));
|
||||||
|
|
||||||
sd2_0[drive] = 0;
|
|
||||||
num_6[drive] = 0;
|
num_6[drive] = 0;
|
||||||
use_4bit[drive] = 0;
|
use_4bit[drive] = 0;
|
||||||
active[drive] = 0;
|
active[drive] = 0;
|
||||||
|
@ -1402,7 +1398,7 @@ static inline void sd_stop_transfer(const int drive)
|
||||||
mutex_unlock(&sd_mtx[drive]);
|
mutex_unlock(&sd_mtx[drive]);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_transfer_sectors(IF_MD(const int drive,) unsigned long start, int count, void* buf, bool write)
|
int sd_transfer_sectors(IF_MD(const int drive,) sector_t start, int count, void* buf, bool write)
|
||||||
{
|
{
|
||||||
struct sd_request request;
|
struct sd_request request;
|
||||||
struct sd_response_r1 r1;
|
struct sd_response_r1 r1;
|
||||||
|
@ -1427,11 +1423,12 @@ int sd_transfer_sectors(IF_MD(const int drive,) unsigned long start, int count,
|
||||||
if ((retval = sd_unpack_r1(&request, &r1)))
|
if ((retval = sd_unpack_r1(&request, &r1)))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
// XXX 64-bit
|
||||||
sd_send_cmd(drive, &request,
|
sd_send_cmd(drive, &request,
|
||||||
(count > 1) ?
|
(count > 1) ?
|
||||||
(write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK) :
|
(write ? SD_WRITE_MULTIPLE_BLOCK : SD_READ_MULTIPLE_BLOCK) :
|
||||||
(write ? SD_WRITE_BLOCK : SD_READ_SINGLE_BLOCK),
|
(write ? SD_WRITE_BLOCK : SD_READ_SINGLE_BLOCK),
|
||||||
sd2_0[drive] ? start : (start * SD_BLOCK_SIZE),
|
card[drive].sd2plus ? start : (start * SD_BLOCK_SIZE),
|
||||||
count, SD_BLOCK_SIZE, RESPONSE_R1, buf);
|
count, SD_BLOCK_SIZE, RESPONSE_R1, buf);
|
||||||
if ((retval = sd_unpack_r1(&request, &r1)))
|
if ((retval = sd_unpack_r1(&request, &r1)))
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -1451,12 +1448,12 @@ err:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start, int count, void* buf)
|
int sd_read_sectors(IF_MD(int drive,) sector_t start, int count, void* buf)
|
||||||
{
|
{
|
||||||
return sd_transfer_sectors(IF_MD(drive,) start, count, buf, false);
|
return sd_transfer_sectors(IF_MD(drive,) start, count, buf, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start, int count, const void* buf)
|
int sd_write_sectors(IF_MD(int drive,) sector_t start, int count, const void* buf)
|
||||||
{
|
{
|
||||||
return sd_transfer_sectors(IF_MD(drive,) start, count, (void*)buf, true);
|
return sd_transfer_sectors(IF_MD(drive,) start, count, (void*)buf, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -844,7 +844,7 @@ int msc_cmd_send_csd(msc_drv* d)
|
||||||
d->cardinfo.csd[i] = req.response[i];
|
d->cardinfo.csd[i] = req.response[i];
|
||||||
sd_parse_csd(&d->cardinfo);
|
sd_parse_csd(&d->cardinfo);
|
||||||
|
|
||||||
if((req.response[0] >> 30) == 1)
|
if(d->cardinfo.sd2plus)
|
||||||
d->driver_flags |= MSC_DF_V2_CARD;
|
d->driver_flags |= MSC_DF_V2_CARD;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -51,7 +51,7 @@ static int sd_init_card(msc_drv* d)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sd_transfer(msc_drv* d, bool write,
|
static int sd_transfer(msc_drv* d, bool write,
|
||||||
unsigned long start, int count, void* buf)
|
sector_t start, int count, void* buf)
|
||||||
{
|
{
|
||||||
int status = -1;
|
int status = -1;
|
||||||
|
|
||||||
|
@ -114,6 +114,7 @@ static int sd_transfer(msc_drv* d, bool write,
|
||||||
: SD_READ_MULTIPLE_BLOCK;
|
: SD_READ_MULTIPLE_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX 64-bit
|
||||||
if(d->driver_flags & MSC_DF_V2_CARD)
|
if(d->driver_flags & MSC_DF_V2_CARD)
|
||||||
req.argument = start;
|
req.argument = start;
|
||||||
else
|
else
|
||||||
|
@ -142,14 +143,14 @@ static int sd_transfer(msc_drv* d, bool write,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_read_sectors(IF_MD(int drive,) unsigned long start,
|
int sd_read_sectors(IF_MD(int drive,) sector_t start,
|
||||||
int count, void* buf)
|
int count, void* buf)
|
||||||
{
|
{
|
||||||
return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], false,
|
return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], false,
|
||||||
start, count, buf);
|
start, count, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int sd_write_sectors(IF_MD(int drive,) unsigned long start,
|
int sd_write_sectors(IF_MD(int drive,) sector_t start,
|
||||||
int count, const void* buf)
|
int count, const void* buf)
|
||||||
{
|
{
|
||||||
return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], true,
|
return sd_transfer(sd_to_msc[IF_MD_DRV(drive)], true,
|
||||||
|
|
|
@ -114,6 +114,9 @@
|
||||||
#define SCSI_REPORT_LUNS 0xa0
|
#define SCSI_REPORT_LUNS 0xa0
|
||||||
#define SCSI_WRITE_BUFFER 0x3b
|
#define SCSI_WRITE_BUFFER 0x3b
|
||||||
|
|
||||||
|
#define SCSI_READ_16 0x88
|
||||||
|
#define SCSI_WRITE_16 0x8a
|
||||||
|
|
||||||
#define UMS_STATUS_GOOD 0x00
|
#define UMS_STATUS_GOOD 0x00
|
||||||
#define UMS_STATUS_FAIL 0x01
|
#define UMS_STATUS_FAIL 0x01
|
||||||
|
|
||||||
|
@ -273,7 +276,7 @@ static union {
|
||||||
static char *cbw_buffer;
|
static char *cbw_buffer;
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
unsigned int sector;
|
sector_t sector;
|
||||||
unsigned int count;
|
unsigned int count;
|
||||||
unsigned int orig_count;
|
unsigned int orig_count;
|
||||||
unsigned int cur_cmd;
|
unsigned int cur_cmd;
|
||||||
|
@ -503,7 +506,7 @@ void usb_storage_transfer_complete(int ep,int dir,int status,int length)
|
||||||
if(dir==USB_DIR_IN) {
|
if(dir==USB_DIR_IN) {
|
||||||
logf("IN received in RECEIVING");
|
logf("IN received in RECEIVING");
|
||||||
}
|
}
|
||||||
logf("scsi write %d %d", cur_cmd.sector, cur_cmd.count);
|
logf("scsi write %llu %d", cur_cmd.sector, cur_cmd.count);
|
||||||
if(status==0) {
|
if(status==0) {
|
||||||
if((unsigned int)length!=(SECTOR_SIZE* cur_cmd.count)
|
if((unsigned int)length!=(SECTOR_SIZE* cur_cmd.count)
|
||||||
&& (unsigned int)length!=WRITE_BUFFER_SIZE) {
|
&& (unsigned int)length!=WRITE_BUFFER_SIZE) {
|
||||||
|
@ -511,7 +514,7 @@ void usb_storage_transfer_complete(int ep,int dir,int status,int length)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int next_sector = cur_cmd.sector +
|
sector_t next_sector = cur_cmd.sector +
|
||||||
(WRITE_BUFFER_SIZE/SECTOR_SIZE);
|
(WRITE_BUFFER_SIZE/SECTOR_SIZE);
|
||||||
unsigned int next_count = cur_cmd.count -
|
unsigned int next_count = cur_cmd.count -
|
||||||
MIN(cur_cmd.count,WRITE_BUFFER_SIZE/SECTOR_SIZE);
|
MIN(cur_cmd.count,WRITE_BUFFER_SIZE/SECTOR_SIZE);
|
||||||
|
@ -739,12 +742,10 @@ static void send_and_read_next(void)
|
||||||
|
|
||||||
static void handle_scsi(struct command_block_wrapper* cbw)
|
static void handle_scsi(struct command_block_wrapper* cbw)
|
||||||
{
|
{
|
||||||
/* USB Mass Storage assumes LBA capability.
|
|
||||||
TODO: support 48-bit LBA */
|
|
||||||
|
|
||||||
struct storage_info info;
|
struct storage_info info;
|
||||||
unsigned int length = cbw->data_transfer_length;
|
unsigned int length = cbw->data_transfer_length;
|
||||||
unsigned int block_size, block_count;
|
sector_t block_count;
|
||||||
|
unsigned int block_size;
|
||||||
bool lun_present=true;
|
bool lun_present=true;
|
||||||
unsigned char lun = cbw->lun;
|
unsigned char lun = cbw->lun;
|
||||||
unsigned int block_size_mult = 1;
|
unsigned int block_size_mult = 1;
|
||||||
|
@ -888,6 +889,12 @@ static void handle_scsi(struct command_block_wrapper* cbw)
|
||||||
memset(tb.ms_data_10->block_descriptor.reserved,0,4);
|
memset(tb.ms_data_10->block_descriptor.reserved,0,4);
|
||||||
memset(tb.ms_data_10->block_descriptor.num_blocks,0,8);
|
memset(tb.ms_data_10->block_descriptor.num_blocks,0,8);
|
||||||
|
|
||||||
|
#ifdef STORAGE_64BIT_SECTOR
|
||||||
|
tb.ms_data_10->block_descriptor.num_blocks[2] =
|
||||||
|
((block_count/block_size_mult) & 0xff00000000ULL)>>40;
|
||||||
|
tb.ms_data_10->block_descriptor.num_blocks[3] =
|
||||||
|
((block_count/block_size_mult) & 0x00ff000000ULL)>>32;
|
||||||
|
#endif
|
||||||
tb.ms_data_10->block_descriptor.num_blocks[4] =
|
tb.ms_data_10->block_descriptor.num_blocks[4] =
|
||||||
((block_count/block_size_mult) & 0xff000000)>>24;
|
((block_count/block_size_mult) & 0xff000000)>>24;
|
||||||
tb.ms_data_10->block_descriptor.num_blocks[5] =
|
tb.ms_data_10->block_descriptor.num_blocks[5] =
|
||||||
|
@ -1070,7 +1077,7 @@ static void handle_scsi(struct command_block_wrapper* cbw)
|
||||||
cbw->command_block[8]);
|
cbw->command_block[8]);
|
||||||
cur_cmd.orig_count = cur_cmd.count;
|
cur_cmd.orig_count = cur_cmd.count;
|
||||||
|
|
||||||
//logf("scsi read %d %d", cur_cmd.sector, cur_cmd.count);
|
logf("scsi read %llu %d", cur_cmd.sector, cur_cmd.count);
|
||||||
|
|
||||||
if((cur_cmd.sector + cur_cmd.count) > block_count) {
|
if((cur_cmd.sector + cur_cmd.count) > block_count) {
|
||||||
send_csw(UMS_STATUS_FAIL);
|
send_csw(UMS_STATUS_FAIL);
|
||||||
|
@ -1092,7 +1099,58 @@ static void handle_scsi(struct command_block_wrapper* cbw)
|
||||||
send_and_read_next();
|
send_and_read_next();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#ifdef STORAGE_64BIT_SECTOR
|
||||||
|
case SCSI_READ_16:
|
||||||
|
logf("scsi read16 %d",lun);
|
||||||
|
if(!lun_present) {
|
||||||
|
send_command_failed_result();
|
||||||
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
||||||
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
||||||
|
cur_sense_data.ascq=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur_cmd.data[0] = tb.transfer_buffer;
|
||||||
|
cur_cmd.data[1] = &tb.transfer_buffer[READ_BUFFER_SIZE];
|
||||||
|
cur_cmd.data_select=0;
|
||||||
|
cur_cmd.sector = block_size_mult *
|
||||||
|
((uint64_t)cbw->command_block[2] << 56 |
|
||||||
|
(uint64_t)cbw->command_block[3] << 48 |
|
||||||
|
(uint64_t)cbw->command_block[4] << 40 |
|
||||||
|
(uint64_t)cbw->command_block[5] << 32 |
|
||||||
|
cbw->command_block[6] << 24 |
|
||||||
|
cbw->command_block[7] << 16 |
|
||||||
|
cbw->command_block[8] << 8 |
|
||||||
|
cbw->command_block[9]);
|
||||||
|
cur_cmd.count = block_size_mult *
|
||||||
|
(cbw->command_block[10] << 24 |
|
||||||
|
cbw->command_block[11] << 16 |
|
||||||
|
cbw->command_block[12] << 8 |
|
||||||
|
cbw->command_block[13]);
|
||||||
|
cur_cmd.orig_count = cur_cmd.count;
|
||||||
|
|
||||||
|
logf("scsi read %llu %d", cur_cmd.sector, cur_cmd.count);
|
||||||
|
|
||||||
|
if((cur_cmd.sector + cur_cmd.count) > block_count) {
|
||||||
|
send_csw(UMS_STATUS_FAIL);
|
||||||
|
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
|
||||||
|
cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
|
||||||
|
cur_sense_data.ascq=0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#ifdef USB_USE_RAMDISK
|
||||||
|
memcpy(cur_cmd.data[cur_cmd.data_select],
|
||||||
|
ramdisk_buffer + cur_cmd.sector*SECTOR_SIZE,
|
||||||
|
MIN(READ_BUFFER_SIZE/SECTOR_SIZE,cur_cmd.count)*SECTOR_SIZE);
|
||||||
|
#else
|
||||||
|
cur_cmd.last_result = storage_read_sectors(IF_MD(cur_cmd.lun,)
|
||||||
|
cur_cmd.sector,
|
||||||
|
MIN(READ_BUFFER_SIZE/SECTOR_SIZE, cur_cmd.count),
|
||||||
|
cur_cmd.data[cur_cmd.data_select]);
|
||||||
|
#endif
|
||||||
|
send_and_read_next();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
case SCSI_WRITE_10:
|
case SCSI_WRITE_10:
|
||||||
logf("scsi write10 %d",lun);
|
logf("scsi write10 %d",lun);
|
||||||
if(!lun_present) {
|
if(!lun_present) {
|
||||||
|
@ -1127,6 +1185,48 @@ static void handle_scsi(struct command_block_wrapper* cbw)
|
||||||
MIN(WRITE_BUFFER_SIZE, cur_cmd.count*SECTOR_SIZE));
|
MIN(WRITE_BUFFER_SIZE, cur_cmd.count*SECTOR_SIZE));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
#ifdef STORAGE_64BIT_SECTOR
|
||||||
|
case SCSI_WRITE_16:
|
||||||
|
logf("scsi write16 %d",lun);
|
||||||
|
if(!lun_present) {
|
||||||
|
send_csw(UMS_STATUS_FAIL);
|
||||||
|
cur_sense_data.sense_key=SENSE_NOT_READY;
|
||||||
|
cur_sense_data.asc=ASC_MEDIUM_NOT_PRESENT;
|
||||||
|
cur_sense_data.ascq=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur_cmd.data[0] = tb.transfer_buffer;
|
||||||
|
cur_cmd.data[1] = &tb.transfer_buffer[WRITE_BUFFER_SIZE];
|
||||||
|
cur_cmd.data_select=0;
|
||||||
|
cur_cmd.sector = block_size_mult *
|
||||||
|
((uint64_t)cbw->command_block[2] << 56 |
|
||||||
|
(uint64_t)cbw->command_block[3] << 48 |
|
||||||
|
(uint64_t)cbw->command_block[4] << 40 |
|
||||||
|
(uint64_t)cbw->command_block[5] << 32 |
|
||||||
|
cbw->command_block[6] << 24 |
|
||||||
|
cbw->command_block[7] << 16 |
|
||||||
|
cbw->command_block[8] << 8 |
|
||||||
|
cbw->command_block[9]);
|
||||||
|
cur_cmd.count = block_size_mult *
|
||||||
|
(cbw->command_block[10] << 24 |
|
||||||
|
cbw->command_block[11] << 16 |
|
||||||
|
cbw->command_block[12] << 8 |
|
||||||
|
cbw->command_block[13]);
|
||||||
|
cur_cmd.orig_count = cur_cmd.count;
|
||||||
|
|
||||||
|
/* expect data */
|
||||||
|
if((cur_cmd.sector + cur_cmd.count) > block_count) {
|
||||||
|
send_csw(UMS_STATUS_FAIL);
|
||||||
|
cur_sense_data.sense_key=SENSE_ILLEGAL_REQUEST;
|
||||||
|
cur_sense_data.asc=ASC_LBA_OUT_OF_RANGE;
|
||||||
|
cur_sense_data.ascq=0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
receive_block_data(cur_cmd.data[0],
|
||||||
|
MIN(WRITE_BUFFER_SIZE, cur_cmd.count*SECTOR_SIZE));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if CONFIG_RTC
|
#if CONFIG_RTC
|
||||||
case SCSI_WRITE_BUFFER:
|
case SCSI_WRITE_BUFFER:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue