scsi request, part usb mass storage class driver, bulk transfers
[quarnos.git] / resources / uhci.cpp
blob73c21e3195a0b86bc1e63e7d6b55a06148a744cc
1 /* Quarn OS
3 * UHCI driver
5 * Copyright (C) 2009 Pawel Dziepak
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program 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
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "pci.h"
24 #include "usb.h"
25 #include "uhci.h"
26 //#include "manes/cds/
27 #include "manes/manec.h"
28 #include "manes/cds/root.h"
30 #include "arch/low/lowlevel.h"
31 //#include "irq.h"
32 #include "arch/low/hlt.h"
33 #include "manes/error.h"
34 #include "services/interrupt_manager.h"
36 using namespace resources;
38 int uhci_count = 0;
40 bool uhci::init_device(p<did> id) {
41 p<pci_did> pid = id.cast<pci_did>();
43 if (!pid.valid())
44 return false;
46 pciid = *pid;
48 uhci_count++;
49 init();
51 manes::manec::get()->get_root()->new_component(manes::cds::component_name::from_path("/type,usb")).cast<bus>()->init_bus(this);
53 return true;
57 bool uhci::check_device(p<did> id) {
58 p<pci_did> pid = id.cast<pci_did>();
60 if (!pid.valid())
61 return false;
63 int devclass = pid->get_bus().cast<pci>()->get_devclass(id);
64 if (devclass != 0x0c0300)
65 return false;
67 return true;
70 void *operator new(unsigned int, int);
71 void *operator new[](unsigned int, int);
74 #define UHCI_STOP 0
75 #define UHCI_RUN 1
76 #define UHCI_HCRESET 2
77 #define UHCI_GRESET 4
78 #define UHCI_EGSM 8
79 #define UHCI_FGR 0x10
80 #define UHCI_SWDGB 0x20
81 #define UHCI_CF 0x40
82 #define UHCI_MAXP 0x80
84 #define TD_TOKEN_DATATOGGLE (1 << 19)
86 #define TD_CONTROL_ACTIVE (1 << 23)
88 #define UHCI_INVALID 1
89 #define UHCI_VALID 0
90 #define UHCI_QUEUEHEAD 2
91 #define UHCI_TRANSFERDESC 0
93 void uhci::init_structures() {
94 /* new does not give aligned pointers */
95 /* Create TD */
96 td = new (16) uhci::transfer_descriptor;
97 td->link_ptr = UHCI_INVALID;
98 td->control = 0;
99 td->token = 0;
101 /* Create QH */
102 qh = new (16) queue_head;
103 qh->head_ptr = UHCI_INVALID;
104 qh->element_ptr = (u32)td | UHCI_TRANSFERDESC;
107 /* Create frame list pointer */
108 if (!fp)
109 fp = (frame_pointer*)new (4096) char[4096];
110 fp[last_fp++] = (u32)qh | UHCI_VALID | UHCI_QUEUEHEAD;
112 for (int i = last_fp; i < 1024; i++)
113 fp[i] = UHCI_INVALID;
115 /* give fp to the uhci */
116 if ((int)fp & 0xfff)
117 __asm__("cli\nhlt"::"a"(fp)); //critical("oh my cthulhu! frame list is not aligned!");
118 pciid.outl(uhci_flbaseadd, (u32)fp);
121 void uhci::irq() {
122 arch::unlock_irqs(pciid.irq);
123 __asm__("cli\nhlt"::"a"(0xdeadbeef));
126 /* bulk transfer */
127 void uhci::bulk_transfer(int pid, void *val, int length, int address, int endp) {
128 init_structures();
130 td->link_ptr = UHCI_INVALID;
131 td->buffer = (u32)val;
132 td->control = 1 << 26 | 1 << 24 | 1 << 23;
133 td->token = (length - 1) << 21 | pid | address << 8 | endp << 15;
135 pciid.outw(uhci_command, UHCI_RUN);
136 /* Wait until frame is completed */
137 while (!(pciid.inw(uhci_status) & 1));
138 pciid.outw(uhci_command, UHCI_STOP);
140 /* Clear uhci status */
141 pciid.outw(uhci_status, 0xffff);
144 /* control transfer */
145 void *uhci::control_transfer(int pid, void *val, int length, int rlength, u8 address) {
146 /* Control message */
147 init_structures();
149 /* setup stage */
150 td->link_ptr = (u32)new (16) transfer_descriptor;
151 td->buffer = (u32)val;
152 td->control = 1 << 26 | 1 << 23;
153 td->token = (length - 1) << 21 | pid | address << 8;
155 u8 *buffer = (u8*)0;
157 transfer_descriptor *status = (transfer_descriptor*)td->link_ptr;
159 /* optional data stage */
160 if (rlength > 0) {
161 buffer = (u8*)new char[rlength];
163 /* In message (get data from device) */
164 transfer_descriptor *rp = (transfer_descriptor *)td->link_ptr;
165 rp->link_ptr = (u32)new (16) transfer_descriptor;
166 status = (transfer_descriptor*)rp->link_ptr;
167 rp->buffer = (u32)buffer;
168 rp->control = 1 << 26 | 1 << 23;
169 rp->token = (rlength - 1) << 21 | usb::pid_in | address << 8;
172 /* status stage */
173 status->link_ptr = UHCI_INVALID;
174 status->buffer = 0;
175 status->control = 1 << 26 | 1 << 23 | 1 << 24;
176 if (rlength != 0)
177 status->token = 0x7ff << 21 | usb::pid_out | address << 8;
178 else
179 status->token = 0x7ff << 21 | usb::pid_in | address << 8;
181 pciid.outw(uhci_command, UHCI_RUN);
182 /* Wait until frame is completed */
183 while (!(pciid.inw(uhci_status) & 1));// __asm__("sti\nhlt");
184 pciid.outw(uhci_command, UHCI_STOP);
186 /* Clear uhci status */
187 pciid.outw(uhci_status, 0xffff);
189 return buffer;
192 int uhci::get_free_address() {
193 return 1;
196 void uhci::init() {
197 manes::manec::get()->get<services::interrupt_manager>("/interrupt_manager")->register_interrupt(pciid.irq, delegate<void>::method(this, &uhci::irq));
199 /* Reset UHCI */
200 pciid.outw(uhci_command, UHCI_STOP | UHCI_HCRESET);
201 while (pciid.inw(uhci_command) & UHCI_HCRESET);
203 /* Prepare structures */
204 // uhci_init_structures(pciaddr);
206 pciid.outw(4, 4);
208 /* Reset ports */
209 pciid.outw(uhci_portsc1, 0x200);
210 pciid.outw(uhci_portsc1 + 2, 0x200);
212 wait_loop();
214 pciid.outw(uhci_portsc1, 0);
215 pciid.outw(uhci_portsc1 + 2, 0);
218 void uhci::scan_bus(delegate<void, int> device_connected) {
219 /* Wait for connected devices */
220 u16 read1 = 0x80, read2 = 0x80;
222 if ((read1 = pciid.inw(uhci_portsc1)) == 0x80 && (read2 = pciid.inw(uhci_portsc1 + 2)) == 0x80)
223 return;
225 if (read1 != 0x80) {
226 /* Enable port */
227 pciid.outw(uhci_portsc1, 4);
228 device_connected(uhci_portsc1);
231 if (read2 != 0x80) {
232 /* Enable port */
233 pciid.outw(uhci_portsc1 + 2, 4);
234 device_connected(uhci_portsc1 + 2);
237 read2 = pciid.inw(uhci_portsc1+2);
241 void uhci::register_type() {
242 manes::manec::get()->register_driver<uhci>("uhci", "pci", delegate<bool, p<did> >::function(&uhci::check_device));