File Index
Files and directories
Sign in
FILE

mwavedd.c

File preview with syntax highlighting.

linux-6.19.14/drivers/char/mwave/mwavedd.c C
  1/*
  2*
  3* mwavedd.c -- mwave device driver
  4*
  5*
  6* Written By: Mike Sullivan IBM Corporation
  7*
  8* Copyright (C) 1999 IBM Corporation
  9*
 10* This program is free software; you can redistribute it and/or modify
 11* it under the terms of the GNU General Public License as published by
 12* the Free Software Foundation; either version 2 of the License, or
 13* (at your option) any later version.
 14*
 15* This program is distributed in the hope that it will be useful,
 16* but WITHOUT ANY WARRANTY; without even the implied warranty of
 17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18* GNU General Public License for more details.
 19*
 20* NO WARRANTY
 21* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
 22* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
 23* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
 24* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
 25* solely responsible for determining the appropriateness of using and
 26* distributing the Program and assumes all risks associated with its
 27* exercise of rights under this Agreement, including but not limited to
 28* the risks and costs of program errors, damage to or loss of data,
 29* programs or equipment, and unavailability or interruption of operations.
 30*
 31* DISCLAIMER OF LIABILITY
 32* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
 33* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 34* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
 35* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 36* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 37* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
 38* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
 39*
 40* You should have received a copy of the GNU General Public License
 41* along with this program; if not, write to the Free Software
 42* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 43*
 44*
 45* 10/23/2000 - Alpha Release
 46*	First release to the public
 47*/
 48
 49#define pr_fmt(fmt) "mwavedd: " fmt
 50
 51#include <linux/module.h>
 52#include <linux/kernel.h>
 53#include <linux/fs.h>
 54#include <linux/init.h>
 55#include <linux/major.h>
 56#include <linux/miscdevice.h>
 57#include <linux/device.h>
 58#include <linux/serial.h>
 59#include <linux/sched.h>
 60#include <linux/spinlock.h>
 61#include <linux/mutex.h>
 62#include <linux/delay.h>
 63#include <linux/serial_8250.h>
 64#include <linux/nospec.h>
 65#include "smapi.h"
 66#include "mwavedd.h"
 67#include "3780i.h"
 68#include "tp3780i.h"
 69
 70MODULE_DESCRIPTION("3780i Advanced Communications Processor (Mwave) driver");
 71MODULE_AUTHOR("Mike Sullivan and Paul Schroeder");
 72MODULE_LICENSE("GPL");
 73
 74/*
 75* These parameters support the setting of MWave resources. Note that no
 76* checks are made against other devices (ie. superio) for conflicts.
 77* We'll depend on users using the tpctl utility to do that for now
 78*/
 79static DEFINE_MUTEX(mwave_mutex);
 80int mwave_3780i_irq = 0;
 81int mwave_3780i_io = 0;
 82int mwave_uart_irq = 0;
 83int mwave_uart_io = 0;
 84module_param_hw(mwave_3780i_irq, int, irq, 0);
 85module_param_hw(mwave_3780i_io, int, ioport, 0);
 86module_param_hw(mwave_uart_irq, int, irq, 0);
 87module_param_hw(mwave_uart_io, int, ioport, 0);
 88
 89struct mwave_device_data mwave_s_mdd;
 90
 91static long mwave_ioctl(struct file *file, unsigned int iocmd,
 92							unsigned long ioarg)
 93{
 94	unsigned int retval = 0;
 95	struct mwave_device_data *pDrvData = &mwave_s_mdd;
 96	void __user *arg = (void __user *)ioarg;
 97
 98	switch (iocmd) {
 99
100		case IOCTL_MW_RESET:
101			mutex_lock(&mwave_mutex);
102			retval = tp3780I_ResetDSP(&pDrvData->rBDData);
103			mutex_unlock(&mwave_mutex);
104			break;
105	
106		case IOCTL_MW_RUN:
107			mutex_lock(&mwave_mutex);
108			retval = tp3780I_StartDSP(&pDrvData->rBDData);
109			mutex_unlock(&mwave_mutex);
110			break;
111	
112		case IOCTL_MW_DSP_ABILITIES: {
113			struct mw_abilities rAbilities;
114	
115			mutex_lock(&mwave_mutex);
116			retval = tp3780I_QueryAbilities(&pDrvData->rBDData,
117					&rAbilities);
118			mutex_unlock(&mwave_mutex);
119			if (retval == 0) {
120				if (copy_to_user(arg, &rAbilities, sizeof(rAbilities)))
121					return -EFAULT;
122			}
123		}
124			break;
125	
126		case IOCTL_MW_READ_DATA:
127		case IOCTL_MW_READCLEAR_DATA: {
128			struct mw_readwrite rReadData;
129			unsigned short __user *pusBuffer = NULL;
130	
131			if( copy_from_user(&rReadData, arg,
132						sizeof(struct mw_readwrite)) )
133				return -EFAULT;
134			pusBuffer = (unsigned short __user *) (rReadData.pBuf);
135	
136			mutex_lock(&mwave_mutex);
137			retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
138					iocmd,
139					pusBuffer,
140					rReadData.ulDataLength,
141					rReadData.usDspAddress);
142			mutex_unlock(&mwave_mutex);
143		}
144			break;
145	
146		case IOCTL_MW_READ_INST: {
147			struct mw_readwrite rReadData;
148			unsigned short __user *pusBuffer = NULL;
149	
150			if (copy_from_user(&rReadData, arg, sizeof(rReadData)))
151				return -EFAULT;
152			pusBuffer = (unsigned short __user *) (rReadData.pBuf);
153	
154			mutex_lock(&mwave_mutex);
155			retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
156				iocmd, pusBuffer,
157				rReadData.ulDataLength / 2,
158				rReadData.usDspAddress);
159			mutex_unlock(&mwave_mutex);
160		}
161			break;
162	
163		case IOCTL_MW_WRITE_DATA: {
164			struct mw_readwrite rWriteData;
165			unsigned short __user *pusBuffer = NULL;
166	
167			if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
168				return -EFAULT;
169			pusBuffer = (unsigned short __user *) (rWriteData.pBuf);
170	
171			mutex_lock(&mwave_mutex);
172			retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData,
173					iocmd, pusBuffer,
174					rWriteData.ulDataLength,
175					rWriteData.usDspAddress);
176			mutex_unlock(&mwave_mutex);
177		}
178			break;
179	
180		case IOCTL_MW_WRITE_INST: {
181			struct mw_readwrite rWriteData;
182			unsigned short __user *pusBuffer = NULL;
183	
184			if (copy_from_user(&rWriteData, arg, sizeof(rWriteData)))
185				return -EFAULT;
186			pusBuffer = (unsigned short __user *)(rWriteData.pBuf);
187	
188			mutex_lock(&mwave_mutex);
189			retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData,
190					iocmd, pusBuffer,
191					rWriteData.ulDataLength,
192					rWriteData.usDspAddress);
193			mutex_unlock(&mwave_mutex);
194		}
195			break;
196	
197		case IOCTL_MW_REGISTER_IPC: {
198			unsigned int ipcnum = (unsigned int) ioarg;
199	
200			if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
201				pr_err("%s: IOCTL_MW_REGISTER_IPC: Error: Invalid ipcnum %x\n",
202				       __func__, ipcnum);
203				return -EINVAL;
204			}
205			ipcnum = array_index_nospec(ipcnum,
206						    ARRAY_SIZE(pDrvData->IPCs));
207
208			mutex_lock(&mwave_mutex);
209			pDrvData->IPCs[ipcnum].bIsHere = false;
210			pDrvData->IPCs[ipcnum].bIsEnabled = true;
211			mutex_unlock(&mwave_mutex);
212		}
213			break;
214	
215		case IOCTL_MW_GET_IPC: {
216			unsigned int ipcnum = (unsigned int) ioarg;
217	
218			if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
219				pr_err("%s: IOCTL_MW_GET_IPC: Error: Invalid ipcnum %x\n", __func__,
220				       ipcnum);
221				return -EINVAL;
222			}
223			ipcnum = array_index_nospec(ipcnum,
224						    ARRAY_SIZE(pDrvData->IPCs));
225
226			mutex_lock(&mwave_mutex);
227			if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
228				DECLARE_WAITQUEUE(wait, current);
229
230				add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
231				pDrvData->IPCs[ipcnum].bIsHere = true;
232				set_current_state(TASK_INTERRUPTIBLE);
233				/* check whether an event was signalled by */
234				/* the interrupt handler while we were gone */
235				if (pDrvData->IPCs[ipcnum].usIntCount == 1) {	/* first int has occurred (race condition) */
236					pDrvData->IPCs[ipcnum].usIntCount = 2;	/* first int has been handled */
237				} else {	/* either 1st int has not yet occurred, or we have already handled the first int */
238					schedule();
239					if (pDrvData->IPCs[ipcnum].usIntCount == 1) {
240						pDrvData->IPCs[ipcnum].usIntCount = 2;
241					}
242				}
243				pDrvData->IPCs[ipcnum].bIsHere = false;
244				remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait);
245				set_current_state(TASK_RUNNING);
246			}
247			mutex_unlock(&mwave_mutex);
248		}
249			break;
250	
251		case IOCTL_MW_UNREGISTER_IPC: {
252			unsigned int ipcnum = (unsigned int) ioarg;
253	
254			if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
255				pr_err("%s: IOCTL_MW_UNREGISTER_IPC: Error: Invalid ipcnum %x\n",
256				       __func__, ipcnum);
257				return -EINVAL;
258			}
259			ipcnum = array_index_nospec(ipcnum,
260						    ARRAY_SIZE(pDrvData->IPCs));
261			mutex_lock(&mwave_mutex);
262			if (pDrvData->IPCs[ipcnum].bIsEnabled == true) {
263				pDrvData->IPCs[ipcnum].bIsEnabled = false;
264				if (pDrvData->IPCs[ipcnum].bIsHere == true) {
265					wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue);
266				}
267			}
268			mutex_unlock(&mwave_mutex);
269		}
270			break;
271	
272		default:
273			return -ENOTTY;
274	} /* switch */
275
276	return retval;
277}
278
279static int register_serial_portandirq(unsigned int port, int irq)
280{
281	struct uart_8250_port uart;
282	
283	switch ( port ) {
284		case 0x3f8:
285		case 0x2f8:
286		case 0x3e8:
287		case 0x2e8:
288			/* OK */
289			break;
290		default:
291			pr_err("%s: Error: Illegal port %x\n", __func__, port);
292			return -1;
293	} /* switch */
294	/* port is okay */
295
296	switch ( irq ) {
297		case 3:
298		case 4:
299		case 5:
300		case 7:
301			/* OK */
302			break;
303		default:
304			pr_err("%s: Error: Illegal irq %x\n", __func__, irq);
305			return -1;
306	} /* switch */
307	/* irq is okay */
308
309	memset(&uart, 0, sizeof(uart));
310	
311	uart.port.uartclk =  1843200;
312	uart.port.iobase = port;
313	uart.port.irq = irq;
314	uart.port.iotype = UPIO_PORT;
315	uart.port.flags =  UPF_SHARE_IRQ;
316	return serial8250_register_8250_port(&uart);
317}
318
319static const struct file_operations mwave_fops = {
320	.owner		= THIS_MODULE,
321	.unlocked_ioctl	= mwave_ioctl,
322	.llseek		= default_llseek,
323};
324
325static struct miscdevice mwave_misc_dev = { MWAVE_MINOR, "mwave", &mwave_fops };
326
327/*
328* mwave_init is called on module load
329*
330* mwave_exit is called on module unload
331* mwave_exit is also used to clean up after an aborted mwave_init
332*/
333static void mwave_exit(void)
334{
335	struct mwave_device_data *pDrvData = &mwave_s_mdd;
336
337	if ( pDrvData->sLine >= 0 ) {
338		serial8250_unregister_port(pDrvData->sLine);
339	}
340	if (pDrvData->bMwaveDevRegistered) {
341		misc_deregister(&mwave_misc_dev);
342	}
343	if (pDrvData->bDSPEnabled) {
344		tp3780I_DisableDSP(&pDrvData->rBDData);
345	}
346	if (pDrvData->bResourcesClaimed) {
347		tp3780I_ReleaseResources(&pDrvData->rBDData);
348	}
349	if (pDrvData->bBDInitialized) {
350		tp3780I_Cleanup(&pDrvData->rBDData);
351	}
352}
353
354module_exit(mwave_exit);
355
356static int __init mwave_init(void)
357{
358	int i;
359	int retval = 0;
360	struct mwave_device_data *pDrvData = &mwave_s_mdd;
361
362	memset(&mwave_s_mdd, 0, sizeof(mwave_s_mdd));
363
364	pDrvData->bBDInitialized = false;
365	pDrvData->bResourcesClaimed = false;
366	pDrvData->bDSPEnabled = false;
367	pDrvData->bDSPReset = false;
368	pDrvData->bMwaveDevRegistered = false;
369	pDrvData->sLine = -1;
370
371	for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) {
372		pDrvData->IPCs[i].bIsEnabled = false;
373		pDrvData->IPCs[i].bIsHere = false;
374		pDrvData->IPCs[i].usIntCount = 0;	/* no ints received yet */
375		init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue);
376	}
377
378	retval = tp3780I_InitializeBoardData(&pDrvData->rBDData);
379	if (retval) {
380		pr_err("%s: Error: Failed to initialize board data\n", __func__);
381		goto cleanup_error;
382	}
383	pDrvData->bBDInitialized = true;
384
385	retval = tp3780I_CalcResources(&pDrvData->rBDData);
386	if (retval) {
387		pr_err("%s: Error: Failed to calculate resources\n", __func__);
388		goto cleanup_error;
389	}
390
391	retval = tp3780I_ClaimResources(&pDrvData->rBDData);
392	if (retval) {
393		pr_err("%s: Error: Failed to claim resources\n", __func__);
394		goto cleanup_error;
395	}
396	pDrvData->bResourcesClaimed = true;
397
398	retval = tp3780I_EnableDSP(&pDrvData->rBDData);
399	if (retval) {
400		pr_err("%s: Error: Failed to enable DSP\n", __func__);
401		goto cleanup_error;
402	}
403	pDrvData->bDSPEnabled = true;
404
405	if (misc_register(&mwave_misc_dev) < 0) {
406		pr_err("%s: Error: Failed to register misc device\n", __func__);
407		goto cleanup_error;
408	}
409	pDrvData->bMwaveDevRegistered = true;
410
411	pDrvData->sLine = register_serial_portandirq(
412		pDrvData->rBDData.rDspSettings.usUartBaseIO,
413		pDrvData->rBDData.rDspSettings.usUartIrq
414	);
415	if (pDrvData->sLine < 0) {
416		pr_err("%s: Error: Failed to register serial driver\n", __func__);
417		goto cleanup_error;
418	}
419	/* uart is registered */
420
421	/* SUCCESS! */
422	return 0;
423
424cleanup_error:
425	pr_err("%s: Error: Failed to initialize\n", __func__);
426	mwave_exit(); /* clean up */
427
428	return -EIO;
429}
430
431module_init(mwave_init);
432
~/ Read-only file preview.