2017-12-05 20:43:07 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2017 Linaro Ltd .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/device.h>
# include <linux/qrtr.h>
# include <linux/net.h>
# include <linux/completion.h>
# include <linux/idr.h>
# include <linux/string.h>
# include <net/sock.h>
# include <linux/workqueue.h>
# include <linux/soc/qcom/qmi.h>
static struct socket * qmi_sock_create ( struct qmi_handle * qmi ,
struct sockaddr_qrtr * sq ) ;
/**
* qmi_recv_new_server ( ) - handler of NEW_SERVER control message
* @ qmi : qmi handle
* @ service : service id of the new server
* @ instance : instance id of the new server
* @ node : node of the new server
* @ port : port of the new server
*
* Calls the new_server callback to inform the client about a newly registered
* server matching the currently registered service lookup .
*/
static void qmi_recv_new_server ( struct qmi_handle * qmi ,
unsigned int service , unsigned int instance ,
unsigned int node , unsigned int port )
{
struct qmi_ops * ops = & qmi - > ops ;
struct qmi_service * svc ;
int ret ;
if ( ! ops - > new_server )
return ;
/* Ignore EOF marker */
if ( ! node & & ! port )
return ;
svc = kzalloc ( sizeof ( * svc ) , GFP_KERNEL ) ;
if ( ! svc )
return ;
svc - > service = service ;
svc - > version = instance & 0xff ;
svc - > instance = instance > > 8 ;
svc - > node = node ;
svc - > port = port ;
ret = ops - > new_server ( qmi , svc ) ;
if ( ret < 0 )
kfree ( svc ) ;
else
list_add ( & svc - > list_node , & qmi - > lookup_results ) ;
}
/**
* qmi_recv_del_server ( ) - handler of DEL_SERVER control message
* @ qmi : qmi handle
* @ node : node of the dying server , a value of - 1 matches all nodes
* @ port : port of the dying server , a value of - 1 matches all ports
*
* Calls the del_server callback for each previously seen server , allowing the
* client to react to the disappearing server .
*/
static void qmi_recv_del_server ( struct qmi_handle * qmi ,
unsigned int node , unsigned int port )
{
struct qmi_ops * ops = & qmi - > ops ;
struct qmi_service * svc ;
struct qmi_service * tmp ;
list_for_each_entry_safe ( svc , tmp , & qmi - > lookup_results , list_node ) {
if ( node ! = - 1 & & svc - > node ! = node )
continue ;
if ( port ! = - 1 & & svc - > port ! = port )
continue ;
if ( ops - > del_server )
ops - > del_server ( qmi , svc ) ;
list_del ( & svc - > list_node ) ;
kfree ( svc ) ;
}
}
/**
* qmi_recv_bye ( ) - handler of BYE control message
* @ qmi : qmi handle
* @ node : id of the dying node
*
* Signals the client that all previously registered services on this node are
* now gone and then calls the bye callback to allow the client client further
* cleaning up resources associated with this remote .
*/
static void qmi_recv_bye ( struct qmi_handle * qmi ,
unsigned int node )
{
struct qmi_ops * ops = & qmi - > ops ;
qmi_recv_del_server ( qmi , node , - 1 ) ;
if ( ops - > bye )
ops - > bye ( qmi , node ) ;
}
/**
* qmi_recv_del_client ( ) - handler of DEL_CLIENT control message
* @ qmi : qmi handle
* @ node : node of the dying client
* @ port : port of the dying client
*
* Signals the client about a dying client , by calling the del_client callback .
*/
static void qmi_recv_del_client ( struct qmi_handle * qmi ,
unsigned int node , unsigned int port )
{
struct qmi_ops * ops = & qmi - > ops ;
if ( ops - > del_client )
ops - > del_client ( qmi , node , port ) ;
}
static void qmi_recv_ctrl_pkt ( struct qmi_handle * qmi ,
const void * buf , size_t len )
{
const struct qrtr_ctrl_pkt * pkt = buf ;
if ( len < sizeof ( struct qrtr_ctrl_pkt ) ) {
pr_debug ( " ignoring short control packet \n " ) ;
return ;
}
switch ( le32_to_cpu ( pkt - > cmd ) ) {
case QRTR_TYPE_BYE :
qmi_recv_bye ( qmi , le32_to_cpu ( pkt - > client . node ) ) ;
break ;
case QRTR_TYPE_NEW_SERVER :
qmi_recv_new_server ( qmi ,
le32_to_cpu ( pkt - > server . service ) ,
le32_to_cpu ( pkt - > server . instance ) ,
le32_to_cpu ( pkt - > server . node ) ,
le32_to_cpu ( pkt - > server . port ) ) ;
break ;
case QRTR_TYPE_DEL_SERVER :
qmi_recv_del_server ( qmi ,
le32_to_cpu ( pkt - > server . node ) ,
le32_to_cpu ( pkt - > server . port ) ) ;
break ;
case QRTR_TYPE_DEL_CLIENT :
qmi_recv_del_client ( qmi ,
le32_to_cpu ( pkt - > client . node ) ,
le32_to_cpu ( pkt - > client . port ) ) ;
break ;
}
}
static void qmi_send_new_lookup ( struct qmi_handle * qmi , struct qmi_service * svc )
{
struct qrtr_ctrl_pkt pkt ;
struct sockaddr_qrtr sq ;
struct msghdr msg = { } ;
struct kvec iv = { & pkt , sizeof ( pkt ) } ;
int ret ;
memset ( & pkt , 0 , sizeof ( pkt ) ) ;
pkt . cmd = cpu_to_le32 ( QRTR_TYPE_NEW_LOOKUP ) ;
pkt . server . service = cpu_to_le32 ( svc - > service ) ;
pkt . server . instance = cpu_to_le32 ( svc - > version | svc - > instance < < 8 ) ;
sq . sq_family = qmi - > sq . sq_family ;
sq . sq_node = qmi - > sq . sq_node ;
sq . sq_port = QRTR_PORT_CTRL ;
msg . msg_name = & sq ;
msg . msg_namelen = sizeof ( sq ) ;
mutex_lock ( & qmi - > sock_lock ) ;
if ( qmi - > sock ) {
ret = kernel_sendmsg ( qmi - > sock , & msg , & iv , 1 , sizeof ( pkt ) ) ;
if ( ret < 0 )
pr_err ( " failed to send lookup registration: %d \n " , ret ) ;
}
mutex_unlock ( & qmi - > sock_lock ) ;
}
/**
* qmi_add_lookup ( ) - register a new lookup with the name service
* @ qmi : qmi handle
* @ service : service id of the request
* @ instance : instance id of the request
* @ version : version number of the request
*
* Registering a lookup query with the name server will cause the name server
* to send NEW_SERVER and DEL_SERVER control messages to this socket as
* matching services are registered .
*
* Return : 0 on success , negative errno on failure .
*/
int qmi_add_lookup ( struct qmi_handle * qmi , unsigned int service ,
unsigned int version , unsigned int instance )
{
struct qmi_service * svc ;
svc = kzalloc ( sizeof ( * svc ) , GFP_KERNEL ) ;
if ( ! svc )
return - ENOMEM ;
svc - > service = service ;
svc - > version = version ;
svc - > instance = instance ;
list_add ( & svc - > list_node , & qmi - > lookups ) ;
qmi_send_new_lookup ( qmi , svc ) ;
return 0 ;
}
EXPORT_SYMBOL ( qmi_add_lookup ) ;
static void qmi_send_new_server ( struct qmi_handle * qmi , struct qmi_service * svc )
{
struct qrtr_ctrl_pkt pkt ;
struct sockaddr_qrtr sq ;
struct msghdr msg = { } ;
struct kvec iv = { & pkt , sizeof ( pkt ) } ;
int ret ;
memset ( & pkt , 0 , sizeof ( pkt ) ) ;
pkt . cmd = cpu_to_le32 ( QRTR_TYPE_NEW_SERVER ) ;
pkt . server . service = cpu_to_le32 ( svc - > service ) ;
pkt . server . instance = cpu_to_le32 ( svc - > version | svc - > instance < < 8 ) ;
pkt . server . node = cpu_to_le32 ( qmi - > sq . sq_node ) ;
pkt . server . port = cpu_to_le32 ( qmi - > sq . sq_port ) ;
sq . sq_family = qmi - > sq . sq_family ;
sq . sq_node = qmi - > sq . sq_node ;
sq . sq_port = QRTR_PORT_CTRL ;
msg . msg_name = & sq ;
msg . msg_namelen = sizeof ( sq ) ;
mutex_lock ( & qmi - > sock_lock ) ;
if ( qmi - > sock ) {
ret = kernel_sendmsg ( qmi - > sock , & msg , & iv , 1 , sizeof ( pkt ) ) ;
if ( ret < 0 )
pr_err ( " send service registration failed: %d \n " , ret ) ;
}
mutex_unlock ( & qmi - > sock_lock ) ;
}
/**
* qmi_add_server ( ) - register a service with the name service
* @ qmi : qmi handle
* @ service : type of the service
* @ instance : instance of the service
* @ version : version of the service
*
* Register a new service with the name service . This allows clients to find
* and start sending messages to the client associated with @ qmi .
*
* Return : 0 on success , negative errno on failure .
*/
int qmi_add_server ( struct qmi_handle * qmi , unsigned int service ,
unsigned int version , unsigned int instance )
{
struct qmi_service * svc ;
svc = kzalloc ( sizeof ( * svc ) , GFP_KERNEL ) ;
if ( ! svc )
return - ENOMEM ;
svc - > service = service ;
svc - > version = version ;
svc - > instance = instance ;
list_add ( & svc - > list_node , & qmi - > services ) ;
qmi_send_new_server ( qmi , svc ) ;
return 0 ;
}
EXPORT_SYMBOL ( qmi_add_server ) ;
/**
* qmi_txn_init ( ) - allocate transaction id within the given QMI handle
* @ qmi : QMI handle
* @ txn : transaction context
* @ ei : description of how to decode a matching response ( optional )
* @ c_struct : pointer to the object to decode the response into ( optional )
*
* This allocates a transaction id within the QMI handle . If @ ei and @ c_struct
* are specified any responses to this transaction will be decoded as described
* by @ ei into @ c_struct .
*
* A client calling qmi_txn_init ( ) must call either qmi_txn_wait ( ) or
* qmi_txn_cancel ( ) to free up the allocated resources .
*
* Return : Transaction id on success , negative errno on failure .
*/
int qmi_txn_init ( struct qmi_handle * qmi , struct qmi_txn * txn ,
struct qmi_elem_info * ei , void * c_struct )
{
int ret ;
memset ( txn , 0 , sizeof ( * txn ) ) ;
mutex_init ( & txn - > lock ) ;
init_completion ( & txn - > completion ) ;
txn - > qmi = qmi ;
txn - > ei = ei ;
txn - > dest = c_struct ;
mutex_lock ( & qmi - > txn_lock ) ;
2018-10-03 08:40:02 +03:00
ret = idr_alloc_cyclic ( & qmi - > txns , txn , 0 , U16_MAX , GFP_KERNEL ) ;
2017-12-05 20:43:07 +03:00
if ( ret < 0 )
pr_err ( " failed to allocate transaction id \n " ) ;
txn - > id = ret ;
mutex_unlock ( & qmi - > txn_lock ) ;
return ret ;
}
EXPORT_SYMBOL ( qmi_txn_init ) ;
/**
* qmi_txn_wait ( ) - wait for a response on a transaction
* @ txn : transaction handle
* @ timeout : timeout , in jiffies
*
* If the transaction is decoded by the means of @ ei and @ c_struct the return
* value will be the returned value of qmi_decode_message ( ) , otherwise it ' s up
* to the specified message handler to fill out the result .
*
* Return : the transaction response on success , negative errno on failure .
*/
int qmi_txn_wait ( struct qmi_txn * txn , unsigned long timeout )
{
struct qmi_handle * qmi = txn - > qmi ;
int ret ;
2019-02-22 05:33:39 +03:00
ret = wait_for_completion_timeout ( & txn - > completion , timeout ) ;
2017-12-05 20:43:07 +03:00
mutex_lock ( & qmi - > txn_lock ) ;
mutex_lock ( & txn - > lock ) ;
idr_remove ( & qmi - > txns , txn - > id ) ;
mutex_unlock ( & txn - > lock ) ;
mutex_unlock ( & qmi - > txn_lock ) ;
2019-02-22 05:33:39 +03:00
if ( ret = = 0 )
2017-12-05 20:43:07 +03:00
return - ETIMEDOUT ;
else
return txn - > result ;
}
EXPORT_SYMBOL ( qmi_txn_wait ) ;
/**
* qmi_txn_cancel ( ) - cancel an ongoing transaction
* @ txn : transaction id
*/
void qmi_txn_cancel ( struct qmi_txn * txn )
{
struct qmi_handle * qmi = txn - > qmi ;
mutex_lock ( & qmi - > txn_lock ) ;
mutex_lock ( & txn - > lock ) ;
idr_remove ( & qmi - > txns , txn - > id ) ;
mutex_unlock ( & txn - > lock ) ;
mutex_unlock ( & qmi - > txn_lock ) ;
}
EXPORT_SYMBOL ( qmi_txn_cancel ) ;
/**
* qmi_invoke_handler ( ) - find and invoke a handler for a message
* @ qmi : qmi handle
* @ sq : sockaddr of the sender
* @ txn : transaction object for the message
* @ buf : buffer containing the message
* @ len : length of @ buf
*
* Find handler and invoke handler for the incoming message .
*/
static void qmi_invoke_handler ( struct qmi_handle * qmi , struct sockaddr_qrtr * sq ,
struct qmi_txn * txn , const void * buf , size_t len )
{
const struct qmi_msg_handler * handler ;
const struct qmi_header * hdr = buf ;
void * dest ;
int ret ;
if ( ! qmi - > handlers )
return ;
for ( handler = qmi - > handlers ; handler - > fn ; handler + + ) {
if ( handler - > type = = hdr - > type & &
handler - > msg_id = = hdr - > msg_id )
break ;
}
if ( ! handler - > fn )
return ;
dest = kzalloc ( handler - > decoded_size , GFP_KERNEL ) ;
if ( ! dest )
return ;
ret = qmi_decode_message ( buf , len , handler - > ei , dest ) ;
if ( ret < 0 )
pr_err ( " failed to decode incoming message \n " ) ;
else
handler - > fn ( qmi , sq , txn , dest ) ;
kfree ( dest ) ;
}
/**
* qmi_handle_net_reset ( ) - invoked to handle ENETRESET on a QMI handle
* @ qmi : the QMI context
*
* As a result of registering a name service with the QRTR all open sockets are
* flagged with ENETRESET and this function will be called . The typical case is
* the initial boot , where this signals that the local node id has been
* configured and as such any bound sockets needs to be rebound . So close the
* socket , inform the client and re - initialize the socket .
*
* For clients it ' s generally sufficient to react to the del_server callbacks ,
* but server code is expected to treat the net_reset callback as a " bye " from
* all nodes .
*
* Finally the QMI handle will send out registration requests for any lookups
* and services .
*/
static void qmi_handle_net_reset ( struct qmi_handle * qmi )
{
struct sockaddr_qrtr sq ;
struct qmi_service * svc ;
struct socket * sock ;
sock = qmi_sock_create ( qmi , & sq ) ;
if ( IS_ERR ( sock ) )
return ;
mutex_lock ( & qmi - > sock_lock ) ;
sock_release ( qmi - > sock ) ;
qmi - > sock = NULL ;
mutex_unlock ( & qmi - > sock_lock ) ;
qmi_recv_del_server ( qmi , - 1 , - 1 ) ;
if ( qmi - > ops . net_reset )
qmi - > ops . net_reset ( qmi ) ;
mutex_lock ( & qmi - > sock_lock ) ;
qmi - > sock = sock ;
qmi - > sq = sq ;
mutex_unlock ( & qmi - > sock_lock ) ;
list_for_each_entry ( svc , & qmi - > lookups , list_node )
qmi_send_new_lookup ( qmi , svc ) ;
list_for_each_entry ( svc , & qmi - > services , list_node )
qmi_send_new_server ( qmi , svc ) ;
}
static void qmi_handle_message ( struct qmi_handle * qmi ,
struct sockaddr_qrtr * sq ,
const void * buf , size_t len )
{
const struct qmi_header * hdr ;
struct qmi_txn tmp_txn ;
struct qmi_txn * txn = NULL ;
int ret ;
if ( len < sizeof ( * hdr ) ) {
pr_err ( " ignoring short QMI packet \n " ) ;
return ;
}
hdr = buf ;
/* If this is a response, find the matching transaction handle */
if ( hdr - > type = = QMI_RESPONSE ) {
mutex_lock ( & qmi - > txn_lock ) ;
txn = idr_find ( & qmi - > txns , hdr - > txn_id ) ;
/* Ignore unexpected responses */
if ( ! txn ) {
mutex_unlock ( & qmi - > txn_lock ) ;
return ;
}
mutex_lock ( & txn - > lock ) ;
mutex_unlock ( & qmi - > txn_lock ) ;
if ( txn - > dest & & txn - > ei ) {
ret = qmi_decode_message ( buf , len , txn - > ei , txn - > dest ) ;
if ( ret < 0 )
pr_err ( " failed to decode incoming message \n " ) ;
txn - > result = ret ;
complete ( & txn - > completion ) ;
} else {
qmi_invoke_handler ( qmi , sq , txn , buf , len ) ;
}
mutex_unlock ( & txn - > lock ) ;
} else {
/* Create a txn based on the txn_id of the incoming message */
memset ( & tmp_txn , 0 , sizeof ( tmp_txn ) ) ;
tmp_txn . id = hdr - > txn_id ;
qmi_invoke_handler ( qmi , sq , & tmp_txn , buf , len ) ;
}
}
static void qmi_data_ready_work ( struct work_struct * work )
{
struct qmi_handle * qmi = container_of ( work , struct qmi_handle , work ) ;
struct qmi_ops * ops = & qmi - > ops ;
struct sockaddr_qrtr sq ;
struct msghdr msg = { . msg_name = & sq , . msg_namelen = sizeof ( sq ) } ;
struct kvec iv ;
ssize_t msglen ;
for ( ; ; ) {
iv . iov_base = qmi - > recv_buf ;
iv . iov_len = qmi - > recv_buf_size ;
mutex_lock ( & qmi - > sock_lock ) ;
if ( qmi - > sock )
msglen = kernel_recvmsg ( qmi - > sock , & msg , & iv , 1 ,
iv . iov_len , MSG_DONTWAIT ) ;
else
msglen = - EPIPE ;
mutex_unlock ( & qmi - > sock_lock ) ;
if ( msglen = = - EAGAIN )
break ;
if ( msglen = = - ENETRESET ) {
qmi_handle_net_reset ( qmi ) ;
/* The old qmi->sock is gone, our work is done */
break ;
}
if ( msglen < 0 ) {
pr_err ( " qmi recvmsg failed: %zd \n " , msglen ) ;
break ;
}
if ( sq . sq_node = = qmi - > sq . sq_node & &
sq . sq_port = = QRTR_PORT_CTRL ) {
qmi_recv_ctrl_pkt ( qmi , qmi - > recv_buf , msglen ) ;
} else if ( ops - > msg_handler ) {
ops - > msg_handler ( qmi , & sq , qmi - > recv_buf , msglen ) ;
} else {
qmi_handle_message ( qmi , & sq , qmi - > recv_buf , msglen ) ;
}
}
}
static void qmi_data_ready ( struct sock * sk )
{
struct qmi_handle * qmi = sk - > sk_user_data ;
/*
* This will be NULL if we receive data while being in
* qmi_handle_release ( )
*/
if ( ! qmi )
return ;
queue_work ( qmi - > wq , & qmi - > work ) ;
}
static struct socket * qmi_sock_create ( struct qmi_handle * qmi ,
struct sockaddr_qrtr * sq )
{
struct socket * sock ;
int ret ;
ret = sock_create_kern ( & init_net , AF_QIPCRTR , SOCK_DGRAM ,
PF_QIPCRTR , & sock ) ;
if ( ret < 0 )
return ERR_PTR ( ret ) ;
2018-02-12 22:00:20 +03:00
ret = kernel_getsockname ( sock , ( struct sockaddr * ) sq ) ;
2017-12-05 20:43:07 +03:00
if ( ret < 0 ) {
sock_release ( sock ) ;
return ERR_PTR ( ret ) ;
}
sock - > sk - > sk_user_data = qmi ;
sock - > sk - > sk_data_ready = qmi_data_ready ;
sock - > sk - > sk_error_report = qmi_data_ready ;
return sock ;
}
/**
* qmi_handle_init ( ) - initialize a QMI client handle
* @ qmi : QMI handle to initialize
* @ recv_buf_size : maximum size of incoming message
* @ ops : reference to callbacks for QRTR notifications
* @ handlers : NULL - terminated list of QMI message handlers
*
* This initializes the QMI client handle to allow sending and receiving QMI
* messages . As messages are received the appropriate handler will be invoked .
*
* Return : 0 on success , negative errno on failure .
*/
int qmi_handle_init ( struct qmi_handle * qmi , size_t recv_buf_size ,
const struct qmi_ops * ops ,
const struct qmi_msg_handler * handlers )
{
int ret ;
mutex_init ( & qmi - > txn_lock ) ;
mutex_init ( & qmi - > sock_lock ) ;
idr_init ( & qmi - > txns ) ;
INIT_LIST_HEAD ( & qmi - > lookups ) ;
INIT_LIST_HEAD ( & qmi - > lookup_results ) ;
INIT_LIST_HEAD ( & qmi - > services ) ;
INIT_WORK ( & qmi - > work , qmi_data_ready_work ) ;
qmi - > handlers = handlers ;
if ( ops )
qmi - > ops = * ops ;
2018-04-27 17:08:17 +03:00
/* Make room for the header */
recv_buf_size + = sizeof ( struct qmi_header ) ;
/* Must also be sufficient to hold a control packet */
2017-12-05 20:43:07 +03:00
if ( recv_buf_size < sizeof ( struct qrtr_ctrl_pkt ) )
recv_buf_size = sizeof ( struct qrtr_ctrl_pkt ) ;
qmi - > recv_buf_size = recv_buf_size ;
qmi - > recv_buf = kzalloc ( recv_buf_size , GFP_KERNEL ) ;
if ( ! qmi - > recv_buf )
return - ENOMEM ;
qmi - > wq = alloc_workqueue ( " qmi_msg_handler " , WQ_UNBOUND , 1 ) ;
if ( ! qmi - > wq ) {
ret = - ENOMEM ;
goto err_free_recv_buf ;
}
qmi - > sock = qmi_sock_create ( qmi , & qmi - > sq ) ;
if ( IS_ERR ( qmi - > sock ) ) {
pr_err ( " failed to create QMI socket \n " ) ;
ret = PTR_ERR ( qmi - > sock ) ;
goto err_destroy_wq ;
}
return 0 ;
err_destroy_wq :
destroy_workqueue ( qmi - > wq ) ;
err_free_recv_buf :
kfree ( qmi - > recv_buf ) ;
return ret ;
}
EXPORT_SYMBOL ( qmi_handle_init ) ;
/**
* qmi_handle_release ( ) - release the QMI client handle
* @ qmi : QMI client handle
*
* This closes the underlying socket and stops any handling of QMI messages .
*/
void qmi_handle_release ( struct qmi_handle * qmi )
{
struct socket * sock = qmi - > sock ;
struct qmi_service * svc , * tmp ;
sock - > sk - > sk_user_data = NULL ;
cancel_work_sync ( & qmi - > work ) ;
qmi_recv_del_server ( qmi , - 1 , - 1 ) ;
mutex_lock ( & qmi - > sock_lock ) ;
sock_release ( sock ) ;
qmi - > sock = NULL ;
mutex_unlock ( & qmi - > sock_lock ) ;
destroy_workqueue ( qmi - > wq ) ;
idr_destroy ( & qmi - > txns ) ;
kfree ( qmi - > recv_buf ) ;
/* Free registered lookup requests */
list_for_each_entry_safe ( svc , tmp , & qmi - > lookups , list_node ) {
list_del ( & svc - > list_node ) ;
kfree ( svc ) ;
}
/* Free registered service information */
list_for_each_entry_safe ( svc , tmp , & qmi - > services , list_node ) {
list_del ( & svc - > list_node ) ;
kfree ( svc ) ;
}
}
EXPORT_SYMBOL ( qmi_handle_release ) ;
/**
* qmi_send_message ( ) - send a QMI message
* @ qmi : QMI client handle
* @ sq : destination sockaddr
* @ txn : transaction object to use for the message
* @ type : type of message to send
* @ msg_id : message id
* @ len : max length of the QMI message
* @ ei : QMI message description
* @ c_struct : object to be encoded
*
* This function encodes @ c_struct using @ ei into a message of type @ type ,
* with @ msg_id and @ txn into a buffer of maximum size @ len , and sends this to
* @ sq .
*
* Return : 0 on success , negative errno on failure .
*/
static ssize_t qmi_send_message ( struct qmi_handle * qmi ,
struct sockaddr_qrtr * sq , struct qmi_txn * txn ,
int type , int msg_id , size_t len ,
struct qmi_elem_info * ei , const void * c_struct )
{
struct msghdr msghdr = { } ;
struct kvec iv ;
void * msg ;
int ret ;
msg = qmi_encode_message ( type ,
msg_id , & len ,
txn - > id , ei ,
c_struct ) ;
if ( IS_ERR ( msg ) )
return PTR_ERR ( msg ) ;
iv . iov_base = msg ;
iv . iov_len = len ;
if ( sq ) {
msghdr . msg_name = sq ;
msghdr . msg_namelen = sizeof ( * sq ) ;
}
mutex_lock ( & qmi - > sock_lock ) ;
if ( qmi - > sock ) {
ret = kernel_sendmsg ( qmi - > sock , & msghdr , & iv , 1 , len ) ;
if ( ret < 0 )
pr_err ( " failed to send QMI message \n " ) ;
} else {
ret = - EPIPE ;
}
mutex_unlock ( & qmi - > sock_lock ) ;
kfree ( msg ) ;
return ret < 0 ? ret : 0 ;
}
/**
* qmi_send_request ( ) - send a request QMI message
* @ qmi : QMI client handle
* @ sq : destination sockaddr
* @ txn : transaction object to use for the message
* @ msg_id : message id
* @ len : max length of the QMI message
* @ ei : QMI message description
* @ c_struct : object to be encoded
*
* Return : 0 on success , negative errno on failure .
*/
ssize_t qmi_send_request ( struct qmi_handle * qmi , struct sockaddr_qrtr * sq ,
struct qmi_txn * txn , int msg_id , size_t len ,
struct qmi_elem_info * ei , const void * c_struct )
{
return qmi_send_message ( qmi , sq , txn , QMI_REQUEST , msg_id , len , ei ,
c_struct ) ;
}
EXPORT_SYMBOL ( qmi_send_request ) ;
/**
* qmi_send_response ( ) - send a response QMI message
* @ qmi : QMI client handle
* @ sq : destination sockaddr
* @ txn : transaction object to use for the message
* @ msg_id : message id
* @ len : max length of the QMI message
* @ ei : QMI message description
* @ c_struct : object to be encoded
*
* Return : 0 on success , negative errno on failure .
*/
ssize_t qmi_send_response ( struct qmi_handle * qmi , struct sockaddr_qrtr * sq ,
struct qmi_txn * txn , int msg_id , size_t len ,
struct qmi_elem_info * ei , const void * c_struct )
{
return qmi_send_message ( qmi , sq , txn , QMI_RESPONSE , msg_id , len , ei ,
c_struct ) ;
}
EXPORT_SYMBOL ( qmi_send_response ) ;
/**
* qmi_send_indication ( ) - send an indication QMI message
* @ qmi : QMI client handle
* @ sq : destination sockaddr
* @ msg_id : message id
* @ len : max length of the QMI message
* @ ei : QMI message description
* @ c_struct : object to be encoded
*
* Return : 0 on success , negative errno on failure .
*/
ssize_t qmi_send_indication ( struct qmi_handle * qmi , struct sockaddr_qrtr * sq ,
int msg_id , size_t len , struct qmi_elem_info * ei ,
const void * c_struct )
{
struct qmi_txn txn ;
ssize_t rval ;
int ret ;
ret = qmi_txn_init ( qmi , & txn , NULL , NULL ) ;
if ( ret < 0 )
return ret ;
rval = qmi_send_message ( qmi , sq , & txn , QMI_INDICATION , msg_id , len , ei ,
c_struct ) ;
/* We don't care about future messages on this txn */
qmi_txn_cancel ( & txn ) ;
return rval ;
}
EXPORT_SYMBOL ( qmi_send_indication ) ;