2008-09-17 16:34:29 +01:00
/*
* HWA Host Controller Driver
* Wire Adapter Control / Data Streaming Iface ( WUSB1 .0 [ 8 ] )
*
* Copyright ( C ) 2005 - 2006 Intel Corporation
* Inaky Perez - Gonzalez < inaky . perez - gonzalez @ intel . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA
* 02110 - 1301 , USA .
*
*
* This driver implements a USB Host Controller ( struct usb_hcd ) for a
* Wireless USB Host Controller based on the Wireless USB 1.0
* Host - Wire - Adapter specification ( in layman terms , a USB - dongle that
* implements a Wireless USB host ) .
*
* Check out the Design - overview . txt file in the source documentation
* for other details on the implementation .
*
* Main blocks :
*
* driver glue with the driver API , workqueue daemon
*
* lc RC instance life cycle management ( create , destroy . . . )
*
* hcd glue with the USB API Host Controller Interface API .
*
* nep Notification EndPoint managent : collect notifications
* and queue them with the workqueue daemon .
*
* Handle notifications as coming from the NEP . Sends them
* off others to their respective modules ( eg : connect ,
* disconnect and reset go to devconnect ) .
*
* rpipe Remote Pipe management ; rpipe is what we use to write
* to an endpoint on a WUSB device that is connected to a
* HWA RC .
*
2009-07-23 08:31:31 +02:00
* xfer Transfer management - - this is all the code that gets a
2008-09-17 16:34:29 +01:00
* buffer and pushes it to a device ( or viceversa ) . *
*
* Some day a lot of this code will be shared between this driver and
* the drivers for DWA ( xfer , rpipe ) .
*
* All starts at driver . c : hwahc_probe ( ) , when one of this guys is
* connected . hwahc_disconnect ( ) stops it .
*
* During operation , the main driver is devices connecting or
* disconnecting . They cause the HWA RC to send notifications into
* nep . c : hwahc_nep_cb ( ) that will dispatch them to
* notif . c : wa_notif_dispatch ( ) . From there they will fan to cause
* device connects , disconnects , etc .
*
* Note much of the activity is difficult to follow . For example a
* device connect goes to devconnect , which will cause the " fake " root
* hub port to show a connect and stop there . Then khubd will notice
* and call into the rh . c : hwahc_rc_port_reset ( ) code to authenticate
* the device ( and this might require user intervention ) and enable
* the port .
*
* We also have a timer workqueue going from devconnect . c that
* schedules in hwahc_devconnect_create ( ) .
*
* The rest of the traffic is in the usual entry points of a USB HCD ,
* which are hooked up in driver . c : hwahc_rc_driver , and defined in
* hcd . c .
*/
# ifndef __HWAHC_INTERNAL_H__
# define __HWAHC_INTERNAL_H__
# include <linux/completion.h>
# include <linux/usb.h>
# include <linux/mutex.h>
# include <linux/spinlock.h>
# include <linux/uwb.h>
# include <linux/usb/wusb.h>
# include <linux/usb/wusb-wa.h>
struct wusbhc ;
struct wahc ;
extern void wa_urb_enqueue_run ( struct work_struct * ws ) ;
/**
* RPipe instance
*
* @ descr ' s fields are kept in LE , as we need to send it back and
* forth .
*
* @ wa is referenced when set
*
* @ segs_available is the number of requests segments that still can
* be submitted to the controller without overloading
* it . It is initialized to descr - > wRequests when
* aiming .
*
* A rpipe supports a max of descr - > wRequests at the same time ; before
* submitting seg_lock has to be taken . If segs_avail > 0 , then we can
* submit ; if not , we have to queue them .
*/
struct wa_rpipe {
struct kref refcnt ;
struct usb_rpipe_descriptor descr ;
struct usb_host_endpoint * ep ;
struct wahc * wa ;
spinlock_t seg_lock ;
struct list_head seg_list ;
atomic_t segs_available ;
u8 buffer [ 1 ] ; /* For reads/writes on USB */
} ;
/**
* Instance of a HWA Host Controller
*
* Except where a more specific lock / mutex applies or atomic , all
* fields protected by @ mutex .
*
* @ wa_descr Can be accessed without locking because it is in
* the same area where the device descriptors were
* read , so it is guaranteed to exist umodified while
* the device exists .
*
* Endianess has been converted to CPU ' s .
*
* @ nep_ * can be accessed without locking as its processing is
* serialized ; we submit a NEP URB and it comes to
* hwahc_nep_cb ( ) , which won ' t issue another URB until it is
* done processing it .
*
* @ xfer_list :
*
* List of active transfers to verify existence from a xfer id
* gotten from the xfer result message . Can ' t use urb - > list because
* it goes by endpoint , and we don ' t know the endpoint at the time
* when we get the xfer result message . We can ' t really rely on the
* pointer ( will have to change for 64 bits ) as the xfer id is 32 bits .
*
* @ xfer_delayed_list : List of transfers that need to be started
* ( with a workqueue , because they were
* submitted from an atomic context ) .
*
* FIXME : this needs to be layered up : a wusbhc layer ( for sharing
* comonalities with WHCI ) , a wa layer ( for sharing
* comonalities with DWA - RC ) .
*/
struct wahc {
struct usb_device * usb_dev ;
struct usb_interface * usb_iface ;
/* HC to deliver notifications */
union {
struct wusbhc * wusb ;
struct dwahc * dwa ;
} ;
const struct usb_endpoint_descriptor * dto_epd , * dti_epd ;
const struct usb_wa_descriptor * wa_descr ;
struct urb * nep_urb ; /* Notification EndPoint [lockless] */
struct edc nep_edc ;
void * nep_buffer ;
size_t nep_buffer_size ;
atomic_t notifs_queued ;
u16 rpipes ;
unsigned long * rpipe_bm ; /* rpipe usage bitmap */
spinlock_t rpipe_bm_lock ; /* protect rpipe_bm */
struct mutex rpipe_mutex ; /* assigning resources to endpoints */
struct urb * dti_urb ; /* URB for reading xfer results */
struct urb * buf_in_urb ; /* URB for reading data in */
struct edc dti_edc ; /* DTI error density counter */
struct wa_xfer_result * xfer_result ; /* real size = dti_ep maxpktsize */
size_t xfer_result_size ;
s32 status ; /* For reading status */
struct list_head xfer_list ;
struct list_head xfer_delayed_list ;
spinlock_t xfer_list_lock ;
struct work_struct xfer_work ;
atomic_t xfer_id_count ;
} ;
extern int wa_create ( struct wahc * wa , struct usb_interface * iface ) ;
extern void __wa_destroy ( struct wahc * wa ) ;
void wa_reset_all ( struct wahc * wa ) ;
/* Miscellaneous constants */
enum {
/** Max number of EPROTO errors we tolerate on the NEP in a
* period of time */
HWAHC_EPROTO_MAX = 16 ,
/** Period of time for EPROTO errors (in jiffies) */
HWAHC_EPROTO_PERIOD = 4 * HZ ,
} ;
/* Notification endpoint handling */
extern int wa_nep_create ( struct wahc * , struct usb_interface * ) ;
extern void wa_nep_destroy ( struct wahc * ) ;
static inline int wa_nep_arm ( struct wahc * wa , gfp_t gfp_mask )
{
struct urb * urb = wa - > nep_urb ;
urb - > transfer_buffer = wa - > nep_buffer ;
urb - > transfer_buffer_length = wa - > nep_buffer_size ;
return usb_submit_urb ( urb , gfp_mask ) ;
}
static inline void wa_nep_disarm ( struct wahc * wa )
{
usb_kill_urb ( wa - > nep_urb ) ;
}
/* RPipes */
static inline void wa_rpipe_init ( struct wahc * wa )
{
spin_lock_init ( & wa - > rpipe_bm_lock ) ;
mutex_init ( & wa - > rpipe_mutex ) ;
}
static inline void wa_init ( struct wahc * wa )
{
edc_init ( & wa - > nep_edc ) ;
atomic_set ( & wa - > notifs_queued , 0 ) ;
wa_rpipe_init ( wa ) ;
edc_init ( & wa - > dti_edc ) ;
INIT_LIST_HEAD ( & wa - > xfer_list ) ;
INIT_LIST_HEAD ( & wa - > xfer_delayed_list ) ;
spin_lock_init ( & wa - > xfer_list_lock ) ;
INIT_WORK ( & wa - > xfer_work , wa_urb_enqueue_run ) ;
atomic_set ( & wa - > xfer_id_count , 1 ) ;
}
/**
* Destroy a pipe ( when refcount drops to zero )
*
* Assumes it has been moved to the " QUIESCING " state .
*/
struct wa_xfer ;
extern void rpipe_destroy ( struct kref * _rpipe ) ;
static inline
void __rpipe_get ( struct wa_rpipe * rpipe )
{
kref_get ( & rpipe - > refcnt ) ;
}
extern int rpipe_get_by_ep ( struct wahc * , struct usb_host_endpoint * ,
struct urb * , gfp_t ) ;
static inline void rpipe_put ( struct wa_rpipe * rpipe )
{
kref_put ( & rpipe - > refcnt , rpipe_destroy ) ;
}
extern void rpipe_ep_disable ( struct wahc * , struct usb_host_endpoint * ) ;
extern int wa_rpipes_create ( struct wahc * ) ;
extern void wa_rpipes_destroy ( struct wahc * ) ;
static inline void rpipe_avail_dec ( struct wa_rpipe * rpipe )
{
atomic_dec ( & rpipe - > segs_available ) ;
}
/**
* Returns true if the rpipe is ready to submit more segments .
*/
static inline int rpipe_avail_inc ( struct wa_rpipe * rpipe )
{
return atomic_inc_return ( & rpipe - > segs_available ) > 0
& & ! list_empty ( & rpipe - > seg_list ) ;
}
/* Transferring data */
extern int wa_urb_enqueue ( struct wahc * , struct usb_host_endpoint * ,
struct urb * , gfp_t ) ;
extern int wa_urb_dequeue ( struct wahc * , struct urb * ) ;
extern void wa_handle_notif_xfer ( struct wahc * , struct wa_notif_hdr * ) ;
/* Misc
*
* FIXME : Refcounting for the actual @ hwahc object is not correct ; I
* mean , this should be refcounting on the HCD underneath , but
* it is not . In any case , the semantics for HCD refcounting
* are * weird * . . . on refcount reaching zero it just frees
* it . . . no RC specific function is called . . . unless I miss
* something .
*
* FIXME : has to go away in favour of an ' struct ' hcd based sollution
*/
static inline struct wahc * wa_get ( struct wahc * wa )
{
usb_get_intf ( wa - > usb_iface ) ;
return wa ;
}
static inline void wa_put ( struct wahc * wa )
{
usb_put_intf ( wa - > usb_iface ) ;
}
static inline int __wa_feature ( struct wahc * wa , unsigned op , u16 feature )
{
return usb_control_msg ( wa - > usb_dev , usb_sndctrlpipe ( wa - > usb_dev , 0 ) ,
op ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE ,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE ,
feature ,
wa - > usb_iface - > cur_altsetting - > desc . bInterfaceNumber ,
NULL , 0 , 1000 /* FIXME: arbitrary */ ) ;
}
static inline int __wa_set_feature ( struct wahc * wa , u16 feature )
{
return __wa_feature ( wa , 1 , feature ) ;
}
static inline int __wa_clear_feature ( struct wahc * wa , u16 feature )
{
return __wa_feature ( wa , 0 , feature ) ;
}
/**
* Return the status of a Wire Adapter
*
* @ wa : Wire Adapter instance
* @ returns < 0 errno code on error , or status bitmap as described
* in WUSB1 .0 [ 8.3 .1 .6 ] .
*
* NOTE : need malloc , some arches don ' t take USB from the stack
*/
static inline
s32 __wa_get_status ( struct wahc * wa )
{
s32 result ;
result = usb_control_msg (
wa - > usb_dev , usb_rcvctrlpipe ( wa - > usb_dev , 0 ) ,
USB_REQ_GET_STATUS ,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE ,
0 , wa - > usb_iface - > cur_altsetting - > desc . bInterfaceNumber ,
& wa - > status , sizeof ( wa - > status ) ,
1000 /* FIXME: arbitrary */ ) ;
if ( result > = 0 )
result = wa - > status ;
return result ;
}
/**
* Waits until the Wire Adapter ' s status matches @ mask / @ value
*
* @ wa : Wire Adapter instance .
* @ returns < 0 errno code on error , otherwise status .
*
* Loop until the WAs status matches the mask and value ( status & mask
* = = value ) . Timeout if it doesn ' t happen .
*
* FIXME : is there an official specification on how long status
* changes can take ?
*/
static inline s32 __wa_wait_status ( struct wahc * wa , u32 mask , u32 value )
{
s32 result ;
unsigned loops = 10 ;
do {
msleep ( 50 ) ;
result = __wa_get_status ( wa ) ;
if ( ( result & mask ) = = value )
break ;
if ( loops - - = = 0 ) {
result = - ETIMEDOUT ;
break ;
}
} while ( result > = 0 ) ;
return result ;
}
/** Command @hwahc to stop, @returns 0 if ok, < 0 errno code on error */
static inline int __wa_stop ( struct wahc * wa )
{
int result ;
struct device * dev = & wa - > usb_iface - > dev ;
result = __wa_clear_feature ( wa , WA_ENABLE ) ;
if ( result < 0 & & result ! = - ENODEV ) {
dev_err ( dev , " error commanding HC to stop: %d \n " , result ) ;
goto out ;
}
result = __wa_wait_status ( wa , WA_ENABLE , 0 ) ;
if ( result < 0 & & result ! = - ENODEV )
dev_err ( dev , " error waiting for HC to stop: %d \n " , result ) ;
out :
return 0 ;
}
# endif /* #ifndef __HWAHC_INTERNAL_H__ */