[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[cdi-devel] [PATCH] usb: OHCI driver
From: Max Reitz <max@xxxxxxxxxx>
+ OHCI driver works on QEMU
Signed-off-by: Max Reitz <max@xxxxxxxxxx>
---
usb/include/ohci.h | 35 ++++++++--
usb/include/usb.h | 1 +
usb/main.c | 4 +
usb/msd.c | 2 +
usb/ohci.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++++++-
usb/uhci.c | 4 +-
6 files changed, 233 insertions(+), 12 deletions(-)
diff --git a/usb/include/ohci.h b/usb/include/ohci.h
index 5f6ad01..4c896b9 100644
--- a/usb/include/ohci.h
+++ b/usb/include/ohci.h
@@ -25,6 +25,8 @@
#include <stdint.h>
+#include "cdi/lists.h"
+
#include "mempool.h"
#include "usb.h"
@@ -174,6 +176,8 @@ struct ohci {
struct mempool* ed_pool;
struct mempool* transfer_pool;
+
+ cdi_list_t ed_list;
};
#define OHC_ED_DIR_TD 0 //Richtung im TD definiert
@@ -190,10 +194,22 @@ struct ohci_ed {
unsigned mps : 11; //Maximale Paketgrö�e
unsigned user : 5; //Für uns frei verfügbar
uint32_t td_queue_tail; //Letzter TD in der Warteschlange
- uint32_t td_queue_head; //Nächster TD in der Warteschlange
+ volatile uint32_t td_queue_head; //Nächster TD in der Warteschlange
uint32_t next_ed; //Nächster ED
} __attribute__((packed));
+struct ohci_ed_desc {
+ struct ohci_ed* virt;
+ uintptr_t phys;
+ int function;
+ int endpoint;
+ cdi_list_t transfers;
+ enum {
+ USB_CONTROL,
+ USB_BULK
+ } type;
+} __attribute__((packed));
+
#define OHC_TD_DIR_SETUP 0
#define OHC_TD_DIR_OUT 1
#define OHC_TD_DIR_IN 2
@@ -208,15 +224,22 @@ struct ohci_td {
//warten soll
unsigned toggle : 2; //Datatoggle (00b oder 01b - Wert muss aus dem ED
//ermittelt werden, 10b = DATA0, 11b = DATA1)
- unsigned error : 2; //Anzahl der aufgetretenen Fehler - bei 11b wird der
- //Status im "condition"-Feld gespeichert
- unsigned condition : 4; //Status
- uint32_t current_buffer_pointer; //Pointer zu den Daten (bei 0 wurden alle
- //Daten übertragen)
+ volatile unsigned error : 2; //Anzahl der aufgetretenen Fehler - bei 11b
+ //wird der Status im "condition"-Feld
+ //gespeichert.
+ volatile unsigned condition : 4; //Status
+ volatile uint32_t current_buffer_pointer; //Pointer zu den Daten (bei 0
+ //wurden alle Daten übertragen)
uint32_t next_td; //Nächster TD
uint32_t buffer_end; //Letztes Datenbyte des Puffers
} __attribute__((packed));
+struct ohci_td_desc {
+ struct ohci_td* virt;
+ uintptr_t phys;
+ struct ohci_ed_desc* endpoint;
+} __attribute__((packed));
+
struct cdi_driver* init_ohcd(void);
void ohci_init(struct cdi_device* cdi_hci);
diff --git a/usb/include/usb.h b/usb/include/usb.h
index 896afc7..3882601 100644
--- a/usb/include/usb.h
+++ b/usb/include/usb.h
@@ -25,6 +25,7 @@
#include <stdint.h>
+#include "cdi/lists.h"
#include "cdi/misc.h"
#include "cdi/pci.h"
diff --git a/usb/main.c b/usb/main.c
index 8861fcf..cfab9a5 100644
--- a/usb/main.c
+++ b/usb/main.c
@@ -349,6 +349,8 @@ static void enum_device(struct usb_device* usbdev)
//Resetten
usbdev->reset(usbdev);
+ usbdev->hci->add_pipe(usbdev->ep0);
+
//Erste acht Bytes des Device-Descriptors abrufen und die maximale Paketgrö�e von EP0 feststellen
dev_desc =
do_control(usbdev, DEV_TO_HOST, NULL, 8, STD_REQUEST, REC_DEVICE,
@@ -369,6 +371,8 @@ static void enum_device(struct usb_device* usbdev)
0);
usbdev->id = id;
+ usbdev->hci->add_pipe(usbdev->ep0);
+
//Den ganzen Device-Descriptor einlesen
dev_desc =
do_control(usbdev, DEV_TO_HOST, NULL, sizeof(*dev_desc), STD_REQUEST,
diff --git a/usb/msd.c b/usb/msd.c
index 4410f37..f91df4d 100644
--- a/usb/msd.c
+++ b/usb/msd.c
@@ -195,6 +195,8 @@ void register_msd(struct usb_device* usbdev)
dprintf("Nicht genügend Endpoints gefunden.\n");
return;
}
+ usbdev->hci->add_pipe(msc->bulk_in);
+ usbdev->hci->add_pipe(msc->bulk_out);
if (!msd_get_capacity(usbdev, &bs, &bc)) {
strgdev->block_size = 0;
strgdev->block_count = 0;
diff --git a/usb/ohci.c b/usb/ohci.c
index e4de271..e697fae 100644
--- a/usb/ohci.c
+++ b/usb/ohci.c
@@ -23,6 +23,7 @@
#include <string.h>
#include "cdi/io.h"
+#include "cdi/lists.h"
#include "cdi/misc.h"
#include "cdi/pci.h"
@@ -133,8 +134,8 @@ void ohci_init(struct cdi_device* cdi_hci)
return;
}
- ohci->transfer_pool = mempool_create(32 * (1023 + sizeof(struct ohci_td)),
- 1023 + sizeof(struct ohci_td));
+ ohci->transfer_pool = mempool_create(32 * (1024 + sizeof(struct ohci_td)),
+ 1024 + sizeof(struct ohci_td));
if (ohci->transfer_pool == NULL) {
dprintf("Transfer-Speicherpool konnte nicht erzeugt werden!\n");
return;
@@ -224,6 +225,8 @@ void ohci_init(struct cdi_device* cdi_hci)
ohci->memory->hc_rh_port_status[i] |= OHC_RP_CCS; //Deaktivieren
}
+ ohci->ed_list = cdi_list_create();
+
gen_hci->find_devices = &get_devices;
gen_hci->activate_device = &activate_device;
gen_hci->do_packet = &ohci_do_packet;
@@ -234,7 +237,117 @@ void ohci_init(struct cdi_device* cdi_hci)
static int ohci_do_packet(struct usb_packet* packet)
{
- return USB_STALLED;
+ struct ohci_ed_desc* edsc;
+ struct ohci_td_desc* tdsc, * otdsc;
+ int i, cond;
+ struct ohci* ohci = (struct ohci*) packet->pipe->device->hci;
+ struct ohci_td* vtd;
+ uintptr_t ptd;
+
+ for (i = 0; (edsc = cdi_list_get(ohci->ed_list, i)) != NULL; i++) {
+ if ((edsc->function == packet->pipe->device->id) &&
+ (edsc->endpoint == packet->pipe->endpoint->endpoint_address))
+ {
+ break;
+ }
+ }
+ if (edsc == NULL) {
+ return USB_TIMEOUT; //Genau das würde passieren
+ }
+ if (mempool_get(ohci->transfer_pool, (void**) &vtd, &ptd) == -1) {
+ return USB_TRIVIAL_ERROR;
+ }
+ if ((packet->data != NULL) && (packet->type != PACKET_IN)) {
+ memcpy((void*) vtd + sizeof(struct ohci_td), packet->data,
+ packet->length);
+ }
+ vtd->rounding = 1; //Warum nicht
+ switch (packet->type) {
+ case PACKET_SETUP:
+ vtd->direction = OHC_TD_DIR_SETUP;
+ break;
+ case PACKET_IN:
+ vtd->direction = OHC_TD_DIR_IN;
+ break;
+ case PACKET_OUT:
+ vtd->direction = OHC_TD_DIR_OUT;
+ break;
+ default:
+ mempool_put(ohci->transfer_pool, vtd);
+ return USB_TRIVIAL_ERROR; //Hm, passt nicht ganz
+ }
+ vtd->di = 0;
+ vtd->toggle = 0x2 | packet->pipe->data_toggle;
+ vtd->error = 0;
+ vtd->condition = 15;
+ if ((packet->data == NULL) || !packet->length) {
+ vtd->current_buffer_pointer = 0;
+ vtd->buffer_end = 0;
+ } else {
+ vtd->current_buffer_pointer = ptd + sizeof(struct ohci_td);
+ vtd->buffer_end = ptd + sizeof(struct ohci_td) + packet->length - 1;
+ }
+ vtd->next_td = 0;
+ tdsc = malloc(sizeof(*tdsc));
+ if (tdsc == NULL) {
+ mempool_put(ohci->transfer_pool, vtd);
+ return USB_TRIVIAL_ERROR;
+ }
+ tdsc->virt = vtd;
+ tdsc->phys = ptd;
+ tdsc->endpoint = edsc;
+ if (cdi_list_size(edsc->transfers)) {
+ otdsc = cdi_list_get(edsc->transfers, cdi_list_size(
+ edsc->transfers) - 1);
+ otdsc->virt->next_td = ptd;
+ }
+ cdi_list_insert(edsc->transfers, cdi_list_size(edsc->transfers), tdsc);
+ if (!edsc->virt->td_queue_head) {
+ edsc->virt->td_queue_head = ptd;
+ }
+ if (edsc->type == USB_CONTROL) {
+ ohci->memory->hc_command_status |= OHC_CMST_CLF;
+ } else if (edsc->type == USB_BULK) {
+ ohci->memory->hc_command_status |= OHC_CMST_BLF;
+ }
+ while (vtd->condition == 15) {
+ cdi_sleep_ms(1);
+ }
+ cond = vtd->condition;
+ if ((packet->data != NULL) && (packet->type == PACKET_IN)) {
+ memcpy(packet->data, (void*) vtd + sizeof(struct ohci_td),
+ packet->length);
+ }
+ if (vtd->next_td == 0) {
+ edsc->virt->td_queue_head = 0;
+ }
+ mempool_put(ohci->transfer_pool, vtd);
+ switch (cond) {
+ case 0x00:
+ return USB_NO_ERROR;
+ case 0x01:
+ return USB_CRC;
+ case 0x02:
+ return USB_BITSTUFF;
+ case 0x03: //Datatoggle-Fehler
+ return USB_CRC;
+ case 0x04:
+ return USB_STALLED;
+ case 0x05:
+ return USB_TIMEOUT;
+ case 0x06: //PID-Fehler
+ case 0x07: //Ebenso
+ return USB_CRC;
+ case 0x08:
+ return USB_BABBLE;
+ case 0x09: //Data underrun
+ return USB_CRC;
+ case 0x0C: //Buffer overrun
+ case 0x0D: //Buffer underrun
+ return USB_BUFFER_ERROR;
+ default: //Ã?hm...
+ return USB_CRC;
+ }
}
static cdi_list_t get_devices(struct hci* gen_hci)
@@ -282,12 +395,73 @@ static void ohci_reset_device(struct usb_device* device)
static void ohci_establish_pipe(struct usb_pipe* pipe)
{
+ struct ohci* ohci = (struct ohci*) pipe->device->hci;
+ struct ohci_ed* ved;
+ uintptr_t ped;
+ struct ohci_ed_desc* dsc;
+ //TODO Mehr als Control und Bulk
+ int iscontrol = 0;
+
+ if (mempool_get(ohci->ed_pool, (void**) &ved, &ped) == -1) {
+ return;
+ }
+ ved->function = pipe->device->id;
+ ved->endpoint = pipe->endpoint->endpoint_address;
+ if (ved->endpoint == 0) { //EP0
+ ved->direction = OHC_ED_DIR_TD;
+ iscontrol = 1;
+ } else if (pipe->endpoint->endpoint_address & 0x80) {
+ ved->direction = OHC_ED_DIR_IN;
+ } else {
+ ved->direction = OHC_ED_DIR_OUT;
+ }
+ ved->low_speed = pipe->device->low_speed;
+ ved->skip = 0;
+ ved->format = 0;
+ ved->mps = pipe->endpoint->max_packet_size;
+ ved->td_queue_tail = 0;
+ ved->td_queue_head = 0;
+ dsc = cdi_list_get(ohci->ed_list, 0);
+ if (dsc == NULL) {
+ ved->next_ed = 0;
+ } else {
+ ved->next_ed = dsc->phys;
+ }
+ dsc = malloc(sizeof(*dsc));
+ if (dsc == NULL) {
+ mempool_put(ohci->ed_pool, ved);
+ return;
+ }
+ dsc->virt = ved;
+ dsc->phys = ped;
+ dsc->function = pipe->device->id;
+ dsc->endpoint = pipe->endpoint->endpoint_address;
+ dsc->type = iscontrol ? USB_CONTROL : USB_BULK;
+ dsc->transfers = cdi_list_create();
+ cdi_list_push(ohci->ed_list, dsc);
+
+ dprintf(
+ "%s-Endpoint %i (Gerät %i) hinzugefügt (%s)\n",
+ iscontrol ? "Control" : "Bulk", ved->endpoint, ved->function,
+ (ved->direction ==
+ OHC_ED_DIR_TD) ? "IN/OUT/SETUP" : ((ved->direction ==
+ OHC_ED_DIR_IN) ? "IN" : "OUT"));
+
+ if (iscontrol) {
+ ohci->memory->hc_control_head_ed = ped;
+ } else {
+ ohci->memory->hc_bulk_head_ed = ped;
+ }
}
static void ohci_handler(struct cdi_device* dev)
{
struct ohci* ohci = (struct ohci*) ((struct cdi_hci*) dev)->hci;
+ struct ohci_ed_desc* edsc;
+ struct ohci_td_desc* tdsc;
uint32_t status, done = 0;
+ int i, j;
+ uintptr_t phys;
status = ohci->memory->hc_interrupt_status;
@@ -296,12 +470,29 @@ static void ohci_handler(struct cdi_device* dev)
}
if (status & OHC_INT_UE) {
dprintf("Schwerer HC-Fehler.\n");
- //TODO HC anhalten
+ ohci->memory->hc_command_status |= OHC_CMST_RESET;
done |= OHC_INT_UE;
}
if (status & OHC_INT_SF) { //Ã?hm... Na ja.
done |= OHC_INT_SF;
}
+ if (status & OHC_INT_WDH) { //Paket gesendet
+ done |= OHC_INT_WDH;
+ phys = ohci->hcca->done_head;
+ for (i = 0; (edsc = cdi_list_get(ohci->ed_list, i)) != NULL; i++) {
+ for (j = 0; (tdsc = cdi_list_get(edsc->transfers, j)) != NULL;
+ j++)
+ {
+ if (tdsc->phys == phys) { //Das ist der fertige Transfer
+ cdi_list_remove(edsc->transfers, j);
+ break;
+ }
+ }
+ if (tdsc != NULL) { //Gefunden
+ break;
+ }
+ }
+ }
if (status & ~done) {
dprintf("Nicht behandelter Interrupt: 0x%08X\n", status & ~done);
diff --git a/usb/uhci.c b/usb/uhci.c
index cb7b857..405a879 100644
--- a/usb/uhci.c
+++ b/usb/uhci.c
@@ -116,8 +116,8 @@ void uhci_init(struct cdi_device* cdi_hci)
return;
}
request_size =
- // Maximale Paketlänge (Full Speed)
- 1023 +
+ // Maximale Paketlänge (Full Speed) ist 1023, Alignment au�erdem
+ 1024 +
// Transferdeskriptor
sizeof(struct uhci_td);
--
1.6.3.3