- added instructions how to update the online documentation
[bochs-mirror.git] / iodev / usb_msd.cc
blob069328b9c5803bd5805f790800e61a999de89b15
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: usb_msd.cc,v 1.8 2008/01/26 22:24:02 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2007 Volker Ruppert
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Lesser General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // Lesser General Public License for more details.
17 // You should have received a copy of the GNU Lesser General Public
18 // License along with this library; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 // USB mass storage device support ported from the Qemu project
24 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
25 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
26 // is used to know when we are exporting symbols and when we are importing.
27 #define BX_PLUGGABLE
29 #define NO_DEVICE_INCLUDES
30 #include "iodev.h"
31 #if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
32 #include "pciusb.h"
33 #include "hdimage.h"
34 #include "scsi_device.h"
35 #include "usb_msd.h"
37 #define LOG_THIS
39 enum USBMSDMode {
40 USB_MSDM_CBW,
41 USB_MSDM_DATAOUT,
42 USB_MSDM_DATAIN,
43 USB_MSDM_CSW
46 struct usb_msd_cbw {
47 Bit32u sig;
48 Bit32u tag;
49 Bit32u data_len;
50 Bit8u flags;
51 Bit8u lun;
52 Bit8u cmd_len;
53 Bit8u cmd[16];
56 struct usb_msd_csw {
57 Bit32u sig;
58 Bit32u tag;
59 Bit32u residue;
60 Bit8u status;
63 // USB requests
64 #define MassStorageReset 0xff
65 #define GetMaxLun 0xfe
67 static const Bit8u bx_msd_dev_descriptor[] = {
68 0x12, /* u8 bLength; */
69 0x01, /* u8 bDescriptorType; Device */
70 0x00, 0x02, /* u16 bcdUSB; v2.0 */
72 0x00, /* u8 bDeviceClass; */
73 0x00, /* u8 bDeviceSubClass; */
74 0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
75 0x40, /* u8 bMaxPacketSize0; 64 Bytes */
77 /* Vendor and product id are arbitrary. */
78 0x00, 0x00, /* u16 idVendor; */
79 0x00, 0x00, /* u16 idProduct; */
80 0x00, 0x01, /* u16 bcdDevice */
82 0x01, /* u8 iManufacturer; */
83 0x02, /* u8 iProduct; */
84 0x03, /* u8 iSerialNumber; */
85 0x01 /* u8 bNumConfigurations; */
88 static const Bit8u bx_msd_config_descriptor[] = {
90 /* one configuration */
91 0x09, /* u8 bLength; */
92 0x02, /* u8 bDescriptorType; Configuration */
93 0x20, 0x00, /* u16 wTotalLength; */
94 0x01, /* u8 bNumInterfaces; (1) */
95 0x01, /* u8 bConfigurationValue; */
96 0x00, /* u8 iConfiguration; */
97 0xc0, /* u8 bmAttributes;
98 Bit 7: must be set,
99 6: Self-powered,
100 5: Remote wakeup,
101 4..0: resvd */
102 0x00, /* u8 MaxPower; */
104 /* one interface */
105 0x09, /* u8 if_bLength; */
106 0x04, /* u8 if_bDescriptorType; Interface */
107 0x00, /* u8 if_bInterfaceNumber; */
108 0x00, /* u8 if_bAlternateSetting; */
109 0x02, /* u8 if_bNumEndpoints; */
110 0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
111 0x06, /* u8 if_bInterfaceSubClass; SCSI */
112 0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
113 0x00, /* u8 if_iInterface; */
115 /* Bulk-In endpoint */
116 0x07, /* u8 ep_bLength; */
117 0x05, /* u8 ep_bDescriptorType; Endpoint */
118 0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
119 0x02, /* u8 ep_bmAttributes; Bulk */
120 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
121 0x00, /* u8 ep_bInterval; */
123 /* Bulk-Out endpoint */
124 0x07, /* u8 ep_bLength; */
125 0x05, /* u8 ep_bDescriptorType; Endpoint */
126 0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
127 0x02, /* u8 ep_bmAttributes; Bulk */
128 0x40, 0x00, /* u16 ep_wMaxPacketSize; */
129 0x00 /* u8 ep_bInterval; */
132 usb_msd_device_t::usb_msd_device_t(void)
134 d.type = USB_DEV_TYPE_DISK;
135 d.speed = USB_SPEED_FULL;
136 strcpy(d.devname, "BOCHS USB HARDDRIVE");
137 memset((void*)&s, 0, sizeof(s));
139 put("USBMS");
140 settype(PCIUSBLOG);
143 usb_msd_device_t::~usb_msd_device_t(void)
145 delete s.scsi_dev;
146 s.hdimage->close();
147 delete s.hdimage;
150 bx_bool usb_msd_device_t::init(const char *filename)
152 s.hdimage = new default_image_t();
153 if (s.hdimage->open(filename) < 0) {
154 BX_ERROR(("could not open hard drive image file '%s'", filename));
155 return 0;
156 } else {
157 s.scsi_dev = new scsi_device_t(s.hdimage, 0, usb_msd_command_complete, (void*)this);
158 s.scsi_dev->register_state(s.sr_list, "scsidev");
159 s.mode = USB_MSDM_CBW;
160 d.connected = 1;
161 return 1;
165 void usb_msd_device_t::register_state_specific(bx_list_c *parent)
167 s.sr_list = new bx_list_c(parent, "s", "USB MSD Device State", 8);
168 new bx_shadow_num_c(s.sr_list, "mode", &s.mode);
169 new bx_shadow_num_c(s.sr_list, "scsi_len", &s.scsi_len);
170 new bx_shadow_num_c(s.sr_list, "usb_len", &s.usb_len);
171 new bx_shadow_num_c(s.sr_list, "data_len", &s.data_len);
172 new bx_shadow_num_c(s.sr_list, "residue", &s.residue);
173 new bx_shadow_num_c(s.sr_list, "tag", &s.tag);
174 new bx_shadow_num_c(s.sr_list, "result", &s.result);
177 void usb_msd_device_t::handle_reset()
179 BX_DEBUG(("Reset"));
180 s.mode = USB_MSDM_CBW;
183 int usb_msd_device_t::handle_control(int request, int value, int index, int length, Bit8u *data)
185 int ret = 0;
187 switch (request) {
188 case DeviceRequest | USB_REQ_GET_STATUS:
189 data[0] = (1 << USB_DEVICE_SELF_POWERED) |
190 (d.remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
191 data[1] = 0x00;
192 ret = 2;
193 break;
194 case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
195 if (value == USB_DEVICE_REMOTE_WAKEUP) {
196 d.remote_wakeup = 0;
197 } else {
198 goto fail;
200 ret = 0;
201 break;
202 case DeviceOutRequest | USB_REQ_SET_FEATURE:
203 if (value == USB_DEVICE_REMOTE_WAKEUP) {
204 d.remote_wakeup = 1;
205 } else {
206 goto fail;
208 ret = 0;
209 break;
210 case DeviceOutRequest | USB_REQ_SET_ADDRESS:
211 d.addr = value;
212 ret = 0;
213 break;
214 case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
215 switch(value >> 8) {
216 case USB_DT_DEVICE:
217 memcpy(data, bx_msd_dev_descriptor, sizeof(bx_msd_dev_descriptor));
218 ret = sizeof(bx_msd_dev_descriptor);
219 break;
220 case USB_DT_CONFIG:
221 memcpy(data, bx_msd_config_descriptor, sizeof(bx_msd_config_descriptor));
222 ret = sizeof(bx_msd_config_descriptor);
223 break;
224 case USB_DT_STRING:
225 switch(value & 0xff) {
226 case 0:
227 // language IDs
228 data[0] = 4;
229 data[1] = 3;
230 data[2] = 0x09;
231 data[3] = 0x04;
232 ret = 4;
233 break;
234 case 1:
235 // vendor description
236 ret = set_usb_string(data, "BOCHS");
237 break;
238 case 2:
239 // product description
240 if (strlen(d.devname) > 0) {
241 ret = set_usb_string(data, d.devname);
242 } else {
243 goto fail;
245 break;
246 case 3:
247 // serial number
248 ret = set_usb_string(data, "1");
249 break;
250 default:
251 BX_ERROR(("USB MSD handle_control: unknown descriptor 0x%02x", value & 0xff));
252 goto fail;
254 break;
255 case USB_DT_DEVICE_QUALIFIER:
256 // device qualifier
257 data[0] = 10;
258 data[1] = USB_DT_DEVICE_QUALIFIER;
259 memcpy(data+2, bx_msd_dev_descriptor+2, 6);
260 data[8] = 1;
261 data[9] = 0;
262 ret = 10;
263 break;
264 default:
265 goto fail;
267 break;
268 case DeviceRequest | USB_REQ_GET_CONFIGURATION:
269 data[0] = 1;
270 ret = 1;
271 break;
272 case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
273 ret = 0;
274 break;
275 case DeviceRequest | USB_REQ_GET_INTERFACE:
276 data[0] = 0;
277 ret = 1;
278 break;
279 case DeviceOutRequest | USB_REQ_SET_INTERFACE:
280 ret = 0;
281 break;
282 case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
283 if (value == 0 && index != 0x81) { /* clear ep halt */
284 goto fail;
286 ret = 0;
287 break;
288 // Class specific requests
289 case MassStorageReset:
290 // Ben: Hack for 0x21 | MassStorageReset
291 // 0x21 = (REQ_TYPE_CLASS | RECPT_INTERFACE);
292 case 0x21FF:
293 s.mode = USB_MSDM_CBW;
294 ret = 0;
295 break;
296 case GetMaxLun:
297 data[0] = 0;
298 ret = 1;
299 break;
300 default:
301 BX_ERROR(("USB MSD handle_control: unknown request 0x%04x", request));
302 fail:
303 ret = USB_RET_STALL;
304 break;
306 return ret;
309 int usb_msd_device_t::handle_data(USBPacket *p)
311 struct usb_msd_cbw cbw;
312 int ret = 0;
313 Bit8u devep = p->devep;
314 Bit8u *data = p->data;
315 int len = p->len;
317 switch (p->pid) {
318 case USB_TOKEN_OUT:
319 usb_dump_packet(data, len);
320 if (devep != 2)
321 goto fail;
323 switch (s.mode) {
324 case USB_MSDM_CBW:
325 if (len != 31) {
326 BX_ERROR(("bad CBW len"));
327 goto fail;
329 memcpy(&cbw, data, 31);
330 if (dtoh32(cbw.sig) != 0x43425355) {
331 BX_ERROR(("bad signature %08x", dtoh32(cbw.sig)));
332 goto fail;
334 BX_DEBUG(("command on LUN %d", cbw.lun));
335 s.tag = dtoh32(cbw.tag);
336 s.data_len = dtoh32(cbw.data_len);
337 if (s.data_len == 0) {
338 s.mode = USB_MSDM_CSW;
339 } else if (cbw.flags & 0x80) {
340 s.mode = USB_MSDM_DATAIN;
341 } else {
342 s.mode = USB_MSDM_DATAOUT;
344 BX_DEBUG(("command tag 0x%x flags %08x len %d data %d",
345 s.tag, cbw.flags, cbw.cmd_len, s.data_len));
346 s.residue = 0;
347 s.scsi_dev->scsi_send_command(s.tag, cbw.cmd, cbw.lun);
348 if (s.residue == 0) {
349 if (s.mode == USB_MSDM_DATAIN) {
350 s.scsi_dev->scsi_read_data(s.tag);
351 } else if (s.mode == USB_MSDM_DATAOUT) {
352 s.scsi_dev->scsi_write_data(s.tag);
355 ret = len;
356 break;
358 case USB_MSDM_DATAOUT:
359 BX_DEBUG(("data out %d/%d", len, s.data_len));
360 if (len > (int)s.data_len)
361 goto fail;
363 s.usb_buf = data;
364 s.usb_len = len;
365 if (s.scsi_len) {
366 copy_data();
368 if (s.residue && s.usb_len) {
369 s.data_len -= s.usb_len;
370 if (s.data_len == 0)
371 s.mode = USB_MSDM_CSW;
372 s.usb_len = 0;
374 if (s.usb_len) {
375 BX_INFO(("deferring packet %p", p));
376 // TODO: defer packet
377 s.packet = p;
378 ret = USB_RET_ASYNC;
379 } else {
380 ret = len;
382 break;
384 default:
385 BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_OUT"));
386 goto fail;
388 break;
390 case USB_TOKEN_IN:
391 if (devep != 1)
392 goto fail;
394 switch (s.mode) {
395 case USB_MSDM_DATAOUT:
396 if (s.data_len != 0 || len < 13)
397 goto fail;
398 // TODO: defer packet
399 s.packet = p;
400 ret = USB_RET_ASYNC;
401 break;
403 case USB_MSDM_CSW:
404 BX_DEBUG(("command status %d tag 0x%x, len %d",
405 s.result, s.tag, len));
406 if (len < 13)
407 return ret;
409 s.usb_len = len;
410 s.usb_buf = data;
411 send_status();
412 s.mode = USB_MSDM_CBW;
413 ret = 13;
414 break;
416 case USB_MSDM_DATAIN:
417 BX_DEBUG(("data in %d/%d", len, s.data_len));
418 if (len > (int)s.data_len)
419 len = s.data_len;
420 s.usb_buf = data;
421 s.usb_len = len;
422 if (s.scsi_len) {
423 copy_data();
425 if (s.residue && s.usb_len) {
426 s.data_len -= s.usb_len;
427 memset(s.usb_buf, 0, s.usb_len);
428 if (s.data_len == 0)
429 s.mode = USB_MSDM_CSW;
430 s.usb_len = 0;
432 if (s.usb_len) {
433 BX_INFO(("deferring packet %p", p));
434 // TODO: defer packet
435 s.packet = p;
436 ret = USB_RET_ASYNC;
437 } else {
438 ret = len;
440 break;
442 default:
443 BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN"));
444 goto fail;
446 if (ret > 0) usb_dump_packet(data, ret);
447 break;
449 default:
450 BX_ERROR(("USB MSD handle_data: bad token"));
451 fail:
452 ret = USB_RET_STALL;
453 break;
456 return ret;
459 void usb_msd_device_t::copy_data()
461 Bit32u len = s.usb_len;
462 if (len > s.scsi_len)
463 len = s.scsi_len;
464 if (s.mode == USB_MSDM_DATAIN) {
465 memcpy(s.usb_buf, s.scsi_buf, len);
466 } else {
467 memcpy(s.scsi_buf, s.usb_buf, len);
469 s.usb_len -= len;
470 s.scsi_len -= len;
471 s.usb_buf += len;
472 s.scsi_buf += len;
473 s.data_len -= len;
474 if (s.scsi_len == 0) {
475 if (s.mode == USB_MSDM_DATAIN) {
476 s.scsi_dev->scsi_read_data(s.tag);
477 } else if (s.mode == USB_MSDM_DATAOUT) {
478 s.scsi_dev->scsi_write_data(s.tag);
483 void usb_msd_device_t::send_status()
485 struct usb_msd_csw csw;
487 csw.sig = htod32(0x53425355);
488 csw.tag = htod32(s.tag);
489 csw.residue = s.residue;
490 csw.status = s.result;
491 memcpy(s.usb_buf, &csw, 13);
494 void usb_msd_device_t::usb_msd_command_complete(void *this_ptr, int reason, Bit32u tag, Bit32u arg)
496 usb_msd_device_t *class_ptr = (usb_msd_device_t *) this_ptr;
497 class_ptr->command_complete(reason, tag, arg);
500 void usb_msd_device_t::command_complete(int reason, Bit32u tag, Bit32u arg)
502 USBPacket *p = s.packet;
504 if (tag != s.tag) {
505 BX_ERROR(("usb-msd_command_complete: unexpected SCSI tag 0x%x", tag));
507 if (reason == SCSI_REASON_DONE) {
508 BX_DEBUG(("command complete %d", arg));
509 s.residue = s.data_len;
510 s.result = arg != 0;
511 if (s.packet) {
512 if (s.data_len == 0 && s.mode == USB_MSDM_DATAOUT) {
513 send_status();
514 s.mode = USB_MSDM_CBW;
515 } else {
516 if (s.data_len) {
517 s.data_len -= s.usb_len;
518 if (s.mode == USB_MSDM_DATAIN)
519 memset(s.usb_buf, 0, s.usb_len);
520 s.usb_len = 0;
522 if (s.data_len == 0)
523 s.mode = USB_MSDM_CSW;
525 s.packet = NULL;
526 } else if (s.data_len == 0) {
527 s.mode = USB_MSDM_CSW;
529 return;
531 s.scsi_len = arg;
532 s.scsi_buf = s.scsi_dev->scsi_get_buf(tag);
533 if (p) {
534 copy_data();
535 if (s.usb_len == 0) {
536 BX_INFO(("packet complete %p", p));
537 s.packet = NULL;
542 #endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB