2016-08-03 00:06:25 +03:00
/*
* rio_cm - RapidIO Channelized Messaging Driver
*
* Copyright 2013 - 2016 Integrated Device Technology , Inc .
* Copyright ( c ) 2015 , Prodrive Technologies
* Copyright ( c ) 2015 , RapidIO Trade Association
*
* 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 .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/dma-mapping.h>
# include <linux/delay.h>
# include <linux/sched.h>
# include <linux/rio.h>
# include <linux/rio_drv.h>
# include <linux/slab.h>
# include <linux/idr.h>
# include <linux/interrupt.h>
# include <linux/cdev.h>
# include <linux/fs.h>
# include <linux/poll.h>
# include <linux/reboot.h>
# include <linux/bitops.h>
# include <linux/printk.h>
# include <linux/rio_cm_cdev.h>
# define DRV_NAME "rio_cm"
# define DRV_VERSION "1.0.0"
# define DRV_AUTHOR "Alexandre Bounine <alexandre.bounine@idt.com>"
# define DRV_DESC "RapidIO Channelized Messaging Driver"
# define DEV_NAME "rio_cm"
/* Debug output filtering masks */
enum {
DBG_NONE = 0 ,
DBG_INIT = BIT ( 0 ) , /* driver init */
DBG_EXIT = BIT ( 1 ) , /* driver exit */
DBG_MPORT = BIT ( 2 ) , /* mport add/remove */
DBG_RDEV = BIT ( 3 ) , /* RapidIO device add/remove */
DBG_CHOP = BIT ( 4 ) , /* channel operations */
DBG_WAIT = BIT ( 5 ) , /* waiting for events */
DBG_TX = BIT ( 6 ) , /* message TX */
DBG_TX_EVENT = BIT ( 7 ) , /* message TX event */
DBG_RX_DATA = BIT ( 8 ) , /* inbound data messages */
DBG_RX_CMD = BIT ( 9 ) , /* inbound REQ/ACK/NACK messages */
DBG_ALL = ~ 0 ,
} ;
# ifdef DEBUG
# define riocm_debug(level, fmt, arg...) \
do { \
if ( DBG_ # # level & dbg_level ) \
pr_debug ( DRV_NAME " : %s " fmt " \n " , \
__func__ , # # arg ) ; \
} while ( 0 )
# else
# define riocm_debug(level, fmt, arg...) \
no_printk ( KERN_DEBUG pr_fmt ( DRV_NAME fmt " \n " ) , # # arg )
# endif
# define riocm_warn(fmt, arg...) \
pr_warn ( DRV_NAME " : %s WARNING " fmt " \n " , __func__ , # # arg )
# define riocm_error(fmt, arg...) \
pr_err ( DRV_NAME " : %s ERROR " fmt " \n " , __func__ , # # arg )
static int cmbox = 1 ;
module_param ( cmbox , int , S_IRUGO ) ;
MODULE_PARM_DESC ( cmbox , " RapidIO Mailbox number (default 1) " ) ;
static int chstart = 256 ;
module_param ( chstart , int , S_IRUGO ) ;
MODULE_PARM_DESC ( chstart ,
" Start channel number for dynamic allocation (default 256) " ) ;
# ifdef DEBUG
static u32 dbg_level = DBG_NONE ;
module_param ( dbg_level , uint , S_IWUSR | S_IRUGO ) ;
MODULE_PARM_DESC ( dbg_level , " Debugging output level (default 0 = none) " ) ;
# endif
MODULE_AUTHOR ( DRV_AUTHOR ) ;
MODULE_DESCRIPTION ( DRV_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
# define RIOCM_TX_RING_SIZE 128
# define RIOCM_RX_RING_SIZE 128
# define RIOCM_CONNECT_TO 3 /* connect response TO (in sec) */
# define RIOCM_MAX_CHNUM 0xffff /* Use full range of u16 field */
# define RIOCM_CHNUM_AUTO 0
# define RIOCM_MAX_EP_COUNT 0x10000 /* Max number of endpoints */
enum rio_cm_state {
RIO_CM_IDLE ,
RIO_CM_CONNECT ,
RIO_CM_CONNECTED ,
RIO_CM_DISCONNECT ,
RIO_CM_CHAN_BOUND ,
RIO_CM_LISTEN ,
RIO_CM_DESTROYING ,
} ;
enum rio_cm_pkt_type {
RIO_CM_SYS = 0xaa ,
RIO_CM_CHAN = 0x55 ,
} ;
enum rio_cm_chop {
CM_CONN_REQ ,
CM_CONN_ACK ,
CM_CONN_CLOSE ,
CM_DATA_MSG ,
} ;
struct rio_ch_base_bhdr {
u32 src_id ;
u32 dst_id ;
# define RIO_HDR_LETTER_MASK 0xffff0000
# define RIO_HDR_MBOX_MASK 0x0000ffff
u8 src_mbox ;
u8 dst_mbox ;
u8 type ;
} __attribute__ ( ( __packed__ ) ) ;
struct rio_ch_chan_hdr {
struct rio_ch_base_bhdr bhdr ;
u8 ch_op ;
u16 dst_ch ;
u16 src_ch ;
u16 msg_len ;
u16 rsrvd ;
} __attribute__ ( ( __packed__ ) ) ;
struct tx_req {
struct list_head node ;
struct rio_dev * rdev ;
void * buffer ;
size_t len ;
} ;
struct cm_dev {
struct list_head list ;
struct rio_mport * mport ;
void * rx_buf [ RIOCM_RX_RING_SIZE ] ;
int rx_slots ;
struct mutex rx_lock ;
void * tx_buf [ RIOCM_TX_RING_SIZE ] ;
int tx_slot ;
int tx_cnt ;
int tx_ack_slot ;
struct list_head tx_reqs ;
spinlock_t tx_lock ;
struct list_head peers ;
u32 npeers ;
struct workqueue_struct * rx_wq ;
struct work_struct rx_work ;
} ;
struct chan_rx_ring {
void * buf [ RIOCM_RX_RING_SIZE ] ;
int head ;
int tail ;
int count ;
/* Tracking RX buffers reported to upper level */
void * inuse [ RIOCM_RX_RING_SIZE ] ;
int inuse_cnt ;
} ;
struct rio_channel {
u16 id ; /* local channel ID */
struct kref ref ; /* channel refcount */
struct file * filp ;
struct cm_dev * cmdev ; /* associated CM device object */
struct rio_dev * rdev ; /* remote RapidIO device */
enum rio_cm_state state ;
int error ;
spinlock_t lock ;
void * context ;
u32 loc_destid ; /* local destID */
u32 rem_destid ; /* remote destID */
u16 rem_channel ; /* remote channel ID */
struct list_head accept_queue ;
struct list_head ch_node ;
struct completion comp ;
struct completion comp_close ;
struct chan_rx_ring rx_ring ;
} ;
struct cm_peer {
struct list_head node ;
struct rio_dev * rdev ;
} ;
struct rio_cm_work {
struct work_struct work ;
struct cm_dev * cm ;
void * data ;
} ;
struct conn_req {
struct list_head node ;
u32 destid ; /* requester destID */
u16 chan ; /* requester channel ID */
struct cm_dev * cmdev ;
} ;
/*
* A channel_dev structure represents a CM_CDEV
* @ cdev Character device
* @ dev Associated device object
*/
struct channel_dev {
struct cdev cdev ;
struct device * dev ;
} ;
static struct rio_channel * riocm_ch_alloc ( u16 ch_num ) ;
static void riocm_ch_free ( struct kref * ref ) ;
static int riocm_post_send ( struct cm_dev * cm , struct rio_dev * rdev ,
void * buffer , size_t len ) ;
static int riocm_ch_close ( struct rio_channel * ch ) ;
static DEFINE_SPINLOCK ( idr_lock ) ;
static DEFINE_IDR ( ch_idr ) ;
static LIST_HEAD ( cm_dev_list ) ;
static DECLARE_RWSEM ( rdev_sem ) ;
static struct class * dev_class ;
static unsigned int dev_major ;
static unsigned int dev_minor_base ;
static dev_t dev_number ;
static struct channel_dev riocm_cdev ;
# define is_msg_capable(src_ops, dst_ops) \
( ( src_ops & RIO_SRC_OPS_DATA_MSG ) & & \
( dst_ops & RIO_DST_OPS_DATA_MSG ) )
# define dev_cm_capable(dev) \
is_msg_capable ( dev - > src_ops , dev - > dst_ops )
static int riocm_cmp ( struct rio_channel * ch , enum rio_cm_state cmp )
{
int ret ;
spin_lock_bh ( & ch - > lock ) ;
ret = ( ch - > state = = cmp ) ;
spin_unlock_bh ( & ch - > lock ) ;
return ret ;
}
static int riocm_cmp_exch ( struct rio_channel * ch ,
enum rio_cm_state cmp , enum rio_cm_state exch )
{
int ret ;
spin_lock_bh ( & ch - > lock ) ;
ret = ( ch - > state = = cmp ) ;
if ( ret )
ch - > state = exch ;
spin_unlock_bh ( & ch - > lock ) ;
return ret ;
}
static enum rio_cm_state riocm_exch ( struct rio_channel * ch ,
enum rio_cm_state exch )
{
enum rio_cm_state old ;
spin_lock_bh ( & ch - > lock ) ;
old = ch - > state ;
ch - > state = exch ;
spin_unlock_bh ( & ch - > lock ) ;
return old ;
}
static struct rio_channel * riocm_get_channel ( u16 nr )
{
struct rio_channel * ch ;
spin_lock_bh ( & idr_lock ) ;
ch = idr_find ( & ch_idr , nr ) ;
if ( ch )
kref_get ( & ch - > ref ) ;
spin_unlock_bh ( & idr_lock ) ;
return ch ;
}
static void riocm_put_channel ( struct rio_channel * ch )
{
kref_put ( & ch - > ref , riocm_ch_free ) ;
}
static void * riocm_rx_get_msg ( struct cm_dev * cm )
{
void * msg ;
int i ;
msg = rio_get_inb_message ( cm - > mport , cmbox ) ;
if ( msg ) {
for ( i = 0 ; i < RIOCM_RX_RING_SIZE ; i + + ) {
if ( cm - > rx_buf [ i ] = = msg ) {
cm - > rx_buf [ i ] = NULL ;
cm - > rx_slots + + ;
break ;
}
}
if ( i = = RIOCM_RX_RING_SIZE )
riocm_warn ( " no record for buffer 0x%p " , msg ) ;
}
return msg ;
}
/*
* riocm_rx_fill - fills a ring of receive buffers for given cm device
* @ cm : cm_dev object
* @ nent : max number of entries to fill
*
* Returns : none
*/
static void riocm_rx_fill ( struct cm_dev * cm , int nent )
{
int i ;
if ( cm - > rx_slots = = 0 )
return ;
for ( i = 0 ; i < RIOCM_RX_RING_SIZE & & cm - > rx_slots & & nent ; i + + ) {
if ( cm - > rx_buf [ i ] = = NULL ) {
cm - > rx_buf [ i ] = kmalloc ( RIO_MAX_MSG_SIZE , GFP_KERNEL ) ;
if ( cm - > rx_buf [ i ] = = NULL )
break ;
rio_add_inb_buffer ( cm - > mport , cmbox , cm - > rx_buf [ i ] ) ;
cm - > rx_slots - - ;
nent - - ;
}
}
}
/*
* riocm_rx_free - frees all receive buffers associated with given cm device
* @ cm : cm_dev object
*
* Returns : none
*/
static void riocm_rx_free ( struct cm_dev * cm )
{
int i ;
for ( i = 0 ; i < RIOCM_RX_RING_SIZE ; i + + ) {
if ( cm - > rx_buf [ i ] ! = NULL ) {
kfree ( cm - > rx_buf [ i ] ) ;
cm - > rx_buf [ i ] = NULL ;
}
}
}
/*
* riocm_req_handler - connection request handler
* @ cm : cm_dev object
* @ req_data : pointer to the request packet
*
* Returns : 0 if success , or
* - EINVAL if channel is not in correct state ,
* - ENODEV if cannot find a channel with specified ID ,
* - ENOMEM if unable to allocate memory to store the request
*/
static int riocm_req_handler ( struct cm_dev * cm , void * req_data )
{
struct rio_channel * ch ;
struct conn_req * req ;
struct rio_ch_chan_hdr * hh = req_data ;
u16 chnum ;
chnum = ntohs ( hh - > dst_ch ) ;
ch = riocm_get_channel ( chnum ) ;
if ( ! ch )
return - ENODEV ;
if ( ch - > state ! = RIO_CM_LISTEN ) {
riocm_debug ( RX_CMD , " channel %d is not in listen state " , chnum ) ;
riocm_put_channel ( ch ) ;
return - EINVAL ;
}
req = kzalloc ( sizeof ( * req ) , GFP_KERNEL ) ;
if ( ! req ) {
riocm_put_channel ( ch ) ;
return - ENOMEM ;
}
req - > destid = ntohl ( hh - > bhdr . src_id ) ;
req - > chan = ntohs ( hh - > src_ch ) ;
req - > cmdev = cm ;
spin_lock_bh ( & ch - > lock ) ;
list_add_tail ( & req - > node , & ch - > accept_queue ) ;
spin_unlock_bh ( & ch - > lock ) ;
complete ( & ch - > comp ) ;
riocm_put_channel ( ch ) ;
return 0 ;
}
/*
* riocm_resp_handler - response to connection request handler
* @ resp_data : pointer to the response packet
*
* Returns : 0 if success , or
* - EINVAL if channel is not in correct state ,
* - ENODEV if cannot find a channel with specified ID ,
*/
static int riocm_resp_handler ( void * resp_data )
{
struct rio_channel * ch ;
struct rio_ch_chan_hdr * hh = resp_data ;
u16 chnum ;
chnum = ntohs ( hh - > dst_ch ) ;
ch = riocm_get_channel ( chnum ) ;
if ( ! ch )
return - ENODEV ;
if ( ch - > state ! = RIO_CM_CONNECT ) {
riocm_put_channel ( ch ) ;
return - EINVAL ;
}
riocm_exch ( ch , RIO_CM_CONNECTED ) ;
ch - > rem_channel = ntohs ( hh - > src_ch ) ;
complete ( & ch - > comp ) ;
riocm_put_channel ( ch ) ;
return 0 ;
}
/*
* riocm_close_handler - channel close request handler
* @ req_data : pointer to the request packet
*
* Returns : 0 if success , or
* - ENODEV if cannot find a channel with specified ID ,
* + error codes returned by riocm_ch_close .
*/
static int riocm_close_handler ( void * data )
{
struct rio_channel * ch ;
struct rio_ch_chan_hdr * hh = data ;
int ret ;
riocm_debug ( RX_CMD , " for ch=%d " , ntohs ( hh - > dst_ch ) ) ;
spin_lock_bh ( & idr_lock ) ;
ch = idr_find ( & ch_idr , ntohs ( hh - > dst_ch ) ) ;
if ( ! ch ) {
spin_unlock_bh ( & idr_lock ) ;
return - ENODEV ;
}
idr_remove ( & ch_idr , ch - > id ) ;
spin_unlock_bh ( & idr_lock ) ;
riocm_exch ( ch , RIO_CM_DISCONNECT ) ;
ret = riocm_ch_close ( ch ) ;
if ( ret )
riocm_debug ( RX_CMD , " riocm_ch_close() returned %d " , ret ) ;
return 0 ;
}
/*
* rio_cm_handler - function that services request ( non - data ) packets
* @ cm : cm_dev object
* @ data : pointer to the packet
*/
static void rio_cm_handler ( struct cm_dev * cm , void * data )
{
struct rio_ch_chan_hdr * hdr ;
if ( ! rio_mport_is_running ( cm - > mport ) )
goto out ;
hdr = data ;
riocm_debug ( RX_CMD , " OP=%x for ch=%d from %d " ,
hdr - > ch_op , ntohs ( hdr - > dst_ch ) , ntohs ( hdr - > src_ch ) ) ;
switch ( hdr - > ch_op ) {
case CM_CONN_REQ :
riocm_req_handler ( cm , data ) ;
break ;
case CM_CONN_ACK :
riocm_resp_handler ( data ) ;
break ;
case CM_CONN_CLOSE :
riocm_close_handler ( data ) ;
break ;
default :
riocm_error ( " Invalid packet header " ) ;
break ;
}
out :
kfree ( data ) ;
}
/*
* rio_rx_data_handler - received data packet handler
* @ cm : cm_dev object
* @ buf : data packet
*
* Returns : 0 if success , or
* - ENODEV if cannot find a channel with specified ID ,
* - EIO if channel is not in CONNECTED state ,
* - ENOMEM if channel RX queue is full ( packet discarded )
*/
static int rio_rx_data_handler ( struct cm_dev * cm , void * buf )
{
struct rio_ch_chan_hdr * hdr ;
struct rio_channel * ch ;
hdr = buf ;
riocm_debug ( RX_DATA , " for ch=%d " , ntohs ( hdr - > dst_ch ) ) ;
ch = riocm_get_channel ( ntohs ( hdr - > dst_ch ) ) ;
if ( ! ch ) {
/* Discard data message for non-existing channel */
kfree ( buf ) ;
return - ENODEV ;
}
/* Place pointer to the buffer into channel's RX queue */
spin_lock ( & ch - > lock ) ;
if ( ch - > state ! = RIO_CM_CONNECTED ) {
/* Channel is not ready to receive data, discard a packet */
riocm_debug ( RX_DATA , " ch=%d is in wrong state=%d " ,
ch - > id , ch - > state ) ;
spin_unlock ( & ch - > lock ) ;
kfree ( buf ) ;
riocm_put_channel ( ch ) ;
return - EIO ;
}
if ( ch - > rx_ring . count = = RIOCM_RX_RING_SIZE ) {
/* If RX ring is full, discard a packet */
riocm_debug ( RX_DATA , " ch=%d is full " , ch - > id ) ;
spin_unlock ( & ch - > lock ) ;
kfree ( buf ) ;
riocm_put_channel ( ch ) ;
return - ENOMEM ;
}
ch - > rx_ring . buf [ ch - > rx_ring . head ] = buf ;
ch - > rx_ring . head + + ;
ch - > rx_ring . count + + ;
ch - > rx_ring . head % = RIOCM_RX_RING_SIZE ;
complete ( & ch - > comp ) ;
spin_unlock ( & ch - > lock ) ;
riocm_put_channel ( ch ) ;
return 0 ;
}
/*
* rio_ibmsg_handler - inbound message packet handler
*/
static void rio_ibmsg_handler ( struct work_struct * work )
{
struct cm_dev * cm = container_of ( work , struct cm_dev , rx_work ) ;
void * data ;
struct rio_ch_chan_hdr * hdr ;
if ( ! rio_mport_is_running ( cm - > mport ) )
return ;
while ( 1 ) {
mutex_lock ( & cm - > rx_lock ) ;
data = riocm_rx_get_msg ( cm ) ;
if ( data )
riocm_rx_fill ( cm , 1 ) ;
mutex_unlock ( & cm - > rx_lock ) ;
if ( data = = NULL )
break ;
hdr = data ;
if ( hdr - > bhdr . type ! = RIO_CM_CHAN ) {
/* For now simply discard packets other than channel */
riocm_error ( " Unsupported TYPE code (0x%x). Msg dropped " ,
hdr - > bhdr . type ) ;
kfree ( data ) ;
continue ;
}
/* Process a channel message */
if ( hdr - > ch_op = = CM_DATA_MSG )
rio_rx_data_handler ( cm , data ) ;
else
rio_cm_handler ( cm , data ) ;
}
}
static void riocm_inb_msg_event ( struct rio_mport * mport , void * dev_id ,
int mbox , int slot )
{
struct cm_dev * cm = dev_id ;
if ( rio_mport_is_running ( cm - > mport ) & & ! work_pending ( & cm - > rx_work ) )
queue_work ( cm - > rx_wq , & cm - > rx_work ) ;
}
/*
* rio_txcq_handler - TX completion handler
* @ cm : cm_dev object
* @ slot : TX queue slot
*
* TX completion handler also ensures that pending request packets are placed
* into transmit queue as soon as a free slot becomes available . This is done
* to give higher priority to request packets during high intensity data flow .
*/
static void rio_txcq_handler ( struct cm_dev * cm , int slot )
{
int ack_slot ;
/* ATTN: Add TX completion notification if/when direct buffer
* transfer is implemented . At this moment only correct tracking
* of tx_count is important .
*/
riocm_debug ( TX_EVENT , " for mport_%d slot %d tx_cnt %d " ,
cm - > mport - > id , slot , cm - > tx_cnt ) ;
spin_lock ( & cm - > tx_lock ) ;
ack_slot = cm - > tx_ack_slot ;
if ( ack_slot = = slot )
riocm_debug ( TX_EVENT , " slot == ack_slot " ) ;
while ( cm - > tx_cnt & & ( ( ack_slot ! = slot ) | |
( cm - > tx_cnt = = RIOCM_TX_RING_SIZE ) ) ) {
cm - > tx_buf [ ack_slot ] = NULL ;
+ + ack_slot ;
ack_slot & = ( RIOCM_TX_RING_SIZE - 1 ) ;
cm - > tx_cnt - - ;
}
if ( cm - > tx_cnt < 0 | | cm - > tx_cnt > RIOCM_TX_RING_SIZE )
riocm_error ( " tx_cnt %d out of sync " , cm - > tx_cnt ) ;
WARN_ON ( ( cm - > tx_cnt < 0 ) | | ( cm - > tx_cnt > RIOCM_TX_RING_SIZE ) ) ;
cm - > tx_ack_slot = ack_slot ;
/*
* If there are pending requests , insert them into transmit queue
*/
if ( ! list_empty ( & cm - > tx_reqs ) & & ( cm - > tx_cnt < RIOCM_TX_RING_SIZE ) ) {
struct tx_req * req , * _req ;
int rc ;
list_for_each_entry_safe ( req , _req , & cm - > tx_reqs , node ) {
list_del ( & req - > node ) ;
cm - > tx_buf [ cm - > tx_slot ] = req - > buffer ;
rc = rio_add_outb_message ( cm - > mport , req - > rdev , cmbox ,
req - > buffer , req - > len ) ;
kfree ( req - > buffer ) ;
kfree ( req ) ;
+ + cm - > tx_cnt ;
+ + cm - > tx_slot ;
cm - > tx_slot & = ( RIOCM_TX_RING_SIZE - 1 ) ;
if ( cm - > tx_cnt = = RIOCM_TX_RING_SIZE )
break ;
}
}
spin_unlock ( & cm - > tx_lock ) ;
}
static void riocm_outb_msg_event ( struct rio_mport * mport , void * dev_id ,
int mbox , int slot )
{
struct cm_dev * cm = dev_id ;
if ( cm & & rio_mport_is_running ( cm - > mport ) )
rio_txcq_handler ( cm , slot ) ;
}
static int riocm_queue_req ( struct cm_dev * cm , struct rio_dev * rdev ,
void * buffer , size_t len )
{
unsigned long flags ;
struct tx_req * treq ;
treq = kzalloc ( sizeof ( * treq ) , GFP_KERNEL ) ;
if ( treq = = NULL )
return - ENOMEM ;
treq - > rdev = rdev ;
treq - > buffer = buffer ;
treq - > len = len ;
spin_lock_irqsave ( & cm - > tx_lock , flags ) ;
list_add_tail ( & treq - > node , & cm - > tx_reqs ) ;
spin_unlock_irqrestore ( & cm - > tx_lock , flags ) ;
return 0 ;
}
/*
* riocm_post_send - helper function that places packet into msg TX queue
* @ cm : cm_dev object
* @ rdev : target RapidIO device object ( required by outbound msg interface )
* @ buffer : pointer to a packet buffer to send
* @ len : length of data to transfer
* @ req : request priority flag
*
* Returns : 0 if success , or error code otherwise .
*/
static int riocm_post_send ( struct cm_dev * cm , struct rio_dev * rdev ,
void * buffer , size_t len )
{
int rc ;
unsigned long flags ;
spin_lock_irqsave ( & cm - > tx_lock , flags ) ;
if ( cm - > mport = = NULL ) {
rc = - ENODEV ;
goto err_out ;
}
if ( cm - > tx_cnt = = RIOCM_TX_RING_SIZE ) {
riocm_debug ( TX , " Tx Queue is full " ) ;
rc = - EBUSY ;
goto err_out ;
}
cm - > tx_buf [ cm - > tx_slot ] = buffer ;
rc = rio_add_outb_message ( cm - > mport , rdev , cmbox , buffer , len ) ;
riocm_debug ( TX , " Add buf@%p destid=%x tx_slot=%d tx_cnt=%d " ,
buffer , rdev - > destid , cm - > tx_slot , cm - > tx_cnt ) ;
+ + cm - > tx_cnt ;
+ + cm - > tx_slot ;
cm - > tx_slot & = ( RIOCM_TX_RING_SIZE - 1 ) ;
err_out :
spin_unlock_irqrestore ( & cm - > tx_lock , flags ) ;
return rc ;
}
/*
* riocm_ch_send - sends a data packet to a remote device
* @ ch_id : local channel ID
* @ buf : pointer to a data buffer to send ( including CM header )
* @ len : length of data to transfer ( including CM header )
*
* ATTN : ASSUMES THAT THE HEADER SPACE IS RESERVED PART OF THE DATA PACKET
*
* Returns : 0 if success , or
* - EINVAL if one or more input parameters is / are not valid ,
* - ENODEV if cannot find a channel with specified ID ,
* - EAGAIN if a channel is not in CONNECTED state ,
* + error codes returned by HW send routine .
*/
static int riocm_ch_send ( u16 ch_id , void * buf , int len )
{
struct rio_channel * ch ;
struct rio_ch_chan_hdr * hdr ;
int ret ;
if ( buf = = NULL | | ch_id = = 0 | | len = = 0 | | len > RIO_MAX_MSG_SIZE )
return - EINVAL ;
ch = riocm_get_channel ( ch_id ) ;
if ( ! ch ) {
riocm_error ( " %s(%d) ch_%d not found " , current - > comm ,
task_pid_nr ( current ) , ch_id ) ;
return - ENODEV ;
}
if ( ! riocm_cmp ( ch , RIO_CM_CONNECTED ) ) {
ret = - EAGAIN ;
goto err_out ;
}
/*
* Fill buffer header section with corresponding channel data
*/
hdr = buf ;
hdr - > bhdr . src_id = htonl ( ch - > loc_destid ) ;
hdr - > bhdr . dst_id = htonl ( ch - > rem_destid ) ;
hdr - > bhdr . src_mbox = cmbox ;
hdr - > bhdr . dst_mbox = cmbox ;
hdr - > bhdr . type = RIO_CM_CHAN ;
hdr - > ch_op = CM_DATA_MSG ;
hdr - > dst_ch = htons ( ch - > rem_channel ) ;
hdr - > src_ch = htons ( ch - > id ) ;
hdr - > msg_len = htons ( ( u16 ) len ) ;
/* ATTN: the function call below relies on the fact that underlying
* HW - specific add_outb_message ( ) routine copies TX data into its own
* internal transfer buffer ( true for all RIONET compatible mport
* drivers ) . Must be reviewed if mport driver uses the buffer directly .
*/
ret = riocm_post_send ( ch - > cmdev , ch - > rdev , buf , len ) ;
if ( ret )
riocm_debug ( TX , " ch %d send_err=%d " , ch - > id , ret ) ;
err_out :
riocm_put_channel ( ch ) ;
return ret ;
}
static int riocm_ch_free_rxbuf ( struct rio_channel * ch , void * buf )
{
int i , ret = - EINVAL ;
spin_lock_bh ( & ch - > lock ) ;
for ( i = 0 ; i < RIOCM_RX_RING_SIZE ; i + + ) {
if ( ch - > rx_ring . inuse [ i ] = = buf ) {
ch - > rx_ring . inuse [ i ] = NULL ;
ch - > rx_ring . inuse_cnt - - ;
ret = 0 ;
break ;
}
}
spin_unlock_bh ( & ch - > lock ) ;
if ( ! ret )
kfree ( buf ) ;
return ret ;
}
/*
* riocm_ch_receive - fetch a data packet received for the specified channel
* @ ch : local channel ID
* @ buf : pointer to a packet buffer
* @ timeout : timeout to wait for incoming packet ( in jiffies )
*
* Returns : 0 and valid buffer pointer if success , or NULL pointer and one of :
* - EAGAIN if a channel is not in CONNECTED state ,
* - ENOMEM if in - use tracking queue is full ,
* - ETIME if wait timeout expired ,
* - EINTR if wait was interrupted .
*/
static int riocm_ch_receive ( struct rio_channel * ch , void * * buf , long timeout )
{
void * rxmsg = NULL ;
int i , ret = 0 ;
long wret ;
if ( ! riocm_cmp ( ch , RIO_CM_CONNECTED ) ) {
ret = - EAGAIN ;
goto out ;
}
if ( ch - > rx_ring . inuse_cnt = = RIOCM_RX_RING_SIZE ) {
/* If we do not have entries to track buffers given to upper
* layer , reject request .
*/
ret = - ENOMEM ;
goto out ;
}
wret = wait_for_completion_interruptible_timeout ( & ch - > comp , timeout ) ;
riocm_debug ( WAIT , " wait on %d returned %ld " , ch - > id , wret ) ;
if ( ! wret )
ret = - ETIME ;
else if ( wret = = - ERESTARTSYS )
ret = - EINTR ;
else
ret = riocm_cmp ( ch , RIO_CM_CONNECTED ) ? 0 : - ECONNRESET ;
if ( ret )
goto out ;
spin_lock_bh ( & ch - > lock ) ;
rxmsg = ch - > rx_ring . buf [ ch - > rx_ring . tail ] ;
ch - > rx_ring . buf [ ch - > rx_ring . tail ] = NULL ;
ch - > rx_ring . count - - ;
ch - > rx_ring . tail + + ;
ch - > rx_ring . tail % = RIOCM_RX_RING_SIZE ;
ret = - ENOMEM ;
for ( i = 0 ; i < RIOCM_RX_RING_SIZE ; i + + ) {
if ( ch - > rx_ring . inuse [ i ] = = NULL ) {
ch - > rx_ring . inuse [ i ] = rxmsg ;
ch - > rx_ring . inuse_cnt + + ;
ret = 0 ;
break ;
}
}
if ( ret ) {
/* We have no entry to store pending message: drop it */
kfree ( rxmsg ) ;
rxmsg = NULL ;
}
spin_unlock_bh ( & ch - > lock ) ;
out :
* buf = rxmsg ;
return ret ;
}
/*
* riocm_ch_connect - sends a connect request to a remote device
* @ loc_ch : local channel ID
* @ cm : CM device to send connect request
* @ peer : target RapidIO device
* @ rem_ch : remote channel ID
*
* Returns : 0 if success , or
* - EINVAL if the channel is not in IDLE state ,
* - EAGAIN if no connection request available immediately ,
* - ETIME if ACK response timeout expired ,
* - EINTR if wait for response was interrupted .
*/
static int riocm_ch_connect ( u16 loc_ch , struct cm_dev * cm ,
struct cm_peer * peer , u16 rem_ch )
{
struct rio_channel * ch = NULL ;
struct rio_ch_chan_hdr * hdr ;
int ret ;
long wret ;
ch = riocm_get_channel ( loc_ch ) ;
if ( ! ch )
return - ENODEV ;
if ( ! riocm_cmp_exch ( ch , RIO_CM_IDLE , RIO_CM_CONNECT ) ) {
ret = - EINVAL ;
goto conn_done ;
}
ch - > cmdev = cm ;
ch - > rdev = peer - > rdev ;
ch - > context = NULL ;
ch - > loc_destid = cm - > mport - > host_deviceid ;
ch - > rem_channel = rem_ch ;
/*
* Send connect request to the remote RapidIO device
*/
hdr = kzalloc ( sizeof ( * hdr ) , GFP_KERNEL ) ;
if ( hdr = = NULL ) {
ret = - ENOMEM ;
goto conn_done ;
}
hdr - > bhdr . src_id = htonl ( ch - > loc_destid ) ;
hdr - > bhdr . dst_id = htonl ( peer - > rdev - > destid ) ;
hdr - > bhdr . src_mbox = cmbox ;
hdr - > bhdr . dst_mbox = cmbox ;
hdr - > bhdr . type = RIO_CM_CHAN ;
hdr - > ch_op = CM_CONN_REQ ;
hdr - > dst_ch = htons ( rem_ch ) ;
hdr - > src_ch = htons ( loc_ch ) ;
/* ATTN: the function call below relies on the fact that underlying
* HW - specific add_outb_message ( ) routine copies TX data into its
* internal transfer buffer . Must be reviewed if mport driver uses
* this buffer directly .
*/
ret = riocm_post_send ( cm , peer - > rdev , hdr , sizeof ( * hdr ) ) ;
if ( ret ! = - EBUSY ) {
kfree ( hdr ) ;
} else {
ret = riocm_queue_req ( cm , peer - > rdev , hdr , sizeof ( * hdr ) ) ;
if ( ret )
kfree ( hdr ) ;
}
if ( ret ) {
riocm_cmp_exch ( ch , RIO_CM_CONNECT , RIO_CM_IDLE ) ;
goto conn_done ;
}
/* Wait for connect response from the remote device */
wret = wait_for_completion_interruptible_timeout ( & ch - > comp ,
RIOCM_CONNECT_TO * HZ ) ;
riocm_debug ( WAIT , " wait on %d returns %ld " , ch - > id , wret ) ;
if ( ! wret )
ret = - ETIME ;
else if ( wret = = - ERESTARTSYS )
ret = - EINTR ;
else
ret = riocm_cmp ( ch , RIO_CM_CONNECTED ) ? 0 : - 1 ;
conn_done :
riocm_put_channel ( ch ) ;
return ret ;
}
static int riocm_send_ack ( struct rio_channel * ch )
{
struct rio_ch_chan_hdr * hdr ;
int ret ;
hdr = kzalloc ( sizeof ( * hdr ) , GFP_KERNEL ) ;
if ( hdr = = NULL )
return - ENOMEM ;
hdr - > bhdr . src_id = htonl ( ch - > loc_destid ) ;
hdr - > bhdr . dst_id = htonl ( ch - > rem_destid ) ;
hdr - > dst_ch = htons ( ch - > rem_channel ) ;
hdr - > src_ch = htons ( ch - > id ) ;
hdr - > bhdr . src_mbox = cmbox ;
hdr - > bhdr . dst_mbox = cmbox ;
hdr - > bhdr . type = RIO_CM_CHAN ;
hdr - > ch_op = CM_CONN_ACK ;
/* ATTN: the function call below relies on the fact that underlying
* add_outb_message ( ) routine copies TX data into its internal transfer
* buffer . Review if switching to direct buffer version .
*/
ret = riocm_post_send ( ch - > cmdev , ch - > rdev , hdr , sizeof ( * hdr ) ) ;
if ( ret = = - EBUSY & & ! riocm_queue_req ( ch - > cmdev ,
ch - > rdev , hdr , sizeof ( * hdr ) ) )
return 0 ;
kfree ( hdr ) ;
if ( ret )
riocm_error ( " send ACK to ch_%d on %s failed (ret=%d) " ,
ch - > id , rio_name ( ch - > rdev ) , ret ) ;
return ret ;
}
/*
* riocm_ch_accept - accept incoming connection request
* @ ch_id : channel ID
* @ new_ch_id : local mport device
* @ timeout : wait timeout ( if 0 non - blocking call , do not wait if connection
* request is not available ) .
*
* Returns : pointer to new channel struct if success , or error - valued pointer :
* - ENODEV - cannot find specified channel or mport ,
* - EINVAL - the channel is not in IDLE state ,
* - EAGAIN - no connection request available immediately ( timeout = 0 ) ,
* - ENOMEM - unable to allocate new channel ,
* - ETIME - wait timeout expired ,
* - EINTR - wait was interrupted .
*/
static struct rio_channel * riocm_ch_accept ( u16 ch_id , u16 * new_ch_id ,
long timeout )
{
2016-08-11 02:27:38 +03:00
struct rio_channel * ch ;
struct rio_channel * new_ch ;
2016-08-03 00:06:25 +03:00
struct conn_req * req ;
struct cm_peer * peer ;
int found = 0 ;
int err = 0 ;
long wret ;
ch = riocm_get_channel ( ch_id ) ;
if ( ! ch )
return ERR_PTR ( - EINVAL ) ;
if ( ! riocm_cmp ( ch , RIO_CM_LISTEN ) ) {
err = - EINVAL ;
goto err_put ;
}
/* Don't sleep if this is a non blocking call */
if ( ! timeout ) {
if ( ! try_wait_for_completion ( & ch - > comp ) ) {
err = - EAGAIN ;
goto err_put ;
}
} else {
riocm_debug ( WAIT , " on %d " , ch - > id ) ;
wret = wait_for_completion_interruptible_timeout ( & ch - > comp ,
timeout ) ;
if ( ! wret ) {
err = - ETIME ;
goto err_put ;
} else if ( wret = = - ERESTARTSYS ) {
err = - EINTR ;
goto err_put ;
}
}
spin_lock_bh ( & ch - > lock ) ;
if ( ch - > state ! = RIO_CM_LISTEN ) {
err = - ECANCELED ;
} else if ( list_empty ( & ch - > accept_queue ) ) {
riocm_debug ( WAIT , " on %d accept_queue is empty on completion " ,
ch - > id ) ;
err = - EIO ;
}
spin_unlock_bh ( & ch - > lock ) ;
if ( err ) {
riocm_debug ( WAIT , " on %d returns %d " , ch - > id , err ) ;
goto err_put ;
}
/* Create new channel for this connection */
new_ch = riocm_ch_alloc ( RIOCM_CHNUM_AUTO ) ;
if ( IS_ERR ( new_ch ) ) {
riocm_error ( " failed to get channel for new req (%ld) " ,
PTR_ERR ( new_ch ) ) ;
err = - ENOMEM ;
goto err_put ;
}
spin_lock_bh ( & ch - > lock ) ;
req = list_first_entry ( & ch - > accept_queue , struct conn_req , node ) ;
list_del ( & req - > node ) ;
new_ch - > cmdev = ch - > cmdev ;
new_ch - > loc_destid = ch - > loc_destid ;
new_ch - > rem_destid = req - > destid ;
new_ch - > rem_channel = req - > chan ;
spin_unlock_bh ( & ch - > lock ) ;
riocm_put_channel ( ch ) ;
2016-08-11 02:27:38 +03:00
ch = NULL ;
2016-08-03 00:06:25 +03:00
kfree ( req ) ;
down_read ( & rdev_sem ) ;
/* Find requester's device object */
list_for_each_entry ( peer , & new_ch - > cmdev - > peers , node ) {
if ( peer - > rdev - > destid = = new_ch - > rem_destid ) {
riocm_debug ( RX_CMD , " found matching device(%s) " ,
rio_name ( peer - > rdev ) ) ;
found = 1 ;
break ;
}
}
up_read ( & rdev_sem ) ;
if ( ! found ) {
/* If peer device object not found, simply ignore the request */
err = - ENODEV ;
2016-08-11 02:27:38 +03:00
goto err_put_new_ch ;
2016-08-03 00:06:25 +03:00
}
new_ch - > rdev = peer - > rdev ;
new_ch - > state = RIO_CM_CONNECTED ;
spin_lock_init ( & new_ch - > lock ) ;
/* Acknowledge the connection request. */
riocm_send_ack ( new_ch ) ;
* new_ch_id = new_ch - > id ;
return new_ch ;
2016-08-11 02:27:38 +03:00
err_put_new_ch :
spin_lock_bh ( & idr_lock ) ;
idr_remove ( & ch_idr , new_ch - > id ) ;
spin_unlock_bh ( & idr_lock ) ;
riocm_put_channel ( new_ch ) ;
2016-08-03 00:06:25 +03:00
err_put :
2016-08-11 02:27:38 +03:00
if ( ch )
riocm_put_channel ( ch ) ;
2016-08-03 00:06:25 +03:00
* new_ch_id = 0 ;
return ERR_PTR ( err ) ;
}
/*
* riocm_ch_listen - puts a channel into LISTEN state
* @ ch_id : channel ID
*
* Returns : 0 if success , or
* - EINVAL if the specified channel does not exists or
* is not in CHAN_BOUND state .
*/
static int riocm_ch_listen ( u16 ch_id )
{
struct rio_channel * ch = NULL ;
int ret = 0 ;
riocm_debug ( CHOP , " (ch_%d) " , ch_id ) ;
ch = riocm_get_channel ( ch_id ) ;
if ( ! ch | | ! riocm_cmp_exch ( ch , RIO_CM_CHAN_BOUND , RIO_CM_LISTEN ) )
ret = - EINVAL ;
riocm_put_channel ( ch ) ;
return ret ;
}
/*
* riocm_ch_bind - associate a channel object and an mport device
* @ ch_id : channel ID
* @ mport_id : local mport device ID
* @ context : pointer to the additional caller ' s context
*
* Returns : 0 if success , or
* - ENODEV if cannot find specified mport ,
* - EINVAL if the specified channel does not exist or
* is not in IDLE state .
*/
static int riocm_ch_bind ( u16 ch_id , u8 mport_id , void * context )
{
struct rio_channel * ch = NULL ;
struct cm_dev * cm ;
int rc = - ENODEV ;
riocm_debug ( CHOP , " ch_%d to mport_%d " , ch_id , mport_id ) ;
/* Find matching cm_dev object */
down_read ( & rdev_sem ) ;
list_for_each_entry ( cm , & cm_dev_list , list ) {
if ( ( cm - > mport - > id = = mport_id ) & &
rio_mport_is_running ( cm - > mport ) ) {
rc = 0 ;
break ;
}
}
if ( rc )
goto exit ;
ch = riocm_get_channel ( ch_id ) ;
if ( ! ch ) {
rc = - EINVAL ;
goto exit ;
}
spin_lock_bh ( & ch - > lock ) ;
if ( ch - > state ! = RIO_CM_IDLE ) {
spin_unlock_bh ( & ch - > lock ) ;
rc = - EINVAL ;
goto err_put ;
}
ch - > cmdev = cm ;
ch - > loc_destid = cm - > mport - > host_deviceid ;
ch - > context = context ;
ch - > state = RIO_CM_CHAN_BOUND ;
spin_unlock_bh ( & ch - > lock ) ;
err_put :
riocm_put_channel ( ch ) ;
exit :
up_read ( & rdev_sem ) ;
return rc ;
}
/*
* riocm_ch_alloc - channel object allocation helper routine
* @ ch_num : channel ID ( 1 . . . RIOCM_MAX_CHNUM , 0 = automatic )
*
* Return value : pointer to newly created channel object ,
* or error - valued pointer
*/
static struct rio_channel * riocm_ch_alloc ( u16 ch_num )
{
int id ;
int start , end ;
struct rio_channel * ch ;
ch = kzalloc ( sizeof ( * ch ) , GFP_KERNEL ) ;
if ( ! ch )
return ERR_PTR ( - ENOMEM ) ;
if ( ch_num ) {
/* If requested, try to obtain the specified channel ID */
start = ch_num ;
end = ch_num + 1 ;
} else {
/* Obtain channel ID from the dynamic allocation range */
start = chstart ;
end = RIOCM_MAX_CHNUM + 1 ;
}
idr_preload ( GFP_KERNEL ) ;
spin_lock_bh ( & idr_lock ) ;
id = idr_alloc_cyclic ( & ch_idr , ch , start , end , GFP_NOWAIT ) ;
spin_unlock_bh ( & idr_lock ) ;
idr_preload_end ( ) ;
if ( id < 0 ) {
kfree ( ch ) ;
return ERR_PTR ( id = = - ENOSPC ? - EBUSY : id ) ;
}
ch - > id = ( u16 ) id ;
ch - > state = RIO_CM_IDLE ;
spin_lock_init ( & ch - > lock ) ;
INIT_LIST_HEAD ( & ch - > accept_queue ) ;
INIT_LIST_HEAD ( & ch - > ch_node ) ;
init_completion ( & ch - > comp ) ;
init_completion ( & ch - > comp_close ) ;
kref_init ( & ch - > ref ) ;
ch - > rx_ring . head = 0 ;
ch - > rx_ring . tail = 0 ;
ch - > rx_ring . count = 0 ;
ch - > rx_ring . inuse_cnt = 0 ;
return ch ;
}
/*
* riocm_ch_create - creates a new channel object and allocates ID for it
* @ ch_num : channel ID ( 1 . . . RIOCM_MAX_CHNUM , 0 = automatic )
*
* Allocates and initializes a new channel object . If the parameter ch_num > 0
* and is within the valid range , riocm_ch_create tries to allocate the
* specified ID for the new channel . If ch_num = 0 , channel ID will be assigned
* automatically from the range ( chstart . . . RIOCM_MAX_CHNUM ) .
* Module parameter ' chstart ' defines start of an ID range available for dynamic
* allocation . Range below ' chstart ' is reserved for pre - defined ID numbers .
* Available channel numbers are limited by 16 - bit size of channel numbers used
* in the packet header .
*
* Return value : PTR to rio_channel structure if successful ( with channel number
* updated via pointer ) or error - valued pointer if error .
*/
static struct rio_channel * riocm_ch_create ( u16 * ch_num )
{
struct rio_channel * ch = NULL ;
ch = riocm_ch_alloc ( * ch_num ) ;
if ( IS_ERR ( ch ) )
riocm_debug ( CHOP , " Failed to allocate channel %d (err=%ld) " ,
* ch_num , PTR_ERR ( ch ) ) ;
else
* ch_num = ch - > id ;
return ch ;
}
/*
* riocm_ch_free - channel object release routine
* @ ref : pointer to a channel ' s kref structure
*/
static void riocm_ch_free ( struct kref * ref )
{
struct rio_channel * ch = container_of ( ref , struct rio_channel , ref ) ;
int i ;
riocm_debug ( CHOP , " (ch_%d) " , ch - > id ) ;
if ( ch - > rx_ring . inuse_cnt ) {
for ( i = 0 ;
i < RIOCM_RX_RING_SIZE & & ch - > rx_ring . inuse_cnt ; i + + ) {
if ( ch - > rx_ring . inuse [ i ] ! = NULL ) {
kfree ( ch - > rx_ring . inuse [ i ] ) ;
ch - > rx_ring . inuse_cnt - - ;
}
}
}
if ( ch - > rx_ring . count )
for ( i = 0 ; i < RIOCM_RX_RING_SIZE & & ch - > rx_ring . count ; i + + ) {
if ( ch - > rx_ring . buf [ i ] ! = NULL ) {
kfree ( ch - > rx_ring . buf [ i ] ) ;
ch - > rx_ring . count - - ;
}
}
complete ( & ch - > comp_close ) ;
}
static int riocm_send_close ( struct rio_channel * ch )
{
struct rio_ch_chan_hdr * hdr ;
int ret ;
/*
* Send CH_CLOSE notification to the remote RapidIO device
*/
hdr = kzalloc ( sizeof ( * hdr ) , GFP_KERNEL ) ;
if ( hdr = = NULL )
return - ENOMEM ;
hdr - > bhdr . src_id = htonl ( ch - > loc_destid ) ;
hdr - > bhdr . dst_id = htonl ( ch - > rem_destid ) ;
hdr - > bhdr . src_mbox = cmbox ;
hdr - > bhdr . dst_mbox = cmbox ;
hdr - > bhdr . type = RIO_CM_CHAN ;
hdr - > ch_op = CM_CONN_CLOSE ;
hdr - > dst_ch = htons ( ch - > rem_channel ) ;
hdr - > src_ch = htons ( ch - > id ) ;
/* ATTN: the function call below relies on the fact that underlying
* add_outb_message ( ) routine copies TX data into its internal transfer
* buffer . Needs to be reviewed if switched to direct buffer mode .
*/
ret = riocm_post_send ( ch - > cmdev , ch - > rdev , hdr , sizeof ( * hdr ) ) ;
if ( ret = = - EBUSY & & ! riocm_queue_req ( ch - > cmdev , ch - > rdev ,
hdr , sizeof ( * hdr ) ) )
return 0 ;
kfree ( hdr ) ;
if ( ret )
riocm_error ( " ch(%d) send CLOSE failed (ret=%d) " , ch - > id , ret ) ;
return ret ;
}
/*
* riocm_ch_close - closes a channel object with specified ID ( by local request )
* @ ch : channel to be closed
*/
static int riocm_ch_close ( struct rio_channel * ch )
{
unsigned long tmo = msecs_to_jiffies ( 3000 ) ;
enum rio_cm_state state ;
long wret ;
int ret = 0 ;
riocm_debug ( CHOP , " ch_%d by %s(%d) " ,
ch - > id , current - > comm , task_pid_nr ( current ) ) ;
state = riocm_exch ( ch , RIO_CM_DESTROYING ) ;
if ( state = = RIO_CM_CONNECTED )
riocm_send_close ( ch ) ;
complete_all ( & ch - > comp ) ;
riocm_put_channel ( ch ) ;
wret = wait_for_completion_interruptible_timeout ( & ch - > comp_close , tmo ) ;
riocm_debug ( WAIT , " wait on %d returns %ld " , ch - > id , wret ) ;
if ( wret = = 0 ) {
/* Timeout on wait occurred */
riocm_debug ( CHOP , " %s(%d) timed out waiting for ch %d " ,
current - > comm , task_pid_nr ( current ) , ch - > id ) ;
ret = - ETIMEDOUT ;
} else if ( wret = = - ERESTARTSYS ) {
/* Wait_for_completion was interrupted by a signal */
riocm_debug ( CHOP , " %s(%d) wait for ch %d was interrupted " ,
current - > comm , task_pid_nr ( current ) , ch - > id ) ;
ret = - EINTR ;
}
if ( ! ret ) {
riocm_debug ( CHOP , " ch_%d resources released " , ch - > id ) ;
kfree ( ch ) ;
} else {
riocm_debug ( CHOP , " failed to release ch_%d resources " , ch - > id ) ;
}
return ret ;
}
/*
* riocm_cdev_open ( ) - Open character device
*/
static int riocm_cdev_open ( struct inode * inode , struct file * filp )
{
riocm_debug ( INIT , " by %s(%d) filp=%p " ,
current - > comm , task_pid_nr ( current ) , filp ) ;
if ( list_empty ( & cm_dev_list ) )
return - ENODEV ;
return 0 ;
}
/*
* riocm_cdev_release ( ) - Release character device
*/
static int riocm_cdev_release ( struct inode * inode , struct file * filp )
{
struct rio_channel * ch , * _c ;
unsigned int i ;
LIST_HEAD ( list ) ;
riocm_debug ( EXIT , " by %s(%d) filp=%p " ,
current - > comm , task_pid_nr ( current ) , filp ) ;
/* Check if there are channels associated with this file descriptor */
spin_lock_bh ( & idr_lock ) ;
idr_for_each_entry ( & ch_idr , ch , i ) {
if ( ch & & ch - > filp = = filp ) {
riocm_debug ( EXIT , " ch_%d not released by %s(%d) " ,
ch - > id , current - > comm ,
task_pid_nr ( current ) ) ;
idr_remove ( & ch_idr , ch - > id ) ;
list_add ( & ch - > ch_node , & list ) ;
}
}
spin_unlock_bh ( & idr_lock ) ;
if ( ! list_empty ( & list ) ) {
list_for_each_entry_safe ( ch , _c , & list , ch_node ) {
list_del ( & ch - > ch_node ) ;
riocm_ch_close ( ch ) ;
}
}
return 0 ;
}
/*
* cm_ep_get_list_size ( ) - Reports number of endpoints in the network
*/
static int cm_ep_get_list_size ( void __user * arg )
{
u32 __user * p = arg ;
u32 mport_id ;
u32 count = 0 ;
struct cm_dev * cm ;
if ( get_user ( mport_id , p ) )
return - EFAULT ;
if ( mport_id > = RIO_MAX_MPORTS )
return - EINVAL ;
/* Find a matching cm_dev object */
down_read ( & rdev_sem ) ;
list_for_each_entry ( cm , & cm_dev_list , list ) {
if ( cm - > mport - > id = = mport_id ) {
count = cm - > npeers ;
up_read ( & rdev_sem ) ;
if ( copy_to_user ( arg , & count , sizeof ( u32 ) ) )
return - EFAULT ;
return 0 ;
}
}
up_read ( & rdev_sem ) ;
return - ENODEV ;
}
/*
* cm_ep_get_list ( ) - Returns list of attached endpoints
*/
static int cm_ep_get_list ( void __user * arg )
{
struct cm_dev * cm ;
struct cm_peer * peer ;
u32 info [ 2 ] ;
void * buf ;
u32 nent ;
u32 * entry_ptr ;
u32 i = 0 ;
int ret = 0 ;
if ( copy_from_user ( & info , arg , sizeof ( info ) ) )
return - EFAULT ;
if ( info [ 1 ] > = RIO_MAX_MPORTS | | info [ 0 ] > RIOCM_MAX_EP_COUNT )
return - EINVAL ;
/* Find a matching cm_dev object */
down_read ( & rdev_sem ) ;
list_for_each_entry ( cm , & cm_dev_list , list )
if ( cm - > mport - > id = = ( u8 ) info [ 1 ] )
goto found ;
up_read ( & rdev_sem ) ;
return - ENODEV ;
found :
nent = min ( info [ 0 ] , cm - > npeers ) ;
buf = kcalloc ( nent + 2 , sizeof ( u32 ) , GFP_KERNEL ) ;
if ( ! buf ) {
up_read ( & rdev_sem ) ;
return - ENOMEM ;
}
entry_ptr = ( u32 * ) ( ( uintptr_t ) buf + 2 * sizeof ( u32 ) ) ;
list_for_each_entry ( peer , & cm - > peers , node ) {
* entry_ptr = ( u32 ) peer - > rdev - > destid ;
entry_ptr + + ;
if ( + + i = = nent )
break ;
}
up_read ( & rdev_sem ) ;
( ( u32 * ) buf ) [ 0 ] = i ; /* report an updated number of entries */
( ( u32 * ) buf ) [ 1 ] = info [ 1 ] ; /* put back an mport ID */
if ( copy_to_user ( arg , buf , sizeof ( u32 ) * ( info [ 0 ] + 2 ) ) )
ret = - EFAULT ;
kfree ( buf ) ;
return ret ;
}
/*
* cm_mport_get_list ( ) - Returns list of available local mport devices
*/
static int cm_mport_get_list ( void __user * arg )
{
int ret = 0 ;
u32 entries ;
void * buf ;
struct cm_dev * cm ;
u32 * entry_ptr ;
int count = 0 ;
if ( copy_from_user ( & entries , arg , sizeof ( entries ) ) )
return - EFAULT ;
if ( entries = = 0 | | entries > RIO_MAX_MPORTS )
return - EINVAL ;
buf = kcalloc ( entries + 1 , sizeof ( u32 ) , GFP_KERNEL ) ;
if ( ! buf )
return - ENOMEM ;
/* Scan all registered cm_dev objects */
entry_ptr = ( u32 * ) ( ( uintptr_t ) buf + sizeof ( u32 ) ) ;
down_read ( & rdev_sem ) ;
list_for_each_entry ( cm , & cm_dev_list , list ) {
if ( count + + < entries ) {
* entry_ptr = ( cm - > mport - > id < < 16 ) |
cm - > mport - > host_deviceid ;
entry_ptr + + ;
}
}
up_read ( & rdev_sem ) ;
* ( ( u32 * ) buf ) = count ; /* report a real number of entries */
if ( copy_to_user ( arg , buf , sizeof ( u32 ) * ( count + 1 ) ) )
ret = - EFAULT ;
kfree ( buf ) ;
return ret ;
}
/*
* cm_chan_create ( ) - Create a message exchange channel
*/
static int cm_chan_create ( struct file * filp , void __user * arg )
{
u16 __user * p = arg ;
u16 ch_num ;
struct rio_channel * ch ;
if ( get_user ( ch_num , p ) )
return - EFAULT ;
riocm_debug ( CHOP , " ch_%d requested by %s(%d) " ,
ch_num , current - > comm , task_pid_nr ( current ) ) ;
ch = riocm_ch_create ( & ch_num ) ;
if ( IS_ERR ( ch ) )
return PTR_ERR ( ch ) ;
ch - > filp = filp ;
riocm_debug ( CHOP , " ch_%d created by %s(%d) " ,
ch_num , current - > comm , task_pid_nr ( current ) ) ;
return put_user ( ch_num , p ) ;
}
/*
* cm_chan_close ( ) - Close channel
* @ filp : Pointer to file object
* @ arg : Channel to close
*/
static int cm_chan_close ( struct file * filp , void __user * arg )
{
u16 __user * p = arg ;
u16 ch_num ;
struct rio_channel * ch ;
if ( get_user ( ch_num , p ) )
return - EFAULT ;
riocm_debug ( CHOP , " ch_%d by %s(%d) " ,
ch_num , current - > comm , task_pid_nr ( current ) ) ;
spin_lock_bh ( & idr_lock ) ;
ch = idr_find ( & ch_idr , ch_num ) ;
if ( ! ch ) {
spin_unlock_bh ( & idr_lock ) ;
return 0 ;
}
if ( ch - > filp ! = filp ) {
spin_unlock_bh ( & idr_lock ) ;
return - EINVAL ;
}
idr_remove ( & ch_idr , ch - > id ) ;
spin_unlock_bh ( & idr_lock ) ;
return riocm_ch_close ( ch ) ;
}
/*
* cm_chan_bind ( ) - Bind channel
* @ arg : Channel number
*/
static int cm_chan_bind ( void __user * arg )
{
struct rio_cm_channel chan ;
if ( copy_from_user ( & chan , arg , sizeof ( chan ) ) )
return - EFAULT ;
if ( chan . mport_id > = RIO_MAX_MPORTS )
return - EINVAL ;
return riocm_ch_bind ( chan . id , chan . mport_id , NULL ) ;
}
/*
* cm_chan_listen ( ) - Listen on channel
* @ arg : Channel number
*/
static int cm_chan_listen ( void __user * arg )
{
u16 __user * p = arg ;
u16 ch_num ;
if ( get_user ( ch_num , p ) )
return - EFAULT ;
return riocm_ch_listen ( ch_num ) ;
}
/*
* cm_chan_accept ( ) - Accept incoming connection
* @ filp : Pointer to file object
* @ arg : Channel number
*/
static int cm_chan_accept ( struct file * filp , void __user * arg )
{
struct rio_cm_accept param ;
long accept_to ;
struct rio_channel * ch ;
if ( copy_from_user ( & param , arg , sizeof ( param ) ) )
return - EFAULT ;
riocm_debug ( CHOP , " on ch_%d by %s(%d) " ,
param . ch_num , current - > comm , task_pid_nr ( current ) ) ;
accept_to = param . wait_to ?
msecs_to_jiffies ( param . wait_to ) : 0 ;
ch = riocm_ch_accept ( param . ch_num , & param . ch_num , accept_to ) ;
if ( IS_ERR ( ch ) )
return PTR_ERR ( ch ) ;
ch - > filp = filp ;
riocm_debug ( CHOP , " new ch_%d for %s(%d) " ,
ch - > id , current - > comm , task_pid_nr ( current ) ) ;
if ( copy_to_user ( arg , & param , sizeof ( param ) ) )
return - EFAULT ;
return 0 ;
}
/*
* cm_chan_connect ( ) - Connect on channel
* @ arg : Channel information
*/
static int cm_chan_connect ( void __user * arg )
{
struct rio_cm_channel chan ;
struct cm_dev * cm ;
struct cm_peer * peer ;
int ret = - ENODEV ;
if ( copy_from_user ( & chan , arg , sizeof ( chan ) ) )
return - EFAULT ;
if ( chan . mport_id > = RIO_MAX_MPORTS )
return - EINVAL ;
down_read ( & rdev_sem ) ;
/* Find matching cm_dev object */
list_for_each_entry ( cm , & cm_dev_list , list ) {
if ( cm - > mport - > id = = chan . mport_id ) {
ret = 0 ;
break ;
}
}
if ( ret )
goto err_out ;
if ( chan . remote_destid > = RIO_ANY_DESTID ( cm - > mport - > sys_size ) ) {
ret = - EINVAL ;
goto err_out ;
}
/* Find corresponding RapidIO endpoint device object */
ret = - ENODEV ;
list_for_each_entry ( peer , & cm - > peers , node ) {
if ( peer - > rdev - > destid = = chan . remote_destid ) {
ret = 0 ;
break ;
}
}
if ( ret )
goto err_out ;
up_read ( & rdev_sem ) ;
return riocm_ch_connect ( chan . id , cm , peer , chan . remote_channel ) ;
err_out :
up_read ( & rdev_sem ) ;
return ret ;
}
/*
* cm_chan_msg_send ( ) - Send a message through channel
* @ arg : Outbound message information
*/
static int cm_chan_msg_send ( void __user * arg )
{
struct rio_cm_msg msg ;
void * buf ;
2016-10-11 23:53:49 +03:00
int ret ;
2016-08-03 00:06:25 +03:00
if ( copy_from_user ( & msg , arg , sizeof ( msg ) ) )
return - EFAULT ;
if ( msg . size > RIO_MAX_MSG_SIZE )
return - EINVAL ;
2016-10-11 23:53:49 +03:00
buf = memdup_user ( ( void __user * ) ( uintptr_t ) msg . msg , msg . size ) ;
if ( IS_ERR ( buf ) )
return PTR_ERR ( buf ) ;
2016-08-03 00:06:25 +03:00
ret = riocm_ch_send ( msg . ch_num , buf , msg . size ) ;
2016-10-11 23:53:49 +03:00
2016-08-03 00:06:25 +03:00
kfree ( buf ) ;
return ret ;
}
/*
* cm_chan_msg_rcv ( ) - Receive a message through channel
* @ arg : Inbound message information
*/
static int cm_chan_msg_rcv ( void __user * arg )
{
struct rio_cm_msg msg ;
struct rio_channel * ch ;
void * buf ;
long rxto ;
int ret = 0 , msg_size ;
if ( copy_from_user ( & msg , arg , sizeof ( msg ) ) )
return - EFAULT ;
if ( msg . ch_num = = 0 | | msg . size = = 0 )
return - EINVAL ;
ch = riocm_get_channel ( msg . ch_num ) ;
if ( ! ch )
return - ENODEV ;
rxto = msg . rxto ? msecs_to_jiffies ( msg . rxto ) : MAX_SCHEDULE_TIMEOUT ;
ret = riocm_ch_receive ( ch , & buf , rxto ) ;
if ( ret )
goto out ;
msg_size = min ( msg . size , ( u16 ) ( RIO_MAX_MSG_SIZE ) ) ;
if ( copy_to_user ( ( void __user * ) ( uintptr_t ) msg . msg , buf , msg_size ) )
ret = - EFAULT ;
riocm_ch_free_rxbuf ( ch , buf ) ;
out :
riocm_put_channel ( ch ) ;
return ret ;
}
/*
* riocm_cdev_ioctl ( ) - IOCTL requests handler
*/
static long
riocm_cdev_ioctl ( struct file * filp , unsigned int cmd , unsigned long arg )
{
switch ( cmd ) {
case RIO_CM_EP_GET_LIST_SIZE :
return cm_ep_get_list_size ( ( void __user * ) arg ) ;
case RIO_CM_EP_GET_LIST :
return cm_ep_get_list ( ( void __user * ) arg ) ;
case RIO_CM_CHAN_CREATE :
return cm_chan_create ( filp , ( void __user * ) arg ) ;
case RIO_CM_CHAN_CLOSE :
return cm_chan_close ( filp , ( void __user * ) arg ) ;
case RIO_CM_CHAN_BIND :
return cm_chan_bind ( ( void __user * ) arg ) ;
case RIO_CM_CHAN_LISTEN :
return cm_chan_listen ( ( void __user * ) arg ) ;
case RIO_CM_CHAN_ACCEPT :
return cm_chan_accept ( filp , ( void __user * ) arg ) ;
case RIO_CM_CHAN_CONNECT :
return cm_chan_connect ( ( void __user * ) arg ) ;
case RIO_CM_CHAN_SEND :
return cm_chan_msg_send ( ( void __user * ) arg ) ;
case RIO_CM_CHAN_RECEIVE :
return cm_chan_msg_rcv ( ( void __user * ) arg ) ;
case RIO_CM_MPORT_GET_LIST :
return cm_mport_get_list ( ( void __user * ) arg ) ;
default :
break ;
}
return - EINVAL ;
}
static const struct file_operations riocm_cdev_fops = {
. owner = THIS_MODULE ,
. open = riocm_cdev_open ,
. release = riocm_cdev_release ,
. unlocked_ioctl = riocm_cdev_ioctl ,
} ;
/*
* riocm_add_dev - add new remote RapidIO device into channel management core
* @ dev : device object associated with RapidIO device
* @ sif : subsystem interface
*
* Adds the specified RapidIO device ( if applicable ) into peers list of
* the corresponding channel management device ( cm_dev ) .
*/
static int riocm_add_dev ( struct device * dev , struct subsys_interface * sif )
{
struct cm_peer * peer ;
struct rio_dev * rdev = to_rio_dev ( dev ) ;
struct cm_dev * cm ;
/* Check if the remote device has capabilities required to support CM */
if ( ! dev_cm_capable ( rdev ) )
return 0 ;
riocm_debug ( RDEV , " (%s) " , rio_name ( rdev ) ) ;
peer = kmalloc ( sizeof ( * peer ) , GFP_KERNEL ) ;
if ( ! peer )
return - ENOMEM ;
/* Find a corresponding cm_dev object */
down_write ( & rdev_sem ) ;
list_for_each_entry ( cm , & cm_dev_list , list ) {
if ( cm - > mport = = rdev - > net - > hport )
goto found ;
}
up_write ( & rdev_sem ) ;
kfree ( peer ) ;
return - ENODEV ;
found :
peer - > rdev = rdev ;
list_add_tail ( & peer - > node , & cm - > peers ) ;
cm - > npeers + + ;
up_write ( & rdev_sem ) ;
return 0 ;
}
/*
* riocm_remove_dev - remove remote RapidIO device from channel management core
* @ dev : device object associated with RapidIO device
* @ sif : subsystem interface
*
* Removes the specified RapidIO device ( if applicable ) from peers list of
* the corresponding channel management device ( cm_dev ) .
*/
static void riocm_remove_dev ( struct device * dev , struct subsys_interface * sif )
{
struct rio_dev * rdev = to_rio_dev ( dev ) ;
struct cm_dev * cm ;
struct cm_peer * peer ;
struct rio_channel * ch , * _c ;
unsigned int i ;
bool found = false ;
LIST_HEAD ( list ) ;
/* Check if the remote device has capabilities required to support CM */
if ( ! dev_cm_capable ( rdev ) )
return ;
riocm_debug ( RDEV , " (%s) " , rio_name ( rdev ) ) ;
/* Find matching cm_dev object */
down_write ( & rdev_sem ) ;
list_for_each_entry ( cm , & cm_dev_list , list ) {
if ( cm - > mport = = rdev - > net - > hport ) {
found = true ;
break ;
}
}
if ( ! found ) {
up_write ( & rdev_sem ) ;
return ;
}
/* Remove remote device from the list of peers */
found = false ;
list_for_each_entry ( peer , & cm - > peers , node ) {
if ( peer - > rdev = = rdev ) {
riocm_debug ( RDEV , " removing peer %s " , rio_name ( rdev ) ) ;
found = true ;
list_del ( & peer - > node ) ;
cm - > npeers - - ;
kfree ( peer ) ;
break ;
}
}
up_write ( & rdev_sem ) ;
if ( ! found )
return ;
/*
* Release channels associated with this peer
*/
spin_lock_bh ( & idr_lock ) ;
idr_for_each_entry ( & ch_idr , ch , i ) {
if ( ch & & ch - > rdev = = rdev ) {
if ( atomic_read ( & rdev - > state ) ! = RIO_DEVICE_SHUTDOWN )
riocm_exch ( ch , RIO_CM_DISCONNECT ) ;
idr_remove ( & ch_idr , ch - > id ) ;
list_add ( & ch - > ch_node , & list ) ;
}
}
spin_unlock_bh ( & idr_lock ) ;
if ( ! list_empty ( & list ) ) {
list_for_each_entry_safe ( ch , _c , & list , ch_node ) {
list_del ( & ch - > ch_node ) ;
riocm_ch_close ( ch ) ;
}
}
}
/*
* riocm_cdev_add ( ) - Create rio_cm char device
* @ devno : device number assigned to device ( MAJ + MIN )
*/
static int riocm_cdev_add ( dev_t devno )
{
int ret ;
cdev_init ( & riocm_cdev . cdev , & riocm_cdev_fops ) ;
riocm_cdev . cdev . owner = THIS_MODULE ;
ret = cdev_add ( & riocm_cdev . cdev , devno , 1 ) ;
if ( ret < 0 ) {
riocm_error ( " Cannot register a device with error %d " , ret ) ;
return ret ;
}
riocm_cdev . dev = device_create ( dev_class , NULL , devno , NULL , DEV_NAME ) ;
if ( IS_ERR ( riocm_cdev . dev ) ) {
cdev_del ( & riocm_cdev . cdev ) ;
return PTR_ERR ( riocm_cdev . dev ) ;
}
riocm_debug ( MPORT , " Added %s cdev(%d:%d) " ,
DEV_NAME , MAJOR ( devno ) , MINOR ( devno ) ) ;
return 0 ;
}
/*
* riocm_add_mport - add new local mport device into channel management core
* @ dev : device object associated with mport
* @ class_intf : class interface
*
* When a new mport device is added , CM immediately reserves inbound and
* outbound RapidIO mailboxes that will be used .
*/
static int riocm_add_mport ( struct device * dev ,
struct class_interface * class_intf )
{
int rc ;
int i ;
struct cm_dev * cm ;
struct rio_mport * mport = to_rio_mport ( dev ) ;
riocm_debug ( MPORT , " add mport %s " , mport - > name ) ;
cm = kzalloc ( sizeof ( * cm ) , GFP_KERNEL ) ;
if ( ! cm )
return - ENOMEM ;
cm - > mport = mport ;
rc = rio_request_outb_mbox ( mport , cm , cmbox ,
RIOCM_TX_RING_SIZE , riocm_outb_msg_event ) ;
if ( rc ) {
riocm_error ( " failed to allocate OBMBOX_%d on %s " ,
cmbox , mport - > name ) ;
kfree ( cm ) ;
return - ENODEV ;
}
rc = rio_request_inb_mbox ( mport , cm , cmbox ,
RIOCM_RX_RING_SIZE , riocm_inb_msg_event ) ;
if ( rc ) {
riocm_error ( " failed to allocate IBMBOX_%d on %s " ,
cmbox , mport - > name ) ;
rio_release_outb_mbox ( mport , cmbox ) ;
kfree ( cm ) ;
return - ENODEV ;
}
/*
* Allocate and register inbound messaging buffers to be ready
* to receive channel and system management requests
*/
for ( i = 0 ; i < RIOCM_RX_RING_SIZE ; i + + )
cm - > rx_buf [ i ] = NULL ;
cm - > rx_slots = RIOCM_RX_RING_SIZE ;
mutex_init ( & cm - > rx_lock ) ;
riocm_rx_fill ( cm , RIOCM_RX_RING_SIZE ) ;
cm - > rx_wq = create_workqueue ( DRV_NAME " /rxq " ) ;
INIT_WORK ( & cm - > rx_work , rio_ibmsg_handler ) ;
cm - > tx_slot = 0 ;
cm - > tx_cnt = 0 ;
cm - > tx_ack_slot = 0 ;
spin_lock_init ( & cm - > tx_lock ) ;
INIT_LIST_HEAD ( & cm - > peers ) ;
cm - > npeers = 0 ;
INIT_LIST_HEAD ( & cm - > tx_reqs ) ;
down_write ( & rdev_sem ) ;
list_add_tail ( & cm - > list , & cm_dev_list ) ;
up_write ( & rdev_sem ) ;
return 0 ;
}
/*
* riocm_remove_mport - remove local mport device from channel management core
* @ dev : device object associated with mport
* @ class_intf : class interface
*
* Removes a local mport device from the list of registered devices that provide
* channel management services . Returns an error if the specified mport is not
* registered with the CM core .
*/
static void riocm_remove_mport ( struct device * dev ,
struct class_interface * class_intf )
{
struct rio_mport * mport = to_rio_mport ( dev ) ;
struct cm_dev * cm ;
struct cm_peer * peer , * temp ;
struct rio_channel * ch , * _c ;
unsigned int i ;
bool found = false ;
LIST_HEAD ( list ) ;
riocm_debug ( MPORT , " %s " , mport - > name ) ;
/* Find a matching cm_dev object */
down_write ( & rdev_sem ) ;
list_for_each_entry ( cm , & cm_dev_list , list ) {
if ( cm - > mport = = mport ) {
list_del ( & cm - > list ) ;
found = true ;
break ;
}
}
up_write ( & rdev_sem ) ;
if ( ! found )
return ;
flush_workqueue ( cm - > rx_wq ) ;
destroy_workqueue ( cm - > rx_wq ) ;
/* Release channels bound to this mport */
spin_lock_bh ( & idr_lock ) ;
idr_for_each_entry ( & ch_idr , ch , i ) {
if ( ch - > cmdev = = cm ) {
riocm_debug ( RDEV , " %s drop ch_%d " ,
mport - > name , ch - > id ) ;
idr_remove ( & ch_idr , ch - > id ) ;
list_add ( & ch - > ch_node , & list ) ;
}
}
spin_unlock_bh ( & idr_lock ) ;
if ( ! list_empty ( & list ) ) {
list_for_each_entry_safe ( ch , _c , & list , ch_node ) {
list_del ( & ch - > ch_node ) ;
riocm_ch_close ( ch ) ;
}
}
rio_release_inb_mbox ( mport , cmbox ) ;
rio_release_outb_mbox ( mport , cmbox ) ;
/* Remove and free peer entries */
if ( ! list_empty ( & cm - > peers ) )
riocm_debug ( RDEV , " ATTN: peer list not empty " ) ;
list_for_each_entry_safe ( peer , temp , & cm - > peers , node ) {
riocm_debug ( RDEV , " removing peer %s " , rio_name ( peer - > rdev ) ) ;
list_del ( & peer - > node ) ;
kfree ( peer ) ;
}
riocm_rx_free ( cm ) ;
kfree ( cm ) ;
riocm_debug ( MPORT , " %s done " , mport - > name ) ;
}
static int rio_cm_shutdown ( struct notifier_block * nb , unsigned long code ,
void * unused )
{
struct rio_channel * ch ;
unsigned int i ;
2016-09-20 00:44:47 +03:00
LIST_HEAD ( list ) ;
2016-08-03 00:06:25 +03:00
riocm_debug ( EXIT , " . " ) ;
2016-09-20 00:44:47 +03:00
/*
* If there are any channels left in connected state send
* close notification to the connection partner .
* First build a list of channels that require a closing
* notification because function riocm_send_close ( ) should
* be called outside of spinlock protected code .
*/
2016-08-03 00:06:25 +03:00
spin_lock_bh ( & idr_lock ) ;
idr_for_each_entry ( & ch_idr , ch , i ) {
2016-09-20 00:44:47 +03:00
if ( ch - > state = = RIO_CM_CONNECTED ) {
riocm_debug ( EXIT , " close ch %d " , ch - > id ) ;
idr_remove ( & ch_idr , ch - > id ) ;
list_add ( & ch - > ch_node , & list ) ;
}
2016-08-03 00:06:25 +03:00
}
spin_unlock_bh ( & idr_lock ) ;
2016-09-20 00:44:47 +03:00
list_for_each_entry ( ch , & list , ch_node )
riocm_send_close ( ch ) ;
2016-08-03 00:06:25 +03:00
return NOTIFY_DONE ;
}
/*
* riocm_interface handles addition / removal of remote RapidIO devices
*/
static struct subsys_interface riocm_interface = {
. name = " rio_cm " ,
. subsys = & rio_bus_type ,
. add_dev = riocm_add_dev ,
. remove_dev = riocm_remove_dev ,
} ;
/*
* rio_mport_interface handles addition / removal local mport devices
*/
static struct class_interface rio_mport_interface __refdata = {
. class = & rio_mport_class ,
. add_dev = riocm_add_mport ,
. remove_dev = riocm_remove_mport ,
} ;
static struct notifier_block rio_cm_notifier = {
. notifier_call = rio_cm_shutdown ,
} ;
static int __init riocm_init ( void )
{
int ret ;
/* Create device class needed by udev */
dev_class = class_create ( THIS_MODULE , DRV_NAME ) ;
if ( IS_ERR ( dev_class ) ) {
riocm_error ( " Cannot create " DRV_NAME " class " ) ;
return PTR_ERR ( dev_class ) ;
}
ret = alloc_chrdev_region ( & dev_number , 0 , 1 , DRV_NAME ) ;
if ( ret ) {
class_destroy ( dev_class ) ;
return ret ;
}
dev_major = MAJOR ( dev_number ) ;
dev_minor_base = MINOR ( dev_number ) ;
riocm_debug ( INIT , " Registered class with %d major " , dev_major ) ;
/*
* Register as rapidio_port class interface to get notifications about
* mport additions and removals .
*/
ret = class_interface_register ( & rio_mport_interface ) ;
if ( ret ) {
riocm_error ( " class_interface_register error: %d " , ret ) ;
goto err_reg ;
}
/*
* Register as RapidIO bus interface to get notifications about
* addition / removal of remote RapidIO devices .
*/
ret = subsys_interface_register ( & riocm_interface ) ;
if ( ret ) {
riocm_error ( " subsys_interface_register error: %d " , ret ) ;
goto err_cl ;
}
ret = register_reboot_notifier ( & rio_cm_notifier ) ;
if ( ret ) {
riocm_error ( " failed to register reboot notifier (err=%d) " , ret ) ;
goto err_sif ;
}
ret = riocm_cdev_add ( dev_number ) ;
if ( ret ) {
unregister_reboot_notifier ( & rio_cm_notifier ) ;
ret = - ENODEV ;
goto err_sif ;
}
return 0 ;
err_sif :
subsys_interface_unregister ( & riocm_interface ) ;
err_cl :
class_interface_unregister ( & rio_mport_interface ) ;
err_reg :
unregister_chrdev_region ( dev_number , 1 ) ;
class_destroy ( dev_class ) ;
return ret ;
}
static void __exit riocm_exit ( void )
{
riocm_debug ( EXIT , " enter " ) ;
unregister_reboot_notifier ( & rio_cm_notifier ) ;
subsys_interface_unregister ( & riocm_interface ) ;
class_interface_unregister ( & rio_mport_interface ) ;
idr_destroy ( & ch_idr ) ;
device_unregister ( riocm_cdev . dev ) ;
cdev_del ( & ( riocm_cdev . cdev ) ) ;
class_destroy ( dev_class ) ;
unregister_chrdev_region ( dev_number , 1 ) ;
}
late_initcall ( riocm_init ) ;
module_exit ( riocm_exit ) ;