blob: fcc404bebde77a7336869dd99027e5f55fbca48a [file] [log] [blame]
Kevin O'Connor114592f2009-09-28 21:32:08 -04001// Main code for handling USB controllers and devices.
2//
3// Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
4//
5// This file may be distributed under the terms of the GNU LGPLv3 license.
6
7#include "util.h" // dprintf
8#include "pci.h" // foreachpci
9#include "config.h" // CONFIG_*
10#include "pci_regs.h" // PCI_CLASS_REVISION
11#include "pci_ids.h" // PCI_CLASS_SERIAL_USB_UHCI
12#include "usb-uhci.h" // uhci_init
Kevin O'Connor59f02832009-10-12 10:09:15 -040013#include "usb-ohci.h" // ohci_init
Kevin O'Connor114592f2009-09-28 21:32:08 -040014#include "usb-hid.h" // usb_keyboard_setup
15#include "usb.h" // struct usb_s
Kevin O'Connor1c46a542009-10-17 23:53:32 -040016#include "biosvar.h" // GET_GLOBAL
Kevin O'Connor114592f2009-09-28 21:32:08 -040017
18struct usb_s USBControllers[16] VAR16VISIBLE;
19
20static int
21send_control(u32 endp, int dir, const void *cmd, int cmdsize
22 , void *data, int datasize)
23{
Kevin O'Connor59f02832009-10-12 10:09:15 -040024 struct usb_s *cntl = endp2cntl(endp);
25 switch (cntl->type) {
26 default:
27 case USB_TYPE_UHCI:
28 return uhci_control(endp, dir, cmd, cmdsize, data, datasize);
29 case USB_TYPE_OHCI:
30 return ohci_control(endp, dir, cmd, cmdsize, data, datasize);
31 }
Kevin O'Connor114592f2009-09-28 21:32:08 -040032}
33
Kevin O'Connor59f02832009-10-12 10:09:15 -040034struct usb_pipe *
Kevin O'Connor114592f2009-09-28 21:32:08 -040035alloc_intr_pipe(u32 endp, int period)
36{
Kevin O'Connor59f02832009-10-12 10:09:15 -040037 struct usb_s *cntl = endp2cntl(endp);
38 switch (cntl->type) {
39 default:
40 case USB_TYPE_UHCI:
41 return uhci_alloc_intr_pipe(endp, period);
42 case USB_TYPE_OHCI:
43 return ohci_alloc_intr_pipe(endp, period);
44 }
Kevin O'Connor114592f2009-09-28 21:32:08 -040045}
46
47int
Kevin O'Connor59f02832009-10-12 10:09:15 -040048usb_poll_intr(struct usb_pipe *pipe, void *data)
Kevin O'Connor114592f2009-09-28 21:32:08 -040049{
Kevin O'Connor1c46a542009-10-17 23:53:32 -040050 u32 endp = GET_FLATPTR(pipe->endp);
51 struct usb_s *cntl = endp2cntl(endp);
52 switch (GET_GLOBAL(cntl->type)) {
Kevin O'Connor59f02832009-10-12 10:09:15 -040053 default:
54 case USB_TYPE_UHCI:
55 return uhci_poll_intr(pipe, data);
56 case USB_TYPE_OHCI:
57 return ohci_poll_intr(pipe, data);
58 }
Kevin O'Connor114592f2009-09-28 21:32:08 -040059}
60
61int
62send_default_control(u32 endp, const struct usb_ctrlrequest *req, void *data)
63{
64 return send_control(endp, req->bRequestType & USB_DIR_IN
65 , req, sizeof(*req), data, req->wLength);
66}
67
68// Get the first 8 bytes of the device descriptor.
69static int
70get_device_info8(struct usb_device_descriptor *dinfo, u32 endp)
71{
72 struct usb_ctrlrequest req;
73 req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
74 req.bRequest = USB_REQ_GET_DESCRIPTOR;
75 req.wValue = USB_DT_DEVICE<<8;
76 req.wIndex = 0;
77 req.wLength = 8;
78 return send_default_control(endp, &req, dinfo);
79}
80
81static struct usb_config_descriptor *
82get_device_config(u32 endp)
83{
84 struct usb_config_descriptor cfg;
85
86 struct usb_ctrlrequest req;
87 req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
88 req.bRequest = USB_REQ_GET_DESCRIPTOR;
89 req.wValue = USB_DT_CONFIG<<8;
90 req.wIndex = 0;
91 req.wLength = sizeof(cfg);
92 int ret = send_default_control(endp, &req, &cfg);
93 if (ret)
94 return NULL;
95
96 void *config = malloc_tmphigh(cfg.wTotalLength);
97 if (!config)
98 return NULL;
99 req.wLength = cfg.wTotalLength;
100 ret = send_default_control(endp, &req, config);
101 if (ret)
102 return NULL;
103 //hexdump(config, cfg.wTotalLength);
104 return config;
105}
106
107static u32
108set_address(u32 endp)
109{
110 dprintf(3, "set_address %x\n", endp);
111 struct usb_s *cntl = endp2cntl(endp);
112 if (cntl->maxaddr >= USB_MAXADDR)
113 return 0;
114
115 struct usb_ctrlrequest req;
116 req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
117 req.bRequest = USB_REQ_SET_ADDRESS;
118 req.wValue = cntl->maxaddr + 1;
119 req.wIndex = 0;
120 req.wLength = 0;
121 int ret = send_default_control(endp, &req, NULL);
122 if (ret)
123 return 0;
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400124 msleep(2);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400125
126 cntl->maxaddr++;
127 return mkendp(cntl, cntl->maxaddr, 0, endp2speed(endp), endp2maxsize(endp));
128}
129
130static int
131set_configuration(u32 endp, u16 val)
132{
133 struct usb_ctrlrequest req;
134 req.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE;
135 req.bRequest = USB_REQ_SET_CONFIGURATION;
136 req.wValue = val;
137 req.wIndex = 0;
138 req.wLength = 0;
139 return send_default_control(endp, &req, NULL);
140}
141
142// Called for every found device - see if a driver is available for
143// this device and do setup if so.
144int
145configure_usb_device(struct usb_s *cntl, int lowspeed)
146{
147 dprintf(1, "config_usb: %p %d\n", cntl, lowspeed);
148
149 // Get device info
150 u32 endp = mkendp(cntl, 0, 0, lowspeed, 8);
151 struct usb_device_descriptor dinfo;
152 int ret = get_device_info8(&dinfo, endp);
153 if (ret)
154 return 0;
155 dprintf(3, "device rev=%04x cls=%02x sub=%02x proto=%02x size=%02x\n"
156 , dinfo.bcdUSB, dinfo.bDeviceClass, dinfo.bDeviceSubClass
157 , dinfo.bDeviceProtocol, dinfo.bMaxPacketSize0);
158 if (dinfo.bMaxPacketSize0 < 8 || dinfo.bMaxPacketSize0 > 64)
159 return 0;
160 endp = mkendp(cntl, 0, 0, lowspeed, dinfo.bMaxPacketSize0);
161
162 // Get configuration
163 struct usb_config_descriptor *config = get_device_config(endp);
164 if (!config)
165 return 0;
166
167 // Determine if a driver exists for this device - only look at the
168 // first interface of the first configuration.
169 struct usb_interface_descriptor *iface = (void*)(&config[1]);
170 if (iface->bInterfaceClass != USB_CLASS_HID
171 || iface->bInterfaceSubClass != USB_INTERFACE_SUBCLASS_BOOT
172 || iface->bInterfaceProtocol != USB_INTERFACE_PROTOCOL_KEYBOARD)
173 // Not a "boot" keyboard
174 goto fail;
175
176 // Set the address and configure device.
177 endp = set_address(endp);
178 if (!endp)
179 goto fail;
180 ret = set_configuration(endp, config->bConfigurationValue);
181 if (ret)
182 goto fail;
183
184 // Configure driver.
185 ret = usb_keyboard_init(endp, iface, ((void*)config + config->wTotalLength
186 - (void*)iface));
187 if (ret)
188 goto fail;
189
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400190 free(config);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400191 return 1;
192fail:
Kevin O'Connor1c46a542009-10-17 23:53:32 -0400193 free(config);
Kevin O'Connor114592f2009-09-28 21:32:08 -0400194 return 0;
195}
196
197void
198usb_setup()
199{
200 if (! CONFIG_USB)
201 return;
202
203 dprintf(3, "init usb\n");
204
205 usb_keyboard_setup();
206
207 // Look for USB controllers
208 int count = 0;
209 int bdf, max;
210 foreachpci(bdf, max) {
211 u32 code = pci_config_readl(bdf, PCI_CLASS_REVISION) >> 8;
212
213 if (code >> 8 != PCI_CLASS_SERIAL_USB)
214 continue;
215
216 struct usb_s *cntl = &USBControllers[count];
217 cntl->bdf = bdf;
218
Kevin O'Connor114592f2009-09-28 21:32:08 -0400219 if (code == PCI_CLASS_SERIAL_USB_UHCI)
Kevin O'Connora5826b52009-10-24 17:57:29 -0400220 run_thread(uhci_init, cntl);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400221 else if (code == PCI_CLASS_SERIAL_USB_OHCI)
Kevin O'Connora5826b52009-10-24 17:57:29 -0400222 run_thread(ohci_init, cntl);
223 else
224 continue;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400225
Kevin O'Connora5826b52009-10-24 17:57:29 -0400226 count++;
227 if (count >= ARRAY_SIZE(USBControllers))
228 break;
Kevin O'Connor114592f2009-09-28 21:32:08 -0400229 }
230}