[PATCH] m68k: dsp56k __user annotations
[linux-2.6.git] / drivers / char / dsp56k.c
1 /*
2  * The DSP56001 Device Driver, saviour of the Free World(tm)
3  *
4  * Authors: Fredrik Noring   <noring@nocrew.org>
5  *          lars brinkhoff   <lars@nocrew.org>
6  *          Tomas Berndtsson <tomas@nocrew.org>
7  *
8  * First version May 1996
9  *
10  * History:
11  *  97-01-29   Tomas Berndtsson,
12  *               Integrated with Linux 2.1.21 kernel sources.
13  *  97-02-15   Tomas Berndtsson,
14  *               Fixed for kernel 2.1.26
15  *
16  * BUGS:
17  *  Hmm... there must be something here :)
18  *
19  * Copyright (C) 1996,1997 Fredrik Noring, lars brinkhoff & Tomas Berndtsson
20  *
21  * This file is subject to the terms and conditions of the GNU General Public
22  * License.  See the file COPYING in the main directory of this archive
23  * for more details.
24  */
25
26 #include <linux/module.h>
27 #include <linux/slab.h> /* for kmalloc() and kfree() */
28 #include <linux/sched.h>        /* for struct wait_queue etc */
29 #include <linux/major.h>
30 #include <linux/types.h>
31 #include <linux/errno.h>
32 #include <linux/delay.h>        /* guess what */
33 #include <linux/fs.h>
34 #include <linux/mm.h>
35 #include <linux/init.h>
36 #include <linux/devfs_fs_kernel.h>
37 #include <linux/smp_lock.h>
38 #include <linux/device.h>
39
40 #include <asm/atarihw.h>
41 #include <asm/traps.h>
42 #include <asm/uaccess.h>        /* For put_user and get_user */
43
44 #include <asm/dsp56k.h>
45
46 /* minor devices */
47 #define DSP56K_DEV_56001        0    /* The only device so far */
48
49 #define TIMEOUT    10   /* Host port timeout in number of tries */
50 #define MAXIO    2048   /* Maximum number of words before sleep */
51 #define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
52
53 #define DSP56K_TX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_TREQ
54 #define DSP56K_RX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_RREQ
55 #define DSP56K_TX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
56 #define DSP56K_RX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
57
58 #define DSP56K_TRANSMIT         (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
59 #define DSP56K_RECEIVE          (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
60
61 #define handshake(count, maxio, timeout, ENABLE, f) \
62 { \
63         long i, t, m; \
64         while (count > 0) { \
65                 m = min_t(unsigned long, count, maxio); \
66                 for (i = 0; i < m; i++) { \
67                         for (t = 0; t < timeout && !ENABLE; t++) \
68                                 msleep(20); \
69                         if(!ENABLE) \
70                                 return -EIO; \
71                         f; \
72                 } \
73                 count -= m; \
74                 if (m == maxio) msleep(20); \
75         } \
76 }
77
78 #define tx_wait(n) \
79 { \
80         int t; \
81         for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
82                 msleep(10); \
83         if(!DSP56K_TRANSMIT) { \
84                 return -EIO; \
85         } \
86 }
87
88 #define rx_wait(n) \
89 { \
90         int t; \
91         for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
92                 msleep(10); \
93         if(!DSP56K_RECEIVE) { \
94                 return -EIO; \
95         } \
96 }
97
98 /* DSP56001 bootstrap code */
99 static char bootstrap[] = {
100         0x0c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
102         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
107         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
110         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
119         0x00, 0x00, 0x60, 0xf4, 0x00, 0x00, 0x00, 0x4f, 0x61, 0xf4,
120         0x00, 0x00, 0x7e, 0xa9, 0x06, 0x2e, 0x80, 0x00, 0x00, 0x47,
121         0x07, 0xd8, 0x84, 0x07, 0x59, 0x84, 0x08, 0xf4, 0xa8, 0x00,
122         0x00, 0x04, 0x08, 0xf4, 0xbf, 0x00, 0x0c, 0x00, 0x00, 0xfe,
123         0xb8, 0x0a, 0xf0, 0x80, 0x00, 0x7e, 0xa9, 0x08, 0xf4, 0xa0,
124         0x00, 0x00, 0x01, 0x08, 0xf4, 0xbe, 0x00, 0x00, 0x00, 0x0a,
125         0xa9, 0x80, 0x00, 0x7e, 0xad, 0x08, 0x4e, 0x2b, 0x44, 0xf4,
126         0x00, 0x00, 0x00, 0x03, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x01,
127         0x0e, 0xa0, 0x00, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb5, 0x08,
128         0x50, 0x2b, 0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xb8, 0x08, 0x46,
129         0x2b, 0x44, 0xf4, 0x45, 0x00, 0x00, 0x02, 0x0a, 0xf0, 0xaa,
130         0x00, 0x7e, 0xc9, 0x20, 0x00, 0x45, 0x0a, 0xf0, 0xaa, 0x00,
131         0x7e, 0xd0, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xc6, 0x0a, 0xa9,
132         0x80, 0x00, 0x7e, 0xc4, 0x08, 0x58, 0x6b, 0x0a, 0xf0, 0x80,
133         0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xcd, 0x0a,
134         0xa9, 0x80, 0x00, 0x7e, 0xcb, 0x08, 0x58, 0xab, 0x0a, 0xf0,
135         0x80, 0x00, 0x7e, 0xad, 0x06, 0xc6, 0x00, 0x00, 0x7e, 0xd4,
136         0x0a, 0xa9, 0x80, 0x00, 0x7e, 0xd2, 0x08, 0x58, 0xeb, 0x0a,
137         0xf0, 0x80, 0x00, 0x7e, 0xad};
138 static int sizeof_bootstrap = 375;
139
140
141 static struct dsp56k_device {
142         long in_use;
143         long maxio, timeout;
144         int tx_wsize, rx_wsize;
145 } dsp56k;
146
147 static struct class *dsp56k_class;
148
149 static int dsp56k_reset(void)
150 {
151         u_char status;
152         
153         /* Power down the DSP */
154         sound_ym.rd_data_reg_sel = 14;
155         status = sound_ym.rd_data_reg_sel & 0xef;
156         sound_ym.wd_data = status;
157         sound_ym.wd_data = status | 0x10;
158   
159         udelay(10);
160   
161         /* Power up the DSP */
162         sound_ym.rd_data_reg_sel = 14;
163         sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
164
165         return 0;
166 }
167
168 static int dsp56k_upload(u_char __user *bin, int len)
169 {
170         int i;
171         u_char *p;
172         
173         dsp56k_reset();
174   
175         p = bootstrap;
176         for (i = 0; i < sizeof_bootstrap/3; i++) {
177                 /* tx_wait(10); */
178                 dsp56k_host_interface.data.b[1] = *p++;
179                 dsp56k_host_interface.data.b[2] = *p++;
180                 dsp56k_host_interface.data.b[3] = *p++;
181         }
182         for (; i < 512; i++) {
183                 /* tx_wait(10); */
184                 dsp56k_host_interface.data.b[1] = 0;
185                 dsp56k_host_interface.data.b[2] = 0;
186                 dsp56k_host_interface.data.b[3] = 0;
187         }
188   
189         for (i = 0; i < len; i++) {
190                 tx_wait(10);
191                 get_user(dsp56k_host_interface.data.b[1], bin++);
192                 get_user(dsp56k_host_interface.data.b[2], bin++);
193                 get_user(dsp56k_host_interface.data.b[3], bin++);
194         }
195
196         tx_wait(10);
197         dsp56k_host_interface.data.l = 3;    /* Magic execute */
198
199         return 0;
200 }
201
202 static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
203                            loff_t *ppos)
204 {
205         struct inode *inode = file->f_dentry->d_inode;
206         int dev = iminor(inode) & 0x0f;
207
208         switch(dev)
209         {
210         case DSP56K_DEV_56001:
211         {
212
213                 long n;
214
215                 /* Don't do anything if nothing is to be done */
216                 if (!count) return 0;
217
218                 n = 0;
219                 switch (dsp56k.rx_wsize) {
220                 case 1:  /* 8 bit */
221                 {
222                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
223                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
224                         return n;
225                 }
226                 case 2:  /* 16 bit */
227                 {
228                         short __user *data;
229
230                         count /= 2;
231                         data = (short __user *) buf;
232                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
233                                   put_user(dsp56k_host_interface.data.w[1], data+n++));
234                         return 2*n;
235                 }
236                 case 3:  /* 24 bit */
237                 {
238                         count /= 3;
239                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
240                                   put_user(dsp56k_host_interface.data.b[1], buf+n++);
241                                   put_user(dsp56k_host_interface.data.b[2], buf+n++);
242                                   put_user(dsp56k_host_interface.data.b[3], buf+n++));
243                         return 3*n;
244                 }
245                 case 4:  /* 32 bit */
246                 {
247                         long __user *data;
248
249                         count /= 4;
250                         data = (long __user *) buf;
251                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
252                                   put_user(dsp56k_host_interface.data.l, data+n++));
253                         return 4*n;
254                 }
255                 }
256                 return -EFAULT;
257         }
258
259         default:
260                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
261                 return -ENXIO;
262         }
263 }
264
265 static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
266                             loff_t *ppos)
267 {
268         struct inode *inode = file->f_dentry->d_inode;
269         int dev = iminor(inode) & 0x0f;
270
271         switch(dev)
272         {
273         case DSP56K_DEV_56001:
274         {
275                 long n;
276
277                 /* Don't do anything if nothing is to be done */
278                 if (!count) return 0;
279
280                 n = 0;
281                 switch (dsp56k.tx_wsize) {
282                 case 1:  /* 8 bit */
283                 {
284                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
285                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
286                         return n;
287                 }
288                 case 2:  /* 16 bit */
289                 {
290                         const short __user *data;
291
292                         count /= 2;
293                         data = (const short __user *)buf;
294                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
295                                   get_user(dsp56k_host_interface.data.w[1], data+n++));
296                         return 2*n;
297                 }
298                 case 3:  /* 24 bit */
299                 {
300                         count /= 3;
301                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
302                                   get_user(dsp56k_host_interface.data.b[1], buf+n++);
303                                   get_user(dsp56k_host_interface.data.b[2], buf+n++);
304                                   get_user(dsp56k_host_interface.data.b[3], buf+n++));
305                         return 3*n;
306                 }
307                 case 4:  /* 32 bit */
308                 {
309                         const long __user *data;
310
311                         count /= 4;
312                         data = (const long __user *)buf;
313                         handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
314                                   get_user(dsp56k_host_interface.data.l, data+n++));
315                         return 4*n;
316                 }
317                 }
318
319                 return -EFAULT;
320         }
321         default:
322                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
323                 return -ENXIO;
324         }
325 }
326
327 static int dsp56k_ioctl(struct inode *inode, struct file *file,
328                         unsigned int cmd, unsigned long arg)
329 {
330         int dev = iminor(inode) & 0x0f;
331         void __user *argp = (void __user *)arg;
332
333         switch(dev)
334         {
335         case DSP56K_DEV_56001:
336
337                 switch(cmd) {
338                 case DSP56K_UPLOAD:
339                 {
340                         char __user *bin;
341                         int r, len;
342                         struct dsp56k_upload __user *binary = argp;
343     
344                         if(get_user(len, &binary->len) < 0)
345                                 return -EFAULT;
346                         if(get_user(bin, &binary->bin) < 0)
347                                 return -EFAULT;
348                 
349                         if (len == 0) {
350                                 return -EINVAL;      /* nothing to upload?!? */
351                         }
352                         if (len > DSP56K_MAX_BINARY_LENGTH) {
353                                 return -EINVAL;
354                         }
355     
356                         r = dsp56k_upload(bin, len);
357                         if (r < 0) {
358                                 return r;
359                         }
360     
361                         break;
362                 }
363                 case DSP56K_SET_TX_WSIZE:
364                         if (arg > 4 || arg < 1)
365                                 return -EINVAL;
366                         dsp56k.tx_wsize = (int) arg;
367                         break;
368                 case DSP56K_SET_RX_WSIZE:
369                         if (arg > 4 || arg < 1)
370                                 return -EINVAL;
371                         dsp56k.rx_wsize = (int) arg;
372                         break;
373                 case DSP56K_HOST_FLAGS:
374                 {
375                         int dir, out, status;
376                         struct dsp56k_host_flags __user *hf = argp;
377     
378                         if(get_user(dir, &hf->dir) < 0)
379                                 return -EFAULT;
380                         if(get_user(out, &hf->out) < 0)
381                                 return -EFAULT;
382
383                         if ((dir & 0x1) && (out & 0x1))
384                                 dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
385                         else if (dir & 0x1)
386                                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
387                         if ((dir & 0x2) && (out & 0x2))
388                                 dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
389                         else if (dir & 0x2)
390                                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
391
392                         status = 0;
393                         if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
394                         if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
395                         if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
396                         if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
397
398                         return put_user(status, &hf->status);
399                 }
400                 case DSP56K_HOST_CMD:
401                         if (arg > 31 || arg < 0)
402                                 return -EINVAL;
403                         dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
404                                                              DSP56K_CVR_HC);
405                         break;
406                 default:
407                         return -EINVAL;
408                 }
409                 return 0;
410
411         default:
412                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
413                 return -ENXIO;
414         }
415 }
416
417 /* As of 2.1.26 this should be dsp56k_poll,
418  * but how do I then check device minor number?
419  * Do I need this function at all???
420  */
421 #if 0
422 static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
423 {
424         int dev = iminor(file->f_dentry->d_inode) & 0x0f;
425
426         switch(dev)
427         {
428         case DSP56K_DEV_56001:
429                 /* poll_wait(file, ???, wait); */
430                 return POLLIN | POLLRDNORM | POLLOUT;
431
432         default:
433                 printk("DSP56k driver: Unknown minor device: %d\n", dev);
434                 return 0;
435         }
436 }
437 #endif
438
439 static int dsp56k_open(struct inode *inode, struct file *file)
440 {
441         int dev = iminor(inode) & 0x0f;
442
443         switch(dev)
444         {
445         case DSP56K_DEV_56001:
446
447                 if (test_and_set_bit(0, &dsp56k.in_use))
448                         return -EBUSY;
449
450                 dsp56k.timeout = TIMEOUT;
451                 dsp56k.maxio = MAXIO;
452                 dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 
453
454                 DSP56K_TX_INT_OFF;
455                 DSP56K_RX_INT_OFF;
456
457                 /* Zero host flags */
458                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
459                 dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
460
461                 break;
462
463         default:
464                 return -ENODEV;
465         }
466
467         return 0;
468 }
469
470 static int dsp56k_release(struct inode *inode, struct file *file)
471 {
472         int dev = iminor(inode) & 0x0f;
473
474         switch(dev)
475         {
476         case DSP56K_DEV_56001:
477                 clear_bit(0, &dsp56k.in_use);
478                 break;
479         default:
480                 printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
481                 return -ENXIO;
482         }
483
484         return 0;
485 }
486
487 static struct file_operations dsp56k_fops = {
488         .owner          = THIS_MODULE,
489         .read           = dsp56k_read,
490         .write          = dsp56k_write,
491         .ioctl          = dsp56k_ioctl,
492         .open           = dsp56k_open,
493         .release        = dsp56k_release,
494 };
495
496
497 /****** Init and module functions ******/
498
499 static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
500
501 static int __init dsp56k_init_driver(void)
502 {
503         int err = 0;
504
505         if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
506                 printk("DSP56k driver: Hardware not present\n");
507                 return -ENODEV;
508         }
509
510         if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
511                 printk("DSP56k driver: Unable to register driver\n");
512                 return -ENODEV;
513         }
514         dsp56k_class = class_create(THIS_MODULE, "dsp56k");
515         if (IS_ERR(dsp56k_class)) {
516                 err = PTR_ERR(dsp56k_class);
517                 goto out_chrdev;
518         }
519         class_device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL, "dsp56k");
520
521         err = devfs_mk_cdev(MKDEV(DSP56K_MAJOR, 0),
522                       S_IFCHR | S_IRUSR | S_IWUSR, "dsp56k");
523         if(err)
524                 goto out_class;
525
526         printk(banner);
527         goto out;
528
529 out_class:
530         class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
531         class_destroy(dsp56k_class);
532 out_chrdev:
533         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
534 out:
535         return err;
536 }
537 module_init(dsp56k_init_driver);
538
539 static void __exit dsp56k_cleanup_driver(void)
540 {
541         class_device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
542         class_destroy(dsp56k_class);
543         unregister_chrdev(DSP56K_MAJOR, "dsp56k");
544         devfs_remove("dsp56k");
545 }
546 module_exit(dsp56k_cleanup_driver);
547
548 MODULE_LICENSE("GPL");