#define pr_fmt(fmt) "mwavedd: " fmt
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/serial.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/serial_8250.h>
#include <linux/nospec.h>
#include "smapi.h"
#include "mwavedd.h"
#include "3780i.h"
#include "tp3780i.h"
MODULE_DESCRIPTION("3780i Advanced Communications Processor (Mwave) driver");
MODULE_AUTHOR("Mike Sullivan and Paul Schroeder");
MODULE_LICENSE("GPL");
static DEFINE_MUTEX(mwave_mutex);
int mwave_3780i_irq = 0;
int mwave_3780i_io = 0;
int mwave_uart_irq = 0;
int mwave_uart_io = 0;
module_param_hw(mwave_3780i_irq, int, irq, 0);
module_param_hw(mwave_3780i_io, int, ioport, 0);
module_param_hw(mwave_uart_irq, int, irq, 0);
module_param_hw(mwave_uart_io, int, ioport, 0);
struct mwave_device_data mwave_s_mdd;
static long mwave_ioctl(struct file *file, unsigned int iocmd,
unsigned long ioarg)
{
unsigned int retval = 0;
struct mwave_device_data *pDrvData = &mwave_s_mdd;
void __user *arg = (void __user *)ioarg;
switch (iocmd) {
case IOCTL_MW_RESET:
mutex_lock(&mwave_mutex);
retval = tp3780I_ResetDSP(&pDrvData->rBDData);
mutex_unlock(&mwave_mutex);
break;
case IOCTL_MW_RUN:
mutex_lock(&mwave_mutex);
retval = tp3780I_StartDSP(&pDrvData->rBDData);
mutex_unlock(&mwave_mutex);
break;
case IOCTL_MW_DSP_ABILITIES: {
struct mw_abilities rAbilities;
mutex_lock(&mwave_mutex);
retval = tp3780I_QueryAbilities(&pDrvData->rBDData,
&rAbilities);
mutex_unlock(&mwave_mutex);
if (retval == 0) {
if (copy_to_user(arg, &rAbilities, sizeof(rAbilities)))
return -EFAULT;
}
}
break;
case IOCTL_MW_READ_DATA:
case IOCTL_MW_READCLEAR_DATA: {
struct mw_readwrite rReadData;
unsigned short __user *pusBuffer = NULL;
if( copy_from_user(&rReadData, arg,
sizeof(struct mw_readwrite)) )
return -EFAULT;
pusBuffer = (unsigned short __user *) (rReadData.pBuf);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd,
pusBuffer,
rReadData.ulDataLength,
rReadData.usDspAddress);
mutex_unlock(&mwave_mutex);
}
break;
case IOCTL_MW_READ_INST: {
struct mw_readwrite rReadData;
unsigned short __user *pusBuffer = NULL;
if (copy_from_user(&rReadData, arg, sizeof(rReadData)))
return -EFAULT;
pusBuffer = (unsigned short __user *) (rReadData.pBuf);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd, pusBuffer,
rReadData.ulDataLength / 2,
rReadData.usDspAddress);
mutex_unlock(&mwave_mutex);
}
break;
case IOCTL_MW_WRITE_DATA: {
struct mw_readwrite rWriteData;
unsigned short __user *pusBuffer = NULL;
if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
return -EFAULT;
pusBuffer = (unsigned short __user *) (rWriteData.pBuf);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
iocmd, pusBuffer,
rWriteData.ulDataLength,
rWriteData.usDspAddress);
mutex_unlock(&mwave_mutex);
}
break;
case IOCTL_MW_WRITE_INST: {
struct mw_readwrite rWriteData;
unsigned short __user *pusBuffer = NULL;
if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
return -EFAULT;
pusBuffer = (unsigned short __user *)(rWriteData.pBuf);
mutex_lock(&mwave_mutex);
retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData,
iocmd, pusBuffer,
rWriteData.ulDataLength,
rWriteData.usDspAddress);
mutex_unlock(&mwave_mutex);
}
break;
case IOCTL_MW_REGISTER_IPC: {
unsigned int ipcnum = (unsigned int) ioarg;
if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
pr_err("%s: IOCTL_MW_REGISTER_IPC: Error: Invalid ipcnum %x\n",
__func__, ipcnum);
return -EINVAL;
}
ipcnum = array_index_nospec(ipcnum,
ARRAY_SIZE(pDrvData->IPCs));
mutex_lock(&mwave_mutex);
pDrvData->IPCs[ipcnum].bIsHere = false;
pDrvData->IPCs[ipcnum].bIsEnabled = true;
mutex_unlock(&mwave_mutex);
}
break;
case IOCTL_MW_GET_IPC: {
unsigned int ipcnum = (unsigned int) ioarg;
if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
pr_err("%s: IOCTL_MW_GET_IPC: Error: Invalid ipcnum %x\n", __func__,
ipcnum);
return -EINVAL;
}
ipcnum = array_index_nospec(ipcnum,
ARRAY_SIZE(pDrvData->IPCs));
mutex_lock(&mwave_mutex);
if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
DECLARE_WAITQUEUE(wait, current);
add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
pDrvData->IPCs[ipcnum].bIsHere = true;
set_current_state(TASK_INTERRUPTIBLE);
if (pDrvData->IPCs[ipcnum].usIntCount == 1) {
pDrvData->IPCs[ipcnum].usIntCount = 2;
} else {
schedule();
if (pDrvData->IPCs[ipcnum].usIntCount == 1) {
pDrvData->IPCs[ipcnum].usIntCount = 2;
}
}
pDrvData->IPCs[ipcnum].bIsHere = false;
remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
set_current_state(TASK_RUNNING);
}
mutex_unlock(&mwave_mutex);
}
break;
case IOCTL_MW_UNREGISTER_IPC: {
unsigned int ipcnum = (unsigned int) ioarg;
if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
pr_err("%s: IOCTL_MW_UNREGISTER_IPC: Error: Invalid ipcnum %x\n",
__func__, ipcnum);
return -EINVAL;
}
ipcnum = array_index_nospec(ipcnum,
ARRAY_SIZE(pDrvData->IPCs));
mutex_lock(&mwave_mutex);
if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
pDrvData->IPCs[ipcnum].bIsEnabled = false;
if (pDrvData->IPCs[ipcnum].bIsHere == true) {
wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
}
}
mutex_unlock(&mwave_mutex);
}
break;
default:
return -ENOTTY;
}
return retval;
}
static int register_serial_portandirq(unsigned int port, int irq)
{
struct uart_8250_port uart;
switch ( port ) {
case 0x3f8:
case 0x2f8:
case 0x3e8:
case 0x2e8:
break;
default:
pr_err("%s: Error: Illegal port %x\n", __func__, port);
return -1;
}
switch ( irq ) {
case 3:
case 4:
case 5:
case 7:
break;
default:
pr_err("%s: Error: Illegal irq %x\n", __func__, irq);
return -1;
}
memset(&uart, 0, sizeof(uart));
uart.port.uartclk = 1843200;
uart.port.iobase = port;
uart.port.irq = irq;
uart.port.iotype = UPIO_PORT;
uart.port.flags = UPF_SHARE_IRQ;
return serial8250_register_8250_port(&uart);
}
static const struct file_operations mwave_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = mwave_ioctl,
.llseek = default_llseek,
};
static struct miscdevice mwave_misc_dev = { MWAVE_MINOR, "mwave", &mwave_fops };
static void mwave_exit(void)
{
struct mwave_device_data *pDrvData = &mwave_s_mdd;
if ( pDrvData->sLine >= 0 ) {
serial8250_unregister_port(pDrvData->sLine);
}
if (pDrvData->bMwaveDevRegistered) {
misc_deregister(&mwave_misc_dev);
}
if (pDrvData->bDSPEnabled) {
tp3780I_DisableDSP(&pDrvData->rBDData);
}
if (pDrvData->bResourcesClaimed) {
tp3780I_ReleaseResources(&pDrvData->rBDData);
}
if (pDrvData->bBDInitialized) {
tp3780I_Cleanup(&pDrvData->rBDData);
}
}
module_exit(mwave_exit);
static int __init mwave_init(void)
{
int i;
int retval = 0;
struct mwave_device_data *pDrvData = &mwave_s_mdd;
memset(&mwave_s_mdd, 0, sizeof(mwave_s_mdd));
pDrvData->bBDInitialized = false;
pDrvData->bResourcesClaimed = false;
pDrvData->bDSPEnabled = false;
pDrvData->bDSPReset = false;
pDrvData->bMwaveDevRegistered = false;
pDrvData->sLine = -1;
for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) {
pDrvData->IPCs[i].bIsEnabled = false;
pDrvData->IPCs[i].bIsHere = false;
pDrvData->IPCs[i].usIntCount = 0;
init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue);
}
retval = tp3780I_InitializeBoardData(&pDrvData->rBDData);
if (retval) {
pr_err("%s: Error: Failed to initialize board data\n", __func__);
goto cleanup_error;
}
pDrvData->bBDInitialized = true;
retval = tp3780I_CalcResources(&pDrvData->rBDData);
if (retval) {
pr_err("%s: Error: Failed to calculate resources\n", __func__);
goto cleanup_error;
}
retval = tp3780I_ClaimResources(&pDrvData->rBDData);
if (retval) {
pr_err("%s: Error: Failed to claim resources\n", __func__);
goto cleanup_error;
}
pDrvData->bResourcesClaimed = true;
retval = tp3780I_EnableDSP(&pDrvData->rBDData);
if (retval) {
pr_err("%s: Error: Failed to enable DSP\n", __func__);
goto cleanup_error;
}
pDrvData->bDSPEnabled = true;
if (misc_register(&mwave_misc_dev) < 0) {
pr_err("%s: Error: Failed to register misc device\n", __func__);
goto cleanup_error;
}
pDrvData->bMwaveDevRegistered = true;
pDrvData->sLine = register_serial_portandirq(
pDrvData->rBDData.rDspSettings.usUartBaseIO,
pDrvData->rBDData.rDspSettings.usUartIrq
);
if (pDrvData->sLine < 0) {
pr_err("%s: Error: Failed to register serial driver\n", __func__);
goto cleanup_error;
}
return 0;
cleanup_error:
pr_err("%s: Error: Failed to initialize\n", __func__);
mwave_exit();
return -EIO;
}
module_init(mwave_init);