diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/arm/raspi.c | 23 | ||||
-rw-r--r-- | hw/char/stm32f2xx_usart.c | 12 | ||||
-rw-r--r-- | hw/display/virtio-gpu-3d.c | 15 | ||||
-rw-r--r-- | hw/display/virtio-gpu.c | 7 | ||||
-rw-r--r-- | hw/misc/aspeed_scu.c | 6 | ||||
-rw-r--r-- | hw/misc/aspeed_sdmc.c | 8 | ||||
-rw-r--r-- | hw/sd/milkymist-memcard.c | 81 | ||||
-rw-r--r-- | hw/sd/sd.c | 467 | ||||
-rw-r--r-- | hw/sd/sdmmc-internal.h | 15 | ||||
-rw-r--r-- | hw/sd/ssi-sd.c | 32 | ||||
-rw-r--r-- | hw/sd/trace-events | 20 | ||||
-rw-r--r-- | hw/usb/dev-mtp.c | 471 |
12 files changed, 895 insertions, 262 deletions
diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 93121c56bf..a37881433c 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -187,3 +187,26 @@ static void raspi2_machine_init(MachineClass *mc) mc->ignore_memory_transaction_failures = true; }; DEFINE_MACHINE("raspi2", raspi2_machine_init) + +#ifdef TARGET_AARCH64 +static void raspi3_init(MachineState *machine) +{ + raspi_init(machine, 3); +} + +static void raspi3_machine_init(MachineClass *mc) +{ + mc->desc = "Raspberry Pi 3"; + mc->init = raspi3_init; + mc->block_default_type = IF_SD; + mc->no_parallel = 1; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a53"); + mc->max_cpus = BCM2836_NCPUS; + mc->min_cpus = BCM2836_NCPUS; + mc->default_cpus = BCM2836_NCPUS; + mc->default_ram_size = 1024 * 1024 * 1024; +} +DEFINE_MACHINE("raspi3", raspi3_machine_init) +#endif diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 07b462d4b6..032b5fda13 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -96,12 +96,10 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, switch (addr) { case USART_SR: retvalue = s->usart_sr; - s->usart_sr &= ~USART_SR_TC; qemu_chr_fe_accept_input(&s->chr); return retvalue; case USART_DR: DB_PRINT("Value: 0x%" PRIx32 ", %c\n", s->usart_dr, (char) s->usart_dr); - s->usart_sr |= USART_SR_TXE; s->usart_sr &= ~USART_SR_RXNE; qemu_chr_fe_accept_input(&s->chr); qemu_set_irq(s->irq, 0); @@ -137,7 +135,9 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, switch (addr) { case USART_SR: if (value <= 0x3FF) { - s->usart_sr = value; + /* I/O being synchronous, TXE is always set. In addition, it may + only be set by hardware, so keep it set here. */ + s->usart_sr = value | USART_SR_TXE; } else { s->usart_sr &= value; } @@ -151,8 +151,12 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &ch, 1); + /* XXX I/O are currently synchronous, making it impossible for + software to observe transient states where TXE or TC aren't + set. Unlike TXE however, which is read-only, software may + clear TC by writing 0 to the SR register, so set it again + on each write. */ s->usart_sr |= USART_SR_TC; - s->usart_sr &= ~USART_SR_TXE; } return; case USART_BRR: diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 7db84efe89..3558f38fe8 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -362,6 +362,11 @@ static void virgl_cmd_get_capset_info(VirtIOGPU *g, virgl_renderer_get_cap_set(resp.capset_id, &resp.capset_max_version, &resp.capset_max_size); + } else if (info.capset_index == 1) { + resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL2; + virgl_renderer_get_cap_set(resp.capset_id, + &resp.capset_max_version, + &resp.capset_max_size); } else { resp.capset_max_version = 0; resp.capset_max_size = 0; @@ -635,4 +640,14 @@ int virtio_gpu_virgl_init(VirtIOGPU *g) return 0; } +int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g) +{ + uint32_t capset2_max_ver, capset2_max_size; + virgl_renderer_get_cap_set(VIRTIO_GPU_CAPSET_VIRGL2, + &capset2_max_ver, + &capset2_max_size); + + return capset2_max_ver ? 2 : 1; +} + #endif /* CONFIG_VIRGL */ diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 6658f6c6a6..2dd3c3481a 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1215,7 +1215,12 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) /* use larger control queue in 3d mode */ g->ctrl_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_ctrl_cb); g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb); - g->virtio_config.num_capsets = 1; + +#if defined(CONFIG_VIRGL) + g->virtio_config.num_capsets = virtio_gpu_virgl_get_num_capsets(g); +#else + g->virtio_config.num_capsets = 0; +#endif } else { g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb); g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb); diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 74537ce975..5e6d5744ee 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -191,7 +191,7 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, } if (reg > PROT_KEY && reg < CPU2_BASE_SEG1 && - s->regs[PROT_KEY] != ASPEED_SCU_PROT_KEY) { + !s->regs[PROT_KEY]) { qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__); return; } @@ -199,6 +199,10 @@ static void aspeed_scu_write(void *opaque, hwaddr offset, uint64_t data, trace_aspeed_scu_write(offset, size, data); switch (reg) { + case PROT_KEY: + s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0; + return; + case FREQ_CNTR_EVAL: case VGA_SCRATCH1 ... VGA_SCRATCH8: case RNG_DATA: diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index f0b3053fae..0df008e52a 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -110,7 +110,12 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data, return; } - if (addr != R_PROT && s->regs[R_PROT] != PROT_KEY_UNLOCK) { + if (addr == R_PROT) { + s->regs[addr] = (data == PROT_KEY_UNLOCK) ? 1 : 0; + return; + } + + if (!s->regs[R_PROT]) { qemu_log_mask(LOG_GUEST_ERROR, "%s: SDMC is locked!\n", __func__); return; } @@ -123,6 +128,7 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data, data &= ~ASPEED_SDMC_READONLY_MASK; break; case AST2500_A0_SILICON_REV: + case AST2500_A1_SILICON_REV: data &= ~ASPEED_SDMC_AST2500_READONLY_MASK; break; default: diff --git a/hw/sd/milkymist-memcard.c b/hw/sd/milkymist-memcard.c index 341da88552..5570c1e9a0 100644 --- a/hw/sd/milkymist-memcard.c +++ b/hw/sd/milkymist-memcard.c @@ -22,11 +22,12 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "sysemu/sysemu.h" #include "trace.h" -#include "qemu/error-report.h" +#include "include/qapi/error.h" #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "hw/sd/sd.h" @@ -68,7 +69,7 @@ struct MilkymistMemcardState { SysBusDevice parent_obj; MemoryRegion regs_region; - SDState *card; + SDBus sdbus; int command_write_ptr; int response_read_ptr; @@ -104,7 +105,7 @@ static void memcard_sd_command(MilkymistMemcardState *s) req.crc = s->command[5]; s->response[0] = req.cmd; - s->response_len = sd_do_command(s->card, &req, s->response+1); + s->response_len = sdbus_do_command(&s->sdbus, &req, s->response + 1); s->response_read_ptr = 0; if (s->response_len == 16) { @@ -138,8 +139,8 @@ static uint64_t memcard_read(void *opaque, hwaddr addr, } else { r = s->response[s->response_read_ptr++]; if (s->response_read_ptr > s->response_len) { - error_report("milkymist_memcard: " - "read more cmd bytes than available. Clipping."); + qemu_log_mask(LOG_GUEST_ERROR, "milkymist_memcard: " + "read more cmd bytes than available. Clipping."); s->response_read_ptr = 0; } } @@ -149,10 +150,10 @@ static uint64_t memcard_read(void *opaque, hwaddr addr, r = 0xffffffff; } else { r = 0; - r |= sd_read_data(s->card) << 24; - r |= sd_read_data(s->card) << 16; - r |= sd_read_data(s->card) << 8; - r |= sd_read_data(s->card); + r |= sdbus_read_data(&s->sdbus) << 24; + r |= sdbus_read_data(&s->sdbus) << 16; + r |= sdbus_read_data(&s->sdbus) << 8; + r |= sdbus_read_data(&s->sdbus); } break; case R_CLK2XDIV: @@ -163,8 +164,9 @@ static uint64_t memcard_read(void *opaque, hwaddr addr, break; default: - error_report("milkymist_memcard: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + qemu_log_mask(LOG_UNIMP, "milkymist_memcard: " + "read access to unknown register 0x%" HWADDR_PRIx "\n", + addr << 2); break; } @@ -205,10 +207,10 @@ static void memcard_write(void *opaque, hwaddr addr, uint64_t value, if (!s->enabled) { break; } - sd_write_data(s->card, (value >> 24) & 0xff); - sd_write_data(s->card, (value >> 16) & 0xff); - sd_write_data(s->card, (value >> 8) & 0xff); - sd_write_data(s->card, value & 0xff); + sdbus_write_data(&s->sdbus, (value >> 24) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 16) & 0xff); + sdbus_write_data(&s->sdbus, (value >> 8) & 0xff); + sdbus_write_data(&s->sdbus, value & 0xff); break; case R_ENABLE: s->regs[addr] = value; @@ -220,8 +222,9 @@ static void memcard_write(void *opaque, hwaddr addr, uint64_t value, break; default: - error_report("milkymist_memcard: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + qemu_log_mask(LOG_UNIMP, "milkymist_memcard: " + "write access to unknown register 0x%" HWADDR_PRIx " " + "(value 0x%" PRIx64 ")\n", addr << 2, value); break; } } @@ -248,33 +251,41 @@ static void milkymist_memcard_reset(DeviceState *d) for (i = 0; i < R_MAX; i++) { s->regs[i] = 0; } - /* Since we're still using the legacy SD API the card is not plugged - * into any bus, and we must reset it manually. - */ - device_reset(DEVICE(s->card)); } -static int milkymist_memcard_init(SysBusDevice *dev) +static void milkymist_memcard_init(Object *obj) +{ + MilkymistMemcardState *s = MILKYMIST_MEMCARD(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s, + "milkymist-memcard", R_MAX * 4); + sysbus_init_mmio(dev, &s->regs_region); +} + +static void milkymist_memcard_realize(DeviceState *dev, Error **errp) { MilkymistMemcardState *s = MILKYMIST_MEMCARD(dev); - DriveInfo *dinfo; + DeviceState *carddev; BlockBackend *blk; + DriveInfo *dinfo; + Error *err = NULL; + + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, + dev, "sd-bus"); + /* Create and plug in the sd card */ /* FIXME use a qdev drive property instead of drive_get_next() */ dinfo = drive_get_next(IF_SD); blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL; - s->card = sd_init(blk, false); - if (s->card == NULL) { - return -1; + carddev = qdev_create(&s->sdbus.qbus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &err); + object_property_set_bool(OBJECT(carddev), true, "realized", &err); + if (err) { + error_setg(errp, "failed to init SD card: %s", error_get_pretty(err)); + return; } - s->enabled = blk && blk_is_inserted(blk); - - memory_region_init_io(&s->regs_region, OBJECT(s), &memcard_mmio_ops, s, - "milkymist-memcard", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; } static const VMStateDescription vmstate_milkymist_memcard = { @@ -297,9 +308,8 @@ static const VMStateDescription vmstate_milkymist_memcard = { static void milkymist_memcard_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_memcard_init; + dc->realize = milkymist_memcard_realize; dc->reset = milkymist_memcard_reset; dc->vmsd = &vmstate_milkymist_memcard; /* Reason: init() method uses drive_get_next() */ @@ -310,6 +320,7 @@ static const TypeInfo milkymist_memcard_info = { .name = TYPE_MILKYMIST_MEMCARD, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MilkymistMemcardState), + .instance_init = milkymist_memcard_init, .class_init = milkymist_memcard_class_init, }; diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 9ac9b63ff8..933890e86f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -32,28 +32,21 @@ #include "qemu/osdep.h" #include "hw/qdev.h" #include "hw/hw.h" +#include "hw/registerfields.h" #include "sysemu/block-backend.h" #include "hw/sd/sd.h" #include "qapi/error.h" #include "qemu/bitmap.h" +#include "qemu/cutils.h" #include "hw/qdev-properties.h" #include "qemu/error-report.h" #include "qemu/timer.h" #include "qemu/log.h" +#include "sdmmc-internal.h" +#include "trace.h" //#define DEBUG_SD 1 -#ifdef DEBUG_SD -#define DPRINTF(fmt, ...) \ -do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define ACMD41_ENQUIRY_MASK 0x00ffffff -#define OCR_POWER_UP 0x80000000 -#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */ - typedef enum { sd_r0 = 0, /* no response */ sd_r1, /* normal response command */ @@ -88,16 +81,21 @@ enum SDCardStates { struct SDState { DeviceState parent_obj; - uint32_t mode; /* current card mode, one of SDCardModes */ - int32_t state; /* current card state, one of SDCardStates */ + /* SD Memory Card Registers */ uint32_t ocr; - QEMUTimer *ocr_power_timer; uint8_t scr[8]; uint8_t cid[16]; uint8_t csd[16]; uint16_t rca; uint32_t card_status; uint8_t sd_status[64]; + + /* Configurable properties */ + BlockBackend *blk; + bool spi; + + uint32_t mode; /* current card mode, one of SDCardModes */ + int32_t state; /* current card state, one of SDCardStates */ uint32_t vhs; bool wp_switch; unsigned long *wp_groups; @@ -110,8 +108,6 @@ struct SDState { uint8_t pwd[16]; uint32_t pwd_len; uint8_t function_group[6]; - - bool spi; uint8_t current_cmd; /* True if we will handle the next command as an ACMD. Note that this does * *not* track the APP_CMD status bit! @@ -123,13 +119,53 @@ struct SDState { uint8_t data[512]; qemu_irq readonly_cb; qemu_irq inserted_cb; - BlockBackend *blk; - + QEMUTimer *ocr_power_timer; bool enable; uint8_t dat_lines; bool cmd_line; }; +static const char *sd_state_name(enum SDCardStates state) +{ + static const char *state_name[] = { + [sd_idle_state] = "idle", + [sd_ready_state] = "ready", + [sd_identification_state] = "identification", + [sd_standby_state] = "standby", + [sd_transfer_state] = "transfer", + [sd_sendingdata_state] = "sendingdata", + [sd_receivingdata_state] = "receivingdata", + [sd_programming_state] = "programming", + [sd_disconnect_state] = "disconnect", + }; + if (state == sd_inactive_state) { + return "inactive"; + } + assert(state <= ARRAY_SIZE(state_name)); + return state_name[state]; +} + +static const char *sd_response_name(sd_rsp_type_t rsp) +{ + static const char *response_name[] = { + [sd_r0] = "RESP#0 (no response)", + [sd_r1] = "RESP#1 (normal cmd)", + [sd_r2_i] = "RESP#2 (CID reg)", + [sd_r2_s] = "RESP#2 (CSD reg)", + [sd_r3] = "RESP#3 (OCR reg)", + [sd_r6] = "RESP#6 (RCA)", + [sd_r7] = "RESP#7 (operating voltage)", + }; + if (rsp == sd_illegal) { + return "ILLEGAL RESP"; + } + if (rsp == sd_r1b) { + rsp = sd_r1; + } + assert(rsp <= ARRAY_SIZE(response_name)); + return response_name[rsp]; +} + static uint8_t sd_get_dat_lines(SDState *sd) { return sd->enable ? sd->dat_lines : 0; @@ -142,6 +178,8 @@ static bool sd_get_cmd_line(SDState *sd) static void sd_set_voltage(SDState *sd, uint16_t millivolts) { + trace_sdcard_set_voltage(millivolts); + switch (millivolts) { case 3001 ... 3600: /* SD_VOLTAGE_3_3V */ case 2001 ... 3000: /* SD_VOLTAGE_3_0V */ @@ -176,18 +214,21 @@ static void sd_set_mode(SDState *sd) } } -static const sd_cmd_type_t sd_cmd_type[64] = { +static const sd_cmd_type_t sd_cmd_type[SDMMC_CMD_MAX] = { sd_bc, sd_none, sd_bcr, sd_bcr, sd_none, sd_none, sd_none, sd_ac, sd_bcr, sd_ac, sd_ac, sd_adtc, sd_ac, sd_ac, sd_none, sd_ac, + /* 16 */ sd_ac, sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac, sd_ac, sd_adtc, sd_none, + /* 32 */ sd_ac, sd_ac, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_none, sd_none, sd_none, sd_bc, sd_none, sd_none, sd_none, sd_none, sd_none, + /* 48 */ sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, }; -static const int sd_cmd_class[64] = { +static const int sd_cmd_class[SDMMC_CMD_MAX] = { 0, 0, 0, 0, 0, 9, 10, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 6, 6, 6, 6, 5, 5, 10, 10, 10, 10, 5, 9, 9, 9, 7, 7, 7, 7, 7, 7, @@ -227,27 +268,54 @@ static uint16_t sd_crc16(void *message, size_t width) return shift_reg; } +#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */ + +FIELD(OCR, VDD_VOLTAGE_WINDOW, 0, 24) +FIELD(OCR, VDD_VOLTAGE_WIN_LO, 0, 8) +FIELD(OCR, DUAL_VOLTAGE_CARD, 7, 1) +FIELD(OCR, VDD_VOLTAGE_WIN_HI, 8, 16) +FIELD(OCR, ACCEPT_SWITCH_1V8, 24, 1) /* Only UHS-I */ +FIELD(OCR, UHS_II_CARD, 29, 1) /* Only UHS-II */ +FIELD(OCR, CARD_CAPACITY, 30, 1) /* 0:SDSC, 1:SDHC/SDXC */ +FIELD(OCR, CARD_POWER_UP, 31, 1) + +#define ACMD41_ENQUIRY_MASK 0x00ffffff +#define ACMD41_R3_MASK (R_OCR_VDD_VOLTAGE_WIN_HI_MASK \ + | R_OCR_ACCEPT_SWITCH_1V8_MASK \ + | R_OCR_UHS_II_CARD_MASK \ + | R_OCR_CARD_CAPACITY_MASK \ + | R_OCR_CARD_POWER_UP_MASK) + static void sd_set_ocr(SDState *sd) { - /* All voltages OK, Standard Capacity SD Memory Card, not yet powered up */ - sd->ocr = 0x00ffff00; + /* All voltages OK */ + sd->ocr = R_OCR_VDD_VOLTAGE_WIN_HI_MASK; } static void sd_ocr_powerup(void *opaque) { SDState *sd = opaque; - /* Set powered up bit in OCR */ - assert(!(sd->ocr & OCR_POWER_UP)); - sd->ocr |= OCR_POWER_UP; + trace_sdcard_powerup(); + assert(!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)); + + /* card power-up OK */ + sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_POWER_UP, 1); + + if (sd->size > 1 * G_BYTE) { + sd->ocr = FIELD_DP32(sd->ocr, OCR, CARD_CAPACITY, 1); + } } static void sd_set_scr(SDState *sd) { - sd->scr[0] = 0x00; /* SCR Structure */ - sd->scr[1] = 0x2f; /* SD Security Support */ - sd->scr[2] = 0x00; + sd->scr[0] = (0 << 4) /* SCR version 1.0 */ + | 0; /* Spec Versions 1.0 and 1.01 */ + sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */ + | 0b0101; /* 1-bit or 4-bit width bus modes */ + sd->scr[2] = 0x00; /* Extended Security is not supported. */ sd->scr[3] = 0x00; + /* reserved for manufacturer usage */ sd->scr[4] = 0x00; sd->scr[5] = 0x00; sd->scr[6] = 0x00; @@ -299,11 +367,11 @@ static void sd_set_csd(SDState *sd, uint64_t size) uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1; uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1; - if (size <= 0x40000000) { /* Standard Capacity SD */ + if (size <= 1 * G_BYTE) { /* Standard Capacity SD */ sd->csd[0] = 0x00; /* CSD structure */ sd->csd[1] = 0x26; /* Data read access-time-1 */ sd->csd[2] = 0x00; /* Data read access-time-2 */ - sd->csd[3] = 0x5a; /* Max. data transfer rate */ + sd->csd[3] = 0x32; /* Max. data transfer rate: 25 MHz */ sd->csd[4] = 0x5f; /* Card Command Classes */ sd->csd[5] = 0x50 | /* Max. read data block length */ HWBLOCK_SHIFT; @@ -324,7 +392,6 @@ static void sd_set_csd(SDState *sd, uint64_t size) sd->csd[13] = 0x20 | /* Max. write data block length */ ((HWBLOCK_SHIFT << 6) & 0xc0); sd->csd[14] = 0x00; /* File format group */ - sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; } else { /* SDHC */ size /= 512 * 1024; size -= 1; @@ -343,9 +410,8 @@ static void sd_set_csd(SDState *sd, uint64_t size) sd->csd[12] = 0x0a; sd->csd[13] = 0x40; sd->csd[14] = 0x00; - sd->csd[15] = 0x00; - sd->ocr |= 1 << 30; /* High Capacity SD Memory Card */ } + sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1; } static void sd_set_rca(SDState *sd) @@ -353,14 +419,56 @@ static void sd_set_rca(SDState *sd) sd->rca += 0x4567; } +FIELD(CSR, AKE_SEQ_ERROR, 3, 1) +FIELD(CSR, APP_CMD, 5, 1) +FIELD(CSR, FX_EVENT, 6, 1) +FIELD(CSR, READY_FOR_DATA, 8, 1) +FIELD(CSR, CURRENT_STATE, 9, 4) +FIELD(CSR, ERASE_RESET, 13, 1) +FIELD(CSR, CARD_ECC_DISABLED, 14, 1) +FIELD(CSR, WP_ERASE_SKIP, 15, 1) +FIELD(CSR, CSD_OVERWRITE, 16, 1) +FIELD(CSR, DEFERRED_RESPONSE, 17, 1) +FIELD(CSR, ERROR, 19, 1) +FIELD(CSR, CC_ERROR, 20, 1) +FIELD(CSR, CARD_ECC_FAILED, 21, 1) +FIELD(CSR, ILLEGAL_COMMAND, 22, 1) +FIELD(CSR, COM_CRC_ERROR, 23, 1) +FIELD(CSR, LOCK_UNLOCK_FAILED, 24, 1) +FIELD(CSR, CARD_IS_LOCKED, 25, 1) +FIELD(CSR, WP_VIOLATION, 26, 1) +FIELD(CSR, ERASE_PARAM, 27, 1) +FIELD(CSR, ERASE_SEQ_ERROR, 28, 1) +FIELD(CSR, BLOCK_LEN_ERROR, 29, 1) +FIELD(CSR, ADDRESS_ERROR, 30, 1) +FIELD(CSR, OUT_OF_RANGE, 31, 1) + /* Card status bits, split by clear condition: * A : According to the card current state * B : Always related to the previous command * C : Cleared by read */ -#define CARD_STATUS_A 0x02004100 -#define CARD_STATUS_B 0x00c01e00 -#define CARD_STATUS_C 0xfd39a028 +#define CARD_STATUS_A (R_CSR_READY_FOR_DATA_MASK \ + | R_CSR_CARD_ECC_DISABLED_MASK \ + | R_CSR_CARD_IS_LOCKED_MASK) +#define CARD_STATUS_B (R_CSR_CURRENT_STATE_MASK \ + | R_CSR_ILLEGAL_COMMAND_MASK \ + | R_CSR_COM_CRC_ERROR_MASK) +#define CARD_STATUS_C (R_CSR_AKE_SEQ_ERROR_MASK \ + | R_CSR_APP_CMD_MASK \ + | R_CSR_ERASE_RESET_MASK \ + | R_CSR_WP_ERASE_SKIP_MASK \ + | R_CSR_CSD_OVERWRITE_MASK \ + | R_CSR_ERROR_MASK \ + | R_CSR_CC_ERROR_MASK \ + | R_CSR_CARD_ECC_FAILED_MASK \ + | R_CSR_LOCK_UNLOCK_FAILED_MASK \ + | R_CSR_WP_VIOLATION_MASK \ + | R_CSR_ERASE_PARAM_MASK \ + | R_CSR_ERASE_SEQ_ERROR_MASK \ + | R_CSR_BLOCK_LEN_ERROR_MASK \ + | R_CSR_ADDRESS_ERROR_MASK \ + | R_CSR_OUT_OF_RANGE_MASK) static void sd_set_cardstatus(SDState *sd) { @@ -376,57 +484,39 @@ static int sd_req_crc_validate(SDRequest *req) { uint8_t buffer[5]; buffer[0] = 0x40 | req->cmd; - buffer[1] = (req->arg >> 24) & 0xff; - buffer[2] = (req->arg >> 16) & 0xff; - buffer[3] = (req->arg >> 8) & 0xff; - buffer[4] = (req->arg >> 0) & 0xff; + stl_be_p(&buffer[1], req->arg); return 0; return sd_crc7(buffer, 5) != req->crc; /* TODO */ } static void sd_response_r1_make(SDState *sd, uint8_t *response) { - uint32_t status = sd->card_status; + stl_be_p(response, sd->card_status); + /* Clear the "clear on read" status bits */ sd->card_status &= ~CARD_STATUS_C; - - response[0] = (status >> 24) & 0xff; - response[1] = (status >> 16) & 0xff; - response[2] = (status >> 8) & 0xff; - response[3] = (status >> 0) & 0xff; } static void sd_response_r3_make(SDState *sd, uint8_t *response) { - response[0] = (sd->ocr >> 24) & 0xff; - response[1] = (sd->ocr >> 16) & 0xff; - response[2] = (sd->ocr >> 8) & 0xff; - response[3] = (sd->ocr >> 0) & 0xff; + stl_be_p(response, sd->ocr & ACMD41_R3_MASK); } static void sd_response_r6_make(SDState *sd, uint8_t *response) { - uint16_t arg; uint16_t status; - arg = sd->rca; status = ((sd->card_status >> 8) & 0xc000) | ((sd->card_status >> 6) & 0x2000) | (sd->card_status & 0x1fff); sd->card_status &= ~(CARD_STATUS_C & 0xc81fff); - - response[0] = (arg >> 8) & 0xff; - response[1] = arg & 0xff; - response[2] = (status >> 8) & 0xff; - response[3] = status & 0xff; + stw_be_p(response + 0, sd->rca); + stw_be_p(response + 2, status); } static void sd_response_r7_make(SDState *sd, uint8_t *response) { - response[0] = (sd->vhs >> 24) & 0xff; - response[1] = (sd->vhs >> 16) & 0xff; - response[2] = (sd->vhs >> 8) & 0xff; - response[3] = (sd->vhs >> 0) & 0xff; + stl_be_p(response, sd->vhs); } static inline uint64_t sd_addr_to_wpnum(uint64_t addr) @@ -440,6 +530,7 @@ static void sd_reset(DeviceState *dev) uint64_t size; uint64_t sect; + trace_sdcard_reset(); if (sd->blk) { blk_get_geometry(sd->blk, §); } else { @@ -493,7 +584,10 @@ static void sd_cardchange(void *opaque, bool load, Error **errp) bool readonly = sd_get_readonly(sd); if (inserted) { + trace_sdcard_inserted(readonly); sd_reset(dev); + } else { + trace_sdcard_ejected(); } /* The IRQ notification is for legacy non-QOM SD controller devices; @@ -521,7 +615,7 @@ static bool sd_ocr_vmstate_needed(void *opaque) SDState *sd = opaque; /* Include the OCR state (and timer) if it is not yet powered up */ - return !(sd->ocr & OCR_POWER_UP); + return !FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP); } static const VMStateDescription sd_ocr_vmstate = { @@ -625,12 +719,13 @@ static void sd_erase(SDState *sd) uint64_t erase_start = sd->erase_start; uint64_t erase_end = sd->erase_end; + trace_sdcard_erase(); if (!sd->erase_start || !sd->erase_end) { sd->card_status |= ERASE_SEQ_ERROR; return; } - if (extract32(sd->ocr, OCR_CCS_BITN, 1)) { + if (FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { /* High capacity memory card: erase units are 512 byte blocks */ erase_start *= 512; erase_end *= 512; @@ -667,7 +762,7 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr) static void sd_function_switch(SDState *sd, uint32_t arg) { - int i, mode, new_func, crc; + int i, mode, new_func; mode = !!(arg & 0x80000000); sd->data[0] = 0x00; /* Maximum current consumption */ @@ -691,9 +786,7 @@ static void sd_function_switch(SDState *sd, uint32_t arg) sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4); } memset(&sd->data[17], 0, 47); - crc = sd_crc16(sd->data, 64); - sd->data[65] = crc >> 8; - sd->data[66] = crc & 0xff; + stw_be_p(sd->data + 65, sd_crc16(sd->data, 64)); } static inline bool sd_wp_addr(SDState *sd, uint64_t addr) @@ -714,6 +807,11 @@ static void sd_lock_command(SDState *sd) else pwd_len = 0; + if (lock) { + trace_sdcard_lock(); + } else { + trace_sdcard_unlock(); + } if (erase) { if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 || set_pwd || clr_pwd || lock || sd->wp_switch || @@ -774,11 +872,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, uint32_t rca = 0x0000; uint64_t addr = (sd->ocr & (1 << 30)) ? (uint64_t) req.arg << 9 : req.arg; + trace_sdcard_normal_command(req.cmd, req.arg, sd_state_name(sd->state)); + /* Not interpreting this as an app command */ sd->card_status &= ~APP_CMD; - if (sd_cmd_type[req.cmd & 0x3F] == sd_ac - || sd_cmd_type[req.cmd & 0x3F] == sd_adtc) { + if (sd_cmd_type[req.cmd] == sd_ac + || sd_cmd_type[req.cmd] == sd_adtc) { rca = req.arg >> 16; } @@ -788,7 +888,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, sd->multi_blk_cnt = 0; } - DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state); switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ case 0: /* CMD0: GO_IDLE_STATE */ @@ -909,23 +1008,19 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, case 8: /* CMD8: SEND_IF_COND */ /* Physical Layer Specification Version 2.00 command */ - switch (sd->state) { - case sd_idle_state: - sd->vhs = 0; - - /* No response if not exactly one VHS bit is set. */ - if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) { - return sd->spi ? sd_r7 : sd_r0; - } - - /* Accept. */ - sd->vhs = req.arg; - return sd_r7; - - default: + if (sd->state != sd_idle_state) { break; } - break; + sd->vhs = 0; + + /* No response if not exactly one VHS bit is set. */ + if (!(req.arg >> 8) || (req.arg >> (ctz32(req.arg & ~0xff) + 1))) { + return sd->spi ? sd_r7 : sd_r0; + } + + /* Accept. */ + sd->vhs = req.arg; + return sd_r7; case 9: /* CMD9: SEND_CSD */ switch (sd->state) { @@ -971,24 +1066,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; - case 11: /* CMD11: READ_DAT_UNTIL_STOP */ - if (sd->spi) - goto bad_cmd; - switch (sd->state) { - case sd_transfer_state: - sd->state = sd_sendingdata_state; - sd->data_start = req.arg; - sd->data_offset = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - return sd_r0; - - default: - break; - } - break; - case 12: /* CMD12: STOP_TRANSMISSION */ switch (sd->state) { case sd_sendingdata_state: @@ -1039,10 +1116,12 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, case 16: /* CMD16: SET_BLOCKLEN */ switch (sd->state) { case sd_transfer_state: - if (req.arg > (1 << HWBLOCK_SHIFT)) + if (req.arg > (1 << HWBLOCK_SHIFT)) { sd->card_status |= BLOCK_LEN_ERROR; - else + } else { + trace_sdcard_set_blocklen(req.arg); sd->blk_len = req.arg; + } return sd_r1; @@ -1096,8 +1175,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ - if (sd->spi) - goto unimplemented_cmd; + if (sd->spi) { + goto unimplemented_spi_cmd; + } switch (sd->state) { case sd_transfer_state: /* Writing in SPI mode not implemented. */ @@ -1122,8 +1202,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */ - if (sd->spi) - goto unimplemented_cmd; + if (sd->spi) { + goto unimplemented_spi_cmd; + } switch (sd->state) { case sd_transfer_state: /* Writing in SPI mode not implemented. */ @@ -1163,8 +1244,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, break; case 27: /* CMD27: PROGRAM_CSD */ - if (sd->spi) - goto unimplemented_cmd; + if (sd->spi) { + goto unimplemented_spi_cmd; + } switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1274,8 +1356,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, /* Lock card commands (Class 7) */ case 42: /* CMD42: LOCK_UNLOCK */ - if (sd->spi) - goto unimplemented_cmd; + if (sd->spi) { + goto unimplemented_spi_cmd; + } switch (sd->state) { case sd_transfer_state: sd->state = sd_receivingdata_state; @@ -1288,9 +1371,8 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; - case 52: - case 53: - /* CMD52, CMD53: reserved for SDIO cards + case 52 ... 54: + /* CMD52, CMD53, CMD54: reserved for SDIO cards * (see the SDIO Simplified Specification V2.0) * Handle as illegal command but do not complain * on stderr, as some OSes may use these in their @@ -1300,16 +1382,29 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, /* Application specific commands (Class 8) */ case 55: /* CMD55: APP_CMD */ - if (sd->rca != rca) - return sd_r0; - + switch (sd->state) { + case sd_ready_state: + case sd_identification_state: + case sd_inactive_state: + return sd_illegal; + case sd_idle_state: + if (rca) { + qemu_log_mask(LOG_GUEST_ERROR, + "SD: illegal RCA 0x%04x for APP_CMD\n", req.cmd); + } + default: + break; + } + if (!sd->spi) { + if (sd->rca != rca) { + return sd_r0; + } + } sd->expecting_acmd = true; sd->card_status |= APP_CMD; return sd_r1; case 56: /* CMD56: GEN_CMD */ - fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg); - switch (sd->state) { case sd_transfer_state: sd->data_offset = 0; @@ -1324,12 +1419,24 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; + case 58: /* CMD58: READ_OCR (SPI) */ + if (!sd->spi) { + goto bad_cmd; + } + return sd_r3; + + case 59: /* CMD59: CRC_ON_OFF (SPI) */ + if (!sd->spi) { + goto bad_cmd; + } + goto unimplemented_spi_cmd; + default: bad_cmd: qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; - unimplemented_cmd: + unimplemented_spi_cmd: /* Commands that are recognised but not yet implemented in SPI mode. */ qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n", req.cmd); @@ -1343,10 +1450,13 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, static sd_rsp_type_t sd_app_command(SDState *sd, SDRequest req) { - DPRINTF("ACMD%d 0x%08x\n", req.cmd, req.arg); + trace_sdcard_app_command(req.cmd, req.arg); sd->card_status |= APP_CMD; switch (req.cmd) { case 6: /* ACMD6: SET_BUS_WIDTH */ + if (sd->spi) { + goto unimplemented_spi_cmd; + } switch (sd->state) { case sd_transfer_state: sd->sd_status[0] &= 0x3f; @@ -1402,42 +1512,41 @@ static sd_rsp_type_t sd_app_command(SDState *sd, sd->state = sd_transfer_state; return sd_r1; } - switch (sd->state) { - case sd_idle_state: - /* If it's the first ACMD41 since reset, we need to decide - * whether to power up. If this is not an enquiry ACMD41, - * we immediately report power on and proceed below to the - * ready state, but if it is, we set a timer to model a - * delay for power up. This works around a bug in EDK2 - * UEFI, which sends an initial enquiry ACMD41, but - * assumes that the card is in ready state as soon as it - * sees the power up bit set. */ - if (!(sd->ocr & OCR_POWER_UP)) { - if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { - timer_del(sd->ocr_power_timer); - sd_ocr_powerup(sd); - } else if (!timer_pending(sd->ocr_power_timer)) { + if (sd->state != sd_idle_state) { + break; + } + /* If it's the first ACMD41 since reset, we need to decide + * whether to power up. If this is not an enquiry ACMD41, + * we immediately report power on and proceed below to the + * ready state, but if it is, we set a timer to model a + * delay for power up. This works around a bug in EDK2 + * UEFI, which sends an initial enquiry ACMD41, but + * assumes that the card is in ready state as soon as it + * sees the power up bit set. */ + if (!FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP)) { + if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { + timer_del(sd->ocr_power_timer); + sd_ocr_powerup(sd); + } else { + trace_sdcard_inquiry_cmd41(); + if (!timer_pending(sd->ocr_power_timer)) { timer_mod_ns(sd->ocr_power_timer, (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + OCR_POWER_DELAY_NS)); } } + } + if (FIELD_EX32(sd->ocr & req.arg, OCR, VDD_VOLTAGE_WINDOW)) { /* We accept any voltage. 10000 V is nothing. * * Once we're powered up, we advance straight to ready state * unless it's an enquiry ACMD41 (bits 23:0 == 0). */ - if (req.arg & ACMD41_ENQUIRY_MASK) { - sd->state = sd_ready_state; - } - - return sd_r3; - - default: - break; + sd->state = sd_ready_state; } - break; + + return sd_r3; case 42: /* ACMD42: SET_CLR_CARD_DETECT */ switch (sd->state) { @@ -1463,9 +1572,27 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } break; + case 18: /* Reserved for SD security applications */ + case 25: + case 26: + case 38: + case 43 ... 49: + /* Refer to the "SD Specifications Part3 Security Specification" for + * information about the SD Security Features. + */ + qemu_log_mask(LOG_UNIMP, "SD: CMD%i Security not implemented\n", + req.cmd); + return sd_illegal; + default: /* Fall back to standard commands. */ return sd_normal_command(sd, req); + + unimplemented_spi_cmd: + /* Commands that are recognised but not yet implemented in SPI mode. */ + qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n", + req.cmd); + return sd_illegal; } qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd); @@ -1488,8 +1615,8 @@ static int cmd_valid_while_locked(SDState *sd, SDRequest *req) if (req->cmd == 16 || req->cmd == 55) { return 1; } - return sd_cmd_class[req->cmd & 0x3F] == 0 - || sd_cmd_class[req->cmd & 0x3F] == 7; + return sd_cmd_class[req->cmd] == 0 + || sd_cmd_class[req->cmd] == 7; } int sd_do_command(SDState *sd, SDRequest *req, @@ -1508,6 +1635,12 @@ int sd_do_command(SDState *sd, SDRequest *req, goto send_response; } + if (req->cmd >= SDMMC_CMD_MAX) { + qemu_log_mask(LOG_GUEST_ERROR, "SD: incorrect command 0x%02x\n", + req->cmd); + req->cmd &= 0x3f; + } + if (sd->card_status & CARD_IS_LOCKED) { if (!cmd_valid_while_locked(sd, req)) { sd->card_status |= ILLEGAL_COMMAND; @@ -1574,10 +1707,12 @@ send_response: case sd_r0: case sd_illegal: - default: rsplen = 0; break; + default: + g_assert_not_reached(); } + trace_sdcard_response(sd_response_name(rtype), rsplen); if (rtype != sd_illegal) { /* Clear the "clear on valid command" status bits now we've @@ -1587,16 +1722,7 @@ send_response: } #ifdef DEBUG_SD - if (rsplen) { - int i; - DPRINTF("Response:"); - for (i = 0; i < rsplen; i++) { - DPRINTF(" %02x", response[i]); - } - DPRINTF(" state %d\n", sd->state); - } else { - DPRINTF("No response %d\n", sd->state); - } + qemu_hexdump((const char *)response, stderr, "Response", rsplen); #endif return rsplen; @@ -1604,8 +1730,7 @@ send_response: static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { - DPRINTF("sd_blk_read: addr = 0x%08llx, len = %d\n", - (unsigned long long) addr, len); + trace_sdcard_read_block(addr, len); if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); } @@ -1613,6 +1738,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { + trace_sdcard_write_block(addr, len); if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } @@ -1639,6 +1765,7 @@ void sd_write_data(SDState *sd, uint8_t value) if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION)) return; + trace_sdcard_write_data(sd->current_cmd, value); switch (sd->current_cmd) { case 24: /* CMD24: WRITE_SINGLE_BLOCK */ sd->data[sd->data_offset ++] = value; @@ -1776,6 +1903,7 @@ uint8_t sd_read_data(SDState *sd) io_len = (sd->ocr & (1 << 30)) ? 512 : sd->blk_len; + trace_sdcard_read_data(sd->current_cmd, io_len); switch (sd->current_cmd) { case 6: /* CMD6: SWITCH_FUNCTION */ ret = sd->data[sd->data_offset ++]; @@ -1792,21 +1920,6 @@ uint8_t sd_read_data(SDState *sd) sd->state = sd_transfer_state; break; - case 11: /* CMD11: READ_DAT_UNTIL_STOP */ - if (sd->data_offset == 0) - BLK_READ_BLOCK(sd->data_start, io_len); - ret = sd->data[sd->data_offset ++]; - - if (sd->data_offset >= io_len) { - sd->data_start += io_len; - sd->data_offset = 0; - if (sd->data_start + io_len > sd->size) { - sd->card_status |= ADDRESS_ERROR; - break; - } - } - break; - case 13: /* ACMD13: SD_STATUS */ ret = sd->sd_status[sd->data_offset ++]; diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h new file mode 100644 index 0000000000..0e96cb0081 --- /dev/null +++ b/hw/sd/sdmmc-internal.h @@ -0,0 +1,15 @@ +/* + * SD/MMC cards common + * + * Copyright (c) 2018 Philippe Mathieu-Daudé <f4bug@amsat.org> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef SD_INTERNAL_H +#define SD_INTERNAL_H + +#define SDMMC_CMD_MAX 64 + +#endif diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index f88f509e0a..ae04b6641b 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -47,7 +47,7 @@ typedef struct { int32_t arglen; int32_t response_pos; int32_t stopping; - SDState *sd; + SDBus sdbus; } ssi_sd_state; #define TYPE_SSI_SD "ssi-sd" @@ -100,7 +100,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) request.arg = (s->cmdarg[0] << 24) | (s->cmdarg[1] << 16) | (s->cmdarg[2] << 8) | s->cmdarg[3]; DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); - s->arglen = sd_do_command(s->sd, &request, longresp); + s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); if (s->arglen <= 0) { s->arglen = 1; s->response[0] = 4; @@ -177,7 +177,7 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) DPRINTF("Response 0x%02x\n", s->response[s->response_pos]); return s->response[s->response_pos++]; } - if (sd_data_ready(s->sd)) { + if (sdbus_data_ready(&s->sdbus)) { DPRINTF("Data read\n"); s->mode = SSI_SD_DATA_START; } else { @@ -190,8 +190,8 @@ static uint32_t ssi_sd_transfer(SSISlave *dev, uint32_t val) s->mode = SSI_SD_DATA_READ; return 0xfe; case SSI_SD_DATA_READ: - val = sd_read_data(s->sd); - if (!sd_data_ready(s->sd)) { + val = sdbus_read_data(&s->sdbus); + if (!sdbus_data_ready(&s->sdbus)) { DPRINTF("Data read end\n"); s->mode = SSI_SD_CMD; } @@ -242,13 +242,24 @@ static const VMStateDescription vmstate_ssi_sd = { static void ssi_sd_realize(SSISlave *d, Error **errp) { ssi_sd_state *s = FROM_SSI_SLAVE(ssi_sd_state, d); + DeviceState *carddev; DriveInfo *dinfo; + Error *err = NULL; + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, + DEVICE(d), "sd-bus"); + + /* Create and plug in the sd card */ /* FIXME use a qdev drive property instead of drive_get_next() */ dinfo = drive_get_next(IF_SD); - s->sd = sd_init(dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, true); - if (s->sd == NULL) { - error_setg(errp, "Device initialization failed."); + carddev = qdev_create(&s->sdbus.qbus, TYPE_SD_CARD); + if (dinfo) { + qdev_prop_set_drive(carddev, "drive", blk_by_legacy_dinfo(dinfo), &err); + } + object_property_set_bool(OBJECT(carddev), true, "spi", &err); + object_property_set_bool(OBJECT(carddev), true, "realized", &err); + if (err) { + error_setg(errp, "failed to init SD card: %s", error_get_pretty(err)); return; } } @@ -264,11 +275,6 @@ static void ssi_sd_reset(DeviceState *dev) s->arglen = 0; s->response_pos = 0; s->stopping = 0; - - /* Since we're still using the legacy SD API the card is not plugged - * into any bus, and we must reset it manually. - */ - device_reset(DEVICE(s->sd)); } static void ssi_sd_class_init(ObjectClass *klass, void *data) diff --git a/hw/sd/trace-events b/hw/sd/trace-events index 0f8536db32..3040d32560 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -23,6 +23,26 @@ sdhci_read_dataport(uint16_t data_count) "all %u bytes of data have been read fr sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of data" sdhci_capareg(const char *desc, uint16_t val) "%s: %u" +# hw/sd/sd.c +sdcard_normal_command(uint8_t cmd, uint32_t arg, const char *state) "CMD%d arg 0x%08x (state %s)" +sdcard_app_command(uint8_t acmd, uint32_t arg) "ACMD%d arg 0x%08x" +sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" +sdcard_powerup(void) "" +sdcard_inquiry_cmd41(void) "" +sdcard_set_enable(bool current_state, bool new_state) "%u -> %u" +sdcard_reset(void) "" +sdcard_set_blocklen(uint16_t length) "0x%04x" +sdcard_inserted(bool readonly) "read_only: %u" +sdcard_ejected(void) "" +sdcard_erase(void) "" +sdcard_lock(void) "" +sdcard_unlock(void) "" +sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" +sdcard_write_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" +sdcard_write_data(uint8_t cmd, uint8_t value) "CMD%02d value 0x%02x" +sdcard_read_data(uint8_t cmd, int length) "CMD%02d len %d" +sdcard_set_voltage(uint16_t millivolts) "%u mV" + # hw/sd/milkymist-memcard.c milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_memcard_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 94c2e94f10..6ecf70a79b 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -46,6 +46,9 @@ enum mtp_code { CMD_GET_OBJECT_HANDLES = 0x1007, CMD_GET_OBJECT_INFO = 0x1008, CMD_GET_OBJECT = 0x1009, + CMD_DELETE_OBJECT = 0x100b, + CMD_SEND_OBJECT_INFO = 0x100c, + CMD_SEND_OBJECT = 0x100d, CMD_GET_PARTIAL_OBJECT = 0x101b, CMD_GET_OBJECT_PROPS_SUPPORTED = 0x9801, CMD_GET_OBJECT_PROP_DESC = 0x9802, @@ -62,7 +65,13 @@ enum mtp_code { RES_INVALID_STORAGE_ID = 0x2008, RES_INVALID_OBJECT_HANDLE = 0x2009, RES_INVALID_OBJECT_FORMAT_CODE = 0x200b, + RES_STORE_FULL = 0x200c, + RES_STORE_READ_ONLY = 0x200e, + RES_PARTIAL_DELETE = 0x2012, + RES_STORE_NOT_AVAILABLE = 0x2013, RES_SPEC_BY_FORMAT_UNSUPPORTED = 0x2014, + RES_INVALID_OBJECTINFO = 0x2015, + RES_DESTINATION_UNSUPPORTED = 0x2020, RES_INVALID_PARENT_OBJECT = 0x201a, RES_INVALID_PARAMETER = 0x201d, RES_SESSION_ALREADY_OPEN = 0x201e, @@ -172,6 +181,7 @@ struct MTPState { MTPControl *result; uint32_t session; uint32_t next_handle; + bool readonly; QTAILQ_HEAD(, MTPObject) objects; #ifdef CONFIG_INOTIFY1 @@ -179,8 +189,44 @@ struct MTPState { int inotifyfd; QTAILQ_HEAD(events, MTPMonEntry) events; #endif + /* Responder is expecting a write operation */ + bool write_pending; + struct { + uint32_t parent_handle; + uint16_t format; + uint32_t size; + char *filename; + } dataset; }; +/* + * ObjectInfo dataset received from initiator + * Fields we don't care about are ignored + */ +typedef struct { + uint32_t storage_id; /*unused*/ + uint16_t format; + uint16_t protection_status; /*unused*/ + uint32_t size; + uint16_t thumb_format; /*unused*/ + uint32_t thumb_comp_sz; /*unused*/ + uint32_t thumb_pix_width; /*unused*/ + uint32_t thumb_pix_height; /*unused*/ + uint32_t image_pix_width; /*unused*/ + uint32_t image_pix_height; /*unused*/ + uint32_t image_bit_depth; /*unused*/ + uint32_t parent; /*unused*/ + uint16_t assoc_type; + uint32_t assoc_desc; + uint32_t seq_no; /*unused*/ + uint8_t length; /*part of filename field*/ + uint16_t filename[0]; + char date_created[0]; /*unused*/ + char date_modified[0]; /*unused*/ + char keywords[0]; /*unused*/ + /* string and other data follows */ +} QEMU_PACKED ObjectInfo; + #define TYPE_USB_MTP "usb-mtp" #define USB_MTP(obj) OBJECT_CHECK(MTPState, (obj), TYPE_USB_MTP) @@ -422,7 +468,6 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, return child; } -#ifdef CONFIG_INOTIFY1 static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, char *name, int len) { @@ -437,6 +482,7 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, return NULL; } +#ifdef CONFIG_INOTIFY1 static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) { MTPObject *iter; @@ -540,9 +586,8 @@ static void inotify_watchfn(void *arg) break; case IN_IGNORED: - o = usb_mtp_object_lookup_name(parent, event->name, event->len); - trace_usb_mtp_inotify_event(s->dev.addr, o->path, - event->mask, "Obj ignored"); + trace_usb_mtp_inotify_event(s->dev.addr, parent->path, + event->mask, "Obj parent dir ignored"); break; default: @@ -765,7 +810,8 @@ static void usb_mtp_add_time(MTPData *data, time_t time) /* ----------------------------------------------------------------------- */ static void usb_mtp_queue_result(MTPState *s, uint16_t code, uint32_t trans, - int argc, uint32_t arg0, uint32_t arg1) + int argc, uint32_t arg0, uint32_t arg1, + uint32_t arg2) { MTPControl *c = g_new0(MTPControl, 1); @@ -778,6 +824,9 @@ static void usb_mtp_queue_result(MTPState *s, uint16_t code, uint32_t trans, if (argc > 1) { c->argv[1] = arg1; } + if (argc > 2) { + c->argv[2] = arg2; + } assert(s->result == NULL); s->result = c; @@ -796,6 +845,9 @@ static MTPData *usb_mtp_get_device_info(MTPState *s, MTPControl *c) CMD_GET_NUM_OBJECTS, CMD_GET_OBJECT_HANDLES, CMD_GET_OBJECT_INFO, + CMD_DELETE_OBJECT, + CMD_SEND_OBJECT_INFO, + CMD_SEND_OBJECT, CMD_GET_OBJECT, CMD_GET_PARTIAL_OBJECT, CMD_GET_OBJECT_PROPS_SUPPORTED, @@ -1110,16 +1162,126 @@ static MTPData *usb_mtp_get_object_prop_value(MTPState *s, MTPControl *c, return d; } +/* Return correct return code for a delete event */ +enum { + ALL_DELETE, + PARTIAL_DELETE, + READ_ONLY, +}; + +/* Assumes that children, if any, have been already freed */ +static void usb_mtp_object_free_one(MTPState *s, MTPObject *o) +{ +#ifndef CONFIG_INOTIFY1 + assert(o->nchildren == 0); + QTAILQ_REMOVE(&s->objects, o, next); + g_free(o->name); + g_free(o->path); + g_free(o); +#endif +} + +static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) +{ + MTPObject *iter, *iter2; + bool partial_delete = false; + bool success = false; + + /* + * TODO: Add support for Protection Status + */ + + QLIST_FOREACH(iter, &o->children, list) { + if (iter->format == FMT_ASSOCIATION) { + QLIST_FOREACH(iter2, &iter->children, list) { + usb_mtp_deletefn(s, iter2, trans); + } + } + } + + if (o->format == FMT_UNDEFINED_OBJECT) { + if (remove(o->path)) { + partial_delete = true; + } else { + usb_mtp_object_free_one(s, o); + success = true; + } + } + + if (o->format == FMT_ASSOCIATION) { + if (rmdir(o->path)) { + partial_delete = true; + } else { + usb_mtp_object_free_one(s, o); + success = true; + } + } + + if (success && partial_delete) { + return PARTIAL_DELETE; + } + if (!success && partial_delete) { + return READ_ONLY; + } + return ALL_DELETE; +} + +static void usb_mtp_object_delete(MTPState *s, uint32_t handle, + uint32_t format_code, uint32_t trans) +{ + MTPObject *o; + int ret; + + /* Return error if store is read-only */ + if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { + usb_mtp_queue_result(s, RES_STORE_READ_ONLY, + trans, 0, 0, 0, 0); + return; + } + + if (format_code != 0) { + usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, + trans, 0, 0, 0, 0); + return; + } + + if (handle == 0xFFFFFFF) { + o = QTAILQ_FIRST(&s->objects); + } else { + o = usb_mtp_object_lookup(s, handle); + } + if (o == NULL) { + usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, + trans, 0, 0, 0, 0); + return; + } + + ret = usb_mtp_deletefn(s, o, trans); + if (ret == PARTIAL_DELETE) { + usb_mtp_queue_result(s, RES_PARTIAL_DELETE, + trans, 0, 0, 0, 0); + return; + } else if (ret == READ_ONLY) { + usb_mtp_queue_result(s, RES_STORE_READ_ONLY, trans, + 0, 0, 0, 0); + return; + } else { + usb_mtp_queue_result(s, RES_OK, trans, + 0, 0, 0, 0); + return; + } +} + static void usb_mtp_command(MTPState *s, MTPControl *c) { MTPData *data_in = NULL; - MTPObject *o; + MTPObject *o = NULL; uint32_t nres = 0, res0 = 0; /* sanity checks */ if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { usb_mtp_queue_result(s, RES_SESSION_NOT_OPEN, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } @@ -1131,12 +1293,12 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) case CMD_OPEN_SESSION: if (s->session) { usb_mtp_queue_result(s, RES_SESSION_ALREADY_OPEN, - c->trans, 1, s->session, 0); + c->trans, 1, s->session, 0, 0); return; } if (c->argv[0] == 0) { usb_mtp_queue_result(s, RES_INVALID_PARAMETER, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } trace_usb_mtp_op_open_session(s->dev.addr); @@ -1165,7 +1327,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) if (c->argv[0] != QEMU_STORAGE_ID && c->argv[0] != 0xffffffff) { usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } data_in = usb_mtp_get_storage_info(s, c); @@ -1175,12 +1337,12 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) if (c->argv[0] != QEMU_STORAGE_ID && c->argv[0] != 0xffffffff) { usb_mtp_queue_result(s, RES_INVALID_STORAGE_ID, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } if (c->argv[1] != 0x00000000) { usb_mtp_queue_result(s, RES_SPEC_BY_FORMAT_UNSUPPORTED, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } if (c->argv[2] == 0x00000000 || @@ -1191,12 +1353,12 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) } if (o == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } if (o->format != FMT_ASSOCIATION) { usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } usb_mtp_object_readdir(s, o); @@ -1212,7 +1374,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) o = usb_mtp_object_lookup(s, c->argv[0]); if (o == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } data_in = usb_mtp_get_object_info(s, c, o); @@ -1221,47 +1383,98 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) o = usb_mtp_object_lookup(s, c->argv[0]); if (o == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } if (o->format == FMT_ASSOCIATION) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } data_in = usb_mtp_get_object(s, c, o); if (data_in == NULL) { usb_mtp_queue_result(s, RES_GENERAL_ERROR, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } break; + case CMD_DELETE_OBJECT: + usb_mtp_object_delete(s, c->argv[0], c->argv[1], c->trans); + return; case CMD_GET_PARTIAL_OBJECT: o = usb_mtp_object_lookup(s, c->argv[0]); if (o == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } if (o->format == FMT_ASSOCIATION) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } data_in = usb_mtp_get_partial_object(s, c, o); if (data_in == NULL) { usb_mtp_queue_result(s, RES_GENERAL_ERROR, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } nres = 1; res0 = data_in->length; break; + case CMD_SEND_OBJECT_INFO: + /* Return error if store is read-only */ + if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { + usb_mtp_queue_result(s, RES_STORE_READ_ONLY, + c->trans, 0, 0, 0, 0); + } else if (c->argv[0] && (c->argv[0] != QEMU_STORAGE_ID)) { + /* First parameter points to storage id or is 0 */ + usb_mtp_queue_result(s, RES_STORE_NOT_AVAILABLE, c->trans, + 0, 0, 0, 0); + } else if (c->argv[1] && !c->argv[0]) { + /* If second parameter is specified, first must also be specified */ + usb_mtp_queue_result(s, RES_DESTINATION_UNSUPPORTED, c->trans, + 0, 0, 0, 0); + } else { + uint32_t handle = c->argv[1]; + if (handle == 0xFFFFFFFF || handle == 0) { + /* root object */ + o = QTAILQ_FIRST(&s->objects); + } else { + o = usb_mtp_object_lookup(s, handle); + } + if (o == NULL) { + usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, c->trans, + 0, 0, 0, 0); + } + if (o->format != FMT_ASSOCIATION) { + usb_mtp_queue_result(s, RES_INVALID_PARENT_OBJECT, c->trans, + 0, 0, 0, 0); + } + } + if (o) { + s->dataset.parent_handle = o->handle; + } + s->data_out = usb_mtp_data_alloc(c); + return; + case CMD_SEND_OBJECT: + if (!FLAG_SET(s, MTP_FLAG_WRITABLE)) { + usb_mtp_queue_result(s, RES_STORE_READ_ONLY, + c->trans, 0, 0, 0, 0); + return; + } + if (!s->write_pending) { + usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, + c->trans, 0, 0, 0, 0); + return; + } + s->data_out = usb_mtp_data_alloc(c); + return; case CMD_GET_OBJECT_PROPS_SUPPORTED: if (c->argv[0] != FMT_UNDEFINED_OBJECT && c->argv[0] != FMT_ASSOCIATION) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } data_in = usb_mtp_get_object_props_supported(s, c); @@ -1270,13 +1483,13 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) if (c->argv[1] != FMT_UNDEFINED_OBJECT && c->argv[1] != FMT_ASSOCIATION) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_FORMAT_CODE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } data_in = usb_mtp_get_object_prop_desc(s, c); if (data_in == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } break; @@ -1284,20 +1497,20 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) o = usb_mtp_object_lookup(s, c->argv[0]); if (o == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_HANDLE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } data_in = usb_mtp_get_object_prop_value(s, c, o); if (data_in == NULL) { usb_mtp_queue_result(s, RES_INVALID_OBJECT_PROP_CODE, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } break; default: trace_usb_mtp_op_unknown(s->dev.addr, c->code); usb_mtp_queue_result(s, RES_OPERATION_NOT_SUPPORTED, - c->trans, 0, 0, 0); + c->trans, 0, 0, 0, 0); return; } @@ -1306,7 +1519,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) assert(s->data_in == NULL); s->data_in = data_in; } - usb_mtp_queue_result(s, RES_OK, c->trans, nres, res0, 0); + usb_mtp_queue_result(s, RES_OK, c->trans, nres, res0, 0, 0); } /* ----------------------------------------------------------------------- */ @@ -1351,12 +1564,191 @@ static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) fprintf(stderr, "%s\n", __func__); } +static void utf16_to_str(uint8_t len, uint16_t *arr, char *name) +{ + int count; + wchar_t *wstr = g_new0(wchar_t, len); + + for (count = 0; count < len; count++) { + wstr[count] = (wchar_t)arr[count]; + } + + wcstombs(name, wstr, len); + g_free(wstr); +} + +static void usb_mtp_write_data(MTPState *s) +{ + MTPData *d = s->data_out; + MTPObject *parent = + usb_mtp_object_lookup(s, s->dataset.parent_handle); + char *path = NULL; + int rc = -1; + mode_t mask = 0644; + + assert(d != NULL); + + if (parent == NULL || !s->write_pending) { + usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, + 0, 0, 0, 0); + return; + } + + if (s->dataset.filename) { + path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); + if (s->dataset.format == FMT_ASSOCIATION) { + d->fd = mkdir(path, mask); + goto free; + } + if (s->dataset.size < d->length) { + usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, + 0, 0, 0, 0); + goto done; + } + d->fd = open(path, O_CREAT | O_WRONLY, mask); + if (d->fd == -1) { + usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, + 0, 0, 0, 0); + goto done; + } + + /* + * Return success if initiator sent 0 sized data + */ + if (!s->dataset.size) { + goto success; + } + + rc = write(d->fd, d->data, s->dataset.size); + if (rc == -1) { + usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, + 0, 0, 0, 0); + goto done; + } + if (rc != s->dataset.size) { + usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, + 0, 0, 0, 0); + goto done; + } + } + +success: + usb_mtp_queue_result(s, RES_OK, d->trans, + 0, 0, 0, 0); + +done: + /* + * The write dataset is kept around and freed only + * on success or if another write request comes in + */ + if (d->fd != -1) { + close(d->fd); + } +free: + g_free(s->dataset.filename); + g_free(path); + s->write_pending = false; +} + +static void usb_mtp_write_metadata(MTPState *s) +{ + MTPData *d = s->data_out; + ObjectInfo *dataset = (ObjectInfo *)d->data; + char *filename = g_new0(char, dataset->length); + MTPObject *o; + MTPObject *p = usb_mtp_object_lookup(s, s->dataset.parent_handle); + uint32_t next_handle = s->next_handle; + + assert(!s->write_pending); + + utf16_to_str(dataset->length, dataset->filename, filename); + + o = usb_mtp_object_lookup_name(p, filename, dataset->length); + if (o != NULL) { + next_handle = o->handle; + } + + s->dataset.filename = filename; + s->dataset.format = dataset->format; + s->dataset.size = dataset->size; + s->dataset.filename = filename; + s->write_pending = true; + + if (s->dataset.format == FMT_ASSOCIATION) { + usb_mtp_write_data(s); + /* next_handle will be allocated to the newly created dir */ + if (d->fd == -1) { + usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, + 0, 0, 0, 0); + return; + } + d->fd = -1; + } + + usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID, + s->dataset.parent_handle, next_handle); +} + +static void usb_mtp_get_data(MTPState *s, mtp_container *container, + USBPacket *p) +{ + MTPData *d = s->data_out; + uint64_t dlen; + uint32_t data_len = p->iov.size; + + if (d->first) { + /* Total length of incoming data */ + d->length = cpu_to_le32(container->length) - sizeof(mtp_container); + /* Length of data in this packet */ + data_len -= sizeof(mtp_container); + usb_mtp_realloc(d, d->length); + d->offset = 0; + d->first = false; + } + + if (d->length - d->offset > data_len) { + dlen = data_len; + } else { + dlen = d->length - d->offset; + } + + switch (d->code) { + case CMD_SEND_OBJECT_INFO: + usb_packet_copy(p, d->data + d->offset, dlen); + d->offset += dlen; + if (d->offset == d->length) { + /* The operation might have already failed */ + if (!s->result) { + usb_mtp_write_metadata(s); + } + usb_mtp_data_free(s->data_out); + s->data_out = NULL; + return; + } + break; + case CMD_SEND_OBJECT: + usb_packet_copy(p, d->data + d->offset, dlen); + d->offset += dlen; + if (d->offset == d->length) { + usb_mtp_write_data(s); + usb_mtp_data_free(s->data_out); + s->data_out = NULL; + return; + } + break; + default: + p->status = USB_RET_STALL; + return; + } +} + static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) { MTPState *s = USB_MTP(dev); MTPControl cmd; mtp_container container; uint32_t params[5]; + uint16_t container_type; int i, rc; switch (p->ep->nr) { @@ -1446,8 +1838,13 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) p->status = USB_RET_STALL; return; } - usb_packet_copy(p, &container, sizeof(container)); - switch (le16_to_cpu(container.type)) { + if (s->data_out && !s->data_out->first) { + container_type = TYPE_DATA; + } else { + usb_packet_copy(p, &container, sizeof(container)); + container_type = le16_to_cpu(container.type); + } + switch (container_type) { case TYPE_COMMAND: if (s->data_in || s->data_out || s->result) { trace_usb_mtp_stall(s->dev.addr, "transaction inflight"); @@ -1478,6 +1875,15 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) (cmd.argc > 4) ? cmd.argv[4] : 0); usb_mtp_command(s, &cmd); break; + case TYPE_DATA: + /* One of the previous transfers has already errored but the + * responder is still sending data associated with it + */ + if (s->result != NULL) { + return; + } + usb_mtp_get_data(s, &container, p); + break; default: /* not needed as long as the mtp device is read-only */ p->status = USB_RET_STALL; @@ -1542,6 +1948,10 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp) return; } s->desc = strrchr(s->root, '/'); + /* Mark store as RW */ + if (!s->readonly) { + s->flags |= (1 << MTP_FLAG_WRITABLE); + } if (s->desc && s->desc[0]) { s->desc = g_strdup(s->desc + 1); } else { @@ -1564,6 +1974,7 @@ static const VMStateDescription vmstate_usb_mtp = { static Property mtp_properties[] = { DEFINE_PROP_STRING("x-root", MTPState, root), DEFINE_PROP_STRING("desc", MTPState, desc), + DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), DEFINE_PROP_END_OF_LIST(), }; |