1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pciusb.cc,v 1.66 2008/12/14 08:56:05 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
5 // Copyright (C) 2004 MandrakeSoft S.A.
9 // 75002 Paris - France
10 // http://www.linux-mandrake.com/
11 // http://www.mandrakesoft.com/
13 // This library is free software; you can redistribute it and/or
14 // modify it under the terms of the GNU Lesser General Public
15 // License as published by the Free Software Foundation; either
16 // version 2 of the License, or (at your option) any later version.
18 // This library is distributed in the hope that it will be useful,
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 // Lesser General Public License for more details.
23 // You should have received a copy of the GNU Lesser General Public
24 // License along with this library; if not, write to the Free Software
25 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 // Experimental PCI USB adapter
28 // Benjamin D Lunt (fys at frontiernet net) coded most of this usb emulation.
31 - I have coded this to be able to use more than one HUB and each
32 device to have more than one function. *However*, at the moment,
33 not all of the code will work with multiple hubs and/or functions.
34 I thought of removing this functionallity to make it easier, but
35 thought that later when multiple hubs and/or functions were desired,
36 it would already be half way there.
37 - Currently, this code is quite messy. This is for all of the debugging
38 I have been doing. Many BX_INFO()'s here and there.
39 - My purpose of coding this emulation was/is to learn about the USB.
40 It has been a challenge, but I have learned a lot.
41 - If I forget, there are a lot of BX_INFO's that can be changed to BX_DEBUG's.
43 I now have a Beagle USB Protocol Analyzer from Total Phase for my research.
44 (http://www.totalphase.com/products/beagle/usb/)
45 With this device, I plan on doing a lot of research and development to get this
46 code to a state where it is actually very useful. I plan on adding support
47 of many "plug-in" type modules so that you can simply add a plug-in for your
48 specific device without having to modify the root code.
49 I hope to have some working code to upload to the CVS as soon as possible.
50 Thanks to Total Phase for their help in my research and the development of
54 // Define BX_PLUGGABLE in files that can be compiled into plugins. For
55 // platforms that require a special tag on exported symbols, BX_PLUGGABLE
56 // is used to know when we are exporting symbols and when we are importing.
60 #if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
64 #define LOG_THIS theUSBDevice->
66 bx_pciusb_c
* theUSBDevice
= NULL
;
68 const Bit8u usb_iomask
[32] = {2, 1, 2, 1, 2, 1, 2, 0, 4, 0, 0, 0, 1, 0, 0, 0,
69 3, 1, 3, 1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
71 // Dumps the contents of a buffer to the log file
72 void usb_dump_packet(Bit8u
*data
, unsigned size
)
74 char the_packet
[256], str
[16];
75 strcpy(the_packet
, "Packet contents (in hex):");
77 for (unsigned p
=0; p
<size
; p
++) {
79 BX_DEBUG(("%s", the_packet
));
80 sprintf(the_packet
, " 0x%04X ", offset
);
83 sprintf(str
, " %02X", data
[p
]);
84 strcat(the_packet
, str
);
86 if (strlen(the_packet
))
87 BX_DEBUG(("%s", the_packet
));
90 int set_usb_string(Bit8u
*buf
, const char *str
)
103 for(i
= 0; i
< len
; i
++) {
110 int libpciusb_LTX_plugin_init(plugin_t
*plugin
, plugintype_t type
, int argc
, char *argv
[])
112 theUSBDevice
= new bx_pciusb_c();
113 bx_devices
.pluginPciUSBAdapter
= theUSBDevice
;
114 BX_REGISTER_DEVICE_DEVMODEL(plugin
, type
, theUSBDevice
, BX_PLUGIN_PCIUSB
);
118 void libpciusb_LTX_plugin_fini(void)
123 bx_pciusb_c::bx_pciusb_c()
127 for (int i
=0; i
<BX_USB_CONFDEV
; i
++) {
128 memset((void*)&hub
[i
], 0, sizeof(bx_usb_t
));
130 device_buffer
= NULL
;
133 bx_pciusb_c::~bx_pciusb_c()
135 if (BX_USB_THIS device_buffer
!= NULL
)
136 delete [] BX_USB_THIS device_buffer
;
138 for (int i
=0; i
<BX_USB_CONFDEV
; i
++) {
139 for (int j
=0; j
<USB_NUM_PORTS
; j
++) {
140 if (BX_USB_THIS hub
[i
].usb_port
[j
].device
!= NULL
) {
141 delete BX_USB_THIS hub
[i
].usb_port
[j
].device
;
146 SIM
->get_param_string(BXPN_USB1_PORT1
)->set_handler(NULL
);
147 SIM
->get_param_string(BXPN_USB1_PORT2
)->set_handler(NULL
);
152 void bx_pciusb_c::init(void)
154 // called once when bochs initializes
156 if (!SIM
->get_param_bool(BXPN_USB1_ENABLED
)->get()) return;
158 BX_USB_THIS device_buffer
= new Bit8u
[65536];
160 // Call our timer routine every 1mS (1,000uS)
161 // Continuous and active
162 BX_USB_THIS hub
[0].timer_index
=
163 bx_pc_system
.register_timer(this, usb_timer_handler
, 1000, 1,1, "usb.timer");
165 BX_USB_THIS hub
[0].devfunc
= BX_PCI_DEVICE(1,2);
166 DEV_register_pci_handlers(this, &BX_USB_THIS hub
[0].devfunc
, BX_PLUGIN_PCIUSB
,
167 "Experimental PCI USB");
169 for (unsigned i
=0; i
<256; i
++) {
170 BX_USB_THIS hub
[0].pci_conf
[i
] = 0x0;
173 BX_USB_THIS hub
[0].base_ioaddr
= 0x0;
175 BX_INFO(("usb1 initialized - I/O base and IRQ assigned by PCI BIOS"));
177 //FIXME: for now, we want a status bar // hub zero, port zero
178 BX_USB_THIS hub
[0].statusbar_id
[0] = bx_gui
->register_statusitem("USB");
180 SIM
->get_param_string(BXPN_USB1_PORT1
)->set_handler(usb_param_handler
);
181 SIM
->get_param_string(BXPN_USB1_PORT1
)->set_runtime_param(1);
182 SIM
->get_param_string(BXPN_USB1_PORT2
)->set_handler(usb_param_handler
);
183 SIM
->get_param_string(BXPN_USB1_PORT2
)->set_runtime_param(1);
185 //HACK: Turn on debug messages from the start
186 //BX_USB_THIS setonoff(LOGLEV_DEBUG, ACT_REPORT);
189 void bx_pciusb_c::reset(unsigned type
)
193 if (!SIM
->get_param_bool(BXPN_USB1_ENABLED
)->get()) return;
195 if (type
== BX_RESET_HARDWARE
) {
196 static const struct reset_vals_t
{
200 { 0x00, 0x86 }, { 0x01, 0x80 }, // 0x8086 = vendor
201 { 0x02, 0x20 }, { 0x03, 0x70 }, // 0x7020 = device
202 { 0x04, 0x05 }, { 0x05, 0x00 }, // command_io
203 { 0x06, 0x80 }, { 0x07, 0x02 }, // status
204 { 0x08, 0x01 }, // revision number
205 { 0x09, 0x00 }, // interface
206 { 0x0a, 0x03 }, // class_sub USB Host Controller
207 { 0x0b, 0x0c }, // class_base Serial Bus Controller
208 { 0x0D, 0x20 }, // bus latency
209 { 0x0e, 0x00 }, // header_type_generic
210 // address space 0x20 - 0x23
211 { 0x20, 0x01 }, { 0x21, 0x00 },
212 { 0x22, 0x00 }, { 0x23, 0x00 },
213 { 0x3c, 0x00 }, // IRQ
214 { 0x3d, BX_PCI_INTD
}, // INT
215 { 0x60, 0x10 }, // USB revision 1.0
216 { 0x6a, 0x01 }, // USB clock
217 { 0xc1, 0x20 } // PIRQ enable
220 for (i
= 0; i
< sizeof(reset_vals
) / sizeof(*reset_vals
); ++i
) {
221 BX_USB_THIS hub
[0].pci_conf
[reset_vals
[i
].addr
] = reset_vals
[i
].val
;
226 BX_USB_THIS busy
= 0;
227 BX_USB_THIS global_reset
= 0;
229 // Put the USB registers into their RESET state
230 for (i
=0; i
<BX_USB_CONFDEV
; i
++) {
231 BX_USB_THIS hub
[i
].usb_command
.max_packet_size
= 0;
232 BX_USB_THIS hub
[i
].usb_command
.configured
= 0;
233 BX_USB_THIS hub
[i
].usb_command
.debug
= 0;
234 BX_USB_THIS hub
[i
].usb_command
.resume
= 0;
235 BX_USB_THIS hub
[i
].usb_command
.suspend
= 0;
236 BX_USB_THIS hub
[i
].usb_command
.reset
= 0;
237 BX_USB_THIS hub
[i
].usb_command
.host_reset
= 0;
238 BX_USB_THIS hub
[i
].usb_command
.schedule
= 0;
239 BX_USB_THIS hub
[i
].usb_status
.error_interrupt
= 0;
240 BX_USB_THIS hub
[i
].usb_status
.host_error
= 0;
241 BX_USB_THIS hub
[i
].usb_status
.host_halted
= 0;
242 BX_USB_THIS hub
[i
].usb_status
.interrupt
= 0;
243 BX_USB_THIS hub
[i
].usb_status
.pci_error
= 0;
244 BX_USB_THIS hub
[i
].usb_status
.resume
= 0;
245 BX_USB_THIS hub
[i
].usb_enable
.short_packet
= 0;
246 BX_USB_THIS hub
[i
].usb_enable
.on_complete
= 0;
247 BX_USB_THIS hub
[i
].usb_enable
.resume
= 0;
248 BX_USB_THIS hub
[i
].usb_enable
.timeout_crc
= 0;
249 BX_USB_THIS hub
[i
].usb_frame_num
.frame_num
= 0x0000;
250 BX_USB_THIS hub
[i
].usb_frame_base
.frame_base
= 0x00000000;
251 BX_USB_THIS hub
[i
].usb_sof
.sof_timing
= 0x40;
252 for (j
=0; j
<USB_NUM_PORTS
; j
++) {
253 BX_USB_THIS hub
[i
].usb_port
[j
].connect_changed
= 0;
254 BX_USB_THIS hub
[i
].usb_port
[j
].line_dminus
= 0;
255 BX_USB_THIS hub
[i
].usb_port
[j
].line_dplus
= 0;
256 BX_USB_THIS hub
[i
].usb_port
[j
].low_speed
= 0;
257 BX_USB_THIS hub
[i
].usb_port
[j
].reset
= 0;
258 BX_USB_THIS hub
[i
].usb_port
[j
].resume
= 0;
259 BX_USB_THIS hub
[i
].usb_port
[j
].suspend
= 0;
260 BX_USB_THIS hub
[i
].usb_port
[j
].enabled
= 0;
261 BX_USB_THIS hub
[i
].usb_port
[j
].able_changed
= 0;
262 BX_USB_THIS hub
[i
].usb_port
[j
].status
= 0;
263 if (BX_USB_THIS hub
[i
].usb_port
[j
].device
!= NULL
) {
264 delete BX_USB_THIS hub
[i
].usb_port
[j
].device
;
265 BX_USB_THIS hub
[i
].usb_port
[j
].device
= NULL
;
270 BX_USB_THIS mousedev
= NULL
;
271 BX_USB_THIS keybdev
= NULL
;
273 init_device(0, SIM
->get_param_string(BXPN_USB1_PORT1
)->getptr());
274 init_device(1, SIM
->get_param_string(BXPN_USB1_PORT2
)->getptr());
277 void bx_pciusb_c::register_state(void)
280 char hubnum
[8], portnum
[8];
281 bx_list_c
*hub
, *usb_cmd
, *usb_st
, *usb_en
, *port
;
283 bx_list_c
*list
= new bx_list_c(SIM
->get_bochs_root(), "pciusb", "PCI USB Controller State", BX_USB_CONFDEV
+ 2);
284 for (i
=0; i
<BX_USB_CONFDEV
; i
++) {
285 sprintf(hubnum
, "hub%d", i
+1);
286 hub
= new bx_list_c(list
, hubnum
, USB_NUM_PORTS
+ 7);
287 usb_cmd
= new bx_list_c(hub
, "usb_command", 8);
288 new bx_shadow_bool_c(usb_cmd
, "max_packet_size", &BX_USB_THIS hub
[i
].usb_command
.max_packet_size
);
289 new bx_shadow_bool_c(usb_cmd
, "configured", &BX_USB_THIS hub
[i
].usb_command
.configured
);
290 new bx_shadow_bool_c(usb_cmd
, "debug", &BX_USB_THIS hub
[i
].usb_command
.debug
);
291 new bx_shadow_bool_c(usb_cmd
, "resume", &BX_USB_THIS hub
[i
].usb_command
.resume
);
292 new bx_shadow_bool_c(usb_cmd
, "suspend", &BX_USB_THIS hub
[i
].usb_command
.suspend
);
293 new bx_shadow_bool_c(usb_cmd
, "reset", &BX_USB_THIS hub
[i
].usb_command
.reset
);
294 new bx_shadow_bool_c(usb_cmd
, "host_reset", &BX_USB_THIS hub
[i
].usb_command
.host_reset
);
295 new bx_shadow_bool_c(usb_cmd
, "schedule", &BX_USB_THIS hub
[i
].usb_command
.schedule
);
296 usb_st
= new bx_list_c(hub
, "usb_status", 6);
297 new bx_shadow_bool_c(usb_st
, "host_halted", &BX_USB_THIS hub
[i
].usb_status
.host_halted
);
298 new bx_shadow_bool_c(usb_st
, "host_error", &BX_USB_THIS hub
[i
].usb_status
.host_error
);
299 new bx_shadow_bool_c(usb_st
, "pci_error", &BX_USB_THIS hub
[i
].usb_status
.pci_error
);
300 new bx_shadow_bool_c(usb_st
, "resume", &BX_USB_THIS hub
[i
].usb_status
.resume
);
301 new bx_shadow_bool_c(usb_st
, "error_interrupt", &BX_USB_THIS hub
[i
].usb_status
.error_interrupt
);
302 new bx_shadow_bool_c(usb_st
, "interrupt", &BX_USB_THIS hub
[i
].usb_status
.interrupt
);
303 usb_en
= new bx_list_c(hub
, "usb_enable", 4);
304 new bx_shadow_bool_c(usb_en
, "short_packet", &BX_USB_THIS hub
[i
].usb_enable
.short_packet
);
305 new bx_shadow_bool_c(usb_en
, "on_complete", &BX_USB_THIS hub
[i
].usb_enable
.on_complete
);
306 new bx_shadow_bool_c(usb_en
, "resume", &BX_USB_THIS hub
[i
].usb_enable
.resume
);
307 new bx_shadow_bool_c(usb_en
, "timeout_crc", &BX_USB_THIS hub
[i
].usb_enable
.timeout_crc
);
308 new bx_shadow_num_c(hub
, "frame_num", &BX_USB_THIS hub
[i
].usb_frame_num
.frame_num
, BASE_HEX
);
309 new bx_shadow_num_c(hub
, "frame_base", &BX_USB_THIS hub
[i
].usb_frame_base
.frame_base
, BASE_HEX
);
310 new bx_shadow_num_c(hub
, "sof_timing", &BX_USB_THIS hub
[i
].usb_sof
.sof_timing
, BASE_HEX
);
311 for (j
=0; j
<USB_NUM_PORTS
; j
++) {
312 sprintf(portnum
, "port%d", j
+1);
313 port
= new bx_list_c(hub
, portnum
, 11);
314 new bx_shadow_bool_c(port
, "suspend", &BX_USB_THIS hub
[i
].usb_port
[j
].suspend
);
315 new bx_shadow_bool_c(port
, "reset", &BX_USB_THIS hub
[i
].usb_port
[j
].reset
);
316 new bx_shadow_bool_c(port
, "low_speed", &BX_USB_THIS hub
[i
].usb_port
[j
].low_speed
);
317 new bx_shadow_bool_c(port
, "resume", &BX_USB_THIS hub
[i
].usb_port
[j
].resume
);
318 new bx_shadow_bool_c(port
, "line_dminus", &BX_USB_THIS hub
[i
].usb_port
[j
].line_dminus
);
319 new bx_shadow_bool_c(port
, "line_dplus", &BX_USB_THIS hub
[i
].usb_port
[j
].line_dplus
);
320 new bx_shadow_bool_c(port
, "able_changed", &BX_USB_THIS hub
[i
].usb_port
[j
].able_changed
);
321 new bx_shadow_bool_c(port
, "enabled", &BX_USB_THIS hub
[i
].usb_port
[j
].enabled
);
322 new bx_shadow_bool_c(port
, "connect_changed", &BX_USB_THIS hub
[i
].usb_port
[j
].connect_changed
);
323 new bx_shadow_bool_c(port
, "status", &BX_USB_THIS hub
[i
].usb_port
[j
].status
);
324 // empty list for USB device state
325 new bx_list_c(port
, "device", 20);
327 register_pci_state(hub
, BX_USB_THIS hub
[i
].pci_conf
);
329 new bx_shadow_bool_c(list
, "busy", &BX_USB_THIS busy
);
330 new bx_shadow_num_c(list
, "global_reset", &BX_USB_THIS global_reset
);
333 void bx_pciusb_c::after_restore_state(void)
335 if (DEV_pci_set_base_io(BX_USB_THIS_PTR
, read_handler
, write_handler
,
336 &BX_USB_THIS hub
[0].base_ioaddr
,
337 &BX_USB_THIS hub
[0].pci_conf
[0x20],
338 32, &usb_iomask
[0], "USB Hub #1"))
340 BX_INFO(("new base address: 0x%04x", BX_USB_THIS hub
[0].base_ioaddr
));
342 for (int i
=0; i
<BX_USB_CONFDEV
; i
++) {
343 for (int j
=0; j
<USB_NUM_PORTS
; j
++) {
344 if (BX_USB_THIS hub
[i
].usb_port
[j
].device
!= NULL
) {
345 BX_USB_THIS hub
[i
].usb_port
[j
].device
->after_restore_state();
351 void bx_pciusb_c::init_device(Bit8u port
, const char *devname
)
353 usbdev_type type
= USB_DEV_TYPE_NONE
;
354 char pname
[BX_PATHNAME_LEN
];
356 if (!strlen(devname
) || !strcmp(devname
, "none")) return;
358 if (!strcmp(devname
, "mouse")) {
359 type
= USB_DEV_TYPE_MOUSE
;
360 BX_USB_THIS hub
[0].usb_port
[port
].device
= new usb_hid_device_t(type
);
361 if (BX_USB_THIS mousedev
== NULL
) {
362 BX_USB_THIS mousedev
= (usb_hid_device_t
*)BX_USB_THIS hub
[0].usb_port
[port
].device
;
364 } else if (!strcmp(devname
, "tablet")) {
365 type
= USB_DEV_TYPE_TABLET
;
366 BX_USB_THIS hub
[0].usb_port
[port
].device
= new usb_hid_device_t(type
);
367 if (BX_USB_THIS mousedev
== NULL
) {
368 BX_USB_THIS mousedev
= (usb_hid_device_t
*)BX_USB_THIS hub
[0].usb_port
[port
].device
;
370 } else if (!strcmp(devname
, "keypad")) {
371 type
= USB_DEV_TYPE_KEYPAD
;
372 BX_USB_THIS hub
[0].usb_port
[port
].device
= new usb_hid_device_t(type
);
373 if (BX_USB_THIS keybdev
== NULL
) {
374 BX_USB_THIS keybdev
= (usb_hid_device_t
*)BX_USB_THIS hub
[0].usb_port
[port
].device
;
376 } else if (!strncmp(devname
, "disk", 4)) {
377 if ((strlen(devname
) > 5) && (devname
[4] == ':')) {
378 type
= USB_DEV_TYPE_DISK
;
379 BX_USB_THIS hub
[0].usb_port
[port
].device
= new usb_msd_device_t();
381 BX_PANIC(("USB device 'disk' needs a filename separated with a colon"));
385 BX_PANIC(("unknown USB device: %s", devname
));
388 sprintf(pname
, "pciusb.hub1.port%d.device", port
+1);
389 bx_list_c
*devlist
= (bx_list_c
*)SIM
->get_param(pname
, SIM
->get_bochs_root());
390 BX_USB_THIS hub
[0].usb_port
[port
].device
->register_state(devlist
);
391 usb_set_connect_status(port
, type
, 1);
394 void bx_pciusb_c::set_irq_level(bx_bool level
)
396 DEV_pci_set_irq(BX_USB_THIS hub
[0].devfunc
, BX_USB_THIS hub
[0].pci_conf
[0x3d], level
);
399 // static IO port read callback handler
400 // redirects to non-static class handler to avoid virtual functions
402 Bit32u
bx_pciusb_c::read_handler(void *this_ptr
, Bit32u address
, unsigned io_len
)
404 #if !BX_USE_PCIUSB_SMF
405 bx_pciusb_c
*class_ptr
= (bx_pciusb_c
*) this_ptr
;
406 return class_ptr
->read(address
, io_len
);
409 Bit32u
bx_pciusb_c::read(Bit32u address
, unsigned io_len
)
413 #endif // !BX_USE_PCIUSB_SMF
417 offset
= address
- BX_USB_THIS hub
[0].base_ioaddr
;
420 case 0x00: // command register (16-bit)
421 val
= BX_USB_THIS hub
[0].usb_command
.max_packet_size
<< 7
422 | BX_USB_THIS hub
[0].usb_command
.configured
<< 6
423 | BX_USB_THIS hub
[0].usb_command
.debug
<< 5
424 | BX_USB_THIS hub
[0].usb_command
.resume
<< 4
425 | BX_USB_THIS hub
[0].usb_command
.suspend
<< 3
426 | BX_USB_THIS hub
[0].usb_command
.reset
<< 2
427 | BX_USB_THIS hub
[0].usb_command
.host_reset
<< 1
428 | BX_USB_THIS hub
[0].usb_command
.schedule
;
431 case 0x02: // status register (16-bit)
432 val
= BX_USB_THIS hub
[0].usb_status
.host_halted
<< 5
433 | BX_USB_THIS hub
[0].usb_status
.host_error
<< 4
434 | BX_USB_THIS hub
[0].usb_status
.pci_error
<< 3
435 | BX_USB_THIS hub
[0].usb_status
.resume
<< 2
436 | BX_USB_THIS hub
[0].usb_status
.error_interrupt
<< 1
437 | BX_USB_THIS hub
[0].usb_status
.interrupt
;
440 case 0x04: // interrupt enable register (16-bit)
441 val
= BX_USB_THIS hub
[0].usb_enable
.short_packet
<< 3
442 | BX_USB_THIS hub
[0].usb_enable
.on_complete
<< 2
443 | BX_USB_THIS hub
[0].usb_enable
.resume
<< 1
444 | BX_USB_THIS hub
[0].usb_enable
.timeout_crc
;
447 case 0x06: // frame number register (16-bit)
448 val
= BX_USB_THIS hub
[0].usb_frame_num
.frame_num
;
451 case 0x08: // frame base register (32-bit)
452 val
= BX_USB_THIS hub
[0].usb_frame_base
.frame_base
;
455 case 0x0C: // start of Frame Modify register (8-bit)
456 val
= BX_USB_THIS hub
[0].usb_sof
.sof_timing
;
459 case 0x14: // port #3 non existant, but linux systems check it to see if there are more than 2
460 BX_ERROR(("read from non existant offset 0x14 (port #3)"));
464 case 0x10: // port #1
466 case 0x12: // port #2
468 port
= (offset
& 0x0F) >> 1;
469 if (port
< USB_NUM_PORTS
) {
470 val
= BX_USB_THIS hub
[0].usb_port
[port
].suspend
<< 12
471 | 1 << 10 // some Root Hubs have bit 10 set ?????
472 | BX_USB_THIS hub
[0].usb_port
[port
].reset
<< 9
473 | BX_USB_THIS hub
[0].usb_port
[port
].low_speed
<< 8
475 | BX_USB_THIS hub
[0].usb_port
[port
].resume
<< 6
476 | BX_USB_THIS hub
[0].usb_port
[port
].line_dminus
<< 5
477 | BX_USB_THIS hub
[0].usb_port
[port
].line_dplus
<< 4
478 | BX_USB_THIS hub
[0].usb_port
[port
].able_changed
<< 3
479 | BX_USB_THIS hub
[0].usb_port
[port
].enabled
<< 2
480 | BX_USB_THIS hub
[0].usb_port
[port
].connect_changed
<< 1
481 | BX_USB_THIS hub
[0].usb_port
[port
].status
;
482 if (offset
& 1) val
>>= 8;
484 } // else fall through to default
486 val
= 0xFF7F; // keep compiler happy
487 BX_ERROR(("unsupported io read from address=0x%04x!", (unsigned) address
));
491 BX_DEBUG(("register read from address 0x%04X: 0x%08X (%2i bits)", (unsigned) address
, (Bit32u
) val
, io_len
* 8));
496 // static IO port write callback handler
497 // redirects to non-static class handler to avoid virtual functions
499 void bx_pciusb_c::write_handler(void *this_ptr
, Bit32u address
, Bit32u value
, unsigned io_len
)
501 #if !BX_USE_PCIUSB_SMF
502 bx_pciusb_c
*class_ptr
= (bx_pciusb_c
*) this_ptr
;
503 class_ptr
->write(address
, value
, io_len
);
506 void bx_pciusb_c::write(Bit32u address
, Bit32u value
, unsigned io_len
)
510 #endif // !BX_USE_PCIUSB_SMF
513 BX_DEBUG(("register write to address 0x%04X: 0x%08X (%2i bits)", (unsigned) address
, (unsigned) value
, io_len
* 8));
515 offset
= address
- BX_USB_THIS hub
[0].base_ioaddr
;
518 case 0x00: // command register (16-bit) (R/W)
520 BX_DEBUG(("write to command register with bits 15:8 not zero: 0x%04x", value
));
522 BX_USB_THIS hub
[0].usb_command
.max_packet_size
= (value
& 0x80) ? 1: 0;
523 BX_USB_THIS hub
[0].usb_command
.configured
= (value
& 0x40) ? 1: 0;
524 BX_USB_THIS hub
[0].usb_command
.debug
= (value
& 0x20) ? 1: 0;
525 BX_USB_THIS hub
[0].usb_command
.resume
= (value
& 0x10) ? 1: 0;
526 BX_USB_THIS hub
[0].usb_command
.suspend
= (value
& 0x08) ? 1: 0;
527 BX_USB_THIS hub
[0].usb_command
.reset
= (value
& 0x04) ? 1: 0;
528 BX_USB_THIS hub
[0].usb_command
.host_reset
= (value
& 0x02) ? 1: 0;
529 BX_USB_THIS hub
[0].usb_command
.schedule
= (value
& 0x01) ? 1: 0;
532 if (BX_USB_THIS hub
[0].usb_command
.host_reset
) {
533 BX_USB_THIS
reset(0);
534 for (unsigned i
=0; i
<USB_NUM_PORTS
; i
++) {
535 if (BX_USB_THIS hub
[0].usb_port
[i
].status
) {
536 if (BX_USB_THIS hub
[0].usb_port
[i
].device
!= NULL
) {
537 BX_USB_THIS
usb_send_msg(BX_USB_THIS hub
[0].usb_port
[i
].device
, USB_MSG_RESET
);
540 BX_USB_THIS hub
[0].usb_port
[i
].connect_changed
= 1;
541 BX_USB_THIS hub
[0].usb_port
[i
].enabled
= 0;
542 BX_USB_THIS hub
[0].usb_port
[i
].able_changed
= 1;
546 // If software set the GRESET bit, we need to send the reset to all USB.
547 // The software should guarentee that the reset is for at least 10ms.
548 // We hold the reset until software resets this bit
549 if (BX_USB_THIS hub
[0].usb_command
.reset
) {
550 BX_USB_THIS global_reset
= 1;
551 BX_DEBUG(("Global Reset"));
553 // if software cleared the reset, then we need to reset the usb registers.
554 if (BX_USB_THIS global_reset
) {
555 BX_USB_THIS global_reset
= 0;
556 unsigned int running
= BX_USB_THIS hub
[0].usb_command
.schedule
;
557 BX_USB_THIS
reset(0);
558 BX_USB_THIS hub
[0].usb_status
.host_halted
= (running
) ? 1 : 0;
562 // If Run/Stop, identify in log
563 if (BX_USB_THIS hub
[0].usb_command
.schedule
) {
564 BX_USB_THIS hub
[0].usb_status
.host_halted
= 0;
565 BX_DEBUG(("Schedule bit set in Command register"));
567 BX_USB_THIS hub
[0].usb_status
.host_halted
= 1;
568 BX_DEBUG(("Schedule bit clear in Command register"));
571 // If Debug mode set, panic. Not implemented
572 if (BX_USB_THIS hub
[0].usb_command
.debug
)
573 BX_PANIC(("Software set DEBUG bit in Command register. Not implemented"));
577 case 0x02: // status register (16-bit) (R/WC)
579 BX_DEBUG(("write to status register with bits 15:6 not zero: 0x%04x", value
));
581 // host_halted, even though not specified in the specs, is read only
582 //BX_USB_THIS hub[0].usb_status.host_halted = (value & 0x20) ? 0: BX_USB_THIS hub[0].usb_status.host_halted;
583 BX_USB_THIS hub
[0].usb_status
.host_error
= (value
& 0x10) ? 0: BX_USB_THIS hub
[0].usb_status
.host_error
;
584 BX_USB_THIS hub
[0].usb_status
.pci_error
= (value
& 0x08) ? 0: BX_USB_THIS hub
[0].usb_status
.pci_error
;
585 BX_USB_THIS hub
[0].usb_status
.resume
= (value
& 0x04) ? 0: BX_USB_THIS hub
[0].usb_status
.resume
;
586 BX_USB_THIS hub
[0].usb_status
.error_interrupt
= (value
& 0x02) ? 0: BX_USB_THIS hub
[0].usb_status
.error_interrupt
;
587 BX_USB_THIS hub
[0].usb_status
.interrupt
= (value
& 0x01) ? 0: BX_USB_THIS hub
[0].usb_status
.interrupt
;
590 case 0x04: // interrupt enable register (16-bit)
592 BX_DEBUG(("write to interrupt enable register with bits 15:4 not zero: 0x%04x", value
));
594 BX_USB_THIS hub
[0].usb_enable
.short_packet
= (value
& 0x08) ? 1: 0;
595 BX_USB_THIS hub
[0].usb_enable
.on_complete
= (value
& 0x04) ? 1: 0;
596 BX_USB_THIS hub
[0].usb_enable
.resume
= (value
& 0x02) ? 1: 0;
597 BX_USB_THIS hub
[0].usb_enable
.timeout_crc
= (value
& 0x01) ? 1: 0;
600 BX_DEBUG(("Host set Enable Interrupt on Short Packet"));
603 BX_DEBUG(("Host set Enable Interrupt on Complete"));
606 BX_DEBUG(("Host set Enable Interrupt on Resume"));
610 case 0x06: // frame number register (16-bit)
612 BX_DEBUG(("write to frame number register with bits 15:11 not zero: 0x%04x", value
));
614 if (BX_USB_THIS hub
[0].usb_status
.host_halted
)
615 BX_USB_THIS hub
[0].usb_frame_num
.frame_num
= (value
& 0x07FF);
617 // ignored by the hardward, but lets report it anyway
618 BX_DEBUG(("write to frame number register with STATUS.HALTED == 0"));
621 case 0x08: // frame base register (32-bit)
623 BX_PANIC(("write to frame base register with bits 11:0 not zero: 0x%08x", value
));
625 BX_USB_THIS hub
[0].usb_frame_base
.frame_base
= (value
& ~0xfff);
628 case 0x0C: // start of Frame Modify register (8-bit)
630 BX_DEBUG(("write to SOF Modify register with bit 7 not zero: 0x%04x", value
));
632 BX_USB_THIS hub
[0].usb_sof
.sof_timing
= value
;
635 case 0x14: // port #3 non existant, but linux systems check it to see if there are more than 2
636 BX_ERROR(("write to non existant offset 0x14 (port #3)"));
639 case 0x10: // port #1
640 case 0x12: // port #2
641 port
= (offset
& 0x0F) >> 1;
642 if ((port
< USB_NUM_PORTS
) && (io_len
== 2)) {
643 // If the ports reset bit is set, don't allow any writes unless the new write will clear the reset bit
644 if (BX_USB_THIS hub
[0].usb_port
[port
].reset
& (value
& (1<<9)))
646 if (value
& ((1<<5) | (1<<4) | (1<<0)))
647 BX_DEBUG(("write to one or more read-only bits in port #%d register: 0x%04x", port
+1, value
));
648 if (!(value
& (1<<7)))
649 BX_DEBUG(("write to port #%d register bit 7 = 0", port
+1));
651 BX_DEBUG(("write to bit 8 in port #%d register ignored", port
+1));
652 if ((value
& (1<<12)) && BX_USB_THIS hub
[0].usb_command
.suspend
)
653 BX_DEBUG(("write to port #%d register bit 12 when in Global-Suspend", port
+1));
655 BX_USB_THIS hub
[0].usb_port
[port
].suspend
= (value
& (1<<12)) ? 1 : 0;
656 BX_USB_THIS hub
[0].usb_port
[port
].reset
= (value
& (1<<9)) ? 1 : 0;
657 BX_USB_THIS hub
[0].usb_port
[port
].resume
= (value
& (1<<6)) ? 1 : 0;
658 if (!BX_USB_THIS hub
[0].usb_port
[port
].enabled
&& (value
& (1<<2)))
659 BX_USB_THIS hub
[0].usb_port
[port
].able_changed
= 0;
661 BX_USB_THIS hub
[0].usb_port
[port
].able_changed
= (value
& (1<<3)) ? 0 : BX_USB_THIS hub
[0].usb_port
[port
].able_changed
;
662 BX_USB_THIS hub
[0].usb_port
[port
].enabled
= (value
& (1<<2)) ? 1 : 0;
663 BX_USB_THIS hub
[0].usb_port
[port
].connect_changed
= (value
& (1<<1)) ? 0 : BX_USB_THIS hub
[0].usb_port
[port
].connect_changed
;
665 // if port reset, reset function(s)
666 //TODO: only reset items on the downstream...
667 // for now, reset the one and only
668 // TODO: descriptors, etc....
669 if (BX_USB_THIS hub
[0].usb_port
[port
].reset
) {
670 BX_USB_THIS hub
[0].usb_port
[port
].suspend
= 0;
671 BX_USB_THIS hub
[0].usb_port
[port
].resume
= 0;
672 BX_USB_THIS hub
[0].usb_port
[port
].enabled
= 0;
673 // are we are currently connected/disconnected
674 if (BX_USB_THIS hub
[0].usb_port
[port
].status
) {
675 if (BX_USB_THIS hub
[0].usb_port
[port
].device
!= NULL
) {
676 BX_USB_THIS hub
[0].usb_port
[port
].low_speed
=
677 (BX_USB_THIS hub
[0].usb_port
[port
].device
->get_speed() == USB_SPEED_LOW
);
678 usb_set_connect_status(port
, BX_USB_THIS hub
[0].usb_port
[port
].device
->get_type(), 1);
679 BX_USB_THIS
usb_send_msg(BX_USB_THIS hub
[0].usb_port
[port
].device
, USB_MSG_RESET
);
682 BX_INFO(("Port%d: Reset", port
+1));
686 // else fall through to default
688 BX_ERROR(("unsupported io write to address=0x%04x!", (unsigned) address
));
693 void bx_pciusb_c::usb_timer_handler(void *this_ptr
)
695 bx_pciusb_c
*class_ptr
= (bx_pciusb_c
*) this_ptr
;
696 class_ptr
->usb_timer();
699 // Called once every 1ms
700 #define USB_STACK_SIZE 256
701 void bx_pciusb_c::usb_timer(void)
705 // If the "global reset" bit was set by software
706 if (BX_USB_THIS global_reset
) {
707 for (i
=0; i
<USB_NUM_PORTS
; i
++) {
708 BX_USB_THIS hub
[0].usb_port
[i
].able_changed
= 0;
709 BX_USB_THIS hub
[0].usb_port
[i
].connect_changed
= 0;
710 BX_USB_THIS hub
[0].usb_port
[i
].enabled
= 0;
711 BX_USB_THIS hub
[0].usb_port
[i
].line_dminus
= 0;
712 BX_USB_THIS hub
[0].usb_port
[i
].line_dplus
= 0;
713 BX_USB_THIS hub
[0].usb_port
[i
].low_speed
= 0;
714 BX_USB_THIS hub
[0].usb_port
[i
].reset
= 0;
715 BX_USB_THIS hub
[0].usb_port
[i
].resume
= 0;
716 BX_USB_THIS hub
[0].usb_port
[i
].status
= 0;
717 BX_USB_THIS hub
[0].usb_port
[i
].suspend
= 0;
722 // If command.schedule = 1, then run schedule
723 // *** This assumes that we can complete the frame within the 1ms time allowed ***
724 // Actually, not complete, but reach the end of the frame. This means that there may still
725 // be TDs and QHs that were BREADTH defined and will be executed on the next cycle/iteration.
727 if (BX_USB_THIS busy
) {
728 BX_PANIC(("Did not complete last frame before the 1ms was over. Starting next frame."));
729 BX_USB_THIS busy
= 0;
731 if (BX_USB_THIS hub
[0].usb_command
.schedule
) {
732 BX_USB_THIS busy
= 1;
733 bx_bool fire_int
= 0;
734 set_irq_level(0); // make sure it is low
735 bx_bool interrupt
= 0, shortpacket
= 0, stalled
= 0;
737 struct HCSTACK stack
[USB_STACK_SIZE
+1]; // queue stack for this item only
739 Bit32u item
, address
, lastvertaddr
= 0, queue_num
= 0;
740 Bit32u frame
, frm_addr
= BX_USB_THIS hub
[0].usb_frame_base
.frame_base
+
741 (BX_USB_THIS hub
[0].usb_frame_num
.frame_num
<< 2);
742 DEV_MEM_READ_PHYSICAL(frm_addr
, 4, (Bit8u
*) &frame
);
743 if ((frame
& 1) == 0) {
744 stack
[stk
].next
= (frame
& ~0xF);
746 stack
[stk
].q
= (frame
& 0x0002) ? 1 : 0;
750 // Linux seems to just loop a few queues together and wait for the 1ms to end.
751 // We will just count the stack and exit when we get to a good point to stop.
752 if (stk
>= USB_STACK_SIZE
) break;
754 // check to make sure we are not done before continue-ing on
755 if ((stack
[stk
].d
== HC_VERT
) && stack
[stk
].t
) { stk
--; continue; }
756 if ((stack
[stk
].d
== HC_HORZ
) && stack
[stk
].t
) break;
757 if (stack
[stk
].q
) { // is a queue
758 address
= stack
[stk
].next
;
759 lastvertaddr
= address
+ 4;
762 DEV_MEM_READ_PHYSICAL(address
, 4, (Bit8u
*) &item
);
763 stack
[stk
].next
= item
& ~0xF;
764 stack
[stk
].d
= HC_HORZ
;
765 stack
[stk
].q
= (item
& 0x0002) ? 1 : 0;
766 stack
[stk
].t
= (item
& 0x0001) ? 1 : 0;
769 DEV_MEM_READ_PHYSICAL(lastvertaddr
, 4, (Bit8u
*) &item
);
770 stack
[stk
].next
= item
& ~0xF;
771 stack
[stk
].d
= HC_VERT
;
772 stack
[stk
].q
= (item
& 0x0002) ? 1 : 0;
773 stack
[stk
].t
= (item
& 0x0001) ? 1 : 0;
774 BX_DEBUG(("Queue %3i: 0x%08X %i %i 0x%08X %i %i", queue_num
,
775 stack
[stk
-1].next
, stack
[stk
-1].q
, stack
[stk
-1].t
,
776 stack
[stk
].next
, stack
[stk
].q
, stack
[stk
].t
));
778 } else { // else is a TD
779 address
= stack
[stk
].next
;
780 DEV_MEM_READ_PHYSICAL(address
, 4, (Bit8u
*) &td
.dword0
);
781 DEV_MEM_READ_PHYSICAL(address
+4, 4, (Bit8u
*) &td
.dword1
);
782 DEV_MEM_READ_PHYSICAL(address
+8, 4, (Bit8u
*) &td
.dword2
);
783 DEV_MEM_READ_PHYSICAL(address
+12, 4, (Bit8u
*) &td
.dword3
);
784 bx_bool spd
= (td
.dword1
& (1<<29)) ? 1 : 0;
785 stack
[stk
].next
= td
.dword0
& ~0xF;
786 bx_bool depthbreadth
= (td
.dword0
& 0x0004) ? 1 : 0; // 1 = depth first, 0 = breadth first
787 stack
[stk
].q
= (td
.dword0
& 0x0002) ? 1 : 0;
788 stack
[stk
].t
= (td
.dword0
& 0x0001) ? 1 : 0;
789 if (td
.dword1
& (1<<24)) interrupt
= 1;
790 if (td
.dword1
& (1<<23)) { // is it an active TD
791 BX_DEBUG(("Frame: %04i (0x%04X)", BX_USB_THIS hub
[0].usb_frame_num
.frame_num
, BX_USB_THIS hub
[0].usb_frame_num
.frame_num
));
792 if (BX_USB_THIS
DoTransfer(address
, queue_num
, &td
)) {
793 // issue short packet?
794 Bit16u r_actlen
= (((td
.dword1
& 0x7FF)+1) & 0x7FF);
795 Bit16u r_maxlen
= (((td
.dword2
>>21)+1) & 0x7FF);
796 BX_DEBUG((" r_actlen = 0x%04X r_maxlen = 0x%04X", r_actlen
, r_maxlen
));
797 if (((td
.dword2
& 0xFF) == USB_TOKEN_IN
) && spd
&& stk
&& (r_actlen
< r_maxlen
) && ((td
.dword1
& 0x00FF0000) == 0)) {
799 td
.dword1
|= (1<<29);
801 if (td
.dword1
& (1<<22)) stalled
= 1;
803 DEV_MEM_WRITE_PHYSICAL(address
+4, 4, (Bit8u
*) &td
.dword1
); // write back the status
804 // copy pointer for next queue item, in to vert queue head
805 if ((stk
> 0) && !shortpacket
&& (stack
[stk
].d
== HC_VERT
))
806 DEV_MEM_WRITE_PHYSICAL(lastvertaddr
, 4, (Bit8u
*) &td
.dword0
);
811 // if last TD in HORZ queue pointer, then we are done.
812 if (stack
[stk
].t
&& (stack
[stk
].d
== HC_HORZ
)) break;
813 // if Breadth first or last item in queue, move to next queue.
814 if (!depthbreadth
|| stack
[stk
].t
) {
815 if (stack
[stk
].d
== HC_HORZ
) queue_num
--; // <-- really, this should never happen until we
816 stk
--; // support bandwidth reclamation...
820 if (stack
[stk
].t
) break;
826 // set the status register bit:0 to 1 if SPD is enabled
827 // and if interrupts not masked via interrupt register, raise irq interrupt.
828 if (shortpacket
&& BX_USB_THIS hub
[0].usb_enable
.short_packet
) {
830 BX_DEBUG((" [SPD] We want it to fire here (Frame: %04i)", BX_USB_THIS hub
[0].usb_frame_num
.frame_num
));
833 // if one of the TD's in this frame had the ioc bit set, we need to
834 // raise an interrupt, if interrupts are not masked via interrupt register.
835 // always set the status register if IOC.
836 if (interrupt
&& BX_USB_THIS hub
[0].usb_enable
.on_complete
) {
838 BX_DEBUG((" [IOC] We want it to fire here (Frame: %04i)", BX_USB_THIS hub
[0].usb_frame_num
.frame_num
));
841 if (stalled
&& BX_USB_THIS hub
[0].usb_enable
.timeout_crc
) {
843 BX_DEBUG((" [stalled] We want it to fire here (Frame: %04i)", BX_USB_THIS hub
[0].usb_frame_num
.frame_num
));
847 // The Frame Number Register is incremented every 1ms
848 BX_USB_THIS hub
[0].usb_frame_num
.frame_num
++;
849 BX_USB_THIS hub
[0].usb_frame_num
.frame_num
&= (1024-1);
851 // if we needed to fire an interrupt now, lets do it *after* we increment the frame_num register
853 BX_USB_THIS hub
[0].usb_status
.interrupt
= 1;
854 BX_USB_THIS hub
[0].usb_status
.error_interrupt
= stalled
;
858 BX_USB_THIS busy
= 0; // ready to do next frame item
859 } // end run schedule
861 // if host turned off the schedule, set the halted bit in the status register
862 // Note: Can not use an else from the if() above since the host can changed this bit
863 // while we are processing a frame.
864 if (BX_USB_THIS hub
[0].usb_command
.schedule
== 0)
865 BX_USB_THIS hub
[0].usb_status
.host_halted
= 1;
868 // If in Global_Suspend mode and any of usb_port[i] bits 6,3, or 1 are set,
869 // we need to issue a Global_Resume (set the global resume bit).
870 // However, since we don't do anything, let's not.
873 bx_bool
bx_pciusb_c::DoTransfer(Bit32u address
, Bit32u queue_num
, struct TD
*td
) {
875 int i
, len
= 0, ret
= 0;
876 usb_device_t
*dev
= NULL
;
878 Bit16u maxlen
= (td
->dword2
>> 21);
879 Bit8u addr
= (td
->dword2
>> 8) & 0x7F;
880 Bit8u endpt
= (td
->dword2
>> 15) & 0x0F;
881 Bit8u pid
= td
->dword2
& 0xFF;
883 BX_DEBUG(("QH%03i:TD found at address: 0x%08X", queue_num
, address
));
884 BX_DEBUG((" %08X %08X %08X %08X", td
->dword0
, td
->dword1
, td
->dword2
, td
->dword3
));
886 // check TD to make sure it is valid
887 // A max length 0x500 to 0x77E is illegal
888 if (((td
->dword2
>> 21) >= 0x500) && ((td
->dword2
>> 21) != 0x7FF)) {
889 BX_ERROR(("error at 11111111111"));
890 return 1; // error = consistency check failure
893 //if (td->dword0 & 0x8) return 1; // error = reserved bits in dword0 set
894 // other error checks here
896 // find device address
897 bx_bool at_least_one
= 0;
898 for (i
=0; i
<USB_NUM_PORTS
; i
++) {
899 if (BX_USB_THIS hub
[0].usb_port
[i
].device
!= NULL
) {
900 if (BX_USB_THIS hub
[0].usb_port
[i
].device
->get_connected()) {
902 if (BX_USB_THIS hub
[0].usb_port
[i
].device
->get_address() == addr
) {
903 dev
= BX_USB_THIS hub
[0].usb_port
[i
].device
;
910 BX_USB_THIS
set_status(td
, 1, 0, 0, 0, (pid
==USB_TOKEN_SETUP
)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
914 if ((pid
== USB_TOKEN_OUT
) && (maxlen
== 0x7FF) && (addr
== 0)) {
915 // This is the "keep awake" packet that Windows sends once a schedule cycle.
916 // For now, let it pass through to the code below.
918 BX_PANIC(("Device not found for addr: %i", addr
));
919 BX_USB_THIS
set_status(td
, 1, 0, 0, 0, (pid
==USB_TOKEN_SETUP
)?1:0, 0, 0x007); // an 8 byte packet was received, but stalled
920 return 1; // device not found
924 // the device should remain in a stall state until the next setup packet is recieved
925 // For some reason, this doesn't work yet.
926 //if (dev && dev->in_stall && (pid != USB_TOKEN_SETUP))
933 BX_USB_THIS usb_packet
.pid
= pid
;
934 BX_USB_THIS usb_packet
.devaddr
= addr
;
935 BX_USB_THIS usb_packet
.devep
= endpt
;
936 BX_USB_THIS usb_packet
.data
= device_buffer
;
937 BX_USB_THIS usb_packet
.len
= maxlen
;
940 case USB_TOKEN_SETUP
:
942 DEV_MEM_READ_PHYSICAL_BLOCK(td
->dword3
, maxlen
, device_buffer
);
944 ret
= dev
->handle_packet(&BX_USB_THIS usb_packet
);
948 ret
= dev
->handle_packet(&BX_USB_THIS usb_packet
);
953 ret
= USB_RET_BABBLE
;
956 DEV_MEM_WRITE_PHYSICAL_BLOCK(td
->dword3
, len
, device_buffer
);
963 BX_USB_THIS hub
[i
].usb_status
.host_error
= 1;
964 BX_USB_THIS
set_irq_level(1);
967 BX_USB_THIS
set_status(td
, 0, 0, 0, 0, 0, 0, len
-1);
969 BX_USB_THIS
set_status(td
, 1, 0, 0, 0, 0, 0, 0x007); // stalled
976 // If the request fails, set the stall bit ????
977 void bx_pciusb_c::set_status(struct TD
*td
, bx_bool stalled
, bx_bool data_buffer_error
, bx_bool babble
,
978 bx_bool nak
, bx_bool crc_time_out
, bx_bool bitstuff_error
, Bit16u act_len
)
980 // clear out the bits we can modify and/or want zero
981 td
->dword1
&= 0xDF00F800;
983 // now set the bits according to the passed param's
984 td
->dword1
|= stalled
? (1<<22) : 0; // stalled
985 td
->dword1
|= data_buffer_error
? (1<<21) : 0; // data buffer error
986 td
->dword1
|= babble
? (1<<20) : 0; // babble
987 td
->dword1
|= nak
? (1<<19) : 0; // nak
988 td
->dword1
|= crc_time_out
? (1<<18) : 0; // crc/timeout
989 td
->dword1
|= bitstuff_error
? (1<<17) : 0; // bitstuff error
990 td
->dword1
|= (act_len
& 0x7FF); // actual length
991 if (stalled
|| data_buffer_error
|| babble
|| nak
|| crc_time_out
|| bitstuff_error
)
992 td
->dword1
&= ~((1<<28) | (1<<27)); // clear the c_err field in there was an error
995 // pci configuration space read callback handler
996 Bit32u
bx_pciusb_c::pci_read_handler(Bit8u address
, unsigned io_len
)
1000 if (io_len
> 4 || io_len
== 0) {
1001 BX_ERROR(("Experimental USB PCI read register 0x%02x, len=%u !",
1002 (unsigned) address
, (unsigned) io_len
));
1006 const char* pszName
= " ";
1008 case 0x00: if (io_len
== 2) {
1009 pszName
= "(vendor id) ";
1010 } else if (io_len
== 4) {
1011 pszName
= "(vendor + device) ";
1014 case 0x04: if (io_len
== 2) {
1015 pszName
= "(command) ";
1016 } else if (io_len
== 4) {
1017 pszName
= "(command+status) ";
1020 case 0x08: if (io_len
== 1) {
1021 pszName
= "(revision id) ";
1022 } else if (io_len
== 4) {
1023 pszName
= "(rev.+class code) ";
1026 case 0x0c: pszName
= "(cache line size) "; break;
1027 case 0x20: pszName
= "(base address) "; break;
1028 case 0x28: pszName
= "(cardbus cis) "; break;
1029 case 0x2c: pszName
= "(subsys. vendor+) "; break;
1030 case 0x30: pszName
= "(rom base) "; break;
1031 case 0x3c: pszName
= "(interrupt line+) "; break;
1032 case 0x3d: pszName
= "(interrupt pin) "; break;
1035 // This odd code is to display only what bytes actually were read.
1040 for (unsigned i
=0; i
<io_len
; i
++) {
1041 value
|= (BX_USB_THIS hub
[0].pci_conf
[address
+i
] << (i
*8));
1042 sprintf(szTmp2
, "%02x", (BX_USB_THIS hub
[0].pci_conf
[address
+i
]));
1044 strcat(szTmp
, szTmp2
);
1047 BX_DEBUG(("USB PCI read register 0x%02x %svalue 0x%s", address
, pszName
, szTmp
));
1052 // pci configuration space write callback handler
1053 void bx_pciusb_c::pci_write_handler(Bit8u address
, Bit32u value
, unsigned io_len
)
1055 Bit8u value8
, oldval
;
1056 bx_bool baseaddr_change
= 0;
1058 if (((address
>= 0x10) && (address
< 0x20)) ||
1059 ((address
> 0x23) && (address
< 0x34)))
1062 // This odd code is to display only what bytes actually were written.
1068 for (unsigned i
=0; i
<io_len
; i
++) {
1069 value8
= (value
>> (i
*8)) & 0xFF;
1070 oldval
= BX_USB_THIS hub
[0].pci_conf
[address
+i
];
1071 switch (address
+i
) {
1074 BX_USB_THIS hub
[0].pci_conf
[address
+i
] = value8
;
1075 sprintf(szTmp2
, "%02x", value8
);
1080 case 0x05: // disallowing write to command hi-byte
1081 case 0x06: // disallowing write to status lo-byte (is that expected?)
1082 strcpy(szTmp2
, "..");
1085 if (value8
!= oldval
) {
1086 BX_INFO(("new irq line = %d", value8
));
1087 BX_USB_THIS hub
[0].pci_conf
[address
+i
] = value8
;
1089 sprintf(szTmp2
, "%02x", value8
);
1092 value8
= (value8
& 0xfc) | 0x01;
1096 baseaddr_change
|= (value8
!= oldval
);
1098 BX_USB_THIS hub
[0].pci_conf
[address
+i
] = value8
;
1099 sprintf(szTmp2
, "%02x", value8
);
1102 strcat(szTmp
, szTmp2
);
1104 if (baseaddr_change
) {
1105 if (DEV_pci_set_base_io(BX_USB_THIS_PTR
, read_handler
, write_handler
,
1106 &BX_USB_THIS hub
[0].base_ioaddr
,
1107 &BX_USB_THIS hub
[0].pci_conf
[0x20],
1108 32, &usb_iomask
[0], "USB Hub #1")) {
1109 BX_INFO(("new base address: 0x%04x", BX_USB_THIS hub
[0].base_ioaddr
));
1114 BX_DEBUG(("USB PCI write register 0x%02x value 0x%s", address
, szTmp
));
1117 void bx_pciusb_c::usb_mouse_enabled_changed(bx_bool enable
)
1119 if (enable
&& (BX_USB_THIS mousedev
!= NULL
)) {
1120 mousedev
->handle_reset();
1124 void bx_pciusb_c::usb_set_connect_status(Bit8u port
, int type
, bx_bool connected
)
1126 char pname
[BX_PATHNAME_LEN
];
1127 char fname
[BX_PATHNAME_LEN
];
1129 if (BX_USB_THIS hub
[0].usb_port
[port
].device
!= NULL
) {
1130 if (BX_USB_THIS hub
[0].usb_port
[port
].device
->get_type() == type
) {
1132 if (!BX_USB_THIS hub
[0].usb_port
[port
].device
->get_connected()) {
1133 BX_USB_THIS hub
[0].usb_port
[port
].low_speed
=
1134 (BX_USB_THIS hub
[0].usb_port
[port
].device
->get_speed() == USB_SPEED_LOW
);
1136 if (BX_USB_THIS hub
[0].usb_port
[port
].low_speed
) {
1137 BX_USB_THIS hub
[0].usb_port
[port
].line_dminus
= 1; // dminus=1 & dplus=0 = low speed (at idle time)
1138 BX_USB_THIS hub
[0].usb_port
[port
].line_dplus
= 0; // dminus=0 & dplus=1 = high speed (at idle time)
1140 BX_USB_THIS hub
[0].usb_port
[port
].line_dminus
= 0; // dminus=1 & dplus=0 = low speed (at idle time)
1141 BX_USB_THIS hub
[0].usb_port
[port
].line_dplus
= 1; // dminus=0 & dplus=1 = high speed (at idle time)
1143 BX_USB_THIS hub
[0].usb_port
[port
].status
= 1; //
1144 BX_USB_THIS hub
[0].usb_port
[port
].connect_changed
= 1;
1145 BX_USB_THIS hub
[0].usb_port
[port
].able_changed
= 1;
1147 // if in suspend state, signal resume
1148 if (BX_USB_THIS hub
[0].usb_command
.suspend
) {
1149 BX_USB_THIS hub
[0].usb_port
[port
].resume
= 1;
1150 BX_USB_THIS hub
[0].usb_status
.resume
= 1;
1151 if (BX_USB_THIS hub
[0].usb_enable
.resume
) {
1152 BX_USB_THIS hub
[0].usb_status
.interrupt
= 1;
1157 if ((type
== USB_DEV_TYPE_DISK
) &&
1158 (!BX_USB_THIS hub
[0].usb_port
[port
].device
->get_connected())) {
1160 strcpy(pname
, BXPN_USB1_PORT1
);
1162 strcpy(pname
, BXPN_USB1_PORT2
);
1164 strcpy(fname
, SIM
->get_param_string(pname
)->getptr() + 5);
1165 if (!((usb_msd_device_t
*)BX_USB_THIS hub
[0].usb_port
[port
].device
)->init(fname
)) {
1166 usb_set_connect_status(port
, USB_DEV_TYPE_DISK
, 0);
1168 BX_INFO(("HD on USB port #%d: '%s'", port
+1, fname
));
1172 BX_USB_THIS hub
[0].usb_port
[port
].status
= 0;
1173 BX_USB_THIS hub
[0].usb_port
[port
].connect_changed
= 1;
1174 BX_USB_THIS hub
[0].usb_port
[port
].enabled
= 0;
1175 BX_USB_THIS hub
[0].usb_port
[port
].able_changed
= 1;
1176 BX_USB_THIS hub
[0].usb_port
[port
].low_speed
= 0;
1177 BX_USB_THIS hub
[0].usb_port
[port
].line_dminus
= 0; // dminus=1 & dplus=0 = low speed (at idle time)
1178 BX_USB_THIS hub
[0].usb_port
[port
].line_dplus
= 0; // dminus=0 & dplus=1 = high speed (at idle time)
1179 if ((type
== USB_DEV_TYPE_MOUSE
) ||
1180 (type
== USB_DEV_TYPE_TABLET
)) {
1181 if (BX_USB_THIS hub
[0].usb_port
[port
].device
== BX_USB_THIS mousedev
) {
1182 BX_USB_THIS mousedev
= NULL
;
1184 } else if (type
== USB_DEV_TYPE_KEYPAD
) {
1185 if (BX_USB_THIS hub
[0].usb_port
[port
].device
== BX_USB_THIS keybdev
) {
1186 BX_USB_THIS keybdev
= NULL
;
1189 if (BX_USB_THIS hub
[0].usb_port
[port
].device
!= NULL
) {
1190 delete BX_USB_THIS hub
[0].usb_port
[port
].device
;
1191 BX_USB_THIS hub
[0].usb_port
[port
].device
= NULL
;
1192 sprintf(pname
, "pciusb.hub1.port%d.device", port
+1);
1193 bx_list_c
*devlist
= (bx_list_c
*)SIM
->get_param(pname
, SIM
->get_bochs_root());
1201 void bx_pciusb_c::usb_mouse_enq(int delta_x
, int delta_y
, int delta_z
, unsigned button_state
)
1203 if (BX_USB_THIS mousedev
!= NULL
) {
1204 mousedev
->mouse_enq(delta_x
, delta_y
, delta_z
, button_state
);
1208 bx_bool
bx_pciusb_c::usb_key_enq(Bit8u
*scan_code
)
1210 if (BX_USB_THIS keybdev
!= NULL
) {
1211 return keybdev
->key_enq(scan_code
);
1216 bx_bool
bx_pciusb_c::usb_keyboard_connected()
1218 return (BX_USB_THIS keybdev
!= NULL
);
1221 bx_bool
bx_pciusb_c::usb_mouse_connected()
1223 return (BX_USB_THIS mousedev
!= NULL
);
1226 // Send an internal message to a USB device
1227 void bx_pciusb_c::usb_send_msg(usb_device_t
*dev
, int msg
)
1230 memset(&p
, 0, sizeof(p
));
1232 dev
->handle_packet(&p
);
1235 // base class for USB devices
1237 usb_device_t::usb_device_t(void)
1239 memset((void*)&d
, 0, sizeof(d
));
1242 void usb_device_t::register_state(bx_list_c
*parent
)
1244 bx_list_c
*list
= new bx_list_c(parent
, "d", "Common USB Device State");
1245 new bx_shadow_num_c(list
, "addr", &d
.addr
);
1246 new bx_shadow_num_c(list
, "state", &d
.state
);
1247 new bx_shadow_num_c(list
, "remote_wakeup", &d
.remote_wakeup
);
1248 register_state_specific(parent
);
1251 // generic USB packet handler
1253 #define SETUP_STATE_IDLE 0
1254 #define SETUP_STATE_DATA 1
1255 #define SETUP_STATE_ACK 2
1257 int usb_device_t::handle_packet(USBPacket
*p
)
1261 Bit8u
*data
= p
->data
;
1264 case USB_MSG_ATTACH
:
1265 d
.state
= USB_STATE_ATTACHED
;
1267 case USB_MSG_DETACH
:
1268 d
.state
= USB_STATE_NOTATTACHED
;
1271 d
.remote_wakeup
= 0;
1273 d
.state
= USB_STATE_DEFAULT
;
1276 case USB_TOKEN_SETUP
:
1277 if (d
.state
< USB_STATE_DEFAULT
|| p
->devaddr
!= d
.addr
)
1278 return USB_RET_NODEV
;
1281 memcpy(d
.setup_buf
, data
, 8);
1282 d
.setup_len
= (d
.setup_buf
[7] << 8) | d
.setup_buf
[6];
1284 if (d
.setup_buf
[0] & USB_DIR_IN
) {
1285 ret
= handle_control((d
.setup_buf
[0] << 8) | d
.setup_buf
[1],
1286 (d
.setup_buf
[3] << 8) | d
.setup_buf
[2],
1287 (d
.setup_buf
[5] << 8) | d
.setup_buf
[4],
1288 d
.setup_len
, d
.data_buf
);
1291 if (ret
< d
.setup_len
)
1293 d
.setup_state
= SETUP_STATE_DATA
;
1295 if (d
.setup_len
== 0)
1296 d
.setup_state
= SETUP_STATE_ACK
;
1298 d
.setup_state
= SETUP_STATE_DATA
;
1302 if (d
.state
< USB_STATE_DEFAULT
|| p
->devaddr
!= d
.addr
)
1303 return USB_RET_NODEV
;
1306 switch(d
.setup_state
) {
1307 case SETUP_STATE_ACK
:
1308 if (!(d
.setup_buf
[0] & USB_DIR_IN
)) {
1309 d
.setup_state
= SETUP_STATE_IDLE
;
1310 ret
= handle_control((d
.setup_buf
[0] << 8) | d
.setup_buf
[1],
1311 (d
.setup_buf
[3] << 8) | d
.setup_buf
[2],
1312 (d
.setup_buf
[5] << 8) | d
.setup_buf
[4],
1313 d
.setup_len
, d
.data_buf
);
1320 case SETUP_STATE_DATA
:
1321 if (d
.setup_buf
[0] & USB_DIR_IN
) {
1322 l
= d
.setup_len
- d
.setup_index
;
1325 memcpy(data
, d
.data_buf
+ d
.setup_index
, l
);
1327 if (d
.setup_index
>= d
.setup_len
)
1328 d
.setup_state
= SETUP_STATE_ACK
;
1331 d
.setup_state
= SETUP_STATE_IDLE
;
1340 ret
= handle_data(p
);
1345 if (d
.state
< USB_STATE_DEFAULT
|| p
->devaddr
!= d
.addr
)
1346 return USB_RET_NODEV
;
1349 switch(d
.setup_state
) {
1350 case SETUP_STATE_ACK
:
1351 if (d
.setup_buf
[0] & USB_DIR_IN
) {
1352 d
.setup_state
= SETUP_STATE_IDLE
;
1355 // ignore additionnal output
1358 case SETUP_STATE_DATA
:
1359 if (!(d
.setup_buf
[0] & USB_DIR_IN
)) {
1360 l
= d
.setup_len
- d
.setup_index
;
1363 memcpy(d
.data_buf
+ d
.setup_index
, data
, l
);
1365 if (d
.setup_index
>= d
.setup_len
)
1366 d
.setup_state
= SETUP_STATE_ACK
;
1369 d
.setup_state
= SETUP_STATE_IDLE
;
1378 ret
= handle_data(p
);
1384 ret
= USB_RET_STALL
;
1391 // USB runtime parameter handler
1393 const char *bx_pciusb_c::usb_param_handler(bx_param_string_c
*param
, int set
, const char *val
, int maxlen
)
1395 usbdev_type type
= USB_DEV_TYPE_NONE
;
1397 // handler for USB runtime parameters
1399 char pname
[BX_PATHNAME_LEN
];
1400 param
->get_param_path(pname
, BX_PATHNAME_LEN
);
1401 if (!strcmp(pname
, BXPN_USB1_PORT1
)) {
1402 BX_INFO(("USB port #1 experimental device change"));
1403 if (!strcmp(val
, "none") && BX_USB_THIS hub
[0].usb_port
[0].status
) {
1404 if (BX_USB_THIS hub
[0].usb_port
[0].device
!= NULL
) {
1405 type
= BX_USB_THIS hub
[0].usb_port
[0].device
->get_type();
1407 usb_set_connect_status(0, type
, 0);
1408 } else if (strcmp(val
, "none") && !BX_USB_THIS hub
[0].usb_port
[0].status
) {
1409 init_device(0, val
);
1411 } else if (!strcmp(pname
, BXPN_USB1_PORT2
)) {
1412 BX_INFO(("USB port #2 experimental device change"));
1413 if (!strcmp(val
, "none") && BX_USB_THIS hub
[0].usb_port
[1].status
) {
1414 if (BX_USB_THIS hub
[0].usb_port
[1].device
!= NULL
) {
1415 type
= BX_USB_THIS hub
[0].usb_port
[1].device
->get_type();
1417 usb_set_connect_status(1, type
, 0);
1418 } else if (strcmp(val
, "none") && !BX_USB_THIS hub
[0].usb_port
[1].status
) {
1419 init_device(1, val
);
1422 BX_PANIC(("usb_param_handler called with unexpected parameter '%s'", pname
));
1428 #endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB