git-svn-id: svn://localhost/Users/andi/Downloads/code/DML@27 be6c1b03-d731-4111-a574-e37d80d43941
This commit is contained in:
parent
b61acfedff
commit
206dff56e5
78
ehci-mem.c
78
ehci-mem.c
@ -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
830
ehci.c
@ -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
282
ehci.h
@ -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 */
|
160
ehci_defs.h
160
ehci_defs.h
@ -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 */
|
55
ehci_types.h
55
ehci_types.h
@ -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
|
81
iosmodule.ld
81
iosmodule.ld
@ -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*)
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
@ -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
244
usb.c
@ -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);
|
||||
}
|
78
usb_os.c
78
usb_os.c
@ -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);
|
||||
}
|
755
usbstorage.c
755
usbstorage.c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user