2022-02-10 17:05:24 +02:00
// SPDX-License-Identifier: GPL-2.0-only
//
// Copyright(c) 2022 Intel Corporation. All rights reserved.
//
// Author: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
//
# include <linux/auxiliary_bus.h>
# include <linux/completion.h>
# include <linux/debugfs.h>
# include <linux/ktime.h>
# include <linux/mod_devicetable.h>
# include <linux/module.h>
# include <linux/pm_runtime.h>
# include <linux/slab.h>
# include <linux/uaccess.h>
# include <sound/sof/header.h>
2022-05-06 16:26:46 +03:00
# include <sound/sof/ipc4/header.h>
2022-02-10 17:05:24 +02:00
# include "sof-client.h"
# define SOF_IPC_CLIENT_SUSPEND_DELAY_MS 3000
struct sof_msg_inject_priv {
struct dentry * dfs_file ;
2022-05-06 16:26:41 +03:00
size_t max_msg_size ;
2022-05-06 16:26:46 +03:00
enum sof_ipc_type ipc_type ;
2022-02-10 17:05:24 +02:00
void * tx_buffer ;
void * rx_buffer ;
} ;
static int sof_msg_inject_dfs_open ( struct inode * inode , struct file * file )
{
struct sof_client_dev * cdev = inode - > i_private ;
int ret ;
if ( sof_client_get_fw_state ( cdev ) = = SOF_FW_CRASHED )
return - ENODEV ;
ret = debugfs_file_get ( file - > f_path . dentry ) ;
if ( unlikely ( ret ) )
return ret ;
ret = simple_open ( inode , file ) ;
if ( ret )
debugfs_file_put ( file - > f_path . dentry ) ;
return ret ;
}
static ssize_t sof_msg_inject_dfs_read ( struct file * file , char __user * buffer ,
size_t count , loff_t * ppos )
{
struct sof_client_dev * cdev = file - > private_data ;
struct sof_msg_inject_priv * priv = cdev - > data ;
struct sof_ipc_reply * rhdr = priv - > rx_buffer ;
if ( ! rhdr - > hdr . size | | ! count | | * ppos )
return 0 ;
if ( count > rhdr - > hdr . size )
count = rhdr - > hdr . size ;
if ( copy_to_user ( buffer , priv - > rx_buffer , count ) )
return - EFAULT ;
* ppos + = count ;
return count ;
}
2022-05-06 16:26:46 +03:00
static ssize_t sof_msg_inject_ipc4_dfs_read ( struct file * file ,
char __user * buffer ,
size_t count , loff_t * ppos )
{
struct sof_client_dev * cdev = file - > private_data ;
struct sof_msg_inject_priv * priv = cdev - > data ;
struct sof_ipc4_msg * ipc4_msg = priv - > rx_buffer ;
2022-05-16 12:24:42 +03:00
size_t header_size = sizeof ( ipc4_msg - > header_u64 ) ;
2022-05-06 16:26:46 +03:00
size_t remaining ;
if ( ! ipc4_msg - > header_u64 | | ! count | | * ppos )
return 0 ;
2022-05-16 12:24:42 +03:00
/* we need space for the header at minimum (u64) */
if ( count < header_size )
return - ENOSPC ;
remaining = header_size ;
2022-05-06 16:26:46 +03:00
/* Only get large config have payload */
if ( SOF_IPC4_MSG_IS_MODULE_MSG ( ipc4_msg - > primary ) & &
( SOF_IPC4_MSG_TYPE_GET ( ipc4_msg - > primary ) = = SOF_IPC4_MOD_LARGE_CONFIG_GET ) )
remaining + = ipc4_msg - > data_size ;
if ( count > remaining )
count = remaining ;
2022-05-16 12:24:42 +03:00
else if ( count < remaining )
remaining = count ;
2022-05-06 16:26:46 +03:00
/* copy the header first */
2022-05-16 12:24:42 +03:00
if ( copy_to_user ( buffer , & ipc4_msg - > header_u64 , header_size ) )
2022-05-06 16:26:46 +03:00
return - EFAULT ;
2022-05-16 12:24:42 +03:00
* ppos + = header_size ;
remaining - = header_size ;
2022-05-06 16:26:46 +03:00
if ( ! remaining )
return count ;
if ( remaining > ipc4_msg - > data_size )
remaining = ipc4_msg - > data_size ;
/* Copy the payload */
if ( copy_to_user ( buffer + * ppos , ipc4_msg - > data_ptr , remaining ) )
return - EFAULT ;
* ppos + = remaining ;
return count ;
}
2022-05-06 16:26:45 +03:00
static int sof_msg_inject_send_message ( struct sof_client_dev * cdev )
2022-02-10 17:05:24 +02:00
{
struct sof_msg_inject_priv * priv = cdev - > data ;
struct device * dev = & cdev - > auxdev . dev ;
int ret , err ;
2022-04-26 13:41:06 -05:00
ret = pm_runtime_resume_and_get ( dev ) ;
2022-02-10 17:05:24 +02:00
if ( ret < 0 & & ret ! = - EACCES ) {
dev_err_ratelimited ( dev , " debugfs write failed to resume %d \n " , ret ) ;
return ret ;
}
/* send the message */
ret = sof_client_ipc_tx_message ( cdev , priv - > tx_buffer , priv - > rx_buffer ,
2022-05-06 16:26:41 +03:00
priv - > max_msg_size ) ;
2022-05-06 16:26:45 +03:00
if ( ret )
dev_err ( dev , " IPC message send failed: %d \n " , ret ) ;
2022-02-10 17:05:24 +02:00
pm_runtime_mark_last_busy ( dev ) ;
err = pm_runtime_put_autosuspend ( dev ) ;
if ( err < 0 )
dev_err_ratelimited ( dev , " debugfs write failed to idle %d \n " , err ) ;
return ret ;
2022-05-06 16:26:45 +03:00
}
static ssize_t sof_msg_inject_dfs_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * ppos )
{
struct sof_client_dev * cdev = file - > private_data ;
struct sof_msg_inject_priv * priv = cdev - > data ;
2022-06-02 12:08:25 +03:00
ssize_t size ;
2022-05-06 16:26:45 +03:00
int ret ;
if ( * ppos )
return 0 ;
size = simple_write_to_buffer ( priv - > tx_buffer , priv - > max_msg_size ,
ppos , buffer , count ) ;
2022-06-02 12:08:25 +03:00
if ( size < 0 )
return size ;
2022-05-06 16:26:45 +03:00
if ( size ! = count )
2022-06-02 12:08:25 +03:00
return - EFAULT ;
2022-05-06 16:26:45 +03:00
memset ( priv - > rx_buffer , 0 , priv - > max_msg_size ) ;
ret = sof_msg_inject_send_message ( cdev ) ;
/* return the error code if test failed */
if ( ret < 0 )
size = ret ;
return size ;
2022-02-10 17:05:24 +02:00
} ;
2022-05-06 16:26:46 +03:00
static ssize_t sof_msg_inject_ipc4_dfs_write ( struct file * file ,
const char __user * buffer ,
size_t count , loff_t * ppos )
{
struct sof_client_dev * cdev = file - > private_data ;
struct sof_msg_inject_priv * priv = cdev - > data ;
struct sof_ipc4_msg * ipc4_msg = priv - > tx_buffer ;
2022-07-08 16:48:36 +03:00
size_t data_size ;
2022-05-06 16:26:46 +03:00
int ret ;
if ( * ppos )
return 0 ;
if ( count < sizeof ( ipc4_msg - > header_u64 ) )
return - EINVAL ;
/* copy the header first */
2022-07-08 16:48:36 +03:00
if ( copy_from_user ( & ipc4_msg - > header_u64 , buffer ,
sizeof ( ipc4_msg - > header_u64 ) ) )
2022-06-02 12:08:25 +03:00
return - EFAULT ;
2022-05-06 16:26:46 +03:00
2022-07-08 16:48:36 +03:00
data_size = count - sizeof ( ipc4_msg - > header_u64 ) ;
if ( data_size > priv - > max_msg_size )
return - EINVAL ;
2022-06-02 12:09:35 +03:00
/* Copy the payload */
2022-07-08 16:48:36 +03:00
if ( copy_from_user ( ipc4_msg - > data_ptr ,
buffer + sizeof ( ipc4_msg - > header_u64 ) , data_size ) )
2022-06-02 12:09:35 +03:00
return - EFAULT ;
2022-05-06 16:26:46 +03:00
2022-07-08 16:48:36 +03:00
ipc4_msg - > data_size = data_size ;
2022-05-06 16:26:46 +03:00
/* Initialize the reply storage */
ipc4_msg = priv - > rx_buffer ;
ipc4_msg - > header_u64 = 0 ;
ipc4_msg - > data_size = priv - > max_msg_size ;
memset ( ipc4_msg - > data_ptr , 0 , priv - > max_msg_size ) ;
ret = sof_msg_inject_send_message ( cdev ) ;
/* return the error code if test failed */
if ( ret < 0 )
2022-07-08 16:48:36 +03:00
return ret ;
2022-05-06 16:26:46 +03:00
2022-07-08 16:48:36 +03:00
return count ;
2022-05-06 16:26:46 +03:00
} ;
2022-02-10 17:05:24 +02:00
static int sof_msg_inject_dfs_release ( struct inode * inode , struct file * file )
{
debugfs_file_put ( file - > f_path . dentry ) ;
return 0 ;
}
static const struct file_operations sof_msg_inject_fops = {
. open = sof_msg_inject_dfs_open ,
. read = sof_msg_inject_dfs_read ,
. write = sof_msg_inject_dfs_write ,
. llseek = default_llseek ,
. release = sof_msg_inject_dfs_release ,
. owner = THIS_MODULE ,
} ;
2022-05-06 16:26:46 +03:00
static const struct file_operations sof_msg_inject_ipc4_fops = {
. open = sof_msg_inject_dfs_open ,
. read = sof_msg_inject_ipc4_dfs_read ,
. write = sof_msg_inject_ipc4_dfs_write ,
. llseek = default_llseek ,
. release = sof_msg_inject_dfs_release ,
. owner = THIS_MODULE ,
} ;
2022-02-10 17:05:24 +02:00
static int sof_msg_inject_probe ( struct auxiliary_device * auxdev ,
const struct auxiliary_device_id * id )
{
struct sof_client_dev * cdev = auxiliary_dev_to_sof_client_dev ( auxdev ) ;
struct dentry * debugfs_root = sof_client_get_debugfs_root ( cdev ) ;
2022-05-06 16:26:46 +03:00
static const struct file_operations * fops ;
2022-02-10 17:05:24 +02:00
struct device * dev = & auxdev - > dev ;
struct sof_msg_inject_priv * priv ;
2022-05-06 16:26:46 +03:00
size_t alloc_size ;
2022-02-10 17:05:24 +02:00
/* allocate memory for client data */
priv = devm_kzalloc ( & auxdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
2022-05-06 16:26:46 +03:00
priv - > ipc_type = sof_client_get_ipc_type ( cdev ) ;
2022-05-06 16:26:41 +03:00
priv - > max_msg_size = sof_client_get_ipc_max_payload_size ( cdev ) ;
2022-05-06 16:26:46 +03:00
alloc_size = priv - > max_msg_size ;
if ( priv - > ipc_type = = SOF_INTEL_IPC4 )
alloc_size + = sizeof ( struct sof_ipc4_msg ) ;
priv - > tx_buffer = devm_kmalloc ( dev , alloc_size , GFP_KERNEL ) ;
priv - > rx_buffer = devm_kzalloc ( dev , alloc_size , GFP_KERNEL ) ;
2022-02-10 17:05:24 +02:00
if ( ! priv - > tx_buffer | | ! priv - > rx_buffer )
return - ENOMEM ;
2022-05-06 16:26:46 +03:00
if ( priv - > ipc_type = = SOF_INTEL_IPC4 ) {
struct sof_ipc4_msg * ipc4_msg ;
ipc4_msg = priv - > tx_buffer ;
ipc4_msg - > data_ptr = priv - > tx_buffer + sizeof ( struct sof_ipc4_msg ) ;
ipc4_msg = priv - > rx_buffer ;
ipc4_msg - > data_ptr = priv - > rx_buffer + sizeof ( struct sof_ipc4_msg ) ;
fops = & sof_msg_inject_ipc4_fops ;
} else {
fops = & sof_msg_inject_fops ;
}
2022-02-10 17:05:24 +02:00
cdev - > data = priv ;
priv - > dfs_file = debugfs_create_file ( " ipc_msg_inject " , 0644 , debugfs_root ,
2022-05-06 16:26:46 +03:00
cdev , fops ) ;
2022-02-10 17:05:24 +02:00
/* enable runtime PM */
pm_runtime_set_autosuspend_delay ( dev , SOF_IPC_CLIENT_SUSPEND_DELAY_MS ) ;
pm_runtime_use_autosuspend ( dev ) ;
pm_runtime_enable ( dev ) ;
pm_runtime_mark_last_busy ( dev ) ;
pm_runtime_idle ( dev ) ;
return 0 ;
}
static void sof_msg_inject_remove ( struct auxiliary_device * auxdev )
{
struct sof_client_dev * cdev = auxiliary_dev_to_sof_client_dev ( auxdev ) ;
struct sof_msg_inject_priv * priv = cdev - > data ;
pm_runtime_disable ( & auxdev - > dev ) ;
debugfs_remove ( priv - > dfs_file ) ;
}
static const struct auxiliary_device_id sof_msg_inject_client_id_table [ ] = {
{ . name = " snd_sof.msg_injector " } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( auxiliary , sof_msg_inject_client_id_table ) ;
/*
* No need for driver pm_ops as the generic pm callbacks in the auxiliary bus
* type are enough to ensure that the parent SOF device resumes to bring the DSP
* back to D0 .
* Driver name will be set based on KBUILD_MODNAME .
*/
static struct auxiliary_driver sof_msg_inject_client_drv = {
. probe = sof_msg_inject_probe ,
. remove = sof_msg_inject_remove ,
. id_table = sof_msg_inject_client_id_table ,
} ;
module_auxiliary_driver ( sof_msg_inject_client_drv ) ;
MODULE_DESCRIPTION ( " SOF IPC Message Injector Client Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_IMPORT_NS ( SND_SOC_SOF_CLIENT ) ;