2011-10-20 23:10:55 +04:00
/*
* Virtio - based remote processor messaging bus
*
* Copyright ( C ) 2011 Texas Instruments , Inc .
* Copyright ( C ) 2011 Google , Inc .
*
* Ohad Ben - Cohen < ohad @ wizery . com >
* Brian Swetland < swetland @ google . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*/
# define pr_fmt(fmt) "%s: " fmt, __func__
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/virtio.h>
# include <linux/virtio_ids.h>
# include <linux/virtio_config.h>
# include <linux/scatterlist.h>
# include <linux/dma-mapping.h>
# include <linux/slab.h>
# include <linux/idr.h>
# include <linux/jiffies.h>
# include <linux/sched.h>
# include <linux/wait.h>
# include <linux/rpmsg.h>
# include <linux/mutex.h>
/**
* struct virtproc_info - virtual remote processor state
* @ vdev : the virtio device
* @ rvq : rx virtqueue
* @ svq : tx virtqueue
* @ rbufs : kernel address of rx buffers
* @ sbufs : kernel address of tx buffers
2014-09-16 22:33:07 +04:00
* @ num_bufs : total number of buffers for rx and tx
2011-10-20 23:10:55 +04:00
* @ last_sbuf : index of last tx buffer used
* @ bufs_dma : dma base addr of the buffers
* @ tx_lock : protects svq , sbufs and sleepers , to allow concurrent senders .
* sending a message might require waking up a dozing remote
* processor , which involves sleeping , hence the mutex .
* @ endpoints : idr of local endpoints , allows fast retrieval
* @ endpoints_lock : lock of the endpoints set
* @ sendq : wait queue of sending contexts waiting for a tx buffers
* @ sleepers : number of senders that are waiting for a tx buffer
* @ ns_ept : the bus ' s name service endpoint
*
* This structure stores the rpmsg state of a given virtio remote processor
* device ( there might be several virtio proc devices for each physical
* remote processor ) .
*/
struct virtproc_info {
struct virtio_device * vdev ;
struct virtqueue * rvq , * svq ;
void * rbufs , * sbufs ;
2014-09-16 22:33:07 +04:00
unsigned int num_bufs ;
2011-10-20 23:10:55 +04:00
int last_sbuf ;
dma_addr_t bufs_dma ;
struct mutex tx_lock ;
struct idr endpoints ;
struct mutex endpoints_lock ;
wait_queue_head_t sendq ;
atomic_t sleepers ;
struct rpmsg_endpoint * ns_ept ;
} ;
/**
* struct rpmsg_channel_info - internal channel info representation
* @ name : name of service
* @ src : local address
* @ dst : destination address
*/
struct rpmsg_channel_info {
char name [ RPMSG_NAME_SIZE ] ;
u32 src ;
u32 dst ;
} ;
# define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev)
# define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv)
/*
2014-09-16 22:33:07 +04:00
* We ' re allocating buffers of 512 bytes each for communications . The
* number of buffers will be computed from the number of buffers supported
* by the vring , upto a maximum of 512 buffers ( 256 in each direction ) .
2011-10-20 23:10:55 +04:00
*
* Each buffer will have 16 bytes for the msg header and 496 bytes for
* the payload .
*
2014-09-16 22:33:07 +04:00
* This will utilize a maximum total space of 256 KB for the buffers .
2011-10-20 23:10:55 +04:00
*
* We might also want to add support for user - provided buffers in time .
* This will allow bigger buffer size flexibility , and can also be used
* to achieve zero - copy messaging .
*
* Note that these numbers are purely a decision of this driver - we
* can change this without changing anything in the firmware of the remote
* processor .
*/
2014-09-16 22:33:07 +04:00
# define MAX_RPMSG_NUM_BUFS (512)
2011-10-20 23:10:55 +04:00
# define RPMSG_BUF_SIZE (512)
/*
* Local addresses are dynamically allocated on - demand .
* We do not dynamically assign addresses from the low 1024 range ,
* in order to reserve that address range for predefined services .
*/
# define RPMSG_RESERVED_ADDRESSES (1024)
/* Address 53 is reserved for advertising remote services */
# define RPMSG_NS_ADDR (53)
/* sysfs show configuration fields */
# define rpmsg_show_attr(field, path, format_string) \
static ssize_t \
field # # _show ( struct device * dev , \
struct device_attribute * attr , char * buf ) \
{ \
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ; \
\
return sprintf ( buf , format_string , rpdev - > path ) ; \
}
/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
rpmsg_show_attr ( name , id . name , " %s \n " ) ;
rpmsg_show_attr ( src , src , " 0x%x \n " ) ;
rpmsg_show_attr ( dst , dst , " 0x%x \n " ) ;
rpmsg_show_attr ( announce , announce ? " true " : " false " , " %s \n " ) ;
/*
* Unique ( and free running ) index for rpmsg devices .
*
* Yeah , we ' re not recycling those numbers ( yet ? ) . will be easy
* to change if / when we want to .
*/
static unsigned int rpmsg_dev_index ;
static ssize_t modalias_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ;
return sprintf ( buf , RPMSG_DEVICE_MODALIAS_FMT " \n " , rpdev - > id . name ) ;
}
static struct device_attribute rpmsg_dev_attrs [ ] = {
__ATTR_RO ( name ) ,
__ATTR_RO ( modalias ) ,
__ATTR_RO ( dst ) ,
__ATTR_RO ( src ) ,
__ATTR_RO ( announce ) ,
__ATTR_NULL
} ;
/* rpmsg devices and drivers are matched using the service name */
static inline int rpmsg_id_match ( const struct rpmsg_channel * rpdev ,
const struct rpmsg_device_id * id )
{
return strncmp ( id - > name , rpdev - > id . name , RPMSG_NAME_SIZE ) = = 0 ;
}
/* match rpmsg channel and rpmsg driver */
static int rpmsg_dev_match ( struct device * dev , struct device_driver * drv )
{
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ;
struct rpmsg_driver * rpdrv = to_rpmsg_driver ( drv ) ;
const struct rpmsg_device_id * ids = rpdrv - > id_table ;
unsigned int i ;
for ( i = 0 ; ids [ i ] . name [ 0 ] ; i + + )
if ( rpmsg_id_match ( rpdev , & ids [ i ] ) )
return 1 ;
return 0 ;
}
static int rpmsg_uevent ( struct device * dev , struct kobj_uevent_env * env )
{
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ;
return add_uevent_var ( env , " MODALIAS= " RPMSG_DEVICE_MODALIAS_FMT ,
rpdev - > id . name ) ;
}
2012-06-06 11:09:25 +04:00
/**
* __ept_release ( ) - deallocate an rpmsg endpoint
* @ kref : the ept ' s reference count
*
* This function deallocates an ept , and is invoked when its @ kref refcount
* drops to zero .
*
* Never invoke this function directly !
*/
static void __ept_release ( struct kref * kref )
{
struct rpmsg_endpoint * ept = container_of ( kref , struct rpmsg_endpoint ,
refcount ) ;
/*
* At this point no one holds a reference to ept anymore ,
* so we can directly free it
*/
kfree ( ept ) ;
}
2011-10-20 23:10:55 +04:00
/* for more info, see below documentation of rpmsg_create_ept() */
static struct rpmsg_endpoint * __rpmsg_create_ept ( struct virtproc_info * vrp ,
struct rpmsg_channel * rpdev , rpmsg_rx_cb_t cb ,
void * priv , u32 addr )
{
2013-02-28 05:04:40 +04:00
int id_min , id_max , id ;
2011-10-20 23:10:55 +04:00
struct rpmsg_endpoint * ept ;
struct device * dev = rpdev ? & rpdev - > dev : & vrp - > vdev - > dev ;
ept = kzalloc ( sizeof ( * ept ) , GFP_KERNEL ) ;
if ( ! ept ) {
dev_err ( dev , " failed to kzalloc a new ept \n " ) ;
return NULL ;
}
2012-06-06 11:09:25 +04:00
kref_init ( & ept - > refcount ) ;
2012-06-07 16:39:35 +04:00
mutex_init ( & ept - > cb_lock ) ;
2012-06-06 11:09:25 +04:00
2011-10-20 23:10:55 +04:00
ept - > rpdev = rpdev ;
ept - > cb = cb ;
ept - > priv = priv ;
/* do we need to allocate a local address ? */
2013-02-28 05:04:40 +04:00
if ( addr = = RPMSG_ADDR_ANY ) {
id_min = RPMSG_RESERVED_ADDRESSES ;
id_max = 0 ;
} else {
id_min = addr ;
id_max = addr + 1 ;
}
2011-10-20 23:10:55 +04:00
mutex_lock ( & vrp - > endpoints_lock ) ;
/* bind the endpoint to an rpmsg address (and allocate one if needed) */
2013-02-28 05:04:40 +04:00
id = idr_alloc ( & vrp - > endpoints , ept , id_min , id_max , GFP_KERNEL ) ;
if ( id < 0 ) {
dev_err ( dev , " idr_alloc failed: %d \n " , id ) ;
2011-10-20 23:10:55 +04:00
goto free_ept ;
}
2013-02-28 05:04:40 +04:00
ept - > addr = id ;
2011-10-20 23:10:55 +04:00
mutex_unlock ( & vrp - > endpoints_lock ) ;
return ept ;
free_ept :
mutex_unlock ( & vrp - > endpoints_lock ) ;
2012-06-06 11:09:25 +04:00
kref_put ( & ept - > refcount , __ept_release ) ;
2011-10-20 23:10:55 +04:00
return NULL ;
}
/**
* rpmsg_create_ept ( ) - create a new rpmsg_endpoint
* @ rpdev : rpmsg channel device
* @ cb : rx callback handler
* @ priv : private data for the driver ' s use
* @ addr : local rpmsg address to bind with @ cb
*
* Every rpmsg address in the system is bound to an rx callback ( so when
* inbound messages arrive , they are dispatched by the rpmsg bus using the
* appropriate callback handler ) by means of an rpmsg_endpoint struct .
*
* This function allows drivers to create such an endpoint , and by that ,
* bind a callback , and possibly some private data too , to an rpmsg address
* ( either one that is known in advance , or one that will be dynamically
* assigned for them ) .
*
* Simple rpmsg drivers need not call rpmsg_create_ept , because an endpoint
* is already created for them when they are probed by the rpmsg bus
* ( using the rx callback provided when they registered to the rpmsg bus ) .
*
* So things should just work for simple drivers : they already have an
* endpoint , their rx callback is bound to their rpmsg address , and when
* relevant inbound messages arrive ( i . e . messages which their dst address
* equals to the src address of their rpmsg channel ) , the driver ' s handler
* is invoked to process it .
*
* That said , more complicated drivers might do need to allocate
* additional rpmsg addresses , and bind them to different rx callbacks .
* To accomplish that , those drivers need to call this function .
*
* Drivers should provide their @ rpdev channel ( so the new endpoint would belong
* to the same remote processor their channel belongs to ) , an rx callback
* function , an optional private data ( which is provided back when the
* rx callback is invoked ) , and an address they want to bind with the
* callback . If @ addr is RPMSG_ADDR_ANY , then rpmsg_create_ept will
* dynamically assign them an available rpmsg address ( drivers should have
* a very good reason why not to always use RPMSG_ADDR_ANY here ) .
*
* Returns a pointer to the endpoint on success , or NULL on error .
*/
struct rpmsg_endpoint * rpmsg_create_ept ( struct rpmsg_channel * rpdev ,
rpmsg_rx_cb_t cb , void * priv , u32 addr )
{
return __rpmsg_create_ept ( rpdev - > vrp , rpdev , cb , priv , addr ) ;
}
EXPORT_SYMBOL ( rpmsg_create_ept ) ;
/**
2012-02-09 17:16:41 +04:00
* __rpmsg_destroy_ept ( ) - destroy an existing rpmsg endpoint
* @ vrp : virtproc which owns this ept
2011-10-20 23:10:55 +04:00
* @ ept : endpoing to destroy
*
2012-02-09 17:16:41 +04:00
* An internal function which destroy an ept without assuming it is
* bound to an rpmsg channel . This is needed for handling the internal
* name service endpoint , which isn ' t bound to an rpmsg channel .
* See also __rpmsg_create_ept ( ) .
2011-10-20 23:10:55 +04:00
*/
2012-02-09 17:16:41 +04:00
static void
__rpmsg_destroy_ept ( struct virtproc_info * vrp , struct rpmsg_endpoint * ept )
2011-10-20 23:10:55 +04:00
{
2012-06-07 16:39:35 +04:00
/* make sure new inbound messages can't find this ept anymore */
2011-10-20 23:10:55 +04:00
mutex_lock ( & vrp - > endpoints_lock ) ;
idr_remove ( & vrp - > endpoints , ept - > addr ) ;
mutex_unlock ( & vrp - > endpoints_lock ) ;
2012-06-07 16:39:35 +04:00
/* make sure in-flight inbound messages won't invoke cb anymore */
mutex_lock ( & ept - > cb_lock ) ;
ept - > cb = NULL ;
mutex_unlock ( & ept - > cb_lock ) ;
2012-06-06 11:09:25 +04:00
kref_put ( & ept - > refcount , __ept_release ) ;
2011-10-20 23:10:55 +04:00
}
2012-02-09 17:16:41 +04:00
/**
* rpmsg_destroy_ept ( ) - destroy an existing rpmsg endpoint
* @ ept : endpoing to destroy
*
* Should be used by drivers to destroy an rpmsg endpoint previously
* created with rpmsg_create_ept ( ) .
*/
void rpmsg_destroy_ept ( struct rpmsg_endpoint * ept )
{
__rpmsg_destroy_ept ( ept - > rpdev - > vrp , ept ) ;
}
2011-10-20 23:10:55 +04:00
EXPORT_SYMBOL ( rpmsg_destroy_ept ) ;
/*
* when an rpmsg driver is probed with a channel , we seamlessly create
* it an endpoint , binding its rx callback to a unique local rpmsg
* address .
*
* if we need to , we also announce about this channel to the remote
* processor ( needed in case the driver is exposing an rpmsg service ) .
*/
static int rpmsg_dev_probe ( struct device * dev )
{
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ;
struct rpmsg_driver * rpdrv = to_rpmsg_driver ( rpdev - > dev . driver ) ;
struct virtproc_info * vrp = rpdev - > vrp ;
struct rpmsg_endpoint * ept ;
int err ;
ept = rpmsg_create_ept ( rpdev , rpdrv - > callback , NULL , rpdev - > src ) ;
if ( ! ept ) {
dev_err ( dev , " failed to create endpoint \n " ) ;
err = - ENOMEM ;
goto out ;
}
rpdev - > ept = ept ;
rpdev - > src = ept - > addr ;
err = rpdrv - > probe ( rpdev ) ;
if ( err ) {
dev_err ( dev , " %s: failed: %d \n " , __func__ , err ) ;
rpmsg_destroy_ept ( ept ) ;
goto out ;
}
/* need to tell remote processor's name service about this channel ? */
if ( rpdev - > announce & &
virtio_has_feature ( vrp - > vdev , VIRTIO_RPMSG_F_NS ) ) {
struct rpmsg_ns_msg nsm ;
strncpy ( nsm . name , rpdev - > id . name , RPMSG_NAME_SIZE ) ;
nsm . addr = rpdev - > src ;
nsm . flags = RPMSG_NS_CREATE ;
err = rpmsg_sendto ( rpdev , & nsm , sizeof ( nsm ) , RPMSG_NS_ADDR ) ;
if ( err )
dev_err ( dev , " failed to announce service %d \n " , err ) ;
}
out :
return err ;
}
static int rpmsg_dev_remove ( struct device * dev )
{
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ;
struct rpmsg_driver * rpdrv = to_rpmsg_driver ( rpdev - > dev . driver ) ;
struct virtproc_info * vrp = rpdev - > vrp ;
int err = 0 ;
/* tell remote processor's name service we're removing this channel */
if ( rpdev - > announce & &
virtio_has_feature ( vrp - > vdev , VIRTIO_RPMSG_F_NS ) ) {
struct rpmsg_ns_msg nsm ;
strncpy ( nsm . name , rpdev - > id . name , RPMSG_NAME_SIZE ) ;
nsm . addr = rpdev - > src ;
nsm . flags = RPMSG_NS_DESTROY ;
err = rpmsg_sendto ( rpdev , & nsm , sizeof ( nsm ) , RPMSG_NS_ADDR ) ;
if ( err )
dev_err ( dev , " failed to announce service %d \n " , err ) ;
}
rpdrv - > remove ( rpdev ) ;
rpmsg_destroy_ept ( rpdev - > ept ) ;
return err ;
}
static struct bus_type rpmsg_bus = {
. name = " rpmsg " ,
. match = rpmsg_dev_match ,
. dev_attrs = rpmsg_dev_attrs ,
. uevent = rpmsg_uevent ,
. probe = rpmsg_dev_probe ,
. remove = rpmsg_dev_remove ,
} ;
/**
* register_rpmsg_driver ( ) - register an rpmsg driver with the rpmsg bus
* @ rpdrv : pointer to a struct rpmsg_driver
*
* Returns 0 on success , and an appropriate error value on failure .
*/
int register_rpmsg_driver ( struct rpmsg_driver * rpdrv )
{
rpdrv - > drv . bus = & rpmsg_bus ;
return driver_register ( & rpdrv - > drv ) ;
}
EXPORT_SYMBOL ( register_rpmsg_driver ) ;
/**
* unregister_rpmsg_driver ( ) - unregister an rpmsg driver from the rpmsg bus
* @ rpdrv : pointer to a struct rpmsg_driver
*
* Returns 0 on success , and an appropriate error value on failure .
*/
void unregister_rpmsg_driver ( struct rpmsg_driver * rpdrv )
{
driver_unregister ( & rpdrv - > drv ) ;
}
EXPORT_SYMBOL ( unregister_rpmsg_driver ) ;
static void rpmsg_release_device ( struct device * dev )
{
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ;
kfree ( rpdev ) ;
}
/*
* match an rpmsg channel with a channel info struct .
* this is used to make sure we ' re not creating rpmsg devices for channels
* that already exist .
*/
static int rpmsg_channel_match ( struct device * dev , void * data )
{
struct rpmsg_channel_info * chinfo = data ;
struct rpmsg_channel * rpdev = to_rpmsg_channel ( dev ) ;
if ( chinfo - > src ! = RPMSG_ADDR_ANY & & chinfo - > src ! = rpdev - > src )
return 0 ;
if ( chinfo - > dst ! = RPMSG_ADDR_ANY & & chinfo - > dst ! = rpdev - > dst )
return 0 ;
if ( strncmp ( chinfo - > name , rpdev - > id . name , RPMSG_NAME_SIZE ) )
return 0 ;
/* found a match ! */
return 1 ;
}
/*
* create an rpmsg channel using its name and address info .
* this function will be used to create both static and dynamic
* channels .
*/
static struct rpmsg_channel * rpmsg_create_channel ( struct virtproc_info * vrp ,
struct rpmsg_channel_info * chinfo )
{
struct rpmsg_channel * rpdev ;
struct device * tmp , * dev = & vrp - > vdev - > dev ;
int ret ;
/* make sure a similar channel doesn't already exist */
tmp = device_find_child ( dev , chinfo , rpmsg_channel_match ) ;
if ( tmp ) {
/* decrement the matched device's refcount back */
put_device ( tmp ) ;
dev_err ( dev , " channel %s:%x:%x already exist \n " ,
chinfo - > name , chinfo - > src , chinfo - > dst ) ;
return NULL ;
}
rpdev = kzalloc ( sizeof ( struct rpmsg_channel ) , GFP_KERNEL ) ;
if ( ! rpdev ) {
pr_err ( " kzalloc failed \n " ) ;
return NULL ;
}
rpdev - > vrp = vrp ;
rpdev - > src = chinfo - > src ;
rpdev - > dst = chinfo - > dst ;
/*
* rpmsg server channels has predefined local address ( for now ) ,
* and their existence needs to be announced remotely
*/
rpdev - > announce = rpdev - > src ! = RPMSG_ADDR_ANY ? true : false ;
strncpy ( rpdev - > id . name , chinfo - > name , RPMSG_NAME_SIZE ) ;
/* very simple device indexing plumbing which is enough for now */
dev_set_name ( & rpdev - > dev , " rpmsg%d " , rpmsg_dev_index + + ) ;
rpdev - > dev . parent = & vrp - > vdev - > dev ;
rpdev - > dev . bus = & rpmsg_bus ;
rpdev - > dev . release = rpmsg_release_device ;
ret = device_register ( & rpdev - > dev ) ;
if ( ret ) {
dev_err ( dev , " device_register failed: %d \n " , ret ) ;
put_device ( & rpdev - > dev ) ;
return NULL ;
}
return rpdev ;
}
/*
* find an existing channel using its name + address properties ,
* and destroy it
*/
static int rpmsg_destroy_channel ( struct virtproc_info * vrp ,
struct rpmsg_channel_info * chinfo )
{
struct virtio_device * vdev = vrp - > vdev ;
struct device * dev ;
dev = device_find_child ( & vdev - > dev , chinfo , rpmsg_channel_match ) ;
if ( ! dev )
return - EINVAL ;
device_unregister ( dev ) ;
put_device ( dev ) ;
return 0 ;
}
/* super simple buffer "allocator" that is just enough for now */
static void * get_a_tx_buf ( struct virtproc_info * vrp )
{
unsigned int len ;
void * ret ;
/* support multiple concurrent senders */
mutex_lock ( & vrp - > tx_lock ) ;
/*
* either pick the next unused tx buffer
* ( half of our buffers are used for sending messages )
*/
2014-09-16 22:33:07 +04:00
if ( vrp - > last_sbuf < vrp - > num_bufs / 2 )
2011-10-20 23:10:55 +04:00
ret = vrp - > sbufs + RPMSG_BUF_SIZE * vrp - > last_sbuf + + ;
/* or recycle a used one */
else
ret = virtqueue_get_buf ( vrp - > svq , & len ) ;
mutex_unlock ( & vrp - > tx_lock ) ;
return ret ;
}
/**
* rpmsg_upref_sleepers ( ) - enable " tx-complete " interrupts , if needed
* @ vrp : virtual remote processor state
*
* This function is called before a sender is blocked , waiting for
* a tx buffer to become available .
*
* If we already have blocking senders , this function merely increases
* the " sleepers " reference count , and exits .
*
* Otherwise , if this is the first sender to block , we also enable
* virtio ' s tx callbacks , so we ' d be immediately notified when a tx
* buffer is consumed ( we rely on virtio ' s tx callback in order
* to wake up sleeping senders as soon as a tx buffer is used by the
* remote processor ) .
*/
static void rpmsg_upref_sleepers ( struct virtproc_info * vrp )
{
/* support multiple concurrent senders */
mutex_lock ( & vrp - > tx_lock ) ;
/* are we the first sleeping context waiting for tx buffers ? */
if ( atomic_inc_return ( & vrp - > sleepers ) = = 1 )
/* enable "tx-complete" interrupts before dozing off */
virtqueue_enable_cb ( vrp - > svq ) ;
mutex_unlock ( & vrp - > tx_lock ) ;
}
/**
* rpmsg_downref_sleepers ( ) - disable " tx-complete " interrupts , if needed
* @ vrp : virtual remote processor state
*
* This function is called after a sender , that waited for a tx buffer
* to become available , is unblocked .
*
* If we still have blocking senders , this function merely decreases
* the " sleepers " reference count , and exits .
*
* Otherwise , if there are no more blocking senders , we also disable
* virtio ' s tx callbacks , to avoid the overhead incurred with handling
* those ( now redundant ) interrupts .
*/
static void rpmsg_downref_sleepers ( struct virtproc_info * vrp )
{
/* support multiple concurrent senders */
mutex_lock ( & vrp - > tx_lock ) ;
/* are we the last sleeping context waiting for tx buffers ? */
if ( atomic_dec_and_test ( & vrp - > sleepers ) )
/* disable "tx-complete" interrupts */
virtqueue_disable_cb ( vrp - > svq ) ;
mutex_unlock ( & vrp - > tx_lock ) ;
}
/**
* rpmsg_send_offchannel_raw ( ) - send a message across to the remote processor
* @ rpdev : the rpmsg channel
* @ src : source address
* @ dst : destination address
* @ data : payload of message
* @ len : length of payload
* @ wait : indicates whether caller should block in case no TX buffers available
*
* This function is the base implementation for all of the rpmsg sending API .
*
* It will send @ data of length @ len to @ dst , and say it ' s from @ src . The
* message will be sent to the remote processor which the @ rpdev channel
* belongs to .
*
* The message is sent using one of the TX buffers that are available for
* communication with this remote processor .
*
* If @ wait is true , the caller will be blocked until either a TX buffer is
* available , or 15 seconds elapses ( we don ' t want callers to
* sleep indefinitely due to misbehaving remote processors ) , and in that
* case - ERESTARTSYS is returned . The number ' 15 ' itself was picked
* arbitrarily ; there ' s little point in asking drivers to provide a timeout
* value themselves .
*
* Otherwise , if @ wait is false , and there are no TX buffers available ,
* the function will immediately fail , and - ENOMEM will be returned .
*
* Normally drivers shouldn ' t use this function directly ; instead , drivers
* should use the appropriate rpmsg_ { try } send { to , _offchannel } API
* ( see include / linux / rpmsg . h ) .
*
* Returns 0 on success and an appropriate error value on failure .
*/
int rpmsg_send_offchannel_raw ( struct rpmsg_channel * rpdev , u32 src , u32 dst ,
void * data , int len , bool wait )
{
struct virtproc_info * vrp = rpdev - > vrp ;
struct device * dev = & rpdev - > dev ;
struct scatterlist sg ;
struct rpmsg_hdr * msg ;
int err ;
/* bcasting isn't allowed */
if ( src = = RPMSG_ADDR_ANY | | dst = = RPMSG_ADDR_ANY ) {
dev_err ( dev , " invalid addr (src 0x%x, dst 0x%x) \n " , src , dst ) ;
return - EINVAL ;
}
/*
* We currently use fixed - sized buffers , and therefore the payload
* length is limited .
*
* One of the possible improvements here is either to support
* user - provided buffers ( and then we can also support zero - copy
* messaging ) , or to improve the buffer allocator , to support
* variable - length buffer sizes .
*/
if ( len > RPMSG_BUF_SIZE - sizeof ( struct rpmsg_hdr ) ) {
dev_err ( dev , " message is too big (%d) \n " , len ) ;
return - EMSGSIZE ;
}
/* grab a buffer */
msg = get_a_tx_buf ( vrp ) ;
if ( ! msg & & ! wait )
return - ENOMEM ;
/* no free buffer ? wait for one (but bail after 15 seconds) */
while ( ! msg ) {
/* enable "tx-complete" interrupts, if not already enabled */
rpmsg_upref_sleepers ( vrp ) ;
/*
* sleep until a free buffer is available or 15 secs elapse .
* the timeout period is not configurable because there ' s
* little point in asking drivers to specify that .
* if later this happens to be required , it ' d be easy to add .
*/
err = wait_event_interruptible_timeout ( vrp - > sendq ,
( msg = get_a_tx_buf ( vrp ) ) ,
msecs_to_jiffies ( 15000 ) ) ;
/* disable "tx-complete" interrupts if we're the last sleeper */
rpmsg_downref_sleepers ( vrp ) ;
/* timeout ? */
if ( ! err ) {
dev_err ( dev , " timeout waiting for a tx buffer \n " ) ;
return - ERESTARTSYS ;
}
}
msg - > len = len ;
msg - > flags = 0 ;
msg - > src = src ;
msg - > dst = dst ;
msg - > reserved = 0 ;
memcpy ( msg - > data , data , len ) ;
dev_dbg ( dev , " TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d \n " ,
msg - > src , msg - > dst , msg - > len ,
msg - > flags , msg - > reserved ) ;
print_hex_dump ( KERN_DEBUG , " rpmsg_virtio TX: " , DUMP_PREFIX_NONE , 16 , 1 ,
msg , sizeof ( * msg ) + msg - > len , true ) ;
sg_init_one ( & sg , msg , sizeof ( * msg ) + len ) ;
mutex_lock ( & vrp - > tx_lock ) ;
/* add message to the remote processor's virtqueue */
2013-03-20 09:14:29 +04:00
err = virtqueue_add_outbuf ( vrp - > svq , & sg , 1 , msg , GFP_KERNEL ) ;
2012-10-16 17:26:15 +04:00
if ( err ) {
2011-10-20 23:10:55 +04:00
/*
* need to reclaim the buffer here , otherwise it ' s lost
* ( memory won ' t leak , but rpmsg won ' t use it again for TX ) .
* this will wait for a buffer management overhaul .
*/
2013-03-20 09:14:29 +04:00
dev_err ( dev , " virtqueue_add_outbuf failed: %d \n " , err ) ;
2011-10-20 23:10:55 +04:00
goto out ;
}
/* tell the remote processor it has a pending message to read */
virtqueue_kick ( vrp - > svq ) ;
out :
mutex_unlock ( & vrp - > tx_lock ) ;
return err ;
}
EXPORT_SYMBOL ( rpmsg_send_offchannel_raw ) ;
2013-04-05 18:38:52 +04:00
static int rpmsg_recv_single ( struct virtproc_info * vrp , struct device * dev ,
struct rpmsg_hdr * msg , unsigned int len )
2011-10-20 23:10:55 +04:00
{
struct rpmsg_endpoint * ept ;
struct scatterlist sg ;
int err ;
dev_dbg ( dev , " From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d \n " ,
msg - > src , msg - > dst , msg - > len ,
msg - > flags , msg - > reserved ) ;
print_hex_dump ( KERN_DEBUG , " rpmsg_virtio RX: " , DUMP_PREFIX_NONE , 16 , 1 ,
msg , sizeof ( * msg ) + msg - > len , true ) ;
2012-02-28 18:16:48 +04:00
/*
* We currently use fixed - sized buffers , so trivially sanitize
* the reported payload length .
*/
if ( len > RPMSG_BUF_SIZE | |
msg - > len > ( len - sizeof ( struct rpmsg_hdr ) ) ) {
dev_warn ( dev , " inbound msg too big: (%d, %d) \n " , len , msg - > len ) ;
2013-04-05 18:38:52 +04:00
return - EINVAL ;
2012-02-28 18:16:48 +04:00
}
2011-10-20 23:10:55 +04:00
/* use the dst addr to fetch the callback of the appropriate user */
mutex_lock ( & vrp - > endpoints_lock ) ;
2012-06-06 11:09:25 +04:00
2011-10-20 23:10:55 +04:00
ept = idr_find ( & vrp - > endpoints , msg - > dst ) ;
2012-06-06 11:09:25 +04:00
/* let's make sure no one deallocates ept while we use it */
if ( ept )
kref_get ( & ept - > refcount ) ;
2011-10-20 23:10:55 +04:00
mutex_unlock ( & vrp - > endpoints_lock ) ;
2012-06-07 16:39:35 +04:00
if ( ept ) {
/* make sure ept->cb doesn't go away while we use it */
mutex_lock ( & ept - > cb_lock ) ;
2011-10-20 23:10:55 +04:00
2012-06-07 16:39:35 +04:00
if ( ept - > cb )
ept - > cb ( ept - > rpdev , msg - > data , msg - > len , ept - > priv ,
msg - > src ) ;
mutex_unlock ( & ept - > cb_lock ) ;
/* farewell, ept, we don't need you anymore */
2012-06-06 11:09:25 +04:00
kref_put ( & ept - > refcount , __ept_release ) ;
2012-06-07 16:39:35 +04:00
} else
2012-12-28 21:00:09 +04:00
dev_warn ( dev , " msg received with no recipient \n " ) ;
2012-06-06 11:09:25 +04:00
2012-02-28 18:11:28 +04:00
/* publish the real size of the buffer */
sg_init_one ( & sg , msg , RPMSG_BUF_SIZE ) ;
2011-10-20 23:10:55 +04:00
/* add the buffer back to the remote processor's virtqueue */
2013-03-20 09:14:29 +04:00
err = virtqueue_add_inbuf ( vrp - > rvq , & sg , 1 , msg , GFP_KERNEL ) ;
2011-10-20 23:10:55 +04:00
if ( err < 0 ) {
dev_err ( dev , " failed to add a virtqueue buffer: %d \n " , err ) ;
2013-04-05 18:38:52 +04:00
return err ;
}
return 0 ;
}
/* called when an rx buffer is used, and it's time to digest a message */
static void rpmsg_recv_done ( struct virtqueue * rvq )
{
struct virtproc_info * vrp = rvq - > vdev - > priv ;
struct device * dev = & rvq - > vdev - > dev ;
struct rpmsg_hdr * msg ;
unsigned int len , msgs_received = 0 ;
int err ;
msg = virtqueue_get_buf ( rvq , & len ) ;
if ( ! msg ) {
dev_err ( dev , " uhm, incoming signal, but no used buffer ? \n " ) ;
2011-10-20 23:10:55 +04:00
return ;
}
2013-04-05 18:38:52 +04:00
while ( msg ) {
err = rpmsg_recv_single ( vrp , dev , msg , len ) ;
if ( err )
break ;
msgs_received + + ;
msg = virtqueue_get_buf ( rvq , & len ) ;
} ;
dev_dbg ( dev , " Received %u messages \n " , msgs_received ) ;
2011-10-20 23:10:55 +04:00
/* tell the remote processor we added another available rx buffer */
2013-04-05 18:38:52 +04:00
if ( msgs_received )
virtqueue_kick ( vrp - > rvq ) ;
2011-10-20 23:10:55 +04:00
}
/*
* This is invoked whenever the remote processor completed processing
* a TX msg we just sent it , and the buffer is put back to the used ring .
*
* Normally , though , we suppress this " tx complete " interrupt in order to
* avoid the incurred overhead .
*/
static void rpmsg_xmit_done ( struct virtqueue * svq )
{
struct virtproc_info * vrp = svq - > vdev - > priv ;
dev_dbg ( & svq - > vdev - > dev , " %s \n " , __func__ ) ;
/* wake up potential senders that are waiting for a tx buffer */
wake_up_interruptible ( & vrp - > sendq ) ;
}
/* invoked when a name service announcement arrives */
static void rpmsg_ns_cb ( struct rpmsg_channel * rpdev , void * data , int len ,
void * priv , u32 src )
{
struct rpmsg_ns_msg * msg = data ;
struct rpmsg_channel * newch ;
struct rpmsg_channel_info chinfo ;
struct virtproc_info * vrp = priv ;
struct device * dev = & vrp - > vdev - > dev ;
int ret ;
print_hex_dump ( KERN_DEBUG , " NS announcement: " ,
DUMP_PREFIX_NONE , 16 , 1 ,
data , len , true ) ;
if ( len ! = sizeof ( * msg ) ) {
dev_err ( dev , " malformed ns msg (%d) \n " , len ) ;
return ;
}
/*
* the name service ept does _not_ belong to a real rpmsg channel ,
* and is handled by the rpmsg bus itself .
* for sanity reasons , make sure a valid rpdev has _not_ sneaked
* in somehow .
*/
if ( rpdev ) {
dev_err ( dev , " anomaly: ns ept has an rpdev handle \n " ) ;
return ;
}
/* don't trust the remote processor for null terminating the name */
msg - > name [ RPMSG_NAME_SIZE - 1 ] = ' \0 ' ;
dev_info ( dev , " %sing channel %s addr 0x%x \n " ,
msg - > flags & RPMSG_NS_DESTROY ? " destroy " : " creat " ,
msg - > name , msg - > addr ) ;
strncpy ( chinfo . name , msg - > name , sizeof ( chinfo . name ) ) ;
chinfo . src = RPMSG_ADDR_ANY ;
chinfo . dst = msg - > addr ;
if ( msg - > flags & RPMSG_NS_DESTROY ) {
ret = rpmsg_destroy_channel ( vrp , & chinfo ) ;
if ( ret )
dev_err ( dev , " rpmsg_destroy_channel failed: %d \n " , ret ) ;
} else {
newch = rpmsg_create_channel ( vrp , & chinfo ) ;
if ( ! newch )
dev_err ( dev , " rpmsg_create_channel failed \n " ) ;
}
}
static int rpmsg_probe ( struct virtio_device * vdev )
{
vq_callback_t * vq_cbs [ ] = { rpmsg_recv_done , rpmsg_xmit_done } ;
const char * names [ ] = { " input " , " output " } ;
struct virtqueue * vqs [ 2 ] ;
struct virtproc_info * vrp ;
void * bufs_va ;
int err = 0 , i ;
2014-09-16 22:33:07 +04:00
size_t total_buf_space ;
2015-03-12 04:24:41 +03:00
bool notify ;
2011-10-20 23:10:55 +04:00
vrp = kzalloc ( sizeof ( * vrp ) , GFP_KERNEL ) ;
if ( ! vrp )
return - ENOMEM ;
vrp - > vdev = vdev ;
idr_init ( & vrp - > endpoints ) ;
mutex_init ( & vrp - > endpoints_lock ) ;
mutex_init ( & vrp - > tx_lock ) ;
init_waitqueue_head ( & vrp - > sendq ) ;
/* We expect two virtqueues, rx and tx (and in this order) */
err = vdev - > config - > find_vqs ( vdev , 2 , vqs , vq_cbs , names ) ;
if ( err )
goto free_vrp ;
vrp - > rvq = vqs [ 0 ] ;
vrp - > svq = vqs [ 1 ] ;
2014-09-16 22:33:07 +04:00
/* we expect symmetric tx/rx vrings */
WARN_ON ( virtqueue_get_vring_size ( vrp - > rvq ) ! =
virtqueue_get_vring_size ( vrp - > svq ) ) ;
/* we need less buffers if vrings are small */
if ( virtqueue_get_vring_size ( vrp - > rvq ) < MAX_RPMSG_NUM_BUFS / 2 )
vrp - > num_bufs = virtqueue_get_vring_size ( vrp - > rvq ) * 2 ;
else
vrp - > num_bufs = MAX_RPMSG_NUM_BUFS ;
total_buf_space = vrp - > num_bufs * RPMSG_BUF_SIZE ;
2011-10-20 23:10:55 +04:00
/* allocate coherent memory for the buffers */
remoteproc: maintain a generic child device for each rproc
For each registered rproc, maintain a generic remoteproc device whose
parent is the low level platform-specific device (commonly a pdev, but
it may certainly be any other type of device too).
With this in hand, the resulting device hierarchy might then look like:
omap-rproc.0
|
- remoteproc0 <---- new !
|
- virtio0
|
- virtio1
|
- rpmsg0
|
- rpmsg1
|
- rpmsg2
Where:
- omap-rproc.0 is the low level device that's bound to the
driver which invokes rproc_register()
- remoteproc0 is the result of this patch, and will be added by the
remoteproc framework when rproc_register() is invoked
- virtio0 and virtio1 are vdevs that are registered by remoteproc
when it realizes that they are supported by the firmware
of the physical remote processor represented by omap-rproc.0
- rpmsg0, rpmsg1 and rpmsg2 are rpmsg devices that represent rpmsg
channels, and are registerd by the rpmsg bus when it gets notified
about their existence
Technically, this patch:
- changes 'struct rproc' to contain this generic remoteproc.x device
- creates a new "remoteproc" type, to which this new generic remoteproc.x
device belong to.
- adds a super simple enumeration method for the indices of the
remoteproc.x devices
- updates all dev_* messaging to use the generic remoteproc.x device
instead of the low level platform-specific device
- updates all dma_* allocations to use the parent of remoteproc.x (where
the platform-specific memory pools, most commonly CMA, are to be found)
Adding this generic device has several merits:
- we can now add remoteproc runtime PM support simply by hooking onto the
new "remoteproc" type
- all remoteproc log messages will now carry a common name prefix
instead of having a platform-specific one
- having a device as part of the rproc struct makes it possible to simplify
refcounting (see subsequent patch)
Thanks to Stephen Boyd <sboyd@codeaurora.org> for suggesting and
discussing these ideas in one of the remoteproc review threads and
to Fernando Guzman Lugo <fernando.lugo@ti.com> for trying them out
with the (upcoming) runtime PM support for remoteproc.
Cc: Fernando Guzman Lugo <fernando.lugo@ti.com>
Reviewed-by: Stephen Boyd <sboyd@codeaurora.org>
Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
2012-05-30 23:01:25 +04:00
bufs_va = dma_alloc_coherent ( vdev - > dev . parent - > parent ,
2014-09-16 22:33:07 +04:00
total_buf_space , & vrp - > bufs_dma ,
GFP_KERNEL ) ;
2013-04-30 03:17:09 +04:00
if ( ! bufs_va ) {
err = - ENOMEM ;
2011-10-20 23:10:55 +04:00
goto vqs_del ;
2013-04-30 03:17:09 +04:00
}
2011-10-20 23:10:55 +04:00
2012-03-04 15:33:28 +04:00
dev_dbg ( & vdev - > dev , " buffers: va %p, dma 0x%llx \n " , bufs_va ,
( unsigned long long ) vrp - > bufs_dma ) ;
2011-10-20 23:10:55 +04:00
/* half of the buffers is dedicated for RX */
vrp - > rbufs = bufs_va ;
/* and half is dedicated for TX */
2014-09-16 22:33:07 +04:00
vrp - > sbufs = bufs_va + total_buf_space / 2 ;
2011-10-20 23:10:55 +04:00
/* set up the receive buffers */
2014-09-16 22:33:07 +04:00
for ( i = 0 ; i < vrp - > num_bufs / 2 ; i + + ) {
2011-10-20 23:10:55 +04:00
struct scatterlist sg ;
void * cpu_addr = vrp - > rbufs + i * RPMSG_BUF_SIZE ;
sg_init_one ( & sg , cpu_addr , RPMSG_BUF_SIZE ) ;
2013-03-20 09:14:29 +04:00
err = virtqueue_add_inbuf ( vrp - > rvq , & sg , 1 , cpu_addr ,
2011-10-20 23:10:55 +04:00
GFP_KERNEL ) ;
2012-10-16 17:26:15 +04:00
WARN_ON ( err ) ; /* sanity check; this can't really happen */
2011-10-20 23:10:55 +04:00
}
/* suppress "tx-complete" interrupts */
virtqueue_disable_cb ( vrp - > svq ) ;
vdev - > priv = vrp ;
/* if supported by the remote processor, enable the name service */
if ( virtio_has_feature ( vdev , VIRTIO_RPMSG_F_NS ) ) {
/* a dedicated endpoint handles the name service msgs */
vrp - > ns_ept = __rpmsg_create_ept ( vrp , NULL , rpmsg_ns_cb ,
vrp , RPMSG_NS_ADDR ) ;
if ( ! vrp - > ns_ept ) {
dev_err ( & vdev - > dev , " failed to create the ns ept \n " ) ;
err = - ENOMEM ;
goto free_coherent ;
}
}
2015-03-12 04:24:41 +03:00
/*
* Prepare to kick but don ' t notify yet - we can ' t do this before
* device is ready .
*/
notify = virtqueue_kick_prepare ( vrp - > rvq ) ;
/* From this point on, we can notify and get callbacks. */
virtio_device_ready ( vdev ) ;
2011-10-20 23:10:55 +04:00
/* tell the remote processor it can start sending messages */
2015-03-12 04:24:41 +03:00
/*
* this might be concurrent with callbacks , but we are only
* doing notify , not a full kick here , so that ' s ok .
*/
if ( notify )
virtqueue_notify ( vrp - > rvq ) ;
2011-10-20 23:10:55 +04:00
dev_info ( & vdev - > dev , " rpmsg host is online \n " ) ;
return 0 ;
free_coherent :
2014-09-16 22:33:07 +04:00
dma_free_coherent ( vdev - > dev . parent - > parent , total_buf_space ,
bufs_va , vrp - > bufs_dma ) ;
2011-10-20 23:10:55 +04:00
vqs_del :
vdev - > config - > del_vqs ( vrp - > vdev ) ;
free_vrp :
kfree ( vrp ) ;
return err ;
}
static int rpmsg_remove_device ( struct device * dev , void * data )
{
device_unregister ( dev ) ;
return 0 ;
}
2012-12-22 03:14:44 +04:00
static void rpmsg_remove ( struct virtio_device * vdev )
2011-10-20 23:10:55 +04:00
{
struct virtproc_info * vrp = vdev - > priv ;
2014-09-16 22:33:07 +04:00
size_t total_buf_space = vrp - > num_bufs * RPMSG_BUF_SIZE ;
2011-10-20 23:10:55 +04:00
int ret ;
vdev - > config - > reset ( vdev ) ;
ret = device_for_each_child ( & vdev - > dev , NULL , rpmsg_remove_device ) ;
if ( ret )
dev_warn ( & vdev - > dev , " can't remove rpmsg device: %d \n " , ret ) ;
2012-02-09 17:16:41 +04:00
if ( vrp - > ns_ept )
__rpmsg_destroy_ept ( vrp , vrp - > ns_ept ) ;
2011-10-20 23:10:55 +04:00
idr_destroy ( & vrp - > endpoints ) ;
vdev - > config - > del_vqs ( vrp - > vdev ) ;
2014-09-16 22:33:07 +04:00
dma_free_coherent ( vdev - > dev . parent - > parent , total_buf_space ,
vrp - > rbufs , vrp - > bufs_dma ) ;
2011-10-20 23:10:55 +04:00
kfree ( vrp ) ;
}
static struct virtio_device_id id_table [ ] = {
{ VIRTIO_ID_RPMSG , VIRTIO_DEV_ANY_ID } ,
{ 0 } ,
} ;
static unsigned int features [ ] = {
VIRTIO_RPMSG_F_NS ,
} ;
static struct virtio_driver virtio_ipc_driver = {
. feature_table = features ,
. feature_table_size = ARRAY_SIZE ( features ) ,
. driver . name = KBUILD_MODNAME ,
. driver . owner = THIS_MODULE ,
. id_table = id_table ,
. probe = rpmsg_probe ,
2012-12-22 03:14:44 +04:00
. remove = rpmsg_remove ,
2011-10-20 23:10:55 +04:00
} ;
static int __init rpmsg_init ( void )
{
int ret ;
ret = bus_register ( & rpmsg_bus ) ;
if ( ret ) {
pr_err ( " failed to register rpmsg bus: %d \n " , ret ) ;
return ret ;
}
ret = register_virtio_driver ( & virtio_ipc_driver ) ;
if ( ret ) {
pr_err ( " failed to register virtio driver: %d \n " , ret ) ;
bus_unregister ( & rpmsg_bus ) ;
}
return ret ;
}
2012-07-16 11:36:51 +04:00
subsys_initcall ( rpmsg_init ) ;
2011-10-20 23:10:55 +04:00
static void __exit rpmsg_fini ( void )
{
unregister_virtio_driver ( & virtio_ipc_driver ) ;
bus_unregister ( & rpmsg_bus ) ;
}
module_exit ( rpmsg_fini ) ;
MODULE_DEVICE_TABLE ( virtio , id_table ) ;
MODULE_DESCRIPTION ( " Virtio-based remote processor messaging bus " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;