- added instructions how to update the online documentation
[bochs-mirror.git] / iodev / pciusb.cc
blob43a4c58e1309a821c6c5e790dfb8ae24ab33120e
1 /////////////////////////////////////////////////////////////////////////
2 // $Id: pciusb.cc,v 1.66 2008/12/14 08:56:05 vruppert Exp $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2004 MandrakeSoft S.A.
6 //
7 // MandrakeSoft S.A.
8 // 43, rue d'Aboukir
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.
30 /* Notes:
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.
42 - 31 July 2006:
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
51 this project.
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.
57 #define BX_PLUGGABLE
59 #include "iodev.h"
60 #if BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB
61 #include "usb_hid.h"
62 #include "usb_msd.h"
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):");
76 unsigned offset = 0;
77 for (unsigned p=0; p<size; p++) {
78 if (!(p & 0x0F)) {
79 BX_DEBUG(("%s", the_packet));
80 sprintf(the_packet, " 0x%04X ", offset);
81 offset += 16;
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)
92 size_t len, i;
93 Bit8u *q;
95 q = buf;
96 len = strlen(str);
97 if (len > 32) {
98 *q = 0;
99 return 0;
101 *q++ = 2 * len + 2;
102 *q++ = 3;
103 for(i = 0; i < len; i++) {
104 *q++ = str[i];
105 *q++ = 0;
107 return q - buf;
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);
115 return 0; // Success
118 void libpciusb_LTX_plugin_fini(void)
120 delete theUSBDevice;
123 bx_pciusb_c::bx_pciusb_c()
125 put("USB");
126 settype(PCIUSBLOG);
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);
149 BX_DEBUG(("Exit"));
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)
191 unsigned i, j;
193 if (!SIM->get_param_bool(BXPN_USB1_ENABLED)->get()) return;
195 if (type == BX_RESET_HARDWARE) {
196 static const struct reset_vals_t {
197 unsigned addr;
198 unsigned char val;
199 } reset_vals[] = {
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;
225 // reset locals
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)
279 unsigned i, j;
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();
380 } else {
381 BX_PANIC(("USB device 'disk' needs a filename separated with a colon"));
382 return;
384 } else {
385 BX_PANIC(("unknown USB device: %s", devname));
386 return;
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)
411 #else
412 UNUSED(this_ptr);
413 #endif // !BX_USE_PCIUSB_SMF
414 Bit32u val = 0x0;
415 Bit8u offset,port;
417 offset = address - BX_USB_THIS hub[0].base_ioaddr;
419 switch (offset) {
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;
429 break;
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;
438 break;
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;
445 break;
447 case 0x06: // frame number register (16-bit)
448 val = BX_USB_THIS hub[0].usb_frame_num.frame_num;
449 break;
451 case 0x08: // frame base register (32-bit)
452 val = BX_USB_THIS hub[0].usb_frame_base.frame_base;
453 break;
455 case 0x0C: // start of Frame Modify register (8-bit)
456 val = BX_USB_THIS hub[0].usb_sof.sof_timing;
457 break;
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)"));
461 val = 0xFF7F;
462 break;
464 case 0x10: // port #1
465 case 0x11:
466 case 0x12: // port #2
467 case 0x13:
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
474 | 1 << 7
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;
483 break;
484 } // else fall through to default
485 default:
486 val = 0xFF7F; // keep compiler happy
487 BX_ERROR(("unsupported io read from address=0x%04x!", (unsigned) address));
488 break;
491 BX_DEBUG(("register read from address 0x%04X: 0x%08X (%2i bits)", (unsigned) address, (Bit32u) val, io_len * 8));
493 return(val);
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)
508 #else
509 UNUSED(this_ptr);
510 #endif // !BX_USE_PCIUSB_SMF
511 Bit8u offset,port;
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;
517 switch (offset) {
518 case 0x00: // command register (16-bit) (R/W)
519 if (value & 0xFF00)
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;
531 // HCRESET
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"));
552 } else {
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"));
566 } else {
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"));
575 break;
577 case 0x02: // status register (16-bit) (R/WC)
578 if (value & 0xFFC0)
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;
588 break;
590 case 0x04: // interrupt enable register (16-bit)
591 if (value & 0xFFF0)
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;
599 if (value & 0x08) {
600 BX_DEBUG(("Host set Enable Interrupt on Short Packet"));
602 if (value & 0x04) {
603 BX_DEBUG(("Host set Enable Interrupt on Complete"));
605 if (value & 0x02) {
606 BX_DEBUG(("Host set Enable Interrupt on Resume"));
608 break;
610 case 0x06: // frame number register (16-bit)
611 if (value & 0xF800)
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);
616 else
617 // ignored by the hardward, but lets report it anyway
618 BX_DEBUG(("write to frame number register with STATUS.HALTED == 0"));
619 break;
621 case 0x08: // frame base register (32-bit)
622 if (value & 0xFFF)
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);
626 break;
628 case 0x0C: // start of Frame Modify register (8-bit)
629 if (value & 0x80)
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;
633 break;
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)"));
637 break;
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)))
645 break;
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));
650 if (value & (1<<8))
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;
660 else
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));
684 break;
686 // else fall through to default
687 default:
688 BX_ERROR(("unsupported io write to address=0x%04x!", (unsigned) address));
689 break;
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)
703 int i;
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;
719 return;
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;
736 struct TD td;
737 struct HCSTACK stack[USB_STACK_SIZE+1]; // queue stack for this item only
738 Bit32s stk = 0;
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);
745 stack[stk].d = 0;
746 stack[stk].q = (frame & 0x0002) ? 1 : 0;
747 stack[stk].t = 0;
748 while (stk > -1) {
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;
760 // get HORZ slot
761 stk++;
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;
767 // get VERT slot
768 stk++;
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));
777 queue_num++;
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)) {
798 shortpacket = 1;
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);
810 if (stk > 0) {
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...
818 if (stk < 1) break;
819 } else {
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) {
829 fire_int = 1;
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) {
837 fire_int = 1;
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) {
842 fire_int = 1;
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
852 if (fire_int) {
853 BX_USB_THIS hub[0].usb_status.interrupt = 1;
854 BX_USB_THIS hub[0].usb_status.error_interrupt = stalled;
855 set_irq_level(1);
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;
867 // TODO:
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()) {
901 at_least_one = 1;
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;
904 break;
909 if (!at_least_one) {
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
911 return 1;
913 if (dev == NULL) {
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.
917 } else {
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))
927 // return FALSE;
929 maxlen++;
930 maxlen &= 0x7FF;
932 if (dev != NULL) {
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;
938 switch (pid) {
939 case USB_TOKEN_OUT:
940 case USB_TOKEN_SETUP:
941 if (maxlen > 0) {
942 DEV_MEM_READ_PHYSICAL_BLOCK(td->dword3, maxlen, device_buffer);
944 ret = dev->handle_packet(&BX_USB_THIS usb_packet);
945 len = maxlen;
946 break;
947 case USB_TOKEN_IN:
948 ret = dev->handle_packet(&BX_USB_THIS usb_packet);
949 if (ret >= 0) {
950 len = ret;
951 if (len > maxlen) {
952 len = maxlen;
953 ret = USB_RET_BABBLE;
955 if (len > 0) {
956 DEV_MEM_WRITE_PHYSICAL_BLOCK(td->dword3, len, device_buffer);
958 } else {
959 len = 0;
961 break;
962 default:
963 BX_USB_THIS hub[i].usb_status.host_error = 1;
964 BX_USB_THIS set_irq_level(1);
966 if (ret >= 0) {
967 BX_USB_THIS set_status(td, 0, 0, 0, 0, 0, 0, len-1);
968 } else {
969 BX_USB_THIS set_status(td, 1, 0, 0, 0, 0, 0, 0x007); // stalled
971 return 1;
973 return 0;
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)
998 Bit32u value = 0;
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));
1003 return 0xffffffff;
1006 const char* pszName = " ";
1007 switch (address) {
1008 case 0x00: if (io_len == 2) {
1009 pszName = "(vendor id) ";
1010 } else if (io_len == 4) {
1011 pszName = "(vendor + device) ";
1013 break;
1014 case 0x04: if (io_len == 2) {
1015 pszName = "(command) ";
1016 } else if (io_len == 4) {
1017 pszName = "(command+status) ";
1019 break;
1020 case 0x08: if (io_len == 1) {
1021 pszName = "(revision id) ";
1022 } else if (io_len == 4) {
1023 pszName = "(rev.+class code) ";
1025 break;
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.
1036 char szTmp[9];
1037 char szTmp2[3];
1038 szTmp[0] = '\0';
1039 szTmp2[0] = '\0';
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]));
1043 strrev(szTmp2);
1044 strcat(szTmp, szTmp2);
1046 strrev(szTmp);
1047 BX_DEBUG(("USB PCI read register 0x%02x %svalue 0x%s", address, pszName, szTmp));
1048 return value;
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)))
1060 return;
1062 // This odd code is to display only what bytes actually were written.
1063 char szTmp[9];
1064 char szTmp2[3];
1065 szTmp[0] = '\0';
1066 szTmp2[0] = '\0';
1067 if (io_len <= 4) {
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) {
1072 case 0x04:
1073 value8 &= 0x05;
1074 BX_USB_THIS hub[0].pci_conf[address+i] = value8;
1075 sprintf(szTmp2, "%02x", value8);
1076 break;
1077 case 0x3d: //
1078 case 0x3e: //
1079 case 0x3f: //
1080 case 0x05: // disallowing write to command hi-byte
1081 case 0x06: // disallowing write to status lo-byte (is that expected?)
1082 strcpy(szTmp2, "..");
1083 break;
1084 case 0x3c:
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);
1090 break;
1091 case 0x20:
1092 value8 = (value8 & 0xfc) | 0x01;
1093 case 0x21:
1094 case 0x22:
1095 case 0x23:
1096 baseaddr_change |= (value8 != oldval);
1097 default:
1098 BX_USB_THIS hub[0].pci_conf[address+i] = value8;
1099 sprintf(szTmp2, "%02x", value8);
1101 strrev(szTmp2);
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));
1113 strrev(szTmp);
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) {
1131 if (connected) {
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)
1139 } else {
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;
1153 set_irq_level(1);
1157 if ((type == USB_DEV_TYPE_DISK) &&
1158 (!BX_USB_THIS hub[0].usb_port[port].device->get_connected())) {
1159 if (port == 0) {
1160 strcpy(pname, BXPN_USB1_PORT1);
1161 } else {
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);
1167 } else {
1168 BX_INFO(("HD on USB port #%d: '%s'", port+1, fname));
1171 } else {
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());
1194 devlist->clear();
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);
1213 return 0;
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)
1229 USBPacket p;
1230 memset(&p, 0, sizeof(p));
1231 p.pid = msg;
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)
1259 int l, ret = 0;
1260 int len = p->len;
1261 Bit8u *data = p->data;
1263 switch(p->pid) {
1264 case USB_MSG_ATTACH:
1265 d.state = USB_STATE_ATTACHED;
1266 break;
1267 case USB_MSG_DETACH:
1268 d.state = USB_STATE_NOTATTACHED;
1269 break;
1270 case USB_MSG_RESET:
1271 d.remote_wakeup = 0;
1272 d.addr = 0;
1273 d.state = USB_STATE_DEFAULT;
1274 handle_reset();
1275 break;
1276 case USB_TOKEN_SETUP:
1277 if (d.state < USB_STATE_DEFAULT || p->devaddr != d.addr)
1278 return USB_RET_NODEV;
1279 if (len != 8)
1280 goto fail;
1281 memcpy(d.setup_buf, data, 8);
1282 d.setup_len = (d.setup_buf[7] << 8) | d.setup_buf[6];
1283 d.setup_index = 0;
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);
1289 if (ret < 0)
1290 return ret;
1291 if (ret < d.setup_len)
1292 d.setup_len = ret;
1293 d.setup_state = SETUP_STATE_DATA;
1294 } else {
1295 if (d.setup_len == 0)
1296 d.setup_state = SETUP_STATE_ACK;
1297 else
1298 d.setup_state = SETUP_STATE_DATA;
1300 break;
1301 case USB_TOKEN_IN:
1302 if (d.state < USB_STATE_DEFAULT || p->devaddr != d.addr)
1303 return USB_RET_NODEV;
1304 switch(p->devep) {
1305 case 0:
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);
1314 if (ret > 0)
1315 ret = 0;
1316 } else {
1317 // return 0 byte
1319 break;
1320 case SETUP_STATE_DATA:
1321 if (d.setup_buf[0] & USB_DIR_IN) {
1322 l = d.setup_len - d.setup_index;
1323 if (l > len)
1324 l = len;
1325 memcpy(data, d.data_buf + d.setup_index, l);
1326 d.setup_index += l;
1327 if (d.setup_index >= d.setup_len)
1328 d.setup_state = SETUP_STATE_ACK;
1329 ret = l;
1330 } else {
1331 d.setup_state = SETUP_STATE_IDLE;
1332 goto fail;
1334 break;
1335 default:
1336 goto fail;
1338 break;
1339 default:
1340 ret = handle_data(p);
1341 break;
1343 break;
1344 case USB_TOKEN_OUT:
1345 if (d.state < USB_STATE_DEFAULT || p->devaddr != d.addr)
1346 return USB_RET_NODEV;
1347 switch(p->devep) {
1348 case 0:
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;
1353 // transfer OK
1354 } else {
1355 // ignore additionnal output
1357 break;
1358 case SETUP_STATE_DATA:
1359 if (!(d.setup_buf[0] & USB_DIR_IN)) {
1360 l = d.setup_len - d.setup_index;
1361 if (l > len)
1362 l = len;
1363 memcpy(d.data_buf + d.setup_index, data, l);
1364 d.setup_index += l;
1365 if (d.setup_index >= d.setup_len)
1366 d.setup_state = SETUP_STATE_ACK;
1367 ret = l;
1368 } else {
1369 d.setup_state = SETUP_STATE_IDLE;
1370 goto fail;
1372 break;
1373 default:
1374 goto fail;
1376 break;
1377 default:
1378 ret = handle_data(p);
1379 break;
1381 break;
1382 default:
1383 fail:
1384 ret = USB_RET_STALL;
1385 break;
1387 return ret;
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
1398 if (set) {
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);
1421 } else {
1422 BX_PANIC(("usb_param_handler called with unexpected parameter '%s'", pname));
1425 return val;
1428 #endif // BX_SUPPORT_PCI && BX_SUPPORT_PCIUSB