1
0
Fork 0
forked from len0rd/rockbox

MMC driver does now handle block sizes != 512 bytes, which is necessary to support cards > 1 GB. Changed error handling to use the same method as other parts of rockbox, allowing to trace the call chain. Long policy, code cleanup.

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6366 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Jens Arnold 2005-04-28 01:11:21 +00:00
parent 7e3f91d3d8
commit a7f7781dca
3 changed files with 357 additions and 166 deletions

View file

@ -1678,8 +1678,10 @@ bool dbg_mmc_info(void)
(int) mmc_extract_bits(card->cid, 0, 8), (int) mmc_extract_bits(card->cid, 0, 8),
(int) mmc_extract_bits(card->cid, 8, 16)); (int) mmc_extract_bits(card->cid, 8, 16));
lcd_puts(0, 4, pbuf); lcd_puts(0, 4, pbuf);
snprintf(pbuf, sizeof(pbuf), "Sectors: %08x", card->numsectors); snprintf(pbuf, sizeof(pbuf), "Blocks: %08lx", card->numblocks);
lcd_puts(0, 5, pbuf); lcd_puts(0, 5, pbuf);
snprintf(pbuf, sizeof(pbuf), "Blocksize: %d", card->blocksize);
lcd_puts(0, 6, pbuf);
} }
else /* Technical details */ else /* Technical details */
{ {

View file

@ -35,6 +35,7 @@
#include "disk.h" /* for mount/unmount */ #include "disk.h" /* for mount/unmount */
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
#define MAX_BLOCK_SIZE 2048
/* Command definitions */ /* Command definitions */
#define CMD_GO_IDLE_STATE 0x40 /* R1 */ #define CMD_GO_IDLE_STATE 0x40 /* R1 */
@ -43,6 +44,7 @@
#define CMD_SEND_CID 0x4a /* R1 */ #define CMD_SEND_CID 0x4a /* R1 */
#define CMD_STOP_TRANSMISSION 0x4c /* R1 */ #define CMD_STOP_TRANSMISSION 0x4c /* R1 */
#define CMD_SEND_STATUS 0x4d /* R2 */ #define CMD_SEND_STATUS 0x4d /* R2 */
#define CMD_SET_BLOCKLEN 0x50 /* R1 */
#define CMD_READ_SINGLE_BLOCK 0x51 /* R1 */ #define CMD_READ_SINGLE_BLOCK 0x51 /* R1 */
#define CMD_READ_MULTIPLE_BLOCK 0x52 /* R1 */ #define CMD_READ_MULTIPLE_BLOCK 0x52 /* R1 */
#define CMD_WRITE_BLOCK 0x58 /* R1b */ #define CMD_WRITE_BLOCK 0x58 /* R1b */
@ -75,15 +77,13 @@
/* Data start tokens */ /* Data start tokens */
#define DT_START_BLOCK 0xfe #define DT_START_BLOCK 0xfe
#define DT_START_WRITE_MULTIPLE 0xfc #define DT_START_WRITE_MULTIPLE 0xfc
#define DT_STOP_TRAN 0xfd #define DT_STOP_TRAN 0xfd
/* for compatibility */ /* for compatibility */
bool old_recorder = false; /* FIXME: get rid of this cross-dependency */ bool old_recorder = false; /* FIXME: get rid of this cross-dependency */
int ata_spinup_time = 0; int ata_spinup_time = 0;
char ata_device = 0; /* device 0 (master) or 1 (slave) */
int ata_io_address = 0; /* 0x300 or 0x200, only valid on recorder */
long last_disk_activity = -1; long last_disk_activity = -1;
/* private variables */ /* private variables */
@ -91,7 +91,7 @@ long last_disk_activity = -1;
static struct mutex mmc_mutex; static struct mutex mmc_mutex;
#ifdef HAVE_HOTSWAP #ifdef HAVE_HOTSWAP
static long mmc_stack[DEFAULT_STACK_SIZE/sizeof(long)]; static long mmc_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
static const char mmc_thread_name[] = "mmc"; static const char mmc_thread_name[] = "mmc";
static struct event_queue mmc_queue; static struct event_queue mmc_queue;
#endif #endif
@ -111,10 +111,22 @@ static const unsigned char dummy[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
}; };
/* 2 buffers for writing, include start token and dummy crc and an extra struct block_cache_entry {
* byte to keep word alignment */ bool inuse;
static unsigned char sector_buffer[2][(SECTOR_SIZE+4)]; #ifdef HAVE_MULTIVOLUME
static int current_buffer = 0; int drive;
#endif
unsigned long blocknum;
unsigned char data[MAX_BLOCK_SIZE+4];
/* include start token, dummy crc, and an extra byte at the start
* to keep the data word aligned. */
};
/* 2 buffers used alternatively for writing, and also for reading
* and sub-block writing if block size > sector size */
#define NUMCACHES 2
static struct block_cache_entry block_cache[NUMCACHES];
static int current_cache = 0;
static tCardInfo card_info[2]; static tCardInfo card_info[2];
#ifndef HAVE_MULTIVOLUME #ifndef HAVE_MULTIVOLUME
@ -135,17 +147,19 @@ static void write_transfer(const unsigned char *buf, int len)
__attribute__ ((section(".icode"))); __attribute__ ((section(".icode")));
static void read_transfer(unsigned char *buf, int len) static void read_transfer(unsigned char *buf, int len)
__attribute__ ((section(".icode"))); __attribute__ ((section(".icode")));
static unsigned char poll_byte(int timeout); static unsigned char poll_byte(long timeout);
static unsigned char poll_busy(int timeout); static unsigned char poll_busy(long timeout);
static int send_cmd(int cmd, unsigned long parameter, unsigned char *response); static int send_cmd(int cmd, unsigned long parameter, unsigned char *response);
static int receive_cxd(unsigned char *buf); static int receive_cxd(unsigned char *buf);
static int initialize_card(int card_no); static int initialize_card(int card_no);
static int receive_sector(unsigned char *inbuf, unsigned char *swapbuf, static void swapcopy(void *dst, const void *src, unsigned long size);
int timeout); static int receive_block(unsigned char *inbuf, unsigned char *swapbuf,
static void swapcopy_sector(const unsigned char *buf); int size, long timeout);
static int send_sector(const unsigned char *nextbuf, int timeout); static void swapcopy_block(const unsigned char *buf, int size);
static int send_single_sector(const unsigned char *buf, int timeout); static int send_block(const unsigned char *nextbuf, int size,
unsigned char start_token, long timeout);
static int cache_block(IF_MV2(int drive,) unsigned long blocknum,
int size, long timeout);
static void mmc_tick(void); static void mmc_tick(void);
/* implementation */ /* implementation */
@ -227,9 +241,9 @@ static void write_transfer(const unsigned char *buf, int len)
if (serial_mode != SER_POLL_WRITE) if (serial_mode != SER_POLL_WRITE)
{ {
while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */
SCR1 = 0; /* disable transmitter & receiver */ SCR1 = 0; /* disable transmitter & receiver */
SSR1 = 0; /* clear all flags */ SSR1 = 0; /* clear all flags */
SCR1 = SCI_TE; /* enable transmitter only */ SCR1 = SCI_TE; /* enable transmitter only */
serial_mode = SER_POLL_WRITE; serial_mode = SER_POLL_WRITE;
} }
@ -264,9 +278,9 @@ static void read_transfer(unsigned char *buf, int len)
} }
/* returns 0xFF on timeout, timeout is in bytes */ /* returns 0xFF on timeout, timeout is in bytes */
static unsigned char poll_byte(int timeout) static unsigned char poll_byte(long timeout)
{ {
int i; long i;
unsigned char data = 0; /* stop the compiler complaining */ unsigned char data = 0; /* stop the compiler complaining */
if (serial_mode != SER_POLL_READ) if (serial_mode != SER_POLL_READ)
@ -283,9 +297,9 @@ static unsigned char poll_byte(int timeout)
} }
/* returns 0 on timeout, timeout is in bytes */ /* returns 0 on timeout, timeout is in bytes */
static unsigned char poll_busy(int timeout) static unsigned char poll_busy(long timeout)
{ {
int i; long i;
unsigned char data, dummy; unsigned char data, dummy;
if (serial_mode != SER_POLL_READ) if (serial_mode != SER_POLL_READ)
@ -329,7 +343,7 @@ static int send_cmd(int cmd, unsigned long parameter, unsigned char *response)
if (response[0] != 0x00) if (response[0] != 0x00)
{ {
write_transfer(dummy, 1); write_transfer(dummy, 1);
return -10; return -1;
} }
switch (cmd) switch (cmd)
@ -364,7 +378,7 @@ static int receive_cxd(unsigned char *buf)
if (poll_byte(20) != DT_START_BLOCK) if (poll_byte(20) != DT_START_BLOCK)
{ {
write_transfer(dummy, 1); write_transfer(dummy, 1);
return -11; /* not start of data */ return -1; /* not start of data */
} }
read_transfer(buf, 16); read_transfer(buf, 16);
@ -408,7 +422,7 @@ unsigned long mmc_extract_bits(
static int initialize_card(int card_no) static int initialize_card(int card_no)
{ {
int i, temp; int rc, i, temp;
unsigned char response[5]; unsigned char response[5];
tCardInfo *card = &card_info[card_no]; tCardInfo *card = &card_info[card_no];
@ -437,24 +451,39 @@ static int initialize_card(int card_no)
return -2; /* not ready */ return -2; /* not ready */
/* get OCR register */ /* get OCR register */
if (send_cmd(CMD_READ_OCR, 0, response)) rc = send_cmd(CMD_READ_OCR, 0, response);
return -3; if (rc)
card->ocr = (response[1] << 24) + (response[2] << 16) return rc * 10 - 3;
+ (response[3] << 8) + response[4]; card->ocr = (response[1] << 24) | (response[2] << 16)
| (response[3] << 8) | response[4];
/* check voltage */ /* check voltage */
if (!(card->ocr & 0x00100000)) /* 3.2 .. 3.3 V */ if (!(card->ocr & 0x00100000)) /* 3.2 .. 3.3 V */
return -4; return -4;
/* get CSD register */ /* get CSD register */
if (send_cmd(CMD_SEND_CSD, 0, response)) rc = send_cmd(CMD_SEND_CSD, 0, response);
return -5; if (rc)
if (receive_cxd((unsigned char*)card->csd)) return rc * 10 - 5;
return -6; rc = receive_cxd((unsigned char*)card->csd);
if (rc)
return rc * 10 - 6;
/* check block size */ /* check block sizes */
if ((1 << mmc_extract_bits(card->csd, 44, 4)) != SECTOR_SIZE) card->block_exp = mmc_extract_bits(card->csd, 44, 4);
card->blocksize = 1 << card->block_exp;
if ((mmc_extract_bits(card->csd, 102, 4) != card->block_exp)
|| card->blocksize > MAX_BLOCK_SIZE)
{
return -7; return -7;
}
if (card->blocksize != SECTOR_SIZE)
{
rc = send_cmd(CMD_SET_BLOCKLEN, card->blocksize, response);
if (rc)
return rc * 10 - 8;
}
/* max transmission speed, clock divider */ /* max transmission speed, clock divider */
temp = mmc_extract_bits(card->csd, 29, 3); temp = mmc_extract_bits(card->csd, 29, 3);
@ -474,23 +503,30 @@ static int initialize_card(int card_no)
card->tsac = card->tsac * exponent[temp] / 10; card->tsac = card->tsac * exponent[temp] / 10;
/* r2w_factor, write timeout */ /* r2w_factor, write timeout */
temp = mmc_extract_bits(card->csd, 99, 3); card->r2w_factor = 1 << mmc_extract_bits(card->csd, 99, 3);
temp = (temp > 5) ? 5 : temp; if (card->r2w_factor > 32) /* dirty MMC spec violation */
card->r2w_factor = 1 << temp; {
card->write_timeout = card->read_timeout * card->r2w_factor; card->read_timeout *= 4; /* add safety factor */
card->write_timeout = card->read_timeout * 8;
}
else
card->write_timeout = card->read_timeout * card->r2w_factor;
/* card size */ /* card size */
card->numsectors = (mmc_extract_bits(card->csd, 54, 12) + 1) card->numblocks = (mmc_extract_bits(card->csd, 54, 12) + 1)
* (1 << (mmc_extract_bits(card->csd, 78, 3)+2)); * (1 << (mmc_extract_bits(card->csd, 78, 3) + 2));
card->size = card->numblocks * card->blocksize;
/* switch to full speed */ /* switch to full speed */
setup_sci1(card->bitrate_register); setup_sci1(card->bitrate_register);
/* get CID register */ /* get CID register */
if (send_cmd(CMD_SEND_CID, 0, response)) rc = send_cmd(CMD_SEND_CID, 0, response);
return -8; if (rc)
if (receive_cxd((unsigned char*)card->cid)) return rc * 10 - 9;
return -9; rc = receive_cxd((unsigned char*)card->cid);
if (rc)
return rc * 10 - 9;
card->initialized = true; card->initialized = true;
return 0; return 0;
@ -510,17 +546,23 @@ tCardInfo *mmc_card_info(int card_no)
return card; return card;
} }
/* Receive one sector with dma, possibly swapping the previously received static void swapcopy(void *dst, const void *src, unsigned long size)
* sector in the background */ {
static int receive_sector(unsigned char *inbuf, unsigned char *swapbuf, memcpy(dst, src, size);
int timeout) bitswap(dst, size);
}
/* Receive one block with dma, possibly swapping the previously received
* block in the background */
static int receive_block(unsigned char *inbuf, unsigned char *swapbuf,
int size, long timeout)
{ {
if (poll_byte(timeout) != DT_START_BLOCK) if (poll_byte(timeout) != DT_START_BLOCK)
{ {
write_transfer(dummy, 1); write_transfer(dummy, 1);
return -12; /* not start of data */ return -1; /* not start of data */
} }
while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */
SCR1 = 0; /* disable serial */ SCR1 = 0; /* disable serial */
@ -530,7 +572,7 @@ static int receive_sector(unsigned char *inbuf, unsigned char *swapbuf,
CHCR2 = 0; /* disable */ CHCR2 = 0; /* disable */
SAR2 = RDR1_ADDR; SAR2 = RDR1_ADDR;
DAR2 = (unsigned long) inbuf; DAR2 = (unsigned long) inbuf;
DTCR2 = SECTOR_SIZE; DTCR2 = size;
CHCR2 = 0x4601; /* fixed source address, RXI1, enable */ CHCR2 = 0x4601; /* fixed source address, RXI1, enable */
DMAOR = 0x0001; DMAOR = 0x0001;
SCR1 = (SCI_RE|SCI_RIE); /* kick off DMA */ SCR1 = (SCI_RE|SCI_RIE); /* kick off DMA */
@ -540,8 +582,8 @@ static int receive_sector(unsigned char *inbuf, unsigned char *swapbuf,
* the second one is lost because of the SCI overrun. However, this * the second one is lost because of the SCI overrun. However, this
* behaviour conveniently discards the crc. */ * behaviour conveniently discards the crc. */
if (swapbuf != NULL) /* bitswap previous sector */ if (swapbuf != NULL) /* bitswap previous block */
bitswap(swapbuf, SECTOR_SIZE); bitswap(swapbuf, size);
yield(); /* be nice */ yield(); /* be nice */
while (!(CHCR2 & 0x0002)); /* wait for end of DMA */ while (!(CHCR2 & 0x0002)); /* wait for end of DMA */
@ -553,26 +595,25 @@ static int receive_sector(unsigned char *inbuf, unsigned char *swapbuf,
return 0; return 0;
} }
/* copies one sector into the next-current write buffer, then bitswaps */ /* copies one block into the next-current block cache, then bitswaps */
static void swapcopy_sector(const unsigned char *buf) static void swapcopy_block(const unsigned char *buf, int size)
{ {
unsigned char *curbuf; current_cache = (current_cache + 1) % NUMCACHES; /* next cache */
current_buffer ^= 1; /* toggles between 0 and 1 */ block_cache[current_cache].inuse = false;
swapcopy(block_cache[current_cache].data + 2, buf, size);
curbuf = sector_buffer[current_buffer];
curbuf[1] = DT_START_WRITE_MULTIPLE;
curbuf[(SECTOR_SIZE+2)] = curbuf[(SECTOR_SIZE+3)] = 0xFF; /* dummy crc */
memcpy(curbuf + 2, buf, SECTOR_SIZE);
bitswap(curbuf + 1, (SECTOR_SIZE+1));
} }
/* Send one sector with dma from the current sector buffer, possibly preparing /* Send one block with dma from the current block cache, possibly preparing
* the next sector within the other sector buffer in the background. Use * the next block within the next block cache in the background. */
* for multisector transfer only */ static int send_block(const unsigned char *nextbuf, int size,
static int send_sector(const unsigned char *nextbuf, int timeout) unsigned char start_token, long timeout)
{ {
int ret = 0; int rc = 0;
unsigned char *curbuf = block_cache[current_cache].data;
curbuf[1] = fliptable[(signed char)start_token];
*(unsigned short *)(curbuf + size + 2) = 0xFFFF;
while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */ while (!(SSR1 & SCI_TEND)); /* wait for end of transfer */
@ -581,15 +622,15 @@ static int send_sector(const unsigned char *nextbuf, int timeout)
/* setup DMA channel 2 */ /* setup DMA channel 2 */
CHCR2 = 0; /* disable */ CHCR2 = 0; /* disable */
SAR2 = (unsigned long)(sector_buffer[current_buffer] + 1); SAR2 = (unsigned long)(curbuf + 1);
DAR2 = TDR1_ADDR; DAR2 = TDR1_ADDR;
DTCR2 = (SECTOR_SIZE+3); DTCR2 = size + 3; /* start token + block + dummy crc */
CHCR2 = 0x1701; /* fixed dest. address, TXI1, enable */ CHCR2 = 0x1701; /* fixed dest. address, TXI1, enable */
DMAOR = 0x0001; DMAOR = 0x0001;
SCR1 = (SCI_TE|SCI_TIE); /* kick off DMA */ SCR1 = (SCI_TE|SCI_TIE); /* kick off DMA */
if (nextbuf != NULL) /* prepare next sector */ if (nextbuf != NULL) /* prepare next sector */
swapcopy_sector(nextbuf); swapcopy_block(nextbuf, size);
yield(); /* be nice */ yield(); /* be nice */
while (!(CHCR2 & 0x0002)); /* wait for end of DMA */ while (!(CHCR2 & 0x0002)); /* wait for end of DMA */
@ -598,29 +639,52 @@ static int send_sector(const unsigned char *nextbuf, int timeout)
serial_mode = SER_DISABLED; serial_mode = SER_DISABLED;
if ((poll_busy(timeout) & 0x1F) != 0x05) /* something went wrong */ if ((poll_busy(timeout) & 0x1F) != 0x05) /* something went wrong */
ret = -13; rc = -1;
write_transfer(dummy, 1); write_transfer(dummy, 1);
return ret; return rc;
} }
/* Send one sector with polled i/o. Use for single sector transfers only. */ static int cache_block(IF_MV2(int drive,) unsigned long blocknum,
static int send_single_sector(const unsigned char *buf, int timeout) int size, long timeout)
{ {
int ret = 0; int rc, i;
unsigned char start_token = DT_START_BLOCK; unsigned char response;
/* check whether the block is already cached */
for (i = 0; i < NUMCACHES; i++)
{
if (block_cache[i].inuse && (block_cache[i].blocknum == blocknum)
#ifdef HAVE_MULTIVOLUME
&& (block_cache[i].drive == drive)
#endif
)
{
current_cache = i;
return 0;
}
}
/* not found: read the block */
current_cache = (current_cache + 1) % NUMCACHES;
rc = send_cmd(CMD_READ_SINGLE_BLOCK, blocknum * size, &response);
if (rc)
return rc * 10 - 1;
write_transfer(&start_token, 1); block_cache[current_cache].inuse = false;
write_transfer(buf, SECTOR_SIZE); rc = receive_block(block_cache[current_cache].data + 2, NULL,
write_transfer(dummy, 2); /* crc - dontcare */ size, timeout);
if (rc)
return rc * 10 - 2;
if ((poll_busy(timeout) & 0x1F) != 0x05) /* something went wrong */ #ifdef HAVE_MULTIVOLUME
ret = -14; block_cache[current_cache].drive = drive;
#endif
block_cache[current_cache].blocknum = blocknum;
block_cache[current_cache].inuse = true;
last_disk_activity = current_tick;
write_transfer(dummy, 1); return 0;
return ret;
} }
int ata_read_sectors(IF_MV2(int drive,) int ata_read_sectors(IF_MV2(int drive,)
@ -628,75 +692,120 @@ int ata_read_sectors(IF_MV2(int drive,)
int incount, int incount,
void* inbuf) void* inbuf)
{ {
int ret = 0; int rc = 0;
int last_sector; unsigned int blocksize, offset;
unsigned long addr; unsigned long c_addr, c_end_addr;
unsigned long c_block, c_end_block;
unsigned char response; unsigned char response;
void *inbuf_prev = NULL; void *inbuf_prev = NULL;
tCardInfo *card; tCardInfo *card;
addr = start * SECTOR_SIZE; c_addr = start * SECTOR_SIZE;
c_end_addr = c_addr + incount * SECTOR_SIZE;
mutex_lock(&mmc_mutex); mutex_lock(&mmc_mutex);
led(true); led(true);
#ifdef HAVE_MULTIVOLUME #ifdef HAVE_MULTIVOLUME
card = &card_info[drive]; card = &card_info[drive];
ret = select_card(drive); rc = select_card(drive);
#else #else
card = &card_info[current_card]; card = &card_info[current_card];
ret = select_card(current_card); rc = select_card(current_card);
#endif #endif
if (start + incount > card->numsectors) if (rc)
{ {
ret = -15; rc = rc * 10 - 1;
/* panicf("Reading %d@%d, past end of card %d\n", goto error;
incount, start, card->numsectors); */ }
if (c_end_addr > card->size)
{
rc = -2;
goto error;
} }
blocksize = card->blocksize;
offset = c_addr & (blocksize - 1);
c_block = c_addr >> card->block_exp;
c_end_block = c_end_addr >> card->block_exp;
if (offset) /* first partial block */
{
unsigned long len = MIN(c_end_addr - c_addr, blocksize - offset);
rc = cache_block(IF_MV2(drive,) c_block, blocksize,
card->read_timeout);
if (rc)
{
rc = rc * 10 - 3;
goto error;
}
swapcopy(inbuf, block_cache[current_cache].data + 2 + offset, len);
inbuf += len;
c_addr += len;
c_block++;
}
/* some cards don't like reading the very last sector with /* some cards don't like reading the very last sector with
* CMD_READ_MULTIPLE_BLOCK, so make sure this sector is always * CMD_READ_MULTIPLE_BLOCK, so make sure this sector is always
* read with CMD_READ_SINGLE_BLOCK. */ * read with CMD_READ_SINGLE_BLOCK. This is caught by the 'last
last_sector = (start + incount == card->numsectors) ? 1 : 0; * partial block' read. */
if (c_end_block == card->numblocks)
c_end_block--;
if (ret == 0) if (c_block < c_end_block)
{ {
if (incount > 1) rc = send_cmd(CMD_READ_MULTIPLE_BLOCK, c_addr, &response);
if (rc)
{ {
ret = send_cmd(CMD_READ_MULTIPLE_BLOCK, addr, &response); rc = rc * 10 - 4;
for (; (incount > last_sector) && (ret == 0); incount--) goto error;
{
ret = receive_sector(inbuf, inbuf_prev, card->read_timeout);
inbuf_prev = inbuf;
inbuf += SECTOR_SIZE;
last_disk_activity = current_tick;
}
if (ret == 0)
ret = send_cmd(CMD_STOP_TRANSMISSION, 0, &response);
} }
if (incount && (ret == 0)) while (c_block < c_end_block)
{ {
ret = send_cmd(CMD_READ_SINGLE_BLOCK, addr, &response); rc = receive_block(inbuf, inbuf_prev, blocksize,
if (ret == 0) card->read_timeout);
if (rc)
{ {
ret = receive_sector(inbuf, inbuf_prev, card->read_timeout); rc = rc * 10 - 5;
inbuf_prev = inbuf; goto error;
last_disk_activity = current_tick;
} }
last_disk_activity = current_tick;
inbuf_prev = inbuf;
inbuf += blocksize;
c_addr += blocksize;
c_block++;
} }
rc = send_cmd(CMD_STOP_TRANSMISSION, 0, &response);
if (ret == 0) if (rc)
bitswap(inbuf_prev, SECTOR_SIZE); {
rc = rc * 10 - 6;
goto error;
}
bitswap(inbuf_prev, blocksize);
} }
if (c_addr < c_end_addr) /* last partial block */
{
rc = cache_block(IF_MV2(drive,) c_block, blocksize,
card->read_timeout);
if (rc)
{
rc = rc * 10 - 7;
goto error;
}
swapcopy(inbuf, block_cache[current_cache].data + 2,
c_end_addr - c_addr);
}
error:
deselect_card(); deselect_card();
led(false); led(false);
mutex_unlock(&mmc_mutex); mutex_unlock(&mmc_mutex);
/* only flush if reading went ok */ /* only flush if reading went ok */
if ( (ret == 0) && delayed_write ) if ( (rc == 0) && delayed_write )
ata_flush(); ata_flush();
return ret; return rc;
} }
int ata_write_sectors(IF_MV2(int drive,) int ata_write_sectors(IF_MV2(int drive,)
@ -704,70 +813,145 @@ int ata_write_sectors(IF_MV2(int drive,)
int count, int count,
const void* buf) const void* buf)
{ {
int ret = 0; int rc = 0;
unsigned long addr; unsigned int blocksize, offset;
unsigned long c_addr, c_end_addr;
unsigned long c_block, c_end_block;
unsigned char response; unsigned char response;
tCardInfo *card; tCardInfo *card;
if (start == 0) if (start == 0)
panicf("Writing on sector 0\n"); panicf("Writing on sector 0\n");
addr = start * SECTOR_SIZE; c_addr = start * SECTOR_SIZE;
c_end_addr = c_addr + count * SECTOR_SIZE;
mutex_lock(&mmc_mutex); mutex_lock(&mmc_mutex);
led(true); led(true);
#ifdef HAVE_MULTIVOLUME #ifdef HAVE_MULTIVOLUME
card = &card_info[drive]; card = &card_info[drive];
ret = select_card(drive); rc = select_card(drive);
#else #else
card = &card_info[current_card]; card = &card_info[current_card];
ret = select_card(current_card); rc = select_card(current_card);
#endif #endif
if (start + count > card->numsectors) if (rc)
{
rc = rc * 10 - 1;
goto error;
}
if (c_end_addr > card->size)
panicf("Writing past end of card\n"); panicf("Writing past end of card\n");
if (ret == 0) blocksize = card->blocksize;
offset = c_addr & (blocksize - 1);
c_block = c_addr >> card->block_exp;
c_end_block = c_end_addr >> card->block_exp;
if (offset) /* first partial block */
{ {
if (count == 1) unsigned long len = MIN(c_end_addr - c_addr, blocksize - offset);
rc = cache_block(IF_MV2(drive,) c_block, blocksize,
card->read_timeout);
if (rc)
{ {
ret = send_cmd(CMD_WRITE_BLOCK, addr, &response); rc = rc * 10 - 2;
if (ret == 0) goto error;
ret = send_single_sector(buf, card->write_timeout);
last_disk_activity = current_tick;
} }
else swapcopy(block_cache[current_cache].data + 2 + offset, buf, len);
rc = send_cmd(CMD_WRITE_BLOCK, c_addr - offset, &response);
if (rc)
{ {
swapcopy_sector(buf); /* prepare first sector */ rc = rc * 10 - 3;
ret = send_cmd(CMD_WRITE_MULTIPLE_BLOCK, addr, &response); goto error;
for (; (count > 1) && (ret == 0); count--) }
buf += len;
rc = send_block(NULL, blocksize, DT_START_BLOCK, card->write_timeout);
if (rc)
{
rc = rc * 10 - 4;
goto error;
}
c_addr += len;
c_block++;
}
if (c_block < c_end_block)
{
swapcopy_block(buf, blocksize);
rc = send_cmd(CMD_WRITE_MULTIPLE_BLOCK, c_addr, &response);
if (rc)
{
rc = rc * 10 - 5;
goto error;
}
while (c_block < c_end_block - 1)
{
buf += blocksize;
rc = send_block(buf, blocksize, DT_START_WRITE_MULTIPLE,
card->write_timeout);
if (rc)
{ {
buf += SECTOR_SIZE; rc = rc * 10 - 6;
ret = send_sector(buf, card->write_timeout); goto error;
last_disk_activity = current_tick;
}
if (ret == 0)
{
ret = send_sector(NULL, card->write_timeout);
if (ret == 0)
{
response = DT_STOP_TRAN;
write_transfer(&response, 1);
poll_busy(card->write_timeout);
}
last_disk_activity = current_tick;
} }
last_disk_activity = current_tick;
c_addr += blocksize;
c_block++;
}
buf += blocksize;
rc = send_block(NULL, blocksize, DT_START_WRITE_MULTIPLE,
card->write_timeout);
if (rc)
{
rc = rc * 10 - 7;
goto error;
}
last_disk_activity = current_tick;
c_addr += blocksize;
c_block++;
response = DT_STOP_TRAN;
write_transfer(&response, 1);
poll_busy(card->write_timeout);
}
if (c_addr < c_end_addr) /* last partial block */
{
rc = cache_block(IF_MV2(drive,) c_block, blocksize,
card->read_timeout);
if (rc)
{
rc = rc * 10 - 8;
goto error;
}
swapcopy(block_cache[current_cache].data + 2, buf,
c_end_addr - c_addr);
rc = send_cmd(CMD_WRITE_BLOCK, c_addr, &response);
if (rc)
{
rc = rc * 10 - 9;
goto error;
}
rc = send_block(NULL, blocksize, DT_START_BLOCK, card->write_timeout);
if (rc)
{
rc = rc * 10 - 9;
goto error;
} }
} }
error:
deselect_card(); deselect_card();
led(false); led(false);
mutex_unlock(&mmc_mutex); mutex_unlock(&mmc_mutex);
/* only flush if writing went ok */ /* only flush if writing went ok */
if ( (ret == 0) && delayed_write ) if ( (rc == 0) && delayed_write )
ata_flush(); ata_flush();
return ret; return rc;
} }
/* While there is no spinup, the delayed write is still here to avoid /* While there is no spinup, the delayed write is still here to avoid
@ -782,7 +966,8 @@ extern void ata_delayed_write(unsigned long sector, const void* buf)
/* write the delayed sector to volume 0 */ /* write the delayed sector to volume 0 */
extern void ata_flush(void) extern void ata_flush(void)
{ {
if ( delayed_write ) { if ( delayed_write )
{
DEBUGF("ata_flush()\n"); DEBUGF("ata_flush()\n");
delayed_write = false; delayed_write = false;
ata_write_sectors(IF_MV2(0,) delayed_sector_num, 1, delayed_sector); ata_write_sectors(IF_MV2(0,) delayed_sector_num, 1, delayed_sector);
@ -823,7 +1008,8 @@ static void mmc_thread(void)
while (1) { while (1) {
queue_wait(&mmc_queue, &ev); queue_wait(&mmc_queue, &ev);
switch ( ev.id ) { switch ( ev.id )
{
case SYS_USB_CONNECTED: case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK); usb_acknowledge(SYS_USB_CONNECTED_ACK);
/* Wait until the USB cable is extracted again */ /* Wait until the USB cable is extracted again */

View file

@ -23,17 +23,20 @@ typedef struct
{ {
bool initialized; bool initialized;
unsigned char bitrate_register; unsigned char bitrate_register;
unsigned int read_timeout; /* n * 8 clock cycles */ unsigned long read_timeout; /* n * 8 clock cycles */
unsigned int write_timeout; /* n * 8 clock cycles */ unsigned long write_timeout; /* n * 8 clock cycles */
unsigned long ocr; /* OCR register */ unsigned long ocr; /* OCR register */
unsigned long csd[4]; /* CSD register, 16 bytes */ unsigned long csd[4]; /* CSD register, 16 bytes */
unsigned long cid[4]; /* CID register, 16 bytes */ unsigned long cid[4]; /* CID register, 16 bytes */
unsigned int speed; /* bit/s */ unsigned long speed; /* bit/s */
unsigned int nsac; /* clock cycles */ unsigned int nsac; /* clock cycles */
unsigned int tsac; /* n * 0.1 ns */ unsigned long tsac; /* n * 0.1 ns */
unsigned int r2w_factor; unsigned int r2w_factor;
unsigned int numsectors; /* size in sectors */ unsigned long size; /* size in bytes */
unsigned long numblocks; /* size in flash blocks */
unsigned int blocksize; /* block size in bytes */
unsigned int block_exp; /* block size exponent */
} tCardInfo; } tCardInfo;
void mmc_select_clock(int card_no); void mmc_select_clock(int card_no);