Commit 2f275b8f9ff82864d389b5cfff1e3b65a7b78923
1 parent
c3278b7b
SCSI support (Blue Swirl)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1344 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
224 additions
and
18 deletions
hw/esp.c
... | ... | @@ -24,7 +24,7 @@ |
24 | 24 | #include "vl.h" |
25 | 25 | |
26 | 26 | /* debug ESP card */ |
27 | -#define DEBUG_ESP | |
27 | +//#define DEBUG_ESP | |
28 | 28 | |
29 | 29 | #ifdef DEBUG_ESP |
30 | 30 | #define DPRINTF(fmt, args...) \ |
... | ... | @@ -39,16 +39,178 @@ do { printf("ESP: " fmt , ##args); } while (0) |
39 | 39 | |
40 | 40 | typedef struct ESPState { |
41 | 41 | BlockDriverState **bd; |
42 | - uint8_t regs[ESP_MAXREG]; | |
42 | + uint8_t rregs[ESP_MAXREG]; | |
43 | + uint8_t wregs[ESP_MAXREG]; | |
43 | 44 | int irq; |
44 | 45 | uint32_t espdmaregs[ESPDMA_REGS]; |
46 | + uint32_t ti_size; | |
47 | + int ti_dir; | |
48 | + uint8_t ti_buf[65536]; | |
45 | 49 | } ESPState; |
46 | 50 | |
51 | +#define STAT_DO 0x00 | |
52 | +#define STAT_DI 0x01 | |
53 | +#define STAT_CD 0x02 | |
54 | +#define STAT_ST 0x03 | |
55 | +#define STAT_MI 0x06 | |
56 | +#define STAT_MO 0x07 | |
57 | + | |
58 | +#define STAT_TC 0x10 | |
59 | +#define STAT_IN 0x80 | |
60 | + | |
61 | +#define INTR_FC 0x08 | |
62 | +#define INTR_BS 0x10 | |
63 | +#define INTR_DC 0x20 | |
64 | + | |
65 | +#define SEQ_0 0x0 | |
66 | +#define SEQ_CD 0x4 | |
67 | + | |
68 | +static void handle_satn(ESPState *s) | |
69 | +{ | |
70 | + uint8_t buf[32]; | |
71 | + uint32_t dmaptr, dmalen; | |
72 | + unsigned int i; | |
73 | + int64_t nb_sectors; | |
74 | + int target; | |
75 | + | |
76 | + dmaptr = iommu_translate(s->espdmaregs[1]); | |
77 | + dmalen = s->wregs[0] | (s->wregs[1] << 8); | |
78 | + DPRINTF("Select with ATN at %8.8x len %d\n", dmaptr, dmalen); | |
79 | + DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); | |
80 | + cpu_physical_memory_read(dmaptr, buf, dmalen); | |
81 | + for (i = 0; i < dmalen; i++) { | |
82 | + DPRINTF("Command %2.2x\n", buf[i]); | |
83 | + } | |
84 | + s->ti_dir = 0; | |
85 | + s->ti_size = 0; | |
86 | + target = s->wregs[4] & 7; | |
87 | + | |
88 | + if (target > 4 || !s->bd[target]) { // No such drive | |
89 | + s->rregs[4] = STAT_IN; | |
90 | + s->rregs[5] = INTR_DC; | |
91 | + s->rregs[6] = SEQ_0; | |
92 | + s->espdmaregs[0] |= 1; | |
93 | + pic_set_irq(s->irq, 1); | |
94 | + return; | |
95 | + } | |
96 | + switch (buf[1]) { | |
97 | + case 0x0: | |
98 | + DPRINTF("Test Unit Ready (len %d)\n", buf[5]); | |
99 | + break; | |
100 | + case 0x12: | |
101 | + DPRINTF("Inquiry (len %d)\n", buf[5]); | |
102 | + memset(s->ti_buf, 0, 36); | |
103 | + if (bdrv_get_type_hint(s->bd[target]) == BDRV_TYPE_CDROM) { | |
104 | + s->ti_buf[0] = 5; | |
105 | + memcpy(&s->ti_buf[16], "QEMU CDROM ", 16); | |
106 | + } else { | |
107 | + s->ti_buf[0] = 0; | |
108 | + memcpy(&s->ti_buf[16], "QEMU HARDDISK ", 16); | |
109 | + } | |
110 | + memcpy(&s->ti_buf[8], "QEMU ", 8); | |
111 | + s->ti_buf[2] = 1; | |
112 | + s->ti_buf[3] = 2; | |
113 | + s->ti_dir = 1; | |
114 | + s->ti_size = 36; | |
115 | + break; | |
116 | + case 0x1a: | |
117 | + DPRINTF("Mode Sense(6) (page %d, len %d)\n", buf[3], buf[5]); | |
118 | + break; | |
119 | + case 0x25: | |
120 | + DPRINTF("Read Capacity (len %d)\n", buf[5]); | |
121 | + memset(s->ti_buf, 0, 8); | |
122 | + bdrv_get_geometry(s->bd[target], &nb_sectors); | |
123 | + s->ti_buf[0] = (nb_sectors >> 24) & 0xff; | |
124 | + s->ti_buf[1] = (nb_sectors >> 16) & 0xff; | |
125 | + s->ti_buf[2] = (nb_sectors >> 8) & 0xff; | |
126 | + s->ti_buf[3] = nb_sectors & 0xff; | |
127 | + s->ti_buf[4] = 0; | |
128 | + s->ti_buf[5] = 0; | |
129 | + s->ti_buf[6] = 2; | |
130 | + s->ti_buf[7] = 0; | |
131 | + s->ti_dir = 1; | |
132 | + s->ti_size = 8; | |
133 | + break; | |
134 | + case 0x28: | |
135 | + { | |
136 | + int64_t offset, len; | |
137 | + | |
138 | + offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; | |
139 | + len = (buf[8] << 8) | buf[9]; | |
140 | + DPRINTF("Read (10) (offset %lld len %lld)\n", offset, len); | |
141 | + bdrv_read(s->bd[target], offset, s->ti_buf, len); | |
142 | + s->ti_dir = 1; | |
143 | + s->ti_size = len * 512; | |
144 | + break; | |
145 | + } | |
146 | + case 0x2a: | |
147 | + { | |
148 | + int64_t offset, len; | |
149 | + | |
150 | + offset = (buf[3] << 24) | (buf[4] << 16) | (buf[5] << 8) | buf[6]; | |
151 | + len = (buf[8] << 8) | buf[9]; | |
152 | + DPRINTF("Write (10) (offset %lld len %lld)\n", offset, len); | |
153 | + bdrv_write(s->bd[target], offset, s->ti_buf, len); | |
154 | + s->ti_dir = 0; | |
155 | + s->ti_size = len * 512; | |
156 | + break; | |
157 | + } | |
158 | + default: | |
159 | + DPRINTF("Unknown command (%2.2x)\n", buf[1]); | |
160 | + break; | |
161 | + } | |
162 | + s->rregs[4] = STAT_IN | STAT_TC | STAT_DI; | |
163 | + s->rregs[5] = INTR_BS | INTR_FC; | |
164 | + s->rregs[6] = SEQ_CD; | |
165 | + s->espdmaregs[0] |= 1; | |
166 | + pic_set_irq(s->irq, 1); | |
167 | +} | |
168 | + | |
169 | +static void dma_write(ESPState *s, const uint8_t *buf, uint32_t len) | |
170 | +{ | |
171 | + uint32_t dmaptr, dmalen; | |
172 | + | |
173 | + dmaptr = iommu_translate(s->espdmaregs[1]); | |
174 | + dmalen = s->wregs[0] | (s->wregs[1] << 8); | |
175 | + DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); | |
176 | + cpu_physical_memory_write(dmaptr, buf, len); | |
177 | + s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; | |
178 | + s->rregs[5] = INTR_BS | INTR_FC; | |
179 | + s->rregs[6] = SEQ_CD; | |
180 | + s->espdmaregs[0] |= 1; | |
181 | + pic_set_irq(s->irq, 1); | |
182 | + | |
183 | +} | |
184 | +static const uint8_t okbuf[] = {0, 0}; | |
185 | + | |
186 | +static void handle_ti(ESPState *s) | |
187 | +{ | |
188 | + uint32_t dmaptr, dmalen; | |
189 | + unsigned int i; | |
190 | + | |
191 | + dmaptr = iommu_translate(s->espdmaregs[1]); | |
192 | + dmalen = s->wregs[0] | (s->wregs[1] << 8); | |
193 | + DPRINTF("Transfer Information at %8.8x len %d\n", dmaptr, dmalen); | |
194 | + DPRINTF("DMA Direction: %c\n", s->espdmaregs[0] & 0x100? 'w': 'r'); | |
195 | + for (i = 0; i < s->ti_size; i++) { | |
196 | + dmaptr = iommu_translate(s->espdmaregs[1] + i); | |
197 | + if (s->ti_dir) | |
198 | + cpu_physical_memory_write(dmaptr, &s->ti_buf[i], 1); | |
199 | + else | |
200 | + cpu_physical_memory_read(dmaptr, &s->ti_buf[i], 1); | |
201 | + } | |
202 | + s->rregs[4] = STAT_IN | STAT_TC | STAT_ST; | |
203 | + s->rregs[5] = INTR_BS; | |
204 | + s->rregs[6] = 0; | |
205 | + s->espdmaregs[0] |= 1; | |
206 | + pic_set_irq(s->irq, 1); | |
207 | +} | |
208 | + | |
47 | 209 | static void esp_reset(void *opaque) |
48 | 210 | { |
49 | 211 | ESPState *s = opaque; |
50 | - memset(s->regs, 0, ESP_MAXREG); | |
51 | - s->regs[0x0e] = 0x4; // Indicate fas100a | |
212 | + memset(s->rregs, 0, ESP_MAXREG); | |
213 | + s->rregs[0x0e] = 0x4; // Indicate fas100a | |
52 | 214 | memset(s->espdmaregs, 0, ESPDMA_REGS * 4); |
53 | 215 | } |
54 | 216 | |
... | ... | @@ -62,8 +224,8 @@ static uint32_t esp_mem_readb(void *opaque, target_phys_addr_t addr) |
62 | 224 | default: |
63 | 225 | break; |
64 | 226 | } |
65 | - DPRINTF("esp: read reg[%d]: 0x%2.2x\n", saddr, s->regs[saddr]); | |
66 | - return s->regs[saddr]; | |
227 | + DPRINTF("read reg[%d]: 0x%2.2x\n", saddr, s->rregs[saddr]); | |
228 | + return s->rregs[saddr]; | |
67 | 229 | } |
68 | 230 | |
69 | 231 | static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
... | ... | @@ -72,30 +234,51 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
72 | 234 | uint32_t saddr; |
73 | 235 | |
74 | 236 | saddr = (addr & ESP_MAXREG) >> 2; |
75 | - DPRINTF("esp: write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->regs[saddr], val); | |
237 | + DPRINTF("write reg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->wregs[saddr], val); | |
76 | 238 | switch (saddr) { |
77 | 239 | case 3: |
78 | 240 | // Command |
79 | 241 | switch(val & 0x7f) { |
80 | 242 | case 0: |
81 | - DPRINTF("esp: NOP (%2.2x)\n", val); | |
243 | + DPRINTF("NOP (%2.2x)\n", val); | |
244 | + break; | |
245 | + case 1: | |
246 | + DPRINTF("Flush FIFO (%2.2x)\n", val); | |
247 | + s->rregs[6] = 0; | |
248 | + s->rregs[5] = INTR_FC; | |
82 | 249 | break; |
83 | 250 | case 2: |
84 | - DPRINTF("esp: Chip reset (%2.2x)\n", val); | |
251 | + DPRINTF("Chip reset (%2.2x)\n", val); | |
85 | 252 | esp_reset(s); |
86 | 253 | break; |
87 | 254 | case 3: |
88 | - DPRINTF("esp: Bus reset (%2.2x)\n", val); | |
255 | + DPRINTF("Bus reset (%2.2x)\n", val); | |
256 | + break; | |
257 | + case 0x10: | |
258 | + handle_ti(s); | |
259 | + break; | |
260 | + case 0x11: | |
261 | + DPRINTF("Initiator Command Complete Sequence (%2.2x)\n", val); | |
262 | + dma_write(s, okbuf, 2); | |
263 | + break; | |
264 | + case 0x12: | |
265 | + DPRINTF("Message Accepted (%2.2x)\n", val); | |
266 | + dma_write(s, okbuf, 2); | |
267 | + s->rregs[5] = INTR_DC; | |
268 | + s->rregs[6] = 0; | |
89 | 269 | break; |
90 | 270 | case 0x1a: |
91 | - DPRINTF("esp: Set ATN (%2.2x)\n", val); | |
271 | + DPRINTF("Set ATN (%2.2x)\n", val); | |
92 | 272 | break; |
93 | 273 | case 0x42: |
94 | - DPRINTF("esp: Select with ATN (%2.2x)\n", val); | |
95 | - s->regs[4] = 0x1a; // Status: TCNT | TDONE | CMD | |
96 | - s->regs[5] = 0x20; // Intr: Disconnect, nobody there | |
97 | - s->regs[6] = 0x4; // Seq: Cmd done | |
98 | - pic_set_irq(s->irq, 1); | |
274 | + handle_satn(s); | |
275 | + break; | |
276 | + case 0x43: | |
277 | + DPRINTF("Set ATN & stop (%2.2x)\n", val); | |
278 | + handle_satn(s); | |
279 | + break; | |
280 | + default: | |
281 | + DPRINTF("Unhandled command (%2.2x)\n", val); | |
99 | 282 | break; |
100 | 283 | } |
101 | 284 | break; |
... | ... | @@ -103,9 +286,9 @@ static void esp_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) |
103 | 286 | case 9 ... 0xf: |
104 | 287 | break; |
105 | 288 | default: |
106 | - s->regs[saddr] = val; | |
107 | 289 | break; |
108 | 290 | } |
291 | + s->wregs[saddr] = val; | |
109 | 292 | } |
110 | 293 | |
111 | 294 | static CPUReadMemoryFunc *esp_mem_read[3] = { |
... | ... | @@ -126,6 +309,7 @@ static uint32_t espdma_mem_readl(void *opaque, target_phys_addr_t addr) |
126 | 309 | uint32_t saddr; |
127 | 310 | |
128 | 311 | saddr = (addr & ESPDMA_MAXADDR) >> 2; |
312 | + DPRINTF("read dmareg[%d]: 0x%2.2x\n", saddr, s->espdmaregs[saddr]); | |
129 | 313 | return s->espdmaregs[saddr]; |
130 | 314 | } |
131 | 315 | |
... | ... | @@ -135,6 +319,15 @@ static void espdma_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t va |
135 | 319 | uint32_t saddr; |
136 | 320 | |
137 | 321 | saddr = (addr & ESPDMA_MAXADDR) >> 2; |
322 | + DPRINTF("write dmareg[%d]: 0x%2.2x -> 0x%2.2x\n", saddr, s->espdmaregs[saddr], val); | |
323 | + switch (saddr) { | |
324 | + case 0: | |
325 | + if (!(val & 0x10)) | |
326 | + pic_set_irq(s->irq, 0); | |
327 | + break; | |
328 | + default: | |
329 | + break; | |
330 | + } | |
138 | 331 | s->espdmaregs[saddr] = val; |
139 | 332 | } |
140 | 333 | |
... | ... | @@ -153,16 +346,29 @@ static CPUWriteMemoryFunc *espdma_mem_write[3] = { |
153 | 346 | static void esp_save(QEMUFile *f, void *opaque) |
154 | 347 | { |
155 | 348 | ESPState *s = opaque; |
156 | - | |
349 | + unsigned int i; | |
350 | + | |
351 | + qemu_put_buffer(f, s->rregs, ESP_MAXREG); | |
352 | + qemu_put_buffer(f, s->wregs, ESP_MAXREG); | |
353 | + qemu_put_be32s(f, &s->irq); | |
354 | + for (i = 0; i < ESPDMA_REGS; i++) | |
355 | + qemu_put_be32s(f, &s->espdmaregs[i]); | |
157 | 356 | } |
158 | 357 | |
159 | 358 | static int esp_load(QEMUFile *f, void *opaque, int version_id) |
160 | 359 | { |
161 | 360 | ESPState *s = opaque; |
361 | + unsigned int i; | |
162 | 362 | |
163 | 363 | if (version_id != 1) |
164 | 364 | return -EINVAL; |
165 | 365 | |
366 | + qemu_get_buffer(f, s->rregs, ESP_MAXREG); | |
367 | + qemu_get_buffer(f, s->wregs, ESP_MAXREG); | |
368 | + qemu_get_be32s(f, &s->irq); | |
369 | + for (i = 0; i < ESPDMA_REGS; i++) | |
370 | + qemu_get_be32s(f, &s->espdmaregs[i]); | |
371 | + | |
166 | 372 | return 0; |
167 | 373 | } |
168 | 374 | ... | ... |