opengnsys_ipxe/src/filo/usb/uhci.c

1144 lines
22 KiB
C

#ifdef USB_DISK
/*******************************************************************************
*
*
* Copyright 2003 Steven James <pyro@linuxlabs.com> and
* LinuxLabs http://www.linuxlabs.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
******************************************************************************/
#include <etherboot.h>
#include <pci.h>
#include <timer.h>
#include <lib.h>
#define DEBUG_THIS DEBUG_USB
#include <debug.h>
#define DPRINTF debug
#include "usb.h"
#include "uhci.h"
#include "debug_x.h"
#define ALLOCATE 1
extern int usec_offset;
int wait_head( queue_head_t *head, int count)
{
td_t *td;
while(!head->depth.terminate) {
td = MEM_ADDR(head->depth.link);
if(!td->active)
return(-1); // queue failed
if(count)
if(! --count)
return(0); // still active
udelay(500); // give it some time
}
return(1); // success
}
queue_head_t *free_qh;
queue_head_t _queue_heads[MAX_QUEUEHEAD];
queue_head_t *queue_heads = _queue_heads;
queue_head_t *new_queue_head(void)
{
queue_head_t *qh;
if(!free_qh)
return(NULL);
qh = free_qh;
free_qh = MEM_ADDR(qh->bredth.link);
memset(qh,0,sizeof(queue_head_t));
qh->bredth.terminate = qh->depth.terminate=1;
return(qh);
}
void free_queue_head( queue_head_t *qh)
{
qh->bredth.link = LINK_ADDR(free_qh);
if(!free_qh)
qh->bredth.terminate=1;
qh->depth.terminate=1;
free_qh = qh;
}
void init_qh(void)
{
int i;
for(i=0; i<MAX_QUEUEHEAD-1; i++) {
memset(queue_heads+i, 0, sizeof(queue_head_t));
queue_heads[i].bredth.link = LINK_ADDR( &queue_heads[i+1] );
queue_heads[i].depth.terminate=1;
}
queue_heads[MAX_QUEUEHEAD-1].depth.terminate=1;
queue_heads[MAX_QUEUEHEAD-1].bredth.terminate=1;
free_qh = queue_heads;
}
td_t *free_td_list;
td_t _tds[MAX_TD];
td_t *tds = _tds; // indirection added for kernel testing
void init_td(void)
{
int i;
for(i=0; i<MAX_TD-1; i++) {
memset(tds+i, 0, sizeof(td_t));
tds[i].link.link = LINK_ADDR( &tds[i+1]);
}
memset( &tds[MAX_TD-1], 0, sizeof(td_t));
tds[MAX_TD-1].link.terminate=1;
free_td_list = tds;
}
td_t *new_td(void)
{
td_t *td;
if(!free_td_list)
return(NULL);
// DPRINTF("new_td: free_td = %p\n", free_td_list);
td = free_td_list;
free_td_list = MEM_ADDR( td->link.link);
// DPRINTF("new_td: free_td_list = %p\n", free_td_list);
memset(td, 0, sizeof(td_t));
td->link.terminate=1;
// DPRINTF("new_td: returning %p\n", td);
return(td);
}
td_t *find_last_td(td_t *td)
{
td_t *last;
last = td;
while(!last->link.terminate)
last = MEM_ADDR(last->link.link);
return(last);
}
void free_td( td_t *td)
{
td_t *last_td;
last_td = find_last_td(td);
last_td->link.link = LINK_ADDR(free_td_list);
if(!free_td_list)
last_td->link.terminate=1;
else
last_td->link.terminate=0;
free_td_list = td;
}
link_pointer_t *queue_end( queue_head_t *queue)
{
link_pointer_t *link;
link = &(queue->depth);
while(!link->terminate)
link = MEM_ADDR(link->link);
return(link);
}
void add_td( queue_head_t *head, td_t *td)
{
link_pointer_t *link;
link = queue_end(head);
link->link = LINK_ADDR(td);
link->terminate=0;
}
transaction_t transactions[MAX_TRANSACTIONS];
transaction_t *free_transactions;
void init_transactions(void)
{
int i;
memset(transactions, 0, sizeof(transactions));
for(i=0; i<MAX_TRANSACTIONS-1; i++)
transactions[i].next = &transactions[i+1];
free_transactions = transactions;
}
void free_transaction( transaction_t *trans )
{
transaction_t *my_current, *last;
my_current = trans;
if(my_current==0) return;
while(my_current) {
free_td( my_current->td_list );
free_queue_head( my_current->qh );
last = my_current;
my_current = my_current->next;
}
last->next = free_transactions;
free_transactions = trans;
}
transaction_t *new_transaction(td_t *td)
{
transaction_t *trans = free_transactions;
queue_head_t *qh;
if(!trans) {
DPRINTF("new_transaction( td = %x) failed!\n", td);
return(NULL);
}
free_transactions = trans->next;
memset(trans, 0, sizeof(transaction_t));
if(td) {
qh = new_queue_head();
if(!qh) {
free_transaction(trans);
return(NULL);
}
trans->qh = qh;
trans->td_list = td;
qh->depth.link = LINK_ADDR(td);
qh->depth.terminate = 0;
qh->bredth.terminate=1;
}
return(trans);
}
transaction_t *add_transaction( transaction_t *trans, td_t *td)
{
transaction_t *t1;
t1 = new_transaction(td);
if(!t1)
return(NULL);
trans->next = t1;
trans->qh->bredth.terminate=0;
trans->qh->bredth.link = LINK_ADDR(t1->qh);
trans->qh->bredth.queue=1;
return(trans);
}
link_pointer_t *frame_list[MAX_CONTROLLERS];
#if 0
uchar fl_buffer[MAX_CONTROLLERS][8192];
#endif
void init_framelist(uchar dev)
{
int i;
#if 0
DPRINTF("raw frame_list is at %x\n", fl_buffer[dev]);
frame_list[dev] = (link_pointer_t *) ((bus_to_virt)(((unsigned int)virt_to_bus(fl_buffer[dev]) & ~0xfff) + 0x1000));
#else
frame_list[dev] = (link_pointer_t *) allot2(sizeof(link_pointer_t)*1024, 0xfff); // 4K alignment
if(frame_list[dev]==0) {
printf("init_framelist: no mem\n");
}
#endif
memset(frame_list[dev], 0, 1024 * sizeof(link_pointer_t));
DPRINTF("frame_list is at %x\n", frame_list[dev]);
for(i=0;i<1024;i++)
frame_list[dev][i].terminate=1;
}
extern int num_controllers;
extern uint32_t hc_base[MAX_CONTROLLERS];
extern uint8_t hc_type[MAX_CONTROLLERS];
void uhc_clear_stat()
{
unsigned short value;
value = inw(USBSTS(0));
outw(value, USBSTS(0));
}
void clear_uport_stat(unsigned short port)
{
unsigned short value;
value = inw(port);
outw(value, port);
}
void uport_suspend( unsigned short port)
{
unsigned short value;
value = inw(port);
value |= 0x1000;
outw( value, port);
}
void uport_wakeup( unsigned short port)
{
unsigned short value;
value = inw(port);
value &= ~0x1000;
outw( value, port);
}
#if 0
void uport_resume( unsigned short port)
{
unsigned short value;
value = inw(port);
value |= 0x40;
outw(value, port);
udelay(20000+usec_offset);
value &= ~0x40;
outw(value, port);
do {
value = inw(port);
} while(value & 0x40);
}
#endif
void uport_enable( unsigned short port)
{
unsigned short value;
value = inw(port);
value |= 0x04;
outw( value, port);
do {
value = inw(port);
} while( !(value & 0x04) && (value & 0x01));
}
void uport_disable( unsigned short port)
{
unsigned short value;
value = inw(port);
value &= ~0x04;
outw( value, port);
}
void uport_reset(unsigned short port)
{
unsigned short value;
int i;
value = inw(port);
value |= 0x200;
outw( value, port);
for(i=0;i<5;i++)
udelay(10000+usec_offset);
value &= ~0x200;
outw( value, port);
// DPRINTF("Port %04x reset\n", port);
}
void uport_reset_long(unsigned short port)
{
unsigned short value;
int i;
value = inw(port);
value |= 0x200;
outw( value, port);
for(i=0; i<20; i++)
udelay(10000);
value &= ~0x200;
outw( value, port);
// DPRINTF("Port %04x reset\n", port);
}
void uhc_reset(uchar controller)
{
DPRINTF("Resetting UHCI\n");
outw(0x04, USBCMD(controller));
udelay(20000);
outw(0, USBCMD(controller));
}
#if 0
int uhc_stop(uchar dev)
{
unsigned short tmp;
tmp = inw(USBCMD(dev));
tmp &= ~USBCMDRUN;
outw( tmp, USBCMD(dev));
while(! (inw(USBSTS(dev)) & USBSTSHALTED) );
outw( USBSTSHALTED, USBSTS(dev)); // clear the status
return(0);
}
#endif
int uhc_start(uchar dev) {
unsigned short tmp;
DPRINTF("Starting UHCI\n");
tmp = inw(USBCMD(dev));
tmp |= USBCMDRUN;
// tmp |= USBCMD_DEBUG;
outw( tmp, USBCMD(dev));
return(0);
}
int uhc_init(struct pci_device *dev)
{
int16_t word;
pci_read_config_word(dev, 0x20, &word);
hc_base[num_controllers] = word;
hc_base[num_controllers] &= ~1;
DPRINTF("Found UHCI at %04x\n", hc_base[num_controllers]);
uhc_reset(num_controllers);
// set master
pci_read_config_word(dev, 0x04, &word);
word |= 0x04;
pci_write_config_word(dev, 0x04, word);
#if 0
if( ((unsigned int) virt_to_bus(frame_list[num_controllers])) != ( ( (unsigned int)virt_to_bus(frame_list[num_controllers])) & ~0x7ff) ) {
DPRINTF("UHCI: grave error, misaligned framelist (%x)\n", frame_list[num_controllers]);
return(-1);
}
#endif
DPRINTF("uhc_init setting framelist to: %08x\n", (unsigned int) virt_to_bus( (frame_list[num_controllers]) ));
outl( (unsigned int) virt_to_bus(frame_list[num_controllers]), FLBASE(num_controllers));
outw( 0, FRNUM(num_controllers));
outw( 0, USBINTR(num_controllers)); // no interrupts!
outw(0x1000, PORTSC1(num_controllers));
outw(0x1000, PORTSC2(num_controllers));
uhc_start(num_controllers);
dump_uhci(hc_base[num_controllers]);
num_controllers++;
return(0);
}
queue_head_t *sched_queue[MAX_CONTROLLERS];
queue_head_t *term_qh[MAX_CONTROLLERS];
//td_t *dummy_td[MAX_CONTROLLERS];
td_t *loop_td[MAX_CONTROLLERS];
void init_sched(uchar dev)
{
int i;
// dummy_td[dev] = new_td();
loop_td[dev] = new_td();
term_qh[dev] = new_queue_head();
sched_queue[dev] = new_queue_head();
sched_queue[dev]->bredth.terminate=0;
sched_queue[dev]->bredth.queue=1;
sched_queue[dev]->bredth.link=LINK_ADDR(term_qh[dev]);
sched_queue[dev]->depth.terminate=1;
term_qh[dev]->bredth.terminate=1;
term_qh[dev]->depth.link = LINK_ADDR(loop_td[dev]);
term_qh[dev]->depth.terminate=0;
// dummy_td->link.link = LINK_ADDR(sched_queue);
// dummy_td->link.queue = 1;
// dummy_td->link.depth=1;
// dummy_td->link.terminate=0;
// dummy_td->packet_type = IN_TOKEN;
// dummy_td->max_transfer = 0x7;
// dummy_td->isochronous=1;
// dummy_td->active=1;
// dummy_td->device_addr = 0x7f;
// dummy_td->endpoint=0x01;
// dummy_td->buffer = virt_to_bus(&dummy_td->data[2]);
// dummy_td->retrys=3;
//dump_hex( (uchar *) dummy_td, sizeof(td_t), "dummy_td ");
loop_td[dev]->link.link = LINK_ADDR(loop_td[dev]);
loop_td[dev]->link.terminate=0;
loop_td[dev]->link.queue=0;
loop_td[dev]->packet_type = IN_TOKEN;
loop_td[dev]->max_transfer=7;
loop_td[dev]->retrys=0;
loop_td[dev]->device_addr=0x7f;
for(i=0; i< 1024; i++) {
frame_list[dev][i].link = LINK_ADDR(sched_queue[dev]);
frame_list[dev][i].queue=1;
frame_list[dev][i].terminate=0;
// frame_list[dev][i].terminate=1;
}
dump_link( frame_list[dev], "frame_list_link: ");
// DPRINTF("dummy_td = %x\n", dummy_td[dev]);
// dump_frame_list("sched:");
}
void uhci_init(void)
{
int i;
init_td();
init_qh();
init_transactions();
for(i=0;i<MAX_CONTROLLERS; i++) {
if(hc_type[i] == 0x00) {
init_framelist(i);
init_sched(i);
}
}
// From now should not change num_controllers any more
}
int poll_queue_head( queue_head_t *qh)
{
td_t *td;
int strikes=3;
if(qh->depth.terminate)
return(1);
while(strikes--) {
if(qh->depth.terminate)
return(1);
td = MEM_ADDR(qh->depth.link);
if(td->active)
return(0);
udelay(1000);
// if(!td->active)
// return(1);
}
return(1);
}
int wait_queue_complete( queue_head_t *qh)
{
int ret;
int spins=1000;
while( --spins && !(ret = poll_queue_head(qh))) {
udelay(1500);
// if(!(spins%30))
// DPRINTF("wait_queue_complete: spin\n");
}
// DPRINTF("wait_queue_complete: returning %d\n", ret);
if(!spins)
return(-1);
return(ret);
}
#define BULK_DEPTH 1
transaction_t *_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data)
{
uchar dt;
transaction_t *trans;
td_t *td, *cur, *last;
int remaining = len;
uchar *pos = data;
int max;
uchar type = OUT_TOKEN;
int packet_length;
if(ep & 0x80)
type = IN_TOKEN;
ep &= 0x7f;
td = cur = last = NULL;
dt = usb_device[devnum].toggle[ep];
max = usb_device[devnum].max_packet[ep];
while(remaining) {
cur = new_td();
cur->packet_type = type;
cur->data_toggle = dt;
cur->endpoint = ep&0x7f;
cur->device_addr = devnum;
cur->detect_short=1;
cur->active=1;
dt = dt^0x01;
if(!td){
td = cur;
}
if(last) {
last->link.terminate=0;
last->link.link = LINK_ADDR(cur);
}
cur->buffer = (void *) virt_to_bus(pos);
if(remaining>max) {
packet_length = max;
}
else {
packet_length = remaining;
}
cur->max_transfer=packet_length-1;
cur->link.depth = BULK_DEPTH;
remaining -= packet_length;
pos+= packet_length;
last = cur;
}
// if( packet_length == max) { // if final packet wasn't short, add a zero packet
// cur = new_td();
// dt = dt^0x01;
// cur->packet_type = type;
// cur->max_transfer = 0x7ff; // zero length code
// last->link.terminate=0;
// last->link.link = LINK_ADDR(cur);
//
// }
cur->link.terminate=1;
trans = new_transaction(td);
usb_device[devnum].toggle[ep] = dt;
return(trans);
}
#define DEPTH 0
transaction_t *ctrl_msg(uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, uchar *data)
{
td_t *td;
td_t *current_td;
td_t *last_td;
transaction_t *trans;
ctrl_msg_t *message;
unsigned char type;
int remaining = wLength;
uchar *pos = data;
uchar dt=1;
// DPRINTF("ctrl_msg( %02x, %02x, %02x, %04x, %04x, %04x, %p)\n", devnum, request_type, request, wValue, wIndex, wLength, data);
// DPRINTF("%d bytes in payload\n", remaining);
// DPRINTF("lowspeed = %u\n", usb_device[devnum].lowspeed);
last_td = td = new_td();
td->packet_type = SETUP_TOKEN;
td->device_addr = devnum & 0x7f;
td->max_transfer = 7; // fixed for setup packets
td->retrys = CTRL_RETRIES;
td->active=1;
td->data_toggle=0;
td->link.depth=DEPTH;
td->detect_short=0;
td->interrupt=1;
td->lowspeed = usb_device[devnum].lowspeed;
// steal 8 bytes from so-called software area to hole the control message itself
td->buffer = (void *) virt_to_bus(&(td->data[2]));
message = bus_to_virt( (unsigned int) td->buffer);
message->bmRequestType = request_type;
message->bRequest = request;
message->wValue = wValue;
message->wIndex = wIndex;
message->wLength = wLength;
//dump_hex(td, sizeof(td_t), "ctrl_msg:");
trans = new_transaction(td);
if(!trans) {
DPRINTF("ctrl_msg: couldn't allocate a transaction!\n");
return(NULL);
}
if(request_type & CONTROL_DIR_MASK)
type = IN_TOKEN;
else
type = OUT_TOKEN;
while(remaining >0) {
int length;
// DPRINTF("ctrl_msg loop %d remaining, maxpacket = %u\n", remaining, usb_device[devnum].max_packet[0]);
current_td = new_td();
last_td->link.link = LINK_ADDR(current_td);
last_td->link.terminate=0;
last_td->link.queue=0;
last_td->link.depth=DEPTH;
current_td->device_addr = devnum & 0x7f;
current_td->retrys = CTRL_RETRIES;
current_td->active=1;
current_td->data_toggle=dt;
current_td->link.depth=DEPTH;
current_td->lowspeed = usb_device[devnum].lowspeed;
current_td->detect_short=1;
dt = dt^0x01;
current_td->packet_type = type;
// if(type == IN_TOKEN)
// current_td->detect_short=1;
if(remaining >usb_device[devnum].max_packet[0])
length = usb_device[devnum].max_packet[0];
else
length = remaining;
current_td->max_transfer = length-1;
current_td->buffer = (void *) virt_to_bus(pos);
remaining -= length;
pos += length;
last_td = current_td;
}
current_td = new_td();
current_td->device_addr = devnum & 0x7f;
current_td->retrys = CONTROL_STS_RETRIES;
current_td->active=1;
current_td->lowspeed = usb_device[devnum].lowspeed;
if(type == IN_TOKEN)
current_td->packet_type = OUT_TOKEN;
else
current_td->packet_type = IN_TOKEN;
current_td->max_transfer=0x7ff;
current_td->link.terminate=1;
current_td->data_toggle=1;
current_td->link.depth=DEPTH;
last_td->link.link = LINK_ADDR(current_td);
last_td->link.terminate=0;
last_td->link.queue=0;
last_td->link.depth=DEPTH;
return(trans);
}
int schedule_transaction( uchar dev, transaction_t *trans)
{
unsigned short value;
if(!sched_queue[dev]->depth.terminate)
return(-EBUSY);
sched_queue[dev]->depth.link = LINK_ADDR(trans->qh);
sched_queue[dev]->depth.terminate = 0;
sched_queue[dev]->depth.queue=1;
if(hc_type[dev]==0x00) {
value = inw(hc_base[dev]);
value |=1;
outw( value, hc_base[dev]);
}
#if 0
else if (hc_type[dev]==0x10) {
uint32_t value;
ohci_regs_t *ohci_regs = (ohci_regs_t *) hc_base[dev];
value = readl(&ohci_regs->control);
value |=OHCI_USB_OPER;
writel( value, &ohci_regs->control);
}
#endif
return(0);
}
int wait_transaction( transaction_t *trans)
{
queue_head_t *qh;
qh = trans->qh;
while(!qh->bredth.terminate)
qh = MEM_ADDR(qh->bredth.link);
return( wait_queue_complete(qh));
}
void unlink_transaction( uchar dev, transaction_t *trans)
{
sched_queue[dev]->depth.terminate=1;
sched_queue[dev]->depth.link = 0; // just in case
}
int uhci_bulk_transfer( uchar devnum, uchar ep, unsigned int len, uchar *data)
{
transaction_t *trans;
td_t *td;
int data_len;
int ret;
uchar *buffer;
DPRINTF("bulk_transfer: ep = %x len=%d\n", ep, len);
#if ALLOCATE==1
buffer = allot2(2048, 0x7ff);
if(buffer==0){
printf("bulk_transfer: can not allot\n");
}
memset(buffer,0,2048);
// DPRINTF("bulk_transfer: buffer(virt) = %x buffer(phys) = %x len = %d\n", buffer, virt_to_phys(buffer), len);
if( !(ep & 0x80))
memcpy(buffer, data, len);
#else
buffer = data;
#endif
trans = _bulk_transfer(devnum, ep, len, buffer);
#if 0
#ifdef DEBUG
dump_transaction(trans, "bulk_transfer:");
#endif
#endif
schedule_transaction( usb_device[devnum].controller, trans);
ret = wait_transaction(trans);
if(ret<0) {
#ifdef DEBUG
dump_uhci(hc_base[usb_device[devnum].controller] );
dump_td(trans->td_list, "failed_bulk_transaction: ");
#endif
unlink_transaction( usb_device[devnum].controller, trans);
free_transaction(trans);
#if ALLOCATE==1
forget2(buffer);
#endif
return(-1);
}
unlink_transaction( usb_device[devnum].controller, trans);
data_len=0;
td = trans->td_list;
do {
if(td->active)
break;
if(td->max_transfer == 0x7ff)
break;
data_len += td->actual +1;
if(td->actual < td->max_transfer) // short packet also check for errors here
break;
if(!td->link.terminate){
td = MEM_ADDR(td->link.link);
}
else {
td=NULL;
}
} while(td);
#if 0
#ifdef DEBUG
dump_td(trans->td_list, "bulk_transfer_success:");
#endif
#endif
if(data_len < len) {
DPRINTF("bulk_transfer( dev= %d, ep = %d, len = %d, buffer = %x) = %d:short transaction:\n", devnum, ep, len, data, data_len);
dump_td(trans->td_list, "short_transaction:");
}
free_transaction(trans);
#if ALLOCATE==1
if( (ep & 0x80))
memcpy(data, buffer, len);
forget2(buffer);
#endif
DPRINTF("bulk_transfer returning %d\n", data_len);
return(data_len);
}
int uhci_control_msg( uchar devnum, uchar request_type, uchar request, unsigned short wValue, unsigned short wIndex, unsigned short wLength, void *data)
{
transaction_t *trans;
td_t *td;
int data_len=0;
uchar *buffer;
int ret;
DPRINTF("uhci_control_msg: request_type = %x request = %x wLength=%d\n", request_type, request, wLength);
#if ALLOCATE==1
// if( (wLength!=0) && (data!=NULL) ) {
buffer = allot2(2048+wLength,0x7ff);
if(buffer==0){
printf("uhci_control_msg: can not allot\n");
}
memset(buffer,0,2048+wLength);
//DPRINTF("uhci_control_msg: buffer(virt) = %x buffer(phys) = %x wLength=%d\n", buffer, virt_to_phys(buffer), wLength);
if( !(request_type & 0x80))
memcpy(buffer, data, wLength);
// } else {
// buffer=NULL;
// }
#else
buffer = data;
#endif
trans = ctrl_msg(devnum, request_type, request, wValue, wIndex, wLength, buffer);
if(!trans) {
DPRINTF("uhci_control_msg: ctrl_msg failed!\n");
#if ALLOCATE==1
forget2(buffer);
#endif
return(-1);
}
schedule_transaction( usb_device[devnum].controller, trans);
ret = wait_transaction(trans);
if(ret<0) {
#ifdef DEBUG
dump_uhci(hc_base[usb_device[devnum].controller] );
dump_td(trans->td_list, "failed_transaction: ");
#endif
unlink_transaction( usb_device[devnum].controller, trans);
free_transaction(trans);
#if ALLOCATE==1
forget2(buffer);
#endif
return(ret);
}
//#ifdef DEBUG
// dump_td(trans->td_list, "success: ");
//#endif
unlink_transaction( usb_device[devnum].controller, trans);
// now, see what happened
if(!trans->qh->depth.terminate) {
// handle setup error
dump_uhci(hc_base);
dump_td(trans->td_list, "qh->depth failed_transaction: ");
free_transaction(trans);
#if ALLOCATE==1
forget2(buffer);
#endif
return(-1);
}
td = trans->td_list;
do {
if(td->packet_type != SETUP_TOKEN)
data_len += td->actual;
if(td->actual < td->max_transfer) // short packet also check for errors here
break;
if(!td->link.terminate) {
td = MEM_ADDR(td->link.link);
}
else {
td=NULL;
}
} while(td);
free_transaction(trans);
#if ALLOCATE==1
if ( (wLength!=0) && (data!=NULL)){
if( (request_type & 0x80))
memcpy(data, buffer, wLength);
forget2(buffer);
}
#endif
DPRINTF("usb_control_message returning %d\n", data_len);
return(data_len);
}
int poll_u_root_hub(unsigned short port, uchar controller)
{
ushort value;
int addr=0;
int i;
static int do_over=0;
value = inw(port);
debug("poll_u_root_hub1 v=%08x\t", value);
if(value & 0x02 || do_over == port) {
debug("poll_u_root_hub2 v=%08x\t", value);
do_over=0;
if(value & 0x01 ) { // if port connected
debug("poll_u_root_hub21 v=%08x\t", value);
DPRINTF("Connection on port %04x\n", port);
outw(value, port);
for(i=0; i<40; i++) {
udelay(10000+usec_offset);
value = inw(port);
if(value & 0x02) {
outw(value, port);
i=0;
DPRINTF("BOUNCE!\n");
}
}
uport_wakeup(port);
// DPRINTF("Wakup %04x\n", port);
uport_reset(port);
udelay(10);
uport_enable(port);
if(!value & 0x01) {
DPRINTF("Device went away!\n");
return(-1);
}
addr = configure_device( port, controller, value & 0x100);
if(addr<0) {
uport_disable(port);
udelay(20000);
// uport_reset(port);
uport_reset_long(port);
uport_suspend(port);
do_over=port;
uhc_clear_stat();
// dump_uhci(0x38c0);
}
} else {
uport_suspend(port);
uport_disable(port);
DPRINTF("Port %04x disconnected\n", port);
// wave hands, deconfigure devices on this port!
}
}
return(addr);
}
#endif