root / hw / scsi-disk.c @ c5d6edc3
History | View | Annotate | Download (13.3 kB)
1 | 2e5d83bb | pbrook | /*
|
---|---|---|---|
2 | 2e5d83bb | pbrook | * SCSI Device emulation
|
3 | 2e5d83bb | pbrook | *
|
4 | 2e5d83bb | pbrook | * Copyright (c) 2006 CodeSourcery.
|
5 | 2e5d83bb | pbrook | * Based on code by Fabrice Bellard
|
6 | 2e5d83bb | pbrook | *
|
7 | 2e5d83bb | pbrook | * Written by Paul Brook
|
8 | 2e5d83bb | pbrook | *
|
9 | 2e5d83bb | pbrook | * This code is licenced under the LGPL.
|
10 | 2e5d83bb | pbrook | */
|
11 | 2e5d83bb | pbrook | |
12 | 2e5d83bb | pbrook | //#define DEBUG_SCSI
|
13 | 2e5d83bb | pbrook | |
14 | 2e5d83bb | pbrook | #ifdef DEBUG_SCSI
|
15 | 2e5d83bb | pbrook | #define DPRINTF(fmt, args...) \
|
16 | 2e5d83bb | pbrook | do { printf("scsi-disk: " fmt , ##args); } while (0) |
17 | 2e5d83bb | pbrook | #else
|
18 | 2e5d83bb | pbrook | #define DPRINTF(fmt, args...) do {} while(0) |
19 | 2e5d83bb | pbrook | #endif
|
20 | 2e5d83bb | pbrook | |
21 | 2e5d83bb | pbrook | #define BADF(fmt, args...) \
|
22 | 2e5d83bb | pbrook | do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0) |
23 | 2e5d83bb | pbrook | |
24 | 2e5d83bb | pbrook | #include "vl.h" |
25 | 2e5d83bb | pbrook | |
26 | 2e5d83bb | pbrook | #define SENSE_NO_SENSE 0 |
27 | 2e5d83bb | pbrook | #define SENSE_ILLEGAL_REQUEST 5 |
28 | 2e5d83bb | pbrook | |
29 | 2e5d83bb | pbrook | struct SCSIDevice
|
30 | 2e5d83bb | pbrook | { |
31 | 2e5d83bb | pbrook | int command;
|
32 | 2e5d83bb | pbrook | uint32_t tag; |
33 | 2e5d83bb | pbrook | BlockDriverState *bdrv; |
34 | 7c22dd52 | pbrook | /* The qemu block layer uses a fixed 512 byte sector size.
|
35 | 7c22dd52 | pbrook | This is the number of 512 byte blocks in a single scsi sector. */
|
36 | 7c22dd52 | pbrook | int cluster_size;
|
37 | 2e5d83bb | pbrook | /* When transfering data buf_pos and buf_len contain a partially
|
38 | 2e5d83bb | pbrook | transferred block of data (or response to a command), and
|
39 | 7c22dd52 | pbrook | sector/sector_count identify any remaining sectors.
|
40 | 7c22dd52 | pbrook | Both sector and sector_count are in terms of qemu 512 byte blocks. */
|
41 | 2e5d83bb | pbrook | /* ??? We should probably keep track of whether the data trasfer is
|
42 | 2e5d83bb | pbrook | a read or a write. Currently we rely on the host getting it right. */
|
43 | 2e5d83bb | pbrook | int sector;
|
44 | 2e5d83bb | pbrook | int sector_count;
|
45 | 2e5d83bb | pbrook | int buf_pos;
|
46 | 2e5d83bb | pbrook | int buf_len;
|
47 | 2e5d83bb | pbrook | int sense;
|
48 | 7c22dd52 | pbrook | char buf[512]; |
49 | 2e5d83bb | pbrook | scsi_completionfn completion; |
50 | 2e5d83bb | pbrook | void *opaque;
|
51 | 2e5d83bb | pbrook | }; |
52 | 2e5d83bb | pbrook | |
53 | 2e5d83bb | pbrook | static void scsi_command_complete(SCSIDevice *s, int sense) |
54 | 2e5d83bb | pbrook | { |
55 | 2e5d83bb | pbrook | s->sense = sense; |
56 | 0fc5c15a | pbrook | s->completion(s->opaque, s->tag, sense); |
57 | 2e5d83bb | pbrook | } |
58 | 2e5d83bb | pbrook | |
59 | 2e5d83bb | pbrook | /* Read data from a scsi device. Returns nonzero on failure. */
|
60 | 2e5d83bb | pbrook | int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
61 | 2e5d83bb | pbrook | { |
62 | 2e5d83bb | pbrook | uint32_t n; |
63 | 2e5d83bb | pbrook | |
64 | 2e5d83bb | pbrook | DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
|
65 | 2e5d83bb | pbrook | if (s->buf_len == 0 && s->sector_count == 0) |
66 | 2e5d83bb | pbrook | return 1; |
67 | 2e5d83bb | pbrook | |
68 | 2e5d83bb | pbrook | if (s->buf_len) {
|
69 | 2e5d83bb | pbrook | n = s->buf_len; |
70 | 2e5d83bb | pbrook | if (n > len)
|
71 | 2e5d83bb | pbrook | n = len; |
72 | 2e5d83bb | pbrook | memcpy(data, s->buf + s->buf_pos, n); |
73 | 2e5d83bb | pbrook | s->buf_pos += n; |
74 | 2e5d83bb | pbrook | s->buf_len -= n; |
75 | 2e5d83bb | pbrook | data += n; |
76 | 2e5d83bb | pbrook | len -= n; |
77 | 2e5d83bb | pbrook | if (s->buf_len == 0) |
78 | 2e5d83bb | pbrook | s->buf_pos = 0;
|
79 | 2e5d83bb | pbrook | } |
80 | 2e5d83bb | pbrook | |
81 | 7c22dd52 | pbrook | n = len / 512;
|
82 | 2e5d83bb | pbrook | if (n > s->sector_count)
|
83 | 2e5d83bb | pbrook | n = s->sector_count; |
84 | 2e5d83bb | pbrook | |
85 | 2e5d83bb | pbrook | if (n != 0) { |
86 | 2e5d83bb | pbrook | bdrv_read(s->bdrv, s->sector, data, n); |
87 | 7c22dd52 | pbrook | data += n * 512;
|
88 | 7c22dd52 | pbrook | len -= n * 512;
|
89 | 2e5d83bb | pbrook | s->sector += n; |
90 | 2e5d83bb | pbrook | s->sector_count -= n; |
91 | 2e5d83bb | pbrook | } |
92 | 2e5d83bb | pbrook | |
93 | 2e5d83bb | pbrook | if (len && s->sector_count) {
|
94 | cac782d4 | pbrook | bdrv_read(s->bdrv, s->sector, s->buf, 1);
|
95 | 2e5d83bb | pbrook | s->sector++; |
96 | 2e5d83bb | pbrook | s->sector_count--; |
97 | 2e5d83bb | pbrook | s->buf_pos = 0;
|
98 | 7c22dd52 | pbrook | s->buf_len = 512;
|
99 | 2e5d83bb | pbrook | /* Recurse to complete the partial read. */
|
100 | 2e5d83bb | pbrook | return scsi_read_data(s, data, len);
|
101 | 2e5d83bb | pbrook | } |
102 | 2e5d83bb | pbrook | |
103 | 2e5d83bb | pbrook | if (len != 0) |
104 | 2e5d83bb | pbrook | return 1; |
105 | 2e5d83bb | pbrook | |
106 | 2e5d83bb | pbrook | if (s->buf_len == 0 && s->sector_count == 0) |
107 | 2e5d83bb | pbrook | scsi_command_complete(s, SENSE_NO_SENSE); |
108 | 2e5d83bb | pbrook | |
109 | 2e5d83bb | pbrook | return 0; |
110 | 2e5d83bb | pbrook | } |
111 | 2e5d83bb | pbrook | |
112 | 2e5d83bb | pbrook | /* Read data to a scsi device. Returns nonzero on failure. */
|
113 | 2e5d83bb | pbrook | int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
|
114 | 2e5d83bb | pbrook | { |
115 | 2e5d83bb | pbrook | uint32_t n; |
116 | 2e5d83bb | pbrook | |
117 | 2e5d83bb | pbrook | DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
|
118 | 2e5d83bb | pbrook | if (s->buf_pos != 0) { |
119 | 2e5d83bb | pbrook | BADF("Bad state on write\n");
|
120 | 2e5d83bb | pbrook | return 1; |
121 | 2e5d83bb | pbrook | } |
122 | 2e5d83bb | pbrook | |
123 | 2e5d83bb | pbrook | if (s->sector_count == 0) |
124 | 2e5d83bb | pbrook | return 1; |
125 | 2e5d83bb | pbrook | |
126 | 7c22dd52 | pbrook | if (s->buf_len != 0 || len < 512) { |
127 | 7c22dd52 | pbrook | n = 512 - s->buf_len;
|
128 | 2e5d83bb | pbrook | if (n > len)
|
129 | 2e5d83bb | pbrook | n = len; |
130 | 2e5d83bb | pbrook | |
131 | 2e5d83bb | pbrook | memcpy(s->buf + s->buf_len, data, n); |
132 | 2e5d83bb | pbrook | data += n; |
133 | 2e5d83bb | pbrook | s->buf_len += n; |
134 | 2e5d83bb | pbrook | len -= n; |
135 | 7c22dd52 | pbrook | if (s->buf_len == 512) { |
136 | 2e5d83bb | pbrook | /* A full sector has been accumulated. Write it to disk. */
|
137 | 2e5d83bb | pbrook | bdrv_write(s->bdrv, s->sector, s->buf, 1);
|
138 | 2e5d83bb | pbrook | s->buf_len = 0;
|
139 | 2e5d83bb | pbrook | s->sector++; |
140 | 2e5d83bb | pbrook | s->sector_count--; |
141 | 2e5d83bb | pbrook | } |
142 | 2e5d83bb | pbrook | } |
143 | 2e5d83bb | pbrook | |
144 | 7c22dd52 | pbrook | n = len / 512;
|
145 | 2e5d83bb | pbrook | if (n > s->sector_count)
|
146 | 2e5d83bb | pbrook | n = s->sector_count; |
147 | 2e5d83bb | pbrook | |
148 | 2e5d83bb | pbrook | if (n != 0) { |
149 | 2e5d83bb | pbrook | bdrv_write(s->bdrv, s->sector, data, n); |
150 | 7c22dd52 | pbrook | data += n * 512;
|
151 | 7c22dd52 | pbrook | len -= n * 512;
|
152 | 2e5d83bb | pbrook | s->sector += n; |
153 | 2e5d83bb | pbrook | s->sector_count -= n; |
154 | 2e5d83bb | pbrook | } |
155 | 2e5d83bb | pbrook | |
156 | 7c22dd52 | pbrook | if (len >= 512) |
157 | 2e5d83bb | pbrook | return 1; |
158 | 2e5d83bb | pbrook | |
159 | 2e5d83bb | pbrook | if (len && s->sector_count) {
|
160 | 2e5d83bb | pbrook | /* Recurse to complete the partial write. */
|
161 | 2e5d83bb | pbrook | return scsi_write_data(s, data, len);
|
162 | 2e5d83bb | pbrook | } |
163 | 2e5d83bb | pbrook | |
164 | 2e5d83bb | pbrook | if (len != 0) |
165 | 2e5d83bb | pbrook | return 1; |
166 | 2e5d83bb | pbrook | |
167 | 2e5d83bb | pbrook | if (s->sector_count == 0) |
168 | 2e5d83bb | pbrook | scsi_command_complete(s, SENSE_NO_SENSE); |
169 | 2e5d83bb | pbrook | |
170 | 2e5d83bb | pbrook | return 0; |
171 | 2e5d83bb | pbrook | } |
172 | 2e5d83bb | pbrook | |
173 | 2e5d83bb | pbrook | /* Execute a scsi command. Returns the length of the data expected by the
|
174 | 2e5d83bb | pbrook | command. This will be Positive for data transfers from the device
|
175 | 2e5d83bb | pbrook | (eg. disk reads), negative for transfers to the device (eg. disk writes),
|
176 | 2e5d83bb | pbrook | and zero if the command does not transfer any data. */
|
177 | 2e5d83bb | pbrook | |
178 | 0fc5c15a | pbrook | int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
|
179 | 2e5d83bb | pbrook | { |
180 | 2e5d83bb | pbrook | int64_t nb_sectors; |
181 | 2e5d83bb | pbrook | uint32_t lba; |
182 | 2e5d83bb | pbrook | uint32_t len; |
183 | 2e5d83bb | pbrook | int cmdlen;
|
184 | 2e5d83bb | pbrook | int is_write;
|
185 | 2e5d83bb | pbrook | |
186 | 2e5d83bb | pbrook | s->command = buf[0];
|
187 | 2e5d83bb | pbrook | s->tag = tag; |
188 | 2e5d83bb | pbrook | s->sector_count = 0;
|
189 | 2e5d83bb | pbrook | s->buf_pos = 0;
|
190 | 2e5d83bb | pbrook | s->buf_len = 0;
|
191 | 2e5d83bb | pbrook | is_write = 0;
|
192 | 2e5d83bb | pbrook | DPRINTF("Command: 0x%02x", buf[0]); |
193 | 2e5d83bb | pbrook | switch (s->command >> 5) { |
194 | 2e5d83bb | pbrook | case 0: |
195 | 2e5d83bb | pbrook | lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16); |
196 | 2e5d83bb | pbrook | len = buf[4];
|
197 | 2e5d83bb | pbrook | cmdlen = 6;
|
198 | 2e5d83bb | pbrook | break;
|
199 | 2e5d83bb | pbrook | case 1: |
200 | 2e5d83bb | pbrook | case 2: |
201 | 2e5d83bb | pbrook | lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24); |
202 | 2e5d83bb | pbrook | len = buf[8] | (buf[7] << 8); |
203 | 2e5d83bb | pbrook | cmdlen = 10;
|
204 | 2e5d83bb | pbrook | break;
|
205 | 2e5d83bb | pbrook | case 4: |
206 | 2e5d83bb | pbrook | lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24); |
207 | 2e5d83bb | pbrook | len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24); |
208 | 2e5d83bb | pbrook | cmdlen = 16;
|
209 | 2e5d83bb | pbrook | break;
|
210 | 2e5d83bb | pbrook | case 5: |
211 | 2e5d83bb | pbrook | lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24); |
212 | 2e5d83bb | pbrook | len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24); |
213 | 2e5d83bb | pbrook | cmdlen = 12;
|
214 | 2e5d83bb | pbrook | break;
|
215 | 2e5d83bb | pbrook | default:
|
216 | 17acfe32 | pbrook | BADF("Unsupported command length, command %x\n", s->command);
|
217 | 2e5d83bb | pbrook | goto fail;
|
218 | 2e5d83bb | pbrook | } |
219 | 2e5d83bb | pbrook | #ifdef DEBUG_SCSI
|
220 | 2e5d83bb | pbrook | { |
221 | 2e5d83bb | pbrook | int i;
|
222 | 2e5d83bb | pbrook | for (i = 1; i < cmdlen; i++) { |
223 | 2e5d83bb | pbrook | printf(" 0x%02x", buf[i]);
|
224 | 2e5d83bb | pbrook | } |
225 | 2e5d83bb | pbrook | printf("\n");
|
226 | 2e5d83bb | pbrook | } |
227 | 2e5d83bb | pbrook | #endif
|
228 | 0fc5c15a | pbrook | if (lun || buf[1] >> 5) { |
229 | 2e5d83bb | pbrook | /* Only LUN 0 supported. */
|
230 | 0fc5c15a | pbrook | DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5); |
231 | 2e5d83bb | pbrook | goto fail;
|
232 | 2e5d83bb | pbrook | } |
233 | 2e5d83bb | pbrook | switch (s->command) {
|
234 | 2e5d83bb | pbrook | case 0x0: |
235 | 2e5d83bb | pbrook | DPRINTF("Test Unit Ready\n");
|
236 | 2e5d83bb | pbrook | break;
|
237 | 2e5d83bb | pbrook | case 0x03: |
238 | 2e5d83bb | pbrook | DPRINTF("Request Sense (len %d)\n", len);
|
239 | 2e5d83bb | pbrook | if (len < 4) |
240 | 2e5d83bb | pbrook | goto fail;
|
241 | 2e5d83bb | pbrook | memset(buf, 0, 4); |
242 | 2e5d83bb | pbrook | s->buf[0] = 0xf0; |
243 | 2e5d83bb | pbrook | s->buf[1] = 0; |
244 | 2e5d83bb | pbrook | s->buf[2] = s->sense;
|
245 | 2e5d83bb | pbrook | s->buf_len = 4;
|
246 | 2e5d83bb | pbrook | break;
|
247 | 2e5d83bb | pbrook | case 0x12: |
248 | 7d8406be | pbrook | DPRINTF("Inquiry (len %d)\n", len);
|
249 | 2e5d83bb | pbrook | if (len < 36) { |
250 | 2e5d83bb | pbrook | BADF("Inquiry buffer too small (%d)\n", len);
|
251 | 2e5d83bb | pbrook | } |
252 | 2e5d83bb | pbrook | memset(s->buf, 0, 36); |
253 | 2e5d83bb | pbrook | if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
|
254 | 2e5d83bb | pbrook | s->buf[0] = 5; |
255 | 2e5d83bb | pbrook | s->buf[1] = 0x80; |
256 | 7d8406be | pbrook | memcpy(&s->buf[16], "QEMU CD-ROM ", 16); |
257 | 2e5d83bb | pbrook | } else {
|
258 | 2e5d83bb | pbrook | s->buf[0] = 0; |
259 | 2e5d83bb | pbrook | memcpy(&s->buf[16], "QEMU HARDDISK ", 16); |
260 | 2e5d83bb | pbrook | } |
261 | 2e5d83bb | pbrook | memcpy(&s->buf[8], "QEMU ", 8); |
262 | 7d8406be | pbrook | memcpy(&s->buf[32], QEMU_VERSION, 4); |
263 | 17acfe32 | pbrook | /* Identify device as SCSI-3 rev 1.
|
264 | 17acfe32 | pbrook | Some later commands are also implemented. */
|
265 | 17acfe32 | pbrook | s->buf[2] = 3; |
266 | 2e5d83bb | pbrook | s->buf[3] = 2; /* Format 2 */ |
267 | 2e5d83bb | pbrook | s->buf[4] = 32; |
268 | 2e5d83bb | pbrook | s->buf_len = 36;
|
269 | 2e5d83bb | pbrook | break;
|
270 | 2e5d83bb | pbrook | case 0x16: |
271 | 2e5d83bb | pbrook | DPRINTF("Reserve(6)\n");
|
272 | 2e5d83bb | pbrook | if (buf[1] & 1) |
273 | 2e5d83bb | pbrook | goto fail;
|
274 | 2e5d83bb | pbrook | break;
|
275 | 2e5d83bb | pbrook | case 0x17: |
276 | 2e5d83bb | pbrook | DPRINTF("Release(6)\n");
|
277 | 2e5d83bb | pbrook | if (buf[1] & 1) |
278 | 2e5d83bb | pbrook | goto fail;
|
279 | 2e5d83bb | pbrook | break;
|
280 | 2e5d83bb | pbrook | case 0x1a: |
281 | 7d8406be | pbrook | case 0x5a: |
282 | 17acfe32 | pbrook | { |
283 | 17acfe32 | pbrook | char *p;
|
284 | 17acfe32 | pbrook | int page;
|
285 | 17acfe32 | pbrook | |
286 | 17acfe32 | pbrook | page = buf[2] & 0x3f; |
287 | 17acfe32 | pbrook | DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
|
288 | 17acfe32 | pbrook | p = s->buf; |
289 | 17acfe32 | pbrook | memset(p, 0, 4); |
290 | 7d8406be | pbrook | s->buf[1] = 0; /* Default media type. */ |
291 | 7d8406be | pbrook | s->buf[3] = 0; /* Block descriptor length. */ |
292 | 17acfe32 | pbrook | if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
|
293 | 17acfe32 | pbrook | s->buf[2] = 0x80; /* Readonly. */ |
294 | 17acfe32 | pbrook | } |
295 | 17acfe32 | pbrook | p += 4;
|
296 | 17acfe32 | pbrook | if ((page == 8 || page == 0x3f)) { |
297 | 17acfe32 | pbrook | /* Caching page. */
|
298 | 17acfe32 | pbrook | p[0] = 8; |
299 | 17acfe32 | pbrook | p[1] = 0x12; |
300 | 17acfe32 | pbrook | p[2] = 4; /* WCE */ |
301 | 17acfe32 | pbrook | p += 19;
|
302 | 17acfe32 | pbrook | } |
303 | 17acfe32 | pbrook | if ((page == 0x3f || page == 0x2a) |
304 | 17acfe32 | pbrook | && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) { |
305 | 17acfe32 | pbrook | /* CD Capabilities and Mechanical Status page. */
|
306 | 17acfe32 | pbrook | p[0] = 0x2a; |
307 | 17acfe32 | pbrook | p[1] = 0x14; |
308 | 17acfe32 | pbrook | p[2] = 3; // CD-R & CD-RW read |
309 | 17acfe32 | pbrook | p[3] = 0; // Writing not supported |
310 | 17acfe32 | pbrook | p[4] = 0x7f; /* Audio, composite, digital out, |
311 | 17acfe32 | pbrook | mode 2 form 1&2, multi session */
|
312 | 17acfe32 | pbrook | p[5] = 0xff; /* CD DA, DA accurate, RW supported, |
313 | 17acfe32 | pbrook | RW corrected, C2 errors, ISRC,
|
314 | 17acfe32 | pbrook | UPC, Bar code */
|
315 | 17acfe32 | pbrook | p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0); |
316 | 17acfe32 | pbrook | /* Locking supported, jumper present, eject, tray */
|
317 | 17acfe32 | pbrook | p[7] = 0; /* no volume & mute control, no |
318 | 17acfe32 | pbrook | changer */
|
319 | 17acfe32 | pbrook | p[8] = (50 * 176) >> 8; // 50x read speed |
320 | 17acfe32 | pbrook | p[9] = (50 * 176) & 0xff; |
321 | 17acfe32 | pbrook | p[10] = 0 >> 8; // No volume |
322 | 17acfe32 | pbrook | p[11] = 0 & 0xff; |
323 | 17acfe32 | pbrook | p[12] = 2048 >> 8; // 2M buffer |
324 | 17acfe32 | pbrook | p[13] = 2048 & 0xff; |
325 | 17acfe32 | pbrook | p[14] = (16 * 176) >> 8; // 16x read speed current |
326 | 17acfe32 | pbrook | p[15] = (16 * 176) & 0xff; |
327 | 17acfe32 | pbrook | p[18] = (16 * 176) >> 8; // 16x write speed |
328 | 17acfe32 | pbrook | p[19] = (16 * 176) & 0xff; |
329 | 17acfe32 | pbrook | p[20] = (16 * 176) >> 8; // 16x write speed current |
330 | 17acfe32 | pbrook | p[21] = (16 * 176) & 0xff; |
331 | 17acfe32 | pbrook | p += 21;
|
332 | 17acfe32 | pbrook | } |
333 | 17acfe32 | pbrook | s->buf_len = p - s->buf; |
334 | 17acfe32 | pbrook | s->buf[0] = s->buf_len - 4; |
335 | 17acfe32 | pbrook | if (s->buf_len > len)
|
336 | 17acfe32 | pbrook | s->buf_len = len; |
337 | 7d8406be | pbrook | } |
338 | 17acfe32 | pbrook | break;
|
339 | 17acfe32 | pbrook | case 0x1b: |
340 | 17acfe32 | pbrook | DPRINTF("Start Stop Unit\n");
|
341 | 17acfe32 | pbrook | break;
|
342 | 17acfe32 | pbrook | case 0x1e: |
343 | 17acfe32 | pbrook | DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3); |
344 | 17acfe32 | pbrook | bdrv_set_locked(s->bdrv, buf[4] & 1); |
345 | 2e5d83bb | pbrook | break;
|
346 | 2e5d83bb | pbrook | case 0x25: |
347 | 2e5d83bb | pbrook | DPRINTF("Read Capacity\n");
|
348 | 2e5d83bb | pbrook | /* The normal LEN field for this command is zero. */
|
349 | 2e5d83bb | pbrook | memset(s->buf, 0, 8); |
350 | 2e5d83bb | pbrook | bdrv_get_geometry(s->bdrv, &nb_sectors); |
351 | 2e5d83bb | pbrook | s->buf[0] = (nb_sectors >> 24) & 0xff; |
352 | 2e5d83bb | pbrook | s->buf[1] = (nb_sectors >> 16) & 0xff; |
353 | 2e5d83bb | pbrook | s->buf[2] = (nb_sectors >> 8) & 0xff; |
354 | 2e5d83bb | pbrook | s->buf[3] = nb_sectors & 0xff; |
355 | 2e5d83bb | pbrook | s->buf[4] = 0; |
356 | 2e5d83bb | pbrook | s->buf[5] = 0; |
357 | 7c22dd52 | pbrook | s->buf[6] = s->cluster_size * 2; |
358 | 7c22dd52 | pbrook | s->buf[7] = 0; |
359 | 2e5d83bb | pbrook | s->buf_len = 8;
|
360 | 2e5d83bb | pbrook | break;
|
361 | 2e5d83bb | pbrook | case 0x08: |
362 | 2e5d83bb | pbrook | case 0x28: |
363 | 2e5d83bb | pbrook | DPRINTF("Read (sector %d, count %d)\n", lba, len);
|
364 | 7c22dd52 | pbrook | s->sector = lba * s->cluster_size; |
365 | 7c22dd52 | pbrook | s->sector_count = len * s->cluster_size; |
366 | 2e5d83bb | pbrook | break;
|
367 | 2e5d83bb | pbrook | case 0x0a: |
368 | 2e5d83bb | pbrook | case 0x2a: |
369 | 2e5d83bb | pbrook | DPRINTF("Write (sector %d, count %d)\n", lba, len);
|
370 | 7c22dd52 | pbrook | s->sector = lba * s->cluster_size; |
371 | 7c22dd52 | pbrook | s->sector_count = len * s->cluster_size; |
372 | 2e5d83bb | pbrook | is_write = 1;
|
373 | 2e5d83bb | pbrook | break;
|
374 | 7d8406be | pbrook | case 0x35: |
375 | 7d8406be | pbrook | DPRINTF("Syncronise cache (sector %d, count %d)\n", lba, len);
|
376 | 7a6cba61 | pbrook | bdrv_flush(s->bdrv); |
377 | 7d8406be | pbrook | break;
|
378 | 2e5d83bb | pbrook | case 0x43: |
379 | 2e5d83bb | pbrook | { |
380 | 7c22dd52 | pbrook | int start_track, format, msf, toclen;
|
381 | 2e5d83bb | pbrook | |
382 | 2e5d83bb | pbrook | msf = buf[1] & 2; |
383 | 2e5d83bb | pbrook | format = buf[2] & 0xf; |
384 | 2e5d83bb | pbrook | start_track = buf[6];
|
385 | 2e5d83bb | pbrook | bdrv_get_geometry(s->bdrv, &nb_sectors); |
386 | 2e5d83bb | pbrook | DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); |
387 | 2e5d83bb | pbrook | switch(format) {
|
388 | 2e5d83bb | pbrook | case 0: |
389 | 7c22dd52 | pbrook | toclen = cdrom_read_toc(nb_sectors, s->buf, msf, start_track); |
390 | 2e5d83bb | pbrook | break;
|
391 | 2e5d83bb | pbrook | case 1: |
392 | 2e5d83bb | pbrook | /* multi session : only a single session defined */
|
393 | 7c22dd52 | pbrook | toclen = 12;
|
394 | 2e5d83bb | pbrook | memset(s->buf, 0, 12); |
395 | 2e5d83bb | pbrook | s->buf[1] = 0x0a; |
396 | 2e5d83bb | pbrook | s->buf[2] = 0x01; |
397 | 2e5d83bb | pbrook | s->buf[3] = 0x01; |
398 | 2e5d83bb | pbrook | break;
|
399 | 2e5d83bb | pbrook | case 2: |
400 | 7c22dd52 | pbrook | toclen = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track); |
401 | 2e5d83bb | pbrook | break;
|
402 | 2e5d83bb | pbrook | default:
|
403 | 7c22dd52 | pbrook | goto error_cmd;
|
404 | 7c22dd52 | pbrook | } |
405 | 7c22dd52 | pbrook | if (toclen > 0) { |
406 | 7c22dd52 | pbrook | if (len > toclen)
|
407 | 7c22dd52 | pbrook | len = toclen; |
408 | 7c22dd52 | pbrook | s->buf_len = len; |
409 | 7c22dd52 | pbrook | break;
|
410 | 2e5d83bb | pbrook | } |
411 | 7c22dd52 | pbrook | error_cmd:
|
412 | 7c22dd52 | pbrook | DPRINTF("Read TOC error\n");
|
413 | 7c22dd52 | pbrook | goto fail;
|
414 | 2e5d83bb | pbrook | } |
415 | 17acfe32 | pbrook | case 0x46: |
416 | 17acfe32 | pbrook | DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len); |
417 | 17acfe32 | pbrook | memset(s->buf, 0, 8); |
418 | 17acfe32 | pbrook | /* ??? This shoud probably return much more information. For now
|
419 | 17acfe32 | pbrook | just return the basic header indicating the CD-ROM profile. */
|
420 | 17acfe32 | pbrook | s->buf[7] = 8; // CD-ROM |
421 | 17acfe32 | pbrook | s->buf_len = 8;
|
422 | 17acfe32 | pbrook | break;
|
423 | 2e5d83bb | pbrook | case 0x56: |
424 | 2e5d83bb | pbrook | DPRINTF("Reserve(10)\n");
|
425 | 2e5d83bb | pbrook | if (buf[1] & 3) |
426 | 2e5d83bb | pbrook | goto fail;
|
427 | 2e5d83bb | pbrook | break;
|
428 | 2e5d83bb | pbrook | case 0x57: |
429 | 2e5d83bb | pbrook | DPRINTF("Release(10)\n");
|
430 | 2e5d83bb | pbrook | if (buf[1] & 3) |
431 | 2e5d83bb | pbrook | goto fail;
|
432 | 2e5d83bb | pbrook | break;
|
433 | 2e5d83bb | pbrook | case 0xa0: |
434 | 2e5d83bb | pbrook | DPRINTF("Report LUNs (len %d)\n", len);
|
435 | 2e5d83bb | pbrook | if (len < 16) |
436 | 2e5d83bb | pbrook | goto fail;
|
437 | 2e5d83bb | pbrook | memset(s->buf, 0, 16); |
438 | 2e5d83bb | pbrook | s->buf[3] = 8; |
439 | 2e5d83bb | pbrook | s->buf_len = 16;
|
440 | 2e5d83bb | pbrook | break;
|
441 | 2e5d83bb | pbrook | default:
|
442 | 2e5d83bb | pbrook | DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]); |
443 | 2e5d83bb | pbrook | fail:
|
444 | 2e5d83bb | pbrook | scsi_command_complete(s, SENSE_ILLEGAL_REQUEST); |
445 | 2e5d83bb | pbrook | return 0; |
446 | 2e5d83bb | pbrook | } |
447 | 2e5d83bb | pbrook | if (s->sector_count == 0 && s->buf_len == 0) { |
448 | 2e5d83bb | pbrook | scsi_command_complete(s, SENSE_NO_SENSE); |
449 | 2e5d83bb | pbrook | } |
450 | 7c22dd52 | pbrook | len = s->sector_count * 512 + s->buf_len;
|
451 | 2e5d83bb | pbrook | return is_write ? -len : len;
|
452 | 2e5d83bb | pbrook | } |
453 | 2e5d83bb | pbrook | |
454 | 2e5d83bb | pbrook | void scsi_disk_destroy(SCSIDevice *s)
|
455 | 2e5d83bb | pbrook | { |
456 | 2e5d83bb | pbrook | bdrv_close(s->bdrv); |
457 | 2e5d83bb | pbrook | qemu_free(s); |
458 | 2e5d83bb | pbrook | } |
459 | 2e5d83bb | pbrook | |
460 | 2e5d83bb | pbrook | SCSIDevice *scsi_disk_init(BlockDriverState *bdrv, |
461 | 2e5d83bb | pbrook | scsi_completionfn completion, |
462 | 2e5d83bb | pbrook | void *opaque)
|
463 | 2e5d83bb | pbrook | { |
464 | 2e5d83bb | pbrook | SCSIDevice *s; |
465 | 2e5d83bb | pbrook | |
466 | 2e5d83bb | pbrook | s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
|
467 | 2e5d83bb | pbrook | s->bdrv = bdrv; |
468 | 2e5d83bb | pbrook | s->completion = completion; |
469 | 2e5d83bb | pbrook | s->opaque = opaque; |
470 | 2e5d83bb | pbrook | if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
|
471 | 7c22dd52 | pbrook | s->cluster_size = 4;
|
472 | 2e5d83bb | pbrook | } else {
|
473 | 7c22dd52 | pbrook | s->cluster_size = 1;
|
474 | 2e5d83bb | pbrook | } |
475 | 2e5d83bb | pbrook | |
476 | 2e5d83bb | pbrook | return s;
|
477 | 2e5d83bb | pbrook | } |