/* Receive one block with DMA and bitswap it (chasing bitswap). */
static int receive_block(unsigned char *inbuf, long timeout)
{
    unsigned long buf_end;

    if (poll_byte(timeout) != DT_START_BLOCK)
    {
        write_transfer(dummy, 1);
        return -1;                /* not start of data */
    }

    while (!(SSR1 & SCI_TEND));   /* wait for end of transfer */

    SCR1 = 0;                     /* disable serial */
    SSR1 = 0;                     /* clear all flags */
    
    /* setup DMA channel 0 */
    CHCR0 = 0;                    /* disable */
    SAR0 = RDR1_ADDR;
    DAR0 = (unsigned long) inbuf;
    DTCR0 = BLOCK_SIZE;
    CHCR0 = 0x4601;               /* fixed source address, RXI1, enable */
    DMAOR = 0x0001;
    SCR1 = (SCI_RE|SCI_RIE);      /* kick off DMA */

    /* DMA receives 2 bytes more than DTCR2, but the last 2 bytes are not
     * stored. The first extra byte is available from RDR1 after the DMA ends,
     * the second one is lost because of the SCI overrun. However, this
     * behaviour conveniently discards the crc. */

    yield();                      /* be nice */
    
    /* Bitswap received data, chasing the DMA pointer */
    buf_end = (unsigned long)inbuf + BLOCK_SIZE;
    do
    {
        /* Call bitswap whenever (a multiple of) 8 bytes are
         * available (value optimised by experimentation). */
        int swap_now = (DAR0 - (unsigned long)inbuf) & ~0x00000007;
        if (swap_now)
        {
            bitswap(inbuf, swap_now);
            inbuf += swap_now;
        }
    }
    while ((unsigned long)inbuf < buf_end);

    while (!(CHCR0 & 0x0002));    /* wait for end of DMA */
    while (!(SSR1 & SCI_ORER));   /* wait for the trailing bytes */
    SCR1 = 0;
    serial_mode = SER_DISABLED;

    write_transfer(dummy, 1);     /* send trailer */
    last_disk_activity = current_tick;
    return 0;
}
/* Receive CID/ CSD data (16 bytes) */
static int receive_cxd(unsigned char *buf)
{
    if (poll_byte(20) != DT_START_BLOCK)
    {
        write_transfer(dummy, 1);
        return -1;                /* not start of data */
    }
    
    read_transfer(buf, 16);
    write_transfer(dummy, 3);     /* 2 bytes dontcare crc + 1 byte trailer */
    return 0;
}
/* Send MMC command and get response. Returns R1 byte directly.
 * Returns further R2 or R3 bytes in *data (can be NULL for other commands) */
static unsigned char send_cmd(int cmd, unsigned long parameter, void *data)
{
    static struct {
        unsigned char cmd;
        unsigned long parameter;
        const unsigned char crc7;    /* fixed, valid for CMD0 only */
        const unsigned char trailer;
    } __attribute__((packed)) command = {0x40, 0, 0x95, 0xFF};

    unsigned char ret;

    command.cmd = cmd;
    command.parameter = htobe32(parameter);

    write_transfer((unsigned char *)&command, sizeof(command));

    ret = poll_byte(20);

    switch (cmd)
    {
        case CMD_SEND_CSD:        /* R1 response, leave open */
        case CMD_SEND_CID:
        case CMD_READ_SINGLE_BLOCK:
        case CMD_READ_MULTIPLE_BLOCK:
            return ret;

        case CMD_SEND_STATUS:     /* R2 response, close with dummy */
            read_transfer(data, 1);
            break;

        case CMD_READ_OCR:        /* R3 response, close with dummy */
            read_transfer(data, 4);
            break;

        default:                  /* R1 response, close with dummy */
            break;                /* also catches block writes */
    }
    write_transfer(dummy, 1);
    return ret;
}
Beispiel #4
0
int poll_event(uint32_t *event)
{
	while(poll_byte())
	{
		if(last_byte == 0x00 || last_byte == 0xEE || last_byte >= 0xFA)
			port_state = IDLE;
		else if(last_byte == 0xE0)
			port_state = RECEIVED_E0;
		else if(last_byte == 0xF0)
			port_state = port_state == RECEIVED_E0 ? RECEIVED_E0F0 : RECEIVED_F0;
		else if(last_byte == 0xE1)
			port_state = RECEIVED_E1;
		else
		{
			uint32_t modifier = 0;
			if(port_state == IDLE)
				modifier = DOWN;
			else if(port_state == RECEIVED_E0)
				modifier = E0DOWN;
			else if(port_state == RECEIVED_F0)
				modifier = F0DOWN;
			else if(port_state == RECEIVED_E0F0)
				modifier = E0F0DOWN;
			else if(port_state == RECEIVED_E1)
				modifier = E1DOWN;
			if(last_byte & 0x80)
				key_state[last_byte & 0x7F] &= ~(1 << modifier);
			else
				key_state[last_byte & 0x7F] |= 1 << modifier;
			port_state = IDLE;
			*event = modifier << 8 | last_byte;
			outb(0x60, 0xEE);
			return 1;
		}
	}
	return 0;
}