]>
Commit | Line | Data |
---|---|---|
34d97308 TH |
1 | /* |
2 | * QEMU USB OHCI Emulation | |
3 | * Copyright (c) 2004 Gianni Tedesco | |
4 | * Copyright (c) 2006 CodeSourcery | |
5 | * Copyright (c) 2006 Openedhand Ltd. | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include "qemu/osdep.h" | |
34d97308 TH |
22 | #include "qapi/error.h" |
23 | #include "qemu/timer.h" | |
24 | #include "hw/usb.h" | |
d6454270 | 25 | #include "migration/vmstate.h" |
34d97308 TH |
26 | #include "hw/pci/pci.h" |
27 | #include "hw/sysbus.h" | |
28 | #include "hw/qdev-dma.h" | |
a27bd6c7 | 29 | #include "hw/qdev-properties.h" |
34d97308 TH |
30 | #include "trace.h" |
31 | #include "hcd-ohci.h" | |
db1015e9 | 32 | #include "qom/object.h" |
34d97308 TH |
33 | |
34 | #define TYPE_PCI_OHCI "pci-ohci" | |
db1015e9 | 35 | typedef struct OHCIPCIState OHCIPCIState; |
34d97308 TH |
36 | #define PCI_OHCI(obj) OBJECT_CHECK(OHCIPCIState, (obj), TYPE_PCI_OHCI) |
37 | ||
db1015e9 | 38 | struct OHCIPCIState { |
34d97308 TH |
39 | /*< private >*/ |
40 | PCIDevice parent_obj; | |
41 | /*< public >*/ | |
42 | ||
43 | OHCIState state; | |
44 | char *masterbus; | |
45 | uint32_t num_ports; | |
46 | uint32_t firstport; | |
db1015e9 | 47 | }; |
34d97308 TH |
48 | |
49 | /** | |
50 | * A typical PCI OHCI will additionally set PERR in its configspace to | |
51 | * signal that it got an error. | |
52 | */ | |
53 | static void ohci_pci_die(struct OHCIState *ohci) | |
54 | { | |
55 | OHCIPCIState *dev = container_of(ohci, OHCIPCIState, state); | |
56 | ||
57 | ohci_sysbus_die(ohci); | |
58 | ||
59 | pci_set_word(dev->parent_obj.config + PCI_STATUS, | |
60 | PCI_STATUS_DETECTED_PARITY); | |
61 | } | |
62 | ||
63 | static void usb_ohci_realize_pci(PCIDevice *dev, Error **errp) | |
64 | { | |
65 | Error *err = NULL; | |
66 | OHCIPCIState *ohci = PCI_OHCI(dev); | |
67 | ||
68 | dev->config[PCI_CLASS_PROG] = 0x10; /* OHCI */ | |
69 | dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ | |
70 | ||
71 | usb_ohci_init(&ohci->state, DEVICE(dev), ohci->num_ports, 0, | |
72 | ohci->masterbus, ohci->firstport, | |
73 | pci_get_address_space(dev), ohci_pci_die, &err); | |
74 | if (err) { | |
75 | error_propagate(errp, err); | |
76 | return; | |
77 | } | |
78 | ||
79 | ohci->state.irq = pci_allocate_irq(dev); | |
80 | pci_register_bar(dev, 0, 0, &ohci->state.mem); | |
81 | } | |
82 | ||
83 | static void usb_ohci_exit(PCIDevice *dev) | |
84 | { | |
85 | OHCIPCIState *ohci = PCI_OHCI(dev); | |
86 | OHCIState *s = &ohci->state; | |
87 | ||
88 | trace_usb_ohci_exit(s->name); | |
89 | ohci_bus_stop(s); | |
90 | ||
91 | if (s->async_td) { | |
92 | usb_cancel_packet(&s->usb_packet); | |
93 | s->async_td = 0; | |
94 | } | |
95 | ohci_stop_endpoints(s); | |
96 | ||
97 | if (!ohci->masterbus) { | |
98 | usb_bus_release(&s->bus); | |
99 | } | |
100 | ||
101 | timer_del(s->eof_timer); | |
102 | timer_free(s->eof_timer); | |
103 | } | |
104 | ||
105 | static void usb_ohci_reset_pci(DeviceState *d) | |
106 | { | |
107 | PCIDevice *dev = PCI_DEVICE(d); | |
108 | OHCIPCIState *ohci = PCI_OHCI(dev); | |
109 | OHCIState *s = &ohci->state; | |
110 | ||
111 | ohci_hard_reset(s); | |
112 | } | |
113 | ||
114 | static Property ohci_pci_properties[] = { | |
115 | DEFINE_PROP_STRING("masterbus", OHCIPCIState, masterbus), | |
116 | DEFINE_PROP_UINT32("num-ports", OHCIPCIState, num_ports, 3), | |
117 | DEFINE_PROP_UINT32("firstport", OHCIPCIState, firstport, 0), | |
118 | DEFINE_PROP_END_OF_LIST(), | |
119 | }; | |
120 | ||
121 | static const VMStateDescription vmstate_ohci = { | |
122 | .name = "ohci", | |
123 | .version_id = 1, | |
124 | .minimum_version_id = 1, | |
125 | .fields = (VMStateField[]) { | |
126 | VMSTATE_PCI_DEVICE(parent_obj, OHCIPCIState), | |
127 | VMSTATE_STRUCT(state, OHCIPCIState, 1, vmstate_ohci_state, OHCIState), | |
128 | VMSTATE_END_OF_LIST() | |
129 | } | |
130 | }; | |
131 | ||
132 | static void ohci_pci_class_init(ObjectClass *klass, void *data) | |
133 | { | |
134 | DeviceClass *dc = DEVICE_CLASS(klass); | |
135 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); | |
136 | ||
137 | k->realize = usb_ohci_realize_pci; | |
138 | k->exit = usb_ohci_exit; | |
139 | k->vendor_id = PCI_VENDOR_ID_APPLE; | |
140 | k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB; | |
141 | k->class_id = PCI_CLASS_SERIAL_USB; | |
142 | set_bit(DEVICE_CATEGORY_USB, dc->categories); | |
143 | dc->desc = "Apple USB Controller"; | |
4f67d30b | 144 | device_class_set_props(dc, ohci_pci_properties); |
34d97308 TH |
145 | dc->hotpluggable = false; |
146 | dc->vmsd = &vmstate_ohci; | |
147 | dc->reset = usb_ohci_reset_pci; | |
148 | } | |
149 | ||
150 | static const TypeInfo ohci_pci_info = { | |
151 | .name = TYPE_PCI_OHCI, | |
152 | .parent = TYPE_PCI_DEVICE, | |
153 | .instance_size = sizeof(OHCIPCIState), | |
154 | .class_init = ohci_pci_class_init, | |
155 | .interfaces = (InterfaceInfo[]) { | |
156 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, | |
157 | { }, | |
158 | }, | |
159 | }; | |
160 | ||
161 | static void ohci_pci_register_types(void) | |
162 | { | |
163 | type_register_static(&ohci_pci_info); | |
164 | } | |
165 | ||
166 | type_init(ohci_pci_register_types) |