aboutsummaryrefslogtreecommitdiffstats
path: root/hw
diff options
context:
space:
mode:
authorSan Mehat <san@google.com>2009-12-05 09:54:44 -0800
committerSan Mehat <san@google.com>2009-12-07 14:48:32 -0800
commit68a8f7b5ed6ee2bbbc7b67070b9c401c2276426f (patch)
tree59d93e62f0ee5789b80986b347cc1c52dbefb21a /hw
parentdc3dd741551c323bb853782656c0d693db98ecdc (diff)
downloadexternal_qemu-68a8f7b5ed6ee2bbbc7b67070b9c401c2276426f.zip
external_qemu-68a8f7b5ed6ee2bbbc7b67070b9c401c2276426f.tar.gz
external_qemu-68a8f7b5ed6ee2bbbc7b67070b9c401c2276426f.tar.bz2
qemu: android: Add support for multiple SD cards
Add commandline support for an additional SD card, and plumb support to the MMC emulation layer. This patch also reworks the way the hw emulation layer registers devices. Previously, the mmc virtual hardware was only created if the sdcard image existed. Now, two virtual MMC controllers are *always* created as on real hardware. When a request is made to one of our controllers which has no image file bound to it, a new interrupt status indicating a command timeout is sent to the guest driver, as is standard with MMC controllers. This patch also sets the stage for supporting hot-add / hot-remove. Signed-off-by: San Mehat <san@google.com> qemu: android: Integrate card detect into the virtual mmc hardware and wire it up to the STATE_CHANGE irq status Signed-off-by: San Mehat <san@google.com> fixups from review - mmc now works with legacy drivers Signed-off-by: San Mehat <san@google.com>
Diffstat (limited to 'hw')
-rw-r--r--hw/android_arm.c21
-rw-r--r--hw/goldfish_device.h6
-rw-r--r--hw/goldfish_mmc.c78
3 files changed, 89 insertions, 16 deletions
diff --git a/hw/android_arm.c b/hw/android_arm.c
index 32f6925..4ef4a10 100644
--- a/hw/android_arm.c
+++ b/hw/android_arm.c
@@ -42,7 +42,18 @@ static struct goldfish_device nand_device = {
/* Board init. */
-#define TEST_SWITCH 1
+static void goldfish_sdcard_init(int n, unsigned base)
+{
+ int idx = drive_get_index( IF_IDE, 0, n );
+
+ goldfish_mmc_init(base, n);
+
+ if (idx >= 0) {
+ goldfish_mmc_insert(n, drives_table[idx].bdrv);
+ }
+}
+
+// #define TEST_SWITCH 1
#if TEST_SWITCH
uint32_t switch_test_write(void *opaque, uint32_t state)
{
@@ -113,11 +124,9 @@ static void android_arm_init_(ram_addr_t ram_size,
#ifdef HAS_AUDIO
goldfish_audio_init(0xff004000, 0, audio_input_source);
#endif
- {
- int idx = drive_get_index( IF_IDE, 0, 0 );
- if (idx >= 0)
- goldfish_mmc_init(0xff005000, 0, drives_table[idx].bdrv);
- }
+
+ goldfish_sdcard_init(0, 0xff005000);
+ goldfish_sdcard_init(1, 0xff007000);
goldfish_memlog_init(0xff006000);
diff --git a/hw/goldfish_device.h b/hw/goldfish_device.h
index d04a166..7eb21de 100644
--- a/hw/goldfish_device.h
+++ b/hw/goldfish_device.h
@@ -46,7 +46,11 @@ void goldfish_audio_init(uint32_t base, int id, const char* input_source);
void goldfish_battery_init();
void goldfish_battery_set_prop(int ac, int property, int value);
void goldfish_battery_display(void (* callback)(void *data, const char* string), void *data);
-void goldfish_mmc_init(uint32_t base, int id, BlockDriverState* bs);
+
+void goldfish_mmc_init(uint32_t base, int id);
+void goldfish_mmc_insert(int id, BlockDriverState* bs);
+void goldfish_mmc_remove(int id, BlockDriverState *bs);
+
void *goldfish_switch_add(char *name, uint32_t (*writefn)(void *opaque, uint32_t state), void *writeopaque, int id);
void goldfish_switch_set_state(void *opaque, uint32_t state);
diff --git a/hw/goldfish_mmc.c b/hw/goldfish_mmc.c
index 3824db9..124f865 100644
--- a/hw/goldfish_mmc.c
+++ b/hw/goldfish_mmc.c
@@ -52,6 +52,7 @@ enum {
MMC_STAT_END_OF_CMD = 1U << 0,
MMC_STAT_END_OF_DATA = 1U << 1,
MMC_STAT_STATE_CHANGE = 1U << 2,
+ MMC_STAT_CMD_TIMEOUT = 1U << 3,
/* MMC_STATE bits */
MMC_STATE_INSERTED = 1U << 0,
@@ -82,6 +83,9 @@ struct goldfish_mmc_state {
uint8_t* buf;
};
+#define GOLDFISH_MMC_MAX 2
+static struct goldfish_mmc_state *gDrvState[GOLDFISH_MMC_MAX];
+
#define GOLDFISH_MMC_SAVE_VERSION 2
#define QFIELD_STRUCT struct goldfish_mmc_state
QFIELD_BEGIN(goldfish_mmc_fields)
@@ -218,13 +222,24 @@ static void goldfish_mmc_do_command(struct goldfish_mmc_state *s, uint32_t cmd,
int new_status = MMC_STAT_END_OF_CMD;
int opcode = cmd & 63;
-// fprintf(stderr, "goldfish_mmc_do_command opcode: %s (0x%04X), arg: %d\n", get_command_name(opcode), cmd, arg);
-
+ //fprintf(stderr, "goldfish_mmc_do_command opcode: %s (0x%04X), arg: %d\n", get_command_name(opcode), cmd, arg);
s->resp[0] = 0;
s->resp[1] = 0;
s->resp[2] = 0;
s->resp[3] = 0;
+ if (!s->bs) {
+ /*
+ * No backing store available. Signal a command timeout
+ * to the host. If the command timeout irq enable is set
+ * then also set the status bit - otherwise we're assuming
+ * a legacy driver which doesnt support timeouts.
+ */
+ if (s->int_enable & MMC_STAT_CMD_TIMEOUT)
+ new_status |= MMC_STAT_CMD_TIMEOUT;
+ goto skip;
+ }
+
#define SET_R1_CURRENT_STATE(s) ((s << 9) & 0x00001E00) /* sx, b (4 bits) */
switch (opcode) {
@@ -405,6 +420,7 @@ static void goldfish_mmc_do_command(struct goldfish_mmc_state *s, uint32_t cmd,
break;
}
+ skip:
s->int_status |= new_status;
if ((s->int_status & s->int_enable)) {
@@ -414,7 +430,7 @@ static void goldfish_mmc_do_command(struct goldfish_mmc_state *s, uint32_t cmd,
static uint32_t goldfish_mmc_read(void *opaque, target_phys_addr_t offset)
{
- uint32_t ret;
+ uint32_t ret = 0;
struct goldfish_mmc_state *s = opaque;
switch(offset) {
@@ -430,9 +446,11 @@ static uint32_t goldfish_mmc_read(void *opaque, target_phys_addr_t offset)
case MMC_RESP_3:
return s->resp[3];
case MMC_STATE: {
- ret = MMC_STATE_INSERTED;
- if (bdrv_is_read_only(s->bs)) {
- ret |= MMC_STATE_READ_ONLY;
+ if (s->bs) {
+ ret = MMC_STATE_INSERTED;
+ if (bdrv_is_read_only(s->bs)) {
+ ret |= MMC_STATE_READ_ONLY;
+ }
}
return ret;
}
@@ -499,22 +517,64 @@ static CPUWriteMemoryFunc *goldfish_mmc_writefn[] = {
goldfish_mmc_write
};
-void goldfish_mmc_init(uint32_t base, int id, BlockDriverState* bs)
+void goldfish_mmc_init(uint32_t base, int id)
{
struct goldfish_mmc_state *s;
+ if (id >= GOLDFISH_MMC_MAX) {
+ fprintf(stderr, "mmc controller %d out of range\n", id);
+ return;
+ }
+
s = (struct goldfish_mmc_state *)qemu_mallocz(sizeof(*s));
s->dev.name = "goldfish_mmc";
s->dev.id = id;
s->dev.base = base;
s->dev.size = 0x1000;
s->dev.irq_count = 1;
- s->bs = bs;
+ s->bs = NULL;
s->buf = qemu_memalign(512,512);
+ gDrvState[id] = s;
+
goldfish_device_add(&s->dev, goldfish_mmc_readfn, goldfish_mmc_writefn, s);
- register_savevm( "goldfish_mmc", 0, GOLDFISH_MMC_SAVE_VERSION,
+ register_savevm( (!id ? "goldfish_mmc0" : "goldfish_mmc1"),
+ id, GOLDFISH_MMC_SAVE_VERSION,
goldfish_mmc_save, goldfish_mmc_load, s);
}
+static void goldfish_mmc_setbs(struct goldfish_mmc_state* s, BlockDriverState* bs)
+{
+ s->bs = bs;
+ s->int_status |= MMC_STAT_STATE_CHANGE;
+
+ /*
+ * Legacy - only send state change irq if
+ * the driver has the CMD_TIMEOUT irq enabled.
+ */
+ if (s->int_enable & MMC_STAT_CMD_TIMEOUT)
+ goldfish_device_set_irq(&s->dev, 0, (s->int_status & s->int_enable));
+}
+
+void goldfish_mmc_insert(int id, BlockDriverState* bs)
+{
+ if (id >= GOLDFISH_MMC_MAX) {
+ fprintf(stderr, "mmc controller %d out of range\n", id);
+ return;
+ }
+
+ goldfish_mmc_setbs(gDrvState[id], bs);
+}
+
+void goldfish_mmc_remove(int id, BlockDriverState* bs)
+{
+ if (id >= GOLDFISH_MMC_MAX) {
+ fprintf(stderr, "mmc controller %d out of range\n", id);
+ return;
+ }
+
+ goldfish_mmc_setbs(gDrvState[id], NULL);
+}
+
+