]> Git Repo - qemu.git/blob - hw/scsi-bus.c
scsi: add xfer mode
[qemu.git] / hw / scsi-bus.c
1 #include "hw.h"
2 #include "sysemu.h"
3 #include "scsi.h"
4 #include "scsi-defs.h"
5 #include "block.h"
6 #include "qdev.h"
7
8 static struct BusInfo scsi_bus_info = {
9     .name  = "SCSI",
10     .size  = sizeof(SCSIBus),
11     .props = (Property[]) {
12         DEFINE_PROP_UINT32("scsi-id", SCSIDevice, id, -1),
13         DEFINE_PROP_END_OF_LIST(),
14     },
15 };
16 static int next_scsi_bus;
17
18 /* Create a scsi bus, and attach devices to it.  */
19 void scsi_bus_new(SCSIBus *bus, DeviceState *host, int tcq, int ndev,
20                   scsi_completionfn complete)
21 {
22     qbus_create_inplace(&bus->qbus, &scsi_bus_info, host, NULL);
23     bus->busnr = next_scsi_bus++;
24     bus->tcq = tcq;
25     bus->ndev = ndev;
26     bus->complete = complete;
27     bus->qbus.allow_hotplug = 1;
28 }
29
30 static int scsi_qdev_init(DeviceState *qdev, DeviceInfo *base)
31 {
32     SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
33     SCSIDeviceInfo *info = DO_UPCAST(SCSIDeviceInfo, qdev, base);
34     SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
35     int rc = -1;
36
37     if (dev->id == -1) {
38         for (dev->id = 0; dev->id < bus->ndev; dev->id++) {
39             if (bus->devs[dev->id] == NULL)
40                 break;
41         }
42     }
43     if (dev->id >= bus->ndev) {
44         qemu_error("bad scsi device id: %d\n", dev->id);
45         goto err;
46     }
47
48     if (bus->devs[dev->id]) {
49         qdev_free(&bus->devs[dev->id]->qdev);
50     }
51     bus->devs[dev->id] = dev;
52
53     dev->info = info;
54     QTAILQ_INIT(&dev->requests);
55     rc = dev->info->init(dev);
56     if (rc != 0) {
57         bus->devs[dev->id] = NULL;
58     }
59
60 err:
61     return rc;
62 }
63
64 static int scsi_qdev_exit(DeviceState *qdev)
65 {
66     SCSIDevice *dev = DO_UPCAST(SCSIDevice, qdev, qdev);
67     SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, dev->qdev.parent_bus);
68
69     assert(bus->devs[dev->id] != NULL);
70     if (bus->devs[dev->id]->info->destroy) {
71         bus->devs[dev->id]->info->destroy(bus->devs[dev->id]);
72     }
73     bus->devs[dev->id] = NULL;
74     return 0;
75 }
76
77 void scsi_qdev_register(SCSIDeviceInfo *info)
78 {
79     info->qdev.bus_info = &scsi_bus_info;
80     info->qdev.init     = scsi_qdev_init;
81     info->qdev.unplug   = qdev_simple_unplug_cb;
82     info->qdev.exit     = scsi_qdev_exit;
83     qdev_register(&info->qdev);
84 }
85
86 /* handle legacy '-drive if=scsi,...' cmd line args */
87 /* FIXME callers should check for failure, but don't */
88 SCSIDevice *scsi_bus_legacy_add_drive(SCSIBus *bus, DriveInfo *dinfo, int unit)
89 {
90     const char *driver;
91     DeviceState *dev;
92
93     driver = bdrv_is_sg(dinfo->bdrv) ? "scsi-generic" : "scsi-disk";
94     dev = qdev_create(&bus->qbus, driver);
95     qdev_prop_set_uint32(dev, "scsi-id", unit);
96     qdev_prop_set_drive(dev, "drive", dinfo);
97     if (qdev_init(dev) < 0)
98         return NULL;
99     return DO_UPCAST(SCSIDevice, qdev, dev);
100 }
101
102 void scsi_bus_legacy_handle_cmdline(SCSIBus *bus)
103 {
104     DriveInfo *dinfo;
105     int unit;
106
107     for (unit = 0; unit < MAX_SCSI_DEVS; unit++) {
108         dinfo = drive_get(IF_SCSI, bus->busnr, unit);
109         if (dinfo == NULL) {
110             continue;
111         }
112         scsi_bus_legacy_add_drive(bus, dinfo, unit);
113     }
114 }
115
116 SCSIRequest *scsi_req_alloc(size_t size, SCSIDevice *d, uint32_t tag, uint32_t lun)
117 {
118     SCSIRequest *req;
119
120     req = qemu_mallocz(size);
121     req->bus = scsi_bus_from_device(d);
122     req->dev = d;
123     req->tag = tag;
124     req->lun = lun;
125     QTAILQ_INSERT_TAIL(&d->requests, req, next);
126     return req;
127 }
128
129 SCSIRequest *scsi_req_find(SCSIDevice *d, uint32_t tag)
130 {
131     SCSIRequest *req;
132
133     QTAILQ_FOREACH(req, &d->requests, next) {
134         if (req->tag == tag) {
135             return req;
136         }
137     }
138     return NULL;
139 }
140
141 void scsi_req_free(SCSIRequest *req)
142 {
143     QTAILQ_REMOVE(&req->dev->requests, req, next);
144     qemu_free(req);
145 }
146
147 static int scsi_req_length(SCSIRequest *req, uint8_t *cmd)
148 {
149     switch (cmd[0] >> 5) {
150     case 0:
151         req->cmd.xfer = cmd[4];
152         req->cmd.len = 6;
153         /* length 0 means 256 blocks */
154         if (req->cmd.xfer == 0)
155             req->cmd.xfer = 256;
156         break;
157     case 1:
158     case 2:
159         req->cmd.xfer = cmd[8] | (cmd[7] << 8);
160         req->cmd.len = 10;
161         break;
162     case 4:
163         req->cmd.xfer = cmd[13] | (cmd[12] << 8) | (cmd[11] << 16) | (cmd[10] << 24);
164         req->cmd.len = 16;
165         break;
166     case 5:
167         req->cmd.xfer = cmd[9] | (cmd[8] << 8) | (cmd[7] << 16) | (cmd[6] << 24);
168         req->cmd.len = 12;
169         break;
170     default:
171         return -1;
172     }
173
174     switch(cmd[0]) {
175     case TEST_UNIT_READY:
176     case REZERO_UNIT:
177     case START_STOP:
178     case SEEK_6:
179     case WRITE_FILEMARKS:
180     case SPACE:
181     case ERASE:
182     case ALLOW_MEDIUM_REMOVAL:
183     case VERIFY:
184     case SEEK_10:
185     case SYNCHRONIZE_CACHE:
186     case LOCK_UNLOCK_CACHE:
187     case LOAD_UNLOAD:
188     case SET_CD_SPEED:
189     case SET_LIMITS:
190     case WRITE_LONG:
191     case MOVE_MEDIUM:
192     case UPDATE_BLOCK:
193         req->cmd.xfer = 0;
194         break;
195     case MODE_SENSE:
196         break;
197     case WRITE_SAME:
198         req->cmd.xfer = 1;
199         break;
200     case READ_CAPACITY:
201         req->cmd.xfer = 8;
202         break;
203     case READ_BLOCK_LIMITS:
204         req->cmd.xfer = 6;
205         break;
206     case READ_POSITION:
207         req->cmd.xfer = 20;
208         break;
209     case SEND_VOLUME_TAG:
210         req->cmd.xfer *= 40;
211         break;
212     case MEDIUM_SCAN:
213         req->cmd.xfer *= 8;
214         break;
215     case WRITE_10:
216     case WRITE_VERIFY:
217     case WRITE_6:
218     case WRITE_12:
219     case WRITE_VERIFY_12:
220         req->cmd.xfer *= req->dev->blocksize;
221         break;
222     case READ_10:
223     case READ_6:
224     case READ_REVERSE:
225     case RECOVER_BUFFERED_DATA:
226     case READ_12:
227         req->cmd.xfer *= req->dev->blocksize;
228         break;
229     case INQUIRY:
230         req->cmd.xfer = cmd[4] | (cmd[3] << 8);
231         break;
232     }
233     return 0;
234 }
235
236 static int scsi_req_stream_length(SCSIRequest *req, uint8_t *cmd)
237 {
238     switch(cmd[0]) {
239     /* stream commands */
240     case READ_6:
241     case READ_REVERSE:
242     case RECOVER_BUFFERED_DATA:
243     case WRITE_6:
244         req->cmd.len = 6;
245         req->cmd.xfer = cmd[4] | (cmd[3] << 8) | (cmd[2] << 16);
246         if (cmd[1] & 0x01) /* fixed */
247             req->cmd.xfer *= req->dev->blocksize;
248         break;
249     case REWIND:
250     case START_STOP:
251         req->cmd.len = 6;
252         req->cmd.xfer = 0;
253         break;
254     /* generic commands */
255     default:
256         return scsi_req_length(req, cmd);
257     }
258     return 0;
259 }
260
261 static void scsi_req_xfer_mode(SCSIRequest *req)
262 {
263     switch (req->cmd.buf[0]) {
264     case WRITE_6:
265     case WRITE_10:
266     case WRITE_VERIFY:
267     case WRITE_12:
268     case WRITE_VERIFY_12:
269     case COPY:
270     case COPY_VERIFY:
271     case COMPARE:
272     case CHANGE_DEFINITION:
273     case LOG_SELECT:
274     case MODE_SELECT:
275     case MODE_SELECT_10:
276     case SEND_DIAGNOSTIC:
277     case WRITE_BUFFER:
278     case FORMAT_UNIT:
279     case REASSIGN_BLOCKS:
280     case RESERVE:
281     case SEARCH_EQUAL:
282     case SEARCH_HIGH:
283     case SEARCH_LOW:
284     case UPDATE_BLOCK:
285     case WRITE_LONG:
286     case WRITE_SAME:
287     case SEARCH_HIGH_12:
288     case SEARCH_EQUAL_12:
289     case SEARCH_LOW_12:
290     case SET_WINDOW:
291     case MEDIUM_SCAN:
292     case SEND_VOLUME_TAG:
293     case WRITE_LONG_2:
294         req->cmd.mode = SCSI_XFER_TO_DEV;
295         break;
296     default:
297         if (req->cmd.xfer)
298             req->cmd.mode = SCSI_XFER_FROM_DEV;
299         else {
300             req->cmd.mode = SCSI_XFER_NONE;
301         }
302         break;
303     }
304 }
305
306 static uint64_t scsi_req_lba(SCSIRequest *req)
307 {
308     uint8_t *buf = req->cmd.buf;
309     uint64_t lba;
310
311     switch (buf[0] >> 5) {
312     case 0:
313         lba = (uint64_t) buf[3] | ((uint64_t) buf[2] << 8) |
314               (((uint64_t) buf[1] & 0x1f) << 16);
315         break;
316     case 1:
317     case 2:
318         lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
319               ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
320         break;
321     case 4:
322         lba = (uint64_t) buf[9] | ((uint64_t) buf[8] << 8) |
323               ((uint64_t) buf[7] << 16) | ((uint64_t) buf[6] << 24) |
324               ((uint64_t) buf[5] << 32) | ((uint64_t) buf[4] << 40) |
325               ((uint64_t) buf[3] << 48) | ((uint64_t) buf[2] << 56);
326         break;
327     case 5:
328         lba = (uint64_t) buf[5] | ((uint64_t) buf[4] << 8) |
329               ((uint64_t) buf[3] << 16) | ((uint64_t) buf[2] << 24);
330         break;
331     default:
332         lba = -1;
333
334     }
335     return lba;
336 }
337
338 int scsi_req_parse(SCSIRequest *req, uint8_t *buf)
339 {
340     int rc;
341
342     if (req->dev->type == TYPE_TAPE) {
343         rc = scsi_req_stream_length(req, buf);
344     } else {
345         rc = scsi_req_length(req, buf);
346     }
347     if (rc != 0)
348         return rc;
349
350     memcpy(req->cmd.buf, buf, req->cmd.len);
351     scsi_req_xfer_mode(req);
352     req->cmd.lba = scsi_req_lba(req);
353     return 0;
354 }
This page took 0.041118 seconds and 4 git commands to generate.