git-svn-id: svn://localhost/Users/andi/Downloads/code/DML@27 be6c1b03-d731-4111-a574-e37d80d43941

This commit is contained in:
crediar@rypp.net 2012-08-09 00:19:28 +00:00
parent b61acfedff
commit 206dff56e5
11 changed files with 0 additions and 2680 deletions

View File

@ -1,78 +0,0 @@
/*
* Original Copyright (c) 2001 by David Brownell
*
* 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.
*/
/* this file is part of ehci.c */
static inline void ehci_qtd_init(struct ehci_qtd *qtd
)
{
dma_addr_t dma = ehci_virt_to_dma(qtd);
memset (qtd, 0, sizeof *qtd);
qtd->qtd_dma = dma;
qtd->hw_token = (QTD_STS_HALT);
qtd->hw_next = EHCI_LIST_END();
qtd->hw_alt_next = EHCI_LIST_END();
}
static inline struct ehci_qtd * ehci_qtd_alloc(void)
{
struct ehci_qtd *qtd ;
//debug_printf("ehci_qtd used=%x\n",ehci->qtd_used);
BUG_ON(ehci->qtd_used>=EHCI_MAX_QTD);
qtd = ehci->qtds[ehci->qtd_used++];
ehci_qtd_init(qtd);
return qtd;
}
int ehci_mem_init (void)
{
int i;
u32 ptr = 0x1800000 - DEFAULT_I_TDPS * sizeof(__le32);
ehci->periodic = (u32*)ptr;
ehci->periodic_dma = ehci_virt_to_dma(ehci->periodic);
for (i = 0; i < DEFAULT_I_TDPS; i++)
ehci->periodic[i] = EHCI_LIST_END();
ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list );
for(i=0;i<EHCI_MAX_QTD;i++)
{
ptr -= sizeof(struct ehci_qtd);
ehci->qtds[i] = (struct ehci_qtd*)(ptr);
}
ehci->qtd_used = 0;
ptr -= sizeof(struct ehci_qh);
ehci->asyncqh = (struct ehci_qh*)ptr;
ehci->asyncqh->ehci = ehci;
ehci->asyncqh->qh_dma = ehci_virt_to_dma(ehci->asyncqh);
ehci->asyncqh->qtd_head = NULL;
ptr -= sizeof(struct ehci_qh);
ehci->async = (struct ehci_qh*)ptr;
ehci->async->ehci = ehci;
ehci->async->qh_dma = ehci_virt_to_dma(ehci->async);
ehci->async->qtd_head = NULL;
return 0;
}

830
ehci.c
View File

@ -1,830 +0,0 @@
/* simplest usb-ehci driver which features:
control and bulk transfers only
only one transfer pending
driver is synchronous (waiting for the end of the transfer)
endianess independant
no uncached memory allocation needed
this driver is originally based on the GPL linux ehci-hcd driver
* Original Copyright (c) 2001 by David Brownell
*
* 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.
*/
/* magic numbers that can affect system performance */
u32 IsInit = 0;
#undef DEBUG
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
#define EHCI_TUNE_RL_TT 0
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
extern int verbose;
static void
dbg_qtd (const char *label, struct ehci_qtd *qtd)
{
//ehci_dbg( "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
// hc32_to_cpup( &qtd->hw_next),
// hc32_to_cpup( &qtd->hw_alt_next),
// hc32_to_cpup( &qtd->hw_token),
// hc32_to_cpup( &qtd->hw_buf [0]));
//if (qtd->hw_buf [1])
// ehci_dbg( " p1=%08x p2=%08x p3=%08x p4=%08x\n",
// hc32_to_cpup( &qtd->hw_buf[1]),
// hc32_to_cpup( &qtd->hw_buf[2]),
// hc32_to_cpup( &qtd->hw_buf[3]),
// hc32_to_cpup( &qtd->hw_buf[4]));
}
static void
dbg_qh (const char *label, struct ehci_qh *qh)
{
//ehci_dbg ( "%s qh %p n%08x info %x %x qtd %x\n", label,
// qh,
// hc32_to_cpu(qh->hw_next),
// hc32_to_cpu(qh->hw_info1),
// hc32_to_cpu(qh->hw_info2),
// hc32_to_cpu(qh->hw_current));
//dbg_qtd ("overlay", (struct ehci_qtd *) &qh->hw_qtd_next);
}
static void
dbg_command (void)
{
#ifdef DEBUG
u32 command=ehci_readl( &ehci->regs->command);
u32 async=ehci_readl( &ehci->regs->async_next);
ehci_dbg ("async_next: %08x\n",async);
ehci_dbg (
"command %06x %s=%d ithresh=%d%s%s%s%s %s %s\n",
command,
(command & CMD_PARK) ? "park" : "(park)",
CMD_PARK_CNT (command),
(command >> 16) & 0x3f,
(command & CMD_LRESET) ? " LReset" : "",
(command & CMD_IAAD) ? " IAAD" : "",
(command & CMD_ASE) ? " Async" : "",
(command & CMD_PSE) ? " Periodic" : "",
(command & CMD_RESET) ? " Reset" : "",
(command & CMD_RUN) ? "RUN" : "HALT"
);
#endif
}
static void
dbg_status (void)
{
#ifdef DEBUG
u32 status=ehci_readl( &ehci->regs->status);
ehci_dbg (
"status %04x%s%s%s%s%s%s%s%s%s%s\n",
status,
(status & STS_ASS) ? " Async" : "",
(status & STS_PSS) ? " Periodic" : "",
(status & STS_RECL) ? " Recl" : "",
(status & STS_HALT) ? " Halt" : "",
(status & STS_IAA) ? " IAA" : "",
(status & STS_FATAL) ? " FATAL" : "",
(status & STS_FLR) ? " FLR" : "",
(status & STS_PCD) ? " PCD" : "",
(status & STS_ERR) ? " ERR" : "",
(status & STS_INT) ? " INT" : ""
);
#endif
}
void debug_qtds(void)
{
//struct ehci_qh *qh = ehci->async;
//struct ehci_qtd *qtd;
//dbg_qh ("qh",qh);
//dbg_command ();
//dbg_status ();
//for(qtd = qh->qtd_head; qtd; qtd = qtd->next)
//{
// ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd));
// dbg_qtd("qtd",qtd);
// ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd));
//}
}
void dump_qh(struct ehci_qh *qh)
{
//struct ehci_qtd *qtd;
//dbg_command ();
//dbg_status ();
//ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh));
//dbg_qh("qh",qh);
//print_hex_dump_bytes("qh:",DUMP_PREFIX_OFFSET,(void*)qh,12*4);
//for(qtd = qh->qtd_head; qtd; qtd = qtd->next){
// u32 *buf;
// ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd));
// dbg_qtd("qtd",qtd);
// print_hex_dump_bytes("qtd:",DUMP_PREFIX_OFFSET,(void*)qtd,8*4);
// buf = (u32*)hc32_to_cpu(qtd->hw_buf[0]);
// if(buf)
// print_hex_dump_bytes("qtd buf:",DUMP_PREFIX_OFFSET,(void*)(buf),8*4);
//}
}
/*-------------------------------------------------------------------------*/
/*
* handshake - spin reading hc until handshake completes or fails
* @ptr: address of hc register to be read
* @mask: bits to look at in result of read
* @done: value of those bits when handshake succeeds
* @usec: timeout in microseconds
*
* Returns negative errno, or zero on success
*
* Success happens when the "mask" bits have the specified value (hardware
* handshake done). There are two failure modes: "usec" have passed (major
* hardware flakeout), or the register reads as all-ones (hardware removed).
*
* That last failure should_only happen in cases like physical cardbus eject
* before driver shutdown. But it also seems to be caused by bugs in cardbus
* bridge shutdown: shutting down the bridge before the devices using it.
*/
static int handshake (void __iomem *ptr,
u32 mask, u32 done, int usec)
{
u32 result;
do {
result = ehci_readl( ptr);
if (result == ~(u32)0) /* card removed */
return -ENODEV;
result &= mask;
if (result == done)
return 0;
udelay(1);
usec--;
} while (usec > 0);
ehci_dbg("EHCI:handshake timeout!!\n");
dump_qh(ehci->async);
dump_qh(ehci->asyncqh);
//BUG();
return -ETIMEDOUT;
}
#include "ehci-mem.c"
/* one-time init, only for memory state */
static int ehci_init(void)
{
int retval;
if( (retval = ehci_mem_init()) < 0 )
return retval;
/*
* dedicate a qh for the async ring head, since we couldn't unlink
* a 'real' qh without stopping the async schedule [4.8]. use it
* as the 'reclamation list head' too.
* its dummy is used in hw_alt_next of many tds, to prevent the qh
* from automatically advancing to the next td after short reads.
*/
ehci->async->hw_next = QH_NEXT( ehci->async->qh_dma);
ehci->async->hw_info1 = cpu_to_hc32( QH_HEAD);
ehci->async->hw_token = cpu_to_hc32( QTD_STS_HALT);
ehci->async->hw_qtd_next = EHCI_LIST_END();
ehci->async->hw_alt_next = EHCI_LIST_END();//QTD_NEXT( ehci->async->dummy->qtd_dma);
if( ehci->ctrl_buffer != NULL )
USB_Free( ehci->ctrl_buffer );
ehci->ctrl_buffer = USB_Alloc(sizeof(usbctrlrequest));
ehci->command = 0;
ehci_writel( 0x00800002, &ehci->regs->command);
ehci_writel( ehci->periodic_dma, &ehci->regs->frame_list);
ehci_writel( ehci->async->qh_dma, &ehci->regs->async_next);
ehci_writel( 0x00010009, &ehci->regs->command);
ehci_writel( 1, &ehci->regs->configured_flag);
ehci_writel( 0x00010029, &ehci->regs->command);
return 0;
}
/* fill a qtd, returning how much of the buffer we were able to queue up */
static int qtd_fill( struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token, int maxpacket)
{
int i, count;
u64 addr = buf;
//ehci_dbg("fill qtd with dma %X len %X\n",buf,len);
/* one buffer entry per 4K ... first might be short or unaligned */
qtd->hw_buf[0] = cpu_to_hc32( (u32)addr);
qtd->hw_buf_hi[0] = 0;
count = 0x1000 - (buf & 0x0fff); /* rest of that page */
if (likely (len < count)) /* ... iff needed */
count = len;
else {
buf += 0x1000;
buf &= ~0x0fff;
/* per-qtd limit: from 16K to 20K (best alignment) */
for (i = 1; count < len && i < 5; i++) {
addr = buf;
qtd->hw_buf[i] = cpu_to_hc32( (u32)addr);
qtd->hw_buf_hi[i] = cpu_to_hc32(
(u32)(addr >> 32));
buf += 0x1000;
if ((count + 0x1000) < len)
count += 0x1000;
else
count = len;
}
/* short packets may only terminate transfers */
if (count != len)
count -= (count % maxpacket);
}
qtd->hw_token = cpu_to_hc32( (count << 16) | token);
qtd->length = count;
return count;
}
// high bandwidth multiplier, as encoded in highspeed endpoint descriptors
#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03))
// ... and packet size, for any kind of endpoint descriptor
#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff)
/*
* reverse of qh_urb_transaction: free a list of TDs.
* also count the actual transfer length.
*
*/
static void qh_end_transfer (void)
{
struct ehci_qtd *qtd;
struct ehci_qh *qh = ehci->asyncqh;
u32 token;
int error = 0;
for(qtd = qh->qtd_head; qtd; qtd = qtd->next)
{
token = hc32_to_cpu( qtd->hw_token);
if (likely (QTD_PID (token) != 2))
qtd->urb->actual_length += qtd->length - QTD_LENGTH (token);
if (!(qtd->length ==0 && ((token & 0xff)==QTD_STS_HALT)) && (token & QTD_STS_HALT))
{
ehci_dbg("EHCI:qtd error!:");
if (token & QTD_STS_BABBLE)
{
/* FIXME "must" disable babbling device's port too */
ehci_dbg(" BABBLE");
}
if (token & QTD_STS_MMF)
{
/* fs/ls interrupt xfer missed the complete-split */
ehci_dbg(" missed micro frame");
}
if (token & QTD_STS_DBE)
{
ehci_dbg(" databuffer error");
}
if (token & QTD_STS_XACT)
{
ehci_dbg(" wrong ack");
}
if (QTD_CERR (token)==0)
ehci_dbg(" toomany errors");
ehci_dbg("\n");
error = -1;
}
}
if(error)
{
dump_qh(ehci->asyncqh);
qtd->urb->actual_length = error;
}
ehci->qtd_used = 0;
}
/*
* create a list of filled qtds for this URB; won't link into qh.
*/
struct ehci_qtd *qh_urb_transaction (
struct ehci_urb *urb
) {
struct ehci_qtd *qtd, *qtd_prev;
struct ehci_qtd *head;
dma_addr_t buf;
int len, maxpacket;
int is_input;
u32 token;
/*
* URBs map to sequences of QTDs: one logical transaction
*/
head = qtd = ehci_qtd_alloc ();
qtd->urb = urb;
urb->actual_length = 0;
token = QTD_STS_ACTIVE;
token |= (EHCI_TUNE_CERR << 10);
/* for split transactions, SplitXState initialized to zero */
len = urb->transfer_buffer_length;
is_input = urb->input;
if (urb->ep==0) {/* is control */
/* SETUP pid */
qtd_fill( qtd, urb->setup_dma, sizeof (usbctrlrequest), token | (2 /* "setup" */ << 8), 8);
/* ... and always at least one more pid */
token ^= QTD_TOGGLE;
qtd_prev = qtd;
qtd = ehci_qtd_alloc ();
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma);
qtd_prev->next = qtd;
/* for zero length DATA stages, STATUS is always IN */
if (len == 0)
token |= (1 /* "in" */ << 8);
}
/*
* data transfer stage: buffer setup
*/
buf = urb->transfer_dma;
if (is_input)
token |= (1 /* "in" */ << 8);
/* else it's already initted to "out" pid (0 << 8) */
maxpacket = max_packet(urb->maxpacket);
/*
* buffer gets wrapped in one or more qtds;
* last one may be "short" (including zero len)
* and may serve as a control status ack
*/
for (;;) {
int this_qtd_len;
this_qtd_len = qtd_fill( qtd, buf, len, token, maxpacket);
len -= this_qtd_len;
buf += this_qtd_len;
/*
* short reads advance to a "magic" dummy instead of the next
* qtd ... that forces the queue to stop, for manual cleanup.
* (this will usually be overridden later.)
*/
if (is_input)
qtd->hw_alt_next = ehci->asyncqh->hw_alt_next;
/* qh makes control packets use qtd toggle; maybe switch it */
if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0)
token ^= QTD_TOGGLE;
if (likely (len <= 0))
break;
qtd_prev = qtd;
qtd = ehci_qtd_alloc ();
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma);
qtd_prev->next = qtd;
}
qtd->hw_alt_next = EHCI_LIST_END();
/*
* control requests may need a terminating data "status" ack;
* bulk ones may need a terminating short packet (zero length).
*/
if (likely (urb->transfer_buffer_length != 0)) {
int one_more = 0;
if (urb->ep==0) {
one_more = 1;
token ^= 0x0100; /* "in" <--> "out" */
token |= QTD_TOGGLE; /* force DATA1 */
} else if(!(urb->transfer_buffer_length % maxpacket)) {
//one_more = 1;
}
if (one_more) {
qtd_prev = qtd;
qtd = ehci_qtd_alloc ();
qtd->urb = urb;
qtd_prev->hw_next = QTD_NEXT( qtd->qtd_dma);
qtd_prev->next = qtd;
/* never any data in such packets */
qtd_fill( qtd, 0, 0, token, 0);
}
}
/* by default, enable interrupt on urb completion */
qtd->hw_token |= cpu_to_hc32( QTD_IOC);
return head;
}
int ehci_do_urb ( struct ehci_device *dev, struct ehci_urb *urb)
{
struct ehci_qh *qh;
struct ehci_qtd *qtd;
u32 info1 = 0, info2 = 0;
int is_input;
int maxp = 0;
int retval;
//ehci_dbg ("do urb %X %X ep %X\n",urb->setup_buffer,urb->transfer_buffer,urb->ep);
if(urb->ep==0) //control message
urb->setup_dma = ehci_dma_map_to(urb->setup_buffer,sizeof (usbctrlrequest));
if( urb->transfer_buffer_length )
{
if(urb->input)
urb->transfer_dma = ehci_dma_map_to( urb->transfer_buffer, urb->transfer_buffer_length );
else
urb->transfer_dma = ehci_dma_map_from( urb->transfer_buffer, urb->transfer_buffer_length );
}
qh = ehci->asyncqh;
memset(qh,0,12*4);
qtd = qh_urb_transaction( urb );
qh->qtd_head = qtd;
info1 |= ((urb->ep)&0xf)<<8;
info1 |= dev->id;
is_input = urb->input;
maxp = urb->maxpacket;
info1 |= (2 << 12); /* EPS "high" */
if(urb->ep==0)// control
{
info1 |= (EHCI_TUNE_RL_HS << 28);
info1 |= 64 << 16; /* usb2 fixed maxpacket */
info1 |= 1 << 14; /* toggle from qtd */
info2 |= (EHCI_TUNE_MULT_HS << 30);
} else {//bulk
info1 |= (EHCI_TUNE_RL_HS << 28);
/* The USB spec says that high speed bulk endpoints
* always use 512 byte maxpacket. But some device
* vendors decided to ignore that, and MSFT is happy
* to help them do so. So now people expect to use
* such nonconformant devices with Linux too; sigh.
*/
info1 |= max_packet(maxp) << 16;
info2 |= (EHCI_TUNE_MULT_HS << 30);
}
//ehci_dbg("HW info: %08X\n",info1);
qh->hw_info1 = cpu_to_hc32( info1);
qh->hw_info2 = cpu_to_hc32( info2);
qh->hw_next = QH_NEXT( qh->qh_dma );
qh->hw_qtd_next = QTD_NEXT( qtd->qtd_dma );
qh->hw_alt_next = EHCI_LIST_END();
if( urb->ep != 0 )
{
if( get_toggle( dev, urb->ep ) )
qh->hw_token |= cpu_to_hc32(QTD_TOGGLE);
else
qh->hw_token &= ~cpu_to_hc32(QTD_TOGGLE);
//ehci_dbg("toggle for ep %x: %d %x\n", urb->ep, get_toggle(dev,urb->ep), qh->hw_token );
}
qh->hw_token &= cpu_to_hc32( QTD_TOGGLE | QTD_STS_PING);
qh->hw_next = QH_NEXT(ehci->async->qh_dma);
ehci_dma_map_bidir(qh,sizeof(struct ehci_qh));
for(qtd = qh->qtd_head; qtd; qtd = qtd->next)
ehci_dma_map_bidir(qtd,sizeof(struct ehci_qtd));
// start (link qh)
ehci->async->hw_next = QH_NEXT(qh->qh_dma);
ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh));
retval = handshake(&ehci->regs->status,STS_INT,STS_INT,1000*1000);
//print_hex_dump_bytes ("qh mem",0,(void*)qh,17*4);
//retval = poll_transfer_end(1000*1000);
ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh));
ehci_dma_unmap_bidir(qh->qh_dma,sizeof(struct ehci_qh));
for(qtd = qh->qtd_head; qtd; qtd = qtd->next)
ehci_dma_unmap_bidir(qtd->qtd_dma,sizeof(struct ehci_qtd));
// stop (unlink qh)
ehci->async->hw_next = QH_NEXT(ehci->async->qh_dma);
ehci_dma_map_bidir(ehci->async,sizeof(struct ehci_qh));
ehci_dma_unmap_bidir(ehci->async->qh_dma,sizeof(struct ehci_qh));
// ack
ehci_writel( STS_RECL|STS_IAA|STS_INT, &ehci->regs->status);
if(urb->ep!=0)
{
set_toggle(dev,urb->ep,(qh->hw_token & cpu_to_hc32(QTD_TOGGLE))?1:0);
//ehci_dbg("toggle for ep %x: %d %d %x %X\n",urb->ep,get_toggle(dev,urb->ep),(qh->hw_token & cpu_to_hc32(QTD_TOGGLE)),qh->hw_token,dev->toggles);
}
if( retval >= 0 )
// wait hc really stopped
retval = handshake(&ehci->regs->async_next,~0,ehci->async->qh_dma,1*1000);
//release memory, and actualise urb->actual_length
qh_end_transfer();
if(urb->transfer_buffer_length)
{
if(urb->input)
ehci_dma_unmap_to(urb->transfer_dma,urb->transfer_buffer_length);
else
ehci_dma_unmap_from(urb->transfer_dma,urb->transfer_buffer_length);
}
if( urb->ep == 0 ) //control message
ehci_dma_unmap_to(urb->setup_dma,sizeof (usbctrlrequest));
if(retval==0)
{
return urb->actual_length;
}
ehci_dbg ( "EHCI:unsuccessfull urb %d!!\n", retval);
return retval;
}
s32 ehci_control_message(struct ehci_device *dev,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *buf)
{
struct ehci_urb urb;
int ret;
usbctrlrequest *req = (void*)0x17C0;
if( verbose )
ehci_dbg("control msg: rt%02X r%02X v%04X i%04X s%04x %p\n", bmRequestType, bmRequest, wValue, wIndex, wLength, buf );
u32 *_req = (u32*)req;
_req[0] = (bmRequestType<<24) | (bmRequest<<16) | swab16(wValue);
_req[1] = (swab16(wIndex) << 16) | swab16(wLength);
urb.setup_buffer = req;
urb.ep = 0;
urb.input = (bmRequestType&USB_CTRLTYPE_DIR_DEVICE2HOST)!=0;
urb.maxpacket = 64;
urb.transfer_buffer_length = wLength;
if( urb.transfer_buffer_length )
{
if( ((u32)buf >> 28 ) == 0xF )
{
urb.transfer_buffer = USB_Alloc( wLength );
//dbgprintf("memcpy(%p,%p,%u)\n", urb.transfer_buffer, buf, wLength );
memcpy( urb.transfer_buffer, buf, wLength );
ret = ehci_do_urb( dev, &urb );
memcpy( buf, urb.transfer_buffer, wLength );
USB_Free( urb.transfer_buffer );
} else {
urb.transfer_buffer = buf;
ret = ehci_do_urb( dev, &urb );
}
//hexdump( buf, wLength > 0x20 ? 0x20 : wLength );
} else {
urb.transfer_buffer = NULL;
ret = ehci_do_urb( dev, &urb );
}
return ret;
}
s32 ehci_bulk_message(struct ehci_device *dev,u8 bEndpoint,u16 wLength,void *rpData)
{
struct ehci_urb urb;
s32 ret;
urb.setup_buffer = NULL;
urb.ep = bEndpoint;
urb.input = (bEndpoint&0x80)!=0;
urb.maxpacket = 512;
urb.transfer_buffer_length = wLength;
urb.transfer_buffer = rpData;
if (verbose)
ehci_dbg ( "bulk msg: ep:%02X size:%02X addr:%04X\n", bEndpoint, wLength, urb.transfer_buffer );
// hexdump( urb.transfer_buffer, urb.transfer_buffer_length );
if( ((u32)rpData >> 28) == 0xF )
{
memcpy( (void*)0xFE0, rpData, wLength );
urb.transfer_buffer = (u8*)0xFE0;
ret = ehci_do_urb( dev, &urb );
memcpy( rpData, (void*)0xFE0, wLength );
} else {
ret = ehci_do_urb( dev, &urb );
}
// hexdump( urb.transfer_buffer, urb.transfer_buffer_length );
if (verbose)
ehci_dbg ( "==>%d\n", ret);
return ret;
}
int ehci_reset_port(int port)
{
u32 __iomem *status_reg = &ehci->regs->port_status[port];
struct ehci_device *dev = &ehci->devices[port];
u32 status = ehci_readl(status_reg);
int retval = 0;
dev->id = 0;
if ((PORT_OWNER&status) || !(PORT_CONNECT&status))
{
int retries = 10;
while (!(PORT_CONNECT&status) && retries > 0)
{
msleep(1000); // sleep 1 second
status = ehci_readl(status_reg);
ehci_dbg ( "EHCI:port %d status at retry %d %X \n", port,retries,status);
retries--;
}
if( retries <= 0 )
{
ehci_writel( PORT_OWNER, status_reg);
ehci_dbg ( "EHCI:port %d had no usb2 device connected at startup %X \n", port,ehci_readl(status_reg) );
return -ENODEV;// no USB2 device connected
}
}
ehci_dbg ( "EHCI:port %d has usb2 device connected! reset it...\n", port);
ehci_writel( 0x1803,status_reg);
while ((ehci_readl(status_reg) & 0x1801) != 0x1801)
{
ehci_dbg ( "EHCI:Waiting for port %d to settle...(%04x)\n", port, ehci_readl(status_reg));
ehci_writel( 0x1803,status_reg);
msleep(500);
}
ehci_writel( 0x1903,status_reg);
//ehci_writel( PORT_OWNER|PORT_POWER|PORT_RESET,status_reg);
msleep(50);// wait 50ms for the reset sequence
ehci_writel( 0x1001,status_reg);
retval = handshake( status_reg, PORT_RESET, 0, 2000);
if (retval != 0)
{
ehci_dbg ( "EHCI:port %d reset error %d\n", port, retval);
return retval;
}
ehci_dbg ( "EHCI:port %d reseted status:%04x...\n", port,ehci_readl(status_reg));
msleep(50);
// now the device has the default device id
retval = ehci_control_message( dev, USB_CTRLTYPE_DIR_DEVICE2HOST, USB_REQ_GETDESCRIPTOR, USB_DT_DEVICE<<8, 0, sizeof(dev->desc), &dev->desc );
if (retval < 0)
{
ehci_dbg("EHCI:unable to get device desc...\n");
return retval;
}
retval = ehci_control_message( dev, USB_CTRLTYPE_DIR_HOST2DEVICE, USB_REQ_SETADDRESS,port+1,0,0,0);
if (retval < 0)
{
ehci_dbg("EHCI:unable to set device addr...\n");
return retval;
}
dev->toggles = 0;
dev->id = port+1;
ehci_dbg ( "EHCI:device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct));
return retval;
}
int ehci_reset_device(struct ehci_device *dev)
{
return ehci_reset_port(dev->port);
}
#include "usbstorage.h"
int ehci_discover(void)
{
int i;
int ret = 0 ;
// precondition: the ehci should be halted
for(i = 0;i<ehci->num_port; i++)
{
struct ehci_device *dev = &ehci->devices[i];
dev->port = i;
ret = ehci_reset_port(i);
if( ret != -ENODEV )
break;
}
return ret;
}
/* wii: quickly release non ehci or not connected ports,
as we can't kick OHCI drivers laters if we discover a device for them.
*/
int ehci_release_ports(void)
{
int i;
u32 __iomem *status_reg = &ehci->regs->port_status[2];
while(ehci_readl(&ehci->regs->port_status[2]) == 0x1000);// wait port 2 to init
msleep(1);// wait another msec..
for(i = 0;i<ehci->num_port; i++){
status_reg = &ehci->regs->port_status[i];
u32 status = ehci_readl(status_reg);
if (i==2 || !(PORT_CONNECT&status) || PORT_USB11(status))
ehci_writel( PORT_OWNER,status_reg); // release port.
}
return 0;
}
int ehci_open_device(int vid,int pid,int fd)
{
int i;
for(i=0;i<ehci->num_port;i++)
{
ehci_dbg("try device: %d\n",i);
if(ehci->devices[i].fd == 0 &&
le16_to_cpu(ehci->devices[i].desc.idVendor) == vid &&
le16_to_cpu(ehci->devices[i].desc.idProduct) == pid)
{
ehci_dbg("found device: %x %x\n",vid,pid);
ehci->devices[i].fd = fd;
return fd;
}
}
return -6;
}
int ehci_close_device(struct ehci_device *dev)
{
if (dev)
dev->fd = 0;
return 0;
}
void * ehci_fd_to_dev(int fd)
{
int i;
for(i=0;i<ehci->num_port;i++)
{
struct ehci_device *dev = &ehci->devices[i];
ehci_dbg ( "EHCI:device %d:fd:%d %X %X...\n", dev->id,dev->fd,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct));
if(dev->fd == fd){
return dev;
}
}
ehci_dbg("unkown fd! %d\n",fd);
return 0;
}
#define g_ehci #error
int ehci_get_device_list(u8 maxdev,u8 b0,u8*num,u16*buf)
{
int i,j = 0;
for(i=0;i<ehci->num_port && j<maxdev ;i++)
{
struct ehci_device *dev = &ehci->devices[i];
if(dev->id != 0){
ehci_dbg ( "EHCI:device %d: %X %X...\n", dev->id,le16_to_cpu(dev->desc.idVendor),le16_to_cpu(dev->desc.idProduct));
buf[j*4] = 0;
buf[j*4+1] = 0;
buf[j*4+2] = le16_to_cpu(dev->desc.idVendor);
buf[j*4+3] = le16_to_cpu(dev->desc.idProduct);
j++;
}
}
ehci_dbg("EHCI:found %d devices\n",j);
*num = j;
return 0;
}
#include "usb.c"
#include "usbstorage.c"

282
ehci.h
View File

@ -1,282 +0,0 @@
/*
* Copyright (c) 2009 Kwiirk
* Original Copyright (c) 2001-2002 by David Brownell
*
* 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 "ehci_types.h"
#ifndef __LINUX_EHCI_HCD_H
#define __LINUX_EHCI_HCD_H
/* definitions used for the EHCI driver */
/*
* __hc32 and __hc16 are "Host Controller" types, they may be equivalent to
* __leXX (normally) or __beXX (given EHCI_BIG_ENDIAN_DESC), depending on
* the host controller implementation.
*
* To facilitate the strongest possible byte-order checking from "sparse"
* and so on, we use __leXX unless that's not practical.
*/
#ifdef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
typedef __u32 __bitwise __hc32;
typedef __u16 __bitwise __hc16;
#else
#define __hc32 __le32
#define __hc16 __le16
#endif
#define EHCI_MAX_ROOT_PORTS 4 /* see HCS_N_PORTS */
#define EHCI_MAX_QTD 8
#include "usb.h"
struct ehci_device{
usb_devdesc desc;
int id;
int port;
int fd;
u32 toggles;
};
#define ep_bit(ep) (((ep)&0xf)+(((ep)>>7)?16:0))
#define get_toggle(dev,ep) (((dev)->toggles>>ep_bit(ep))&1)
#define set_toggle(dev,ep,v) (dev)->toggles = ((dev)->toggles &(~(1<<ep_bit(ep)))) | ((v)<<ep_bit(ep))
struct ehci_urb{
void* setup_buffer;
dma_addr_t setup_dma;
void* transfer_buffer;
dma_addr_t transfer_dma;
u32 transfer_buffer_length;
u32 actual_length;
u8 ep;
u8 input;
u32 maxpacket;
};
struct ehci_hcd { /* one per controller */
/* glue to PCI and HCD framework */
void __iomem *_regs;
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
struct ehci_dbg_port __iomem *debug;
void *device;
__u32 hcs_params; /* cached register copy */
/* async schedule support */
struct ehci_qh *async; // the head never gets a qtd inside.
struct ehci_qh *asyncqh;
struct ehci_qtd *qtds[EHCI_MAX_QTD];
int qtd_used;
unsigned long next_statechange;
u32 command;
/* HW need periodic table initialised even if we dont use it @todo:is it really true? */
#define DEFAULT_I_TDPS 1024 /* some HCs can do less */
__hc32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
u8 num_port;
struct ehci_device devices[EHCI_MAX_ROOT_PORTS]; /* the attached device list per port */
void *ctrl_buffer; /* pre allocated buffer for control messages */
};
/*-------------------------------------------------------------------------*/
#include "ehci_defs.h"
/*-------------------------------------------------------------------------*/
#define QTD_NEXT( dma) cpu_to_hc32( (u32)dma)
/*
* EHCI Specification 0.95 Section 3.5
* QTD: describe data transfer components (buffer, direction, ...)
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
*
* These are associated only with "QH" (Queue Head) structures,
* used with control, bulk, and interrupt transfers.
*/
struct ehci_qtd {
/* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.5.1 */
__hc32 hw_alt_next; /* see EHCI 3.5.2 */
__hc32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
#define QTD_CERR(tok) (((tok)>>10) & 0x3)
#define QTD_PID(tok) (((tok)>>8) & 0x3)
#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
#define QTD_STS_HALT (1 << 6) /* halted on error */
#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
#define QTD_STS_STS (1 << 1) /* split transaction state */
#define QTD_STS_PING (1 << 0) /* issue PING? */
#define ACTIVE_BIT(ehci) cpu_to_hc32( QTD_STS_ACTIVE)
#define HALT_BIT(ehci) cpu_to_hc32( QTD_STS_HALT)
#define STATUS_BIT(ehci) cpu_to_hc32( QTD_STS_STS)
__hc32 hw_buf [5]; /* see EHCI 3.5.4 */
__hc32 hw_buf_hi [5]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
struct ehci_qtd *next; /* sw qtd list */
struct ehci_urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
} __attribute__ ((aligned (32)));
/* mask NakCnt+T in qh->hw_alt_next */
#define QTD_MASK(ehci) cpu_to_hc32 ( ~0x1f)
#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
/*-------------------------------------------------------------------------*/
/* type tag from {qh,itd,sitd,fstn}->hw_next */
#define Q_NEXT_TYPE(dma) ((dma) & cpu_to_hc32( 3 << 1))
/*
* Now the following defines are not converted using the
* __constant_cpu_to_le32() macro anymore, since we have to support
* "dynamic" switching between be and le support, so that the driver
* can be used on one system with SoC EHCI controller using big-endian
* descriptors as well as a normal little-endian PCI EHCI controller.
*/
/* values for that type tag */
#define Q_TYPE_ITD (0 << 1)
#define Q_TYPE_QH (1 << 1)
#define Q_TYPE_SITD (2 << 1)
#define Q_TYPE_FSTN (3 << 1)
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(dma) (cpu_to_hc32( (((u32)dma)&~0x01f)|Q_TYPE_QH))
/* for periodic/async schedules and qtd lists, mark end of list */
#define EHCI_LIST_END() cpu_to_hc32( 1) /* "null pointer" to hw */
/*
* Entries in periodic shadow table are pointers to one of four kinds
* of data structure. That's dictated by the hardware; a type tag is
* encoded in the low bits of the hardware's periodic schedule. Use
* Q_NEXT_TYPE to get the tag.
*
* For entries in the async schedule, the type tag always says "qh".
*/
union ehci_shadow {
struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
__hc32 *hw_next; /* (all types) */
void *ptr;
};
/*-------------------------------------------------------------------------*/
/*
* EHCI Specification 0.95 Section 3.6
* QH: describes control/bulk/interrupt endpoints
* See Fig 3-7 "Queue Head Structure Layout".
*
* These appear in both the async and (for interrupt) periodic schedules.
*/
struct ehci_qh {
/* first part defined by EHCI spec */
__hc32 hw_next; /* see EHCI 3.6.1 */
__hc32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
__hc32 hw_info2; /* see EHCI 3.6.2 */
#define QH_SMASK 0x000000ff
#define QH_CMASK 0x0000ff00
#define QH_HUBADDR 0x007f0000
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
__hc32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct ehci_qtd) */
__hc32 hw_qtd_next;
__hc32 hw_alt_next;
__hc32 hw_token;
__hc32 hw_buf [5];
__hc32 hw_buf_hi [5];
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
struct ehci_qtd *qtd_head; /* sw qtd list */
struct ehci_hcd *ehci;
#define NO_FRAME ((unsigned short)~0) /* pick new start */
} __attribute__ ((aligned (32)));
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/* cpu to ehci */
#define cpu_to_hc32(b) cpu_to_le32(b)
#define hc32_to_cpu(b) le32_to_cpu(b)
#define hc32_to_cpup(b) le32_to_cpu(*(b))
/*-------------------------------------------------------------------------*/
/* os specific functions */
void*ehci_maligned(int size,int alignement,int crossing);
dma_addr_t ehci_virt_to_dma(void *);
dma_addr_t ehci_dma_map_to(void *buf,size_t len);
dma_addr_t ehci_dma_map_from(void *buf,size_t len);
dma_addr_t ehci_dma_map_bidir(void *buf,size_t len);
void ehci_dma_unmap_to(dma_addr_t buf,size_t len);
void ehci_dma_unmap_from(dma_addr_t buf,size_t len);
void ehci_dma_unmap_bidir(dma_addr_t buf,size_t len);
/* extern API */
s32 ehci_control_message(struct ehci_device *dev,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *buf);
s32 ehci_bulk_message(struct ehci_device *dev,u8 bEndpoint,u16 wLength,void *rpData);
int ehci_discover(void);
int ehci_get_device_list(u8 maxdev,u8 b0,u8*num,u16*buf);
extern struct ehci_hcd *ehci; /* @todo put ehci as a static global and remove ehci from APIs.. */
extern int ehci_open_device(int vid,int pid,int fd);
extern int ehci_close_device(struct ehci_device *dev);
extern void * ehci_fd_to_dev(int fd);
extern int ehci_release_ports(void);
/* UMS API */
s32 USBStorage_Init(void);
s32 USBStorage_Get_Capacity(u32*sector_size);
s32 USBStorage_Read_Sectors(u32 sector, u32 numSectors, void *buffer);
s32 USBStorage_Read_Stress(u32 sector, u32 numSectors, void *buffer);
s32 USBStorage_Write_Sectors(u32 sector, u32 numSectors, const void *buffer);
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
/*-------------------------------------------------------------------------*/
#endif /* __LINUX_EHCI_HCD_H */

View File

@ -1,160 +0,0 @@
/*
* Copyright (c) 2001-2002 by David Brownell
*
* 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.
*/
#ifndef __LINUX_USB_EHCI_DEF_H
#define __LINUX_USB_EHCI_DEF_H
/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
/* Section 2.2 Host Controller Capability Registers */
struct ehci_caps {
/* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses.
*/
u32 hc_capbase;
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
u8 portroute [8]; /* nibbles for routing - offset 0xC */
} __attribute__ ((packed));
/* Section 2.3 Host Controller Operational Registers */
struct ehci_regs {
/* USBCMD: offset 0x00 */
u32 command;
/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
#define CMD_PARK (1<<11) /* enable "park" on async qh */
#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
#define CMD_ASE (1<<5) /* async schedule enable */
#define CMD_PSE (1<<4) /* periodic schedule enable */
/* 3:2 is periodic frame list size */
#define CMD_RESET (1<<1) /* reset HC not bus */
#define CMD_RUN (1<<0) /* start/stop HC */
/* USBSTS: offset 0x04 */
u32 status;
#define STS_ASS (1<<15) /* Async Schedule Status */
#define STS_PSS (1<<14) /* Periodic Schedule Status */
#define STS_RECL (1<<13) /* Reclamation */
#define STS_HALT (1<<12) /* Not running (any reason) */
/* some bits reserved */
/* these STS_* flags are also intr_enable bits (USBINTR) */
#define STS_IAA (1<<5) /* Interrupted on async advance */
#define STS_FATAL (1<<4) /* such as some PCI access errors */
#define STS_FLR (1<<3) /* frame list rolled over */
#define STS_PCD (1<<2) /* port change detect */
#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
#define STS_INT (1<<0) /* "normal" completion (short, ...) */
/* USBINTR: offset 0x08 */
u32 intr_enable;
/* FRINDEX: offset 0x0C */
u32 frame_index; /* current microframe number */
/* CTRLDSSEGMENT: offset 0x10 */
u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */
u32 frame_list; /* points to periodic list */
/* ASYNCLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */
u32 reserved [9];
/* CONFIGFLAG: offset 0x40 */
u32 configured_flag;
#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
/* PORTSC: offset 0x44 */
u32 port_status [0]; /* up to N_PORTS */
/* 31:23 reserved */
#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
/* 19:16 for port testing */
#define PORT_LED_OFF (0<<14)
#define PORT_LED_AMBER (1<<14)
#define PORT_LED_GREEN (2<<14)
#define PORT_LED_MASK (3<<14)
#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
#define PORT_POWER (1<<12) /* true: has power (see PPC) */
#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
/* 9 reserved */
#define PORT_RESET (1<<8) /* reset port */
#define PORT_SUSPEND (1<<7) /* suspend port */
#define PORT_RESUME (1<<6) /* resume it */
#define PORT_OCC (1<<5) /* over current change */
#define PORT_OC (1<<4) /* over current active */
#define PORT_PEC (1<<3) /* port enable change */
#define PORT_PE (1<<2) /* port enable */
#define PORT_CSC (1<<1) /* connect status change */
#define PORT_CONNECT (1<<0) /* device connected */
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
} __attribute__ ((packed));
#define USBMODE 0x68 /* USB Device mode */
#define USBMODE_SDIS (1<<3) /* Stream disable */
#define USBMODE_BE (1<<2) /* BE/LE endianness select */
#define USBMODE_CM_HC (3<<0) /* host controller mode */
#define USBMODE_CM_IDLE (0<<0) /* idle state */
/* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.)
*/
struct ehci_dbg_port {
u32 control;
#define DBGP_OWNER (1<<30)
#define DBGP_ENABLED (1<<28)
#define DBGP_DONE (1<<16)
#define DBGP_INUSE (1<<10)
#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
# define DBGP_ERR_BAD 1
# define DBGP_ERR_SIGNAL 2
#define DBGP_ERROR (1<<6)
#define DBGP_GO (1<<5)
#define DBGP_OUT (1<<4)
#define DBGP_LEN(x) (((x)>>0)&0x0f)
u32 pids;
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
u32 data03;
u32 data47;
u32 address;
#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
} __attribute__ ((packed));
#endif /* __LINUX_USB_EHCI_DEF_H */

View File

@ -1,55 +0,0 @@
#ifndef __EHCI_TYPES_H__
#define __EHCI_TYPES_H__
#include "global.h"
/* linux kernel types needed by our code */
#define __iomem
typedef unsigned long uint32_t;
#if 0
typedef unsigned long u32;
typedef signed long s32;
typedef unsigned short u16;
typedef unsigned char u8;
typedef char s8;
typedef unsigned long long u64;
#endif
#define __u32 u32
#define __le32 u32
#define dma_addr_t u32
#define __GNUG__
typedef u32 spinlock_t;
typedef enum
{
GFP_KERNEL=1
}gfp_t;
struct timer_list
{
int time;
};
enum{
ENODEV =1,
ETIMEDOUT,
EINVAL,
ENOMEM,
};
#define jiffies 0
#define likely(x) (x)
#define unlikely(x) (x)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#endif

View File

@ -1,81 +0,0 @@
OUTPUT_FORMAT("elf32-bigarm")
OUTPUT_ARCH(arm)
ENTRY(_start)
__stack_size = 0x4000;
MEMORY
{
sram : ORIGIN = 0xFFFF0000, LENGTH = 0x10000
stack : ORIGIN = 0xFFFE0000, LENGTH = 0x4000
}
PHDRS
{
sram PT_LOAD AT ( 0xFFFF0000 ) ;
stack PT_LOAD AT ( 0xFFFE0000 ) ;
}
SECTIONS
{
.init :
{
*(.init)
. = ALIGN(4);
} >sram :sram
.text :
{
*(.text*)
*(.text.*)
*(.gnu.warning)
*(.gnu.linkonce.t*)
*(.glue_7)
*(.glue_7t)
. = ALIGN(4);
} >sram :sram
.rodata :
{
*(.rodata)
*all.rodata*(*)
*(.roda)
*(.rodata.*)
*(.gnu.linkonce.r*)
. = ALIGN(4);
} >sram :sram
.data :
{
*(.data)
*(.data.*)
*(.gnu.linkonce.d*)
. = ALIGN(4);
} >sram :sram
.bss :
{
__bss_start = . ;
*(.dynbss)
*(.gnu.linkonce.b*)
*(.bss*)
*(COMMON)
. = ALIGN(4);
__bss_end = . ;
} >sram :sram
.stack :
{
__stack_end = .;
. = . +__stack_size;
. = ALIGN(4);
__stack_addr = .;
} >stack :stack
/DISCARD/ :
{
*(.ARM.exidx*)
*(.ARM.extab*)
}
}

View File

@ -1,92 +0,0 @@
/*
EHCI glue. A bit hacky for the moment. needs cleaning..
Copyright (C) 2008 kwiirk.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "tiny_ehci_glue.h"
#define static
#define inline extern
int verbose=0;
void BUG(void)
{
dbgprintf("bug\n");
while(1);
}
#define BUG_ON(a) if(a)BUG()
void msleep(int msec)
{
udelay(2048*msec);
}
extern u32 __exe_start_virt__;
extern u32 __ram_start_virt__;
extern u32 ios_thread_stack;
void print_hex_dump_bytes(char *header,int prefix,u8 *buf,int len)
{
int i;
if (len>0x100)len=0x100;
dbgprintf("%s %08X\n",header,(u32)buf);
for (i=0;i<len;i++){
dbgprintf("%02x ",buf[i]);
if((i&0xf) == 0xf)
dbgprintf("\n");
}
dbgprintf("\n");
}
#define DUMP_PREFIX_OFFSET 1
#include "ehci.h"
#define ehci_readl(a) ((*((volatile u32*)(a))))
//#define ehci_writel(e,v,a) do{msleep(40);dbgprintf("writel %08X %08X\n",a,v);*((volatile u32*)(a))=(v);}while(0)
#define ehci_writel(v,a) do{*((volatile u32*)(a))=(v);}while(0)
struct ehci_hcd _ehci;
struct ehci_hcd *ehci;
#include "ehci.c"
int usb_os_init(void);
int tiny_ehci_init(void)
{
int retval;
ehci = &_ehci;
//memset(ehci,0,sizeof(*ehci));
if(usb_os_init()<0)
return 0;
ehci->caps = (void*)0x0D040000;
ehci->regs = (void*)(0x0D040000 + HC_LENGTH(ehci_readl(&ehci->caps->hc_capbase)));
ehci->num_port = 4;
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = ehci_readl( &ehci->caps->hcs_params );
/* data structure init */
retval = ehci_init();
if (retval)
return retval;
ehci_release_ports(); //quickly release none usb2 port
return 0;
}

View File

@ -1,25 +0,0 @@
#ifndef _TEGLUE_
#define _TEGLUE_
#include "string.h"
#include "ehci_types.h"
#include "utils.h"
#include "vsprintf.h"
int tiny_ehci_init(void);
#define readl(a) (*((volatile u32*)(a)))
#define writel(v,a) do{*((volatile u32*)(a))=(v);}while(0)
#define ehci_dbg(a...) dbgprintf(a)
#define printk(a...) dbgprintf(a)
#define get_timer() (*(((volatile u32*)0x0D800010)))
#define cpu_to_le32(a) swab32(a)
#define le32_to_cpu(a) swab32(a)
#define cpu_to_le16(a) swab16(a)
#define le16_to_cpu(a) swab16(a)
#define cpu_to_be32(a) (a)
#define be32_to_cpu(a) (a)
#endif

244
usb.c
View File

@ -1,244 +0,0 @@
/*
This file implements libogc usb API, but with ehci direct call
most of the file credit goes to libogc devs
*/
#define __usb_control_message(fd, b, c,d, e, f, g, h, i) ehci_control_message(fd,b,c,d,e,f,g)
static s32 __usb_getdesc(struct ehci_device * fd, u8 *buffer, u8 type, u8 index, u8 size)
{
//printk("usb_get desc %X %X %p\n",type,index,buffer);
return __usb_control_message(fd, USB_ENDPOINT_IN ,USB_REQ_GETDESCRIPTOR, (type << 8) | index, 0, size, buffer, NULL, NULL);
}
s32 USB_GetDescriptors(struct ehci_device * fd, usb_devdesc *udd)
{
u8 *buffer = NULL;
u8 *ptr = NULL;
usb_configurationdesc *ucd = NULL;
usb_interfacedesc *uid = NULL;
usb_endpointdesc *ued = NULL;
s32 retval = 0;
u32 iConf, iInterface, iEndpoint;
buffer = USB_Alloc(sizeof(*udd));
if(buffer == NULL)
{
retval = -ENOMEM;
goto free_and_error;
}
retval = __usb_getdesc(fd, buffer, USB_DT_DEVICE, 0, USB_DT_DEVICE_SIZE);
if(retval < 0)
{
dbgprintf("USB:__usb_getdesc():%d failed\n", retval );
goto free_and_error;
}
memcpy( udd, buffer, USB_DT_DEVICE_SIZE );
USB_Free(buffer);
u32 *_udd = (u32*)udd;
_udd[1] = ( udd->bLength << 24 ) | ( udd->bDescriptorType << 16 ) | cpu_to_le16(udd->bcdUSB);
_udd[2] = ( cpu_to_le16(udd->idVendor) << 16 ) | cpu_to_le16(udd->idProduct);
_udd[3] = ( cpu_to_le16(udd->bcdDevice) << 16 ) | (udd->iManufacturer<<8) | udd->iProduct;
u32 _ptr = (u32)USB_Alloc( udd->bNumConfigurations * sizeof(*udd->configurations) );
//udd->configurations = USB_Alloc(udd->bNumConfigurations* sizeof(*udd->configurations));
if( _ptr == 0)
{
dbgprintf("USB:USB_Alloc():failed:%u\n", __LINE__ );
retval = -ENOMEM;
goto free_and_error;
}
_udd[4] = ( udd->iSerialNumber << 24 ) | ( udd->bNumConfigurations << 16 ) | (_ptr>>16);
_udd[5] = ((_ptr & 0xFFFF) << 16) | (_udd[5]&0xFFFF);
memset( udd->configurations, 0, udd->bNumConfigurations * sizeof(*udd->configurations) );
for( iConf = 0; iConf < udd->bNumConfigurations; iConf++)
{
buffer = USB_Alloc( USB_DT_CONFIG_SIZE );
if(buffer == NULL)
{
retval = -ENOMEM;
goto free_and_error;
}
retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, USB_DT_CONFIG_SIZE);
ucd = &udd->configurations[iConf];
memcpy( ucd, buffer, USB_DT_CONFIG_SIZE );
USB_Free( buffer );
u32 *_ucd = (u32*)ucd;
_ucd[0] = ( ucd->bLength << 24 ) | ( ucd->bDescriptorType << 16 ) | cpu_to_le16(ucd->wTotalLength);
//ucd->wTotalLength = cpu_to_le16(ucd->wTotalLength);
buffer = USB_Alloc( ucd->wTotalLength);
if(buffer == NULL)
{
retval = -ENOMEM;
goto free_and_error;
}
retval = __usb_getdesc(fd, buffer, USB_DT_CONFIG, iConf, ucd->wTotalLength);
if(retval < 0)
goto free_and_error;
ptr = buffer;
ptr += ucd->bLength;
retval = -ENOMEM;
//ucd->interfaces = USB_Alloc(ucd->bNumInterfaces* sizeof(*ucd->interfaces));
//if(ucd->interfaces == NULL)
// goto free_and_error;
u32 _ptrB = (u32)USB_Alloc(ucd->bNumInterfaces* sizeof(*ucd->interfaces));
if( _ptrB == 0 )
goto free_and_error;
_ucd[2] = ( ucd->bMaxPower << 24 ) | (_ptrB>>8);
_ucd[3] = ((_ptrB & 0xFF) << 24) | (_ucd[3]&0xFFFFFF);
//dbgprintf("ucd->interfaces:%p\n", ucd->interfaces );
memset( ucd->interfaces, 0, ucd->bNumInterfaces * sizeof(*ucd->interfaces) );
for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
{
uid = &ucd->interfaces[iInterface];
memcpy(uid, ptr, USB_DT_INTERFACE_SIZE);
ptr += uid->bLength;
//uid->endpoints = USB_Alloc(uid->bNumEndpoints* sizeof(*uid->endpoints));
//if(uid->endpoints == NULL)
// goto free_and_error;
u32 *_uid = (u32*)uid;
u32 _ptrC = (u32)USB_Alloc(uid->bNumEndpoints* sizeof(*uid->endpoints));
_uid[2] = (uid->iInterface<<24) | (_ptrC >> 8);
_uid[3] = (_ptrC<<24) | (_uid[3]&0xFFFFFF);
memset( uid->endpoints, 0, uid->bNumEndpoints * sizeof(*uid->endpoints) );
for( iEndpoint = 0; iEndpoint < uid->bNumEndpoints; iEndpoint++)
{
ued = &uid->endpoints[iEndpoint];
memcpy( ued, ptr, USB_DT_ENDPOINT_SIZE );
ptr += ued->bLength;
//ued->wMaxPacketSize = cpu_to_le16(ued->wMaxPacketSize);
u32 *_ued = (u32*)ued;
_ued[1] = (cpu_to_le16(ued->wMaxPacketSize) << 16 ) | (_ued[1] & 0xFFFF);
}
USB_Free((void*)_ptrC);
}
USB_Free((void*)_ptrB);
USB_Free( buffer);
buffer = (u8*)NULL;
}
retval = 0;
free_and_error:
if( _ptr != 0 )
USB_Free((void*)_ptr);
if(buffer != NULL)
USB_Free(buffer);
if(retval < 0)
USB_FreeDescriptors(udd);
return retval;
}
void USB_FreeDescriptors(usb_devdesc *udd)
{
int iConf, iInterface;
usb_configurationdesc *ucd;
usb_interfacedesc *uid;
if(udd->configurations != NULL)
{
for(iConf = 0; iConf < udd->bNumConfigurations; iConf++)
{
ucd = &udd->configurations[iConf];
if(ucd->interfaces != NULL)
{
for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
{
uid = &ucd->interfaces[iInterface];
if(uid->endpoints != NULL)
USB_Free(uid->endpoints);
}
USB_Free(ucd->interfaces);
}
}
USB_Free(udd->configurations);
}
}
void USB_SuspendDevice(struct ehci_device *fd)
{
return ;
}
void USB_ResumeDevice(struct ehci_device *fd)
{
return ;
}
s32 USB_WriteBlkMsg(struct ehci_device *fd,u8 bEndpoint,u16 wLength,void *rpData)
{
return ehci_bulk_message(fd,bEndpoint,wLength,rpData);
}
s32 USB_WriteCtrlMsg(struct ehci_device *fd,u8 bmRequestType,u8 bmRequest,u16 wValue,u16 wIndex,u16 wLength,void *rpData)
{
return __usb_control_message(fd,bmRequestType,bmRequest,wValue,wIndex,wLength,rpData,NULL,NULL);
}
s32 USB_GetConfiguration(struct ehci_device *fd, u8 *configuration)
{
u8 *_configuration;
s32 retval;
_configuration = USB_Alloc(1);
if(_configuration == NULL)
return -ENOMEM;
retval = __usb_control_message(fd, (USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_GETCONFIG, 0, 0, 1, _configuration, NULL, NULL);
if(retval >= 0)
*configuration = *_configuration;
USB_Free( _configuration);
return retval;
}
s32 USB_SetConfiguration(struct ehci_device *fd, u8 configuration)
{
return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_DEVICE), USB_REQ_SETCONFIG, configuration, 0, 0, NULL, NULL, NULL);
}
s32 USB_SetAlternativeInterface(struct ehci_device *fd, u8 interface, u8 alternateSetting)
{
if(alternateSetting == 0)
return -EINVAL;
return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_INTERFACE),
USB_REQ_SETINTERFACE, alternateSetting, interface, 0, NULL, NULL, NULL);
}
s32 USB_ClearHalt(struct ehci_device *fd, u8 endpoint)
{
return __usb_control_message(fd, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_STANDARD | USB_CTRLTYPE_REC_ENDPOINT),
USB_REQ_CLEARFEATURE, USB_FEATURE_ENDPOINT_HALT, endpoint, 0, NULL, NULL, NULL);
}

View File

@ -1,78 +0,0 @@
#include <string.h>
#include "ehci_types.h"
#include "usb.h"
#include "ehci.h"
#include "alloc.h"
void *VirtualToPhysical( void *address )
{
if( ((u32)address & 0xFFFF0000) == 0xFFFF0000 )
return (void*)address;
return (void*)((u32)address & 0x7FFFFFFF);
}
void sync_after_write( void *a, u32 v )
{
dc_flushrange( a, v );
}
void sync_before_read( void *a, u32 v )
{
dc_invalidaterange( a, v );
}
int usb_os_init(void)
{
return 0;
}
void *ehci_maligned(int size,int alignement,int crossing)
{
return (void*)malloca( size, alignement );
}
dma_addr_t ehci_virt_to_dma(void *a)
{
return (dma_addr_t)VirtualToPhysical(a);
}
dma_addr_t ehci_dma_map_to(void *buf,size_t len)
{
sync_after_write(buf, len);
return (dma_addr_t)VirtualToPhysical(buf);
}
dma_addr_t ehci_dma_map_from(void *buf,size_t len)
{
sync_after_write(buf, len);
return (dma_addr_t)VirtualToPhysical(buf);
}
dma_addr_t ehci_dma_map_bidir(void *buf,size_t len)
{
sync_after_write(buf, len);
return (dma_addr_t)VirtualToPhysical(buf);
}
void ehci_dma_unmap_to(dma_addr_t buf,size_t len)
{
sync_before_read((void*)buf, len);
}
void ehci_dma_unmap_from(dma_addr_t buf,size_t len)
{
sync_before_read((void*)buf, len);
}
void ehci_dma_unmap_bidir(dma_addr_t buf,size_t len)
{
sync_before_read((void*)buf, len);
}
void *USB_Alloc(int size)
{
//u32 val;
//__asm("mov %0,lr": "=r" (val) );
//dbgprintf("USB_Alloc(%u) LR:%08x\n", size, val );
//void *ptr = malloc(size);
//memset( ptr, 0, size );
//return ptr;
return malloc(size);
}
void USB_Free(void *ptr)
{
return free(ptr);
}

View File

@ -1,755 +0,0 @@
/*-------------------------------------------------------------
usbstorage.c -- Bulk-only USB mass storage support
Copyright (C) 2008
Sven Peter (svpe) <svpe@gmx.net>
quick port to ehci/ios: Kwiirk
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
-------------------------------------------------------------*/
#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f)
#define HEAP_SIZE (32*1024)
#define TAG_START 0x1//BADC0DE
#define CBW_SIZE 31
#define CBW_SIGNATURE 0x43425355
#define CBW_IN (1 << 7)
#define CBW_OUT 0
#define CSW_SIZE 13
#define CSW_SIGNATURE 0x53425355
#define SCSI_TEST_UNIT_READY 0x00
#define SCSI_INQUIRY 0x12
#define SCSI_REQUEST_SENSE 0x03
#define SCSI_READ_CAPACITY 0x25
#define SCSI_READ_10 0x28
#define SCSI_WRITE_10 0x2A
#define SCSI_SENSE_REPLY_SIZE 18
#define SCSI_SENSE_NOT_READY 0x02
#define SCSI_SENSE_MEDIUM_ERROR 0x03
#define SCSI_SENSE_HARDWARE_ERROR 0x04
#define USB_CLASS_MASS_STORAGE 0x08
#define MASS_STORAGE_SCSI_COMMANDS 0x06
#define MASS_STORAGE_BULK_ONLY 0x50
#define USBSTORAGE_GET_MAX_LUN 0xFE
#define USBSTORAGE_RESET 0xFF
#define USB_ENDPOINT_BULK 0x02
#define USBSTORAGE_CYCLE_RETRIES 3
#define MAX_TRANSFER_SIZE 4096
#define DEVLIST_MAXSIZE 8
static s32 __usbstorage_reset(usbstorage_handle *dev);
static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun);
// ehci driver has its own timeout.
static s32 __USB_BlkMsgTimeout(usbstorage_handle *dev, u8 bEndpoint, u16 wLength, void *rpData)
{
return USB_WriteBlkMsg(dev->usb_fd, bEndpoint, wLength, rpData);
}
static s32 __USB_CtrlMsgTimeout(usbstorage_handle *dev, u8 bmRequestType, u8 bmRequest, u16 wValue, u16 wIndex, u16 wLength, void *rpData)
{
return USB_WriteCtrlMsg(dev->usb_fd, bmRequestType, bmRequest, wValue, wIndex, wLength, rpData);
}
static s32 __send_cbw(usbstorage_handle *dev, u8 lun, u32 len, u8 flags, const u8 *cb, u8 cbLen)
{
s32 retval = USBSTORAGE_OK;
if(cbLen == 0 || cbLen > 16)
return -EINVAL;
memset(dev->buffer, 0, CBW_SIZE);
((u32*)dev->buffer)[0]=cpu_to_le32(CBW_SIGNATURE);
((u32*)dev->buffer)[1]=cpu_to_le32(dev->tag);
((u32*)dev->buffer)[2]=cpu_to_le32(len);
dev->buffer[12] = flags;
dev->buffer[13] = lun;
dev->buffer[14] = (cbLen > 6 ? 0x10 : 6);
memcpy(dev->buffer + 15, (void*)cb, cbLen);
retval = __USB_BlkMsgTimeout(dev, dev->ep_out, CBW_SIZE, (void *)dev->buffer);
if(retval == CBW_SIZE) return USBSTORAGE_OK;
else if(retval >= 0) return USBSTORAGE_ESHORTWRITE;
return retval;
}
static s32 __read_csw(usbstorage_handle *dev, u8 *status, u32 *dataResidue)
{
s32 retval = USBSTORAGE_OK;
u32 signature, tag, _dataResidue, _status;
memset(dev->buffer, 0xff, CSW_SIZE);
retval = __USB_BlkMsgTimeout(dev, dev->ep_in, CSW_SIZE, dev->buffer);
//print_hex_dump_bytes("csv resp:",DUMP_PREFIX_OFFSET,dev->buffer,CSW_SIZE);
if(retval >= 0 && retval != CSW_SIZE) return USBSTORAGE_ESHORTREAD;
else if(retval < 0) return retval;
signature = le32_to_cpu(((u32*)dev->buffer)[0]);
tag = le32_to_cpu(((u32*)dev->buffer)[1]);
_dataResidue = le32_to_cpu(((u32*)dev->buffer)[2]);
_status = dev->buffer[12];
if(signature != CSW_SIGNATURE) {
BUG();
return USBSTORAGE_ESIGNATURE;
}
if(dataResidue != NULL)
*dataResidue = _dataResidue;
if(status != NULL)
*status = _status;
if(tag != dev->tag) return USBSTORAGE_ETAG;
dev->tag++;
return USBSTORAGE_OK;
}
static s32 __cycle(usbstorage_handle *dev, u8 lun, u8 *buffer, u32 len, u8 *cb, u8 cbLen, u8 write, u8 *_status, u32 *_dataResidue)
{
s32 retval = USBSTORAGE_OK;
u8 status = 0;
u32 dataResidue = 0;
u32 thisLen;
s8 retries = USBSTORAGE_CYCLE_RETRIES + 1;
do
{
retries--;
if(retval == USBSTORAGE_ETIMEDOUT)
break;
if(write)
{
retval = __send_cbw(dev, lun, len, CBW_OUT, cb, cbLen);
if(retval == USBSTORAGE_ETIMEDOUT)
break;
if(retval < 0)
{
if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
retval = USBSTORAGE_ETIMEDOUT;
continue;
}
while(len > 0)
{
thisLen=len;
retval = __USB_BlkMsgTimeout(dev, dev->ep_out, thisLen, buffer);
if(retval == USBSTORAGE_ETIMEDOUT)
break;
if(retval < 0)
{
retval = USBSTORAGE_EDATARESIDUE;
break;
}
if(retval != thisLen && len > 0)
{
retval = USBSTORAGE_EDATARESIDUE;
break;
}
len -= retval;
buffer += retval;
}
if(retval < 0)
{
if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
retval = USBSTORAGE_ETIMEDOUT;
continue;
}
}
else
{
retval = __send_cbw(dev, lun, len, CBW_IN, cb, cbLen);
if(retval == USBSTORAGE_ETIMEDOUT)
break;
if(retval < 0)
{
if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
retval = USBSTORAGE_ETIMEDOUT;
continue;
}
while(len > 0)
{
thisLen=len;
retval = __USB_BlkMsgTimeout(dev, dev->ep_in, thisLen, buffer);
//print_hex_dump_bytes("usbs in:",DUMP_PREFIX_OFFSET,dev->buffer,36);
if(retval < 0)
break;
len -= retval;
buffer += retval;
if(retval != thisLen)
break;
}
if(retval < 0)
{
if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
retval = USBSTORAGE_ETIMEDOUT;
continue;
}
}
retval = __read_csw(dev, &status, &dataResidue);
if(retval == USBSTORAGE_ETIMEDOUT)
break;
if(retval < 0)
{
if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
retval = USBSTORAGE_ETIMEDOUT;
continue;
}
retval = USBSTORAGE_OK;
} while(retval < 0 && retries > 0);
if(retval < 0 && retval != USBSTORAGE_ETIMEDOUT)
{
if(__usbstorage_reset(dev) == USBSTORAGE_ETIMEDOUT)
retval = USBSTORAGE_ETIMEDOUT;
}
if(_status != NULL)
*_status = status;
if(_dataResidue != NULL)
*_dataResidue = dataResidue;
return retval;
}
static s32 __usbstorage_clearerrors(usbstorage_handle *dev, u8 lun)
{
s32 retval;
u8 cmd[16];
u8 *sense= USB_Alloc(SCSI_SENSE_REPLY_SIZE);
u8 status = 0;
memset(cmd, 0, sizeof(cmd));
cmd[0] = SCSI_TEST_UNIT_READY;
retval = __cycle(dev, lun, NULL, 0, cmd, 1, 1, &status, NULL);
if(retval < 0) return retval;
if(status != 0)
{
cmd[0] = SCSI_REQUEST_SENSE;
cmd[1] = lun << 5;
cmd[4] = SCSI_SENSE_REPLY_SIZE;
cmd[5] = 0;
memset(sense, 0, SCSI_SENSE_REPLY_SIZE);
retval = __cycle(dev, lun, sense, SCSI_SENSE_REPLY_SIZE, cmd, 6, 0, NULL, NULL);
if(retval < 0) goto error;
status = sense[2] & 0x0F;
if(status == SCSI_SENSE_NOT_READY || status == SCSI_SENSE_MEDIUM_ERROR || status == SCSI_SENSE_HARDWARE_ERROR)
retval = USBSTORAGE_ESENSE;
}
error:
USB_Free(sense);
return retval;
}
static s32 __usbstorage_reset(usbstorage_handle *dev)
{
s32 retval;
if(dev->suspended == 1)
{
USB_ResumeDevice(dev->usb_fd);
dev->suspended = 0;
}
/*
retval = ehci_reset_device(dev->usb_fd);
if(retval < 0 && retval != -7004)
goto end;
*/
//debug_printf("usbstorage reset..\n");
retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_HOST2DEVICE | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_RESET, 0, dev->interface, 0, NULL);
/* FIXME?: some devices return -7004 here which definitely violates the usb ms protocol but they still seem to be working... */
if(retval < 0 && retval != -7004)
goto end;
/* gives device enough time to process the reset */
msleep(10);
//debug_printf("cleat halt on bulk ep..\n");
retval = USB_ClearHalt(dev->usb_fd, dev->ep_in);
if(retval < 0)
goto end;
retval = USB_ClearHalt(dev->usb_fd, dev->ep_out);
end:
return retval;
}
s32 USBStorage_Open(usbstorage_handle *dev, struct ehci_device *fd)
{
s32 retval = -1;
u8 conf,*max_lun = NULL;
u32 iConf, iInterface, iEp;
usb_devdesc udd;
usb_configurationdesc *ucd;
usb_interfacedesc *uid;
usb_endpointdesc *ued;
max_lun = USB_Alloc(1);
if(max_lun==NULL) return -ENOMEM;
memset(dev, 0, sizeof(*dev));
dev->tag = TAG_START;
dev->usb_fd = fd;
retval = USB_GetDescriptors(dev->usb_fd, &udd);
if(retval < 0)
{
dbgprintf("USB_GetDescriptors():%d\n", retval );
goto free_and_return;
}
dbgprintf("udd.bNumConfigurations:%u\n", udd.bNumConfigurations );
for(iConf = 0; iConf < udd.bNumConfigurations; iConf++)
{
ucd = &udd.configurations[iConf];
for(iInterface = 0; iInterface < ucd->bNumInterfaces; iInterface++)
{
uid = &ucd->interfaces[iInterface];
if(uid->bInterfaceClass == USB_CLASS_MASS_STORAGE &&
uid->bInterfaceSubClass == MASS_STORAGE_SCSI_COMMANDS &&
uid->bInterfaceProtocol == MASS_STORAGE_BULK_ONLY)
{
if(uid->bNumEndpoints < 2)
continue;
dev->ep_in = dev->ep_out = 0;
for(iEp = 0; iEp < uid->bNumEndpoints; iEp++)
{
ued = &uid->endpoints[iEp];
if(ued->bmAttributes != USB_ENDPOINT_BULK)
continue;
if(ued->bEndpointAddress & USB_ENDPOINT_IN)
{
dev->ep_in = ued->bEndpointAddress;
dbgprintf("%08X:%08X\n", dev->ep_in, ued->bEndpointAddress );
} else {
dev->ep_out = ued->bEndpointAddress;
dbgprintf("%08X:%08X\n", dev->ep_out, ued->bEndpointAddress );
}
}
if(dev->ep_in != 0 && dev->ep_out != 0)
{
dev->configuration = ucd->bConfigurationValue;
dev->interface = uid->bInterfaceNumber;
dev->altInterface = uid->bAlternateSetting;
dbgprintf("%08X:%08X\n", dev->configuration , ucd->bConfigurationValue );
dbgprintf("%08X:%08X\n", dev->interface , uid->bInterfaceNumber );
dbgprintf("%08X:%08X\n", dev->altInterface , uid->bAlternateSetting );
goto found;
}
}
}
}
USB_FreeDescriptors(&udd);
retval = USBSTORAGE_ENOINTERFACE;
goto free_and_return;
found:
USB_FreeDescriptors(&udd);
retval = USBSTORAGE_EINIT;
if(USB_GetConfiguration(dev->usb_fd, &conf) < 0)
goto free_and_return;
if(conf != dev->configuration && USB_SetConfiguration(dev->usb_fd, dev->configuration) < 0)
goto free_and_return;
if(dev->altInterface != 0 && USB_SetAlternativeInterface(dev->usb_fd, dev->interface, dev->altInterface) < 0)
goto free_and_return;
dev->suspended = 0;
retval = USBStorage_Reset(dev);
if(retval < 0)
goto free_and_return;
retval = __USB_CtrlMsgTimeout(dev, (USB_CTRLTYPE_DIR_DEVICE2HOST | USB_CTRLTYPE_TYPE_CLASS | USB_CTRLTYPE_REC_INTERFACE), USBSTORAGE_GET_MAX_LUN, 0, dev->interface, 1, max_lun);
if(retval < 0)
dev->max_lun = 1;
else
dev->max_lun = *max_lun;
if(retval == USBSTORAGE_ETIMEDOUT)
goto free_and_return;
retval = USBSTORAGE_OK;
if(dev->max_lun == 0)
dev->max_lun++;
/* taken from linux usbstorage module (drivers/usb/storage/transport.c) */
/*
* Some devices (i.e. Iomega Zip100) need this -- apparently
* the bulk pipes get STALLed when the GetMaxLUN request is
* processed. This is, in theory, harmless to all other devices
* (regardless of if they stall or not).
*/
USB_ClearHalt(dev->usb_fd, dev->ep_in);
USB_ClearHalt(dev->usb_fd, dev->ep_out);
dev->buffer = /*USB_Alloc(MAX_TRANSFER_SIZE)*/(u8*)0xFFFE4000;
if(dev->buffer == NULL) retval = -ENOMEM;
else retval = USBSTORAGE_OK;
free_and_return:
if(max_lun!=NULL)
USB_Free(max_lun);
if(retval < 0)
{
if(dev->buffer != NULL)
USB_Free(dev->buffer);
memset(dev, 0, sizeof(*dev));
return retval;
}
return 0;
}
s32 USBStorage_Close(usbstorage_handle *dev)
{
if(dev->buffer != NULL)
USB_Free(dev->buffer);
memset(dev, 0, sizeof(*dev));
return 0;
}
s32 USBStorage_Reset(usbstorage_handle *dev)
{
s32 retval;
retval = __usbstorage_reset(dev);
return retval;
}
s32 USBStorage_GetMaxLUN(usbstorage_handle *dev)
{
return dev->max_lun;
}
s32 USBStorage_MountLUN(usbstorage_handle *dev, u8 lun)
{
s32 retval;
if(lun >= dev->max_lun)
return -EINVAL;
retval = __usbstorage_clearerrors(dev, lun);
if(retval < 0)
return retval;
retval = USBStorage_Inquiry(dev, lun);
retval = USBStorage_ReadCapacity(dev, lun, &dev->sector_size[lun], &dev->n_sector[lun]);
return retval;
}
s32 USBStorage_Inquiry(usbstorage_handle *dev, u8 lun)
{
s32 retval;
u8 cmd[] = {SCSI_INQUIRY, lun << 5,0,0,36,0};
u8 *response = USB_Alloc(36);
retval = __cycle(dev, lun, response, 36, cmd, 6, 0, NULL, NULL);
//print_hex_dump_bytes("inquiry result:",DUMP_PREFIX_OFFSET,response,36);
USB_Free(response);
return retval;
}
s32 USBStorage_ReadCapacity(usbstorage_handle *dev, u8 lun, u32 *sector_size, u32 *n_sectors)
{
s32 retval;
u8 cmd[] = {SCSI_READ_CAPACITY, lun << 5};
u8 *response = USB_Alloc(8);
u32 val;
retval = __cycle(dev, lun, response, 8, cmd, 2, 0, NULL, NULL);
if(retval >= 0)
{
memcpy(&val, response, 4);
if(n_sectors != NULL)
*n_sectors = be32_to_cpu(val);
memcpy(&val, response + 4, 4);
if(sector_size != NULL)
*sector_size = be32_to_cpu(val);
retval = USBSTORAGE_OK;
}
USB_Free(response);
return retval;
}
s32 USBStorage_Read(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, u8 *buffer)
{
u8 status = 0;
s32 retval;
u8 cmd[] = {
SCSI_READ_10,
lun << 5,
sector >> 24,
sector >> 16,
sector >> 8,
sector,
0,
n_sectors >> 8,
n_sectors,
0
};
if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
return -EINVAL;
retval = __cycle(dev, lun, buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 0, &status, NULL);
if(retval > 0 && status != 0)
retval = USBSTORAGE_ESTATUS;
return retval;
}
s32 USBStorage_Write(usbstorage_handle *dev, u8 lun, u32 sector, u16 n_sectors, const u8 *buffer)
{
u8 status = 0;
s32 retval;
u8 cmd[] = {
SCSI_WRITE_10,
lun << 5,
sector >> 24,
sector >> 16,
sector >> 8,
sector,
0,
n_sectors >> 8,
n_sectors,
0
};
if(lun >= dev->max_lun || dev->sector_size[lun] == 0)
return -EINVAL;
retval = __cycle(dev, lun, (u8 *)buffer, n_sectors * dev->sector_size[lun], cmd, sizeof(cmd), 1, &status, NULL);
if(retval > 0 && status != 0)
retval = USBSTORAGE_ESTATUS;
return retval;
}
s32 USBStorage_Suspend(usbstorage_handle *dev)
{
if(dev->suspended == 1)
return USBSTORAGE_OK;
USB_SuspendDevice(dev->usb_fd);
dev->suspended = 1;
return USBSTORAGE_OK;
}
/*
The following is for implementing the ioctl interface inpired by the disc_io.h
as used by libfat
This opens the first lun of the first usbstorage device found.
*/
static usbstorage_handle __usbfd;
static u8 __lun = 0;
static u8 __mounted = 0;
static u16 __vid = 0;
static u16 __pid = 0;
void switchbuf(void)
{
memcpy( (void*)0x080A0000, __usbfd.buffer, CBW_SIZE );
__usbfd.buffer = (u8*)0x080A0000;
}
/* perform 512 time the same read */
s32 USBStorage_Read_Stress(u32 sector, u32 numSectors, void *buffer)
{
s32 retval;
int i;
if(__mounted != 1)
return false;
for(i=0;i<512;i++){
retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
sector+=numSectors;
if(retval == USBSTORAGE_ETIMEDOUT)
{
__mounted = 0;
USBStorage_Close(&__usbfd);
}
if(retval < 0)
return false;
}
return true;
}
// temp function before libfat is available */
s32 USBStorage_Try_Device(struct ehci_device *fd)
{
int maxLun,j,retval;
int ret = USBStorage_Open(&__usbfd, fd);
if( ret < 0)
{
dbgprintf("USB:Could not open device:%d\n",ret);
return -EINVAL;
}
maxLun = USBStorage_GetMaxLUN(&__usbfd);
if(maxLun == USBSTORAGE_ETIMEDOUT)
{
dbgprintf("USB:Device timed out\n");
return -EINVAL;
}
for(j = 0; j < maxLun; j++)
{
retval = USBStorage_MountLUN(&__usbfd, j);
if(retval == USBSTORAGE_ETIMEDOUT)
{
USBStorage_Reset(&__usbfd);
USBStorage_Close(&__usbfd);
break;
}
if(retval < 0)
continue;
__vid=fd->desc.idVendor;
__pid=fd->desc.idProduct;
__mounted = 1;
__lun = j;
return 0;
}
dbgprintf("USB:Device failed to mount\n");
return -EINVAL;
}
static int ums_init_done = 0;
s32 USBStorage_Init(void)
{
s32 res = -ENODEV;
int i;
//debug_printf("usbstorage init %d\n", ums_init_done);
if(ums_init_done)
return 0;
for(i = 0;i<ehci->num_port; i++)
{
struct ehci_device *dev = &ehci->devices[i];
if(dev->id != 0)
{
res = USBStorage_Try_Device(dev);
if( res == 0 )
ums_init_done = 1;
}
}
return res;
}
s32 USBStorage_Get_Capacity(u32*sector_size)
{
if(__mounted == 1)
{
if(sector_size){
*sector_size = __usbfd.sector_size[__lun];
}
return __usbfd.n_sector[__lun];
}
return 0;
}
s32 USBStorage_Read_Sectors(u32 sector, u32 numSectors, void *buffer)
{
s32 retval;
if(__mounted != 1)
return false;
retval = USBStorage_Read(&__usbfd, __lun, sector, numSectors, buffer);
if(retval == USBSTORAGE_ETIMEDOUT)
{
__mounted = 0;
USBStorage_Close(&__usbfd);
}
if(retval < 0)
return false;
return true;
}
s32 USBStorage_Write_Sectors(u32 sector, u32 numSectors, const void *buffer)
{
s32 retval;
if(__mounted != 1)
return false;
retval = USBStorage_Write(&__usbfd, __lun, sector, numSectors, buffer);
if(retval == USBSTORAGE_ETIMEDOUT)
{
__mounted = 0;
USBStorage_Close(&__usbfd);
}
if(retval < 0)
return false;
return true;
}