Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
47c1b9b3
Commit
47c1b9b3
authored
Dec 08, 2004
by
Greg Kroah-Hartman
Committed by
Linus Torvalds
Dec 08, 2004
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
USB: removed unused hc_sl811 driver from the tree
Signed-off-by:
Greg Kroah-Hartman
<
greg@kroah.com
>
parent
d5acaac4
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
0 additions
and
3595 deletions
+0
-3595
drivers/usb/host/hc_simple.c
drivers/usb/host/hc_simple.c
+0
-1039
drivers/usb/host/hc_simple.h
drivers/usb/host/hc_simple.h
+0
-231
drivers/usb/host/hc_sl811.c
drivers/usb/host/hc_sl811.c
+0
-1357
drivers/usb/host/hc_sl811.h
drivers/usb/host/hc_sl811.h
+0
-385
drivers/usb/host/hc_sl811_rh.c
drivers/usb/host/hc_sl811_rh.c
+0
-583
No files found.
drivers/usb/host/hc_simple.c
deleted
100644 → 0
View file @
d5acaac4
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*
* simple generic USB HCD frontend Version 0.9.5 (10/28/2001)
* for embedded HCs (SL811HS)
*
* USB URB handling, hci_ hcs_
* URB queueing, qu_
* Transfer scheduling, sh_
*
*
*-------------------------------------------------------------------------*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*-------------------------------------------------------------------------*/
/* main lock for urb access */
static
spinlock_t
usb_urb_lock
=
SPIN_LOCK_UNLOCKED
;
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*/
/* URB HCD API function layer
* * * */
/***************************************************************************
* Function Name : hcs_urb_queue
*
* This function initializes the urb status and length before queueing the
* urb.
*
* Input: hci = data structure for the host controller
* urb = USB request block data structure
*
* Return: 0
**************************************************************************/
static
inline
int
hcs_urb_queue
(
hci_t
*
hci
,
struct
urb
*
urb
)
{
int
i
;
DBGFUNC
(
"enter hcs_urb_queue
\n
"
);
if
(
usb_pipeisoc
(
urb
->
pipe
))
{
DBGVERBOSE
(
"hcs_urb_queue: isoc pipe
\n
"
);
for
(
i
=
0
;
i
<
urb
->
number_of_packets
;
i
++
)
{
urb
->
iso_frame_desc
[
i
].
actual_length
=
0
;
urb
->
iso_frame_desc
[
i
].
status
=
-
EXDEV
;
}
/* urb->next hack : 1 .. resub, 0 .. single shot */
/* urb->interval = urb->next ? 1 : 0; */
}
urb
->
status
=
-
EINPROGRESS
;
urb
->
actual_length
=
0
;
urb
->
error_count
=
0
;
if
(
usb_pipecontrol
(
urb
->
pipe
))
hc_flush_data_cache
(
hci
,
urb
->
setup_packet
,
8
);
if
(
usb_pipeout
(
urb
->
pipe
))
hc_flush_data_cache
(
hci
,
urb
->
transfer_buffer
,
urb
->
transfer_buffer_length
);
qu_queue_urb
(
hci
,
urb
);
return
0
;
}
/***************************************************************************
* Function Name : hcs_return_urb
*
* This function the return path of URB back to the USB core. It calls the
* the urb complete function if exist, and also handles the resubmition of
* interrupt URBs.
*
* Input: hci = data structure for the host controller
* urb = USB request block data structure
* resub_ok = resubmit flag: 1 = submit urb again, 0 = not submit
*
* Return: 0
**************************************************************************/
static
int
hcs_return_urb
(
hci_t
*
hci
,
struct
urb
*
urb
,
int
resub_ok
)
{
struct
usb_device
*
dev
=
urb
->
dev
;
int
resubmit
=
0
;
DBGFUNC
(
"enter hcs_return_urb, urb pointer = 0x%x, "
"transferbuffer point = 0x%x, "
" setup packet pointer = 0x%x, context pointer = 0x%x
\n
"
,
(
__u32
*
)
urb
,
(
__u32
*
)
urb
->
transfer_buffer
,
(
__u32
*
)
urb
->
setup_packet
,
(
__u32
*
)
urb
->
context
);
if
(
urb_debug
)
urb_print
(
urb
,
"RET"
,
usb_pipeout
(
urb
->
pipe
));
resubmit
=
urb
->
interval
&&
resub_ok
;
urb
->
dev
=
urb
->
hcpriv
=
NULL
;
if
(
urb
->
complete
)
{
urb
->
complete
(
urb
,
NULL
);
/* call complete */
}
if
(
resubmit
)
{
/* requeue the URB */
urb
->
dev
=
dev
;
hcs_urb_queue
(
hci
,
urb
);
}
else
{
usb_put_urb
(
urb
);
}
return
0
;
}
/***************************************************************************
* Function Name : hci_submit_urb
*
* This function is called by the USB core API when an URB is available to
* process. This function does the following
*
* 1) Check the validity of the URB
* 2) Parse the device number from the URB
* 3) Pass the URB to the root hub routine if its intended for the hub, else
* queue the urb for the attached device.
*
* Input: urb = USB request block data structure
*
* Return: 0 if success or error code
**************************************************************************/
static
int
hci_submit_urb
(
struct
urb
*
urb
,
int
mem_flags
)
{
hci_t
*
hci
;
unsigned
int
pipe
=
urb
->
pipe
;
unsigned
long
flags
;
int
ret
;
DBGFUNC
(
"enter hci_submit_urb, pipe = 0x%x
\n
"
,
urb
->
pipe
);
if
(
!
urb
->
dev
||
!
urb
->
dev
->
bus
||
urb
->
hcpriv
)
return
-
EINVAL
;
hci
=
(
hci_t
*
)
urb
->
dev
->
bus
->
hcpriv
;
/* a request to the virtual root hub */
if
(
usb_pipedevice
(
pipe
)
==
hci
->
rh
.
devnum
)
{
if
(
urb_debug
>
1
)
urb_print
(
urb
,
"SUB-RH"
,
usb_pipein
(
pipe
));
return
rh_submit_urb
(
urb
);
}
/* increment urb's reference count, we now control it. */
urb
=
usb_get_urb
(
urb
);
/* queue the URB to its endpoint-queue */
spin_lock_irqsave
(
&
usb_urb_lock
,
flags
);
ret
=
hcs_urb_queue
(
hci
,
urb
);
if
(
ret
!=
0
)
{
/* error on return */
DBGERR
(
"hci_submit_urb: return err, ret = 0x%x, urb->status = 0x%x
\n
"
,
ret
,
urb
->
status
);
usb_put_urb
(
urb
);
}
spin_unlock_irqrestore
(
&
usb_urb_lock
,
flags
);
return
ret
;
}
/***************************************************************************
* Function Name : hci_unlink_urb
*
* This function mark the URB to unlink
*
* Input: urb = USB request block data structure
*
* Return: 0 if success or error code
**************************************************************************/
static
int
hci_unlink_urb
(
struct
urb
*
urb
,
int
status
)
{
unsigned
long
flags
;
hci_t
*
hci
;
DECLARE_WAITQUEUE
(
wait
,
current
);
void
*
comp
=
NULL
;
DBGFUNC
(
"enter hci_unlink_urb
\n
"
);
if
(
!
urb
)
/* just to be sure */
return
-
EINVAL
;
if
(
!
urb
->
dev
||
!
urb
->
dev
->
bus
)
return
-
ENODEV
;
hci
=
(
hci_t
*
)
urb
->
dev
->
bus
->
hcpriv
;
/* a request to the virtual root hub */
if
(
usb_pipedevice
(
urb
->
pipe
)
==
hci
->
rh
.
devnum
)
{
return
rh_unlink_urb
(
urb
);
}
if
(
urb_debug
)
urb_print
(
urb
,
"UNLINK"
,
1
);
spin_lock_irqsave
(
&
usb_urb_lock
,
flags
);
if
(
!
list_empty
(
&
urb
->
urb_list
)
&&
urb
->
status
==
-
EINPROGRESS
)
{
/* URB active? */
/* asynchronous with callback */
/* relink the urb to the del list */
list_move
(
&
urb
->
urb_list
,
&
hci
->
del_list
);
urb
->
status
=
status
;
spin_unlock_irqrestore
(
&
usb_urb_lock
,
flags
);
}
else
{
/* hcd does not own URB but we keep the driver happy anyway */
spin_unlock_irqrestore
(
&
usb_urb_lock
,
flags
);
if
(
urb
->
complete
)
{
urb
->
status
=
status
;
urb
->
actual_length
=
0
;
urb
->
complete
(
urb
,
NULL
);
if
(
urb
->
reject
)
wake_up
(
&
usb_kill_urb_queue
);
}
}
return
0
;
}
/***************************************************************************
* Function Name : hci_alloc_dev
*
* This function allocates private data space for the usb device and
* initialize the endpoint descriptor heads.
*
* Input: usb_dev = pointer to the usb device
*
* Return: 0 if success or error code
**************************************************************************/
static
int
hci_alloc_dev
(
struct
usb_device
*
usb_dev
)
{
struct
hci_device
*
dev
;
int
i
;
DBGFUNC
(
"enter hci_alloc_dev
\n
"
);
dev
=
kmalloc
(
sizeof
(
*
dev
),
GFP_KERNEL
);
if
(
!
dev
)
return
-
ENOMEM
;
memset
(
dev
,
0
,
sizeof
(
*
dev
));
for
(
i
=
0
;
i
<
32
;
i
++
)
{
INIT_LIST_HEAD
(
&
(
dev
->
ed
[
i
].
urb_queue
));
dev
->
ed
[
i
].
pipe_head
=
NULL
;
}
usb_dev
->
hcpriv
=
dev
;
DBGVERBOSE
(
"USB HC dev alloc %d bytes
\n
"
,
sizeof
(
*
dev
));
return
0
;
}
/***************************************************************************
* Function Name : hci_free_dev
*
* This function de-allocates private data space for the usb devic
*
* Input: usb_dev = pointer to the usb device
*
* Return: 0
**************************************************************************/
static
int
hci_free_dev
(
struct
usb_device
*
usb_dev
)
{
DBGFUNC
(
"enter hci_free_dev
\n
"
);
if
(
usb_dev
->
hcpriv
)
kfree
(
usb_dev
->
hcpriv
);
usb_dev
->
hcpriv
=
NULL
;
return
0
;
}
/***************************************************************************
* Function Name : hci_get_current_frame_number
*
* This function get the current USB frame number
*
* Input: usb_dev = pointer to the usb device
*
* Return: frame number
**************************************************************************/
static
int
hci_get_current_frame_number
(
struct
usb_device
*
usb_dev
)
{
hci_t
*
hci
=
usb_dev
->
bus
->
hcpriv
;
DBGFUNC
(
"enter hci_get_current_frame_number, frame = 0x%x
\r\n
"
,
hci
->
frame_number
);
return
(
hci
->
frame_number
);
}
/***************************************************************************
* List of all io-functions
**************************************************************************/
static
struct
usb_operations
hci_device_operations
=
{
.
allocate
=
hci_alloc_dev
,
.
deallocate
=
hci_free_dev
,
.
get_frame_number
=
hci_get_current_frame_number
,
.
submit_urb
=
hci_submit_urb
,
.
unlink_urb
=
hci_unlink_urb
,
};
/***************************************************************************
* URB queueing:
*
* For each type of transfer (INTR, BULK, ISO, CTRL) there is a list of
* active URBs.
* (hci->intr_list, hci->bulk_list, hci->iso_list, hci->ctrl_list)
* For every endpoint the head URB of the queued URBs is linked to one of
* those lists.
*
* The rest of the queued URBs of an endpoint are linked into a
* private URB list for each endpoint. (hci_dev->ed [endpoint_io].urb_queue)
* hci_dev->ed [endpoint_io].pipe_head .. points to the head URB which is
* in one of the active URB lists.
*
* The index of an endpoint consists of its number and its direction.
*
* The state of an intr and iso URB is 0.
* For ctrl URBs the states are US_CTRL_SETUP, US_CTRL_DATA, US_CTRL_ACK
* Bulk URBs states are US_BULK and US_BULK0 (with 0-len packet)
*
**************************************************************************/
/***************************************************************************
* Function Name : qu_urb_timeout
*
* This function is called when the URB timeout. The function unlinks the
* URB.
*
* Input: lurb: URB
*
* Return: none
**************************************************************************/
#ifdef HC_URB_TIMEOUT
static
void
qu_urb_timeout
(
unsigned
long
lurb
)
{
struct
urb
*
urb
=
(
struct
urb
*
)
lurb
;
DBGFUNC
(
"enter qu_urb_timeout
\n
"
);
hci_unlink_urb
(
urb
);
}
#endif
/***************************************************************************
* Function Name : qu_pipeindex
*
* This function gets the index of the pipe.
*
* Input: pipe: the urb pipe
*
* Return: index
**************************************************************************/
static
inline
int
qu_pipeindex
(
__u32
pipe
)
{
DBGFUNC
(
"enter qu_pipeindex
\n
"
);
return
(
usb_pipeendpoint
(
pipe
)
<<
1
)
|
(
usb_pipecontrol
(
pipe
)
?
0
:
usb_pipeout
(
pipe
));
}
/***************************************************************************
* Function Name : qu_seturbstate
*
* This function set the state of the URB.
*
* control pipe: 3 states -- Setup, data, status
* interrupt and bulk pipe: 1 state -- data
*
* Input: urb = USB request block data structure
* state = the urb state
*
* Return: none
**************************************************************************/
static
inline
void
qu_seturbstate
(
struct
urb
*
urb
,
int
state
)
{
DBGFUNC
(
"enter qu_seturbstate
\n
"
);
urb
->
pipe
&=
~
0x1f
;
urb
->
pipe
|=
state
&
0x1f
;
}
/***************************************************************************
* Function Name : qu_urbstate
*
* This function get the current state of the URB.
*
* Input: urb = USB request block data structure
*
* Return: none
**************************************************************************/
static
inline
int
qu_urbstate
(
struct
urb
*
urb
)
{
DBGFUNC
(
"enter qu_urbstate
\n
"
);
return
urb
->
pipe
&
0x1f
;
}
/***************************************************************************
* Function Name : qu_queue_active_urb
*
* This function adds the urb to the appropriate active urb list and set
* the urb state.
*
* There are four active lists: isochoronous list, interrupt list,
* control list, and bulk list.
*
* Input: hci = data structure for the host controller
* urb = USB request block data structure
* ed = endpoint descriptor
*
* Return: none
**************************************************************************/
static
inline
void
qu_queue_active_urb
(
hci_t
*
hci
,
struct
urb
*
urb
,
epd_t
*
ed
)
{
int
urb_state
=
0
;
DBGFUNC
(
"enter qu_queue_active_urb
\n
"
);
switch
(
usb_pipetype
(
urb
->
pipe
))
{
case
PIPE_CONTROL
:
list_add
(
&
urb
->
urb_list
,
&
hci
->
ctrl_list
);
urb_state
=
US_CTRL_SETUP
;
break
;
case
PIPE_BULK
:
list_add
(
&
urb
->
urb_list
,
&
hci
->
bulk_list
);
if
((
urb
->
transfer_flags
&
URB_ZERO_PACKET
)
&&
urb
->
transfer_buffer_length
>
0
&&
((
urb
->
transfer_buffer_length
%
usb_maxpacket
(
urb
->
dev
,
urb
->
pipe
,
usb_pipeout
(
urb
->
pipe
)))
==
0
))
{
urb_state
=
US_BULK0
;
}
break
;
case
PIPE_INTERRUPT
:
urb
->
start_frame
=
hci
->
frame_number
;
list_add
(
&
urb
->
urb_list
,
&
hci
->
intr_list
);
break
;
case
PIPE_ISOCHRONOUS
:
list_add
(
&
urb
->
urb_list
,
&
hci
->
iso_list
);
break
;
}
#ifdef HC_URB_TIMEOUT
if
(
urb
->
timeout
)
{
ed
->
timeout
.
data
=
(
unsigned
long
)
urb
;
ed
->
timeout
.
expires
=
urb
->
timeout
+
jiffies
;
ed
->
timeout
.
function
=
qu_urb_timeout
;
add_timer
(
&
ed
->
timeout
);
}
#endif
qu_seturbstate
(
urb
,
urb_state
);
}
/***************************************************************************
* Function Name : qu_queue_urb
*
* This function adds the urb to the endpoint descriptor list
*
* Input: hci = data structure for the host controller
* urb = USB request block data structure
*
* Return: none
**************************************************************************/
static
int
qu_queue_urb
(
hci_t
*
hci
,
struct
urb
*
urb
)
{
struct
hci_device
*
hci_dev
=
usb_to_hci
(
urb
->
dev
);
epd_t
*
ed
=
&
hci_dev
->
ed
[
qu_pipeindex
(
urb
->
pipe
)];
DBGFUNC
(
"Enter qu_queue_urb
\n
"
);
/* for ISOC transfers calculate start frame index */
if
(
usb_pipeisoc
(
urb
->
pipe
)
&&
urb
->
transfer_flags
&
URB_ISO_ASAP
)
{
urb
->
start_frame
=
((
ed
->
pipe_head
)
?
(
ed
->
last_iso
+
1
)
:
hci_get_current_frame_number
(
urb
->
dev
)
+
1
)
&
0xffff
;
}
if
(
ed
->
pipe_head
)
{
__list_add
(
&
urb
->
urb_list
,
ed
->
urb_queue
.
prev
,
&
(
ed
->
urb_queue
));
}
else
{
ed
->
pipe_head
=
urb
;
qu_queue_active_urb
(
hci
,
urb
,
ed
);
if
(
++
hci
->
active_urbs
==
1
)
hc_start_int
(
hci
);
}
return
0
;
}
/***************************************************************************
* Function Name : qu_next_urb
*
* This function removes the URB from the queue and add the next URB to
* active list.
*
* Input: hci = data structure for the host controller
* urb = USB request block data structure
* resub_ok = resubmit flag
*
* Return: pointer to the next urb
**************************************************************************/
static
struct
urb
*
qu_next_urb
(
hci_t
*
hci
,
struct
urb
*
urb
,
int
resub_ok
)
{
struct
hci_device
*
hci_dev
=
usb_to_hci
(
urb
->
dev
);
epd_t
*
ed
=
&
hci_dev
->
ed
[
qu_pipeindex
(
urb
->
pipe
)];
DBGFUNC
(
"enter qu_next_urb
\n
"
);
list_del_init
(
&
urb
->
urb_list
);
if
(
ed
->
pipe_head
==
urb
)
{
#ifdef HC_URB_TIMEOUT
if
(
urb
->
timeout
)
del_timer
(
&
ed
->
timeout
);
#endif
if
(
!--
hci
->
active_urbs
)
hc_stop_int
(
hci
);
if
(
!
list_empty
(
&
ed
->
urb_queue
))
{
urb
=
list_entry
(
ed
->
urb_queue
.
next
,
struct
urb
,
urb_list
);
list_del_init
(
&
urb
->
urb_list
);
ed
->
pipe_head
=
urb
;
qu_queue_active_urb
(
hci
,
urb
,
ed
);
}
else
{
ed
->
pipe_head
=
NULL
;
urb
=
NULL
;
}
}
return
urb
;
}
/***************************************************************************
* Function Name : qu_return_urb
*
* This function is part of the return path.
*
* Input: hci = data structure for the host controller
* urb = USB request block data structure
* resub_ok = resubmit flag
*
* Return: pointer to the next urb
**************************************************************************/
static
struct
urb
*
qu_return_urb
(
hci_t
*
hci
,
struct
urb
*
urb
,
int
resub_ok
)
{
struct
urb
*
next_urb
;
DBGFUNC
(
"enter qu_return_rub
\n
"
);
next_urb
=
qu_next_urb
(
hci
,
urb
,
resub_ok
);
hcs_return_urb
(
hci
,
urb
,
resub_ok
);
return
next_urb
;
}
/***************************************************************************
* Function Name : sh_scan_iso_urb_list
*
* This function goes through the isochronous urb list and schedule the
* the transfer.
*
* Note: This function has not tested yet
*
* Input: hci = data structure for the host controller
* list_lh = pointer to the isochronous list
* frame_number = the frame number
*
* Return: 0 = unsuccessful; 1 = successful
**************************************************************************/
static
int
sh_scan_iso_urb_list
(
hci_t
*
hci
,
struct
list_head
*
list_lh
,
int
frame_number
)
{
struct
list_head
*
lh
=
list_lh
->
next
;
struct
urb
*
urb
;
DBGFUNC
(
"enter sh_scan_iso_urb_list
\n
"
);
hci
->
td_array
->
len
=
0
;
while
(
lh
!=
list_lh
)
{
urb
=
list_entry
(
lh
,
struct
urb
,
urb_list
);
lh
=
lh
->
next
;
if
(((
frame_number
-
urb
->
start_frame
)
&
0x7ff
)
<
urb
->
number_of_packets
)
{
if
(
!
sh_add_packet
(
hci
,
urb
))
{
return
0
;
}
else
{
if
(((
frame_number
-
urb
->
start_frame
)
&
0x7ff
)
>
0x400
)
{
if
(
qu_urbstate
(
urb
)
>
0
)
urb
=
qu_return_urb
(
hci
,
urb
,
1
);
else
urb
=
qu_next_urb
(
hci
,
urb
,
1
);
if
(
lh
==
list_lh
&&
urb
)
lh
=
&
urb
->
urb_list
;
}
}
}
}
return
1
;
}
/***************************************************************************
* Function Name : sh_scan_urb_list
*
* This function goes through the urb list and schedule the
* the transaction.
*
* Input: hci = data structure for the host controller
* list_lh = pointer to the isochronous list
*
* Return: 0 = unsuccessful; 1 = successful
**************************************************************************/
static
int
sh_scan_urb_list
(
hci_t
*
hci
,
struct
list_head
*
list_lh
)
{
struct
list_head
*
lh
=
NULL
;
struct
urb
*
urb
;
if
(
list_lh
==
NULL
)
{
DBGERR
(
"sh_scan_urb_list: error, list_lh == NULL
\n
"
);
}
DBGFUNC
(
"enter sh_scan_urb_list: frame#
\n
"
);
list_for_each
(
lh
,
list_lh
)
{
urb
=
list_entry
(
lh
,
struct
urb
,
urb_list
);
if
(
urb
==
NULL
)
return
1
;
if
(
!
usb_pipeint
(
urb
->
pipe
)
||
(((
hci
->
frame_number
-
urb
->
start_frame
)
&
0x7ff
)
>=
urb
->
interval
))
{
DBGVERBOSE
(
"sh_scan_urb_list !INT: %d fr_no: %d int: %d pint: %d
\n
"
,
urb
->
start_frame
,
hci
->
frame_number
,
urb
->
interval
,
usb_pipeint
(
urb
->
pipe
));
if
(
!
sh_add_packet
(
hci
,
urb
))
{
return
0
;
}
else
{
DBGVERBOSE
(
"INT: start: %d fr_no: %d int: %d pint: %d
\n
"
,
urb
->
start_frame
,
hci
->
frame_number
,
urb
->
interval
,
usb_pipeint
(
urb
->
pipe
));
urb
->
start_frame
=
hci
->
frame_number
;
return
0
;
}
}
}
return
1
;
}
/***************************************************************************
* Function Name : sh_shedule_trans
*
* This function schedule the USB transaction.
* This function will process the endpoint in the following order:
* interrupt, control, and bulk.
*
* Input: hci = data structure for the host controller
* isSOF = flag indicate if Start Of Frame has occurred
*
* Return: 0
**************************************************************************/
static
int
sh_schedule_trans
(
hci_t
*
hci
,
int
isSOF
)
{
int
units_left
=
1
;
struct
list_head
*
lh
;
if
(
hci
==
NULL
)
{
DBGERR
(
"sh_schedule_trans: hci == NULL
\n
"
);
return
0
;
}
if
(
hci
->
td_array
==
NULL
)
{
DBGERR
(
"sh_schedule_trans: hci->td_array == NULL
\n
"
);
return
0
;
}
if
(
hci
->
td_array
->
len
!=
0
)
{
DBGERR
(
"ERROR: schedule, hci->td_array->len = 0x%x, s/b: 0
\n
"
,
hci
->
td_array
->
len
);
}
/* schedule the next available interrupt transfer or the next
* stage of the interrupt transfer */
if
(
hci
->
td_array
->
len
==
0
&&
!
list_empty
(
&
hci
->
intr_list
))
{
units_left
=
sh_scan_urb_list
(
hci
,
&
hci
->
intr_list
);
}
/* schedule the next available control transfer or the next
* stage of the control transfer */
if
(
hci
->
td_array
->
len
==
0
&&
!
list_empty
(
&
hci
->
ctrl_list
)
&&
units_left
>
0
)
{
units_left
=
sh_scan_urb_list
(
hci
,
&
hci
->
ctrl_list
);
}
/* schedule the next available bulk transfer or the next
* stage of the bulk transfer */
if
(
hci
->
td_array
->
len
==
0
&&
!
list_empty
(
&
hci
->
bulk_list
)
&&
units_left
>
0
)
{
sh_scan_urb_list
(
hci
,
&
hci
->
bulk_list
);
/* be fair to each BULK URB (move list head around)
* only when the new SOF happens */
lh
=
hci
->
bulk_list
.
next
;
list_move
(
&
hci
->
bulk_list
,
lh
);
}
return
0
;
}
/***************************************************************************
* Function Name : sh_add_packet
*
* This function forms the packet and transmit the packet. This function
* will handle all endpoint type: isochoronus, interrupt, control, and
* bulk.
*
* Input: hci = data structure for the host controller
* urb = USB request block data structure
*
* Return: 0 = unsucessful; 1 = successful
**************************************************************************/
static
int
sh_add_packet
(
hci_t
*
hci
,
struct
urb
*
urb
)
{
__u8
*
data
=
NULL
;
int
len
=
0
;
int
toggle
=
0
;
int
maxps
=
usb_maxpacket
(
urb
->
dev
,
urb
->
pipe
,
usb_pipeout
(
urb
->
pipe
));
int
endpoint
=
usb_pipeendpoint
(
urb
->
pipe
);
int
address
=
usb_pipedevice
(
urb
->
pipe
);
int
slow
=
(((
urb
->
pipe
)
>>
26
)
&
1
);
int
out
=
usb_pipeout
(
urb
->
pipe
);
int
pid
=
0
;
int
ret
;
int
i
=
0
;
int
iso
=
0
;
DBGFUNC
(
"enter sh_add_packet
\n
"
);
if
(
maxps
==
0
)
maxps
=
8
;
/* calculate len, toggle bit and add the transaction */
switch
(
usb_pipetype
(
urb
->
pipe
))
{
case
PIPE_ISOCHRONOUS
:
pid
=
out
?
PID_OUT
:
PID_IN
;
iso
=
1
;
i
=
hci
->
frame_number
-
urb
->
start_frame
;
data
=
urb
->
transfer_buffer
+
urb
->
iso_frame_desc
[
i
].
offset
;
len
=
urb
->
iso_frame_desc
[
i
].
length
;
break
;
case
PIPE_BULK
:
/* BULK and BULK0 */
case
PIPE_INTERRUPT
:
pid
=
out
?
PID_OUT
:
PID_IN
;
len
=
urb
->
transfer_buffer_length
-
urb
->
actual_length
;
data
=
urb
->
transfer_buffer
+
urb
->
actual_length
;
toggle
=
usb_gettoggle
(
urb
->
dev
,
endpoint
,
out
);
break
;
case
PIPE_CONTROL
:
switch
(
qu_urbstate
(
urb
))
{
case
US_CTRL_SETUP
:
len
=
8
;
pid
=
PID_SETUP
;
data
=
urb
->
setup_packet
;
toggle
=
0
;
break
;
case
US_CTRL_DATA
:
if
(
!
hci
->
last_packet_nak
)
{
/* The last packet received is not a nak:
* reset the nak count
*/
hci
->
nakCnt
=
0
;
}
if
(
urb
->
transfer_buffer_length
!=
0
)
{
pid
=
out
?
PID_OUT
:
PID_IN
;
len
=
urb
->
transfer_buffer_length
-
urb
->
actual_length
;
data
=
urb
->
transfer_buffer
+
urb
->
actual_length
;
toggle
=
(
urb
->
actual_length
&
maxps
)
?
0
:
1
;
usb_settoggle
(
urb
->
dev
,
usb_pipeendpoint
(
urb
->
pipe
),
usb_pipeout
(
urb
->
pipe
),
toggle
);
break
;
}
else
{
/* correct state and fall through */
qu_seturbstate
(
urb
,
US_CTRL_ACK
);
}
case
US_CTRL_ACK
:
len
=
0
;
/* reply in opposite direction */
pid
=
!
out
?
PID_OUT
:
PID_IN
;
toggle
=
1
;
usb_settoggle
(
urb
->
dev
,
usb_pipeendpoint
(
urb
->
pipe
),
usb_pipeout
(
urb
->
pipe
),
toggle
);
break
;
}
}
ret
=
hc_add_trans
(
hci
,
len
,
data
,
toggle
,
maxps
,
slow
,
endpoint
,
address
,
pid
,
iso
,
qu_urbstate
(
urb
));
DBGVERBOSE
(
"transfer_pa: addr:%d ep:%d pid:%x tog:%x iso:%x sl:%x "
"max:%d
\n
len:%d ret:%d data:%p left:%d
\n
"
,
address
,
endpoint
,
pid
,
toggle
,
iso
,
slow
,
maxps
,
len
,
ret
,
data
,
hci
->
hp
.
units_left
);
if
(
ret
>=
0
)
{
hci
->
td_array
->
td
[
hci
->
td_array
->
len
].
urb
=
urb
;
hci
->
td_array
->
td
[
hci
->
td_array
->
len
].
len
=
ret
;
hci
->
td_array
->
td
[
hci
->
td_array
->
len
].
iso_index
=
i
;
hci
->
td_array
->
len
++
;
hci
->
active_trans
=
1
;
return
1
;
}
return
0
;
}
/***************************************************************************
* Function Name : cc_to_error
*
* This function maps the SL811HS hardware error code to the linux USB error
* code.
*
* Input: cc = hardware error code
*
* Return: USB error code
**************************************************************************/
static
int
cc_to_error
(
int
cc
)
{
int
errCode
=
0
;
if
(
cc
&
SL11H_STATMASK_ERROR
)
{
errCode
|=
-
EILSEQ
;
}
else
if
(
cc
&
SL11H_STATMASK_OVF
)
{
errCode
|=
-
EOVERFLOW
;
}
else
if
(
cc
&
SL11H_STATMASK_STALL
)
{
errCode
|=
-
EPIPE
;
}
return
errCode
;
}
/***************************************************************************
* Function Name : sh_done_list
*
* This function process the packet when it has done finish transfer.
*
* 1) It handles hardware error
* 2) It updates the URB state
* 3) If the USB transaction is complete, it start the return stack path.
*
* Input: hci = data structure for the host controller
* isExcessNak = flag tells if there excess NAK condition occurred
*
* Return: urb_state or -1 if the transaction has complete
**************************************************************************/
static
int
sh_done_list
(
hci_t
*
hci
,
int
*
isExcessNak
)
{
int
actbytes
=
0
;
int
active
=
0
;
void
*
data
=
NULL
;
int
cc
;
int
maxps
;
int
toggle
;
struct
urb
*
urb
;
int
urb_state
=
0
;
int
ret
=
1
;
/* -1 parse abbort, 1 parse ok, 0 last element */
int
trans
=
0
;
int
len
;
int
iso_index
=
0
;
int
out
;
int
pid
=
0
;
int
debugLen
=
0
;
*
isExcessNak
=
0
;
DBGFUNC
(
"enter sh_done_list: td_array->len = 0x%x
\n
"
,
hci
->
td_array
->
len
);
debugLen
=
hci
->
td_array
->
len
;
if
(
debugLen
>
1
)
DBGERR
(
"sh_done_list: td_array->len = 0x%x > 1
\n
"
,
hci
->
td_array
->
len
);
for
(
trans
=
0
;
ret
&&
trans
<
hci
->
td_array
->
len
&&
trans
<
MAX_TRANS
;
trans
++
)
{
urb
=
hci
->
td_array
->
td
[
trans
].
urb
;
len
=
hci
->
td_array
->
td
[
trans
].
len
;
out
=
usb_pipeout
(
urb
->
pipe
);
if
(
usb_pipeisoc
(
urb
->
pipe
))
{
iso_index
=
hci
->
td_array
->
td
[
trans
].
iso_index
;
data
=
urb
->
transfer_buffer
+
urb
->
iso_frame_desc
[
iso_index
].
offset
;
toggle
=
0
;
}
else
{
data
=
urb
->
transfer_buffer
+
urb
->
actual_length
;
toggle
=
usb_gettoggle
(
urb
->
dev
,
usb_pipeendpoint
(
urb
->
pipe
),
usb_pipeout
(
urb
->
pipe
));
}
urb_state
=
qu_urbstate
(
urb
);
pid
=
out
?
PID_OUT
:
PID_IN
;
ret
=
hc_parse_trans
(
hci
,
&
actbytes
,
data
,
&
cc
,
&
toggle
,
len
,
pid
,
urb_state
);
maxps
=
usb_maxpacket
(
urb
->
dev
,
urb
->
pipe
,
usb_pipeout
(
urb
->
pipe
));
if
(
maxps
==
0
)
maxps
=
8
;
active
=
(
urb_state
!=
US_CTRL_SETUP
)
&&
(
actbytes
&&
!
(
actbytes
&
(
maxps
-
1
)));
/* If the transfer is not bulk in, then it is necessary to get all
* data specify by the urb->transfer_len.
*/
if
(
!
(
usb_pipebulk
(
urb
->
pipe
)
&&
usb_pipein
(
urb
->
pipe
)))
active
=
active
&&
(
urb
->
transfer_buffer_length
!=
urb
->
actual_length
+
actbytes
);
if
(
urb
->
transfer_buffer_length
==
urb
->
actual_length
+
actbytes
)
active
=
0
;
if
((
cc
&
(
SL11H_STATMASK_ERROR
|
SL11H_STATMASK_TMOUT
|
SL11H_STATMASK_OVF
|
SL11H_STATMASK_STALL
))
&&
!
(
cc
&
SL11H_STATMASK_NAK
))
{
if
(
++
urb
->
error_count
>
3
)
{
DBGERR
(
"done_list: excessive error: errcount = 0x%x, cc = 0x%x
\n
"
,
urb
->
error_count
,
cc
);
urb_state
=
0
;
active
=
0
;
}
else
{
DBGERR
(
"done_list: packet err, cc = 0x%x, "
" urb->length = 0x%x, actual_len = 0x%x,"
" urb_state =0x%x
\n
"
,
cc
,
urb
->
transfer_buffer_length
,
urb
->
actual_length
,
urb_state
);
// if (cc & SL11H_STATMASK_STALL) {
/* The USB function is STALLED on a control pipe (0),
* then it needs to send the SETUP command again to
* clear the STALL condition
*/
// if (usb_pipeendpoint (urb->pipe) == 0) {
// urb_state = 2;
// active = 0;
// }
// } else
active
=
1
;
}
}
else
{
if
(
cc
&
SL11H_STATMASK_NAK
)
{
if
(
hci
->
nakCnt
<
0x10000
)
{
hci
->
nakCnt
++
;
hci
->
last_packet_nak
=
1
;
active
=
1
;
*
isExcessNak
=
0
;
}
else
{
DBGERR
(
"done_list: nak count exceed limit
\n
"
);
active
=
0
;
*
isExcessNak
=
1
;
hci
->
nakCnt
=
0
;
}
}
else
{
hci
->
nakCnt
=
0
;
hci
->
last_packet_nak
=
0
;
}
if
(
urb_state
!=
US_CTRL_SETUP
)
{
/* no error */
urb
->
actual_length
+=
actbytes
;
usb_settoggle
(
urb
->
dev
,
usb_pipeendpoint
(
urb
->
pipe
),
usb_pipeout
(
urb
->
pipe
),
toggle
);
}
if
(
usb_pipeisoc
(
urb
->
pipe
))
{
urb
->
iso_frame_desc
[
iso_index
].
actual_length
=
actbytes
;
urb
->
iso_frame_desc
[
iso_index
].
status
=
cc_to_error
(
cc
);
active
=
(
iso_index
<
urb
->
number_of_packets
);
}
}
if
(
!
active
)
{
if
(
!
urb_state
)
{
urb
->
status
=
cc_to_error
(
cc
);
if
(
urb
->
status
)
{
DBGERR
(
"error on received packet: urb->status = 0x%x
\n
"
,
urb
->
status
);
}
hci
->
td_array
->
len
=
0
;
qu_return_urb
(
hci
,
urb
,
1
);
return
-
1
;
}
else
{
/* We do not want to decrement the urb_state if exceeded nak,
* because we need to finish the data stage of the control
* packet
*/
if
(
!
(
*
isExcessNak
))
urb_state
--
;
qu_seturbstate
(
urb
,
urb_state
);
}
}
}
if
(
urb_state
<
0
)
DBGERR
(
"ERROR: done_list, urb_state = %d, suppose > 0
\n
"
,
urb_state
);
if
(
debugLen
!=
hci
->
td_array
->
len
)
{
DBGERR
(
"ERROR: done_list, debugLen!= td_array->len,"
"debugLen = 0x%x, hci->td_array->len = 0x%x
\n
"
,
debugLen
,
hci
->
td_array
->
len
);
}
hci
->
td_array
->
len
=
0
;
return
urb_state
;
}
drivers/usb/host/hc_simple.h
deleted
100644 → 0
View file @
d5acaac4
/*-------------------------------------------------------------------------*/
/* list of all controllers using this driver
* */
static
LIST_HEAD
(
hci_hcd_list
);
/* URB states (urb_state) */
/* isoc, interrupt single state */
/* bulk transfer main state and 0-length packet */
#define US_BULK 0
#define US_BULK0 1
/* three setup states */
#define US_CTRL_SETUP 2
#define US_CTRL_DATA 1
#define US_CTRL_ACK 0
/*-------------------------------------------------------------------------*/
/* HC private part of a device descriptor
* */
#define NUM_EDS 32
typedef
struct
epd
{
struct
urb
*
pipe_head
;
struct
list_head
urb_queue
;
// int urb_state;
struct
timer_list
timeout
;
int
last_iso
;
/* timestamp of last queued ISOC transfer */
}
epd_t
;
struct
hci_device
{
epd_t
ed
[
NUM_EDS
];
};
/*-------------------------------------------------------------------------*/
/* Virtual Root HUB
*/
#define usb_to_hci(usb) ((struct hci_device *)(usb)->hcpriv)
struct
virt_root_hub
{
int
devnum
;
/* Address of Root Hub endpoint */
void
*
urb
;
/* interrupt URB of root hub */
int
send
;
/* active flag */
int
interval
;
/* interval of roothub interrupt transfers */
struct
timer_list
rh_int_timer
;
/* interval timer for rh interrupt EP */
};
#if 1
/* USB HUB CONSTANTS (not OHCI-specific; see hub.h and USB spec) */
/* destination of request */
#define RH_INTERFACE 0x01
#define RH_ENDPOINT 0x02
#define RH_OTHER 0x03
#define RH_CLASS 0x20
#define RH_VENDOR 0x40
/* Requests: bRequest << 8 | bmRequestType */
#define RH_GET_STATUS 0x0080
#define RH_CLEAR_FEATURE 0x0100
#define RH_SET_FEATURE 0x0300
#define RH_SET_ADDRESS 0x0500
#define RH_GET_DESCRIPTOR 0x0680
#define RH_SET_DESCRIPTOR 0x0700
#define RH_GET_CONFIGURATION 0x0880
#define RH_SET_CONFIGURATION 0x0900
#define RH_GET_STATE 0x0280
#define RH_GET_INTERFACE 0x0A80
#define RH_SET_INTERFACE 0x0B00
#define RH_SYNC_FRAME 0x0C80
/* Our Vendor Specific Request */
#define RH_SET_EP 0x2000
/* Hub port features */
#define RH_PORT_CONNECTION 0x00
#define RH_PORT_ENABLE 0x01
#define RH_PORT_SUSPEND 0x02
#define RH_PORT_OVER_CURRENT 0x03
#define RH_PORT_RESET 0x04
#define RH_PORT_POWER 0x08
#define RH_PORT_LOW_SPEED 0x09
#define RH_C_PORT_CONNECTION 0x10
#define RH_C_PORT_ENABLE 0x11
#define RH_C_PORT_SUSPEND 0x12
#define RH_C_PORT_OVER_CURRENT 0x13
#define RH_C_PORT_RESET 0x14
/* Hub features */
#define RH_C_HUB_LOCAL_POWER 0x00
#define RH_C_HUB_OVER_CURRENT 0x01
#define RH_DEVICE_REMOTE_WAKEUP 0x00
#define RH_ENDPOINT_STALL 0x01
#endif
/*-------------------------------------------------------------------------*/
/* struct for each HC
*/
#define MAX_TRANS 32
typedef
struct
td
{
struct
urb
*
urb
;
__u16
len
;
__u16
iso_index
;
}
td_t
;
typedef
struct
td_array
{
int
len
;
td_t
td
[
MAX_TRANS
];
}
td_array_t
;
typedef
struct
hci
{
struct
virt_root_hub
rh
;
/* roothub */
wait_queue_head_t
waitq
;
/* deletion of URBs and devices needs a waitqueue */
int
active
;
/* HC is operating */
struct
list_head
ctrl_list
;
/* set of ctrl endpoints */
struct
list_head
bulk_list
;
/* set of bulk endpoints */
struct
list_head
iso_list
;
/* set of isoc endpoints */
struct
list_head
intr_list
;
/* ordered (tree) set of int endpoints */
struct
list_head
del_list
;
/* set of entpoints to be deleted */
td_array_t
*
td_array
;
td_array_t
a_td_array
;
td_array_t
i_td_array
[
2
];
struct
list_head
hci_hcd_list
;
/* list of all hci_hcd */
struct
usb_bus
*
bus
;
/* our bus */
// int trans; /* number of transactions pending */
int
active_urbs
;
int
active_trans
;
int
frame_number
;
/* frame number */
hcipriv_t
hp
;
/* individual part of hc type */
int
nakCnt
;
int
last_packet_nak
;
}
hci_t
;
/*-------------------------------------------------------------------------*/
/* condition (error) CC codes and mapping OHCI like
*/
#define TD_CC_NOERROR 0x00
#define TD_CC_CRC 0x01
#define TD_CC_BITSTUFFING 0x02
#define TD_CC_DATATOGGLEM 0x03
#define TD_CC_STALL 0x04
#define TD_DEVNOTRESP 0x05
#define TD_PIDCHECKFAIL 0x06
#define TD_UNEXPECTEDPID 0x07
#define TD_DATAOVERRUN 0x08
#define TD_DATAUNDERRUN 0x09
#define TD_BUFFEROVERRUN 0x0C
#define TD_BUFFERUNDERRUN 0x0D
#define TD_NOTACCESSED 0x0F
/* urb interface functions */
static
int
hci_get_current_frame_number
(
struct
usb_device
*
usb_dev
);
static
int
hci_unlink_urb
(
struct
urb
*
urb
);
static
int
qu_queue_urb
(
hci_t
*
hci
,
struct
urb
*
urb
);
/* root hub */
static
int
rh_init_int_timer
(
struct
urb
*
urb
);
static
int
rh_submit_urb
(
struct
urb
*
urb
);
static
int
rh_unlink_urb
(
struct
urb
*
urb
);
/* schedule functions */
static
int
sh_add_packet
(
hci_t
*
hci
,
struct
urb
*
urb
);
/* hc specific functions */
static
inline
void
hc_flush_data_cache
(
hci_t
*
hci
,
void
*
data
,
int
len
);
static
inline
int
hc_parse_trans
(
hci_t
*
hci
,
int
*
actbytes
,
__u8
*
data
,
int
*
cc
,
int
*
toggle
,
int
length
,
int
pid
,
int
urb_state
);
static
inline
int
hc_add_trans
(
hci_t
*
hci
,
int
len
,
void
*
data
,
int
toggle
,
int
maxps
,
int
slow
,
int
endpoint
,
int
address
,
int
pid
,
int
format
,
int
urb_state
);
static
void
hc_start_int
(
hci_t
*
hci
);
static
void
hc_stop_int
(
hci_t
*
hci
);
static
void
SL811Write
(
hci_t
*
hci
,
char
offset
,
char
data
);
/* debug| print the main components of an URB
* small: 0) header + data packets 1) just header */
static
void
urb_print
(
struct
urb
*
urb
,
char
*
str
,
int
small
)
{
unsigned
int
pipe
=
urb
->
pipe
;
int
i
,
len
;
if
(
!
urb
->
dev
||
!
urb
->
dev
->
bus
)
{
dbg
(
"%s URB: no dev"
,
str
);
return
;
}
printk
(
"%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)
\n
"
,
str
,
hci_get_current_frame_number
(
urb
->
dev
),
usb_pipedevice
(
pipe
),
usb_pipeendpoint
(
pipe
),
usb_pipeout
(
pipe
)
?
'O'
:
'I'
,
usb_pipetype
(
pipe
)
<
2
?
(
usb_pipeint
(
pipe
)
?
"INTR"
:
"ISOC"
)
:
(
usb_pipecontrol
(
pipe
)
?
"CTRL"
:
"BULK"
),
urb
->
transfer_flags
,
urb
->
actual_length
,
urb
->
transfer_buffer_length
,
urb
->
status
,
urb
->
status
);
if
(
!
small
)
{
if
(
usb_pipecontrol
(
pipe
))
{
printk
(
__FILE__
": cmd(8):"
);
for
(
i
=
0
;
i
<
8
;
i
++
)
printk
(
" %02x"
,
((
__u8
*
)
urb
->
setup_packet
)[
i
]);
printk
(
"
\n
"
);
}
if
(
urb
->
transfer_buffer_length
>
0
&&
urb
->
transfer_buffer
)
{
printk
(
__FILE__
": data(%d/%d):"
,
urb
->
actual_length
,
urb
->
transfer_buffer_length
);
len
=
usb_pipeout
(
pipe
)
?
urb
->
transfer_buffer_length
:
urb
->
actual_length
;
for
(
i
=
0
;
i
<
2096
&&
i
<
len
;
i
++
)
printk
(
" %02x"
,
((
__u8
*
)
urb
->
transfer_buffer
)[
i
]);
printk
(
"%s stat:%d
\n
"
,
i
<
len
?
"..."
:
""
,
urb
->
status
);
}
}
}
drivers/usb/host/hc_sl811.c
deleted
100644 → 0
View file @
d5acaac4
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*
* SL811HS USB HCD for Linux Version 0.1 (10/28/2001)
*
* requires (includes) hc_simple.[hc] simple generic HCD frontend
*
* COPYRIGHT(C) 2001 by CYPRESS SEMICONDUCTOR INC.
*
*-------------------------------------------------------------------------*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*-------------------------------------------------------------------------*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/list.h>
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/usb.h>
#include "../core/hcd.h"
#undef HC_URB_TIMEOUT
#undef HC_SWITCH_INT
#undef HC_ENABLE_ISOC
#define SL811_DEBUG_ERR
#ifdef SL811_DEBUG_ERR
#define DBGERR(fmt, args...) printk(fmt,## args)
#else
#define DBGERR(fmt, args...)
#endif
#ifdef SL811_DEBUG
#define DBG(fmt, args...) printk(fmt,## args)
#else
#define DBG(fmt, args...)
#endif
#ifdef SL811_DEBUG_FUNC
#define DBGFUNC(fmt, args...) printk(fmt,## args)
#else
#define DBGFUNC(fmt, args...)
#endif
#ifdef SL811_DEBUG_DATA
#define DBGDATAR(fmt, args...) printk(fmt,## args)
#define DBGDATAW(fmt, args...) printk(fmt,## args)
#else
#define DBGDATAR(fmt, args...)
#define DBGDATAW(fmt, args...)
#endif
#ifdef SL811_DEBUG_VERBOSE
#define DBGVERBOSE(fmt, args...) printk(fmt,## args)
#else
#define DBGVERBOSE(fmt, args...)
#endif
#define TRUE 1
#define FALSE 0
#define HC_SWITCH_INT
#include "hc_sl811.h"
#include "hc_simple.h"
static
int
urb_debug
=
0
;
#include "hc_simple.c"
#include "hc_sl811_rh.c"
/* The base_addr, data_reg_addr, and irq number are board specific.
* The current values are design to run on the Accelent SA1110 IDP
* NOTE: values need to modify for different development boards
*/
static
int
base_addr
=
0xd3800000
;
static
int
data_reg_addr
=
0xd3810000
;
static
int
irq
=
34
;
/* forware declaration */
int
SL11StartXaction
(
hci_t
*
hci
,
__u8
addr
,
__u8
epaddr
,
int
pid
,
int
len
,
int
toggle
,
int
slow
,
int
urb_state
);
static
int
sofWaitCnt
=
0
;
module_param
(
urb_debug
,
int
,
0
);
MODULE_PARM_DESC
(
urb_debug
,
"debug urb messages, default is 0 (no)"
);
module_param
(
base_addr
,
int
,
0
);
MODULE_PARM_DESC
(
base_addr
,
"sl811 base address 0xd3800000"
);
module_param
(
data_reg_addr
,
int
,
0
);
MODULE_PARM_DESC
(
data_reg_addr
,
"sl811 data register address 0xd3810000"
);
module_param
(
irq
,
int
,
0
);
MODULE_PARM_DESC
(
irq
,
"IRQ 34 (default)"
);
static
int
hc_reset
(
hci_t
*
hci
);
/***************************************************************************
* Function Name : SL811Read
*
* Read a byte of data from the SL811H/SL11H
*
* Input: hci = data structure for the host controller
* offset = address of SL811/SL11H register or memory
*
* Return: data
**************************************************************************/
char
SL811Read
(
hci_t
*
hci
,
char
offset
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
char
data
;
writeb
(
offset
,
hp
->
hcport
);
wmb
();
data
=
readb
(
hp
->
hcport2
);
rmb
();
return
(
data
);
}
/***************************************************************************
* Function Name : SL811Write
*
* Write a byte of data to the SL811H/SL11H
*
* Input: hci = data structure for the host controller
* offset = address of SL811/SL11H register or memory
* data = the data going to write to SL811H
*
* Return: none
**************************************************************************/
void
SL811Write
(
hci_t
*
hci
,
char
offset
,
char
data
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
writeb
(
offset
,
hp
->
hcport
);
writeb
(
data
,
hp
->
hcport2
);
wmb
();
}
/***************************************************************************
* Function Name : SL811BufRead
*
* Read consecutive bytes of data from the SL811H/SL11H buffer
*
* Input: hci = data structure for the host controller
* offset = SL811/SL11H register offset
* buf = the buffer where the data will store
* size = number of bytes to read
*
* Return: none
**************************************************************************/
void
SL811BufRead
(
hci_t
*
hci
,
short
offset
,
char
*
buf
,
short
size
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
if
(
size
<=
0
)
return
;
writeb
((
char
)
offset
,
hp
->
hcport
);
wmb
();
DBGDATAR
(
"SL811BufRead: offset = 0x%x, data = "
,
offset
);
while
(
size
--
)
{
*
buf
++
=
(
char
)
readb
(
hp
->
hcport2
);
DBGDATAR
(
"0x%x "
,
*
(
buf
-
1
));
rmb
();
}
DBGDATAR
(
"
\n
"
);
}
/***************************************************************************
* Function Name : SL811BufWrite
*
* Write consecutive bytes of data to the SL811H/SL11H buffer
*
* Input: hci = data structure for the host controller
* offset = SL811/SL11H register offset
* buf = the data buffer
* size = number of bytes to write
*
* Return: none
**************************************************************************/
void
SL811BufWrite
(
hci_t
*
hci
,
short
offset
,
char
*
buf
,
short
size
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
if
(
size
<=
0
)
return
;
writeb
((
char
)
offset
,
hp
->
hcport
);
wmb
();
DBGDATAW
(
"SL811BufWrite: offset = 0x%x, data = "
,
offset
);
while
(
size
--
)
{
DBGDATAW
(
"0x%x "
,
*
buf
);
writeb
(
*
buf
,
hp
->
hcport2
);
wmb
();
buf
++
;
}
DBGDATAW
(
"
\n
"
);
}
/***************************************************************************
* Function Name : regTest
*
* This routine test the Read/Write functionality of SL811HS registers
*
* 1) Store original register value into a buffer
* 2) Write to registers with a RAMP pattern. (10, 11, 12, ..., 255)
* 3) Read from register
* 4) Compare the written value with the read value and make sure they are
* equivalent
* 5) Restore the original register value
*
* Input: hci = data structure for the host controller
*
*
* Return: TRUE = passed; FALSE = failed
**************************************************************************/
int
regTest
(
hci_t
*
hci
)
{
int
i
,
data
,
result
=
TRUE
;
char
buf
[
256
];
DBGFUNC
(
"Enter regTest
\n
"
);
for
(
i
=
0x10
;
i
<
256
;
i
++
)
{
/* save the original buffer */
buf
[
i
]
=
(
char
)
SL811Read
(
hci
,
i
);
/* Write the new data to the buffer */
SL811Write
(
hci
,
i
,
i
);
}
/* compare the written data */
for
(
i
=
0x10
;
i
<
256
;
i
++
)
{
data
=
SL811Read
(
hci
,
i
);
if
(
data
!=
i
)
{
DBGERR
(
"Pattern test failed!! value = 0x%x, s/b 0x%x
\n
"
,
data
,
i
);
result
=
FALSE
;
}
}
/* restore the data */
for
(
i
=
0x10
;
i
<
256
;
i
++
)
{
SL811Write
(
hci
,
i
,
buf
[
i
]);
}
return
(
result
);
}
/***************************************************************************
* Function Name : regShow
*
* Display all SL811HS register values
*
* Input: hci = data structure for the host controller
*
* Return: none
**************************************************************************/
void
regShow
(
hci_t
*
hci
)
{
int
i
;
for
(
i
=
0
;
i
<
256
;
i
++
)
{
printk
(
"offset %d: 0x%x
\n
"
,
i
,
SL811Read
(
hci
,
i
));
}
}
/************************************************************************
* Function Name : USBReset
*
* This function resets SL811HS controller and detects the speed of
* the connecting device
*
* Input: hci = data structure for the host controller
*
* Return: 0 = no device attached; 1 = USB device attached
*
***********************************************************************/
static
int
USBReset
(
hci_t
*
hci
)
{
int
status
;
hcipriv_t
*
hp
=
&
hci
->
hp
;
DBGFUNC
(
"enter USBReset
\n
"
);
SL811Write
(
hci
,
SL11H_CTLREG2
,
0xae
);
// setup master and full speed
SL811Write
(
hci
,
SL11H_CTLREG1
,
0x08
);
// reset USB
mdelay
(
20
);
// 20ms
SL811Write
(
hci
,
SL11H_CTLREG1
,
0
);
// remove SE0
for
(
status
=
0
;
status
<
100
;
status
++
)
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
// clear all interrupt bits
status
=
SL811Read
(
hci
,
SL11H_INTSTATREG
);
if
(
status
&
0x40
)
// Check if device is removed
{
DBG
(
"USBReset: Device removed
\n
"
);
SL811Write
(
hci
,
SL11H_INTENBLREG
,
SL11H_INTMASK_XFERDONE
|
SL11H_INTMASK_SOFINTR
|
SL11H_INTMASK_INSRMV
);
hp
->
RHportStatus
->
portStatus
&=
~
(
PORT_CONNECT_STAT
|
PORT_ENABLE_STAT
);
return
0
;
}
SL811Write
(
hci
,
SL11H_BUFLNTHREG_B
,
0
);
//zero lenth
SL811Write
(
hci
,
SL11H_PIDEPREG_B
,
0x50
);
//send SOF to EP0
SL811Write
(
hci
,
SL11H_DEVADDRREG_B
,
0x01
);
//address0
SL811Write
(
hci
,
SL11H_SOFLOWREG
,
0xe0
);
if
(
!
(
status
&
0x80
))
{
/* slow speed device connect directly to root-hub */
DBG
(
"USBReset: low speed Device attached
\n
"
);
SL811Write
(
hci
,
SL11H_CTLREG1
,
0x8
);
mdelay
(
20
);
SL811Write
(
hci
,
SL11H_SOFTMRREG
,
0xee
);
SL811Write
(
hci
,
SL11H_CTLREG1
,
0x21
);
/* start the SOF or EOP */
SL811Write
(
hci
,
SL11H_HOSTCTLREG_B
,
0x01
);
hp
->
RHportStatus
->
portStatus
|=
(
PORT_CONNECT_STAT
|
PORT_LOW_SPEED_DEV_ATTACH_STAT
);
/* clear all interrupt bits */
for
(
status
=
0
;
status
<
20
;
status
++
)
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
}
else
{
/* full speed device connect directly to root hub */
DBG
(
"USBReset: full speed Device attached
\n
"
);
SL811Write
(
hci
,
SL11H_CTLREG1
,
0x8
);
mdelay
(
20
);
SL811Write
(
hci
,
SL11H_SOFTMRREG
,
0xae
);
SL811Write
(
hci
,
SL11H_CTLREG1
,
0x01
);
/* start the SOF or EOP */
SL811Write
(
hci
,
SL11H_HOSTCTLREG_B
,
0x01
);
hp
->
RHportStatus
->
portStatus
|=
(
PORT_CONNECT_STAT
);
hp
->
RHportStatus
->
portStatus
&=
~
PORT_LOW_SPEED_DEV_ATTACH_STAT
;
/* clear all interrupt bits */
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
}
/* enable all interrupts */
SL811Write
(
hci
,
SL11H_INTENBLREG
,
SL11H_INTMASK_XFERDONE
|
SL11H_INTMASK_SOFINTR
|
SL11H_INTMASK_INSRMV
);
return
1
;
}
/*-------------------------------------------------------------------------*/
/* tl functions */
static
inline
void
hc_mark_last_trans
(
hci_t
*
hci
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
__u8
*
ptd
=
hp
->
tl
;
dbg
(
"enter hc_mark_last_trans
\n
"
);
if
(
ptd
==
NULL
)
{
printk
(
"hc_mark_last_trans: ptd = null
\n
"
);
return
;
}
if
(
hp
->
xferPktLen
>
0
)
*
(
ptd
+
hp
->
tl_last
)
|=
(
1
<<
3
);
}
static
inline
void
hc_flush_data_cache
(
hci_t
*
hci
,
void
*
data
,
int
len
)
{
}
/************************************************************************
* Function Name : hc_add_trans
*
* This function sets up the SL811HS register and transmit the USB packets.
*
* 1) Determine if enough time within the current frame to send the packet
* 2) Load the data into the SL811HS register
* 3) Set the appropriate command to the register and trigger the transmit
*
* Input: hci = data structure for the host controller
* len = data length
* data = transmitting data
* toggle = USB toggle bit, either 0 or 1
* maxps = maximum packet size for this endpoint
* slow = speed of the device
* endpoint = endpoint number
* address = USB address of the device
* pid = packet ID
* format =
* urb_state = the current stage of USB transaction
*
* Return: 0 = no time left to schedule the transfer
* 1 = success
*
***********************************************************************/
static
inline
int
hc_add_trans
(
hci_t
*
hci
,
int
len
,
void
*
data
,
int
toggle
,
int
maxps
,
int
slow
,
int
endpoint
,
int
address
,
int
pid
,
int
format
,
int
urb_state
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
__u16
speed
;
int
ii
,
jj
,
kk
;
DBGFUNC
(
"enter hc_addr_trans: len =0x%x, toggle:0x%x, endpoing:0x%x,"
" addr:0x%x, pid:0x%x,format:0x%x
\n
"
,
len
,
toggle
,
endpoint
,
i
address
,
pid
,
format
);
if
(
len
>
maxps
)
{
len
=
maxps
;
}
speed
=
hp
->
RHportStatus
->
portStatus
;
if
(
speed
&
PORT_LOW_SPEED_DEV_ATTACH_STAT
)
{
// ii = (8*7*8 + 6*3) * len + 800;
ii
=
8
*
8
*
len
+
1024
;
}
else
{
if
(
slow
)
{
// ii = (8*7*8 + 6*3) * len + 800;
ii
=
8
*
8
*
len
+
2048
;
}
else
// ii = (8*7 + 6*3)*len + 110;
ii
=
8
*
len
+
256
;
}
ii
+=
2
*
10
*
len
;
jj
=
SL811Read
(
hci
,
SL11H_SOFTMRREG
);
kk
=
(
jj
&
0xFF
)
*
64
-
ii
;
if
(
kk
<
0
)
{
DBGVERBOSE
(
"hc_add_trans: no bandwidth for schedule, ii = 0x%x,"
"jj = 0x%x, len =0x%x, active_trans = 0x%x
\n
"
,
ii
,
jj
,
len
,
hci
->
active_trans
);
return
(
-
1
);
}
if
(
pid
!=
PID_IN
)
{
/* Load data into hc */
SL811BufWrite
(
hci
,
SL11H_DATA_START
,
(
__u8
*
)
data
,
len
);
}
/* transmit */
SL11StartXaction
(
hci
,
(
__u8
)
address
,
(
__u8
)
endpoint
,
(
__u8
)
pid
,
len
,
toggle
,
slow
,
urb_state
);
return
len
;
}
/************************************************************************
* Function Name : hc_parse_trans
*
* This function checks the status of the transmitted or received packet
* and copy the data from the SL811HS register into a buffer.
*
* 1) Check the status of the packet
* 2) If successful, and IN packet then copy the data from the SL811HS register
* into a buffer
*
* Input: hci = data structure for the host controller
* actbytes = pointer to actual number of bytes
* data = data buffer
* cc = packet status
* length = the urb transmit length
* pid = packet ID
* urb_state = the current stage of USB transaction
*
* Return: 0
***********************************************************************/
static
inline
int
hc_parse_trans
(
hci_t
*
hci
,
int
*
actbytes
,
__u8
*
data
,
int
*
cc
,
int
*
toggle
,
int
length
,
int
pid
,
int
urb_state
)
{
__u8
addr
;
__u8
len
;
DBGFUNC
(
"enter hc_parse_trans
\n
"
);
/* get packet status; convert ack rcvd to ack-not-rcvd */
*
cc
=
(
int
)
SL811Read
(
hci
,
SL11H_PKTSTATREG
);
if
(
*
cc
&
(
SL11H_STATMASK_ERROR
|
SL11H_STATMASK_TMOUT
|
SL11H_STATMASK_OVF
|
SL11H_STATMASK_NAK
|
SL11H_STATMASK_STALL
))
{
if
(
*
cc
&
SL11H_STATMASK_OVF
)
DBGERR
(
"parse trans: error recv ack, cc = 0x%x, TX_BASE_Len = "
"0x%x, TX_count=0x%x
\n
"
,
*
cc
,
SL811Read
(
hci
,
SL11H_BUFLNTHREG
),
SL811Read
(
hci
,
SL11H_XFERCNTREG
));
}
else
{
DBGVERBOSE
(
"parse trans: recv ack, cc = 0x%x, len = 0x%x,
\n
"
,
*
cc
,
length
);
/* Successful data */
if
((
pid
==
PID_IN
)
&&
(
urb_state
!=
US_CTRL_SETUP
))
{
/* Find the base address */
addr
=
SL811Read
(
hci
,
SL11H_BUFADDRREG
);
/* Find the Transmit Length */
len
=
SL811Read
(
hci
,
SL11H_BUFLNTHREG
);
/* The actual data length = xmit length reg - xfer count reg */
*
actbytes
=
len
-
SL811Read
(
hci
,
SL11H_XFERCNTREG
);
if
((
data
!=
NULL
)
&&
(
*
actbytes
>
0
))
{
SL811BufRead
(
hci
,
addr
,
data
,
*
actbytes
);
}
else
if
((
data
==
NULL
)
&&
(
*
actbytes
<=
0
))
{
DBGERR
(
"hc_parse_trans: data = NULL or actbyte = 0x%x
\n
"
,
*
actbytes
);
return
0
;
}
}
else
if
(
pid
==
PID_OUT
)
{
*
actbytes
=
length
;
}
else
{
// printk ("ERR:parse_trans, pid != IN or OUT, pid = 0x%x\n", pid);
}
*
toggle
=
!*
toggle
;
}
return
0
;
}
/************************************************************************
* Function Name : hc_start_int
*
* This function enables SL811HS interrupts
*
* Input: hci = data structure for the host controller
*
* Return: none
***********************************************************************/
static
void
hc_start_int
(
hci_t
*
hci
)
{
#ifdef HC_SWITCH_INT
int
mask
=
SL11H_INTMASK_XFERDONE
|
SL11H_INTMASK_SOFINTR
|
SL11H_INTMASK_INSRMV
|
SL11H_INTMASK_USBRESET
;
SL811Write
(
hci
,
IntEna
,
mask
);
#endif
}
/************************************************************************
* Function Name : hc_stop_int
*
* This function disables SL811HS interrupts
*
* Input: hci = data structure for the host controller
*
* Return: none
***********************************************************************/
static
void
hc_stop_int
(
hci_t
*
hci
)
{
#ifdef HC_SWITCH_INT
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
// SL811Write(hci, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV);
#endif
}
/************************************************************************
* Function Name : handleInsRmvIntr
*
* This function handles the insertion or removal of device on SL811HS.
* It resets the controller and updates the port status
*
* Input: hci = data structure for the host controller
*
* Return: none
***********************************************************************/
void
handleInsRmvIntr
(
hci_t
*
hci
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
USBReset
(
hci
);
/* Changes in connection status */
hp
->
RHportStatus
->
portChange
|=
PORT_CONNECT_CHANGE
;
/* Port Enable or Disable */
if
(
hp
->
RHportStatus
->
portStatus
&
PORT_CONNECT_STAT
)
{
/* device is connected to the port:
* 1) Enable port
* 2) Resume ??
*/
// hp->RHportStatus->portChange |= PORT_ENABLE_CHANGE;
/* Over Current is not supported by the SL811 HW ?? */
/* How about the Port Power ?? */
}
else
{
/* Device has disconnect:
* 1) Disable port
*/
hp
->
RHportStatus
->
portStatus
&=
~
(
PORT_ENABLE_STAT
);
hp
->
RHportStatus
->
portChange
|=
PORT_ENABLE_CHANGE
;
}
}
/*****************************************************************
*
* Function Name: SL11StartXaction
*
* This functions load the registers with appropriate value and
* transmit the packet.
*
* Input: hci = data structure for the host controller
* addr = USB address of the device
* epaddr = endpoint number
* pid = packet ID
* len = data length
* toggle = USB toggle bit, either 0 or 1
* slow = speed of the device
* urb_state = the current stage of USB transaction
*
* Return: 0 = error; 1 = successful
*
*****************************************************************/
int
SL11StartXaction
(
hci_t
*
hci
,
__u8
addr
,
__u8
epaddr
,
int
pid
,
int
len
,
int
toggle
,
int
slow
,
int
urb_state
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
__u8
cmd
=
0
;
__u8
setup_data
[
4
];
__u16
speed
;
speed
=
hp
->
RHportStatus
->
portStatus
;
if
(
!
(
speed
&
PORT_LOW_SPEED_DEV_ATTACH_STAT
)
&&
slow
)
{
cmd
|=
SL11H_HCTLMASK_PREAMBLE
;
}
switch
(
pid
)
{
case
PID_SETUP
:
cmd
&=
SL11H_HCTLMASK_PREAMBLE
;
cmd
|=
(
SL11H_HCTLMASK_ARM
|
SL11H_HCTLMASK_ENBLEP
|
SL11H_HCTLMASK_WRITE
);
break
;
case
PID_OUT
:
cmd
&=
(
SL11H_HCTLMASK_SEQ
|
SL11H_HCTLMASK_PREAMBLE
);
cmd
|=
(
SL11H_HCTLMASK_ARM
|
SL11H_HCTLMASK_ENBLEP
|
SL11H_HCTLMASK_WRITE
);
if
(
toggle
)
{
cmd
|=
SL11H_HCTLMASK_SEQ
;
}
break
;
case
PID_IN
:
cmd
&=
(
SL11H_HCTLMASK_SEQ
|
SL11H_HCTLMASK_PREAMBLE
);
cmd
|=
(
SL11H_HCTLMASK_ARM
|
SL11H_HCTLMASK_ENBLEP
);
break
;
default:
DBGERR
(
"ERR: SL11StartXaction: unknow pid = 0x%x
\n
"
,
pid
);
return
0
;
}
setup_data
[
0
]
=
SL11H_DATA_START
;
setup_data
[
1
]
=
len
;
setup_data
[
2
]
=
(((
pid
&
0x0F
)
<<
4
)
|
(
epaddr
&
0xF
));
setup_data
[
3
]
=
addr
&
0x7F
;
SL811BufWrite
(
hci
,
SL11H_BUFADDRREG
,
(
__u8
*
)
&
setup_data
[
0
],
4
);
SL811Write
(
hci
,
SL11H_HOSTCTLREG
,
cmd
);
#if 0
/* The SL811 has a hardware flaw when hub devices sends out
* SE0 between packets. It has been found in a TI chipset and
* cypress hub chipset. It causes the SL811 to hang
* The workaround is to re-issue the preample again.
*/
if ((cmd & SL11H_HCTLMASK_PREAMBLE)) {
SL811Write (hci, SL11H_PIDEPREG_B, 0xc0);
SL811Write (hci, SL11H_HOSTCTLREG_B, 0x1); // send the premable
}
#endif
return
1
;
}
/*****************************************************************
*
* Function Name: hc_interrupt
*
* Interrupt service routine.
*
* 1) determine the causes of interrupt
* 2) clears all interrupts
* 3) calls appropriate function to service the interrupt
*
* Input: irq = interrupt line associated with the controller
* hci = data structure for the host controller
* r = holds the snapshot of the processor's context before
* the processor entered interrupt code. (not used here)
*
* Return value : None.
*
*****************************************************************/
static
void
hc_interrupt
(
int
irq
,
void
*
__hci
,
struct
pt_regs
*
r
)
{
char
ii
;
hci_t
*
hci
=
__hci
;
int
isExcessNak
=
0
;
int
urb_state
=
0
;
char
tmpIrq
=
0
;
/* Get value from interrupt status register */
ii
=
SL811Read
(
hci
,
SL11H_INTSTATREG
);
if
(
ii
&
SL11H_INTMASK_INSRMV
)
{
/* Device insertion or removal detected for the USB port */
SL811Write
(
hci
,
SL11H_INTENBLREG
,
0
);
SL811Write
(
hci
,
SL11H_CTLREG1
,
0
);
mdelay
(
100
);
// wait for device stable
handleInsRmvIntr
(
hci
);
return
;
}
/* Clear all interrupts */
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
if
(
ii
&
SL11H_INTMASK_XFERDONE
)
{
/* USB Done interrupt occurred */
urb_state
=
sh_done_list
(
hci
,
&
isExcessNak
);
#ifdef WARNING
if
(
hci
->
td_array
->
len
>
0
)
printk
(
"WARNING: IRQ, td_array->len = 0x%x, s/b:0
\n
"
,
hci
->
td_array
->
len
);
#endif
if
(
hci
->
td_array
->
len
==
0
&&
!
isExcessNak
&&
!
(
ii
&
SL11H_INTMASK_SOFINTR
)
&&
(
urb_state
==
0
))
{
if
(
urb_state
==
0
)
{
/* All urb_state has not been finished yet!
* continue with the current urb transaction
*/
if
(
hci
->
last_packet_nak
==
0
)
{
if
(
!
usb_pipecontrol
(
hci
->
td_array
->
td
[
0
].
urb
->
pipe
))
sh_add_packet
(
hci
,
hci
->
td_array
->
td
[
0
].
urb
);
}
}
else
{
/* The last transaction has completed:
* schedule the next transaction
*/
sh_schedule_trans
(
hci
,
0
);
}
}
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
return
;
}
if
(
ii
&
SL11H_INTMASK_SOFINTR
)
{
hci
->
frame_number
=
(
hci
->
frame_number
+
1
)
%
2048
;
if
(
hci
->
td_array
->
len
==
0
)
sh_schedule_trans
(
hci
,
1
);
else
{
if
(
sofWaitCnt
++
>
100
)
{
/* The last transaction has not completed.
* Need to retire the current td, and let
* it transmit again later on.
* (THIS NEEDS TO BE WORK ON MORE, IT SHOULD NEVER
* GET TO THIS POINT)
*/
DBGERR
(
"SOF interrupt: td_array->len = 0x%x, s/b: 0
\n
"
,
hci
->
td_array
->
len
);
urb_print
(
hci
->
td_array
->
td
[
hci
->
td_array
->
len
-
1
].
urb
,
"INTERRUPT"
,
0
);
sh_done_list
(
hci
,
&
isExcessNak
);
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
hci
->
td_array
->
len
=
0
;
sofWaitCnt
=
0
;
}
}
tmpIrq
=
SL811Read
(
hci
,
SL11H_INTSTATREG
)
&
SL811Read
(
hci
,
SL11H_INTENBLREG
);
if
(
tmpIrq
)
{
DBG
(
"IRQ occurred while service SOF: irq = 0x%x
\n
"
,
tmpIrq
);
/* If we receive a DONE IRQ after schedule, need to
* handle DONE IRQ again
*/
if
(
tmpIrq
&
SL11H_INTMASK_XFERDONE
)
{
DBGERR
(
"IRQ occurred while service SOF: irq = 0x%x
\n
"
,
tmpIrq
);
urb_state
=
sh_done_list
(
hci
,
&
isExcessNak
);
}
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
}
}
else
{
DBG
(
"SL811 ISR: unknown, int = 0x%x
\n
"
,
ii
);
}
SL811Write
(
hci
,
SL11H_INTSTATREG
,
0xff
);
return
;
}
/*****************************************************************
*
* Function Name: hc_reset
*
* This function does register test and resets the SL811HS
* controller.
*
* Input: hci = data structure for the host controller
*
* Return value : 0
*
*****************************************************************/
static
int
hc_reset
(
hci_t
*
hci
)
{
int
attachFlag
=
0
;
DBGFUNC
(
"Enter hc_reset
\n
"
);
regTest
(
hci
);
attachFlag
=
USBReset
(
hci
);
if
(
attachFlag
)
{
setPortChange
(
hci
,
PORT_CONNECT_CHANGE
);
}
return
(
0
);
}
/*****************************************************************
*
* Function Name: hc_alloc_trans_buffer
*
* This function allocates all transfer buffer
*
* Input: hci = data structure for the host controller
*
* Return value : 0
*
*****************************************************************/
static
int
hc_alloc_trans_buffer
(
hci_t
*
hci
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
int
maxlen
;
hp
->
itl0_len
=
0
;
hp
->
itl1_len
=
0
;
hp
->
atl_len
=
0
;
hp
->
itl_buffer_len
=
1024
;
hp
->
atl_buffer_len
=
4096
-
2
*
hp
->
itl_buffer_len
;
/* 2048 */
maxlen
=
(
hp
->
itl_buffer_len
>
hp
->
atl_buffer_len
)
?
hp
->
itl_buffer_len
:
hp
->
atl_buffer_len
;
hp
->
tl
=
kmalloc
(
maxlen
,
GFP_KERNEL
);
if
(
!
hp
->
tl
)
return
-
ENOMEM
;
memset
(
hp
->
tl
,
0
,
maxlen
);
return
0
;
}
/*****************************************************************
*
* Function Name: getPortStatusAndChange
*
* This function gets the ports status from SL811 and format it
* to a USB request format
*
* Input: hci = data structure for the host controller
*
* Return value : port status and change
*
*****************************************************************/
static
__u32
getPortStatusAndChange
(
hci_t
*
hci
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
__u32
portstatus
;
DBGFUNC
(
"enter getPorStatusAndChange
\n
"
);
portstatus
=
hp
->
RHportStatus
->
portChange
<<
16
|
hp
->
RHportStatus
->
portStatus
;
return
(
portstatus
);
}
/*****************************************************************
*
* Function Name: setPortChange
*
* This function set the bit position of portChange.
*
* Input: hci = data structure for the host controller
* bitPos = the bit position
*
* Return value : none
*
*****************************************************************/
static
void
setPortChange
(
hci_t
*
hci
,
__u16
bitPos
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
switch
(
bitPos
)
{
case
PORT_CONNECT_STAT
:
hp
->
RHportStatus
->
portChange
|=
bitPos
;
break
;
case
PORT_ENABLE_STAT
:
hp
->
RHportStatus
->
portChange
|=
bitPos
;
break
;
case
PORT_RESET_STAT
:
hp
->
RHportStatus
->
portChange
|=
bitPos
;
break
;
case
PORT_POWER_STAT
:
hp
->
RHportStatus
->
portChange
|=
bitPos
;
break
;
case
PORT_SUSPEND_STAT
:
hp
->
RHportStatus
->
portChange
|=
bitPos
;
break
;
case
PORT_OVER_CURRENT_STAT
:
hp
->
RHportStatus
->
portChange
|=
bitPos
;
break
;
}
}
/*****************************************************************
*
* Function Name: clrPortChange
*
* This function clear the bit position of portChange.
*
* Input: hci = data structure for the host controller
* bitPos = the bit position
*
* Return value : none
*
*****************************************************************/
static
void
clrPortChange
(
hci_t
*
hci
,
__u16
bitPos
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
switch
(
bitPos
)
{
case
PORT_CONNECT_CHANGE
:
hp
->
RHportStatus
->
portChange
&=
~
bitPos
;
break
;
case
PORT_ENABLE_CHANGE
:
hp
->
RHportStatus
->
portChange
&=
~
bitPos
;
break
;
case
PORT_RESET_CHANGE
:
hp
->
RHportStatus
->
portChange
&=
~
bitPos
;
break
;
case
PORT_SUSPEND_CHANGE
:
hp
->
RHportStatus
->
portChange
&=
~
bitPos
;
break
;
case
PORT_OVER_CURRENT_CHANGE
:
hp
->
RHportStatus
->
portChange
&=
~
bitPos
;
break
;
}
}
/*****************************************************************
*
* Function Name: clrPortStatus
*
* This function clear the bit position of portStatus.
*
* Input: hci = data structure for the host controller
* bitPos = the bit position
*
* Return value : none
*
*****************************************************************/
static
void
clrPortStatus
(
hci_t
*
hci
,
__u16
bitPos
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
switch
(
bitPos
)
{
case
PORT_ENABLE_STAT
:
hp
->
RHportStatus
->
portStatus
&=
~
bitPos
;
break
;
case
PORT_RESET_STAT
:
hp
->
RHportStatus
->
portStatus
&=
~
bitPos
;
break
;
case
PORT_POWER_STAT
:
hp
->
RHportStatus
->
portStatus
&=
~
bitPos
;
break
;
case
PORT_SUSPEND_STAT
:
hp
->
RHportStatus
->
portStatus
&=
~
bitPos
;
break
;
}
}
/*****************************************************************
*
* Function Name: setPortStatus
*
* This function set the bit position of portStatus.
*
* Input: hci = data structure for the host controller
* bitPos = the bit position
*
* Return value : none
*
*****************************************************************/
static
void
setPortStatus
(
hci_t
*
hci
,
__u16
bitPos
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
switch
(
bitPos
)
{
case
PORT_ENABLE_STAT
:
hp
->
RHportStatus
->
portStatus
|=
bitPos
;
break
;
case
PORT_RESET_STAT
:
hp
->
RHportStatus
->
portStatus
|=
bitPos
;
break
;
case
PORT_POWER_STAT
:
hp
->
RHportStatus
->
portStatus
|=
bitPos
;
break
;
case
PORT_SUSPEND_STAT
:
hp
->
RHportStatus
->
portStatus
|=
bitPos
;
break
;
}
}
/*****************************************************************
*
* Function Name: hc_start
*
* This function starts the root hub functionality.
*
* Input: hci = data structure for the host controller
*
* Return value : 0
*
*****************************************************************/
static
int
hc_start
(
hci_t
*
hci
)
{
DBGFUNC
(
"Enter hc_start
\n
"
);
rh_connect_rh
(
hci
);
return
0
;
}
/*****************************************************************
*
* Function Name: hc_alloc_hci
*
* This function allocates all data structure and store in the
* private data structure.
*
* Input: hci = data structure for the host controller
*
* Return value : 0
*
*****************************************************************/
static
hci_t
*
__devinit
hc_alloc_hci
(
void
)
{
hci_t
*
hci
;
hcipriv_t
*
hp
;
portstat_t
*
ps
;
struct
usb_bus
*
bus
;
DBGFUNC
(
"Enter hc_alloc_hci
\n
"
);
hci
=
(
hci_t
*
)
kmalloc
(
sizeof
(
hci_t
),
GFP_KERNEL
);
if
(
!
hci
)
return
NULL
;
memset
(
hci
,
0
,
sizeof
(
hci_t
));
hp
=
&
hci
->
hp
;
hp
->
irq
=
-
1
;
hp
->
hcport
=
-
1
;
/* setup root hub port status */
ps
=
(
portstat_t
*
)
kmalloc
(
sizeof
(
portstat_t
),
GFP_KERNEL
);
if
(
!
ps
)
return
NULL
;
ps
->
portStatus
=
PORT_STAT_DEFAULT
;
ps
->
portChange
=
PORT_CHANGE_DEFAULT
;
hp
->
RHportStatus
=
ps
;
hci
->
nakCnt
=
0
;
hci
->
last_packet_nak
=
0
;
hci
->
a_td_array
.
len
=
0
;
hci
->
i_td_array
[
0
].
len
=
0
;
hci
->
i_td_array
[
1
].
len
=
0
;
hci
->
td_array
=
&
hci
->
a_td_array
;
hci
->
active_urbs
=
0
;
hci
->
active_trans
=
0
;
INIT_LIST_HEAD
(
&
hci
->
hci_hcd_list
);
list_add
(
&
hci
->
hci_hcd_list
,
&
hci_hcd_list
);
init_waitqueue_head
(
&
hci
->
waitq
);
INIT_LIST_HEAD
(
&
hci
->
ctrl_list
);
INIT_LIST_HEAD
(
&
hci
->
bulk_list
);
INIT_LIST_HEAD
(
&
hci
->
iso_list
);
INIT_LIST_HEAD
(
&
hci
->
intr_list
);
INIT_LIST_HEAD
(
&
hci
->
del_list
);
bus
=
usb_alloc_bus
(
&
hci_device_operations
);
if
(
!
bus
)
{
kfree
(
hci
);
kfree
(
ps
);
return
NULL
;
}
hci
->
bus
=
bus
;
bus
->
hcpriv
=
(
void
*
)
hci
;
return
hci
;
}
/*****************************************************************
*
* Function Name: hc_release_hci
*
* This function De-allocate all resources
*
* Input: hci = data structure for the host controller
*
* Return value : 0
*
*****************************************************************/
static
void
hc_release_hci
(
hci_t
*
hci
)
{
hcipriv_t
*
hp
=
&
hci
->
hp
;
DBGFUNC
(
"Enter hc_release_hci
\n
"
);
/* disconnect all devices */
if
(
hci
->
bus
->
root_hub
)
usb_disconnect
(
&
hci
->
bus
->
root_hub
);
hc_reset
(
hci
);
if
(
hp
->
tl
)
kfree
(
hp
->
tl
);
if
(
hp
->
hcport
>
0
)
{
release_region
(
hp
->
hcport
,
2
);
hp
->
hcport
=
0
;
}
if
(
hp
->
irq
>=
0
)
{
free_irq
(
hp
->
irq
,
hci
);
hp
->
irq
=
-
1
;
}
usb_deregister_bus
(
hci
->
bus
);
usb_put_bus
(
hci
->
bus
);
list_del_init
(
&
hci
->
hci_hcd_list
);
kfree
(
hci
);
}
/*****************************************************************
*
* Function Name: init_irq
*
* This function is board specific. It sets up the interrupt to
* be an edge trigger and trigger on the rising edge
*
* Input: none
*
* Return value : none
*
*****************************************************************/
void
init_irq
(
void
)
{
GPDR
&=
~
(
1
<<
13
);
set_GPIO_IRQ_edge
(
1
<<
13
,
GPIO_RISING_EDGE
);
}
/*****************************************************************
*
* Function Name: hc_found_hci
*
* This function request IO memory regions, request IRQ, and
* allocate all other resources.
*
* Input: addr = first IO address
* addr2 = second IO address
* irq = interrupt number
*
* Return: 0 = success or error condition
*
*****************************************************************/
static
int
__devinit
hc_found_hci
(
int
addr
,
int
addr2
,
int
irq
)
{
hci_t
*
hci
;
hcipriv_t
*
hp
;
DBGFUNC
(
"Enter hc_found_hci
\n
"
);
hci
=
hc_alloc_hci
();
if
(
!
hci
)
{
return
-
ENOMEM
;
}
init_irq
();
hp
=
&
hci
->
hp
;
if
(
!
request_region
(
addr
,
256
,
"SL811 USB HOST"
))
{
DBGERR
(
"request address %d failed"
,
addr
);
hc_release_hci
(
hci
);
return
-
EBUSY
;
}
hp
->
hcport
=
addr
;
if
(
!
hp
->
hcport
)
{
DBGERR
(
"Error mapping SL811 Memory 0x%x"
,
hp
->
hcport
);
}
if
(
!
request_region
(
addr2
,
256
,
"SL811 USB HOST"
))
{
DBGERR
(
"request address %d failed"
,
addr2
);
hc_release_hci
(
hci
);
return
-
EBUSY
;
}
hp
->
hcport2
=
addr2
;
if
(
!
hp
->
hcport2
)
{
DBGERR
(
"Error mapping SL811 Memory 0x%x"
,
hp
->
hcport2
);
}
if
(
hc_alloc_trans_buffer
(
hci
))
{
hc_release_hci
(
hci
);
return
-
ENOMEM
;
}
usb_register_bus
(
hci
->
bus
);
if
(
request_irq
(
irq
,
hc_interrupt
,
0
,
"SL811"
,
hci
)
!=
0
)
{
DBGERR
(
"request interrupt %d failed"
,
irq
);
hc_release_hci
(
hci
);
return
-
EBUSY
;
}
hp
->
irq
=
irq
;
printk
(
KERN_INFO
__FILE__
": USB SL811 at %x, addr2 = %x, IRQ %d
\n
"
,
addr
,
addr2
,
irq
);
hc_reset
(
hci
);
if
(
hc_start
(
hci
)
<
0
)
{
DBGERR
(
"can't start usb-%x"
,
addr
);
hc_release_hci
(
hci
);
return
-
EBUSY
;
}
return
0
;
}
/*****************************************************************
*
* Function Name: hci_hcd_init
*
* This is an init function, and it is the first function being called
*
* Input: none
*
* Return: 0 = success or error condition
*
*****************************************************************/
static
int
__init
hci_hcd_init
(
void
)
{
int
ret
;
DBGFUNC
(
"Enter hci_hcd_init
\n
"
);
if
(
usb_disabled
())
return
-
ENODEV
;
ret
=
hc_found_hci
(
base_addr
,
data_reg_addr
,
irq
);
return
ret
;
}
/*****************************************************************
*
* Function Name: hci_hcd_cleanup
*
* This is a cleanup function, and it is called when module is
* unloaded.
*
* Input: none
*
* Return: none
*
*****************************************************************/
static
void
__exit
hci_hcd_cleanup
(
void
)
{
hci_t
*
hci
,
*
tmp
;
DBGFUNC
(
"Enter hci_hcd_cleanup
\n
"
);
list_for_each_entry_safe
(
hci
,
tmp
,
&
hci_hcd_list
,
hci_hcd_list
)
hc_release_hci
(
hci
);
}
module_init
(
hci_hcd_init
);
module_exit
(
hci_hcd_cleanup
);
MODULE_AUTHOR
(
"Pei Liu <pbl@cypress.com>"
);
MODULE_DESCRIPTION
(
"USB SL811HS Host Controller Driver"
);
drivers/usb/host/hc_sl811.h
deleted
100644 → 0
View file @
d5acaac4
/*
* SL811HS HCD (Host Controller Driver) for USB.
*
* COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC
*
*
*/
#define GET_FRAME_NUMBER(hci) READ_REG32 (hci, HcFmNumber)
/*
* Maximum number of root hub ports
*/
#define MAX_ROOT_PORTS 15
/* maximum OHCI root hub ports */
/* control and status registers */
#define HcRevision 0x00
#define HcControl 0x01
#define HcCommandStatus 0x02
#define HcInterruptStatus 0x03
#define HcInterruptEnable 0x04
#define HcInterruptDisable 0x05
#define HcFmInterval 0x0D
#define HcFmRemaining 0x0E
#define HcFmNumber 0x0F
#define HcLSThreshold 0x11
#define HcRhDescriptorA 0x12
#define HcRhDescriptorB 0x13
#define HcRhStatus 0x14
#define HcRhPortStatus 0x15
#define HcHardwareConfiguration 0x20
#define HcDMAConfiguration 0x21
#define HcTransferCounter 0x22
#define HcuPInterrupt 0x24
#define HcuPInterruptEnable 0x25
#define HcChipID 0x27
#define HcScratch 0x28
#define HcSoftwareReset 0x29
#define HcITLBufferLength 0x2A
#define HcATLBufferLength 0x2B
#define HcBufferStatus 0x2C
#define HcReadBackITL0Length 0x2D
#define HcReadBackITL1Length 0x2E
#define HcITLBufferPort 0x40
#define HcATLBufferPort 0x41
/* OHCI CONTROL AND STATUS REGISTER MASKS */
/*
* HcControl (control) register masks
*/
#define OHCI_CTRL_HCFS (3 << 6)
/* BUS state mask */
#define OHCI_CTRL_RWC (1 << 9)
/* remote wakeup connected */
#define OHCI_CTRL_RWE (1 << 10)
/* remote wakeup enable */
/* pre-shifted values for HCFS */
#define OHCI_USB_RESET (0 << 6)
#define OHCI_USB_RESUME (1 << 6)
#define OHCI_USB_OPER (2 << 6)
#define OHCI_USB_SUSPEND (3 << 6)
/*
* HcCommandStatus (cmdstatus) register masks
*/
#define OHCI_HCR (1 << 0)
/* host controller reset */
#define OHCI_SO (3 << 16)
/* scheduling overrun count */
/*
* masks used with interrupt registers:
* HcInterruptStatus (intrstatus)
* HcInterruptEnable (intrenable)
* HcInterruptDisable (intrdisable)
*/
#define OHCI_INTR_SO (1 << 0)
/* scheduling overrun */
#define OHCI_INTR_SF (1 << 2)
/* start frame */
#define OHCI_INTR_RD (1 << 3)
/* resume detect */
#define OHCI_INTR_UE (1 << 4)
/* unrecoverable error */
#define OHCI_INTR_FNO (1 << 5)
/* frame number overflow */
#define OHCI_INTR_RHSC (1 << 6)
/* root hub status change */
#define OHCI_INTR_ATD (1 << 7)
/* scheduling overrun */
#define OHCI_INTR_MIE (1 << 31)
/* master interrupt enable */
/*
* HcHardwareConfiguration
*/
#define InterruptPinEnable (1 << 0)
#define InterruptPinTrigger (1 << 1)
#define InterruptOutputPolarity (1 << 2)
#define DataBusWidth16 (1 << 3)
#define DREQOutputPolarity (1 << 5)
#define DACKInputPolarity (1 << 6)
#define EOTInputPolarity (1 << 7)
#define DACKMode (1 << 8)
#define AnalogOCEnable (1 << 10)
#define SuspendClkNotStop (1 << 11)
#define DownstreamPort15KRSel (1 << 12)
/*
* HcDMAConfiguration
*/
#define DMAReadWriteSelect (1 << 0)
#define ITL_ATL_DataSelect (1 << 1)
#define DMACounterSelect (1 << 2)
#define DMAEnable (1 << 4)
#define BurstLen_1 0
#define BurstLen_4 (1 << 5)
#define BurstLen_8 (2 << 5)
/*
* HcuPInterrupt
*/
#define SOFITLInt (1 << 0)
#define ATLInt (1 << 1)
#define AllEOTInterrupt (1 << 2)
#define OPR_Reg (1 << 4)
#define HCSuspended (1 << 5)
#define ClkReady (1 << 6)
/*
* HcBufferStatus
*/
#define ITL0BufferFull (1 << 0)
#define ITL1BufferFull (1 << 1)
#define ATLBufferFull (1 << 2)
#define ITL0BufferDone (1 << 3)
#define ITL1BufferDone (1 << 4)
#define ATLBufferDone (1 << 5)
/* OHCI ROOT HUB REGISTER MASKS */
/* roothub.portstatus [i] bits */
#define RH_PS_CCS 0x00000001
/* current connect status */
#define RH_PS_PES 0x00000002
/* port enable status */
#define RH_PS_PSS 0x00000004
/* port suspend status */
#define RH_PS_POCI 0x00000008
/* port over current indicator */
#define RH_PS_PRS 0x00000010
/* port reset status */
#define RH_PS_PPS 0x00000100
/* port power status */
#define RH_PS_LSDA 0x00000200
/* low speed device attached */
#define RH_PS_CSC 0x00010000
/* connect status change */
#define RH_PS_PESC 0x00020000
/* port enable status change */
#define RH_PS_PSSC 0x00040000
/* port suspend status change */
#define RH_PS_OCIC 0x00080000
/* over current indicator change */
#define RH_PS_PRSC 0x00100000
/* port reset status change */
/* roothub.status bits */
#define RH_HS_LPS 0x00000001
/* local power status */
#define RH_HS_OCI 0x00000002
/* over current indicator */
#define RH_HS_DRWE 0x00008000
/* device remote wakeup enable */
#define RH_HS_LPSC 0x00010000
/* local power status change */
#define RH_HS_OCIC 0x00020000
/* over current indicator change */
#define RH_HS_CRWE 0x80000000
/* clear remote wakeup enable */
/* roothub.b masks */
#define RH_B_DR 0x0000ffff
/* device removable flags */
#define RH_B_PPCM 0xffff0000
/* port power control mask */
/* roothub.a masks */
#define RH_A_NDP (0xff << 0)
/* number of downstream ports */
#define RH_A_PSM (1 << 8)
/* power switching mode */
#define RH_A_NPS (1 << 9)
/* no power switching */
#define RH_A_DT (1 << 10)
/* device type (mbz) */
#define RH_A_OCPM (1 << 11)
/* over current protection mode */
#define RH_A_NOCP (1 << 12)
/* no over current protection */
#define RH_A_POTPGT (0xff << 24)
/* power on to power good time */
#define URB_DEL 1
#define PORT_STAT_DEFAULT 0x0100
#define PORT_CONNECT_STAT 0x1
#define PORT_ENABLE_STAT 0x2
#define PORT_SUSPEND_STAT 0x4
#define PORT_OVER_CURRENT_STAT 0x8
#define PORT_RESET_STAT 0x10
#define PORT_POWER_STAT 0x100
#define PORT_LOW_SPEED_DEV_ATTACH_STAT 0x200
#define PORT_CHANGE_DEFAULT 0x0
#define PORT_CONNECT_CHANGE 0x1
#define PORT_ENABLE_CHANGE 0x2
#define PORT_SUSPEND_CHANGE 0x4
#define PORT_OVER_CURRENT_CHANGE 0x8
#define PORT_RESET_CHANGE 0x10
/* Port Status Request info */
typedef
struct
portstat
{
__u16
portChange
;
__u16
portStatus
;
}
portstat_t
;
typedef
struct
hcipriv
{
int
irq
;
int
disabled
;
/* e.g. got a UE, we're hung */
atomic_t
resume_count
;
/* defending against multiple resumes */
struct
ohci_regs
*
regs
;
/* OHCI controller's memory */
int
hcport
;
/* I/O base address */
int
hcport2
;
/* I/O data reg addr */
struct
portstat
*
RHportStatus
;
/* root hub port status */
int
intrstatus
;
__u32
hc_control
;
/* copy of the hc control reg */
int
frame
;
__u8
*
tl
;
int
xferPktLen
;
int
atl_len
;
int
atl_buffer_len
;
int
itl0_len
;
int
itl1_len
;
int
itl_buffer_len
;
int
itl_index
;
int
tl_last
;
int
units_left
;
}
hcipriv_t
;
struct
hci
;
#define cClt 0 // Control
#define cISO 1 // ISO
#define cBULK 2 // BULK
#define cInt 3 // Interrupt
#define ISO_BIT 0x10
/*-------------------------------------------------------------------------
* EP0 use for configuration and Vendor Specific command interface
*------------------------------------------------------------------------*/
#define cMemStart 0x10
#define EP0Buf 0x40
/* SL11H/SL811H memory start at 0x40 */
#define EP0Len 0x40
/* Length of config buffer EP0Buf */
#define EP1Buf 0x60
#define EP1Len 0x40
/*-------------------------------------------------------------------------
* SL11H/SL811H memory from 80h-ffh use as ping-pong buffer.
*------------------------------------------------------------------------*/
#define uBufA 0x80
/* buffer A address for DATA0 */
#define uBufB 0xc0
/* buffer B address for DATA1 */
#define uXferLen 0x40
/* xfer length */
#define sMemSize 0xc0
/* Total SL11 memory size */
#define cMemEnd 256
/*-------------------------------------------------------------------------
* SL811H Register Control memory map
* --Note:
* --SL11H only has one control register set from 0x00-0x04
* --SL811H has two control register set from 0x00-0x04 and 0x08-0x0c
*------------------------------------------------------------------------*/
#define EP0Control 0x00
#define EP0Address 0x01
#define EP0XferLen 0x02
#define EP0Status 0x03
#define EP0Counter 0x04
#define EP1Control 0x08
#define EP1Address 0x09
#define EP1XferLen 0x0a
#define EP1Status 0x0b
#define EP1Counter 0x0c
#define CtrlReg 0x05
#define IntEna 0x06
// 0x07 is reserved
#define IntStatus 0x0d
#define cDATASet 0x0e
#define cSOFcnt 0x0f
#define IntMask 0x57
/* Reset|DMA|EP0|EP2|EP1 for IntEna */
#define HostMask 0x47
/* Host request command for IntStatus */
#define ReadMask 0xd7
/* Read mask interrupt for IntStatus */
/*-------------------------------------------------------------------------
* Standard Chapter 9 definition
*-------------------------------------------------------------------------
*/
#define GET_STATUS 0x00
#define CLEAR_FEATURE 0x01
#define SET_FEATURE 0x03
#define SET_ADDRESS 0x05
#define GET_DESCRIPTOR 0x06
#define SET_DESCRIPTOR 0x07
#define GET_CONFIG 0x08
#define SET_CONFIG 0x09
#define GET_INTERFACE 0x0a
#define SET_INTERFACE 0x0b
#define SYNCH_FRAME 0x0c
#define DEVICE 0x01
#define CONFIGURATION 0x02
#define STRING 0x03
#define INTERFACE 0x04
#define ENDPOINT 0x05
/*-------------------------------------------------------------------------
* SL11H/SL811H definition
*-------------------------------------------------------------------------
*/
#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0)
#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1)
#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0)
#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1)
#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0)
#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1)
#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition
#define PID_SOF 0xA5
#define PID_IN 0x69
#define PID_OUT 0xe1
#define MAX_RETRY 0xffff
#define TIMEOUT 5
/* 2 mseconds */
#define SL11H_HOSTCTLREG 0
#define SL11H_BUFADDRREG 1
#define SL11H_BUFLNTHREG 2
#define SL11H_PKTSTATREG 3
/* read */
#define SL11H_PIDEPREG 3
/* write */
#define SL11H_XFERCNTREG 4
/* read */
#define SL11H_DEVADDRREG 4
/* write */
#define SL11H_CTLREG1 5
#define SL11H_INTENBLREG 6
#define SL11H_HOSTCTLREG_B 8
#define SL11H_BUFADDRREG_B 9
#define SL11H_BUFLNTHREG_B 0x0A
#define SL11H_PKTSTATREG_B 0x0B
/* read */
#define SL11H_PIDEPREG_B 0x0B
/* write */
#define SL11H_XFERCNTREG_B 0x0C
/* read */
#define SL11H_DEVADDRREG_B 0x0C
/* write */
#define SL11H_INTSTATREG 0x0D
/* write clears bitwise */
#define SL11H_HWREVREG 0x0E
/* read */
#define SL11H_SOFLOWREG 0x0E
/* write */
#define SL11H_SOFTMRREG 0x0F
/* read */
#define SL11H_CTLREG2 0x0F
/* write */
#define SL11H_DATA_START 0x10
/* Host control register bits (addr 0) */
#define SL11H_HCTLMASK_ARM 1
#define SL11H_HCTLMASK_ENBLEP 2
#define SL11H_HCTLMASK_WRITE 4
#define SL11H_HCTLMASK_ISOCH 0x10
#define SL11H_HCTLMASK_AFTERSOF 0x20
#define SL11H_HCTLMASK_SEQ 0x40
#define SL11H_HCTLMASK_PREAMBLE 0x80
/* Packet status register bits (addr 3) */
#define SL11H_STATMASK_ACK 1
#define SL11H_STATMASK_ERROR 2
#define SL11H_STATMASK_TMOUT 4
#define SL11H_STATMASK_SEQ 8
#define SL11H_STATMASK_SETUP 0x10
#define SL11H_STATMASK_OVF 0x20
#define SL11H_STATMASK_NAK 0x40
#define SL11H_STATMASK_STALL 0x80
/* Control register 1 bits (addr 5) */
#define SL11H_CTL1MASK_DSBLSOF 1
#define SL11H_CTL1MASK_NOTXEOF2 4
#define SL11H_CTL1MASK_DSTATE 0x18
#define SL11H_CTL1MASK_NSPD 0x20
#define SL11H_CTL1MASK_SUSPEND 0x40
#define SL11H_CTL1MASK_CLK12 0x80
#define SL11H_CTL1VAL_RESET 8
/* Interrupt enable (addr 6) and interrupt status register bits (addr 0xD) */
#define SL11H_INTMASK_XFERDONE 1
#define SL11H_INTMASK_SOFINTR 0x10
#define SL11H_INTMASK_INSRMV 0x20
#define SL11H_INTMASK_USBRESET 0x40
#define SL11H_INTMASK_DSTATE 0x80
/* only in status reg */
/* HW rev and SOF lo register bits (addr 0xE) */
#define SL11H_HWRMASK_HWREV 0xF0
/* SOF counter and control reg 2 (addr 0xF) */
#define SL11H_CTL2MASK_SOFHI 0x3F
#define SL11H_CTL2MASK_DSWAP 0x40
#define SL11H_CTL2MASK_HOSTMODE 0xae
drivers/usb/host/hc_sl811_rh.c
deleted
100644 → 0
View file @
d5acaac4
/*-------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------*
* SL811HS virtual root hub
*
* based on usb-ohci.c by R. Weissgaerber et al.
*-------------------------------------------------------------------------*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*-------------------------------------------------------------------------*/
/* FIXME: reuse the root hub framework in usbcore, shrinking this code. */
#ifdef DEBUG
#undef DEBUG
#endif
static
__u32
getPortStatusAndChange
(
hci_t
*
hci
);
static
void
setPortStatus
(
hci_t
*
hci
,
__u16
bitPos
);
static
void
setPortChange
(
hci_t
*
hci
,
__u16
bitPos
);
static
void
clrPortStatus
(
hci_t
*
hci
,
__u16
bitPos
);
static
void
clrPortChange
(
hci_t
*
hci
,
__u16
bitPos
);
static
int
USBReset
(
hci_t
*
hci
);
static
int
cc_to_error
(
int
cc
);
/*-------------------------------------------------------------------------*
* Virtual Root Hub
*-------------------------------------------------------------------------*/
/* Device descriptor */
static
__u8
root_hub_dev_des
[]
=
{
0x12
,
/* __u8 bLength; */
0x01
,
/* __u8 bDescriptorType; Device */
0x10
,
/* __u16 bcdUSB; v1.1 */
0x01
,
0x09
,
/* __u8 bDeviceClass; HUB_CLASSCODE */
0x00
,
/* __u8 bDeviceSubClass; */
0x00
,
/* __u8 bDeviceProtocol; */
0x08
,
/* __u8 bMaxPacketSize0; 8 Bytes */
0x00
,
/* __u16 idVendor; */
0x00
,
0x00
,
/* __u16 idProduct; */
0x00
,
0x00
,
/* __u16 bcdDevice; */
0x00
,
0x00
,
/* __u8 iManufacturer; */
0x02
,
/* __u8 iProduct; */
0x01
,
/* __u8 iSerialNumber; */
0x01
/* __u8 bNumConfigurations; */
};
/* Configuration descriptor */
static
__u8
root_hub_config_des
[]
=
{
0x09
,
/* __u8 bLength; */
0x02
,
/* __u8 bDescriptorType; Configuration */
0x19
,
/* __u16 wTotalLength; */
0x00
,
0x01
,
/* __u8 bNumInterfaces; */
0x01
,
/* __u8 bConfigurationValue; */
0x00
,
/* __u8 iConfiguration; */
0x40
,
/* __u8 bmAttributes;
Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup,
4..0: resvd */
0x00
,
/* __u8 MaxPower; */
/* interface */
0x09
,
/* __u8 if_bLength; */
0x04
,
/* __u8 if_bDescriptorType; Interface */
0x00
,
/* __u8 if_bInterfaceNumber; */
0x00
,
/* __u8 if_bAlternateSetting; */
0x01
,
/* __u8 if_bNumEndpoints; */
0x09
,
/* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00
,
/* __u8 if_bInterfaceSubClass; */
0x00
,
/* __u8 if_bInterfaceProtocol; */
0x00
,
/* __u8 if_iInterface; */
/* endpoint */
0x07
,
/* __u8 ep_bLength; */
0x05
,
/* __u8 ep_bDescriptorType; Endpoint */
0x81
,
/* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03
,
/* __u8 ep_bmAttributes; Interrupt */
0x02
,
/* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */
0x00
,
0xff
/* __u8 ep_bInterval; 255 ms */
};
/* Hub class-specific descriptor is constructed dynamically */
/***************************************************************************
* Function Name : rh_send_irq
*
* This function examine the port change in the virtual root hub.
*
* Note: This function assumes only one port exist in the root hub.
*
* Input: hci = data structure for the host controller
* rh_data = The pointer to port change data
* rh_len = length of the data in bytes
*
* Return: length of data
**************************************************************************/
static
int
rh_send_irq
(
hci_t
*
hci
,
void
*
rh_data
,
int
rh_len
)
{
int
num_ports
;
int
i
;
int
ret
;
int
len
;
__u8
data
[
8
];
DBGFUNC
(
"enter rh_send_irq:
\n
"
);
/* Assuming the root hub has one port. This value need to change if
* there are more than one port for the root hub
*/
num_ports
=
1
;
/* The root hub status is not implemented, it basically has two fields:
* -- Local Power Status
* -- Over Current Indicator
* -- Local Power Change
* -- Over Current Indicator
*
* Right now, It is assume the power is good and no changes
*/
*
(
__u8
*
)
data
=
0
;
ret
=
*
(
__u8
*
)
data
;
/* Has the port status change within the root hub: It checks for
* -- Port Connect Status change
* -- Port Enable Change
*/
for
(
i
=
0
;
i
<
num_ports
;
i
++
)
{
*
(
__u8
*
)
(
data
+
(
i
+
1
)
/
8
)
|=
(((
getPortStatusAndChange
(
hci
)
>>
16
)
&
(
PORT_CONNECT_STAT
|
PORT_ENABLE_STAT
))
?
1
:
0
)
<<
((
i
+
1
)
%
8
);
ret
+=
*
(
__u8
*
)
(
data
+
(
i
+
1
)
/
8
);
/* After the port change is read, it should be reset so the next time
* is it doesn't trigger a change again */
}
len
=
i
/
8
+
1
;
if
(
ret
>
0
)
{
memcpy
(
rh_data
,
data
,
min
(
len
,
min
(
rh_len
,
(
int
)
sizeof
(
data
))));
return
len
;
}
return
0
;
}
/***************************************************************************
* Function Name : rh_int_timer_do
*
* This function is called when the timer expires. It gets the the port
* change data and pass along to the upper protocol.
*
* Note: The virtual root hub interrupt pipe are polled by the timer
* every "interval" ms
*
* Input: ptr = ptr to the urb
*
* Return: none
**************************************************************************/
static
void
rh_int_timer_do
(
unsigned
long
ptr
)
{
int
len
;
struct
urb
*
urb
=
(
struct
urb
*
)
ptr
;
hci_t
*
hci
=
urb
->
dev
->
bus
->
hcpriv
;
DBGFUNC
(
"enter rh_int_timer_do
\n
"
);
if
(
hci
->
rh
.
send
)
{
len
=
rh_send_irq
(
hci
,
urb
->
transfer_buffer
,
urb
->
transfer_buffer_length
);
if
(
len
>
0
)
{
urb
->
actual_length
=
len
;
if
(
urb_debug
==
2
)
urb_print
(
urb
,
"RET-t(rh)"
,
usb_pipeout
(
urb
->
pipe
));
if
(
urb
->
complete
)
{
urb
->
complete
(
urb
,
NULL
);
}
}
}
/* re-activate the timer */
rh_init_int_timer
(
urb
);
}
/***************************************************************************
* Function Name : rh_init_int_timer
*
* This function creates a timer that act as interrupt pipe in the
* virtual hub.
*
* Note: The virtual root hub's interrupt pipe are polled by the timer
* every "interval" ms
*
* Input: urb = USB request block
*
* Return: 0
**************************************************************************/
static
int
rh_init_int_timer
(
struct
urb
*
urb
)
{
hci_t
*
hci
=
urb
->
dev
->
bus
->
hcpriv
;
hci
->
rh
.
interval
=
urb
->
interval
;
init_timer
(
&
hci
->
rh
.
rh_int_timer
);
hci
->
rh
.
rh_int_timer
.
function
=
rh_int_timer_do
;
hci
->
rh
.
rh_int_timer
.
data
=
(
unsigned
long
)
urb
;
hci
->
rh
.
rh_int_timer
.
expires
=
jiffies
+
(
HZ
*
(
urb
->
interval
<
30
?
30
:
urb
->
interval
))
/
1000
;
add_timer
(
&
hci
->
rh
.
rh_int_timer
);
return
0
;
}
/*-------------------------------------------------------------------------*/
/* for returning string descriptors in UTF-16LE */
static
int
ascii2utf
(
char
*
ascii
,
__u8
*
utf
,
int
utfmax
)
{
int
retval
;
for
(
retval
=
0
;
*
ascii
&&
utfmax
>
1
;
utfmax
-=
2
,
retval
+=
2
)
{
*
utf
++
=
*
ascii
++
&
0x7f
;
*
utf
++
=
0
;
}
return
retval
;
}
static
int
root_hub_string
(
int
id
,
int
serial
,
char
*
type
,
__u8
*
data
,
int
len
)
{
char
buf
[
30
];
// assert (len > (2 * (sizeof (buf) + 1)));
// assert (strlen (type) <= 8);
// language ids
if
(
id
==
0
)
{
*
data
++
=
4
;
*
data
++
=
3
;
/* 4 bytes data */
*
data
++
=
0
;
*
data
++
=
0
;
/* some language id */
return
4
;
// serial number
}
else
if
(
id
==
1
)
{
sprintf
(
buf
,
"%x"
,
serial
);
// product description
}
else
if
(
id
==
2
)
{
sprintf
(
buf
,
"USB %s Root Hub"
,
type
);
// id 3 == vendor description
// unsupported IDs --> "stall"
}
else
return
0
;
data
[
0
]
=
2
+
ascii2utf
(
buf
,
data
+
2
,
len
-
2
);
data
[
1
]
=
3
;
return
data
[
0
];
}
/*-------------------------------------------------------------------------*/
/* helper macro */
#define OK(x) len = (x); break
/***************************************************************************
* Function Name : rh_submit_urb
*
* This function handles all USB request to the the virtual root hub
*
* Input: urb = USB request block
*
* Return: 0
**************************************************************************/
static
int
rh_submit_urb
(
struct
urb
*
urb
)
{
struct
usb_device
*
usb_dev
=
urb
->
dev
;
hci_t
*
hci
=
usb_dev
->
bus
->
hcpriv
;
unsigned
int
pipe
=
urb
->
pipe
;
struct
usb_ctrlrequest
*
cmd
=
(
struct
usb_ctrlrequest
*
)
urb
->
setup_packet
;
void
*
data
=
urb
->
transfer_buffer
;
int
leni
=
urb
->
transfer_buffer_length
;
int
len
=
0
;
int
status
=
TD_CC_NOERROR
;
__u32
datab
[
4
];
__u8
*
data_buf
=
(
__u8
*
)
datab
;
__u16
bmRType_bReq
;
__u16
wValue
;
__u16
wIndex
;
__u16
wLength
;
DBGFUNC
(
"enter rh_submit_urb
\n
"
);
if
(
usb_pipeint
(
pipe
))
{
hci
->
rh
.
urb
=
urb
;
hci
->
rh
.
send
=
1
;
hci
->
rh
.
interval
=
urb
->
interval
;
rh_init_int_timer
(
urb
);
urb
->
status
=
cc_to_error
(
TD_CC_NOERROR
);
return
0
;
}
bmRType_bReq
=
cmd
->
bRequestType
|
(
cmd
->
bRequest
<<
8
);
wValue
=
le16_to_cpu
(
cmd
->
wValue
);
wIndex
=
le16_to_cpu
(
cmd
->
wIndex
);
wLength
=
le16_to_cpu
(
cmd
->
wLength
);
DBG
(
"rh_submit_urb, req = %d(%x) len=%d"
,
bmRType_bReq
,
bmRType_bReq
,
wLength
);
switch
(
bmRType_bReq
)
{
/* Request Destination:
without flags: Device,
RH_INTERFACE: interface,
RH_ENDPOINT: endpoint,
RH_CLASS means HUB here,
RH_OTHER | RH_CLASS almost ever means HUB_PORT here
*/
case
RH_GET_STATUS
:
*
(
__u16
*
)
data_buf
=
cpu_to_le16
(
1
);
OK
(
2
);
case
RH_GET_STATUS
|
RH_INTERFACE
:
*
(
__u16
*
)
data_buf
=
cpu_to_le16
(
0
);
OK
(
2
);
case
RH_GET_STATUS
|
RH_ENDPOINT
:
*
(
__u16
*
)
data_buf
=
cpu_to_le16
(
0
);
OK
(
2
);
case
RH_GET_STATUS
|
RH_CLASS
:
*
(
__u32
*
)
data_buf
=
cpu_to_le32
(
0
);
OK
(
4
);
case
RH_GET_STATUS
|
RH_OTHER
|
RH_CLASS
:
*
(
__u32
*
)
data_buf
=
cpu_to_le32
(
getPortStatusAndChange
(
hci
));
OK
(
4
);
case
RH_CLEAR_FEATURE
|
RH_ENDPOINT
:
switch
(
wValue
)
{
case
(
RH_ENDPOINT_STALL
):
OK
(
0
);
}
break
;
case
RH_CLEAR_FEATURE
|
RH_CLASS
:
switch
(
wValue
)
{
case
RH_C_HUB_LOCAL_POWER
:
OK
(
0
);
case
(
RH_C_HUB_OVER_CURRENT
):
/* Over Current Not Implemented */
OK
(
0
);
}
break
;
case
RH_CLEAR_FEATURE
|
RH_OTHER
|
RH_CLASS
:
switch
(
wValue
)
{
case
(
RH_PORT_ENABLE
):
clrPortStatus
(
hci
,
PORT_ENABLE_STAT
);
OK
(
0
);
case
(
RH_PORT_SUSPEND
):
clrPortStatus
(
hci
,
PORT_SUSPEND_STAT
);
OK
(
0
);
case
(
RH_PORT_POWER
):
clrPortStatus
(
hci
,
PORT_POWER_STAT
);
OK
(
0
);
case
(
RH_C_PORT_CONNECTION
):
clrPortChange
(
hci
,
PORT_CONNECT_STAT
);
OK
(
0
);
case
(
RH_C_PORT_ENABLE
):
clrPortChange
(
hci
,
PORT_ENABLE_STAT
);
OK
(
0
);
case
(
RH_C_PORT_SUSPEND
):
clrPortChange
(
hci
,
PORT_SUSPEND_STAT
);
OK
(
0
);
case
(
RH_C_PORT_OVER_CURRENT
):
clrPortChange
(
hci
,
PORT_OVER_CURRENT_STAT
);
OK
(
0
);
case
(
RH_C_PORT_RESET
):
clrPortChange
(
hci
,
PORT_RESET_STAT
);
OK
(
0
);
}
break
;
case
RH_SET_FEATURE
|
RH_OTHER
|
RH_CLASS
:
switch
(
wValue
)
{
case
(
RH_PORT_SUSPEND
):
setPortStatus
(
hci
,
PORT_SUSPEND_STAT
);
OK
(
0
);
case
(
RH_PORT_RESET
):
setPortStatus
(
hci
,
PORT_RESET_STAT
);
// USBReset(hci);
clrPortChange
(
hci
,
PORT_CONNECT_CHANGE
|
PORT_ENABLE_CHANGE
|
PORT_SUSPEND_CHANGE
|
PORT_OVER_CURRENT_CHANGE
);
setPortChange
(
hci
,
PORT_RESET_CHANGE
);
clrPortStatus
(
hci
,
PORT_RESET_STAT
);
setPortStatus
(
hci
,
PORT_ENABLE_STAT
);
OK
(
0
);
case
(
RH_PORT_POWER
):
setPortStatus
(
hci
,
PORT_POWER_STAT
);
OK
(
0
);
case
(
RH_PORT_ENABLE
):
setPortStatus
(
hci
,
PORT_ENABLE_STAT
);
OK
(
0
);
}
break
;
case
RH_SET_ADDRESS
:
hci
->
rh
.
devnum
=
wValue
;
OK
(
0
);
case
RH_GET_DESCRIPTOR
:
DBGVERBOSE
(
"rh_submit_urb: RH_GET_DESCRIPTOR, wValue = 0x%x
\n
"
,
wValue
);
switch
((
wValue
&
0xff00
)
>>
8
)
{
case
(
0x01
):
/* device descriptor */
len
=
min
(
leni
,
min
((
__u16
)
sizeof
(
root_hub_dev_des
),
wLength
));
data_buf
=
root_hub_dev_des
;
OK
(
len
);
case
(
0x02
):
/* configuration descriptor */
len
=
min
(
leni
,
min
((
__u16
)
sizeof
(
root_hub_config_des
),
wLength
));
data_buf
=
root_hub_config_des
;
OK
(
len
);
case
(
0x03
):
/* string descriptors */
len
=
root_hub_string
(
wValue
&
0xff
,
(
int
)
(
long
)
0
,
"SL811HS"
,
data
,
wLength
);
if
(
len
>
0
)
{
data_buf
=
data
;
OK
(
min
(
leni
,
len
));
}
default:
status
=
SL11H_STATMASK_STALL
;
}
break
;
case
RH_GET_DESCRIPTOR
|
RH_CLASS
:
data_buf
[
0
]
=
9
;
// min length;
data_buf
[
1
]
=
0x29
;
data_buf
[
2
]
=
1
;
// # of downstream port
data_buf
[
3
]
=
0
;
datab
[
1
]
=
0
;
data_buf
[
5
]
=
50
;
// 100 ms for port reset
data_buf
[
7
]
=
0xfc
;
// which port is attachable
if
(
data_buf
[
2
]
<
7
)
{
data_buf
[
8
]
=
0xff
;
}
else
{
}
len
=
min
(
leni
,
min
((
__u16
)
data_buf
[
0
],
wLength
));
OK
(
len
);
case
RH_GET_CONFIGURATION
:
*
(
__u8
*
)
data_buf
=
0x01
;
OK
(
1
);
case
RH_SET_CONFIGURATION
:
OK
(
0
);
default:
DBGERR
(
"unsupported root hub command"
);
status
=
SL11H_STATMASK_STALL
;
}
len
=
min
(
len
,
leni
);
if
(
data
!=
data_buf
)
memcpy
(
data
,
data_buf
,
len
);
urb
->
actual_length
=
len
;
urb
->
status
=
cc_to_error
(
status
);
urb
->
hcpriv
=
NULL
;
urb
->
dev
=
NULL
;
if
(
urb
->
complete
)
{
urb
->
complete
(
urb
,
NULL
);
}
return
0
;
}
/***************************************************************************
* Function Name : rh_unlink_urb
*
* This function unlinks the URB
*
* Input: urb = USB request block
*
* Return: 0
**************************************************************************/
static
int
rh_unlink_urb
(
struct
urb
*
urb
)
{
hci_t
*
hci
=
urb
->
dev
->
bus
->
hcpriv
;
DBGFUNC
(
"enter rh_unlink_urb
\n
"
);
if
(
hci
->
rh
.
urb
==
urb
)
{
hci
->
rh
.
send
=
0
;
del_timer
(
&
hci
->
rh
.
rh_int_timer
);
hci
->
rh
.
urb
=
NULL
;
urb
->
hcpriv
=
NULL
;
usb_put_dev
(
urb
->
dev
);
urb
->
dev
=
NULL
;
if
(
urb
->
transfer_flags
&
URB_ASYNC_UNLINK
)
{
urb
->
status
=
-
ECONNRESET
;
if
(
urb
->
complete
)
{
urb
->
complete
(
urb
,
NULL
);
}
}
else
urb
->
status
=
-
ENOENT
;
}
return
0
;
}
/***************************************************************************
* Function Name : rh_connect_rh
*
* This function connect the virtual root hub to the USB stack
*
* Input: urb = USB request block
*
* Return: 0
**************************************************************************/
static
int
rh_connect_rh
(
hci_t
*
hci
)
{
struct
usb_device
*
usb_dev
;
int
retval
;
hci
->
rh
.
devnum
=
0
;
usb_dev
=
usb_alloc_dev
(
NULL
,
hci
->
bus
,
0
);
if
(
!
usb_dev
)
return
-
ENOMEM
;
usb_dev
->
devnum
=
1
;
usb_dev
->
bus
->
devnum_next
=
usb_dev
->
devnum
+
1
;
set_bit
(
usb_dev
->
devnum
,
usb_dev
->
bus
->
devmap
.
devicemap
);
down
(
&
usb_bus_list_lock
);
hci
->
bus
->
root_hub
=
usb_dev
;
retval
=
usb_new_device
(
usb_dev
);
if
(
retval
!=
0
)
hci
->
bus
->
root_hub
=
NULL
;
up
(
&
usb_bus_list_lock
);
if
(
retval
!=
0
)
{
usb_put_dev
(
usb_dev
);
return
-
ENODEV
;
}
return
0
;
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment