/* 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; }
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; }