2018-04-11 18:06:42 +02:00
// SPDX-License-Identifier: GPL-2.0
/* Driver for Theobroma Systems UCAN devices, Protocol Version 3
*
* Copyright ( C ) 2018 Theobroma Systems Design und Consulting GmbH
*
*
* General Description :
*
* The USB Device uses three Endpoints :
*
* CONTROL Endpoint : Is used the setup the device ( start , stop ,
* info , configure ) .
*
* IN Endpoint : The device sends CAN Frame Messages and Device
* Information using the IN endpoint .
*
* OUT Endpoint : The driver sends configuration requests , and CAN
* Frames on the out endpoint .
*
* Error Handling :
*
* If error reporting is turned on the device encodes error into CAN
* error frames ( see uapi / linux / can / error . h ) and sends it using the
* IN Endpoint . The driver updates statistics and forward it .
*/
# include <linux/can.h>
# include <linux/can/dev.h>
# include <linux/can/error.h>
# include <linux/module.h>
# include <linux/netdevice.h>
# include <linux/signal.h>
# include <linux/skbuff.h>
# include <linux/slab.h>
# include <linux/usb.h>
# define UCAN_DRIVER_NAME "ucan"
# define UCAN_MAX_RX_URBS 8
/* the CAN controller needs a while to enable/disable the bus */
# define UCAN_USB_CTL_PIPE_TIMEOUT 1000
/* this driver currently supports protocol version 3 only */
# define UCAN_PROTOCOL_VERSION_MIN 3
# define UCAN_PROTOCOL_VERSION_MAX 3
/* UCAN Message Definitions
* - - - - - - - - - - - - - - - - - - - - - - - -
*
* ucan_message_out_t and ucan_message_in_t define the messages
* transmitted on the OUT and IN endpoint .
*
* Multibyte fields are transmitted with little endianness
*
* INTR Endpoint : a single uint32_t storing the current space in the fifo
*
* OUT Endpoint : single message of type ucan_message_out_t is
* transmitted on the out endpoint
*
* IN Endpoint : multiple messages ucan_message_in_t concateted in
* the following way :
*
* m [ n ] . len < = > the length if message n ( including the header in bytes )
* m [ n ] is is aligned to a 4 byte boundary , hence
* offset ( m [ 0 ] ) : = 0 ;
* offset ( m [ n + 1 ] ) : = offset ( m [ n ] ) + ( m [ n ] . len + 3 ) & 3
*
* this implies that
* offset ( m [ n ] ) % 4 < = > 0
*/
/* Device Global Commands */
enum {
UCAN_DEVICE_GET_FW_STRING = 0 ,
} ;
/* UCAN Commands */
enum {
/* start the can transceiver - val defines the operation mode */
UCAN_COMMAND_START = 0 ,
/* cancel pending transmissions and stop the can transceiver */
UCAN_COMMAND_STOP = 1 ,
/* send can transceiver into low-power sleep mode */
UCAN_COMMAND_SLEEP = 2 ,
/* wake up can transceiver from low-power sleep mode */
UCAN_COMMAND_WAKEUP = 3 ,
/* reset the can transceiver */
UCAN_COMMAND_RESET = 4 ,
/* get piece of info from the can transceiver - subcmd defines what
* piece
*/
UCAN_COMMAND_GET = 5 ,
/* clear or disable hardware filter - subcmd defines which of the two */
UCAN_COMMAND_FILTER = 6 ,
/* Setup bittiming */
UCAN_COMMAND_SET_BITTIMING = 7 ,
/* recover from bus-off state */
UCAN_COMMAND_RESTART = 8 ,
} ;
/* UCAN_COMMAND_START and UCAN_COMMAND_GET_INFO operation modes (bitmap).
* Undefined bits must be set to 0.
*/
enum {
UCAN_MODE_LOOPBACK = BIT ( 0 ) ,
UCAN_MODE_SILENT = BIT ( 1 ) ,
UCAN_MODE_3_SAMPLES = BIT ( 2 ) ,
UCAN_MODE_ONE_SHOT = BIT ( 3 ) ,
UCAN_MODE_BERR_REPORT = BIT ( 4 ) ,
} ;
/* UCAN_COMMAND_GET subcommands */
enum {
UCAN_COMMAND_GET_INFO = 0 ,
UCAN_COMMAND_GET_PROTOCOL_VERSION = 1 ,
} ;
/* UCAN_COMMAND_FILTER subcommands */
enum {
UCAN_FILTER_CLEAR = 0 ,
UCAN_FILTER_DISABLE = 1 ,
UCAN_FILTER_ENABLE = 2 ,
} ;
/* OUT endpoint message types */
enum {
UCAN_OUT_TX = 2 , /* transmit a CAN frame */
} ;
/* IN endpoint message types */
enum {
UCAN_IN_TX_COMPLETE = 1 , /* CAN frame transmission completed */
UCAN_IN_RX = 2 , /* CAN frame received */
} ;
struct ucan_ctl_cmd_start {
__le16 mode ; /* OR-ing any of UCAN_MODE_* */
} __packed ;
struct ucan_ctl_cmd_set_bittiming {
__le32 tq ; /* Time quanta (TQ) in nanoseconds */
__le16 brp ; /* TQ Prescaler */
__le16 sample_point ; /* Samplepoint on tenth percent */
u8 prop_seg ; /* Propagation segment in TQs */
u8 phase_seg1 ; /* Phase buffer segment 1 in TQs */
u8 phase_seg2 ; /* Phase buffer segment 2 in TQs */
u8 sjw ; /* Synchronisation jump width in TQs */
} __packed ;
struct ucan_ctl_cmd_device_info {
__le32 freq ; /* Clock Frequency for tq generation */
u8 tx_fifo ; /* Size of the transmission fifo */
u8 sjw_max ; /* can_bittiming fields... */
u8 tseg1_min ;
u8 tseg1_max ;
u8 tseg2_min ;
u8 tseg2_max ;
__le16 brp_inc ;
__le32 brp_min ;
__le32 brp_max ; /* ...can_bittiming fields */
__le16 ctrlmodes ; /* supported control modes */
__le16 hwfilter ; /* Number of HW filter banks */
__le16 rxmboxes ; /* Number of receive Mailboxes */
} __packed ;
struct ucan_ctl_cmd_get_protocol_version {
__le32 version ;
} __packed ;
union ucan_ctl_payload {
/* Setup Bittiming
* bmRequest = = UCAN_COMMAND_START
*/
struct ucan_ctl_cmd_start cmd_start ;
/* Setup Bittiming
* bmRequest = = UCAN_COMMAND_SET_BITTIMING
*/
struct ucan_ctl_cmd_set_bittiming cmd_set_bittiming ;
/* Get Device Information
* bmRequest = = UCAN_COMMAND_GET ; wValue = UCAN_COMMAND_GET_INFO
*/
struct ucan_ctl_cmd_device_info cmd_get_device_info ;
/* Get Protocol Version
* bmRequest = = UCAN_COMMAND_GET ;
* wValue = UCAN_COMMAND_GET_PROTOCOL_VERSION
*/
struct ucan_ctl_cmd_get_protocol_version cmd_get_protocol_version ;
u8 raw [ 128 ] ;
} __packed ;
enum {
UCAN_TX_COMPLETE_SUCCESS = BIT ( 0 ) ,
} ;
/* Transmission Complete within ucan_message_in */
struct ucan_tx_complete_entry_t {
u8 echo_index ;
u8 flags ;
} __packed __aligned ( 0x2 ) ;
/* CAN Data message format within ucan_message_in/out */
struct ucan_can_msg {
/* note DLC is computed by
* msg . len - sizeof ( msg . len )
* - sizeof ( msg . type )
* - sizeof ( msg . can_msg . id )
*/
__le32 id ;
union {
u8 data [ CAN_MAX_DLEN ] ; /* Data of CAN frames */
u8 dlc ; /* RTR dlc */
} ;
} __packed ;
/* OUT Endpoint, outbound messages */
struct ucan_message_out {
__le16 len ; /* Length of the content include header */
u8 type ; /* UCAN_OUT_TX and friends */
u8 subtype ; /* command sub type */
union {
/* Transmit CAN frame
* ( type = = UCAN_TX ) & & ( ( msg . can_msg . id & CAN_RTR_FLAG ) = = 0 )
* subtype stores the echo id
*/
struct ucan_can_msg can_msg ;
} msg ;
} __packed __aligned ( 0x4 ) ;
/* IN Endpoint, inbound messages */
struct ucan_message_in {
__le16 len ; /* Length of the content include header */
u8 type ; /* UCAN_IN_RX and friends */
u8 subtype ; /* command sub type */
union {
/* CAN Frame received
* ( type = = UCAN_IN_RX )
* & & ( ( msg . can_msg . id & CAN_RTR_FLAG ) = = 0 )
*/
struct ucan_can_msg can_msg ;
/* CAN transmission complete
* ( type = = UCAN_IN_TX_COMPLETE )
*/
struct ucan_tx_complete_entry_t can_tx_complete_msg [ 0 ] ;
} __aligned ( 0x4 ) msg ;
} __packed ;
/* Macros to calculate message lengths */
# define UCAN_OUT_HDR_SIZE offsetof(struct ucan_message_out, msg)
# define UCAN_IN_HDR_SIZE offsetof(struct ucan_message_in, msg)
# define UCAN_IN_LEN(member) (UCAN_OUT_HDR_SIZE + sizeof(member))
struct ucan_priv ;
/* Context Information for transmission URBs */
struct ucan_urb_context {
struct ucan_priv * up ;
u8 dlc ;
bool allocated ;
} ;
/* Information reported by the USB device */
struct ucan_device_info {
struct can_bittiming_const bittiming_const ;
u8 tx_fifo ;
} ;
/* Driver private data */
struct ucan_priv {
/* must be the first member */
struct can_priv can ;
/* linux USB device structures */
struct usb_device * udev ;
struct usb_interface * intf ;
struct net_device * netdev ;
/* lock for can->echo_skb (used around
* can_put / get / free_echo_skb
*/
spinlock_t echo_skb_lock ;
/* usb device information information */
u8 intf_index ;
u8 in_ep_addr ;
u8 out_ep_addr ;
u16 in_ep_size ;
/* transmission and reception buffers */
struct usb_anchor rx_urbs ;
struct usb_anchor tx_urbs ;
union ucan_ctl_payload * ctl_msg_buffer ;
struct ucan_device_info device_info ;
/* transmission control information and locks */
spinlock_t context_lock ;
unsigned int available_tx_urbs ;
struct ucan_urb_context * context_array ;
} ;
static u8 ucan_get_can_dlc ( struct ucan_can_msg * msg , u16 len )
{
if ( le32_to_cpu ( msg - > id ) & CAN_RTR_FLAG )
return get_can_dlc ( msg - > dlc ) ;
else
return get_can_dlc ( len - ( UCAN_IN_HDR_SIZE + sizeof ( msg - > id ) ) ) ;
}
static void ucan_release_context_array ( struct ucan_priv * up )
{
if ( ! up - > context_array )
return ;
/* lock is not needed because, driver is currently opening or closing */
up - > available_tx_urbs = 0 ;
kfree ( up - > context_array ) ;
up - > context_array = NULL ;
}
static int ucan_alloc_context_array ( struct ucan_priv * up )
{
int i ;
/* release contexts if any */
ucan_release_context_array ( up ) ;
up - > context_array = kcalloc ( up - > device_info . tx_fifo ,
sizeof ( * up - > context_array ) ,
GFP_KERNEL ) ;
if ( ! up - > context_array ) {
netdev_err ( up - > netdev ,
" Not enough memory to allocate tx contexts \n " ) ;
return - ENOMEM ;
}
for ( i = 0 ; i < up - > device_info . tx_fifo ; i + + ) {
up - > context_array [ i ] . allocated = false ;
up - > context_array [ i ] . up = up ;
}
/* lock is not needed because, driver is currently opening */
up - > available_tx_urbs = up - > device_info . tx_fifo ;
return 0 ;
}
static struct ucan_urb_context * ucan_alloc_context ( struct ucan_priv * up )
{
int i ;
unsigned long flags ;
struct ucan_urb_context * ret = NULL ;
if ( WARN_ON_ONCE ( ! up - > context_array ) )
return NULL ;
/* execute context operation atomically */
spin_lock_irqsave ( & up - > context_lock , flags ) ;
for ( i = 0 ; i < up - > device_info . tx_fifo ; i + + ) {
if ( ! up - > context_array [ i ] . allocated ) {
/* update context */
ret = & up - > context_array [ i ] ;
up - > context_array [ i ] . allocated = true ;
/* stop queue if necessary */
up - > available_tx_urbs - - ;
if ( ! up - > available_tx_urbs )
netif_stop_queue ( up - > netdev ) ;
break ;
}
}
spin_unlock_irqrestore ( & up - > context_lock , flags ) ;
return ret ;
}
static bool ucan_release_context ( struct ucan_priv * up ,
struct ucan_urb_context * ctx )
{
unsigned long flags ;
bool ret = false ;
if ( WARN_ON_ONCE ( ! up - > context_array ) )
return false ;
/* execute context operation atomically */
spin_lock_irqsave ( & up - > context_lock , flags ) ;
/* context was not allocated, maybe the device sent garbage */
if ( ctx - > allocated ) {
ctx - > allocated = false ;
/* check if the queue needs to be woken */
if ( ! up - > available_tx_urbs )
netif_wake_queue ( up - > netdev ) ;
up - > available_tx_urbs + + ;
ret = true ;
}
spin_unlock_irqrestore ( & up - > context_lock , flags ) ;
return ret ;
}
static int ucan_ctrl_command_out ( struct ucan_priv * up ,
u8 cmd , u16 subcmd , u16 datalen )
{
return usb_control_msg ( up - > udev ,
usb_sndctrlpipe ( up - > udev , 0 ) ,
cmd ,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE ,
subcmd ,
up - > intf_index ,
up - > ctl_msg_buffer ,
datalen ,
UCAN_USB_CTL_PIPE_TIMEOUT ) ;
}
static int ucan_device_request_in ( struct ucan_priv * up ,
u8 cmd , u16 subcmd , u16 datalen )
{
return usb_control_msg ( up - > udev ,
usb_rcvctrlpipe ( up - > udev , 0 ) ,
cmd ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE ,
subcmd ,
0 ,
up - > ctl_msg_buffer ,
datalen ,
UCAN_USB_CTL_PIPE_TIMEOUT ) ;
}
/* Parse the device information structure reported by the device and
* setup private variables accordingly
*/
static void ucan_parse_device_info ( struct ucan_priv * up ,
struct ucan_ctl_cmd_device_info * device_info )
{
struct can_bittiming_const * bittiming =
& up - > device_info . bittiming_const ;
u16 ctrlmodes ;
/* store the data */
up - > can . clock . freq = le32_to_cpu ( device_info - > freq ) ;
up - > device_info . tx_fifo = device_info - > tx_fifo ;
strcpy ( bittiming - > name , " ucan " ) ;
bittiming - > tseg1_min = device_info - > tseg1_min ;
bittiming - > tseg1_max = device_info - > tseg1_max ;
bittiming - > tseg2_min = device_info - > tseg2_min ;
bittiming - > tseg2_max = device_info - > tseg2_max ;
bittiming - > sjw_max = device_info - > sjw_max ;
bittiming - > brp_min = le32_to_cpu ( device_info - > brp_min ) ;
bittiming - > brp_max = le32_to_cpu ( device_info - > brp_max ) ;
bittiming - > brp_inc = le16_to_cpu ( device_info - > brp_inc ) ;
ctrlmodes = le16_to_cpu ( device_info - > ctrlmodes ) ;
up - > can . ctrlmode_supported = 0 ;
if ( ctrlmodes & UCAN_MODE_LOOPBACK )
up - > can . ctrlmode_supported | = CAN_CTRLMODE_LOOPBACK ;
if ( ctrlmodes & UCAN_MODE_SILENT )
up - > can . ctrlmode_supported | = CAN_CTRLMODE_LISTENONLY ;
if ( ctrlmodes & UCAN_MODE_3_SAMPLES )
up - > can . ctrlmode_supported | = CAN_CTRLMODE_3_SAMPLES ;
if ( ctrlmodes & UCAN_MODE_ONE_SHOT )
up - > can . ctrlmode_supported | = CAN_CTRLMODE_ONE_SHOT ;
if ( ctrlmodes & UCAN_MODE_BERR_REPORT )
up - > can . ctrlmode_supported | = CAN_CTRLMODE_BERR_REPORTING ;
}
/* Handle a CAN error frame that we have received from the device.
* Returns true if the can state has changed .
*/
static bool ucan_handle_error_frame ( struct ucan_priv * up ,
struct ucan_message_in * m ,
canid_t canid )
{
enum can_state new_state = up - > can . state ;
struct net_device_stats * net_stats = & up - > netdev - > stats ;
struct can_device_stats * can_stats = & up - > can . can_stats ;
if ( canid & CAN_ERR_LOSTARB )
can_stats - > arbitration_lost + + ;
if ( canid & CAN_ERR_BUSERROR )
can_stats - > bus_error + + ;
if ( canid & CAN_ERR_ACK )
net_stats - > tx_errors + + ;
if ( canid & CAN_ERR_BUSOFF )
new_state = CAN_STATE_BUS_OFF ;
/* controller problems, details in data[1] */
if ( canid & CAN_ERR_CRTL ) {
u8 d1 = m - > msg . can_msg . data [ 1 ] ;
if ( d1 & CAN_ERR_CRTL_RX_OVERFLOW )
net_stats - > rx_over_errors + + ;
/* controller state bits: if multiple are set the worst wins */
if ( d1 & CAN_ERR_CRTL_ACTIVE )
new_state = CAN_STATE_ERROR_ACTIVE ;
if ( d1 & ( CAN_ERR_CRTL_RX_WARNING | CAN_ERR_CRTL_TX_WARNING ) )
new_state = CAN_STATE_ERROR_WARNING ;
if ( d1 & ( CAN_ERR_CRTL_RX_PASSIVE | CAN_ERR_CRTL_TX_PASSIVE ) )
new_state = CAN_STATE_ERROR_PASSIVE ;
}
/* protocol error, details in data[2] */
if ( canid & CAN_ERR_PROT ) {
u8 d2 = m - > msg . can_msg . data [ 2 ] ;
if ( d2 & CAN_ERR_PROT_TX )
net_stats - > tx_errors + + ;
else
net_stats - > rx_errors + + ;
}
/* no state change - we are done */
if ( up - > can . state = = new_state )
return false ;
/* we switched into a better state */
if ( up - > can . state > new_state ) {
up - > can . state = new_state ;
return true ;
}
/* we switched into a worse state */
up - > can . state = new_state ;
switch ( new_state ) {
case CAN_STATE_BUS_OFF :
can_stats - > bus_off + + ;
can_bus_off ( up - > netdev ) ;
break ;
case CAN_STATE_ERROR_PASSIVE :
can_stats - > error_passive + + ;
break ;
case CAN_STATE_ERROR_WARNING :
can_stats - > error_warning + + ;
break ;
default :
break ;
}
return true ;
}
/* Callback on reception of a can frame via the IN endpoint
*
* This function allocates an skb and transferres it to the Linux
* network stack
*/
static void ucan_rx_can_msg ( struct ucan_priv * up , struct ucan_message_in * m )
{
int len ;
canid_t canid ;
struct can_frame * cf ;
struct sk_buff * skb ;
struct net_device_stats * stats = & up - > netdev - > stats ;
/* get the contents of the length field */
len = le16_to_cpu ( m - > len ) ;
/* check sanity */
if ( len < UCAN_IN_HDR_SIZE + sizeof ( m - > msg . can_msg . id ) ) {
netdev_warn ( up - > netdev , " invalid input message len: %d \n " , len ) ;
return ;
}
/* handle error frames */
canid = le32_to_cpu ( m - > msg . can_msg . id ) ;
if ( canid & CAN_ERR_FLAG ) {
bool busstate_changed = ucan_handle_error_frame ( up , m , canid ) ;
/* if berr-reporting is off only state changes get through */
if ( ! ( up - > can . ctrlmode & CAN_CTRLMODE_BERR_REPORTING ) & &
! busstate_changed )
return ;
} else {
canid_t canid_mask ;
/* compute the mask for canid */
canid_mask = CAN_RTR_FLAG ;
if ( canid & CAN_EFF_FLAG )
canid_mask | = CAN_EFF_MASK | CAN_EFF_FLAG ;
else
canid_mask | = CAN_SFF_MASK ;
if ( canid & ~ canid_mask )
netdev_warn ( up - > netdev ,
" unexpected bits set (canid %x, mask %x) " ,
canid , canid_mask ) ;
canid & = canid_mask ;
}
/* allocate skb */
skb = alloc_can_skb ( up - > netdev , & cf ) ;
if ( ! skb )
return ;
/* fill the can frame */
cf - > can_id = canid ;
/* compute DLC taking RTR_FLAG into account */
cf - > can_dlc = ucan_get_can_dlc ( & m - > msg . can_msg , len ) ;
/* copy the payload of non RTR frames */
if ( ! ( cf - > can_id & CAN_RTR_FLAG ) | | ( cf - > can_id & CAN_ERR_FLAG ) )
memcpy ( cf - > data , m - > msg . can_msg . data , cf - > can_dlc ) ;
/* don't count error frames as real packets */
stats - > rx_packets + + ;
stats - > rx_bytes + = cf - > can_dlc ;
/* pass it to Linux */
netif_rx ( skb ) ;
}
/* callback indicating completed transmission */
static void ucan_tx_complete_msg ( struct ucan_priv * up ,
struct ucan_message_in * m )
{
unsigned long flags ;
u16 count , i ;
u8 echo_index , dlc ;
u16 len = le16_to_cpu ( m - > len ) ;
struct ucan_urb_context * context ;
if ( len < UCAN_IN_HDR_SIZE | | ( len % 2 ! = 0 ) ) {
netdev_err ( up - > netdev , " invalid tx complete length \n " ) ;
return ;
}
count = ( len - UCAN_IN_HDR_SIZE ) / 2 ;
for ( i = 0 ; i < count ; i + + ) {
/* we did not submit such echo ids */
echo_index = m - > msg . can_tx_complete_msg [ i ] . echo_index ;
if ( echo_index > = up - > device_info . tx_fifo ) {
up - > netdev - > stats . tx_errors + + ;
netdev_err ( up - > netdev ,
" invalid echo_index %d received \n " ,
echo_index ) ;
continue ;
}
/* gather information from the context */
context = & up - > context_array [ echo_index ] ;
dlc = READ_ONCE ( context - > dlc ) ;
/* Release context and restart queue if necessary.
* Also check if the context was allocated
*/
if ( ! ucan_release_context ( up , context ) )
continue ;
spin_lock_irqsave ( & up - > echo_skb_lock , flags ) ;
if ( m - > msg . can_tx_complete_msg [ i ] . flags &
UCAN_TX_COMPLETE_SUCCESS ) {
/* update statistics */
up - > netdev - > stats . tx_packets + + ;
up - > netdev - > stats . tx_bytes + = dlc ;
can_get_echo_skb ( up - > netdev , echo_index ) ;
} else {
up - > netdev - > stats . tx_dropped + + ;
can_free_echo_skb ( up - > netdev , echo_index ) ;
}
spin_unlock_irqrestore ( & up - > echo_skb_lock , flags ) ;
}
}
/* callback on reception of a USB message */
static void ucan_read_bulk_callback ( struct urb * urb )
{
int ret ;
int pos ;
struct ucan_priv * up = urb - > context ;
struct net_device * netdev = up - > netdev ;
struct ucan_message_in * m ;
/* the device is not up and the driver should not receive any
* data on the bulk in pipe
*/
if ( WARN_ON ( ! up - > context_array ) ) {
usb_free_coherent ( up - > udev ,
up - > in_ep_size ,
urb - > transfer_buffer ,
urb - > transfer_dma ) ;
return ;
}
/* check URB status */
switch ( urb - > status ) {
case 0 :
break ;
case - ENOENT :
case - EPIPE :
case - EPROTO :
case - ESHUTDOWN :
case - ETIME :
/* urb is not resubmitted -> free dma data */
usb_free_coherent ( up - > udev ,
up - > in_ep_size ,
urb - > transfer_buffer ,
urb - > transfer_dma ) ;
2018-07-30 14:40:37 +01:00
netdev_dbg ( up - > netdev , " not resubmitting urb; status: %d \n " ,
2018-04-11 18:06:42 +02:00
urb - > status ) ;
return ;
default :
goto resubmit ;
}
/* sanity check */
if ( ! netif_device_present ( netdev ) )
return ;
/* iterate over input */
pos = 0 ;
while ( pos < urb - > actual_length ) {
int len ;
/* check sanity (length of header) */
if ( ( urb - > actual_length - pos ) < UCAN_IN_HDR_SIZE ) {
netdev_warn ( up - > netdev ,
" invalid message (short; no hdr; l:%d) \n " ,
urb - > actual_length ) ;
goto resubmit ;
}
/* setup the message address */
m = ( struct ucan_message_in * )
( ( u8 * ) urb - > transfer_buffer + pos ) ;
len = le16_to_cpu ( m - > len ) ;
/* check sanity (length of content) */
if ( urb - > actual_length - pos < len ) {
netdev_warn ( up - > netdev ,
" invalid message (short; no data; l:%d) \n " ,
urb - > actual_length ) ;
print_hex_dump ( KERN_WARNING ,
" raw data: " ,
DUMP_PREFIX_ADDRESS ,
16 ,
1 ,
urb - > transfer_buffer ,
urb - > actual_length ,
true ) ;
goto resubmit ;
}
switch ( m - > type ) {
case UCAN_IN_RX :
ucan_rx_can_msg ( up , m ) ;
break ;
case UCAN_IN_TX_COMPLETE :
ucan_tx_complete_msg ( up , m ) ;
break ;
default :
netdev_warn ( up - > netdev ,
" invalid message (type; t:%d) \n " ,
m - > type ) ;
break ;
}
/* proceed to next message */
pos + = len ;
/* align to 4 byte boundary */
pos = round_up ( pos , 4 ) ;
}
resubmit :
/* resubmit urb when done */
usb_fill_bulk_urb ( urb , up - > udev ,
usb_rcvbulkpipe ( up - > udev ,
up - > in_ep_addr ) ,
urb - > transfer_buffer ,
up - > in_ep_size ,
ucan_read_bulk_callback ,
up ) ;
usb_anchor_urb ( urb , & up - > rx_urbs ) ;
2019-11-28 19:26:03 +01:00
ret = usb_submit_urb ( urb , GFP_ATOMIC ) ;
2018-04-11 18:06:42 +02:00
if ( ret < 0 ) {
netdev_err ( up - > netdev ,
" failed resubmitting read bulk urb: %d \n " ,
ret ) ;
usb_unanchor_urb ( urb ) ;
usb_free_coherent ( up - > udev ,
up - > in_ep_size ,
urb - > transfer_buffer ,
urb - > transfer_dma ) ;
if ( ret = = - ENODEV )
netif_device_detach ( netdev ) ;
}
}
/* callback after transmission of a USB message */
static void ucan_write_bulk_callback ( struct urb * urb )
{
unsigned long flags ;
struct ucan_priv * up ;
struct ucan_urb_context * context = urb - > context ;
/* get the urb context */
if ( WARN_ON_ONCE ( ! context ) )
return ;
/* free up our allocated buffer */
usb_free_coherent ( urb - > dev ,
sizeof ( struct ucan_message_out ) ,
urb - > transfer_buffer ,
urb - > transfer_dma ) ;
up = context - > up ;
if ( WARN_ON_ONCE ( ! up ) )
return ;
/* sanity check */
if ( ! netif_device_present ( up - > netdev ) )
return ;
/* transmission failed (USB - the device will not send a TX complete) */
if ( urb - > status ) {
netdev_warn ( up - > netdev ,
" failed to transmit USB message to device: %d \n " ,
urb - > status ) ;
/* update counters an cleanup */
spin_lock_irqsave ( & up - > echo_skb_lock , flags ) ;
can_free_echo_skb ( up - > netdev , context - up - > context_array ) ;
spin_unlock_irqrestore ( & up - > echo_skb_lock , flags ) ;
up - > netdev - > stats . tx_dropped + + ;
/* release context and restart the queue if necessary */
if ( ! ucan_release_context ( up , context ) )
netdev_err ( up - > netdev ,
" urb failed, failed to release context \n " ) ;
}
}
static void ucan_cleanup_rx_urbs ( struct ucan_priv * up , struct urb * * urbs )
{
int i ;
for ( i = 0 ; i < UCAN_MAX_RX_URBS ; i + + ) {
if ( urbs [ i ] ) {
usb_unanchor_urb ( urbs [ i ] ) ;
usb_free_coherent ( up - > udev ,
up - > in_ep_size ,
urbs [ i ] - > transfer_buffer ,
urbs [ i ] - > transfer_dma ) ;
usb_free_urb ( urbs [ i ] ) ;
}
}
memset ( urbs , 0 , sizeof ( * urbs ) * UCAN_MAX_RX_URBS ) ;
}
static int ucan_prepare_and_anchor_rx_urbs ( struct ucan_priv * up ,
struct urb * * urbs )
{
int i ;
memset ( urbs , 0 , sizeof ( * urbs ) * UCAN_MAX_RX_URBS ) ;
for ( i = 0 ; i < UCAN_MAX_RX_URBS ; i + + ) {
void * buf ;
urbs [ i ] = usb_alloc_urb ( 0 , GFP_KERNEL ) ;
if ( ! urbs [ i ] )
goto err ;
buf = usb_alloc_coherent ( up - > udev ,
up - > in_ep_size ,
GFP_KERNEL , & urbs [ i ] - > transfer_dma ) ;
if ( ! buf ) {
/* cleanup this urb */
usb_free_urb ( urbs [ i ] ) ;
urbs [ i ] = NULL ;
goto err ;
}
usb_fill_bulk_urb ( urbs [ i ] , up - > udev ,
usb_rcvbulkpipe ( up - > udev ,
up - > in_ep_addr ) ,
buf ,
up - > in_ep_size ,
ucan_read_bulk_callback ,
up ) ;
urbs [ i ] - > transfer_flags | = URB_NO_TRANSFER_DMA_MAP ;
usb_anchor_urb ( urbs [ i ] , & up - > rx_urbs ) ;
}
return 0 ;
err :
/* cleanup other unsubmitted urbs */
ucan_cleanup_rx_urbs ( up , urbs ) ;
return - ENOMEM ;
}
/* Submits rx urbs with the semantic: Either submit all, or cleanup
* everything . I case of errors submitted urbs are killed and all urbs in
* the array are freed . I case of no errors every entry in the urb
* array is set to NULL .
*/
static int ucan_submit_rx_urbs ( struct ucan_priv * up , struct urb * * urbs )
{
int i , ret ;
/* Iterate over all urbs to submit. On success remove the urb
* from the list .
*/
for ( i = 0 ; i < UCAN_MAX_RX_URBS ; i + + ) {
ret = usb_submit_urb ( urbs [ i ] , GFP_KERNEL ) ;
if ( ret ) {
netdev_err ( up - > netdev ,
" could not submit urb; code: %d \n " ,
ret ) ;
goto err ;
}
/* Anchor URB and drop reference, USB core will take
* care of freeing it
*/
usb_free_urb ( urbs [ i ] ) ;
urbs [ i ] = NULL ;
}
return 0 ;
err :
/* Cleanup unsubmitted urbs */
ucan_cleanup_rx_urbs ( up , urbs ) ;
/* Kill urbs that are already submitted */
usb_kill_anchored_urbs ( & up - > rx_urbs ) ;
return ret ;
}
/* Open the network device */
static int ucan_open ( struct net_device * netdev )
{
int ret , ret_cleanup ;
u16 ctrlmode ;
struct urb * urbs [ UCAN_MAX_RX_URBS ] ;
struct ucan_priv * up = netdev_priv ( netdev ) ;
ret = ucan_alloc_context_array ( up ) ;
if ( ret )
return ret ;
/* Allocate and prepare IN URBS - allocated and anchored
* urbs are stored in urbs [ ] for clean
*/
ret = ucan_prepare_and_anchor_rx_urbs ( up , urbs ) ;
if ( ret )
goto err_contexts ;
/* Check the control mode */
ctrlmode = 0 ;
if ( up - > can . ctrlmode & CAN_CTRLMODE_LOOPBACK )
ctrlmode | = UCAN_MODE_LOOPBACK ;
if ( up - > can . ctrlmode & CAN_CTRLMODE_LISTENONLY )
ctrlmode | = UCAN_MODE_SILENT ;
if ( up - > can . ctrlmode & CAN_CTRLMODE_3_SAMPLES )
ctrlmode | = UCAN_MODE_3_SAMPLES ;
if ( up - > can . ctrlmode & CAN_CTRLMODE_ONE_SHOT )
ctrlmode | = UCAN_MODE_ONE_SHOT ;
/* Enable this in any case - filtering is down within the
* receive path
*/
ctrlmode | = UCAN_MODE_BERR_REPORT ;
up - > ctl_msg_buffer - > cmd_start . mode = cpu_to_le16 ( ctrlmode ) ;
/* Driver is ready to receive data - start the USB device */
ret = ucan_ctrl_command_out ( up , UCAN_COMMAND_START , 0 , 2 ) ;
if ( ret < 0 ) {
netdev_err ( up - > netdev ,
" could not start device, code: %d \n " ,
ret ) ;
goto err_reset ;
}
/* Call CAN layer open */
ret = open_candev ( netdev ) ;
if ( ret )
goto err_stop ;
/* Driver is ready to receive data. Submit RX URBS */
ret = ucan_submit_rx_urbs ( up , urbs ) ;
if ( ret )
goto err_stop ;
up - > can . state = CAN_STATE_ERROR_ACTIVE ;
/* Start the network queue */
netif_start_queue ( netdev ) ;
return 0 ;
err_stop :
/* The device have started already stop it */
ret_cleanup = ucan_ctrl_command_out ( up , UCAN_COMMAND_STOP , 0 , 0 ) ;
if ( ret_cleanup < 0 )
netdev_err ( up - > netdev ,
" could not stop device, code: %d \n " ,
ret_cleanup ) ;
err_reset :
/* The device might have received data, reset it for
* consistent state
*/
ret_cleanup = ucan_ctrl_command_out ( up , UCAN_COMMAND_RESET , 0 , 0 ) ;
if ( ret_cleanup < 0 )
netdev_err ( up - > netdev ,
" could not reset device, code: %d \n " ,
ret_cleanup ) ;
/* clean up unsubmitted urbs */
ucan_cleanup_rx_urbs ( up , urbs ) ;
err_contexts :
ucan_release_context_array ( up ) ;
return ret ;
}
static struct urb * ucan_prepare_tx_urb ( struct ucan_priv * up ,
struct ucan_urb_context * context ,
struct can_frame * cf ,
u8 echo_index )
{
int mlen ;
struct urb * urb ;
struct ucan_message_out * m ;
/* create a URB, and a buffer for it, and copy the data to the URB */
urb = usb_alloc_urb ( 0 , GFP_ATOMIC ) ;
if ( ! urb ) {
netdev_err ( up - > netdev , " no memory left for URBs \n " ) ;
return NULL ;
}
m = usb_alloc_coherent ( up - > udev ,
sizeof ( struct ucan_message_out ) ,
GFP_ATOMIC ,
& urb - > transfer_dma ) ;
if ( ! m ) {
netdev_err ( up - > netdev , " no memory left for USB buffer \n " ) ;
usb_free_urb ( urb ) ;
return NULL ;
}
/* build the USB message */
m - > type = UCAN_OUT_TX ;
m - > msg . can_msg . id = cpu_to_le32 ( cf - > can_id ) ;
if ( cf - > can_id & CAN_RTR_FLAG ) {
mlen = UCAN_OUT_HDR_SIZE +
offsetof ( struct ucan_can_msg , dlc ) +
sizeof ( m - > msg . can_msg . dlc ) ;
m - > msg . can_msg . dlc = cf - > can_dlc ;
} else {
mlen = UCAN_OUT_HDR_SIZE +
sizeof ( m - > msg . can_msg . id ) + cf - > can_dlc ;
memcpy ( m - > msg . can_msg . data , cf - > data , cf - > can_dlc ) ;
}
m - > len = cpu_to_le16 ( mlen ) ;
context - > dlc = cf - > can_dlc ;
m - > subtype = echo_index ;
/* build the urb */
usb_fill_bulk_urb ( urb , up - > udev ,
usb_sndbulkpipe ( up - > udev ,
up - > out_ep_addr ) ,
m , mlen , ucan_write_bulk_callback , context ) ;
urb - > transfer_flags | = URB_NO_TRANSFER_DMA_MAP ;
return urb ;
}
static void ucan_clean_up_tx_urb ( struct ucan_priv * up , struct urb * urb )
{
usb_free_coherent ( up - > udev , sizeof ( struct ucan_message_out ) ,
urb - > transfer_buffer , urb - > transfer_dma ) ;
usb_free_urb ( urb ) ;
}
/* callback when Linux needs to send a can frame */
static netdev_tx_t ucan_start_xmit ( struct sk_buff * skb ,
struct net_device * netdev )
{
unsigned long flags ;
int ret ;
u8 echo_index ;
struct urb * urb ;
struct ucan_urb_context * context ;
struct ucan_priv * up = netdev_priv ( netdev ) ;
struct can_frame * cf = ( struct can_frame * ) skb - > data ;
/* check skb */
if ( can_dropped_invalid_skb ( netdev , skb ) )
return NETDEV_TX_OK ;
/* allocate a context and slow down tx path, if fifo state is low */
context = ucan_alloc_context ( up ) ;
echo_index = context - up - > context_array ;
if ( WARN_ON_ONCE ( ! context ) )
return NETDEV_TX_BUSY ;
/* prepare urb for transmission */
urb = ucan_prepare_tx_urb ( up , context , cf , echo_index ) ;
if ( ! urb )
goto drop ;
/* put the skb on can loopback stack */
spin_lock_irqsave ( & up - > echo_skb_lock , flags ) ;
can_put_echo_skb ( skb , up - > netdev , echo_index ) ;
spin_unlock_irqrestore ( & up - > echo_skb_lock , flags ) ;
/* transmit it */
usb_anchor_urb ( urb , & up - > tx_urbs ) ;
ret = usb_submit_urb ( urb , GFP_ATOMIC ) ;
/* cleanup urb */
if ( ret ) {
/* on error, clean up */
usb_unanchor_urb ( urb ) ;
ucan_clean_up_tx_urb ( up , urb ) ;
if ( ! ucan_release_context ( up , context ) )
netdev_err ( up - > netdev ,
" xmit err: failed to release context \n " ) ;
/* remove the skb from the echo stack - this also
* frees the skb
*/
spin_lock_irqsave ( & up - > echo_skb_lock , flags ) ;
can_free_echo_skb ( up - > netdev , echo_index ) ;
spin_unlock_irqrestore ( & up - > echo_skb_lock , flags ) ;
if ( ret = = - ENODEV ) {
netif_device_detach ( up - > netdev ) ;
} else {
netdev_warn ( up - > netdev ,
" xmit err: failed to submit urb %d \n " ,
ret ) ;
up - > netdev - > stats . tx_dropped + + ;
}
return NETDEV_TX_OK ;
}
netif_trans_update ( netdev ) ;
/* release ref, as we do not need the urb anymore */
usb_free_urb ( urb ) ;
return NETDEV_TX_OK ;
drop :
if ( ! ucan_release_context ( up , context ) )
netdev_err ( up - > netdev ,
" xmit drop: failed to release context \n " ) ;
dev_kfree_skb ( skb ) ;
up - > netdev - > stats . tx_dropped + + ;
return NETDEV_TX_OK ;
}
/* Device goes down
*
* Clean up used resources
*/
static int ucan_close ( struct net_device * netdev )
{
int ret ;
struct ucan_priv * up = netdev_priv ( netdev ) ;
up - > can . state = CAN_STATE_STOPPED ;
/* stop sending data */
usb_kill_anchored_urbs ( & up - > tx_urbs ) ;
/* stop receiving data */
usb_kill_anchored_urbs ( & up - > rx_urbs ) ;
/* stop and reset can device */
ret = ucan_ctrl_command_out ( up , UCAN_COMMAND_STOP , 0 , 0 ) ;
if ( ret < 0 )
netdev_err ( up - > netdev ,
" could not stop device, code: %d \n " ,
ret ) ;
ret = ucan_ctrl_command_out ( up , UCAN_COMMAND_RESET , 0 , 0 ) ;
if ( ret < 0 )
netdev_err ( up - > netdev ,
" could not reset device, code: %d \n " ,
ret ) ;
netif_stop_queue ( netdev ) ;
ucan_release_context_array ( up ) ;
close_candev ( up - > netdev ) ;
return 0 ;
}
/* CAN driver callbacks */
static const struct net_device_ops ucan_netdev_ops = {
. ndo_open = ucan_open ,
. ndo_stop = ucan_close ,
. ndo_start_xmit = ucan_start_xmit ,
. ndo_change_mtu = can_change_mtu ,
} ;
/* Request to set bittiming
*
* This function generates an USB set bittiming message and transmits
* it to the device
*/
static int ucan_set_bittiming ( struct net_device * netdev )
{
int ret ;
struct ucan_priv * up = netdev_priv ( netdev ) ;
struct ucan_ctl_cmd_set_bittiming * cmd_set_bittiming ;
cmd_set_bittiming = & up - > ctl_msg_buffer - > cmd_set_bittiming ;
cmd_set_bittiming - > tq = cpu_to_le32 ( up - > can . bittiming . tq ) ;
cmd_set_bittiming - > brp = cpu_to_le16 ( up - > can . bittiming . brp ) ;
cmd_set_bittiming - > sample_point =
cpu_to_le16 ( up - > can . bittiming . sample_point ) ;
cmd_set_bittiming - > prop_seg = up - > can . bittiming . prop_seg ;
cmd_set_bittiming - > phase_seg1 = up - > can . bittiming . phase_seg1 ;
cmd_set_bittiming - > phase_seg2 = up - > can . bittiming . phase_seg2 ;
cmd_set_bittiming - > sjw = up - > can . bittiming . sjw ;
ret = ucan_ctrl_command_out ( up , UCAN_COMMAND_SET_BITTIMING , 0 ,
sizeof ( * cmd_set_bittiming ) ) ;
return ( ret < 0 ) ? ret : 0 ;
}
/* Restart the device to get it out of BUS-OFF state.
* Called when the user runs " ip link set can1 type can restart " .
*/
static int ucan_set_mode ( struct net_device * netdev , enum can_mode mode )
{
int ret ;
unsigned long flags ;
struct ucan_priv * up = netdev_priv ( netdev ) ;
switch ( mode ) {
case CAN_MODE_START :
netdev_dbg ( up - > netdev , " restarting device \n " ) ;
ret = ucan_ctrl_command_out ( up , UCAN_COMMAND_RESTART , 0 , 0 ) ;
up - > can . state = CAN_STATE_ERROR_ACTIVE ;
/* check if queue can be restarted,
* up - > available_tx_urbs must be protected by the
* lock
*/
spin_lock_irqsave ( & up - > context_lock , flags ) ;
if ( up - > available_tx_urbs > 0 )
netif_wake_queue ( up - > netdev ) ;
spin_unlock_irqrestore ( & up - > context_lock , flags ) ;
return ret ;
default :
return - EOPNOTSUPP ;
}
}
/* Probe the device, reset it and gather general device information */
static int ucan_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
int ret ;
int i ;
u32 protocol_version ;
struct usb_device * udev ;
struct net_device * netdev ;
struct usb_host_interface * iface_desc ;
struct ucan_priv * up ;
struct usb_endpoint_descriptor * ep ;
u16 in_ep_size ;
u16 out_ep_size ;
u8 in_ep_addr ;
u8 out_ep_addr ;
union ucan_ctl_payload * ctl_msg_buffer ;
char firmware_str [ sizeof ( union ucan_ctl_payload ) + 1 ] ;
udev = interface_to_usbdev ( intf ) ;
/* Stage 1 - Interface Parsing
* - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Identifie the device USB interface descriptor and its
* endpoints . Probing is aborted on errors .
*/
/* check if the interface is sane */
iface_desc = intf - > cur_altsetting ;
if ( ! iface_desc )
return - ENODEV ;
dev_info ( & udev - > dev ,
" %s: probing device on interface #%d \n " ,
UCAN_DRIVER_NAME ,
iface_desc - > desc . bInterfaceNumber ) ;
/* interface sanity check */
if ( iface_desc - > desc . bNumEndpoints ! = 2 ) {
dev_err ( & udev - > dev ,
" %s: invalid EP count (%d) " ,
UCAN_DRIVER_NAME , iface_desc - > desc . bNumEndpoints ) ;
goto err_firmware_needs_update ;
}
/* check interface endpoints */
in_ep_addr = 0 ;
out_ep_addr = 0 ;
in_ep_size = 0 ;
out_ep_size = 0 ;
for ( i = 0 ; i < iface_desc - > desc . bNumEndpoints ; i + + ) {
ep = & iface_desc - > endpoint [ i ] . desc ;
if ( ( ( ep - > bEndpointAddress & USB_ENDPOINT_DIR_MASK ) ! = 0 ) & &
( ( ep - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) = =
USB_ENDPOINT_XFER_BULK ) ) {
/* In Endpoint */
in_ep_addr = ep - > bEndpointAddress ;
in_ep_addr & = USB_ENDPOINT_NUMBER_MASK ;
in_ep_size = le16_to_cpu ( ep - > wMaxPacketSize ) ;
} else if ( ( ( ep - > bEndpointAddress & USB_ENDPOINT_DIR_MASK ) = =
0 ) & &
( ( ep - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) = =
USB_ENDPOINT_XFER_BULK ) ) {
/* Out Endpoint */
out_ep_addr = ep - > bEndpointAddress ;
out_ep_addr & = USB_ENDPOINT_NUMBER_MASK ;
out_ep_size = le16_to_cpu ( ep - > wMaxPacketSize ) ;
}
}
/* check if interface is sane */
if ( ! in_ep_addr | | ! out_ep_addr ) {
dev_err ( & udev - > dev , " %s: invalid endpoint configuration \n " ,
UCAN_DRIVER_NAME ) ;
goto err_firmware_needs_update ;
}
if ( in_ep_size < sizeof ( struct ucan_message_in ) ) {
dev_err ( & udev - > dev , " %s: invalid in_ep MaxPacketSize \n " ,
UCAN_DRIVER_NAME ) ;
goto err_firmware_needs_update ;
}
if ( out_ep_size < sizeof ( struct ucan_message_out ) ) {
dev_err ( & udev - > dev , " %s: invalid out_ep MaxPacketSize \n " ,
UCAN_DRIVER_NAME ) ;
goto err_firmware_needs_update ;
}
/* Stage 2 - Device Identification
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* The device interface seems to be a ucan device . Do further
* compatibility checks . On error probing is aborted , on
* success this stage leaves the ctl_msg_buffer with the
* reported contents of a GET_INFO command ( supported
* bittimings , tx_fifo depth ) . This information is used in
* Stage 3 for the final driver initialisation .
*/
/* Prepare Memory for control transferes */
ctl_msg_buffer = devm_kzalloc ( & udev - > dev ,
sizeof ( union ucan_ctl_payload ) ,
GFP_KERNEL ) ;
if ( ! ctl_msg_buffer ) {
dev_err ( & udev - > dev ,
" %s: failed to allocate control pipe memory \n " ,
UCAN_DRIVER_NAME ) ;
return - ENOMEM ;
}
/* get protocol version
*
* note : ucan_ctrl_command_ * wrappers cannot be used yet
* because ` up ` is initialised in Stage 3
*/
ret = usb_control_msg ( udev ,
usb_rcvctrlpipe ( udev , 0 ) ,
UCAN_COMMAND_GET ,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE ,
UCAN_COMMAND_GET_PROTOCOL_VERSION ,
iface_desc - > desc . bInterfaceNumber ,
ctl_msg_buffer ,
sizeof ( union ucan_ctl_payload ) ,
UCAN_USB_CTL_PIPE_TIMEOUT ) ;
/* older firmware version do not support this command - those
* are not supported by this drive
*/
if ( ret ! = 4 ) {
dev_err ( & udev - > dev ,
" %s: could not read protocol version, ret=%d \n " ,
UCAN_DRIVER_NAME , ret ) ;
if ( ret > = 0 )
ret = - EINVAL ;
goto err_firmware_needs_update ;
}
/* this driver currently supports protocol version 3 only */
protocol_version =
le32_to_cpu ( ctl_msg_buffer - > cmd_get_protocol_version . version ) ;
if ( protocol_version < UCAN_PROTOCOL_VERSION_MIN | |
protocol_version > UCAN_PROTOCOL_VERSION_MAX ) {
dev_err ( & udev - > dev ,
" %s: device protocol version %d is not supported \n " ,
UCAN_DRIVER_NAME , protocol_version ) ;
goto err_firmware_needs_update ;
}
/* request the device information and store it in ctl_msg_buffer
*
* note : ucan_ctrl_command_ * wrappers connot be used yet
* because ` up ` is initialised in Stage 3
*/
ret = usb_control_msg ( udev ,
usb_rcvctrlpipe ( udev , 0 ) ,
UCAN_COMMAND_GET ,
USB_DIR_IN | USB_TYPE_VENDOR |
USB_RECIP_INTERFACE ,
UCAN_COMMAND_GET_INFO ,
iface_desc - > desc . bInterfaceNumber ,
ctl_msg_buffer ,
sizeof ( ctl_msg_buffer - > cmd_get_device_info ) ,
UCAN_USB_CTL_PIPE_TIMEOUT ) ;
if ( ret < 0 ) {
dev_err ( & udev - > dev , " %s: failed to retrieve device info \n " ,
UCAN_DRIVER_NAME ) ;
goto err_firmware_needs_update ;
}
if ( ret < sizeof ( ctl_msg_buffer - > cmd_get_device_info ) ) {
dev_err ( & udev - > dev , " %s: device reported invalid device info \n " ,
UCAN_DRIVER_NAME ) ;
goto err_firmware_needs_update ;
}
if ( ctl_msg_buffer - > cmd_get_device_info . tx_fifo = = 0 ) {
dev_err ( & udev - > dev ,
" %s: device reported invalid tx-fifo size \n " ,
UCAN_DRIVER_NAME ) ;
goto err_firmware_needs_update ;
}
/* Stage 3 - Driver Initialisation
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Register device to Linux , prepare private structures and
* reset the device .
*/
/* allocate driver resources */
netdev = alloc_candev ( sizeof ( struct ucan_priv ) ,
ctl_msg_buffer - > cmd_get_device_info . tx_fifo ) ;
if ( ! netdev ) {
dev_err ( & udev - > dev ,
" %s: cannot allocate candev \n " , UCAN_DRIVER_NAME ) ;
return - ENOMEM ;
}
up = netdev_priv ( netdev ) ;
/* initialze data */
up - > udev = udev ;
up - > intf = intf ;
up - > netdev = netdev ;
up - > intf_index = iface_desc - > desc . bInterfaceNumber ;
up - > in_ep_addr = in_ep_addr ;
up - > out_ep_addr = out_ep_addr ;
up - > in_ep_size = in_ep_size ;
up - > ctl_msg_buffer = ctl_msg_buffer ;
up - > context_array = NULL ;
up - > available_tx_urbs = 0 ;
up - > can . state = CAN_STATE_STOPPED ;
up - > can . bittiming_const = & up - > device_info . bittiming_const ;
up - > can . do_set_bittiming = ucan_set_bittiming ;
up - > can . do_set_mode = & ucan_set_mode ;
spin_lock_init ( & up - > context_lock ) ;
spin_lock_init ( & up - > echo_skb_lock ) ;
netdev - > netdev_ops = & ucan_netdev_ops ;
usb_set_intfdata ( intf , up ) ;
SET_NETDEV_DEV ( netdev , & intf - > dev ) ;
/* parse device information
* the data retrieved in Stage 2 is still available in
* up - > ctl_msg_buffer
*/
ucan_parse_device_info ( up , & ctl_msg_buffer - > cmd_get_device_info ) ;
/* just print some device information - if available */
ret = ucan_device_request_in ( up , UCAN_DEVICE_GET_FW_STRING , 0 ,
sizeof ( union ucan_ctl_payload ) ) ;
if ( ret > 0 ) {
/* copy string while ensuring zero terminiation */
strncpy ( firmware_str , up - > ctl_msg_buffer - > raw ,
sizeof ( union ucan_ctl_payload ) ) ;
firmware_str [ sizeof ( union ucan_ctl_payload ) ] = ' \0 ' ;
} else {
strcpy ( firmware_str , " unknown " ) ;
}
/* device is compatible, reset it */
ret = ucan_ctrl_command_out ( up , UCAN_COMMAND_RESET , 0 , 0 ) ;
if ( ret < 0 )
goto err_free_candev ;
init_usb_anchor ( & up - > rx_urbs ) ;
init_usb_anchor ( & up - > tx_urbs ) ;
up - > can . state = CAN_STATE_STOPPED ;
/* register the device */
ret = register_candev ( netdev ) ;
if ( ret )
goto err_free_candev ;
/* initialisation complete, log device info */
netdev_info ( up - > netdev , " registered device \n " ) ;
netdev_info ( up - > netdev , " firmware string: %s \n " , firmware_str ) ;
/* success */
return 0 ;
err_free_candev :
free_candev ( netdev ) ;
return ret ;
err_firmware_needs_update :
dev_err ( & udev - > dev ,
" %s: probe failed; try to update the device firmware \n " ,
UCAN_DRIVER_NAME ) ;
return - ENODEV ;
}
/* disconnect the device */
static void ucan_disconnect ( struct usb_interface * intf )
{
struct ucan_priv * up = usb_get_intfdata ( intf ) ;
usb_set_intfdata ( intf , NULL ) ;
if ( up ) {
unregister_netdev ( up - > netdev ) ;
free_candev ( up - > netdev ) ;
}
}
static struct usb_device_id ucan_table [ ] = {
/* Mule (soldered onto compute modules) */
{ USB_DEVICE_INTERFACE_NUMBER ( 0x2294 , 0x425a , 0 ) } ,
/* Seal (standalone USB stick) */
{ USB_DEVICE_INTERFACE_NUMBER ( 0x2294 , 0x425b , 0 ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , ucan_table ) ;
/* driver callbacks */
static struct usb_driver ucan_driver = {
. name = UCAN_DRIVER_NAME ,
. probe = ucan_probe ,
. disconnect = ucan_disconnect ,
. id_table = ucan_table ,
} ;
module_usb_driver ( ucan_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Martin Elshuber <martin.elshuber@theobroma-systems.com> " ) ;
MODULE_AUTHOR ( " Jakob Unterwurzacher <jakob.unterwurzacher@theobroma-systems.com> " ) ;
MODULE_DESCRIPTION ( " Driver for Theobroma Systems UCAN devices " ) ;