Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/char/mwave/mwavedd.c
49962 views
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
70
MODULE_DESCRIPTION("3780i Advanced Communications Processor (Mwave) driver");
71
MODULE_AUTHOR("Mike Sullivan and Paul Schroeder");
72
MODULE_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
*/
79
static DEFINE_MUTEX(mwave_mutex);
80
int mwave_3780i_irq = 0;
81
int mwave_3780i_io = 0;
82
int mwave_uart_irq = 0;
83
int mwave_uart_io = 0;
84
module_param_hw(mwave_3780i_irq, int, irq, 0);
85
module_param_hw(mwave_3780i_io, int, ioport, 0);
86
module_param_hw(mwave_uart_irq, int, irq, 0);
87
module_param_hw(mwave_uart_io, int, ioport, 0);
88
89
struct mwave_device_data mwave_s_mdd;
90
91
static 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
279
static 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
319
static const struct file_operations mwave_fops = {
320
.owner = THIS_MODULE,
321
.unlocked_ioctl = mwave_ioctl,
322
.llseek = default_llseek,
323
};
324
325
static 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
*/
333
static 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
354
module_exit(mwave_exit);
355
356
static 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
424
cleanup_error:
425
pr_err("%s: Error: Failed to initialize\n", __func__);
426
mwave_exit(); /* clean up */
427
428
return -EIO;
429
}
430
431
module_init(mwave_init);
432
433
434