11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds *
31da177e4SLinus Torvalds * mwavedd.c -- mwave device driver
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds *
61da177e4SLinus Torvalds * Written By: Mike Sullivan IBM Corporation
71da177e4SLinus Torvalds *
81da177e4SLinus Torvalds * Copyright (C) 1999 IBM Corporation
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify
111da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by
121da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or
131da177e4SLinus Torvalds * (at your option) any later version.
141da177e4SLinus Torvalds *
151da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful,
161da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of
171da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
181da177e4SLinus Torvalds * GNU General Public License for more details.
191da177e4SLinus Torvalds *
201da177e4SLinus Torvalds * NO WARRANTY
211da177e4SLinus Torvalds * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
221da177e4SLinus Torvalds * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
231da177e4SLinus Torvalds * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
241da177e4SLinus Torvalds * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
251da177e4SLinus Torvalds * solely responsible for determining the appropriateness of using and
261da177e4SLinus Torvalds * distributing the Program and assumes all risks associated with its
271da177e4SLinus Torvalds * exercise of rights under this Agreement, including but not limited to
281da177e4SLinus Torvalds * the risks and costs of program errors, damage to or loss of data,
291da177e4SLinus Torvalds * programs or equipment, and unavailability or interruption of operations.
301da177e4SLinus Torvalds *
311da177e4SLinus Torvalds * DISCLAIMER OF LIABILITY
321da177e4SLinus Torvalds * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
331da177e4SLinus Torvalds * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
341da177e4SLinus Torvalds * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
351da177e4SLinus Torvalds * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
361da177e4SLinus Torvalds * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
371da177e4SLinus Torvalds * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
381da177e4SLinus Torvalds * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
391da177e4SLinus Torvalds *
401da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License
411da177e4SLinus Torvalds * along with this program; if not, write to the Free Software
421da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
431da177e4SLinus Torvalds *
441da177e4SLinus Torvalds *
451da177e4SLinus Torvalds * 10/23/2000 - Alpha Release
461da177e4SLinus Torvalds * First release to the public
471da177e4SLinus Torvalds */
481da177e4SLinus Torvalds
491da177e4SLinus Torvalds #include <linux/module.h>
501da177e4SLinus Torvalds #include <linux/kernel.h>
511da177e4SLinus Torvalds #include <linux/fs.h>
521da177e4SLinus Torvalds #include <linux/init.h>
531da177e4SLinus Torvalds #include <linux/major.h>
541da177e4SLinus Torvalds #include <linux/miscdevice.h>
551da177e4SLinus Torvalds #include <linux/device.h>
561da177e4SLinus Torvalds #include <linux/serial.h>
571da177e4SLinus Torvalds #include <linux/sched.h>
581da177e4SLinus Torvalds #include <linux/spinlock.h>
59613655faSArnd Bergmann #include <linux/mutex.h>
601da177e4SLinus Torvalds #include <linux/delay.h>
615981d644SAlan Cox #include <linux/serial_8250.h>
62*701956d4SGustavo A. R. Silva #include <linux/nospec.h>
631da177e4SLinus Torvalds #include "smapi.h"
641da177e4SLinus Torvalds #include "mwavedd.h"
651da177e4SLinus Torvalds #include "3780i.h"
661da177e4SLinus Torvalds #include "tp3780i.h"
671da177e4SLinus Torvalds
681da177e4SLinus Torvalds MODULE_DESCRIPTION("3780i Advanced Communications Processor (Mwave) driver");
691da177e4SLinus Torvalds MODULE_AUTHOR("Mike Sullivan and Paul Schroeder");
701da177e4SLinus Torvalds MODULE_LICENSE("GPL");
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds /*
731da177e4SLinus Torvalds * These parameters support the setting of MWave resources. Note that no
741da177e4SLinus Torvalds * checks are made against other devices (ie. superio) for conflicts.
751da177e4SLinus Torvalds * We'll depend on users using the tpctl utility to do that for now
761da177e4SLinus Torvalds */
77613655faSArnd Bergmann static DEFINE_MUTEX(mwave_mutex);
781da177e4SLinus Torvalds int mwave_debug = 0;
791da177e4SLinus Torvalds int mwave_3780i_irq = 0;
801da177e4SLinus Torvalds int mwave_3780i_io = 0;
811da177e4SLinus Torvalds int mwave_uart_irq = 0;
821da177e4SLinus Torvalds int mwave_uart_io = 0;
831da177e4SLinus Torvalds module_param(mwave_debug, int, 0);
8494b599bcSDavid Howells module_param_hw(mwave_3780i_irq, int, irq, 0);
8594b599bcSDavid Howells module_param_hw(mwave_3780i_io, int, ioport, 0);
8694b599bcSDavid Howells module_param_hw(mwave_uart_irq, int, irq, 0);
8794b599bcSDavid Howells module_param_hw(mwave_uart_io, int, ioport, 0);
881da177e4SLinus Torvalds
891da177e4SLinus Torvalds static int mwave_open(struct inode *inode, struct file *file);
901da177e4SLinus Torvalds static int mwave_close(struct inode *inode, struct file *file);
91909d145fSAlan Cox static long mwave_ioctl(struct file *filp, unsigned int iocmd,
92909d145fSAlan Cox unsigned long ioarg);
931da177e4SLinus Torvalds
941da177e4SLinus Torvalds MWAVE_DEVICE_DATA mwave_s_mdd;
951da177e4SLinus Torvalds
mwave_open(struct inode * inode,struct file * file)961da177e4SLinus Torvalds static int mwave_open(struct inode *inode, struct file *file)
971da177e4SLinus Torvalds {
981da177e4SLinus Torvalds unsigned int retval = 0;
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds PRINTK_3(TRACE_MWAVE,
1011da177e4SLinus Torvalds "mwavedd::mwave_open, entry inode %p file %p\n",
1021da177e4SLinus Torvalds inode, file);
1031da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
1041da177e4SLinus Torvalds "mwavedd::mwave_open, exit return retval %x\n", retval);
1051da177e4SLinus Torvalds
1061da177e4SLinus Torvalds return retval;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds
mwave_close(struct inode * inode,struct file * file)1091da177e4SLinus Torvalds static int mwave_close(struct inode *inode, struct file *file)
1101da177e4SLinus Torvalds {
1111da177e4SLinus Torvalds unsigned int retval = 0;
1121da177e4SLinus Torvalds
1131da177e4SLinus Torvalds PRINTK_3(TRACE_MWAVE,
1141da177e4SLinus Torvalds "mwavedd::mwave_close, entry inode %p file %p\n",
1151da177e4SLinus Torvalds inode, file);
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_close, exit retval %x\n",
1181da177e4SLinus Torvalds retval);
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds return retval;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds
mwave_ioctl(struct file * file,unsigned int iocmd,unsigned long ioarg)123909d145fSAlan Cox static long mwave_ioctl(struct file *file, unsigned int iocmd,
124909d145fSAlan Cox unsigned long ioarg)
1251da177e4SLinus Torvalds {
1261da177e4SLinus Torvalds unsigned int retval = 0;
1271da177e4SLinus Torvalds pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
1281da177e4SLinus Torvalds void __user *arg = (void __user *)ioarg;
1291da177e4SLinus Torvalds
130909d145fSAlan Cox PRINTK_4(TRACE_MWAVE,
131909d145fSAlan Cox "mwavedd::mwave_ioctl, entry file %p cmd %x arg %x\n",
132909d145fSAlan Cox file, iocmd, (int) ioarg);
1331da177e4SLinus Torvalds
1341da177e4SLinus Torvalds switch (iocmd) {
1351da177e4SLinus Torvalds
1361da177e4SLinus Torvalds case IOCTL_MW_RESET:
1371da177e4SLinus Torvalds PRINTK_1(TRACE_MWAVE,
1381da177e4SLinus Torvalds "mwavedd::mwave_ioctl, IOCTL_MW_RESET"
1391da177e4SLinus Torvalds " calling tp3780I_ResetDSP\n");
140613655faSArnd Bergmann mutex_lock(&mwave_mutex);
1411da177e4SLinus Torvalds retval = tp3780I_ResetDSP(&pDrvData->rBDData);
142613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
1431da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
1441da177e4SLinus Torvalds "mwavedd::mwave_ioctl, IOCTL_MW_RESET"
1451da177e4SLinus Torvalds " retval %x from tp3780I_ResetDSP\n",
1461da177e4SLinus Torvalds retval);
1471da177e4SLinus Torvalds break;
1481da177e4SLinus Torvalds
1491da177e4SLinus Torvalds case IOCTL_MW_RUN:
1501da177e4SLinus Torvalds PRINTK_1(TRACE_MWAVE,
1511da177e4SLinus Torvalds "mwavedd::mwave_ioctl, IOCTL_MW_RUN"
1521da177e4SLinus Torvalds " calling tp3780I_StartDSP\n");
153613655faSArnd Bergmann mutex_lock(&mwave_mutex);
1541da177e4SLinus Torvalds retval = tp3780I_StartDSP(&pDrvData->rBDData);
155613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
1561da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
1571da177e4SLinus Torvalds "mwavedd::mwave_ioctl, IOCTL_MW_RUN"
1581da177e4SLinus Torvalds " retval %x from tp3780I_StartDSP\n",
1591da177e4SLinus Torvalds retval);
1601da177e4SLinus Torvalds break;
1611da177e4SLinus Torvalds
1621da177e4SLinus Torvalds case IOCTL_MW_DSP_ABILITIES: {
1631da177e4SLinus Torvalds MW_ABILITIES rAbilities;
1641da177e4SLinus Torvalds
1651da177e4SLinus Torvalds PRINTK_1(TRACE_MWAVE,
1661da177e4SLinus Torvalds "mwavedd::mwave_ioctl,"
1671da177e4SLinus Torvalds " IOCTL_MW_DSP_ABILITIES calling"
1681da177e4SLinus Torvalds " tp3780I_QueryAbilities\n");
169613655faSArnd Bergmann mutex_lock(&mwave_mutex);
1701da177e4SLinus Torvalds retval = tp3780I_QueryAbilities(&pDrvData->rBDData,
1711da177e4SLinus Torvalds &rAbilities);
172613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
1731da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
1741da177e4SLinus Torvalds "mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES"
1751da177e4SLinus Torvalds " retval %x from tp3780I_QueryAbilities\n",
1761da177e4SLinus Torvalds retval);
1771da177e4SLinus Torvalds if (retval == 0) {
1781da177e4SLinus Torvalds if( copy_to_user(arg, &rAbilities,
1791da177e4SLinus Torvalds sizeof(MW_ABILITIES)) )
1801da177e4SLinus Torvalds return -EFAULT;
1811da177e4SLinus Torvalds }
1821da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
1831da177e4SLinus Torvalds "mwavedd::mwave_ioctl, IOCTL_MW_DSP_ABILITIES"
1841da177e4SLinus Torvalds " exit retval %x\n",
1851da177e4SLinus Torvalds retval);
1861da177e4SLinus Torvalds }
1871da177e4SLinus Torvalds break;
1881da177e4SLinus Torvalds
1891da177e4SLinus Torvalds case IOCTL_MW_READ_DATA:
1901da177e4SLinus Torvalds case IOCTL_MW_READCLEAR_DATA: {
1911da177e4SLinus Torvalds MW_READWRITE rReadData;
1921da177e4SLinus Torvalds unsigned short __user *pusBuffer = NULL;
1931da177e4SLinus Torvalds
1941da177e4SLinus Torvalds if( copy_from_user(&rReadData, arg,
1951da177e4SLinus Torvalds sizeof(MW_READWRITE)) )
1961da177e4SLinus Torvalds return -EFAULT;
1971da177e4SLinus Torvalds pusBuffer = (unsigned short __user *) (rReadData.pBuf);
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds PRINTK_4(TRACE_MWAVE,
2001da177e4SLinus Torvalds "mwavedd::mwave_ioctl IOCTL_MW_READ_DATA,"
2011da177e4SLinus Torvalds " size %lx, ioarg %lx pusBuffer %p\n",
2021da177e4SLinus Torvalds rReadData.ulDataLength, ioarg, pusBuffer);
203613655faSArnd Bergmann mutex_lock(&mwave_mutex);
2041da177e4SLinus Torvalds retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
2051da177e4SLinus Torvalds iocmd,
2061da177e4SLinus Torvalds pusBuffer,
2071da177e4SLinus Torvalds rReadData.ulDataLength,
2081da177e4SLinus Torvalds rReadData.usDspAddress);
209613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
2101da177e4SLinus Torvalds }
2111da177e4SLinus Torvalds break;
2121da177e4SLinus Torvalds
2131da177e4SLinus Torvalds case IOCTL_MW_READ_INST: {
2141da177e4SLinus Torvalds MW_READWRITE rReadData;
2151da177e4SLinus Torvalds unsigned short __user *pusBuffer = NULL;
2161da177e4SLinus Torvalds
2171da177e4SLinus Torvalds if( copy_from_user(&rReadData, arg,
2181da177e4SLinus Torvalds sizeof(MW_READWRITE)) )
2191da177e4SLinus Torvalds return -EFAULT;
2201da177e4SLinus Torvalds pusBuffer = (unsigned short __user *) (rReadData.pBuf);
2211da177e4SLinus Torvalds
2221da177e4SLinus Torvalds PRINTK_4(TRACE_MWAVE,
2231da177e4SLinus Torvalds "mwavedd::mwave_ioctl IOCTL_MW_READ_INST,"
2241da177e4SLinus Torvalds " size %lx, ioarg %lx pusBuffer %p\n",
2251da177e4SLinus Torvalds rReadData.ulDataLength / 2, ioarg,
2261da177e4SLinus Torvalds pusBuffer);
227613655faSArnd Bergmann mutex_lock(&mwave_mutex);
2281da177e4SLinus Torvalds retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
2291da177e4SLinus Torvalds iocmd, pusBuffer,
2301da177e4SLinus Torvalds rReadData.ulDataLength / 2,
2311da177e4SLinus Torvalds rReadData.usDspAddress);
232613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
2331da177e4SLinus Torvalds }
2341da177e4SLinus Torvalds break;
2351da177e4SLinus Torvalds
2361da177e4SLinus Torvalds case IOCTL_MW_WRITE_DATA: {
2371da177e4SLinus Torvalds MW_READWRITE rWriteData;
2381da177e4SLinus Torvalds unsigned short __user *pusBuffer = NULL;
2391da177e4SLinus Torvalds
2401da177e4SLinus Torvalds if( copy_from_user(&rWriteData, arg,
2411da177e4SLinus Torvalds sizeof(MW_READWRITE)) )
2421da177e4SLinus Torvalds return -EFAULT;
2431da177e4SLinus Torvalds pusBuffer = (unsigned short __user *) (rWriteData.pBuf);
2441da177e4SLinus Torvalds
2451da177e4SLinus Torvalds PRINTK_4(TRACE_MWAVE,
2461da177e4SLinus Torvalds "mwavedd::mwave_ioctl IOCTL_MW_WRITE_DATA,"
2471da177e4SLinus Torvalds " size %lx, ioarg %lx pusBuffer %p\n",
2481da177e4SLinus Torvalds rWriteData.ulDataLength, ioarg,
2491da177e4SLinus Torvalds pusBuffer);
250613655faSArnd Bergmann mutex_lock(&mwave_mutex);
2511da177e4SLinus Torvalds retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
2521da177e4SLinus Torvalds iocmd, pusBuffer,
2531da177e4SLinus Torvalds rWriteData.ulDataLength,
2541da177e4SLinus Torvalds rWriteData.usDspAddress);
255613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds break;
2581da177e4SLinus Torvalds
2591da177e4SLinus Torvalds case IOCTL_MW_WRITE_INST: {
2601da177e4SLinus Torvalds MW_READWRITE rWriteData;
2611da177e4SLinus Torvalds unsigned short __user *pusBuffer = NULL;
2621da177e4SLinus Torvalds
2631da177e4SLinus Torvalds if( copy_from_user(&rWriteData, arg,
2641da177e4SLinus Torvalds sizeof(MW_READWRITE)) )
2651da177e4SLinus Torvalds return -EFAULT;
2661da177e4SLinus Torvalds pusBuffer = (unsigned short __user *)(rWriteData.pBuf);
2671da177e4SLinus Torvalds
2681da177e4SLinus Torvalds PRINTK_4(TRACE_MWAVE,
2691da177e4SLinus Torvalds "mwavedd::mwave_ioctl IOCTL_MW_WRITE_INST,"
2701da177e4SLinus Torvalds " size %lx, ioarg %lx pusBuffer %p\n",
2711da177e4SLinus Torvalds rWriteData.ulDataLength, ioarg,
2721da177e4SLinus Torvalds pusBuffer);
273613655faSArnd Bergmann mutex_lock(&mwave_mutex);
2741da177e4SLinus Torvalds retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData,
2751da177e4SLinus Torvalds iocmd, pusBuffer,
2761da177e4SLinus Torvalds rWriteData.ulDataLength,
2771da177e4SLinus Torvalds rWriteData.usDspAddress);
278613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
2791da177e4SLinus Torvalds }
2801da177e4SLinus Torvalds break;
2811da177e4SLinus Torvalds
2821da177e4SLinus Torvalds case IOCTL_MW_REGISTER_IPC: {
2831da177e4SLinus Torvalds unsigned int ipcnum = (unsigned int) ioarg;
2841da177e4SLinus Torvalds
285d698f1c7SEric Sesterhenn if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
2861da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
2871da177e4SLinus Torvalds "mwavedd::mwave_ioctl:"
2881da177e4SLinus Torvalds " IOCTL_MW_REGISTER_IPC:"
2891da177e4SLinus Torvalds " Error: Invalid ipcnum %x\n",
2901da177e4SLinus Torvalds ipcnum);
2911da177e4SLinus Torvalds return -EINVAL;
2921da177e4SLinus Torvalds }
293*701956d4SGustavo A. R. Silva ipcnum = array_index_nospec(ipcnum,
294*701956d4SGustavo A. R. Silva ARRAY_SIZE(pDrvData->IPCs));
295dc80df56SRoel Kluin PRINTK_3(TRACE_MWAVE,
296dc80df56SRoel Kluin "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC"
297dc80df56SRoel Kluin " ipcnum %x entry usIntCount %x\n",
298dc80df56SRoel Kluin ipcnum,
299dc80df56SRoel Kluin pDrvData->IPCs[ipcnum].usIntCount);
300dc80df56SRoel Kluin
301613655faSArnd Bergmann mutex_lock(&mwave_mutex);
30226ec99b1SArnd Bergmann pDrvData->IPCs[ipcnum].bIsHere = false;
30326ec99b1SArnd Bergmann pDrvData->IPCs[ipcnum].bIsEnabled = true;
304613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
3051da177e4SLinus Torvalds
3061da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
3071da177e4SLinus Torvalds "mwavedd::mwave_ioctl IOCTL_MW_REGISTER_IPC"
3081da177e4SLinus Torvalds " ipcnum %x exit\n",
3091da177e4SLinus Torvalds ipcnum);
3101da177e4SLinus Torvalds }
3111da177e4SLinus Torvalds break;
3121da177e4SLinus Torvalds
3131da177e4SLinus Torvalds case IOCTL_MW_GET_IPC: {
3141da177e4SLinus Torvalds unsigned int ipcnum = (unsigned int) ioarg;
3151da177e4SLinus Torvalds
316095d030cSEric Sesterhenn if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
3171da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
3181da177e4SLinus Torvalds "mwavedd::mwave_ioctl:"
3191da177e4SLinus Torvalds " IOCTL_MW_GET_IPC: Error:"
3201da177e4SLinus Torvalds " Invalid ipcnum %x\n", ipcnum);
3211da177e4SLinus Torvalds return -EINVAL;
3221da177e4SLinus Torvalds }
323*701956d4SGustavo A. R. Silva ipcnum = array_index_nospec(ipcnum,
324*701956d4SGustavo A. R. Silva ARRAY_SIZE(pDrvData->IPCs));
325dc80df56SRoel Kluin PRINTK_3(TRACE_MWAVE,
326dc80df56SRoel Kluin "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC"
327dc80df56SRoel Kluin " ipcnum %x, usIntCount %x\n",
328dc80df56SRoel Kluin ipcnum,
329dc80df56SRoel Kluin pDrvData->IPCs[ipcnum].usIntCount);
3301da177e4SLinus Torvalds
331613655faSArnd Bergmann mutex_lock(&mwave_mutex);
33226ec99b1SArnd Bergmann if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
3331da177e4SLinus Torvalds DECLARE_WAITQUEUE(wait, current);
3341da177e4SLinus Torvalds
3351da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
3361da177e4SLinus Torvalds "mwavedd::mwave_ioctl, thread for"
3371da177e4SLinus Torvalds " ipc %x going to sleep\n",
3381da177e4SLinus Torvalds ipcnum);
3391da177e4SLinus Torvalds add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
34026ec99b1SArnd Bergmann pDrvData->IPCs[ipcnum].bIsHere = true;
3411da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE);
3421da177e4SLinus Torvalds /* check whether an event was signalled by */
3431da177e4SLinus Torvalds /* the interrupt handler while we were gone */
3441da177e4SLinus Torvalds if (pDrvData->IPCs[ipcnum].usIntCount == 1) { /* first int has occurred (race condition) */
3451da177e4SLinus Torvalds pDrvData->IPCs[ipcnum].usIntCount = 2; /* first int has been handled */
3461da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
3471da177e4SLinus Torvalds "mwavedd::mwave_ioctl"
3481da177e4SLinus Torvalds " IOCTL_MW_GET_IPC ipcnum %x"
3491da177e4SLinus Torvalds " handling first int\n",
3501da177e4SLinus Torvalds ipcnum);
3511da177e4SLinus Torvalds } else { /* either 1st int has not yet occurred, or we have already handled the first int */
3521da177e4SLinus Torvalds schedule();
3531da177e4SLinus Torvalds if (pDrvData->IPCs[ipcnum].usIntCount == 1) {
3541da177e4SLinus Torvalds pDrvData->IPCs[ipcnum].usIntCount = 2;
3551da177e4SLinus Torvalds }
3561da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
3571da177e4SLinus Torvalds "mwavedd::mwave_ioctl"
3581da177e4SLinus Torvalds " IOCTL_MW_GET_IPC ipcnum %x"
3591da177e4SLinus Torvalds " woke up and returning to"
3601da177e4SLinus Torvalds " application\n",
3611da177e4SLinus Torvalds ipcnum);
3621da177e4SLinus Torvalds }
36326ec99b1SArnd Bergmann pDrvData->IPCs[ipcnum].bIsHere = false;
3641da177e4SLinus Torvalds remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
3651da177e4SLinus Torvalds set_current_state(TASK_RUNNING);
3661da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
3671da177e4SLinus Torvalds "mwavedd::mwave_ioctl IOCTL_MW_GET_IPC,"
3681da177e4SLinus Torvalds " returning thread for ipc %x"
3691da177e4SLinus Torvalds " processing\n",
3701da177e4SLinus Torvalds ipcnum);
3711da177e4SLinus Torvalds }
372613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
3731da177e4SLinus Torvalds }
3741da177e4SLinus Torvalds break;
3751da177e4SLinus Torvalds
3761da177e4SLinus Torvalds case IOCTL_MW_UNREGISTER_IPC: {
3771da177e4SLinus Torvalds unsigned int ipcnum = (unsigned int) ioarg;
3781da177e4SLinus Torvalds
3791da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
3801da177e4SLinus Torvalds "mwavedd::mwave_ioctl IOCTL_MW_UNREGISTER_IPC"
3811da177e4SLinus Torvalds " ipcnum %x\n",
3821da177e4SLinus Torvalds ipcnum);
383095d030cSEric Sesterhenn if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
3841da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
3851da177e4SLinus Torvalds "mwavedd::mwave_ioctl:"
3861da177e4SLinus Torvalds " IOCTL_MW_UNREGISTER_IPC:"
3871da177e4SLinus Torvalds " Error: Invalid ipcnum %x\n",
3881da177e4SLinus Torvalds ipcnum);
3891da177e4SLinus Torvalds return -EINVAL;
3901da177e4SLinus Torvalds }
391*701956d4SGustavo A. R. Silva ipcnum = array_index_nospec(ipcnum,
392*701956d4SGustavo A. R. Silva ARRAY_SIZE(pDrvData->IPCs));
393613655faSArnd Bergmann mutex_lock(&mwave_mutex);
39426ec99b1SArnd Bergmann if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
39526ec99b1SArnd Bergmann pDrvData->IPCs[ipcnum].bIsEnabled = false;
39626ec99b1SArnd Bergmann if (pDrvData->IPCs[ipcnum].bIsHere == true) {
3971da177e4SLinus Torvalds wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
3981da177e4SLinus Torvalds }
3991da177e4SLinus Torvalds }
400613655faSArnd Bergmann mutex_unlock(&mwave_mutex);
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds break;
4031da177e4SLinus Torvalds
4041da177e4SLinus Torvalds default:
4051da177e4SLinus Torvalds return -ENOTTY;
4061da177e4SLinus Torvalds } /* switch */
4071da177e4SLinus Torvalds
4081da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE, "mwavedd::mwave_ioctl, exit retval %x\n", retval);
4091da177e4SLinus Torvalds
4101da177e4SLinus Torvalds return retval;
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds
4131da177e4SLinus Torvalds
mwave_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)4141da177e4SLinus Torvalds static ssize_t mwave_read(struct file *file, char __user *buf, size_t count,
4151da177e4SLinus Torvalds loff_t * ppos)
4161da177e4SLinus Torvalds {
4171da177e4SLinus Torvalds PRINTK_5(TRACE_MWAVE,
4181da177e4SLinus Torvalds "mwavedd::mwave_read entry file %p, buf %p, count %zx ppos %p\n",
4191da177e4SLinus Torvalds file, buf, count, ppos);
4201da177e4SLinus Torvalds
4211da177e4SLinus Torvalds return -EINVAL;
4221da177e4SLinus Torvalds }
4231da177e4SLinus Torvalds
4241da177e4SLinus Torvalds
mwave_write(struct file * file,const char __user * buf,size_t count,loff_t * ppos)4251da177e4SLinus Torvalds static ssize_t mwave_write(struct file *file, const char __user *buf,
4261da177e4SLinus Torvalds size_t count, loff_t * ppos)
4271da177e4SLinus Torvalds {
4281da177e4SLinus Torvalds PRINTK_5(TRACE_MWAVE,
4291da177e4SLinus Torvalds "mwavedd::mwave_write entry file %p, buf %p,"
4301da177e4SLinus Torvalds " count %zx ppos %p\n",
4311da177e4SLinus Torvalds file, buf, count, ppos);
4321da177e4SLinus Torvalds
4331da177e4SLinus Torvalds return -EINVAL;
4341da177e4SLinus Torvalds }
4351da177e4SLinus Torvalds
4361da177e4SLinus Torvalds
register_serial_portandirq(unsigned int port,int irq)4371da177e4SLinus Torvalds static int register_serial_portandirq(unsigned int port, int irq)
4381da177e4SLinus Torvalds {
439ce7240e4SAlan Cox struct uart_8250_port uart;
4401da177e4SLinus Torvalds
4411da177e4SLinus Torvalds switch ( port ) {
4421da177e4SLinus Torvalds case 0x3f8:
4431da177e4SLinus Torvalds case 0x2f8:
4441da177e4SLinus Torvalds case 0x3e8:
4451da177e4SLinus Torvalds case 0x2e8:
4461da177e4SLinus Torvalds /* OK */
4471da177e4SLinus Torvalds break;
4481da177e4SLinus Torvalds default:
4491da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
4501da177e4SLinus Torvalds "mwavedd::register_serial_portandirq:"
4511da177e4SLinus Torvalds " Error: Illegal port %x\n", port );
4521da177e4SLinus Torvalds return -1;
4531da177e4SLinus Torvalds } /* switch */
4541da177e4SLinus Torvalds /* port is okay */
4551da177e4SLinus Torvalds
4561da177e4SLinus Torvalds switch ( irq ) {
4571da177e4SLinus Torvalds case 3:
4581da177e4SLinus Torvalds case 4:
4591da177e4SLinus Torvalds case 5:
4601da177e4SLinus Torvalds case 7:
4611da177e4SLinus Torvalds /* OK */
4621da177e4SLinus Torvalds break;
4631da177e4SLinus Torvalds default:
4641da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
4651da177e4SLinus Torvalds "mwavedd::register_serial_portandirq:"
4661da177e4SLinus Torvalds " Error: Illegal irq %x\n", irq );
4671da177e4SLinus Torvalds return -1;
4681da177e4SLinus Torvalds } /* switch */
4691da177e4SLinus Torvalds /* irq is okay */
4701da177e4SLinus Torvalds
471ce7240e4SAlan Cox memset(&uart, 0, sizeof(uart));
4721da177e4SLinus Torvalds
473ce7240e4SAlan Cox uart.port.uartclk = 1843200;
474ce7240e4SAlan Cox uart.port.iobase = port;
475ce7240e4SAlan Cox uart.port.irq = irq;
476ce7240e4SAlan Cox uart.port.iotype = UPIO_PORT;
477ce7240e4SAlan Cox uart.port.flags = UPF_SHARE_IRQ;
478ce7240e4SAlan Cox return serial8250_register_8250_port(&uart);
4791da177e4SLinus Torvalds }
4801da177e4SLinus Torvalds
4811da177e4SLinus Torvalds
48262322d25SArjan van de Ven static const struct file_operations mwave_fops = {
4831da177e4SLinus Torvalds .owner = THIS_MODULE,
4841da177e4SLinus Torvalds .read = mwave_read,
4851da177e4SLinus Torvalds .write = mwave_write,
486909d145fSAlan Cox .unlocked_ioctl = mwave_ioctl,
4871da177e4SLinus Torvalds .open = mwave_open,
4886038f373SArnd Bergmann .release = mwave_close,
4896038f373SArnd Bergmann .llseek = default_llseek,
4901da177e4SLinus Torvalds };
4911da177e4SLinus Torvalds
4921da177e4SLinus Torvalds
4931da177e4SLinus Torvalds static struct miscdevice mwave_misc_dev = { MWAVE_MINOR, "mwave", &mwave_fops };
4941da177e4SLinus Torvalds
4951da177e4SLinus Torvalds #if 0 /* totally b0rked */
4961da177e4SLinus Torvalds /*
4971da177e4SLinus Torvalds * sysfs support <paulsch@us.ibm.com>
4981da177e4SLinus Torvalds */
4991da177e4SLinus Torvalds
5001da177e4SLinus Torvalds struct device mwave_device;
5011da177e4SLinus Torvalds
5021da177e4SLinus Torvalds /* Prevent code redundancy, create a macro for mwave_show_* functions. */
5031da177e4SLinus Torvalds #define mwave_show_function(attr_name, format_string, field) \
50474880c06SYani Ioannou static ssize_t mwave_show_##attr_name(struct device *dev, struct device_attribute *attr, char *buf) \
5051da177e4SLinus Torvalds { \
5061da177e4SLinus Torvalds DSP_3780I_CONFIG_SETTINGS *pSettings = \
5071da177e4SLinus Torvalds &mwave_s_mdd.rBDData.rDspSettings; \
5081da177e4SLinus Torvalds return sprintf(buf, format_string, pSettings->field); \
5091da177e4SLinus Torvalds }
5101da177e4SLinus Torvalds
5111da177e4SLinus Torvalds /* All of our attributes are read attributes. */
5121da177e4SLinus Torvalds #define mwave_dev_rd_attr(attr_name, format_string, field) \
5131da177e4SLinus Torvalds mwave_show_function(attr_name, format_string, field) \
5141da177e4SLinus Torvalds static DEVICE_ATTR(attr_name, S_IRUGO, mwave_show_##attr_name, NULL)
5151da177e4SLinus Torvalds
5161da177e4SLinus Torvalds mwave_dev_rd_attr (3780i_dma, "%i\n", usDspDma);
5171da177e4SLinus Torvalds mwave_dev_rd_attr (3780i_irq, "%i\n", usDspIrq);
5181da177e4SLinus Torvalds mwave_dev_rd_attr (3780i_io, "%#.4x\n", usDspBaseIO);
5191da177e4SLinus Torvalds mwave_dev_rd_attr (uart_irq, "%i\n", usUartIrq);
5201da177e4SLinus Torvalds mwave_dev_rd_attr (uart_io, "%#.4x\n", usUartBaseIO);
5211da177e4SLinus Torvalds
5221da177e4SLinus Torvalds static struct device_attribute * const mwave_dev_attrs[] = {
5231da177e4SLinus Torvalds &dev_attr_3780i_dma,
5241da177e4SLinus Torvalds &dev_attr_3780i_irq,
5251da177e4SLinus Torvalds &dev_attr_3780i_io,
5261da177e4SLinus Torvalds &dev_attr_uart_irq,
5271da177e4SLinus Torvalds &dev_attr_uart_io,
5281da177e4SLinus Torvalds };
5291da177e4SLinus Torvalds #endif
5301da177e4SLinus Torvalds
5311da177e4SLinus Torvalds /*
5321da177e4SLinus Torvalds * mwave_init is called on module load
5331da177e4SLinus Torvalds *
5341da177e4SLinus Torvalds * mwave_exit is called on module unload
5351da177e4SLinus Torvalds * mwave_exit is also used to clean up after an aborted mwave_init
5361da177e4SLinus Torvalds */
mwave_exit(void)5371da177e4SLinus Torvalds static void mwave_exit(void)
5381da177e4SLinus Torvalds {
5391da177e4SLinus Torvalds pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
5401da177e4SLinus Torvalds
5411da177e4SLinus Torvalds PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_exit entry\n");
5421da177e4SLinus Torvalds
5431da177e4SLinus Torvalds #if 0
5441da177e4SLinus Torvalds for (i = 0; i < pDrvData->nr_registered_attrs; i++)
5451da177e4SLinus Torvalds device_remove_file(&mwave_device, mwave_dev_attrs[i]);
5461da177e4SLinus Torvalds pDrvData->nr_registered_attrs = 0;
5471da177e4SLinus Torvalds
5481da177e4SLinus Torvalds if (pDrvData->device_registered) {
5491da177e4SLinus Torvalds device_unregister(&mwave_device);
55026ec99b1SArnd Bergmann pDrvData->device_registered = false;
5511da177e4SLinus Torvalds }
5521da177e4SLinus Torvalds #endif
5531da177e4SLinus Torvalds
5541da177e4SLinus Torvalds if ( pDrvData->sLine >= 0 ) {
5555981d644SAlan Cox serial8250_unregister_port(pDrvData->sLine);
5561da177e4SLinus Torvalds }
5571da177e4SLinus Torvalds if (pDrvData->bMwaveDevRegistered) {
5581da177e4SLinus Torvalds misc_deregister(&mwave_misc_dev);
5591da177e4SLinus Torvalds }
5601da177e4SLinus Torvalds if (pDrvData->bDSPEnabled) {
5611da177e4SLinus Torvalds tp3780I_DisableDSP(&pDrvData->rBDData);
5621da177e4SLinus Torvalds }
5631da177e4SLinus Torvalds if (pDrvData->bResourcesClaimed) {
5641da177e4SLinus Torvalds tp3780I_ReleaseResources(&pDrvData->rBDData);
5651da177e4SLinus Torvalds }
5661da177e4SLinus Torvalds if (pDrvData->bBDInitialized) {
5671da177e4SLinus Torvalds tp3780I_Cleanup(&pDrvData->rBDData);
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds
5701da177e4SLinus Torvalds PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_exit exit\n");
5711da177e4SLinus Torvalds }
5721da177e4SLinus Torvalds
5731da177e4SLinus Torvalds module_exit(mwave_exit);
5741da177e4SLinus Torvalds
mwave_init(void)5751da177e4SLinus Torvalds static int __init mwave_init(void)
5761da177e4SLinus Torvalds {
5771da177e4SLinus Torvalds int i;
5781da177e4SLinus Torvalds int retval = 0;
5791da177e4SLinus Torvalds pMWAVE_DEVICE_DATA pDrvData = &mwave_s_mdd;
5801da177e4SLinus Torvalds
5811da177e4SLinus Torvalds PRINTK_1(TRACE_MWAVE, "mwavedd::mwave_init entry\n");
5821da177e4SLinus Torvalds
5831da177e4SLinus Torvalds memset(&mwave_s_mdd, 0, sizeof(MWAVE_DEVICE_DATA));
5841da177e4SLinus Torvalds
58526ec99b1SArnd Bergmann pDrvData->bBDInitialized = false;
58626ec99b1SArnd Bergmann pDrvData->bResourcesClaimed = false;
58726ec99b1SArnd Bergmann pDrvData->bDSPEnabled = false;
58826ec99b1SArnd Bergmann pDrvData->bDSPReset = false;
58926ec99b1SArnd Bergmann pDrvData->bMwaveDevRegistered = false;
5901da177e4SLinus Torvalds pDrvData->sLine = -1;
5911da177e4SLinus Torvalds
5921da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) {
59326ec99b1SArnd Bergmann pDrvData->IPCs[i].bIsEnabled = false;
59426ec99b1SArnd Bergmann pDrvData->IPCs[i].bIsHere = false;
5951da177e4SLinus Torvalds pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */
5961da177e4SLinus Torvalds init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue);
5971da177e4SLinus Torvalds }
5981da177e4SLinus Torvalds
5991da177e4SLinus Torvalds retval = tp3780I_InitializeBoardData(&pDrvData->rBDData);
6001da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
6011da177e4SLinus Torvalds "mwavedd::mwave_init, return from tp3780I_InitializeBoardData"
6021da177e4SLinus Torvalds " retval %x\n",
6031da177e4SLinus Torvalds retval);
6041da177e4SLinus Torvalds if (retval) {
6051da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6061da177e4SLinus Torvalds "mwavedd::mwave_init: Error:"
6071da177e4SLinus Torvalds " Failed to initialize board data\n");
6081da177e4SLinus Torvalds goto cleanup_error;
6091da177e4SLinus Torvalds }
61026ec99b1SArnd Bergmann pDrvData->bBDInitialized = true;
6111da177e4SLinus Torvalds
6121da177e4SLinus Torvalds retval = tp3780I_CalcResources(&pDrvData->rBDData);
6131da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
6141da177e4SLinus Torvalds "mwavedd::mwave_init, return from tp3780I_CalcResources"
6151da177e4SLinus Torvalds " retval %x\n",
6161da177e4SLinus Torvalds retval);
6171da177e4SLinus Torvalds if (retval) {
6181da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6191da177e4SLinus Torvalds "mwavedd:mwave_init: Error:"
6201da177e4SLinus Torvalds " Failed to calculate resources\n");
6211da177e4SLinus Torvalds goto cleanup_error;
6221da177e4SLinus Torvalds }
6231da177e4SLinus Torvalds
6241da177e4SLinus Torvalds retval = tp3780I_ClaimResources(&pDrvData->rBDData);
6251da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
6261da177e4SLinus Torvalds "mwavedd::mwave_init, return from tp3780I_ClaimResources"
6271da177e4SLinus Torvalds " retval %x\n",
6281da177e4SLinus Torvalds retval);
6291da177e4SLinus Torvalds if (retval) {
6301da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6311da177e4SLinus Torvalds "mwavedd:mwave_init: Error:"
6321da177e4SLinus Torvalds " Failed to claim resources\n");
6331da177e4SLinus Torvalds goto cleanup_error;
6341da177e4SLinus Torvalds }
63526ec99b1SArnd Bergmann pDrvData->bResourcesClaimed = true;
6361da177e4SLinus Torvalds
6371da177e4SLinus Torvalds retval = tp3780I_EnableDSP(&pDrvData->rBDData);
6381da177e4SLinus Torvalds PRINTK_2(TRACE_MWAVE,
6391da177e4SLinus Torvalds "mwavedd::mwave_init, return from tp3780I_EnableDSP"
6401da177e4SLinus Torvalds " retval %x\n",
6411da177e4SLinus Torvalds retval);
6421da177e4SLinus Torvalds if (retval) {
6431da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6441da177e4SLinus Torvalds "mwavedd:mwave_init: Error:"
6451da177e4SLinus Torvalds " Failed to enable DSP\n");
6461da177e4SLinus Torvalds goto cleanup_error;
6471da177e4SLinus Torvalds }
64826ec99b1SArnd Bergmann pDrvData->bDSPEnabled = true;
6491da177e4SLinus Torvalds
6501da177e4SLinus Torvalds if (misc_register(&mwave_misc_dev) < 0) {
6511da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6521da177e4SLinus Torvalds "mwavedd:mwave_init: Error:"
6531da177e4SLinus Torvalds " Failed to register misc device\n");
6541da177e4SLinus Torvalds goto cleanup_error;
6551da177e4SLinus Torvalds }
65626ec99b1SArnd Bergmann pDrvData->bMwaveDevRegistered = true;
6571da177e4SLinus Torvalds
6581da177e4SLinus Torvalds pDrvData->sLine = register_serial_portandirq(
6591da177e4SLinus Torvalds pDrvData->rBDData.rDspSettings.usUartBaseIO,
6601da177e4SLinus Torvalds pDrvData->rBDData.rDspSettings.usUartIrq
6611da177e4SLinus Torvalds );
6621da177e4SLinus Torvalds if (pDrvData->sLine < 0) {
6631da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6641da177e4SLinus Torvalds "mwavedd:mwave_init: Error:"
6651da177e4SLinus Torvalds " Failed to register serial driver\n");
6661da177e4SLinus Torvalds goto cleanup_error;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds /* uart is registered */
6691da177e4SLinus Torvalds
6701da177e4SLinus Torvalds #if 0
6711da177e4SLinus Torvalds /* sysfs */
6721da177e4SLinus Torvalds memset(&mwave_device, 0, sizeof (struct device));
67324d25475SKay Sievers dev_set_name(&mwave_device, "mwave");
6741da177e4SLinus Torvalds
6751da177e4SLinus Torvalds if (device_register(&mwave_device))
6761da177e4SLinus Torvalds goto cleanup_error;
67726ec99b1SArnd Bergmann pDrvData->device_registered = true;
6781da177e4SLinus Torvalds for (i = 0; i < ARRAY_SIZE(mwave_dev_attrs); i++) {
6791da177e4SLinus Torvalds if(device_create_file(&mwave_device, mwave_dev_attrs[i])) {
6801da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6811da177e4SLinus Torvalds "mwavedd:mwave_init: Error:"
6821da177e4SLinus Torvalds " Failed to create sysfs file %s\n",
6831da177e4SLinus Torvalds mwave_dev_attrs[i]->attr.name);
6841da177e4SLinus Torvalds goto cleanup_error;
6851da177e4SLinus Torvalds }
6861da177e4SLinus Torvalds pDrvData->nr_registered_attrs++;
6871da177e4SLinus Torvalds }
6881da177e4SLinus Torvalds #endif
6891da177e4SLinus Torvalds
6901da177e4SLinus Torvalds /* SUCCESS! */
6911da177e4SLinus Torvalds return 0;
6921da177e4SLinus Torvalds
6931da177e4SLinus Torvalds cleanup_error:
6941da177e4SLinus Torvalds PRINTK_ERROR(KERN_ERR_MWAVE
6951da177e4SLinus Torvalds "mwavedd::mwave_init: Error:"
6961da177e4SLinus Torvalds " Failed to initialize\n");
6971da177e4SLinus Torvalds mwave_exit(); /* clean up */
6981da177e4SLinus Torvalds
6991da177e4SLinus Torvalds return -EIO;
7001da177e4SLinus Torvalds }
7011da177e4SLinus Torvalds
7021da177e4SLinus Torvalds module_init(mwave_init);
7031da177e4SLinus Torvalds
704