includes: AROS_UFIxx -> AROS_INTxx change
[AROS.git] / workbench / devs / USB / drivers / UHCI / uhciclass.c
blobedcbeddbaa508694d2b41f2c188f25122d93fd24
1 /*
2 Copyright (C) 2006 by Michal Schulz
3 $Id$
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with this program; if not, write to the
17 Free Software Foundation, Inc.,
18 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #define DEBUG 0
23 #include <inttypes.h>
25 #include <aros/symbolsets.h>
27 #include <aros/debug.h>
29 #include <exec/types.h>
30 #include <exec/lists.h>
31 #include <exec/interrupts.h>
32 #include <exec/errors.h>
33 #include <oop/oop.h>
35 #include <hidd/hidd.h>
36 #include <hidd/pci.h>
38 #include <usb/usb.h>
40 #include <asm/io.h>
42 #include <proto/oop.h>
43 #include <proto/utility.h>
45 #include <dos/dos.h>
47 #include <string.h>
48 #include <stdio.h>
50 #include "uhci.h"
52 OOP_AttrBase HiddUHCIAttrBase;
54 static AROS_INTH1(HubInterrupt, UHCIData *, uhci)
56 AROS_INTFUNC_INIT
58 /* Signal the HUB process about incoming interrupt */
59 uint8_t sts = 0;
60 struct Interrupt *intr;
62 /* Remove itself from msg list */
63 GetMsg(&uhci->mport);
65 if (uhci->timereq->tr_node.io_Error == IOERR_ABORTED)
67 D(bug("[UHCI] INTR Aborted\n"));
68 return FALSE;
71 if (inw(uhci->iobase + UHCI_PORTSC1) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))
72 sts |= 1;
73 if (inw(uhci->iobase + UHCI_PORTSC2) & (UHCI_PORTSC_CSC|UHCI_PORTSC_OCIC))
74 sts |= 2;
76 D(bug("[UHCI.%04x] Status on port: 1=%04x, 2=%04x\n", uhci->iobase, inw(uhci->iobase + UHCI_PORTSC1), inw(uhci->iobase + UHCI_PORTSC2)));
78 if (sts & 1)
79 (bug("[UHCI] Status change on port 1\n"));
80 if (sts & 2)
81 (bug("[UHCI] Status change on port 2\n"));
83 if (sts && uhci->running)
85 D(bug("Causing interrupts from list %p\n", &uhci->intList));
86 ForeachNode(&uhci->intList, intr)
88 Cause(intr);
92 uhci->timereq->tr_node.io_Command = TR_ADDREQUEST;
93 uhci->timereq->tr_time.tv_secs = 0;
94 uhci->timereq->tr_time.tv_micro = 255000;
95 SendIO((struct IORequest *)uhci->timereq);
97 return FALSE;
99 AROS_INTFUNC_EXIT
102 static AROS_INTH1(uhci_Handler, UHCIData *, uhci)
104 AROS_INTFUNC_INIT
106 UHCI_Pipe *p;
108 uint16_t status = inw(uhci->iobase + UHCI_STS);
109 uint16_t frame = inw(uhci->iobase + UHCI_FRNUM);
110 uint16_t port1 = inw(uhci->iobase + UHCI_PORTSC1);
111 uint16_t port2 = inw(uhci->iobase + UHCI_PORTSC2);
112 uint16_t cmd = inw(uhci->iobase + UHCI_CMD);
113 uint16_t sof = inb(uhci->iobase + UHCI_SOF);
115 (void)frame; //unused;
116 (void)port1; // unused
117 (void)port2; // unused
118 (void)sof; // unused;
119 (void)cmd; // unused;
121 /* Check if there's really an interrupt for us */
122 if((status & UHCI_STS_USBINT) == 0)
124 outw(status, uhci->iobase + UHCI_STS);
125 return FALSE;
128 D(bug("[UHCI] INTR Cmd=%04x, SOF=%04x, Status = %04x, Frame=%04x, PortSC1=%04x, PortSC2=%04x\n",
129 cmd, sof, status, frame, port1, port2));
131 if (!IsListEmpty(&uhci->Interrupts))
133 Disable();
134 ForeachNode(&uhci->Interrupts, p)
136 /* If the Linkptr of queue does not point to the first transfer descriptor, an interrupt
137 * has occured. Unfortunately, only the first one, who has added the interrupt to this
138 * pipe may receive data */
139 if ((p->p_Queue->qh_VLink & 0xfffffff1) != (uint32_t)p->p_FirstTD)
141 struct UHCI_Interrupt *intr;
143 if (!(p->p_FirstTD->td_Status & UHCI_TD_NAK))
145 D(bug("[UHCI] Interrupt!!! pipe %p, vlink=%p, FirstTD=%p\n", p, p->p_Queue->qh_VLink, p->p_FirstTD));
146 ForeachNode(&p->p_Intr, intr)
148 D(bug("[UHCI] Issuing interrupt %p\n", intr));
149 Cause(intr->i_intr);
153 /* Reactivate the transfer descriptor */
154 p->p_FirstTD->td_Status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3)
155 | UHCI_TD_ACTIVE
156 | UHCI_TD_IOC);
158 p->p_FirstTD->td_Token ^= 1 << 19;
160 if (p->p_FullSpeed)
161 p->p_FirstTD->td_Status |= UHCI_TD_SPD;
162 else
163 p->p_FirstTD->td_Status |= UHCI_TD_LS | UHCI_TD_SPD;
165 /* Link the first TD to the Queue header */
166 p->p_Queue->qh_VLink = (uint32_t)p->p_FirstTD | UHCI_PTR_TD;
169 Enable();
172 if (!IsListEmpty(&uhci->ControlLS))
174 Disable();
175 ForeachNode(&uhci->ControlLS, p)
177 UHCI_TransferDesc *td = (UHCI_TransferDesc *)p->p_FirstTD;
178 BOOL changed = FALSE;
180 if (p->p_Queue->qh_VLink == UHCI_PTR_T)
181 p->p_LastTD = (APTR)UHCI_PTR_T;
184 UHCI_TransferDesc *t = (UHCI_TransferDesc *)(p->p_Queue->qh_VLink & 0xfffffff1);
185 bug("[UHCI] Control Low: p->p_Queue->qh_VLink=%p\n", p->p_Queue->qh_VLink);
186 while ((uint32_t)t != UHCI_PTR_T)
188 bug("[UHCI] TD=%p (%08x %08x %08x %08x)\n", t,
189 t->td_LinkPtr, t->td_Status, t->td_Token, t->td_Buffer);
190 t = (UHCI_TransferDesc *)(t->td_LinkPtr & 0xfffffff1);
194 while ((uint32_t)td != (p->p_Queue->qh_VLink & 0xfffffff1))
196 D(bug("[UHCI] TD=%p\n", td));
197 UHCI_TransferDesc *tnext = (UHCI_TransferDesc *)(td->td_LinkPtr & 0xfffffff1);
198 uhci_FreeTDQuick(uhci, td);
199 td = tnext;
200 p->p_FirstTD = tnext;
201 changed = TRUE;
204 if (changed && p->p_Queue->qh_VLink == UHCI_PTR_T) {
205 D(bug("[UHCI] INTR Control pipe %p empty (slow)\n", p));
206 p->p_ErrorCode = 0;
208 if (p->p_SigTask)
209 Signal(p->p_SigTask, 1 << p->p_Signal);
212 Enable();
215 if (!IsListEmpty(&uhci->ControlFS))
217 Disable();
218 ForeachNode(&uhci->ControlFS, p)
220 UHCI_TransferDesc *td = (UHCI_TransferDesc *)p->p_FirstTD;
221 BOOL changed = FALSE;
223 if (p->p_Queue->qh_VLink & UHCI_PTR_T)
224 p->p_LastTD = (APTR)UHCI_PTR_T;
227 UHCI_TransferDesc *t = (UHCI_TransferDesc *)(p->p_Queue->qh_VLink & 0xfffffff1);
228 bug("[UHCI] Control Fast: p->p_Queue->qh_VLink=%p\n", p->p_Queue->qh_VLink);
229 while (!((uint32_t)t & UHCI_PTR_T))
231 bug("[UHCI] TD=%p (%08x %08x %08x %08x)\n", t,
232 t->td_LinkPtr, t->td_Status, t->td_Token, t->td_Buffer);
233 t = (UHCI_TransferDesc *)(t->td_LinkPtr & 0xfffffff1);
238 while ((uint32_t)td != (p->p_Queue->qh_VLink & 0xfffffff1))
240 D(bug("[UHCI] TD=%p\n", td));
242 UHCI_TransferDesc *tnext = (UHCI_TransferDesc *)(td->td_LinkPtr & 0xfffffff1);
243 uhci_FreeTDQuick(uhci, td);
244 td = tnext;
245 p->p_FirstTD = tnext;
246 changed = TRUE;
249 if (changed && p->p_Queue->qh_VLink == UHCI_PTR_T) {
250 D(bug("[UHCI] INTR Control pipe %p empty\n", p));
251 p->p_ErrorCode = 0;
253 if (p->p_SigTask)
254 Signal(p->p_SigTask, 1 << p->p_Signal);
256 /* Free the unused TD's */
258 Enable();
261 if (!IsListEmpty(&uhci->Bulk))
263 Disable();
264 ForeachNode(&uhci->Bulk, p)
266 UHCI_TransferDesc *td = (UHCI_TransferDesc *)p->p_FirstTD;
267 BOOL changed = FALSE;
269 if (p->p_Queue->qh_VLink == UHCI_PTR_T)
270 p->p_LastTD = (APTR)UHCI_PTR_T;
273 UHCI_TransferDesc *t = (UHCI_TransferDesc *)(p->p_Queue->qh_VLink & 0xfffffff1);
274 bug("[UHCI] Bulk: p=%p, p->p_Queue->qh_VLink=%p\n", p, p->p_Queue->qh_VLink);
275 while ((uint32_t)t != UHCI_PTR_T)
277 bug("[UHCI] TD=%p (%08x %08x %08x %08x)\n", t,
278 t->td_LinkPtr, t->td_Status, t->td_Token, t->td_Buffer);
279 t = (UHCI_TransferDesc *)(t->td_LinkPtr & 0xfffffff1);
283 while ((uint32_t)td != (p->p_Queue->qh_VLink & 0xfffffff1))
285 UHCI_TransferDesc *tnext = (UHCI_TransferDesc *)(td->td_LinkPtr & 0xfffffff1);
286 uhci_FreeTDQuick(uhci, td);
287 td = tnext;
288 p->p_FirstTD = tnext;
289 changed = TRUE;
292 if (changed && p->p_Queue->qh_VLink == UHCI_PTR_T) {
293 D(bug("[UHCI] INTR Bulk pipe %p empty\n", p));
294 p->p_ErrorCode = 0;
296 if (p->p_SigTask)
297 Signal(p->p_SigTask, 1 << p->p_Signal);
300 Enable();
303 outw(status, uhci->iobase + UHCI_STS);
305 return FALSE;
307 AROS_INTFUNC_EXIT
310 OOP_Object *METHOD(UHCI, Root, New)
312 D(bug("[UHCI] UHCI::New()\n"));
314 BASE(cl->UserData)->LibNode.lib_OpenCnt++;
316 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg) msg);
317 if (o)
319 int i;
320 UHCIData *uhci = OOP_INST_DATA(cl, o);
322 NEWLIST(&uhci->mport.mp_MsgList);
323 uhci->mport.mp_Flags = PA_SOFTINT;
324 uhci->mport.mp_Node.ln_Type = NT_MSGPORT;
325 uhci->mport.mp_SigTask = &uhci->timerint;
326 uhci->timerint.is_Code = (VOID_FUNC)HubInterrupt;
327 uhci->timerint.is_Data = uhci;
329 uhci->timereq = CreateIORequest(&uhci->mport, sizeof(struct timerequest));
330 OpenDevice((STRPTR)"timer.device", UNIT_VBLANK, (struct IORequest *)uhci->timereq, 0);
332 uhci->sd = SD(cl);
334 NEWLIST(&uhci->Isochronous);
335 NEWLIST(&uhci->Interrupts);
336 NEWLIST(&uhci->ControlLS);
337 NEWLIST(&uhci->ControlFS);
338 NEWLIST(&uhci->Bulk);
340 NEWLIST(&uhci->intList);
342 if (uhci->tmp)
343 AddTail(&uhci->intList, (struct Node *)uhci->tmp);
345 uhci->tr = (struct timerequest *)CreateIORequest(
346 CreateMsgPort(), sizeof(struct timerequest));
348 FreeSignal(uhci->tr->tr_node.io_Message.mn_ReplyPort->mp_SigBit);
349 uhci->tr->tr_node.io_Message.mn_ReplyPort->mp_SigBit = SIGBREAKB_CTRL_D;
350 OpenDevice((STRPTR)"timer.device", UNIT_MICROHZ, (struct IORequest *)uhci->tr, 0);
352 uhci->iobase = GetTagData(aHidd_UHCI_IOBase, 0, msg->attrList);
353 uhci->device = (OOP_Object *)GetTagData(aHidd_UHCI_PCIDevice, 0, msg->attrList);
354 uhci->pciDriver = (OOP_Object *)GetTagData(aHidd_UHCI_PCIDriver, 0, msg->attrList);
357 uint32_t base = inw(uhci->iobase + UHCI_FLBASEADDR);
358 uint16_t status = inw(uhci->iobase + UHCI_STS);
359 uint16_t frame = inw(uhci->iobase + UHCI_FRNUM);
360 uint16_t port1 = inw(uhci->iobase + UHCI_PORTSC1);
361 uint16_t port2 = inw(uhci->iobase + UHCI_PORTSC2);
362 uint16_t cmd = inw(uhci->iobase + UHCI_CMD);
363 uint8_t sof = inb(uhci->iobase + UHCI_SOF);
364 int i;
366 bug("[UHCI] Initial state: Base=%08x Cmd=%04x SOF=%02x Status=%04x Frame=%04x PortSC1=%04x PortSC2=%04x\n",
367 base, cmd, sof, status, frame, port1, port2);
369 /* Windows-like BIOS detach procedure */
370 bug("[UHCI] Stopping UHCI\n");
373 * Clearing RS bit will stop the controller. Clearing CF bit will tell potential
374 * legacy USB BIOS that AROS takes over
376 outw(inw(uhci->iobase + UHCI_CMD) & ~(UHCI_CMD_RS | UHCI_CMD_CF), uhci->iobase + UHCI_CMD);
378 for (i=0; i < 10; i++)
380 uhci_sleep(cl, o, 1);
381 if (inw(uhci->iobase + UHCI_STS) & UHCI_STS_HCH)
383 bug("[UHCI] Host controller halted\n");
384 break;
388 struct pHidd_PCIDevice_WriteConfigWord wcw;
389 struct pHidd_PCIDevice_ReadConfigWord rcw;
391 wcw.mID = OOP_GetMethodID((STRPTR)IID_Hidd_PCIDevice, moHidd_PCIDevice_WriteConfigWord);
392 wcw.reg = PCI_LEGSUP;
393 rcw.mID = OOP_GetMethodID((STRPTR)IID_Hidd_PCIDevice, moHidd_PCIDevice_ReadConfigWord);
394 rcw.reg = PCI_LEGSUP;
396 uint16_t reg = OOP_DoMethod(uhci->device, (OOP_Msg)&rcw.mID);
397 bug("[UHCI] PCI_LEGSUP=%04x -> ", reg);
398 reg &= 0xffef;
399 wcw.val = reg;
400 bug("%04x\n", reg);
401 OOP_DoMethod(uhci->device, (OOP_Msg)&wcw.mID);
402 reg = OOP_DoMethod(uhci->device, (OOP_Msg)&rcw.mID);
403 bug("[UHCI] PCI_LEGSUP=%04x\n", reg);
404 wcw.val = 0x2000;
405 OOP_DoMethod(uhci->device, (OOP_Msg)&wcw.mID);
406 reg = OOP_DoMethod(uhci->device, (OOP_Msg)&rcw.mID);
407 bug("[UHCI] PCI_LEGSUP=%04x\n", reg);
412 OOP_GetAttr(uhci->device, aHidd_PCIDevice_INTLine, &uhci->irq);
414 D(bug("[UHCI] New driver with IOBase=%x, IRQ=%d, PCI device=%08x, PCI driver=%08x\n",
415 uhci->iobase, uhci->irq, uhci->device, uhci->pciDriver));
417 uhci->Frame = HIDD_PCIDriver_AllocPCIMem(uhci->pciDriver, 4096);
418 D(bug("[UHCI] Frame = %08x\n", uhci->Frame));
420 outl((uint32_t)uhci->Frame, uhci->iobase + UHCI_FLBASEADDR);
422 uhci->irqHandler.is_Node.ln_Type = NT_INTERRUPT;
423 uhci->irqHandler.is_Node.ln_Name = "UHCI Intr";
424 uhci->irqHandler.is_Node.ln_Pri = 127;
425 uhci->irqHandler.is_Code = (VOID_FUNC)uhci_Handler;
426 uhci->irqHandler.is_Data = uhci;
428 AddIntServer(INTB_KERNEL + uhci->irq, &uhci->irqHandler);
429 D(bug("[UHCI] IRQHandler = %08x\n", &uhci->irqHandler));
431 struct pHidd_PCIDevice_WriteConfigWord __msg = {
432 OOP_GetMethodID((STRPTR)IID_Hidd_PCIDevice, moHidd_PCIDevice_WriteConfigWord), 0xc0, 0x8f00
433 }, *msg = &__msg;
435 OOP_DoMethod(uhci->device, (OOP_Msg)msg);
437 D(bug("[UHCI] Preparing initial QH tree\n"));
439 uhci->dummy_td = uhci_AllocTD(cl, o);
440 uhci->dummy_td->td_Buffer = 0;
441 uhci->dummy_td->td_LinkPtr = 0;
442 uhci->dummy_td->td_Status = 0;
443 uhci->dummy_td->td_Token = 0;
445 uhci->qh01 = uhci_AllocQH(cl, o);
446 uhci->qh01->qh_VLink = UHCI_PTR_T;
447 uhci->qh01->qh_HLink = UHCI_PTR_T;
449 for (i=0; i < 2; i++)
451 uhci->qh02[i] = uhci_AllocQH(cl, o);
452 uhci->qh02[i]->qh_VLink = UHCI_PTR_T;
453 uhci->qh02[i]->qh_HLink = UHCI_PTR_QH | (uint32_t)uhci->qh01;
456 for (i=0; i < 4; i++)
458 uhci->qh04[i] = uhci_AllocQH(cl, o);
459 uhci->qh04[i]->qh_VLink = UHCI_PTR_T;
460 uhci->qh04[i]->qh_HLink = UHCI_PTR_QH | (uint32_t)uhci->qh02[i & 0x01];
463 for (i=0; i < 8; i++)
465 uhci->qh08[i] = uhci_AllocQH(cl, o);
466 uhci->qh08[i]->qh_VLink = UHCI_PTR_T;
467 uhci->qh08[i]->qh_HLink = UHCI_PTR_QH | (uint32_t)uhci->qh04[i & 0x03];
470 for (i=0; i < 16; i++)
472 uhci->qh16[i] = uhci_AllocQH(cl, o);
473 uhci->qh16[i]->qh_VLink = UHCI_PTR_T;
474 uhci->qh16[i]->qh_HLink = UHCI_PTR_QH | (uint32_t)uhci->qh08[i & 0x07];
477 for (i=0; i < 32; i++)
479 uhci->qh32[i] = uhci_AllocQH(cl, o);
480 uhci->qh32[i]->qh_VLink = UHCI_PTR_T;
481 uhci->qh32[i]->qh_HLink = UHCI_PTR_QH | (uint32_t)uhci->qh16[i & 0x0f];
484 for (i=0; i < 1024; i++) {
485 uhci->Frame[i] = UHCI_PTR_QH | (uint32_t)uhci->qh32[i % 0x1f];
488 D(bug("[UHCI] Enabling the controller\n"));
490 outw(0, uhci->iobase + UHCI_INTR);
491 uhci_globalreset(cl, o);
492 uhci_reset(cl, o);
494 struct pHidd_PCIDevice_WriteConfigWord __msg2 = {
495 OOP_GetMethodID((STRPTR)IID_Hidd_PCIDevice, moHidd_PCIDevice_WriteConfigWord), 0xc0, 0x2000
496 }, *msg2 = &__msg2;
498 OOP_DoMethod(uhci->device, (OOP_Msg)msg2);
500 outb(0x40, uhci->iobase + UHCI_SOF);
502 outw(0, uhci->iobase + UHCI_FRNUM);
503 outl((uint32_t)uhci->Frame, uhci->iobase + UHCI_FLBASEADDR);
505 outw(UHCI_CMD_MAXP | UHCI_CMD_CF, uhci->iobase + UHCI_CMD);
507 outw(UHCI_INTR_SPIE | UHCI_INTR_IOCE | UHCI_INTR_RIE | UHCI_INTR_TOCRCIE,
508 uhci->iobase + UHCI_INTR );
512 D(bug("[UHCI] UHCI::New() = %p\n",o));
514 if (!o)
515 BASE(cl->UserData)->LibNode.lib_OpenCnt--;
517 return o;
520 void METHOD(UHCI, Root, Dispose)
522 UHCIData *uhci = OOP_INST_DATA(cl, o);
523 struct uhcibase *base = BASE(cl->UserData);
525 D(bug("[UHCI] UHCI::Dispose\n"));
527 D(bug("[UHCI] Stopping USB transfer\n"));
528 outw(0, uhci->iobase + UHCI_CMD);
530 D(bug("[UHCI] Releasing USB ring\n"));
531 HIDD_PCIDriver_FreePCIMem(uhci->pciDriver, uhci->Frame);
533 D(bug("[UHCI] Removing IRQ handler\n"));
534 RemIntServer(INTB_KERNEL + uhci->irq, &uhci->irqHandler);
536 AbortIO((struct IORequest *)uhci->timereq);
537 DeleteIORequest((struct IORequest *)uhci->timereq);
539 struct MsgPort *port = uhci->tr->tr_node.io_Message.mn_ReplyPort;
540 port->mp_SigTask = FindTask(NULL);
541 CloseDevice((struct IORequest *)uhci->tr);
542 DeleteIORequest((struct IORequest *)uhci->tr);
543 port->mp_SigBit = AllocSignal(-1);
544 DeleteMsgPort(port);
546 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
548 base->LibNode.lib_OpenCnt--;
551 void METHOD(UHCI, Root, Get)
553 uint32_t idx;
555 if (IS_USBDEVICE_ATTR(msg->attrID, idx))
557 switch (idx)
559 case aoHidd_USBDevice_Address:
560 *msg->storage = 1;
561 break;
562 case aoHidd_USBDevice_Hub:
563 *msg->storage = 0;
564 break;
565 case aoHidd_USBDevice_Bus:
566 *msg->storage = (IPTR)o;
567 break;
568 default:
569 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
572 else if (IS_USBDRV_ATTR(msg->attrID, idx))
576 else
577 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
580 void METHOD(UHCI, Root, Set)
582 uint32_t idx;
583 struct TagItem *tag;
584 struct TagItem *tags = msg->attrList;
586 while ((tag = NextTagItem(&tags)))
588 if (IS_USBDEVICE_ATTR(tag->ti_Tag, idx))
591 else if (IS_USBHUB_ATTR(tag->ti_Tag, idx))
595 else if (IS_USBDRV_ATTR(tag->ti_Tag, idx))
600 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
604 APTR METHOD(UHCI, Hidd_USBDrv, CreatePipe)
606 return uhci_CreatePipe(cl, o, msg->type, msg->fullspeed, msg->address, msg->endpoint, msg->period, msg->maxpacket, msg->timeout);
609 void METHOD(UHCI, Hidd_USBDrv, DeletePipe)
611 return uhci_DeletePipe(cl, o, msg->pipe);
614 void METHOD(UHCI, Hidd_USBDrv, SetTimeout)
616 UHCI_Pipe *p = msg->pipe;
618 if (p)
619 p->p_TimeoutVal = msg->timeout;
622 BOOL METHOD(UHCI, Hidd_USBDrv, AddInterrupt)
624 BOOL retval = FALSE;
625 UHCI_Pipe *p = msg->pipe;
627 if (msg->pipe == (APTR)0xdeadbeef)
629 UHCIData *uhci = OOP_INST_DATA(cl, o);
630 D(bug("[UHCI] AddInterrupt() local for the UHCI. Intr %p, list %p\n", msg->interrupt, &uhci->intList));
632 if (!uhci->iobase)
633 uhci->tmp = msg->interrupt;
634 else
635 AddTail(&uhci->intList, &msg->interrupt->is_Node);
637 retval = TRUE;
639 else
641 D(bug("[UHCI] AddInterrupt()\n"));
643 UHCI_Interrupt_t *intr = AllocVecPooled(SD(cl)->MemPool, sizeof(UHCI_Interrupt_t));
645 D(bug("[UHCI::AddInterrupt] intr = %p\n", intr));
647 if (intr)
649 intr->i_intr = msg->interrupt;
651 uhci_QueuedRead(cl, o, p, msg->buffer, msg->length);
652 AddTail((struct List *)&p->p_Intr, (struct Node *)&intr->i_node);
655 intr->i_td = uhci_AllocTD(cl, o);
657 D(bug("[UHCI::AddInterrupt] intr->i_td = %p\n", intr->i_td));
659 if (intr->i_td)
661 intr->i_td->td_Buffer = (uint32_t)msg->buffer;
662 intr->i_td->td_Status = UHCI_TD_ZERO_ACTLEN(UHCI_TD_SET_ERRCNT(3) | UHCI_TD_ACTIVE);
663 intr->i_td->td_Token = UHCI_TD_IN(msg->length, p->p_EndPoint, p->p_DevAddr, p->p_NextToggle);
664 intr->i_td->td_Status |= UHCI_TD_SPD;
665 intr->i_td->td_LinkPtr = UHCI_PTR_T;
667 Disable();
669 AddTail(&p->p_Intr, &intr->i_node);
671 if (p->p_FirstTD == (APTR)UHCI_PTR_T)
673 p->p_FirstTD = intr->i_td;
674 p->p_LastTD = intr->i_td;
676 else
678 p->p_LastTD->td_LinkPtr = (uint32_t)intr->i_td | UHCI_PTR_TD;
679 p->p_LastTD = intr->i_td;
682 if (p->p_Queue->qh_VLink == UHCI_PTR_T)
684 p->p_Queue->qh_VLink = (uint32_t)p->p_FirstTD | UHCI_PTR_TD;
687 Enable();
689 bug("[UHCI::AddInterrupt] p->p_FirstTd = %p, vlink=%p\n", p->p_FirstTD, p->p_Queue->qh_VLink);
690 retval = TRUE;
692 else
693 FreeVecPooled(SD(cl)->MemPool, intr);
697 D(bug("[UHCI::AddInterrupt] %s\n", retval ? "success":"failure"));
699 return retval;
702 BOOL METHOD(UHCI, Hidd_USBDrv, RemInterrupt)
704 UHCI_Interrupt_t *intr;
705 UHCI_Pipe *p = msg->pipe;
707 if (p == (UHCI_Pipe *)0xdeadbeef)
709 Remove((struct Node *)msg->interrupt);
710 return TRUE;
712 else
714 ForeachNode(&p->p_Intr, intr)
716 if (intr->i_intr == msg->interrupt)
717 break;
720 if (intr)
722 Disable();
723 Remove((struct Node *)&intr->i_node);
724 Enable();
726 uhci_FreeTD(cl, o, intr->i_td);
727 FreeVecPooled(SD(cl)->MemPool, intr);
729 return TRUE;
731 return FALSE;
735 BOOL METHOD(UHCI, Hidd_USBDrv, ControlTransfer)
737 UHCI_Pipe *p = msg->pipe;
738 D(UHCIData *uhci = OOP_INST_DATA(cl, o));
740 BOOL retval = TRUE;
743 uint16_t status = inw(uhci->iobase + UHCI_STS);
744 uint16_t frame = inw(uhci->iobase + UHCI_FRNUM);
745 uint16_t port1 = inw(uhci->iobase + UHCI_PORTSC1);
746 uint16_t port2 = inw(uhci->iobase + UHCI_PORTSC2);
747 uint16_t cmd = inw(uhci->iobase + UHCI_CMD);
748 uint16_t sof = inb(uhci->iobase + UHCI_SOF);
750 D(bug("[UHCI] Status check: Cmd=%04x, SOF=%04x, Status = %04x, Frame=%04x, PortSC1=%04x, PortSC2=%04x\n",
751 cmd, sof, status, frame, port1, port2));
754 int8_t sig = AllocSignal(-1);
755 int8_t toutsig = AllocSignal(-1);
756 int32_t msec = p->p_TimeoutVal;
758 D(bug("[UHCI] ControlTransfer(pipe=%p, request=%p, buffer=%p, length=%d, timeout=%d, signal=%d, toutsig=%d)\n",
759 msg->pipe, msg->request, msg->buffer, msg->length, msec, sig, toutsig));
761 if (sig >= 0 && toutsig >= 0)
763 p->p_Signal = sig;
764 p->p_SigTask = FindTask(NULL);
766 if (p->p_TimeoutVal != 0)
768 p->p_Timeout->tr_node.io_Message.mn_ReplyPort->mp_SigBit = toutsig;
769 p->p_Timeout->tr_node.io_Message.mn_ReplyPort->mp_SigTask = FindTask(NULL);
771 p->p_Timeout->tr_node.io_Command = TR_ADDREQUEST;
772 p->p_Timeout->tr_time.tv_secs = msec / 1000;
773 p->p_Timeout->tr_time.tv_micro = 1000 * (msec % 1000);
775 SendIO((struct IORequest *)p->p_Timeout);
777 uhci_ControlTransfer(cl, o, msg->pipe, msg->request, msg->buffer, msg->length);
778 if (Wait((1 << sig) | (1 << toutsig)) & (1 << toutsig))
780 bug("[UHCI] !!!TIMEOUT!!!\n");
781 p->p_Queue->qh_VLink = UHCI_PTR_T;
782 UHCI_TransferDesc *td = (UHCI_TransferDesc *)p->p_FirstTD;
784 GetMsg(p->p_Timeout->tr_node.io_Message.mn_ReplyPort);
786 while ((uint32_t)td != UHCI_PTR_T)
788 bug("[UHCI] TD=%p (%08x %08x %08x %08x)\n", td,
789 td->td_LinkPtr, td->td_Status, td->td_Token, td->td_Buffer);
790 UHCI_TransferDesc *tnext = (UHCI_TransferDesc *)(td->td_LinkPtr & 0xfffffff1);
791 uhci_FreeTD(cl, o, td);
792 td = tnext;
793 p->p_FirstTD = tnext;
795 p->p_LastTD=(APTR)UHCI_PTR_T;
797 retval = FALSE;
799 else
801 if (!CheckIO((struct IORequest *)p->p_Timeout))
802 AbortIO((struct IORequest *)p->p_Timeout);
803 WaitIO((struct IORequest *)p->p_Timeout);
804 SetSignal(0, 1 << toutsig);
806 if (p->p_ErrorCode)
807 retval = FALSE;
809 FreeSignal(sig);
810 FreeSignal(toutsig);
812 return retval;
815 BOOL METHOD(UHCI, Hidd_USBDrv, BulkTransfer)
817 UHCI_Pipe *p = msg->pipe;
819 BOOL retval = TRUE;
821 int8_t sig = AllocSignal(-1);
822 int8_t toutsig = AllocSignal(-1);
823 int32_t msec = p->p_TimeoutVal;
825 D(bug("[UHCI] BulkTransfer(pipe=%p, buffer=%p, length=%d, timeout=%d, signal=%d, toutsig=%d)\n",
826 msg->pipe, msg->buffer, msg->length, msec, sig, toutsig));
828 if (sig >= 0 && toutsig >= 0)
830 p->p_Signal = sig;
831 p->p_SigTask = FindTask(NULL);
833 if (p->p_TimeoutVal != 0)
835 p->p_Timeout->tr_node.io_Message.mn_ReplyPort->mp_SigBit = toutsig;
836 p->p_Timeout->tr_node.io_Message.mn_ReplyPort->mp_SigTask = FindTask(NULL);
838 p->p_Timeout->tr_node.io_Command = TR_ADDREQUEST;
839 p->p_Timeout->tr_time.tv_secs = msec / 1000;
840 p->p_Timeout->tr_time.tv_micro = 1000 * (msec % 1000);
842 SendIO((struct IORequest *)p->p_Timeout);
845 uhci_QueuedTransfer(cl, o, p, msg->buffer, msg->length, p->p_EndPoint & 0x80);
847 if (Wait((1 << sig) | (1 << toutsig)) & (1 << toutsig))
849 bug("[UHCI] !!!TIMEOUT!!!\n");
850 p->p_Queue->qh_VLink = UHCI_PTR_T;
851 UHCI_TransferDesc *td = (UHCI_TransferDesc *)p->p_FirstTD;
853 GetMsg(p->p_Timeout->tr_node.io_Message.mn_ReplyPort);
855 while ((uint32_t)td != UHCI_PTR_T)
857 bug("[UHCI] TD=%p (%08x %08x %08x %08x)\n", td,
858 td->td_LinkPtr, td->td_Status, td->td_Token, td->td_Buffer);
859 UHCI_TransferDesc *tnext = (UHCI_TransferDesc *)(td->td_LinkPtr & 0xfffffff1);
860 uhci_FreeTD(cl, o, td);
861 td = tnext;
862 p->p_FirstTD = tnext;
864 p->p_LastTD=(APTR)UHCI_PTR_T;
866 retval = FALSE;
868 else
870 if (!CheckIO((struct IORequest *)p->p_Timeout))
871 AbortIO((struct IORequest *)p->p_Timeout);
872 WaitIO((struct IORequest *)p->p_Timeout);
873 SetSignal(0, 1 << toutsig);
875 if (p->p_ErrorCode)
876 retval = FALSE;
878 FreeSignal(sig);
879 FreeSignal(toutsig);
883 if (retval)
884 (bug("[UHCI] Transfer OK\n"));
885 else
886 (bug("[UHCI] Transfer FAILED\n"));
889 return retval;
892 BOOL METHOD(UHCI, Hidd_USBHub, OnOff)
894 UHCIData *uhci = OOP_INST_DATA(cl, o);
895 BOOL retval;
896 D(bug("[UHCI] USBHub::OnOff(%d)\n", msg->on));
898 if (!CheckIO((struct IORequest *)uhci->timereq))
899 AbortIO((struct IORequest *)uhci->timereq);
900 GetMsg(&uhci->mport);
901 SetSignal(0, 1 << uhci->mport.mp_SigBit);
903 uhci->running = msg->on;
905 if (msg->on)
907 uhci->timereq->tr_node.io_Command = TR_ADDREQUEST;
908 uhci->timereq->tr_time.tv_secs = 1;
909 uhci->timereq->tr_time.tv_micro = 0;
910 SendIO((struct IORequest *)uhci->timereq);
913 OOP_DoSuperMethod(cl,o,(OOP_Msg)msg);
915 retval = uhci_run(cl, o, msg->on);
916 uhci_sleep(cl, o, 100);
918 return retval;
921 BOOL METHOD(UHCI, Hidd_USBHub, PortReset)
923 UHCIData *uhci = OOP_INST_DATA(cl, o);
924 BOOL retval;
925 D(bug("[UHCI] USBHub::PortReset(%d)\n", msg->portNummer));
926 retval = uhci_PortReset(cl, o, msg->portNummer);
927 uhci->reset |= (1 << (msg->portNummer - 1));
928 uhci_sleep(cl, o, 100);
929 return retval;
932 BOOL METHOD(UHCI, Hidd_USBHub, GetPortStatus)
934 UHCIData *uhci = OOP_INST_DATA(cl, o);
935 uint16_t port, x, status=0, change=0;
937 if (msg->port == 1)
938 port = UHCI_PORTSC1;
939 else if (msg->port == 2)
940 port = UHCI_PORTSC2;
941 else return FALSE;
943 x = inw(uhci->iobase + port);
945 if (x & UHCI_PORTSC_CCS)
946 status |= UPS_CURRENT_CONNECT_STATUS;
947 if (x & UHCI_PORTSC_CSC)
948 change |= UPS_C_CONNECT_STATUS;
949 if (x & UHCI_PORTSC_PE)
950 status |= UPS_PORT_ENABLED;
951 if (x & UHCI_PORTSC_POEDC)
952 change |= UPS_C_PORT_ENABLED;
953 if (x & UHCI_PORTSC_OCI)
954 status |= UPS_OVERCURRENT_INDICATOR;
955 if (x & UHCI_PORTSC_OCIC)
956 change |= UPS_C_OVERCURRENT_INDICATOR;
957 if (x & UHCI_PORTSC_SUSP)
958 status |= UPS_SUSPEND;
959 if (x & UHCI_PORTSC_LSDA)
960 status |= UPS_LOW_SPEED;
962 status |= UPS_PORT_POWER;
964 if (uhci->reset & (1 << (msg->port - 1)))
965 status |= UPS_C_PORT_RESET;
967 msg->status->wPortStatus = AROS_WORD2LE(status);
968 msg->status->wPortChange = AROS_WORD2LE(change);
970 return TRUE;
973 BOOL METHOD(UHCI, Hidd_USBHub, GetHubStatus)
975 return TRUE;
978 BOOL METHOD(UHCI, Hidd_USBHub, ClearHubFeature)
980 return TRUE;
983 BOOL METHOD(UHCI, Hidd_USBHub, SetHubFeature)
985 return TRUE;
988 BOOL METHOD(UHCI, Hidd_USBHub, ClearPortFeature)
990 UHCIData *uhci = OOP_INST_DATA(cl, o);
991 uint16_t port, x;
992 BOOL retval = FALSE;
994 if (msg->port == 1)
995 port = UHCI_PORTSC1;
996 else if (msg->port == 2)
997 port = UHCI_PORTSC2;
998 else return FALSE;
1000 switch(msg->feature)
1002 case UHF_PORT_ENABLE:
1003 x = URWMASK(inw(uhci->iobase + port));
1004 outw(x & ~UHCI_PORTSC_PE, uhci->iobase + port);
1005 retval = TRUE;
1006 break;
1008 case UHF_PORT_SUSPEND:
1009 x = URWMASK(inw(uhci->iobase + port));
1010 outw(x & ~UHCI_PORTSC_SUSP, uhci->iobase + port);
1011 retval = TRUE;
1012 break;
1014 case UHF_PORT_RESET:
1015 x = URWMASK(inw(uhci->iobase + port));
1016 outw(x & ~UHCI_PORTSC_PR, uhci->iobase + port);
1017 retval = TRUE;
1018 break;
1020 case UHF_C_PORT_CONNECTION:
1021 x = URWMASK(inw(uhci->iobase + port));
1022 outw(x | UHCI_PORTSC_CSC, uhci->iobase + port);
1023 retval = TRUE;
1024 break;
1026 case UHF_C_PORT_ENABLE:
1027 x = URWMASK(inw(uhci->iobase + port));
1028 outw(x | UHCI_PORTSC_POEDC, uhci->iobase + port);
1029 retval = TRUE;
1030 break;
1032 case UHF_C_PORT_OVER_CURRENT:
1033 x = URWMASK(inw(uhci->iobase + port));
1034 outw(x | UHCI_PORTSC_OCIC, uhci->iobase + port);
1035 retval = TRUE;
1036 break;
1038 case UHF_C_PORT_RESET:
1039 uhci->reset &= ~(1 << (msg->port - 1));
1040 retval = TRUE;
1041 break;
1043 default:
1044 retval = FALSE;
1045 break;
1048 return retval;
1051 BOOL METHOD(UHCI, Hidd_USBHub, SetPortFeature)
1053 UHCIData *uhci = OOP_INST_DATA(cl, o);
1054 uint16_t port, x;
1055 BOOL retval = FALSE;
1057 if (msg->port == 1)
1058 port = UHCI_PORTSC1;
1059 else if (msg->port == 2)
1060 port = UHCI_PORTSC2;
1061 else return FALSE;
1063 switch(msg->feature)
1065 case UHF_PORT_ENABLE:
1066 x = URWMASK(inw(uhci->iobase + port));
1067 outw(x | UHCI_PORTSC_PE, uhci->iobase + port);
1068 retval = TRUE;
1069 break;
1071 case UHF_PORT_SUSPEND:
1072 x = URWMASK(inw(uhci->iobase + port));
1073 outw(x | UHCI_PORTSC_SUSP, uhci->iobase + port);
1074 retval = TRUE;
1075 break;
1077 case UHF_PORT_RESET:
1078 retval = uhci_PortReset(cl, o, msg->port);
1079 break;
1081 case UHF_PORT_POWER:
1082 retval = TRUE;
1083 break;
1085 default:
1086 retval = FALSE;
1087 break;
1090 return retval;
1093 static const char *string1 = "U\0H\0C\0I\0 \0R\0o\0o\0t\0 \0H\0U\0B";
1094 static const int string1_l = sizeof("U\0H\0C\0I\0 \0R\0o\0o\0t\0 \0H\0U\0B");
1096 static const char *string2 = "T\0h\0e\0 \0A\0R\0O\0S\0 \0D\0e\0v\0e\0l\0o\0p\0m\0e\0n\0t\0 \0T\0e\0a\0m";
1097 static const int string2_l = sizeof("T\0h\0e\0 \0A\0R\0O\0S\0 \0D\0e\0v\0e\0l\0o\0p\0m\0e\0n\0t\0 \0T\0e\0a\0m");
1099 BOOL METHOD(UHCI, Hidd_USBDevice, GetString)
1101 msg->string->bDescriptorType = UDESC_STRING;
1103 if (msg->id == USB_LANGUAGE_TABLE)
1105 msg->string->bLength = 4;
1106 msg->string->bString[0] = 0x0409;
1108 else if (msg->id == 1)
1110 msg->string->bLength = 2 + string1_l;
1111 CopyMem(string1, &msg->string->bString[0], string1_l);
1113 else if (msg->id == 2)
1115 msg->string->bLength = 2 + string2_l;
1116 CopyMem(string2, &msg->string->bString[0], string2_l);
1118 else if (msg->id == 3)
1120 char buff[129];
1121 int i;
1122 snprintf(buff, 128, "%d.%d", VERSION_NUMBER, REVISION_NUMBER);
1123 msg->string->bLength = 2 + 2*strlen(buff);
1124 for (i=0; i < ((msg->string->bLength - 2) >> 1); i++)
1125 msg->string->bString[i] = AROS_WORD2LE(buff[i]);
1127 else
1129 return FALSE;
1131 return TRUE;
1134 static const usb_device_descriptor_t device_descriptor = {
1135 bLength: sizeof(usb_device_descriptor_t),
1136 bDescriptorType: UDESC_DEVICE,
1137 bcdUSB: 0x0101,
1138 bDeviceClass: UDCLASS_HUB,
1139 bDeviceSubClass: UDSUBCLASS_HUB,
1140 bDeviceProtocol: UDPROTO_FSHUB,
1141 bMaxPacketSize: 64,
1142 iManufacturer: 2,
1143 iProduct: 1,
1144 iSerialNumber: 3,
1145 bNumConfigurations: 1,
1148 static const usb_hub_descriptor_t hub_descriptor = {
1149 bDescLength: sizeof(usb_hub_descriptor_t) - 31,
1150 bDescriptorType: UDESC_HUB,
1151 bNbrPorts: 2,
1152 wHubCharacteristics:0,
1153 bPwrOn2PwrGood: 50,
1154 bHubContrCurrent: 0,
1155 DeviceRemovable: {0,},
1158 static const usb_endpoint_descriptor_t endpoint_descriptor = {
1159 bLength: sizeof(usb_endpoint_descriptor_t),
1160 bDescriptorType: UDESC_ENDPOINT,
1161 bEndpointAddress: 0x81,
1162 bmAttributes: 0x03,
1163 wMaxPacketSize: 1,
1164 bInterval: 255
1167 usb_endpoint_descriptor_t * METHOD(UHCI, Hidd_USBDevice, GetEndpoint)
1169 if (msg->interface == 0 && msg->endpoint == 0)
1170 return (usb_endpoint_descriptor_t *)&endpoint_descriptor;
1171 else
1172 return NULL;
1175 BOOL METHOD(UHCI, Hidd_USBDevice, GetDeviceDescriptor)
1177 CopyMem(&device_descriptor, msg->descriptor, sizeof(device_descriptor));
1178 return TRUE;
1181 BOOL METHOD(UHCI, Hidd_USBHub, GetHubDescriptor)
1183 CopyMem(&hub_descriptor, msg->descriptor, sizeof(hub_descriptor));
1184 msg->descriptor->wHubCharacteristics = AROS_WORD2LE(UHD_PWR_NO_SWITCH | UHD_OC_INDIVIDUAL);
1185 return TRUE;
1188 BOOL METHOD(UHCI, Hidd_USBDevice, Configure)
1190 D(bug("[UHCI] ::Configure(%d)", msg->configNr));
1191 return TRUE;
1194 APTR METHOD(UHCI, Hidd_USBDevice, CreatePipe)
1196 if ( (msg->type == PIPE_Interrupt) &&
1197 (msg->endpoint == 0x81))
1198 return (APTR)0xdeadbeef;
1199 else
1200 return NULL;
1203 void METHOD(UHCI, Hidd_USBDevice, DeletePipe)
1207 void METHOD(UHCI, Hidd_USBDevice, SetTimeout)
1211 /* Class initialization and destruction */
1213 #undef SD
1214 #define SD(x) (&LIBBASE->sd)
1216 static int UHCI_InitClass(LIBBASETYPEPTR LIBBASE)
1218 int i;
1219 D(bug("[UHCI] InitClass\n"));
1221 HiddUHCIAttrBase = OOP_ObtainAttrBase(IID_Drv_USB_UHCI);
1223 if (HiddUHCIAttrBase)
1225 struct TagItem tags[] = {
1226 { aHidd_UHCI_IOBase, 0UL },
1227 { aHidd_UHCI_PCIDevice, 0UL },
1228 { aHidd_UHCI_PCIDriver, 0UL },
1229 { aHidd_USBDevice_Address, 1UL },
1230 { aHidd_USBHub_IsRoot, 1UL },
1231 { aHidd_USBHub_NumPorts, 2UL },
1232 { TAG_DONE, 0UL },
1235 for (i=0; i < LIBBASE->sd.num_devices; i++)
1237 tags[0].ti_Data = LIBBASE->sd.iobase[i];
1238 tags[1].ti_Data = (IPTR)LIBBASE->sd.uhciDevice[i];
1239 tags[2].ti_Data = (IPTR)LIBBASE->sd.uhciPCIDriver[i];
1240 LIBBASE->sd.uhciDevice[i] = OOP_NewObject(NULL, CLID_Drv_USB_UHCI, tags);
1241 HIDD_USB_AttachDriver(LIBBASE->sd.usb, LIBBASE->sd.uhciDevice[i]);
1245 return TRUE;
1248 static int UHCI_ExpungeClass(LIBBASETYPEPTR LIBBASE)
1250 D(bug("[UHCI] ExpungeClass\n"));
1252 OOP_ReleaseAttrBase(IID_Drv_USB_UHCI);
1254 return TRUE;
1257 ADD2INITLIB(UHCI_InitClass, 0)
1258 ADD2EXPUNGELIB(UHCI_ExpungeClass, 0)