Rearrange SCSI disk emulation code.
Add USB mass storage device emulation. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1940 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
e6f3e5e016
commit
2e5d83bbef
@ -306,8 +306,11 @@ ifdef CONFIG_ADLIB
|
|||||||
SOUND_HW += fmopl.o adlib.o
|
SOUND_HW += fmopl.o adlib.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
# SCSI layer
|
||||||
|
VL_OBJS+= scsi-disk.o cdrom.o
|
||||||
|
|
||||||
# USB layer
|
# USB layer
|
||||||
VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o
|
VL_OBJS+= usb.o usb-hub.o usb-linux.o usb-hid.o usb-ohci.o usb-msd.o
|
||||||
|
|
||||||
# PCI network cards
|
# PCI network cards
|
||||||
VL_OBJS+= ne2000.o rtl8139.o
|
VL_OBJS+= ne2000.o rtl8139.o
|
||||||
|
156
hw/cdrom.c
Normal file
156
hw/cdrom.c
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
/*
|
||||||
|
* QEMU ATAPI CD-ROM Emulator
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ??? Most of the ATAPI emulation is still in ide.c. It should be moved
|
||||||
|
here. */
|
||||||
|
|
||||||
|
#include <vl.h>
|
||||||
|
|
||||||
|
static void lba_to_msf(uint8_t *buf, int lba)
|
||||||
|
{
|
||||||
|
lba += 150;
|
||||||
|
buf[0] = (lba / 75) / 60;
|
||||||
|
buf[1] = (lba / 75) % 60;
|
||||||
|
buf[2] = lba % 75;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* same toc as bochs. Return -1 if error or the toc length */
|
||||||
|
/* XXX: check this */
|
||||||
|
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
|
||||||
|
{
|
||||||
|
uint8_t *q;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (start_track > 1 && start_track != 0xaa)
|
||||||
|
return -1;
|
||||||
|
q = buf + 2;
|
||||||
|
*q++ = 1; /* first session */
|
||||||
|
*q++ = 1; /* last session */
|
||||||
|
if (start_track <= 1) {
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
*q++ = 0x14; /* ADR, control */
|
||||||
|
*q++ = 1; /* track number */
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
if (msf) {
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
lba_to_msf(q, 0);
|
||||||
|
q += 3;
|
||||||
|
} else {
|
||||||
|
/* sector 0 */
|
||||||
|
cpu_to_be32wu((uint32_t *)q, 0);
|
||||||
|
q += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* lead out track */
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
*q++ = 0x16; /* ADR, control */
|
||||||
|
*q++ = 0xaa; /* track number */
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
if (msf) {
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
lba_to_msf(q, nb_sectors);
|
||||||
|
q += 3;
|
||||||
|
} else {
|
||||||
|
cpu_to_be32wu((uint32_t *)q, nb_sectors);
|
||||||
|
q += 4;
|
||||||
|
}
|
||||||
|
len = q - buf;
|
||||||
|
cpu_to_be16wu((uint16_t *)buf, len - 2);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* mostly same info as PearPc */
|
||||||
|
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num)
|
||||||
|
{
|
||||||
|
uint8_t *q;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
q = buf + 2;
|
||||||
|
*q++ = 1; /* first session */
|
||||||
|
*q++ = 1; /* last session */
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* data track */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 0xa0; /* lead-in */
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 1; /* first track */
|
||||||
|
*q++ = 0x00; /* disk type */
|
||||||
|
*q++ = 0x00;
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* data track */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 0xa1;
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 1; /* last track */
|
||||||
|
*q++ = 0x00;
|
||||||
|
*q++ = 0x00;
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* data track */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 0xa2; /* lead-out */
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
if (msf) {
|
||||||
|
*q++ = 0; /* reserved */
|
||||||
|
lba_to_msf(q, nb_sectors);
|
||||||
|
q += 3;
|
||||||
|
} else {
|
||||||
|
cpu_to_be32wu((uint32_t *)q, nb_sectors);
|
||||||
|
q += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
*q++ = 1; /* session number */
|
||||||
|
*q++ = 0x14; /* ADR, control */
|
||||||
|
*q++ = 0; /* track number */
|
||||||
|
*q++ = 1; /* point */
|
||||||
|
*q++ = 0; /* min */
|
||||||
|
*q++ = 0; /* sec */
|
||||||
|
*q++ = 0; /* frame */
|
||||||
|
if (msf) {
|
||||||
|
*q++ = 0;
|
||||||
|
lba_to_msf(q, 0);
|
||||||
|
q += 3;
|
||||||
|
} else {
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 0;
|
||||||
|
*q++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = q - buf;
|
||||||
|
cpu_to_be16wu((uint16_t *)buf, len - 2);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
417
hw/esp.c
417
hw/esp.c
@ -38,17 +38,14 @@ do { printf("ESP: set_irq(%d): %d\n", (irq), (level)); pic_set_irq((irq),(level)
|
|||||||
#define ESPDMA_REGS 4
|
#define ESPDMA_REGS 4
|
||||||
#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
|
#define ESPDMA_MAXADDR (ESPDMA_REGS * 4 - 1)
|
||||||
#define ESP_MAXREG 0x3f
|
#define ESP_MAXREG 0x3f
|
||||||
#define TI_BUFSZ 1024*1024 // XXX
|
#define TI_BUFSZ 32
|
||||||
#define DMA_VER 0xa0000000
|
#define DMA_VER 0xa0000000
|
||||||
#define DMA_INTR 1
|
#define DMA_INTR 1
|
||||||
#define DMA_INTREN 0x10
|
#define DMA_INTREN 0x10
|
||||||
|
#define DMA_WRITE_MEM 0x100
|
||||||
#define DMA_LOADED 0x04000000
|
#define DMA_LOADED 0x04000000
|
||||||
typedef struct ESPState ESPState;
|
typedef struct ESPState ESPState;
|
||||||
|
|
||||||
typedef int ESPDMAFunc(ESPState *s,
|
|
||||||
target_phys_addr_t phys_addr,
|
|
||||||
int transfer_size1);
|
|
||||||
|
|
||||||
struct ESPState {
|
struct ESPState {
|
||||||
BlockDriverState **bd;
|
BlockDriverState **bd;
|
||||||
uint8_t rregs[ESP_MAXREG];
|
uint8_t rregs[ESP_MAXREG];
|
||||||
@ -57,12 +54,10 @@ struct ESPState {
|
|||||||
uint32_t espdmaregs[ESPDMA_REGS];
|
uint32_t espdmaregs[ESPDMA_REGS];
|
||||||
uint32_t ti_size;
|
uint32_t ti_size;
|
||||||
uint32_t ti_rptr, ti_wptr;
|
uint32_t ti_rptr, ti_wptr;
|
||||||
int ti_dir;
|
|
||||||
uint8_t ti_buf[TI_BUFSZ];
|
uint8_t ti_buf[TI_BUFSZ];
|
||||||
int dma;
|
int dma;
|
||||||
ESPDMAFunc *dma_cb;
|
SCSIDevice *scsi_dev[MAX_DISKS];
|
||||||
int64_t offset, len;
|
SCSIDevice *current_dev;
|
||||||
int target;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define STAT_DO 0x00
|
#define STAT_DO 0x00
|
||||||
@ -83,195 +78,33 @@ struct ESPState {
|
|||||||
#define SEQ_0 0x0
|
#define SEQ_0 0x0
|
||||||
#define SEQ_CD 0x4
|
#define SEQ_CD 0x4
|
||||||
|
|
||||||
/* XXX: stolen from ide.c, move to common ATAPI/SCSI library */
|
|
||||||
static void lba_to_msf(uint8_t *buf, int lba)
|
|
||||||
{
|
|
||||||
lba += 150;
|
|
||||||
buf[0] = (lba / 75) / 60;
|
|
||||||
buf[1] = (lba / 75) % 60;
|
|
||||||
buf[2] = lba % 75;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void cpu_to_ube16(uint8_t *buf, int val)
|
|
||||||
{
|
|
||||||
buf[0] = val >> 8;
|
|
||||||
buf[1] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void cpu_to_ube32(uint8_t *buf, unsigned int val)
|
|
||||||
{
|
|
||||||
buf[0] = val >> 24;
|
|
||||||
buf[1] = val >> 16;
|
|
||||||
buf[2] = val >> 8;
|
|
||||||
buf[3] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* same toc as bochs. Return -1 if error or the toc length */
|
|
||||||
/* XXX: check this */
|
|
||||||
static int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track)
|
|
||||||
{
|
|
||||||
uint8_t *q;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (start_track > 1 && start_track != 0xaa)
|
|
||||||
return -1;
|
|
||||||
q = buf + 2;
|
|
||||||
*q++ = 1; /* first session */
|
|
||||||
*q++ = 1; /* last session */
|
|
||||||
if (start_track <= 1) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
*q++ = 0x14; /* ADR, control */
|
|
||||||
*q++ = 1; /* track number */
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
lba_to_msf(q, 0);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
/* sector 0 */
|
|
||||||
cpu_to_ube32(q, 0);
|
|
||||||
q += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* lead out track */
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
*q++ = 0x16; /* ADR, control */
|
|
||||||
*q++ = 0xaa; /* track number */
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
lba_to_msf(q, nb_sectors);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
cpu_to_ube32(q, nb_sectors);
|
|
||||||
q += 4;
|
|
||||||
}
|
|
||||||
len = q - buf;
|
|
||||||
cpu_to_ube16(buf, len - 2);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* mostly same info as PearPc */
|
|
||||||
static int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf,
|
|
||||||
int session_num)
|
|
||||||
{
|
|
||||||
uint8_t *q;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
q = buf + 2;
|
|
||||||
*q++ = 1; /* first session */
|
|
||||||
*q++ = 1; /* last session */
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* data track */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 0xa0; /* lead-in */
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 1; /* first track */
|
|
||||||
*q++ = 0x00; /* disk type */
|
|
||||||
*q++ = 0x00;
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* data track */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 0xa1;
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 1; /* last track */
|
|
||||||
*q++ = 0x00;
|
|
||||||
*q++ = 0x00;
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* data track */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 0xa2; /* lead-out */
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
lba_to_msf(q, nb_sectors);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
cpu_to_ube32(q, nb_sectors);
|
|
||||||
q += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* ADR, control */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 1; /* point */
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0;
|
|
||||||
lba_to_msf(q, 0);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = q - buf;
|
|
||||||
cpu_to_ube16(buf, len - 2);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int esp_write_dma_cb(ESPState *s,
|
|
||||||
target_phys_addr_t phys_addr,
|
|
||||||
int transfer_size1)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
if (bdrv_get_type_hint(s->bd[s->target]) == BDRV_TYPE_CDROM) {
|
|
||||||
len = transfer_size1/2048;
|
|
||||||
} else {
|
|
||||||
len = transfer_size1/512;
|
|
||||||
}
|
|
||||||
DPRINTF("Write callback (offset %lld len %lld size %d trans_size %d)\n",
|
|
||||||
s->offset, s->len, s->ti_size, transfer_size1);
|
|
||||||
|
|
||||||
bdrv_write(s->bd[s->target], s->offset, s->ti_buf+s->ti_rptr, len);
|
|
||||||
s->offset+=len;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_satn(ESPState *s)
|
static void handle_satn(ESPState *s)
|
||||||
{
|
{
|
||||||
uint8_t buf[32];
|
uint8_t buf[32];
|
||||||
uint32_t dmaptr, dmalen;
|
uint32_t dmaptr, dmalen;
|
||||||
unsigned int i;
|
|
||||||
int64_t nb_sectors;
|
|
||||||
int target;
|
int target;
|
||||||
|
int32_t datalen;
|
||||||
|
|
||||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||||
target = s->wregs[4] & 7;
|
target = s->wregs[4] & 7;
|
||||||
DPRINTF("Select with ATN len %d target %d\n", dmalen, target);
|
DPRINTF("Select with ATN len %d target %d\n", dmalen, target);
|
||||||
if (s->dma) {
|
if (s->dma) {
|
||||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||||
DPRINTF("DMA Direction: %c, addr 0x%8.8x\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr);
|
DPRINTF("DMA Direction: %c, addr 0x%8.8x\n",
|
||||||
|
s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r', dmaptr);
|
||||||
cpu_physical_memory_read(dmaptr, buf, dmalen);
|
cpu_physical_memory_read(dmaptr, buf, dmalen);
|
||||||
} else {
|
} else {
|
||||||
buf[0] = 0;
|
buf[0] = 0;
|
||||||
memcpy(&buf[1], s->ti_buf, dmalen);
|
memcpy(&buf[1], s->ti_buf, dmalen);
|
||||||
dmalen++;
|
dmalen++;
|
||||||
}
|
}
|
||||||
for (i = 0; i < dmalen; i++) {
|
|
||||||
DPRINTF("Command %2.2x\n", buf[i]);
|
|
||||||
}
|
|
||||||
s->ti_dir = 0;
|
|
||||||
s->ti_size = 0;
|
s->ti_size = 0;
|
||||||
s->ti_rptr = 0;
|
s->ti_rptr = 0;
|
||||||
s->ti_wptr = 0;
|
s->ti_wptr = 0;
|
||||||
|
|
||||||
if (target >= 4 || !s->bd[target]) { // No such drive
|
if (target >= 4 || !s->scsi_dev[target]) {
|
||||||
|
// No such drive
|
||||||
s->rregs[4] = STAT_IN;
|
s->rregs[4] = STAT_IN;
|
||||||
s->rregs[5] = INTR_DC;
|
s->rregs[5] = INTR_DC;
|
||||||
s->rregs[6] = SEQ_0;
|
s->rregs[6] = SEQ_0;
|
||||||
@ -279,141 +112,20 @@ static void handle_satn(ESPState *s)
|
|||||||
pic_set_irq(s->irq, 1);
|
pic_set_irq(s->irq, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (buf[1]) {
|
s->current_dev = s->scsi_dev[target];
|
||||||
case 0x0:
|
datalen = scsi_send_command(s->current_dev, 0, &buf[1]);
|
||||||
DPRINTF("Test Unit Ready (len %d)\n", buf[5]);
|
if (datalen == 0) {
|
||||||
break;
|
s->ti_size = 0;
|
||||||
case 0x12:
|
} else {
|
||||||
DPRINTF("Inquiry (len %d)\n", buf[5]);
|
s->rregs[4] = STAT_IN | STAT_TC;
|
||||||
memset(s->ti_buf, 0, 36);
|
if (datalen > 0) {
|
||||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
|
s->rregs[4] |= STAT_DI;
|
||||||
s->ti_buf[0] = 5;
|
s->ti_size = datalen;
|
||||||
memcpy(&s->ti_buf[16], "QEMU CDROM ", 16);
|
} else {
|
||||||
} else {
|
s->rregs[4] |= STAT_DO;
|
||||||
s->ti_buf[0] = 0;
|
s->ti_size = -datalen;
|
||||||
memcpy(&s->ti_buf[16], "QEMU HARDDISK ", 16);
|
|
||||||
}
|
|
||||||
memcpy(&s->ti_buf[8], "QEMU ", 8);
|
|
||||||
s->ti_buf[2] = 1;
|
|
||||||
s->ti_buf[3] = 2;
|
|
||||||
s->ti_buf[4] = 32;
|
|
||||||
s->ti_dir = 1;
|
|
||||||
s->ti_size = 36;
|
|
||||||
break;
|
|
||||||
case 0x1a:
|
|
||||||
DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[3], buf[5]);
|
|
||||||
break;
|
|
||||||
case 0x25:
|
|
||||||
DPRINTF("Read Capacity (len %d)\n", buf[5]);
|
|
||||||
memset(s->ti_buf, 0, 8);
|
|
||||||
bdrv_get_geometry(s->bd[target], &nb_sectors);
|
|
||||||
s->ti_buf[0] = (nb_sectors >> 24) & 0xff;
|
|
||||||
s->ti_buf[1] = (nb_sectors >> 16) & 0xff;
|
|
||||||
s->ti_buf[2] = (nb_sectors >> 8) & 0xff;
|
|
||||||
s->ti_buf[3] = nb_sectors & 0xff;
|
|
||||||
s->ti_buf[4] = 0;
|
|
||||||
s->ti_buf[5] = 0;
|
|
||||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM)
|
|
||||||
s->ti_buf[6] = 8; // sector size 2048
|
|
||||||
else
|
|
||||||
s->ti_buf[6] = 2; // sector size 512
|
|
||||||
s->ti_buf[7] = 0;
|
|
||||||
s->ti_dir = 1;
|
|
||||||
s->ti_size = 8;
|
|
||||||
break;
|
|
||||||
case 0x28:
|
|
||||||
{
|
|
||||||
int64_t offset, len;
|
|
||||||
|
|
||||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
|
|
||||||
offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4;
|
|
||||||
len = ((buf[8] << 8) | buf[9]) * 4;
|
|
||||||
s->ti_size = len * 2048;
|
|
||||||
} else {
|
|
||||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
|
||||||
len = (buf[8] << 8) | buf[9];
|
|
||||||
s->ti_size = len * 512;
|
|
||||||
}
|
|
||||||
DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len);
|
|
||||||
if (s->ti_size > TI_BUFSZ) {
|
|
||||||
DPRINTF("size too large %d\n", s->ti_size);
|
|
||||||
}
|
|
||||||
bdrv_read(s->bd[target], offset, s->ti_buf, len);
|
|
||||||
// XXX error handling
|
|
||||||
s->ti_dir = 1;
|
|
||||||
s->ti_rptr = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x2a:
|
|
||||||
{
|
|
||||||
int64_t offset, len;
|
|
||||||
|
|
||||||
if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) {
|
|
||||||
offset = ((buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]) * 4;
|
|
||||||
len = ((buf[8] << 8) | buf[9]) * 4;
|
|
||||||
s->ti_size = len * 2048;
|
|
||||||
} else {
|
|
||||||
offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6];
|
|
||||||
len = (buf[8] << 8) | buf[9];
|
|
||||||
s->ti_size = len * 512;
|
|
||||||
}
|
|
||||||
DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len);
|
|
||||||
if (s->ti_size > TI_BUFSZ) {
|
|
||||||
DPRINTF("size too large %d\n", s->ti_size);
|
|
||||||
}
|
|
||||||
s->dma_cb = esp_write_dma_cb;
|
|
||||||
s->offset = offset;
|
|
||||||
s->len = len;
|
|
||||||
s->target = target;
|
|
||||||
s->ti_rptr = 0;
|
|
||||||
// XXX error handling
|
|
||||||
s->ti_dir = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 0x43:
|
|
||||||
{
|
|
||||||
int start_track, format, msf, len;
|
|
||||||
|
|
||||||
msf = buf[2] & 2;
|
|
||||||
format = buf[3] & 0xf;
|
|
||||||
start_track = buf[7];
|
|
||||||
bdrv_get_geometry(s->bd[target], &nb_sectors);
|
|
||||||
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
|
|
||||||
switch(format) {
|
|
||||||
case 0:
|
|
||||||
len = cdrom_read_toc(nb_sectors, buf, msf, start_track);
|
|
||||||
if (len < 0)
|
|
||||||
goto error_cmd;
|
|
||||||
s->ti_size = len;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* multi session : only a single session defined */
|
|
||||||
memset(buf, 0, 12);
|
|
||||||
buf[1] = 0x0a;
|
|
||||||
buf[2] = 0x01;
|
|
||||||
buf[3] = 0x01;
|
|
||||||
s->ti_size = 12;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
len = cdrom_read_toc_raw(nb_sectors, buf, msf, start_track);
|
|
||||||
if (len < 0)
|
|
||||||
goto error_cmd;
|
|
||||||
s->ti_size = len;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error_cmd:
|
|
||||||
DPRINTF("Read TOC error\n");
|
|
||||||
// XXX error handling
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
s->ti_dir = 1;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[1]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_DI;
|
|
||||||
s->rregs[5] = INTR_BS | INTR_FC;
|
s->rregs[5] = INTR_BS | INTR_FC;
|
||||||
s->rregs[6] = SEQ_CD;
|
s->rregs[6] = SEQ_CD;
|
||||||
s->espdmaregs[0] |= DMA_INTR;
|
s->espdmaregs[0] |= DMA_INTR;
|
||||||
@ -427,7 +139,8 @@ static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
|
|||||||
DPRINTF("Transfer status len %d\n", len);
|
DPRINTF("Transfer status len %d\n", len);
|
||||||
if (s->dma) {
|
if (s->dma) {
|
||||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||||
DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r');
|
DPRINTF("DMA Direction: %c\n",
|
||||||
|
s->espdmaregs[0] & DMA_WRITE_MEM ? 'w': 'r');
|
||||||
cpu_physical_memory_write(dmaptr, buf, len);
|
cpu_physical_memory_write(dmaptr, buf, len);
|
||||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||||
s->rregs[5] = INTR_BS | INTR_FC;
|
s->rregs[5] = INTR_BS | INTR_FC;
|
||||||
@ -446,10 +159,26 @@ static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len)
|
|||||||
|
|
||||||
static const uint8_t okbuf[] = {0, 0};
|
static const uint8_t okbuf[] = {0, 0};
|
||||||
|
|
||||||
|
static void esp_command_complete(void *opaque, uint32_t tag, int fail)
|
||||||
|
{
|
||||||
|
ESPState *s = (ESPState *)opaque;
|
||||||
|
|
||||||
|
DPRINTF("SCSI Command complete\n");
|
||||||
|
if (s->ti_size != 0)
|
||||||
|
DPRINTF("SCSI command completed unexpectedly\n");
|
||||||
|
s->ti_size = 0;
|
||||||
|
/* ??? Report failures. */
|
||||||
|
if (fail)
|
||||||
|
DPRINTF("Command failed\n");
|
||||||
|
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_ti(ESPState *s)
|
static void handle_ti(ESPState *s)
|
||||||
{
|
{
|
||||||
uint32_t dmaptr, dmalen, minlen, len, from, to;
|
uint32_t dmaptr, dmalen, minlen, len, from, to;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
int to_device;
|
||||||
|
uint8_t buf[TARGET_PAGE_SIZE];
|
||||||
|
|
||||||
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
dmalen = s->wregs[0] | (s->wregs[1] << 8);
|
||||||
if (dmalen==0) {
|
if (dmalen==0) {
|
||||||
@ -460,7 +189,10 @@ static void handle_ti(ESPState *s)
|
|||||||
DPRINTF("Transfer Information len %d\n", minlen);
|
DPRINTF("Transfer Information len %d\n", minlen);
|
||||||
if (s->dma) {
|
if (s->dma) {
|
||||||
dmaptr = iommu_translate(s->espdmaregs[1]);
|
dmaptr = iommu_translate(s->espdmaregs[1]);
|
||||||
DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x %d %d\n", s->espdmaregs[0] & 0x100? 'w': 'r', dmaptr, s->ti_size, s->ti_rptr, s->ti_dir);
|
/* Check if the transfer writes to to reads from the device. */
|
||||||
|
to_device = (s->espdmaregs[0] & DMA_WRITE_MEM) == 0;
|
||||||
|
DPRINTF("DMA Direction: %c, addr 0x%8.8x %08x\n",
|
||||||
|
to_device ? 'r': 'w', dmaptr, s->ti_size);
|
||||||
from = s->espdmaregs[1];
|
from = s->espdmaregs[1];
|
||||||
to = from + minlen;
|
to = from + minlen;
|
||||||
for (i = 0; i < minlen; i += len, from += len) {
|
for (i = 0; i < minlen; i += len, from += len) {
|
||||||
@ -471,35 +203,23 @@ static void handle_ti(ESPState *s)
|
|||||||
len = to - from;
|
len = to - from;
|
||||||
}
|
}
|
||||||
DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
|
DPRINTF("DMA address p %08x v %08x len %08x, from %08x, to %08x\n", dmaptr, s->espdmaregs[1] + i, len, from, to);
|
||||||
if (s->ti_dir)
|
s->ti_size -= len;
|
||||||
cpu_physical_memory_write(dmaptr, &s->ti_buf[s->ti_rptr + i], len);
|
if (to_device) {
|
||||||
else
|
cpu_physical_memory_read(dmaptr, buf, len);
|
||||||
cpu_physical_memory_read(dmaptr, &s->ti_buf[s->ti_rptr + i], len);
|
scsi_write_data(s->current_dev, buf, len);
|
||||||
|
} else {
|
||||||
|
scsi_read_data(s->current_dev, buf, len);
|
||||||
|
cpu_physical_memory_write(dmaptr, buf, len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (s->dma_cb) {
|
if (s->ti_size) {
|
||||||
s->dma_cb(s, s->espdmaregs[1], minlen);
|
s->rregs[4] = STAT_IN | STAT_TC | (to_device ? STAT_DO : STAT_DI);
|
||||||
}
|
|
||||||
if (minlen < s->ti_size) {
|
|
||||||
s->rregs[4] = STAT_IN | STAT_TC | (s->ti_dir ? STAT_DO : STAT_DI);
|
|
||||||
s->ti_size -= minlen;
|
s->ti_size -= minlen;
|
||||||
s->ti_rptr += minlen;
|
|
||||||
} else {
|
|
||||||
s->rregs[4] = STAT_IN | STAT_TC | STAT_ST;
|
|
||||||
s->dma_cb = NULL;
|
|
||||||
s->offset = 0;
|
|
||||||
s->len = 0;
|
|
||||||
s->target = 0;
|
|
||||||
s->ti_rptr = 0;
|
|
||||||
}
|
}
|
||||||
s->rregs[5] = INTR_BS;
|
s->rregs[5] = INTR_BS;
|
||||||
s->rregs[6] = 0;
|
s->rregs[6] = 0;
|
||||||
s->rregs[7] = 0;
|
s->rregs[7] = 0;
|
||||||
s->espdmaregs[0] |= DMA_INTR;
|
s->espdmaregs[0] |= DMA_INTR;
|
||||||
} else {
|
|
||||||
s->ti_size = minlen;
|
|
||||||
s->ti_rptr = 0;
|
|
||||||
s->ti_wptr = 0;
|
|
||||||
s->rregs[7] = minlen;
|
|
||||||
}
|
}
|
||||||
pic_set_irq(s->irq, 1);
|
pic_set_irq(s->irq, 1);
|
||||||
}
|
}
|
||||||
@ -514,9 +234,7 @@ static void esp_reset(void *opaque)
|
|||||||
s->ti_size = 0;
|
s->ti_size = 0;
|
||||||
s->ti_rptr = 0;
|
s->ti_rptr = 0;
|
||||||
s->ti_wptr = 0;
|
s->ti_wptr = 0;
|
||||||
s->ti_dir = 0;
|
|
||||||
s->dma = 0;
|
s->dma = 0;
|
||||||
s->dma_cb = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
||||||
@ -531,7 +249,12 @@ static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr)
|
|||||||
// FIFO
|
// FIFO
|
||||||
if (s->ti_size > 0) {
|
if (s->ti_size > 0) {
|
||||||
s->ti_size--;
|
s->ti_size--;
|
||||||
s->rregs[saddr] = s->ti_buf[s->ti_rptr++];
|
if ((s->rregs[4] & 6) == 0) {
|
||||||
|
/* Data in/out. */
|
||||||
|
scsi_read_data(s->current_dev, &s->rregs[2], 0);
|
||||||
|
} else {
|
||||||
|
s->rregs[2] = s->ti_buf[s->ti_rptr++];
|
||||||
|
}
|
||||||
pic_set_irq(s->irq, 1);
|
pic_set_irq(s->irq, 1);
|
||||||
}
|
}
|
||||||
if (s->ti_size == 0) {
|
if (s->ti_size == 0) {
|
||||||
@ -566,8 +289,15 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
|
|||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// FIFO
|
// FIFO
|
||||||
s->ti_size++;
|
if ((s->rregs[4] & 6) == 0) {
|
||||||
s->ti_buf[s->ti_wptr++] = val & 0xff;
|
uint8_t buf;
|
||||||
|
buf = val & 0xff;
|
||||||
|
s->ti_size--;
|
||||||
|
scsi_write_data(s->current_dev, &buf, 0);
|
||||||
|
} else {
|
||||||
|
s->ti_size++;
|
||||||
|
s->ti_buf[s->ti_wptr++] = val & 0xff;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
s->rregs[saddr] = val;
|
s->rregs[saddr] = val;
|
||||||
@ -723,7 +453,6 @@ static void esp_save(QEMUFile *f, void *opaque)
|
|||||||
qemu_put_be32s(f, &s->ti_size);
|
qemu_put_be32s(f, &s->ti_size);
|
||||||
qemu_put_be32s(f, &s->ti_rptr);
|
qemu_put_be32s(f, &s->ti_rptr);
|
||||||
qemu_put_be32s(f, &s->ti_wptr);
|
qemu_put_be32s(f, &s->ti_wptr);
|
||||||
qemu_put_be32s(f, &s->ti_dir);
|
|
||||||
qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
|
qemu_put_buffer(f, s->ti_buf, TI_BUFSZ);
|
||||||
qemu_put_be32s(f, &s->dma);
|
qemu_put_be32s(f, &s->dma);
|
||||||
}
|
}
|
||||||
@ -744,7 +473,6 @@ static int esp_load(QEMUFile *f, void *opaque, int version_id)
|
|||||||
qemu_get_be32s(f, &s->ti_size);
|
qemu_get_be32s(f, &s->ti_size);
|
||||||
qemu_get_be32s(f, &s->ti_rptr);
|
qemu_get_be32s(f, &s->ti_rptr);
|
||||||
qemu_get_be32s(f, &s->ti_wptr);
|
qemu_get_be32s(f, &s->ti_wptr);
|
||||||
qemu_get_be32s(f, &s->ti_dir);
|
|
||||||
qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
|
qemu_get_buffer(f, s->ti_buf, TI_BUFSZ);
|
||||||
qemu_get_be32s(f, &s->dma);
|
qemu_get_be32s(f, &s->dma);
|
||||||
|
|
||||||
@ -755,6 +483,7 @@ void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdadd
|
|||||||
{
|
{
|
||||||
ESPState *s;
|
ESPState *s;
|
||||||
int esp_io_memory, espdma_io_memory;
|
int esp_io_memory, espdma_io_memory;
|
||||||
|
int i;
|
||||||
|
|
||||||
s = qemu_mallocz(sizeof(ESPState));
|
s = qemu_mallocz(sizeof(ESPState));
|
||||||
if (!s)
|
if (!s)
|
||||||
@ -773,5 +502,11 @@ void esp_init(BlockDriverState **bd, int irq, uint32_t espaddr, uint32_t espdadd
|
|||||||
|
|
||||||
register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
|
register_savevm("esp", espaddr, 1, esp_save, esp_load, s);
|
||||||
qemu_register_reset(esp_reset, s);
|
qemu_register_reset(esp_reset, s);
|
||||||
|
for (i = 0; i < MAX_DISKS; i++) {
|
||||||
|
if (bs_table[i]) {
|
||||||
|
s->scsi_dev[i] =
|
||||||
|
scsi_disk_init(bs_table[i], esp_command_complete, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
125
hw/ide.c
125
hw/ide.c
@ -1082,127 +1082,6 @@ static void ide_atapi_cmd_read(IDEState *s, int lba, int nb_sectors,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* same toc as bochs. Return -1 if error or the toc length */
|
|
||||||
/* XXX: check this */
|
|
||||||
static int cdrom_read_toc(IDEState *s, uint8_t *buf, int msf, int start_track)
|
|
||||||
{
|
|
||||||
uint8_t *q;
|
|
||||||
int nb_sectors, len;
|
|
||||||
|
|
||||||
if (start_track > 1 && start_track != 0xaa)
|
|
||||||
return -1;
|
|
||||||
q = buf + 2;
|
|
||||||
*q++ = 1; /* first session */
|
|
||||||
*q++ = 1; /* last session */
|
|
||||||
if (start_track <= 1) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
*q++ = 0x14; /* ADR, control */
|
|
||||||
*q++ = 1; /* track number */
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
lba_to_msf(q, 0);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
/* sector 0 */
|
|
||||||
cpu_to_ube32(q, 0);
|
|
||||||
q += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* lead out track */
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
*q++ = 0x16; /* ADR, control */
|
|
||||||
*q++ = 0xaa; /* track number */
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
nb_sectors = s->nb_sectors >> 2;
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
lba_to_msf(q, nb_sectors);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
cpu_to_ube32(q, nb_sectors);
|
|
||||||
q += 4;
|
|
||||||
}
|
|
||||||
len = q - buf;
|
|
||||||
cpu_to_ube16(buf, len - 2);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* mostly same info as PearPc */
|
|
||||||
static int cdrom_read_toc_raw(IDEState *s, uint8_t *buf, int msf,
|
|
||||||
int session_num)
|
|
||||||
{
|
|
||||||
uint8_t *q;
|
|
||||||
int nb_sectors, len;
|
|
||||||
|
|
||||||
q = buf + 2;
|
|
||||||
*q++ = 1; /* first session */
|
|
||||||
*q++ = 1; /* last session */
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* data track */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 0xa0; /* lead-in */
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 1; /* first track */
|
|
||||||
*q++ = 0x00; /* disk type */
|
|
||||||
*q++ = 0x00;
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* data track */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 0xa1;
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 1; /* last track */
|
|
||||||
*q++ = 0x00;
|
|
||||||
*q++ = 0x00;
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* data track */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 0xa2; /* lead-out */
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
nb_sectors = s->nb_sectors >> 2;
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0; /* reserved */
|
|
||||||
lba_to_msf(q, nb_sectors);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
cpu_to_ube32(q, nb_sectors);
|
|
||||||
q += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
*q++ = 1; /* session number */
|
|
||||||
*q++ = 0x14; /* ADR, control */
|
|
||||||
*q++ = 0; /* track number */
|
|
||||||
*q++ = 1; /* point */
|
|
||||||
*q++ = 0; /* min */
|
|
||||||
*q++ = 0; /* sec */
|
|
||||||
*q++ = 0; /* frame */
|
|
||||||
if (msf) {
|
|
||||||
*q++ = 0;
|
|
||||||
lba_to_msf(q, 0);
|
|
||||||
q += 3;
|
|
||||||
} else {
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 0;
|
|
||||||
*q++ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = q - buf;
|
|
||||||
cpu_to_ube16(buf, len - 2);
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ide_atapi_cmd(IDEState *s)
|
static void ide_atapi_cmd(IDEState *s)
|
||||||
{
|
{
|
||||||
const uint8_t *packet;
|
const uint8_t *packet;
|
||||||
@ -1449,7 +1328,7 @@ static void ide_atapi_cmd(IDEState *s)
|
|||||||
start_track = packet[6];
|
start_track = packet[6];
|
||||||
switch(format) {
|
switch(format) {
|
||||||
case 0:
|
case 0:
|
||||||
len = cdrom_read_toc(s, buf, msf, start_track);
|
len = cdrom_read_toc(s->nb_sectors >> 2, buf, msf, start_track);
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
goto error_cmd;
|
goto error_cmd;
|
||||||
ide_atapi_cmd_reply(s, len, max_len);
|
ide_atapi_cmd_reply(s, len, max_len);
|
||||||
@ -1463,7 +1342,7 @@ static void ide_atapi_cmd(IDEState *s)
|
|||||||
ide_atapi_cmd_reply(s, 12, max_len);
|
ide_atapi_cmd_reply(s, 12, max_len);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
len = cdrom_read_toc_raw(s, buf, msf, start_track);
|
len = cdrom_read_toc_raw(s->nb_sectors >> 2, buf, msf, start_track);
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
goto error_cmd;
|
goto error_cmd;
|
||||||
ide_atapi_cmd_reply(s, len, max_len);
|
ide_atapi_cmd_reply(s, len, max_len);
|
||||||
|
408
hw/scsi-disk.c
Normal file
408
hw/scsi-disk.c
Normal file
@ -0,0 +1,408 @@
|
|||||||
|
/*
|
||||||
|
* SCSI Device emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 CodeSourcery.
|
||||||
|
* Based on code by Fabrice Bellard
|
||||||
|
*
|
||||||
|
* Written by Paul Brook
|
||||||
|
*
|
||||||
|
* This code is licenced under the LGPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define DEBUG_SCSI
|
||||||
|
|
||||||
|
#ifdef DEBUG_SCSI
|
||||||
|
#define DPRINTF(fmt, args...) \
|
||||||
|
do { printf("scsi-disk: " fmt , ##args); } while (0)
|
||||||
|
#else
|
||||||
|
#define DPRINTF(fmt, args...) do {} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BADF(fmt, args...) \
|
||||||
|
do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
|
||||||
|
|
||||||
|
#include "vl.h"
|
||||||
|
|
||||||
|
#define SENSE_NO_SENSE 0
|
||||||
|
#define SENSE_ILLEGAL_REQUEST 5
|
||||||
|
|
||||||
|
struct SCSIDevice
|
||||||
|
{
|
||||||
|
int command;
|
||||||
|
uint32_t tag;
|
||||||
|
BlockDriverState *bdrv;
|
||||||
|
int sector_size;
|
||||||
|
/* When transfering data buf_pos and buf_len contain a partially
|
||||||
|
transferred block of data (or response to a command), and
|
||||||
|
sector/sector_count identify any remaining sectors. */
|
||||||
|
/* ??? We should probably keep track of whether the data trasfer is
|
||||||
|
a read or a write. Currently we rely on the host getting it right. */
|
||||||
|
int sector;
|
||||||
|
int sector_count;
|
||||||
|
int buf_pos;
|
||||||
|
int buf_len;
|
||||||
|
int sense;
|
||||||
|
char buf[2048];
|
||||||
|
scsi_completionfn completion;
|
||||||
|
void *opaque;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void scsi_command_complete(SCSIDevice *s, int sense)
|
||||||
|
{
|
||||||
|
s->sense = sense;
|
||||||
|
s->completion(s->opaque, s->tag, sense != SENSE_NO_SENSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read data from a scsi device. Returns nonzero on failure. */
|
||||||
|
int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t n;
|
||||||
|
|
||||||
|
DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
|
||||||
|
if (s->buf_len == 0 && s->sector_count == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (s->buf_len) {
|
||||||
|
n = s->buf_len;
|
||||||
|
if (n > len)
|
||||||
|
n = len;
|
||||||
|
memcpy(data, s->buf + s->buf_pos, n);
|
||||||
|
s->buf_pos += n;
|
||||||
|
s->buf_len -= n;
|
||||||
|
data += n;
|
||||||
|
len -= n;
|
||||||
|
if (s->buf_len == 0)
|
||||||
|
s->buf_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
n = len / s->sector_size;
|
||||||
|
if (n > s->sector_count)
|
||||||
|
n = s->sector_count;
|
||||||
|
|
||||||
|
if (n != 0) {
|
||||||
|
bdrv_read(s->bdrv, s->sector, data, n);
|
||||||
|
data += n * s->sector_size;
|
||||||
|
len -= n * s->sector_size;
|
||||||
|
s->sector += n;
|
||||||
|
s->sector_count -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len && s->sector_count) {
|
||||||
|
bdrv_read(s->bdrv, s->sector, s->buf, 1);
|
||||||
|
s->sector++;
|
||||||
|
s->sector_count--;
|
||||||
|
s->buf_pos = 0;
|
||||||
|
s->buf_len = s->sector_size;
|
||||||
|
/* Recurse to complete the partial read. */
|
||||||
|
return scsi_read_data(s, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (s->buf_len == 0 && s->sector_count == 0)
|
||||||
|
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read data to a scsi device. Returns nonzero on failure. */
|
||||||
|
int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t n;
|
||||||
|
|
||||||
|
DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
|
||||||
|
if (s->buf_pos != 0) {
|
||||||
|
BADF("Bad state on write\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->sector_count == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (s->buf_len != 0 || len < s->sector_size) {
|
||||||
|
n = s->sector_size - s->buf_len;
|
||||||
|
if (n > len)
|
||||||
|
n = len;
|
||||||
|
|
||||||
|
memcpy(s->buf + s->buf_len, data, n);
|
||||||
|
data += n;
|
||||||
|
s->buf_len += n;
|
||||||
|
len -= n;
|
||||||
|
if (s->buf_len == s->sector_size) {
|
||||||
|
/* A full sector has been accumulated. Write it to disk. */
|
||||||
|
bdrv_write(s->bdrv, s->sector, s->buf, 1);
|
||||||
|
s->buf_len = 0;
|
||||||
|
s->sector++;
|
||||||
|
s->sector_count--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = len / s->sector_size;
|
||||||
|
if (n > s->sector_count)
|
||||||
|
n = s->sector_count;
|
||||||
|
|
||||||
|
if (n != 0) {
|
||||||
|
bdrv_write(s->bdrv, s->sector, data, n);
|
||||||
|
data += n * s->sector_size;
|
||||||
|
len -= n * s->sector_size;
|
||||||
|
s->sector += n;
|
||||||
|
s->sector_count -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len >= s->sector_size)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (len && s->sector_count) {
|
||||||
|
/* Recurse to complete the partial write. */
|
||||||
|
return scsi_write_data(s, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len != 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (s->sector_count == 0)
|
||||||
|
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Execute a scsi command. Returns the length of the data expected by the
|
||||||
|
command. This will be Positive for data transfers from the device
|
||||||
|
(eg. disk reads), negative for transfers to the device (eg. disk writes),
|
||||||
|
and zero if the command does not transfer any data. */
|
||||||
|
|
||||||
|
int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf)
|
||||||
|
{
|
||||||
|
int64_t nb_sectors;
|
||||||
|
uint32_t lba;
|
||||||
|
uint32_t len;
|
||||||
|
int cmdlen;
|
||||||
|
int is_write;
|
||||||
|
|
||||||
|
s->command = buf[0];
|
||||||
|
s->tag = tag;
|
||||||
|
s->sector_count = 0;
|
||||||
|
s->buf_pos = 0;
|
||||||
|
s->buf_len = 0;
|
||||||
|
is_write = 0;
|
||||||
|
DPRINTF("Command: 0x%02x", buf[0]);
|
||||||
|
switch (s->command >> 5) {
|
||||||
|
case 0:
|
||||||
|
lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
|
||||||
|
len = buf[4];
|
||||||
|
cmdlen = 6;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
|
||||||
|
len = buf[8] | (buf[7] << 8);
|
||||||
|
cmdlen = 10;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
|
||||||
|
len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
|
||||||
|
cmdlen = 16;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
|
||||||
|
len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
|
||||||
|
cmdlen = 12;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BADF("Unsupported command length\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
#ifdef DEBUG_SCSI
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 1; i < cmdlen; i++) {
|
||||||
|
printf(" 0x%02x", buf[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (buf[1] >> 5) {
|
||||||
|
/* Only LUN 0 supported. */
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
switch (s->command) {
|
||||||
|
case 0x0:
|
||||||
|
DPRINTF("Test Unit Ready\n");
|
||||||
|
break;
|
||||||
|
case 0x03:
|
||||||
|
DPRINTF("Request Sense (len %d)\n", len);
|
||||||
|
if (len < 4)
|
||||||
|
goto fail;
|
||||||
|
memset(buf, 0, 4);
|
||||||
|
s->buf[0] = 0xf0;
|
||||||
|
s->buf[1] = 0;
|
||||||
|
s->buf[2] = s->sense;
|
||||||
|
s->buf_len = 4;
|
||||||
|
break;
|
||||||
|
case 0x12:
|
||||||
|
DPRINTF("Inquiry (len %d)\n", len);
|
||||||
|
if (len < 36) {
|
||||||
|
BADF("Inquiry buffer too small (%d)\n", len);
|
||||||
|
}
|
||||||
|
memset(s->buf, 0, 36);
|
||||||
|
if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
|
||||||
|
s->buf[0] = 5;
|
||||||
|
s->buf[1] = 0x80;
|
||||||
|
memcpy(&s->buf[16], "QEMU CDROM ", 16);
|
||||||
|
} else {
|
||||||
|
s->buf[0] = 0;
|
||||||
|
memcpy(&s->buf[16], "QEMU HARDDISK ", 16);
|
||||||
|
}
|
||||||
|
memcpy(&s->buf[8], "QEMU ", 8);
|
||||||
|
s->buf[2] = 3; /* SCSI-3 */
|
||||||
|
s->buf[3] = 2; /* Format 2 */
|
||||||
|
s->buf[4] = 32;
|
||||||
|
s->buf_len = 36;
|
||||||
|
break;
|
||||||
|
case 0x16:
|
||||||
|
DPRINTF("Reserve(6)\n");
|
||||||
|
if (buf[1] & 1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case 0x17:
|
||||||
|
DPRINTF("Release(6)\n");
|
||||||
|
if (buf[1] & 1)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case 0x1a:
|
||||||
|
DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[2], len);
|
||||||
|
memset(s->buf, 0, 4);
|
||||||
|
s->buf[0] = 0x16; /* Mode data length (4 + 0x12). */
|
||||||
|
s->buf[1] = 0; /* Default media type. */
|
||||||
|
s->buf[2] = 0; /* Write enabled. */
|
||||||
|
s->buf[3] = 0; /* Block descriptor length. */
|
||||||
|
/* Caching page. */
|
||||||
|
s->buf[4 + 0] = 8;
|
||||||
|
s->buf[4 + 1] = 0x12;
|
||||||
|
s->buf[4 + 2] = 4; /* WCE */
|
||||||
|
if (len > 0x16)
|
||||||
|
len = 0x16;
|
||||||
|
s->buf_len = len;
|
||||||
|
break;
|
||||||
|
case 0x25:
|
||||||
|
DPRINTF("Read Capacity\n");
|
||||||
|
/* The normal LEN field for this command is zero. */
|
||||||
|
memset(s->buf, 0, 8);
|
||||||
|
bdrv_get_geometry(s->bdrv, &nb_sectors);
|
||||||
|
s->buf[0] = (nb_sectors >> 24) & 0xff;
|
||||||
|
s->buf[1] = (nb_sectors >> 16) & 0xff;
|
||||||
|
s->buf[2] = (nb_sectors >> 8) & 0xff;
|
||||||
|
s->buf[3] = nb_sectors & 0xff;
|
||||||
|
s->buf[4] = 0;
|
||||||
|
s->buf[5] = 0;
|
||||||
|
s->buf[6] = s->sector_size >> 8;
|
||||||
|
s->buf[7] = s->sector_size & 0xff;
|
||||||
|
s->buf_len = 8;
|
||||||
|
break;
|
||||||
|
case 0x08:
|
||||||
|
case 0x28:
|
||||||
|
DPRINTF("Read (sector %d, count %d)\n", lba, len);
|
||||||
|
s->sector = lba;
|
||||||
|
s->sector_count = len;
|
||||||
|
break;
|
||||||
|
case 0x0a:
|
||||||
|
case 0x2a:
|
||||||
|
DPRINTF("Write (sector %d, count %d)\n", lba, len);
|
||||||
|
s->sector = lba;
|
||||||
|
s->sector_count = len;
|
||||||
|
is_write = 1;
|
||||||
|
break;
|
||||||
|
case 0x43:
|
||||||
|
{
|
||||||
|
int start_track, format, msf;
|
||||||
|
|
||||||
|
msf = buf[1] & 2;
|
||||||
|
format = buf[2] & 0xf;
|
||||||
|
start_track = buf[6];
|
||||||
|
bdrv_get_geometry(s->bdrv, &nb_sectors);
|
||||||
|
DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
|
||||||
|
switch(format) {
|
||||||
|
case 0:
|
||||||
|
len = cdrom_read_toc(nb_sectors, s->buf, msf, start_track);
|
||||||
|
if (len < 0)
|
||||||
|
goto error_cmd;
|
||||||
|
s->buf_len = len;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* multi session : only a single session defined */
|
||||||
|
memset(s->buf, 0, 12);
|
||||||
|
s->buf[1] = 0x0a;
|
||||||
|
s->buf[2] = 0x01;
|
||||||
|
s->buf[3] = 0x01;
|
||||||
|
s->buf_len = 12;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
len = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track);
|
||||||
|
if (len < 0)
|
||||||
|
goto error_cmd;
|
||||||
|
s->buf_len = len;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error_cmd:
|
||||||
|
DPRINTF("Read TOC error\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 0x56:
|
||||||
|
DPRINTF("Reserve(10)\n");
|
||||||
|
if (buf[1] & 3)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case 0x57:
|
||||||
|
DPRINTF("Release(10)\n");
|
||||||
|
if (buf[1] & 3)
|
||||||
|
goto fail;
|
||||||
|
break;
|
||||||
|
case 0xa0:
|
||||||
|
DPRINTF("Report LUNs (len %d)\n", len);
|
||||||
|
if (len < 16)
|
||||||
|
goto fail;
|
||||||
|
memset(s->buf, 0, 16);
|
||||||
|
s->buf[3] = 8;
|
||||||
|
s->buf_len = 16;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
|
||||||
|
fail:
|
||||||
|
scsi_command_complete(s, SENSE_ILLEGAL_REQUEST);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (s->sector_count == 0 && s->buf_len == 0) {
|
||||||
|
scsi_command_complete(s, SENSE_NO_SENSE);
|
||||||
|
}
|
||||||
|
len = s->sector_count * s->sector_size + s->buf_len;
|
||||||
|
return is_write ? -len : len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scsi_disk_destroy(SCSIDevice *s)
|
||||||
|
{
|
||||||
|
bdrv_close(s->bdrv);
|
||||||
|
qemu_free(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
|
||||||
|
scsi_completionfn completion,
|
||||||
|
void *opaque)
|
||||||
|
{
|
||||||
|
SCSIDevice *s;
|
||||||
|
|
||||||
|
s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
|
||||||
|
s->bdrv = bdrv;
|
||||||
|
s->completion = completion;
|
||||||
|
s->opaque = opaque;
|
||||||
|
if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
|
||||||
|
s->sector_size = 2048;
|
||||||
|
} else {
|
||||||
|
s->sector_size = 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
@ -323,10 +323,16 @@ static int usb_tablet_poll(USBMouseState *s, uint8_t *buf, int len)
|
|||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_mouse_handle_reset(USBDevice *dev)
|
static void usb_mouse_handle_reset(USBDevice *dev, int destroy)
|
||||||
{
|
{
|
||||||
USBMouseState *s = (USBMouseState *)dev;
|
USBMouseState *s = (USBMouseState *)dev;
|
||||||
|
|
||||||
|
if (destroy) {
|
||||||
|
qemu_add_mouse_event_handler(NULL, NULL, 0);
|
||||||
|
qemu_free(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
s->dx = 0;
|
s->dx = 0;
|
||||||
s->dy = 0;
|
s->dy = 0;
|
||||||
s->dz = 0;
|
s->dz = 0;
|
||||||
|
@ -199,9 +199,11 @@ static void usb_hub_attach(USBPort *port1, USBDevice *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_hub_handle_reset(USBDevice *dev)
|
static void usb_hub_handle_reset(USBDevice *dev, int destroy)
|
||||||
{
|
{
|
||||||
/* XXX: do it */
|
/* XXX: do it */
|
||||||
|
if (destroy)
|
||||||
|
qemu_free(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||||
|
395
hw/usb-msd.c
Normal file
395
hw/usb-msd.c
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
/*
|
||||||
|
* USB Mass Storage Device emulation
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006 CodeSourcery.
|
||||||
|
* Written by Paul Brook
|
||||||
|
*
|
||||||
|
* This code is licenced under the LGPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "vl.h"
|
||||||
|
|
||||||
|
//#define DEBUG_MSD
|
||||||
|
|
||||||
|
#ifdef DEBUG_MSD
|
||||||
|
#define DPRINTF(fmt, args...) \
|
||||||
|
do { printf("usb-msd: " fmt , ##args); } while (0)
|
||||||
|
#else
|
||||||
|
#define DPRINTF(fmt, args...) do {} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* USB requests. */
|
||||||
|
#define MassStorageReset 0xff
|
||||||
|
#define GetMaxLun 0xfe
|
||||||
|
|
||||||
|
enum USBMSDMode {
|
||||||
|
USB_MSDM_CBW, /* Command Block. */
|
||||||
|
USB_MSDM_DATAOUT, /* Tranfer data to device. */
|
||||||
|
USB_MSDM_DATAIN, /* Transfer data from device. */
|
||||||
|
USB_MSDM_CSW /* Command Status. */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
USBDevice dev;
|
||||||
|
enum USBMSDMode mode;
|
||||||
|
uint32_t data_len;
|
||||||
|
uint32_t tag;
|
||||||
|
SCSIDevice *scsi_dev;
|
||||||
|
int result;
|
||||||
|
} MSDState;
|
||||||
|
|
||||||
|
static const uint8_t qemu_msd_dev_descriptor[] = {
|
||||||
|
0x12, /* u8 bLength; */
|
||||||
|
0x01, /* u8 bDescriptorType; Device */
|
||||||
|
0x10, 0x00, /* u16 bcdUSB; v1.0 */
|
||||||
|
|
||||||
|
0x00, /* u8 bDeviceClass; */
|
||||||
|
0x00, /* u8 bDeviceSubClass; */
|
||||||
|
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
||||||
|
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
||||||
|
|
||||||
|
/* Vendor and product id are arbitrary. */
|
||||||
|
0x00, 0x00, /* u16 idVendor; */
|
||||||
|
0x00, 0x00, /* u16 idProduct; */
|
||||||
|
0x00, 0x00, /* u16 bcdDevice */
|
||||||
|
|
||||||
|
0x01, /* u8 iManufacturer; */
|
||||||
|
0x02, /* u8 iProduct; */
|
||||||
|
0x03, /* u8 iSerialNumber; */
|
||||||
|
0x01 /* u8 bNumConfigurations; */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t qemu_msd_config_descriptor[] = {
|
||||||
|
|
||||||
|
/* one configuration */
|
||||||
|
0x09, /* u8 bLength; */
|
||||||
|
0x02, /* u8 bDescriptorType; Configuration */
|
||||||
|
0x20, 0x00, /* u16 wTotalLength; */
|
||||||
|
0x01, /* u8 bNumInterfaces; (1) */
|
||||||
|
0x01, /* u8 bConfigurationValue; */
|
||||||
|
0x00, /* u8 iConfiguration; */
|
||||||
|
0xc0, /* u8 bmAttributes;
|
||||||
|
Bit 7: must be set,
|
||||||
|
6: Self-powered,
|
||||||
|
5: Remote wakeup,
|
||||||
|
4..0: resvd */
|
||||||
|
0x00, /* u8 MaxPower; */
|
||||||
|
|
||||||
|
/* one interface */
|
||||||
|
0x09, /* u8 if_bLength; */
|
||||||
|
0x04, /* u8 if_bDescriptorType; Interface */
|
||||||
|
0x00, /* u8 if_bInterfaceNumber; */
|
||||||
|
0x00, /* u8 if_bAlternateSetting; */
|
||||||
|
0x02, /* u8 if_bNumEndpoints; */
|
||||||
|
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
|
||||||
|
0x06, /* u8 if_bInterfaceSubClass; SCSI */
|
||||||
|
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
|
||||||
|
0x00, /* u8 if_iInterface; */
|
||||||
|
|
||||||
|
/* Bulk-In endpoint */
|
||||||
|
0x07, /* u8 ep_bLength; */
|
||||||
|
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
||||||
|
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
||||||
|
0x02, /* u8 ep_bmAttributes; Bulk */
|
||||||
|
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
||||||
|
0x00, /* u8 ep_bInterval; */
|
||||||
|
|
||||||
|
/* Bulk-Out endpoint */
|
||||||
|
0x07, /* u8 ep_bLength; */
|
||||||
|
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
||||||
|
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
|
||||||
|
0x02, /* u8 ep_bmAttributes; Bulk */
|
||||||
|
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
||||||
|
0x00 /* u8 ep_bInterval; */
|
||||||
|
};
|
||||||
|
|
||||||
|
static void usb_msd_command_complete(void *opaque, uint32_t tag, int fail)
|
||||||
|
{
|
||||||
|
MSDState *s = (MSDState *)opaque;
|
||||||
|
|
||||||
|
DPRINTF("Command complete\n");
|
||||||
|
s->result = fail;
|
||||||
|
s->mode = USB_MSDM_CSW;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_msd_handle_reset(USBDevice *dev, int destroy)
|
||||||
|
{
|
||||||
|
MSDState *s = (MSDState *)dev;
|
||||||
|
|
||||||
|
DPRINTF("Reset\n");
|
||||||
|
s->mode = USB_MSDM_CBW;
|
||||||
|
if (destroy) {
|
||||||
|
scsi_disk_destroy(s->scsi_dev);
|
||||||
|
qemu_free(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
||||||
|
int index, int length, uint8_t *data)
|
||||||
|
{
|
||||||
|
MSDState *s = (MSDState *)dev;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
switch (request) {
|
||||||
|
case DeviceRequest | USB_REQ_GET_STATUS:
|
||||||
|
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
||||||
|
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
||||||
|
data[1] = 0x00;
|
||||||
|
ret = 2;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||||
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||||
|
dev->remote_wakeup = 0;
|
||||||
|
} else {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
||||||
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||||
|
dev->remote_wakeup = 1;
|
||||||
|
} else {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
||||||
|
dev->addr = value;
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
||||||
|
switch(value >> 8) {
|
||||||
|
case USB_DT_DEVICE:
|
||||||
|
memcpy(data, qemu_msd_dev_descriptor,
|
||||||
|
sizeof(qemu_msd_dev_descriptor));
|
||||||
|
ret = sizeof(qemu_msd_dev_descriptor);
|
||||||
|
break;
|
||||||
|
case USB_DT_CONFIG:
|
||||||
|
memcpy(data, qemu_msd_config_descriptor,
|
||||||
|
sizeof(qemu_msd_config_descriptor));
|
||||||
|
ret = sizeof(qemu_msd_config_descriptor);
|
||||||
|
break;
|
||||||
|
case USB_DT_STRING:
|
||||||
|
switch(value & 0xff) {
|
||||||
|
case 0:
|
||||||
|
/* language ids */
|
||||||
|
data[0] = 4;
|
||||||
|
data[1] = 3;
|
||||||
|
data[2] = 0x09;
|
||||||
|
data[3] = 0x04;
|
||||||
|
ret = 4;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
/* vendor description */
|
||||||
|
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
/* product description */
|
||||||
|
ret = set_usb_string(data, "QEMU USB HARDDRIVE");
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
/* serial number */
|
||||||
|
ret = set_usb_string(data, "1");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
||||||
|
data[0] = 1;
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||||
|
data[0] = 0;
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_INTERFACE:
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||||
|
if (value == 0 && index != 0x81) { /* clear ep halt */
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
/* Class specific requests. */
|
||||||
|
case MassStorageReset:
|
||||||
|
/* Reset state ready for the next CBW. */
|
||||||
|
s->mode = USB_MSDM_CBW;
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case GetMaxLun:
|
||||||
|
data[0] = 0;
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fail:
|
||||||
|
ret = USB_RET_STALL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct usb_msd_cbw {
|
||||||
|
uint32_t sig;
|
||||||
|
uint32_t tag;
|
||||||
|
uint32_t data_len;
|
||||||
|
uint8_t flags;
|
||||||
|
uint8_t lun;
|
||||||
|
uint8_t cmd_len;
|
||||||
|
uint8_t cmd[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct usb_msd_csw {
|
||||||
|
uint32_t sig;
|
||||||
|
uint32_t tag;
|
||||||
|
uint32_t residue;
|
||||||
|
uint8_t status;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int usb_msd_handle_data(USBDevice *dev, int pid, uint8_t devep,
|
||||||
|
uint8_t *data, int len)
|
||||||
|
{
|
||||||
|
MSDState *s = (MSDState *)dev;
|
||||||
|
int ret = 0;
|
||||||
|
struct usb_msd_cbw cbw;
|
||||||
|
struct usb_msd_csw csw;
|
||||||
|
|
||||||
|
switch (pid) {
|
||||||
|
case USB_TOKEN_OUT:
|
||||||
|
if (devep != 2)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
switch (s->mode) {
|
||||||
|
case USB_MSDM_CBW:
|
||||||
|
if (len != 31) {
|
||||||
|
fprintf(stderr, "usb-msd: Bad CBW size");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
memcpy(&cbw, data, 31);
|
||||||
|
if (le32_to_cpu(cbw.sig) != 0x43425355) {
|
||||||
|
fprintf(stderr, "usb-msd: Bad signature %08x\n",
|
||||||
|
le32_to_cpu(cbw.sig));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
DPRINTF("Command on LUN %d\n", cbw.lun);
|
||||||
|
if (cbw.lun != 0) {
|
||||||
|
fprintf(stderr, "usb-msd: Bad LUN %d\n", cbw.lun);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
s->tag = le32_to_cpu(cbw.tag);
|
||||||
|
s->data_len = le32_to_cpu(cbw.data_len);
|
||||||
|
if (s->data_len == 0) {
|
||||||
|
s->mode = USB_MSDM_CSW;
|
||||||
|
} else if (cbw.flags & 0x80) {
|
||||||
|
s->mode = USB_MSDM_DATAIN;
|
||||||
|
} else {
|
||||||
|
s->mode = USB_MSDM_DATAOUT;
|
||||||
|
}
|
||||||
|
DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
|
||||||
|
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
|
||||||
|
scsi_send_command(s->scsi_dev, s->tag, cbw.cmd);
|
||||||
|
ret = len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_MSDM_DATAOUT:
|
||||||
|
DPRINTF("Data out %d/%d\n", len, s->data_len);
|
||||||
|
if (len > s->data_len)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (scsi_write_data(s->scsi_dev, data, len))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
s->data_len -= len;
|
||||||
|
if (s->data_len == 0)
|
||||||
|
s->mode = USB_MSDM_CSW;
|
||||||
|
ret = len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DPRINTF("Unexpected write (len %d)\n", len);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_TOKEN_IN:
|
||||||
|
if (devep != 1)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
switch (s->mode) {
|
||||||
|
case USB_MSDM_CSW:
|
||||||
|
DPRINTF("Command status %d tag 0x%x, len %d\n",
|
||||||
|
s->result, s->tag, len);
|
||||||
|
if (len < 13)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
csw.sig = cpu_to_le32(0x53425355);
|
||||||
|
csw.tag = cpu_to_le32(s->tag);
|
||||||
|
csw.residue = 0;
|
||||||
|
csw.status = s->result;
|
||||||
|
memcpy(data, &csw, 13);
|
||||||
|
ret = 13;
|
||||||
|
s->mode = USB_MSDM_CBW;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_MSDM_DATAIN:
|
||||||
|
DPRINTF("Data in %d/%d\n", len, s->data_len);
|
||||||
|
if (len > s->data_len)
|
||||||
|
len = s->data_len;
|
||||||
|
|
||||||
|
if (scsi_read_data(s->scsi_dev, data, len))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
s->data_len -= len;
|
||||||
|
if (s->data_len == 0)
|
||||||
|
s->mode = USB_MSDM_CSW;
|
||||||
|
ret = len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DPRINTF("Unexpected read (len %d)\n", len);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DPRINTF("Bad token\n");
|
||||||
|
fail:
|
||||||
|
ret = USB_RET_STALL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
USBDevice *usb_msd_init(const char *filename)
|
||||||
|
{
|
||||||
|
MSDState *s;
|
||||||
|
BlockDriverState *bdrv;
|
||||||
|
|
||||||
|
s = qemu_mallocz(sizeof(MSDState));
|
||||||
|
if (!s)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
bdrv = bdrv_new("usb");
|
||||||
|
bdrv_open(bdrv, filename, 0);
|
||||||
|
|
||||||
|
s->dev.speed = USB_SPEED_FULL;
|
||||||
|
s->dev.handle_packet = usb_generic_handle_packet;
|
||||||
|
|
||||||
|
s->dev.handle_reset = usb_msd_handle_reset;
|
||||||
|
s->dev.handle_control = usb_msd_handle_control;
|
||||||
|
s->dev.handle_data = usb_msd_handle_data;
|
||||||
|
|
||||||
|
s->scsi_dev = scsi_disk_init(bdrv, usb_msd_command_complete, s);
|
||||||
|
usb_msd_handle_reset((USBDevice *)s, 0);
|
||||||
|
return (USBDevice *)s;
|
||||||
|
}
|
5
hw/usb.c
5
hw/usb.c
@ -55,7 +55,10 @@ int usb_generic_handle_packet(USBDevice *s, int pid,
|
|||||||
s->remote_wakeup = 0;
|
s->remote_wakeup = 0;
|
||||||
s->addr = 0;
|
s->addr = 0;
|
||||||
s->state = USB_STATE_DEFAULT;
|
s->state = USB_STATE_DEFAULT;
|
||||||
s->handle_reset(s);
|
s->handle_reset(s, 0);
|
||||||
|
break;
|
||||||
|
case USB_MSG_DESTROY:
|
||||||
|
s->handle_reset(s, 1);
|
||||||
break;
|
break;
|
||||||
case USB_TOKEN_SETUP:
|
case USB_TOKEN_SETUP:
|
||||||
if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
|
if (s->state < USB_STATE_DEFAULT || devaddr != s->addr)
|
||||||
|
6
hw/usb.h
6
hw/usb.h
@ -29,6 +29,7 @@
|
|||||||
#define USB_MSG_ATTACH 0x100
|
#define USB_MSG_ATTACH 0x100
|
||||||
#define USB_MSG_DETACH 0x101
|
#define USB_MSG_DETACH 0x101
|
||||||
#define USB_MSG_RESET 0x102
|
#define USB_MSG_RESET 0x102
|
||||||
|
#define USB_MSG_DESTROY 0x103
|
||||||
|
|
||||||
#define USB_RET_NODEV (-1)
|
#define USB_RET_NODEV (-1)
|
||||||
#define USB_RET_NAK (-2)
|
#define USB_RET_NAK (-2)
|
||||||
@ -121,7 +122,7 @@ struct USBDevice {
|
|||||||
/* The following fields are used by the generic USB device
|
/* The following fields are used by the generic USB device
|
||||||
layer. They are here just to avoid creating a new structure for
|
layer. They are here just to avoid creating a new structure for
|
||||||
them. */
|
them. */
|
||||||
void (*handle_reset)(USBDevice *dev);
|
void (*handle_reset)(USBDevice *dev, int destroy);
|
||||||
int (*handle_control)(USBDevice *dev, int request, int value,
|
int (*handle_control)(USBDevice *dev, int request, int value,
|
||||||
int index, int length, uint8_t *data);
|
int index, int length, uint8_t *data);
|
||||||
int (*handle_data)(USBDevice *dev, int pid, uint8_t devep,
|
int (*handle_data)(USBDevice *dev, int pid, uint8_t devep,
|
||||||
@ -170,3 +171,6 @@ void usb_host_info(void);
|
|||||||
/* usb-hid.c */
|
/* usb-hid.c */
|
||||||
USBDevice *usb_mouse_init(void);
|
USBDevice *usb_mouse_init(void);
|
||||||
USBDevice *usb_tablet_init(void);
|
USBDevice *usb_tablet_init(void);
|
||||||
|
|
||||||
|
/* usb-msd.c */
|
||||||
|
USBDevice *usb_msd_init(const char *filename);
|
||||||
|
@ -55,7 +55,7 @@ typedef struct USBHostDevice {
|
|||||||
int fd;
|
int fd;
|
||||||
} USBHostDevice;
|
} USBHostDevice;
|
||||||
|
|
||||||
static void usb_host_handle_reset(USBDevice *dev)
|
static void usb_host_handle_reset(USBDevice *dev, int destroy)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
USBHostDevice *s = (USBHostDevice *)dev;
|
USBHostDevice *s = (USBHostDevice *)dev;
|
||||||
|
2
vl.c
2
vl.c
@ -3279,6 +3279,8 @@ static int usb_device_add(const char *devname)
|
|||||||
dev = usb_mouse_init();
|
dev = usb_mouse_init();
|
||||||
} else if (!strcmp(devname, "tablet")) {
|
} else if (!strcmp(devname, "tablet")) {
|
||||||
dev = usb_tablet_init();
|
dev = usb_tablet_init();
|
||||||
|
} else if (strstart(devname, "disk:", &p)) {
|
||||||
|
dev = usb_msd_init(p);
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
17
vl.h
17
vl.h
@ -761,6 +761,10 @@ void pci_piix3_ide_init(PCIBus *bus, BlockDriverState **hd_table, int devfn);
|
|||||||
int pmac_ide_init (BlockDriverState **hd_table,
|
int pmac_ide_init (BlockDriverState **hd_table,
|
||||||
SetIRQFunc *set_irq, void *irq_opaque, int irq);
|
SetIRQFunc *set_irq, void *irq_opaque, int irq);
|
||||||
|
|
||||||
|
/* cdrom.c */
|
||||||
|
int cdrom_read_toc(int nb_sectors, uint8_t *buf, int msf, int start_track);
|
||||||
|
int cdrom_read_toc_raw(int nb_sectors, uint8_t *buf, int msf, int session_num);
|
||||||
|
|
||||||
/* es1370.c */
|
/* es1370.c */
|
||||||
int es1370_init (PCIBus *bus, AudioState *s);
|
int es1370_init (PCIBus *bus, AudioState *s);
|
||||||
|
|
||||||
@ -1031,6 +1035,19 @@ void do_usb_add(const char *devname);
|
|||||||
void do_usb_del(const char *devname);
|
void do_usb_del(const char *devname);
|
||||||
void usb_info(void);
|
void usb_info(void);
|
||||||
|
|
||||||
|
/* scsi-disk.c */
|
||||||
|
typedef struct SCSIDevice SCSIDevice;
|
||||||
|
typedef void (*scsi_completionfn)(void *, uint32_t, int);
|
||||||
|
|
||||||
|
SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
|
||||||
|
scsi_completionfn completion,
|
||||||
|
void *opaque);
|
||||||
|
void scsi_disk_destroy(SCSIDevice *s);
|
||||||
|
|
||||||
|
int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf);
|
||||||
|
int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len);
|
||||||
|
int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len);
|
||||||
|
|
||||||
/* integratorcp.c */
|
/* integratorcp.c */
|
||||||
extern QEMUMachine integratorcp926_machine;
|
extern QEMUMachine integratorcp926_machine;
|
||||||
extern QEMUMachine integratorcp1026_machine;
|
extern QEMUMachine integratorcp1026_machine;
|
||||||
|
Loading…
Reference in New Issue
Block a user