diff --git a/src/chipset/sis_5571.c b/src/chipset/sis_5571.c index 0f77a1a07..aa8ea62f3 100644 --- a/src/chipset/sis_5571.c +++ b/src/chipset/sis_5571.c @@ -83,6 +83,8 @@ typedef struct sis_5571_t { smram_t *smram; usb_t *usb; + usb_params_t usb_params; + } sis_5571_t; static void @@ -638,6 +640,36 @@ pci_isa_bridge_read(int func, int addr, void *priv) } } +static void +sis_5571_usb_raise_interrupt(usb_t* usb, void* priv) +{ + sis_5571_t *dev = (sis_5571_t *) priv; + + if (dev->pci_conf_sb[0][0x68] & 0x80) { + /* TODO: Is the normal PCI interrupt inhibited when USB IRQ remapping is enabled? */ + switch (dev->pci_conf_sb[0][0x68] & 0x0F) { + case 0x00: + case 0x01: + case 0x02: + case 0x08: + case 0x0d: + break; + default: + picint(1 << dev->pci_conf_sb[0][0x68] & 0x0F); + break; + } + } else { + pci_set_irq(dev->sb_pci_slot, PCI_INTA); + } +} + +static uint8_t +sis_5571_usb_handle_smi(usb_t* usb, void* priv) +{ + /* Left unimplemented for now. */ + return 1; +} + static void sis_5571_reset(void *priv) { @@ -722,7 +754,10 @@ sis_5571_init(const device_t *info) dev->ide_drive[1] = device_add_inst(&sff8038i_device, 2); /* USB */ - dev->usb = device_add(&usb_device); + dev->usb_params.parent_priv = dev; + dev->usb_params.raise_interrupt = sis_5571_usb_raise_interrupt; + dev->usb_params.smi_handle = sis_5571_usb_handle_smi; + dev->usb = device_add_parameters(&usb_device, &dev->usb_params); sis_5571_reset(dev); diff --git a/src/include/86box/usb.h b/src/include/86box/usb.h index cf5938a3d..ecb5c0f86 100644 --- a/src/include/86box/usb.h +++ b/src/include/86box/usb.h @@ -28,6 +28,8 @@ typedef struct usb_t usb_t; typedef struct { void (*raise_interrupt)(usb_t*, void*); + /* Handle (but do not raise) SMI. Returns 1 if SMI can be raised, 0 otherwise. */ + uint8_t (*smi_handle)(usb_t*, void*); void* parent_priv; } usb_params_t; @@ -54,6 +56,18 @@ typedef struct uint8_t bLength; uint8_t bDescriptorType; } usb_desc_base_t; + +typedef struct +{ + usb_desc_base_t base; + + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} usb_desc_conf_t; #pragma pack(pop) /* USB endpoint device struct. Incomplete and unused. */ @@ -72,12 +86,22 @@ typedef struct void* priv; } usb_device_t; +enum usb_bus_types +{ + USB_BUS_OHCI = 0, + USB_BUS_UHCI = 1 +}; + /* Global variables. */ extern const device_t usb_device; /* Functions. */ extern void uhci_update_io_mapping(usb_t *dev, uint8_t base_l, uint8_t base_h, int enable); extern void ohci_update_mem_mapping(usb_t *dev, uint8_t base1, uint8_t base2, uint8_t base3, int enable); +/* Attach USB device to a port of a USB bus. Returns the port to which it got attached to. */ +extern uint8_t usb_attach_device(usb_t *dev, usb_device_t* device, uint8_t bus_type); +/* Detach USB device from a port. */ +extern void usb_detach_device(usb_t *dev, uint8_t port, uint8_t bus_type); #ifdef __cplusplus } diff --git a/src/usb.c b/src/usb.c index 7dd8341c3..0951412d6 100644 --- a/src/usb.c +++ b/src/usb.c @@ -76,10 +76,25 @@ enum OHCI_HcRhPortStatus3 = 0x5C }; +/* OHCI HcInterruptEnable/Disable bits */ +enum +{ + OHCI_HcInterruptEnable_SO = 1 << 0, + OHCI_HcInterruptEnable_WDH = 1 << 1, + OHCI_HcInterruptEnable_SF = 1 << 2, + OHCI_HcInterruptEnable_RD = 1 << 3, + OHCI_HcInterruptEnable_UE = 1 << 4, + OHCI_HcInterruptEnable_HNO = 1 << 5, + OHCI_HcInterruptEnable_RHSC = 1 << 6, +}; + static void usb_interrupt_ohci(usb_t* usb) { if (usb->ohci_mmio[OHCI_HcControl + 1] & 1) { + if (usb->usb_params && usb->usb_params->smi_handle && !usb->usb_params->smi_handle(usb, usb->usb_params->parent_priv)) + return; + smi_raise(); } else if (usb->usb_params != NULL) { @@ -183,6 +198,25 @@ ohci_mmio_read(uint32_t addr, void *p) ret = dev->ohci_mmio[addr]; + switch (addr) { + case 0x101: + ret = (ret & 0xfe) | (!!mem_a20_key); + break; + case OHCI_HcRhPortStatus1 + 1: + case OHCI_HcRhPortStatus2 + 1: + case OHCI_HcRhPortStatus3 + 1: + ret |= 0x1; + break; + case OHCI_HcInterruptDisable: + case OHCI_HcInterruptDisable + 1: + case OHCI_HcInterruptDisable + 2: + case OHCI_HcInterruptDisable + 3: + ret = dev->ohci_mmio[OHCI_HcInterruptEnable + (addr - OHCI_HcInterruptDisable)]; + break; + default: + break; + } + if (addr == 0x101) ret = (ret & 0xfe) | (!!mem_a20_key); @@ -212,6 +246,7 @@ ohci_port_reset_callback(void* priv) usb_t *dev = (usb_t *) priv; dev->ohci_mmio[OHCI_HcRhPortStatus1] &= ~0x10; + dev->ohci_mmio[OHCI_HcRhPortStatus1 + 2] |= 0x10; } void @@ -220,6 +255,23 @@ ohci_port_reset_callback_2(void* priv) usb_t *dev = (usb_t *) priv; dev->ohci_mmio[OHCI_HcRhPortStatus2] &= ~0x10; + dev->ohci_mmio[OHCI_HcRhPortStatus2 + 2] |= 0x10; +} + +void +ohci_set_interrupt(usb_t* usb, uint8_t bit) +{ + if (!(usb->ohci_mmio[OHCI_HcInterruptEnable + 3] & 0x80)) + return; + + if (!(usb->ohci_mmio[OHCI_HcInterruptEnable] & bit)) + return; + + if (usb->ohci_mmio[OHCI_HcInterruptDisable] & bit) + return; + + usb->ohci_mmio[OHCI_HcInterruptStatus] |= bit; + usb_interrupt_ohci(usb); } static void @@ -256,6 +308,36 @@ ohci_mmio_write(uint32_t addr, uint8_t val, void *p) break; case OHCI_HcHCCA: return; + case OHCI_HcInterruptEnable: + dev->ohci_mmio[addr] = (val & 0x7f); + dev->ohci_mmio[OHCI_HcInterruptDisable] &= ~(val & 0x7f); + return; + case OHCI_HcInterruptEnable + 1: + case OHCI_HcInterruptEnable + 2: + return; + case OHCI_HcInterruptEnable + 3: + dev->ohci_mmio[addr] = (val & 0x40); + dev->ohci_mmio[addr] |= (val & 0x80); + if (val & 0x80) + dev->ohci_mmio[OHCI_HcInterruptDisable + 3] &= ~0x80; + if (val & 0x40) + dev->ohci_mmio[OHCI_HcInterruptDisable + 3] &= ~0x40; + return; + case OHCI_HcInterruptDisable: + dev->ohci_mmio[addr] = (val & 0x7f); + dev->ohci_mmio[OHCI_HcInterruptEnable] &= ~(val & 0x7f); + return; + case OHCI_HcInterruptDisable + 1: + case OHCI_HcInterruptDisable + 2: + return; + case OHCI_HcInterruptDisable + 3: + dev->ohci_mmio[addr] = (val & 0x40); + dev->ohci_mmio[addr] |= (val & 0x80); + if (val & 0x80) + dev->ohci_mmio[OHCI_HcInterruptEnable + 3] &= ~0x80; + if (val & 0x40) + dev->ohci_mmio[OHCI_HcInterruptEnable + 3] &= ~0x40; + return; case OHCI_HcInterruptStatus: dev->ohci_mmio[addr] &= ~(val & 0x7f); return; @@ -365,7 +447,6 @@ ohci_mmio_write(uint32_t addr, uint8_t val, void *p) if (old & 0x01) { dev->ohci_mmio[addr] |= 0x10; timer_on_auto(&dev->ohci_port_reset_timer[(addr - OHCI_HcRhPortStatus1) / 4], 10000.); - dev->ohci_mmio[addr + 2] |= 0x10; } else dev->ohci_mmio[addr + 2] |= 0x01; } @@ -416,6 +497,14 @@ ohci_mmio_write(uint32_t addr, uint8_t val, void *p) case OHCI_HcRhPortStatus1 + 3: case OHCI_HcRhPortStatus2 + 3: return; + case OHCI_HcDoneHead: + case OHCI_HcBulkCurrentED: + case OHCI_HcBulkHeadED: + case OHCI_HcControlCurrentED: + case OHCI_HcControlHeadED: + case OHCI_HcPeriodCurrentED: + dev->ohci_mmio[addr] = (val & 0xf0); + return; } dev->ohci_mmio[addr] = val; @@ -433,6 +522,18 @@ ohci_update_mem_mapping(usb_t *dev, uint8_t base1, uint8_t base2, uint8_t base3, mem_mapping_set_addr(&dev->ohci_mmio_mapping, dev->ohci_mem_base, 0x1000); } +uint8_t +usb_attach_device(usb_t *dev, usb_device_t* device, uint8_t bus_type) +{ + return 255; +} + +void +usb_detach_device(usb_t *dev, uint8_t port, uint8_t bus_type) +{ + /* Unused. */ +} + static void usb_reset(void *priv) { @@ -446,6 +547,7 @@ usb_reset(void *priv) dev->ohci_mmio[OHCI_HcRevision] = 0x10; dev->ohci_mmio[OHCI_HcRevision + 1] = 0x01; dev->ohci_mmio[OHCI_HcRhDescriptorA] = 0x02; + dev->ohci_mmio[OHCI_HcRhDescriptorA + 1] = 0x02; io_removehandler(dev->uhci_io_base, 0x20, uhci_reg_read, NULL, NULL, uhci_reg_write, uhci_reg_writew, NULL, dev); dev->uhci_enable = 0;