2 * SCSI Device emulation
4 * Copyright (c) 2006 CodeSourcery.
5 * Based on code by Fabrice Bellard
7 * Written by Paul Brook
9 * This code is licenced under the LGPL.
15 #define DPRINTF(fmt, args...) \
16 do { printf("scsi-disk: " fmt , ##args); } while (0)
18 #define DPRINTF(fmt, args...) do {} while(0)
21 #define BADF(fmt, args...) \
22 do { fprintf(stderr, "scsi-disk: " fmt , ##args); } while (0)
26 #define SENSE_NO_SENSE 0
27 #define SENSE_NOT_READY 2
28 #define SENSE_ILLEGAL_REQUEST 5
34 BlockDriverState *bdrv;
35 /* The qemu block layer uses a fixed 512 byte sector size.
36 This is the number of 512 byte blocks in a single scsi sector. */
38 /* When transfering data buf_pos and buf_len contain a partially
39 transferred block of data (or response to a command), and
40 sector/sector_count identify any remaining sectors.
41 Both sector and sector_count are in terms of qemu 512 byte blocks. */
42 /* ??? We should probably keep track of whether the data trasfer is
43 a read or a write. Currently we rely on the host getting it right. */
50 scsi_completionfn completion;
54 static void scsi_command_complete(SCSIDevice *s, int sense)
57 s->completion(s->opaque, s->tag, sense);
60 /* Read data from a scsi device. Returns nonzero on failure. */
61 int scsi_read_data(SCSIDevice *s, uint8_t *data, uint32_t len)
65 DPRINTF("Read %d (%d/%d)\n", len, s->buf_len, s->sector_count);
66 if (s->buf_len == 0 && s->sector_count == 0)
73 memcpy(data, s->buf + s->buf_pos, n);
83 if (n > s->sector_count)
87 bdrv_read(s->bdrv, s->sector, data, n);
94 if (len && s->sector_count) {
95 bdrv_read(s->bdrv, s->sector, s->buf, 1);
100 /* Recurse to complete the partial read. */
101 return scsi_read_data(s, data, len);
107 if (s->buf_len == 0 && s->sector_count == 0)
108 scsi_command_complete(s, SENSE_NO_SENSE);
113 /* Read data to a scsi device. Returns nonzero on failure. */
114 int scsi_write_data(SCSIDevice *s, uint8_t *data, uint32_t len)
118 DPRINTF("Write %d (%d/%d)\n", len, s->buf_len, s->sector_count);
119 if (s->buf_pos != 0) {
120 BADF("Bad state on write\n");
124 if (s->sector_count == 0)
127 if (s->buf_len != 0 || len < 512) {
128 n = 512 - s->buf_len;
132 memcpy(s->buf + s->buf_len, data, n);
136 if (s->buf_len == 512) {
137 /* A full sector has been accumulated. Write it to disk. */
138 bdrv_write(s->bdrv, s->sector, s->buf, 1);
146 if (n > s->sector_count)
150 bdrv_write(s->bdrv, s->sector, data, n);
154 s->sector_count -= n;
160 if (len && s->sector_count) {
161 /* Recurse to complete the partial write. */
162 return scsi_write_data(s, data, len);
168 if (s->sector_count == 0)
169 scsi_command_complete(s, SENSE_NO_SENSE);
174 /* Execute a scsi command. Returns the length of the data expected by the
175 command. This will be Positive for data transfers from the device
176 (eg. disk reads), negative for transfers to the device (eg. disk writes),
177 and zero if the command does not transfer any data. */
179 int32_t scsi_send_command(SCSIDevice *s, uint32_t tag, uint8_t *buf, int lun)
193 DPRINTF("Command: 0x%02x", buf[0]);
194 switch (s->command >> 5) {
196 lba = buf[3] | (buf[2] << 8) | ((buf[1] & 0x1f) << 16);
202 lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
203 len = buf[8] | (buf[7] << 8);
207 lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
208 len = buf[13] | (buf[12] << 8) | (buf[11] << 16) | (buf[10] << 24);
212 lba = buf[5] | (buf[4] << 8) | (buf[3] << 16) | (buf[2] << 24);
213 len = buf[9] | (buf[8] << 8) | (buf[7] << 16) | (buf[6] << 24);
217 BADF("Unsupported command length, command %x\n", s->command);
223 for (i = 1; i < cmdlen; i++) {
224 printf(" 0x%02x", buf[i]);
229 if (lun || buf[1] >> 5) {
230 /* Only LUN 0 supported. */
231 DPRINTF("Unimplemented LUN %d\n", lun ? lun : buf[1] >> 5);
234 switch (s->command) {
236 DPRINTF("Test Unit Ready\n");
239 DPRINTF("Request Sense (len %d)\n", len);
245 s->buf[2] = s->sense;
249 DPRINTF("Inquiry (len %d)\n", len);
251 BADF("Inquiry buffer too small (%d)\n", len);
253 memset(s->buf, 0, 36);
254 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
257 memcpy(&s->buf[16], "QEMU CD-ROM ", 16);
260 memcpy(&s->buf[16], "QEMU HARDDISK ", 16);
262 memcpy(&s->buf[8], "QEMU ", 8);
263 memcpy(&s->buf[32], QEMU_VERSION, 4);
264 /* Identify device as SCSI-3 rev 1.
265 Some later commands are also implemented. */
267 s->buf[3] = 2; /* Format 2 */
272 DPRINTF("Reserve(6)\n");
277 DPRINTF("Release(6)\n");
287 page = buf[2] & 0x3f;
288 DPRINTF("Mode Sense (page %d, len %d)\n", page, len);
291 s->buf[1] = 0; /* Default media type. */
292 s->buf[3] = 0; /* Block descriptor length. */
293 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {
294 s->buf[2] = 0x80; /* Readonly. */
297 if ((page == 8 || page == 0x3f)) {
304 if ((page == 0x3f || page == 0x2a)
305 && (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM)) {
306 /* CD Capabilities and Mechanical Status page. */
309 p[2] = 3; // CD-R & CD-RW read
310 p[3] = 0; // Writing not supported
311 p[4] = 0x7f; /* Audio, composite, digital out,
312 mode 2 form 1&2, multi session */
313 p[5] = 0xff; /* CD DA, DA accurate, RW supported,
314 RW corrected, C2 errors, ISRC,
316 p[6] = 0x2d | (bdrv_is_locked(s->bdrv)? 2 : 0);
317 /* Locking supported, jumper present, eject, tray */
318 p[7] = 0; /* no volume & mute control, no
320 p[8] = (50 * 176) >> 8; // 50x read speed
321 p[9] = (50 * 176) & 0xff;
322 p[10] = 0 >> 8; // No volume
324 p[12] = 2048 >> 8; // 2M buffer
326 p[14] = (16 * 176) >> 8; // 16x read speed current
327 p[15] = (16 * 176) & 0xff;
328 p[18] = (16 * 176) >> 8; // 16x write speed
329 p[19] = (16 * 176) & 0xff;
330 p[20] = (16 * 176) >> 8; // 16x write speed current
331 p[21] = (16 * 176) & 0xff;
334 s->buf_len = p - s->buf;
335 s->buf[0] = s->buf_len - 4;
336 if (s->buf_len > len)
341 DPRINTF("Start Stop Unit\n");
344 DPRINTF("Prevent Allow Medium Removal (prevent = %d)\n", buf[4] & 3);
345 bdrv_set_locked(s->bdrv, buf[4] & 1);
348 DPRINTF("Read Capacity\n");
349 /* The normal LEN field for this command is zero. */
350 memset(s->buf, 0, 8);
351 bdrv_get_geometry(s->bdrv, &nb_sectors);
352 /* Returned value is the address of the last sector. */
355 s->buf[0] = (nb_sectors >> 24) & 0xff;
356 s->buf[1] = (nb_sectors >> 16) & 0xff;
357 s->buf[2] = (nb_sectors >> 8) & 0xff;
358 s->buf[3] = nb_sectors & 0xff;
361 s->buf[6] = s->cluster_size * 2;
365 scsi_command_complete(s, SENSE_NOT_READY);
370 DPRINTF("Read (sector %d, count %d)\n", lba, len);
371 s->sector = lba * s->cluster_size;
372 s->sector_count = len * s->cluster_size;
376 DPRINTF("Write (sector %d, count %d)\n", lba, len);
377 s->sector = lba * s->cluster_size;
378 s->sector_count = len * s->cluster_size;
382 DPRINTF("Syncronise cache (sector %d, count %d)\n", lba, len);
387 int start_track, format, msf, toclen;
390 format = buf[2] & 0xf;
391 start_track = buf[6];
392 bdrv_get_geometry(s->bdrv, &nb_sectors);
393 DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1);
396 toclen = cdrom_read_toc(nb_sectors, s->buf, msf, start_track);
399 /* multi session : only a single session defined */
401 memset(s->buf, 0, 12);
407 toclen = cdrom_read_toc_raw(nb_sectors, s->buf, msf, start_track);
419 DPRINTF("Read TOC error\n");
423 DPRINTF("Get Configuration (rt %d, maxlen %d)\n", buf[1] & 3, len);
424 memset(s->buf, 0, 8);
425 /* ??? This shoud probably return much more information. For now
426 just return the basic header indicating the CD-ROM profile. */
427 s->buf[7] = 8; // CD-ROM
431 DPRINTF("Reserve(10)\n");
436 DPRINTF("Release(10)\n");
441 DPRINTF("Report LUNs (len %d)\n", len);
444 memset(s->buf, 0, 16);
449 DPRINTF("Unknown SCSI command (%2.2x)\n", buf[0]);
451 scsi_command_complete(s, SENSE_ILLEGAL_REQUEST);
454 if (s->sector_count == 0 && s->buf_len == 0) {
455 scsi_command_complete(s, SENSE_NO_SENSE);
457 len = s->sector_count * 512 + s->buf_len;
458 return is_write ? -len : len;
461 void scsi_disk_destroy(SCSIDevice *s)
467 SCSIDevice *scsi_disk_init(BlockDriverState *bdrv,
468 scsi_completionfn completion,
473 s = (SCSIDevice *)qemu_mallocz(sizeof(SCSIDevice));
475 s->completion = completion;
477 if (bdrv_get_type_hint(s->bdrv) == BDRV_TYPE_CDROM) {