1 /////////////////////////////////////////////////////////////////////////
2 // $Id: usb_msd.cc,v 1.8 2008/01/26 22:24:02 sshwarts Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2007 Volker Ruppert
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.
29 #define NO_DEVICE_INCLUDES
31 #if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
34 #include "scsi_device.h"
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;
102 0x00, /* u8 MaxPower; */
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
));
143 usb_msd_device_t::~usb_msd_device_t(void)
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
));
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
;
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()
180 s
.mode
= USB_MSDM_CBW
;
183 int usb_msd_device_t::handle_control(int request
, int value
, int index
, int length
, Bit8u
*data
)
188 case DeviceRequest
| USB_REQ_GET_STATUS
:
189 data
[0] = (1 << USB_DEVICE_SELF_POWERED
) |
190 (d
.remote_wakeup
<< USB_DEVICE_REMOTE_WAKEUP
);
194 case DeviceOutRequest
| USB_REQ_CLEAR_FEATURE
:
195 if (value
== USB_DEVICE_REMOTE_WAKEUP
) {
202 case DeviceOutRequest
| USB_REQ_SET_FEATURE
:
203 if (value
== USB_DEVICE_REMOTE_WAKEUP
) {
210 case DeviceOutRequest
| USB_REQ_SET_ADDRESS
:
214 case DeviceRequest
| USB_REQ_GET_DESCRIPTOR
:
217 memcpy(data
, bx_msd_dev_descriptor
, sizeof(bx_msd_dev_descriptor
));
218 ret
= sizeof(bx_msd_dev_descriptor
);
221 memcpy(data
, bx_msd_config_descriptor
, sizeof(bx_msd_config_descriptor
));
222 ret
= sizeof(bx_msd_config_descriptor
);
225 switch(value
& 0xff) {
235 // vendor description
236 ret
= set_usb_string(data
, "BOCHS");
239 // product description
240 if (strlen(d
.devname
) > 0) {
241 ret
= set_usb_string(data
, d
.devname
);
248 ret
= set_usb_string(data
, "1");
251 BX_ERROR(("USB MSD handle_control: unknown descriptor 0x%02x", value
& 0xff));
255 case USB_DT_DEVICE_QUALIFIER
:
258 data
[1] = USB_DT_DEVICE_QUALIFIER
;
259 memcpy(data
+2, bx_msd_dev_descriptor
+2, 6);
268 case DeviceRequest
| USB_REQ_GET_CONFIGURATION
:
272 case DeviceOutRequest
| USB_REQ_SET_CONFIGURATION
:
275 case DeviceRequest
| USB_REQ_GET_INTERFACE
:
279 case DeviceOutRequest
| USB_REQ_SET_INTERFACE
:
282 case EndpointOutRequest
| USB_REQ_CLEAR_FEATURE
:
283 if (value
== 0 && index
!= 0x81) { /* clear ep halt */
288 // Class specific requests
289 case MassStorageReset
:
290 // Ben: Hack for 0x21 | MassStorageReset
291 // 0x21 = (REQ_TYPE_CLASS | RECPT_INTERFACE);
293 s
.mode
= USB_MSDM_CBW
;
301 BX_ERROR(("USB MSD handle_control: unknown request 0x%04x", request
));
309 int usb_msd_device_t::handle_data(USBPacket
*p
)
311 struct usb_msd_cbw cbw
;
313 Bit8u devep
= p
->devep
;
314 Bit8u
*data
= p
->data
;
319 usb_dump_packet(data
, len
);
326 BX_ERROR(("bad CBW len"));
329 memcpy(&cbw
, data
, 31);
330 if (dtoh32(cbw
.sig
) != 0x43425355) {
331 BX_ERROR(("bad signature %08x", dtoh32(cbw
.sig
)));
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
;
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
));
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
);
358 case USB_MSDM_DATAOUT
:
359 BX_DEBUG(("data out %d/%d", len
, s
.data_len
));
360 if (len
> (int)s
.data_len
)
368 if (s
.residue
&& s
.usb_len
) {
369 s
.data_len
-= s
.usb_len
;
371 s
.mode
= USB_MSDM_CSW
;
375 BX_INFO(("deferring packet %p", p
));
376 // TODO: defer packet
385 BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_OUT"));
395 case USB_MSDM_DATAOUT
:
396 if (s
.data_len
!= 0 || len
< 13)
398 // TODO: defer packet
404 BX_DEBUG(("command status %d tag 0x%x, len %d",
405 s
.result
, s
.tag
, len
));
412 s
.mode
= USB_MSDM_CBW
;
416 case USB_MSDM_DATAIN
:
417 BX_DEBUG(("data in %d/%d", len
, s
.data_len
));
418 if (len
> (int)s
.data_len
)
425 if (s
.residue
&& s
.usb_len
) {
426 s
.data_len
-= s
.usb_len
;
427 memset(s
.usb_buf
, 0, s
.usb_len
);
429 s
.mode
= USB_MSDM_CSW
;
433 BX_INFO(("deferring packet %p", p
));
434 // TODO: defer packet
443 BX_ERROR(("USB MSD handle_data: unexpected mode at USB_TOKEN_IN"));
446 if (ret
> 0) usb_dump_packet(data
, ret
);
450 BX_ERROR(("USB MSD handle_data: bad token"));
459 void usb_msd_device_t::copy_data()
461 Bit32u len
= s
.usb_len
;
462 if (len
> s
.scsi_len
)
464 if (s
.mode
== USB_MSDM_DATAIN
) {
465 memcpy(s
.usb_buf
, s
.scsi_buf
, len
);
467 memcpy(s
.scsi_buf
, s
.usb_buf
, 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
;
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
;
512 if (s
.data_len
== 0 && s
.mode
== USB_MSDM_DATAOUT
) {
514 s
.mode
= USB_MSDM_CBW
;
517 s
.data_len
-= s
.usb_len
;
518 if (s
.mode
== USB_MSDM_DATAIN
)
519 memset(s
.usb_buf
, 0, s
.usb_len
);
523 s
.mode
= USB_MSDM_CSW
;
526 } else if (s
.data_len
== 0) {
527 s
.mode
= USB_MSDM_CSW
;
532 s
.scsi_buf
= s
.scsi_dev
->scsi_get_buf(tag
);
535 if (s
.usb_len
== 0) {
536 BX_INFO(("packet complete %p", p
));
542 #endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB