From: Serge Vakulenko <serge.vakulenko@gmail.com>
To: qemu-devel@nongnu.org
Cc: Serge Vakulenko <serge.vakulenko@gmail.com>,
Leon Alrae <leon.alrae@imgtec.com>,
Aurelien Jarno <aurelien@aurel32.net>
Subject: [Qemu-devel] [PATCH pic32 v3 14/16] pic32: add file pic32_sdcard.c
Date: Sun, 5 Jul 2015 23:15:02 -0700 [thread overview]
Message-ID: <1436163304-6167-15-git-send-email-serge.vakulenko@gmail.com> (raw)
In-Reply-To: <1436163304-6167-1-git-send-email-serge.vakulenko@gmail.com>
Implement access to SD card, attached to pic32 SPI port.
Signed-off-by: Serge Vakulenko <serge.vakulenko@gmail.com>
---
hw/mips/pic32_sdcard.c | 428 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 428 insertions(+)
create mode 100644 hw/mips/pic32_sdcard.c
diff --git a/hw/mips/pic32_sdcard.c b/hw/mips/pic32_sdcard.c
new file mode 100644
index 0000000..7611233
--- /dev/null
+++ b/hw/mips/pic32_sdcard.c
@@ -0,0 +1,428 @@
+/*
+ * SD/MMC card emulation.
+ *
+ * Copyright (C) 2011-2015 Serge Vakulenko
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * and its documentation for any purpose and without fee is hereby
+ * granted, provided that the above copyright notice appear in all
+ * copies and that both that the copyright notice and this
+ * permission notice and warranty disclaimer appear in supporting
+ * documentation, and that the name of the author not be used in
+ * advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission.
+ *
+ * The author disclaim all warranties with regard to this
+ * software, including all implied warranties of merchantability
+ * and fitness. In no event shall the author be liable for any
+ * special, indirect or consequential damages or any damages
+ * whatsoever resulting from loss of use, data or profits, whether
+ * in an action of contract, negligence or other tortious action,
+ * arising out of or in connection with the use or performance of
+ * this software.
+ */
+#include "hw/hw.h"
+#include "pic32_peripherals.h"
+
+/*
+ * Definitions for MMC/SDC commands.
+ */
+#define CMD_GO_IDLE (0x40+0) /* CMD0 */
+#define CMD_SEND_OP_SDC (0x40+41) /* ACMD41 (SDC) */
+#define CMD_SET_BLEN (0x40+16)
+#define CMD_SEND_IF_COND (0x40+8)
+#define CMD_SEND_CSD (0x40+9)
+#define CMD_STOP (0x40+12)
+#define CMD_READ_SINGLE (0x40+17)
+#define CMD_READ_MULTIPLE (0x40+18)
+#define CMD_SET_WBECNT (0x40+23) /* ACMD23 */
+#define CMD_WRITE_SINGLE (0x40+24)
+#define CMD_WRITE_MULTIPLE (0x40+25)
+#define CMD_APP (0x40+55) /* CMD55 */
+
+#define DATA_START_BLOCK 0xFE /* start data for single block */
+#define STOP_TRAN_TOKEN 0xFD /* stop token for write multiple */
+#define WRITE_MULTIPLE_TOKEN 0xFC /* start data for write multiple */
+
+/*
+ * TODO: add option to enable tracing.
+ */
+#define TRACE 0
+
+static void read_data(int fd, unsigned offset,
+ unsigned char *buf, unsigned blen)
+{
+ int result;
+
+ /* Fill uninitialized blocks by FF: simulate real flash media. */
+ memset(buf, 0xFF, blen);
+
+ result = pread(fd, buf, blen, offset);
+ if (result < 0) {
+ printf("sdcard: pread failed, %u bytes, offset %#x: %s\n",
+ blen, offset, strerror(errno));
+ return;
+ }
+ if (result != blen) {
+ /* Partial file read, return zeroes. */
+ memset(buf, 0, blen);
+ }
+#if 0
+ printf("(%#x)\n", offset);
+ int i, k;
+ for (i = 0; i < blen; i += 32) {
+ for (k = 0; k < 32; k++) {
+ printf(" %02x", buf[i+k]);
+ }
+ printf("\n");
+ }
+#endif
+}
+
+static void write_data(int fd, unsigned offset,
+ unsigned char *buf, unsigned blen)
+{
+ if (pwrite(fd, buf, blen, offset) != blen) {
+ printf("sdcard: pwrite failed, offset %#x\n", offset);
+ return;
+ }
+}
+
+static void card_reset(sdcard_t *d)
+{
+ d->select = 0;
+ d->blen = 512;
+ d->count = 0;
+}
+
+/*
+ * Reset sdcard.
+ */
+void pic32_sdcard_reset(pic32_t *s)
+{
+ card_reset(&s->sdcard[0]);
+ card_reset(&s->sdcard[1]);
+}
+
+/*
+ * Initialize SD card.
+ */
+void pic32_sdcard_init(pic32_t *s, int unit, const char *name,
+ const char *filename, int cs_port, int cs_pin)
+{
+ sdcard_t *d = &s->sdcard[unit];
+ struct stat st;
+
+ memset(d, 0, sizeof(*d));
+ d->name = name;
+ if (!filename) {
+ /* No SD card installed. */
+ return;
+ }
+ d->gpio_port = cs_port;
+ d->gpio_cs = (cs_pin >= 0) ? (1 << cs_pin) : 0;
+
+ d->fd = open(filename, O_RDWR);
+ if (d->fd < 0) {
+ /* Fatal: no image available. */
+ perror(filename);
+ exit(1);
+ }
+ fstat(d->fd, &st);
+ d->kbytes = st.st_size / 1024;
+ printf("Card%u image '%s', %d kbytes\n", unit, filename, d->kbytes);
+ if (qemu_logfile) {
+ fprintf(qemu_logfile, "Card%u image '%s', %d kbytes\n",
+ unit, filename, d->kbytes);
+ }
+}
+
+void pic32_sdcard_select(pic32_t *s, int unit, int on)
+{
+ sdcard_t *d = &s->sdcard[unit];
+
+ if (on) {
+ d->select = 1;
+ d->count = 0;
+ } else {
+ d->select = 0;
+ }
+}
+
+/*
+ * Data i/o: send byte to device.
+ * Return received byte.
+ */
+unsigned pic32_sdcard_io(pic32_t *s, unsigned data)
+{
+ sdcard_t *d = s->sdcard[0].select ? &s->sdcard[0] :
+ s->sdcard[1].select ? &s->sdcard[1] : 0;
+ unsigned reply;
+
+ if (!d || !d->fd) {
+ /* Unselected i/o. */
+ return 0xFF;
+ }
+ data = (unsigned char) data;
+ reply = 0xFF;
+ if (d->count == 0) {
+ d->buf[0] = data;
+ if (data != 0xFF) {
+ d->count++;
+ }
+ } else {
+ switch (d->buf[0]) {
+ case CMD_GO_IDLE: /* CMD0: reset */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ reply = 0x01;
+ }
+ break;
+ case CMD_APP: /* CMD55: application prefix */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ reply = 0;
+ d->count = 0;
+ }
+ break;
+ case CMD_SEND_OP_SDC: /* ACMD41: initialization */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ reply = 0;
+ }
+ break;
+ case CMD_SET_BLEN: /* Set block length */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ d->blen = d->buf[1] << 24 | d->buf[2] << 16 |
+ d->buf[3] << 8 | d->buf[4];
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: set block length %u bytes\n",
+ d->unit, d->blen);
+ }
+ reply = (d->blen > 0 && d->blen <= 1024) ? 0 : 4;
+ }
+ break;
+ case CMD_SET_WBECNT: /* Set write block erase count */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ d->wbecnt = d->buf[1] << 24 | d->buf[2] << 16 |
+ d->buf[3] << 8 | d->buf[4];
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: set write block erase count %u\n",
+ d->unit, d->wbecnt);
+ }
+ reply = 0;
+ d->count = 0;
+ }
+ break;
+ case CMD_SEND_CSD: /* Get card data */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ /* Send reply */
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: send media size %u sectors\n",
+ d->unit, d->kbytes * 2);
+ }
+ reply = 0;
+ d->limit = 16 + 3;
+ d->count = 1;
+ d->buf[0] = 0;
+ d->buf[1] = DATA_START_BLOCK;
+ d->buf[2+0] = 1 << 6; /* SDC ver 2.00 */
+ d->buf[2+1] = 0;
+ d->buf[2+2] = 0;
+ d->buf[2+3] = 0;
+ d->buf[2+4] = 0;
+ d->buf[2+5] = 0;
+ d->buf[2+6] = 0;
+ d->buf[2+7] = 0;
+ d->buf[2+8] = (d->kbytes / 512 - 1) >> 8;
+ d->buf[2+9] = d->kbytes / 512 - 1;
+ d->buf[2+10] = 0;
+ d->buf[2+11] = 0;
+ d->buf[2+12] = 0;
+ d->buf[2+13] = 0;
+ d->buf[2+14] = 0;
+ d->buf[2+15] = 0;
+ d->buf[d->limit - 1] = 0xFF;
+ d->buf[d->limit] = 0xFF;
+ }
+ break;
+ case CMD_READ_SINGLE: /* Read block */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ /* Send reply */
+ reply = 0;
+ d->offset = d->buf[1] << 24 | d->buf[2] << 16 |
+ d->buf[3] << 8 | d->buf[4];
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: read offset %#x, length %u bytes\n",
+ d->unit, d->offset, d->blen);
+ }
+ d->limit = d->blen + 3;
+ d->count = 1;
+ d->buf[0] = 0;
+ d->buf[1] = DATA_START_BLOCK;
+ read_data(d->fd, d->offset, &d->buf[2], d->blen);
+ d->buf[d->limit - 1] = 0xFF;
+ d->buf[d->limit] = 0xFF;
+ }
+ break;
+ case CMD_READ_MULTIPLE: /* Read multiple blocks */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ /* Send reply */
+ reply = 0;
+ d->read_multiple = 1;
+ d->offset = d->buf[1] << 24 | d->buf[2] << 16 |
+ d->buf[3] << 8 | d->buf[4];
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: read offset %#x, length %u bytes\n",
+ d->unit, d->offset, d->blen);
+ }
+ d->limit = d->blen + 3;
+ d->count = 1;
+ d->buf[0] = 0;
+ d->buf[1] = DATA_START_BLOCK;
+ read_data(d->fd, d->offset, &d->buf[2], d->blen);
+ d->buf[d->limit - 1] = 0xFF;
+ d->buf[d->limit] = 0xFF;
+ }
+ break;
+ case CMD_WRITE_SINGLE: /* Write block */
+ if (d->count >= sizeof(d->buf)) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ /* Accept command */
+ reply = 0;
+ d->offset = d->buf[1] << 24 | d->buf[2] << 16 |
+ d->buf[3] << 8 | d->buf[4];
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: write offset %#x\n",
+ d->unit, d->offset);
+ }
+ } else if (d->count == 7 + d->blen + 2 + 2) {
+ if (d->buf[7] == DATA_START_BLOCK) {
+ /* Accept data */
+ reply = 0x05;
+ d->offset = d->buf[1] << 24 | d->buf[2] << 16 |
+ d->buf[3] << 8 | d->buf[4];
+ write_data(d->fd, d->offset, &d->buf[8], d->blen);
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: write data, length %u bytes\n",
+ d->unit, d->blen);
+ }
+ } else {
+ /* Reject data */
+ reply = 4;
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: reject write data, tag=%02x\n",
+ d->unit, d->buf[7]);
+ }
+ }
+ }
+ break;
+ case CMD_WRITE_MULTIPLE: /* Write multiple blocks */
+ if (d->count >= 7) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 7) {
+ /* Accept command */
+ reply = 0;
+ d->offset = d->buf[1] << 24 | d->buf[2] << 16 |
+ d->buf[3] << 8 | d->buf[4];
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: write multiple offset %#x\n",
+ d->unit, d->offset);
+ }
+ d->count = 0;
+ }
+ break;
+ case WRITE_MULTIPLE_TOKEN: /* Data for write-miltiple */
+ if (d->count >= sizeof(d->buf)) {
+ break;
+ }
+ d->buf[d->count++] = data;
+ if (d->count == 2 + d->blen + 2) {
+ /* Accept data */
+ reply = 0x05;
+ write_data(d->fd, d->offset, &d->buf[1], d->blen);
+ if (TRACE) {
+ fprintf(qemu_logfile,
+ "--- SD card %d: write sector %u, length %u bytes\n",
+ d->unit, d->offset / 512, d->blen);
+ }
+ d->offset += 512;
+ d->count = 0;
+ }
+ break;
+ case CMD_STOP: /* Stop read-multiple sequence */
+ if (d->count > 1) {
+ break;
+ }
+ d->read_multiple = 0;
+ reply = 0;
+ break;
+ case CMD_SEND_IF_COND: /* Stop read-multiple sequence */
+ if (d->count > 1) {
+ break;
+ }
+ d->read_multiple = 0;
+ reply = 4; /* Unknown command */
+ break;
+ case 0: /* Reply */
+ if (d->count <= d->limit) {
+ reply = d->buf[d->count++];
+ break;
+ }
+ if (d->read_multiple) {
+ /* Next read-multiple block. */
+ d->offset += d->blen;
+ d->count = 1;
+ read_data(d->fd, d->offset, &d->buf[2], d->blen);
+ reply = 0;
+ }
+ break;
+ default: /* Ignore */
+ break;
+ }
+ }
+ return reply;
+}
--
2.2.2
next prev parent reply other threads:[~2015-07-06 6:17 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-07-06 6:14 [Qemu-devel] [PATCH pic32 v3 00/16] add support for pic32 microcontrollers Serge Vakulenko
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 01/16] pic32: make the CPU clock frequency configurable per platform Serge Vakulenko
2015-07-06 8:42 ` Aurelien Jarno
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 02/16] pic32: use LCG algorithm for generated random index of TLBWR instruction Serge Vakulenko
2015-07-06 8:43 ` Aurelien Jarno
2015-09-15 9:46 ` Leon Alrae
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 03/16] pic32: add support for external interrupt controller mode (EIC) Serge Vakulenko
2015-07-06 9:34 ` Aurelien Jarno
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 04/16] pic32: add two MIPS processor variants: M4K and microAptivUP Serge Vakulenko
2015-07-06 9:35 ` Aurelien Jarno
2015-10-02 10:37 ` Leon Alrae
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 05/16] pic32: add file pic32_peripherals.h Serge Vakulenko
2015-07-06 8:02 ` Peter Crosthwaite
2015-07-06 9:01 ` Aurelien Jarno
2015-07-06 17:04 ` Peter Crosthwaite
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 06/16] pic32: add file pic32mx.h Serge Vakulenko
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 07/16] pic32: add file pic32mz.h Serge Vakulenko
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 08/16] pic32: add file mips_pic32mx7.c Serge Vakulenko
2015-07-06 11:18 ` Antony Pavlov
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 09/16] pic32: add file mips_pic32mz.c Serge Vakulenko
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 10/16] pic32: add file pic32_load_hex.c Serge Vakulenko
2015-07-06 6:14 ` [Qemu-devel] [PATCH pic32 v3 11/16] pic32: add file pic32_uart.c Serge Vakulenko
2015-07-06 8:08 ` Peter Crosthwaite
2015-07-06 6:15 ` [Qemu-devel] [PATCH pic32 v3 12/16] pic32: add file pic32_gpio.c Serge Vakulenko
2015-07-06 6:15 ` [Qemu-devel] [PATCH pic32 v3 13/16] pic32: add file pic32_spi.c Serge Vakulenko
2015-07-06 7:58 ` Peter Crosthwaite
2015-07-06 6:15 ` Serge Vakulenko [this message]
2015-07-06 8:05 ` [Qemu-devel] [PATCH pic32 v3 14/16] pic32: add file pic32_sdcard.c Peter Crosthwaite
2015-07-06 6:15 ` [Qemu-devel] [PATCH pic32 v3 15/16] pic32: add file pic32_ethernet.c Serge Vakulenko
2015-07-06 6:15 ` [Qemu-devel] [PATCH pic32 v3 16/16] pic32: update makefiles to cover pic32 support Serge Vakulenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1436163304-6167-15-git-send-email-serge.vakulenko@gmail.com \
--to=serge.vakulenko@gmail.com \
--cc=aurelien@aurel32.net \
--cc=leon.alrae@imgtec.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).