linux/drivers/char/dsp56k.c History
<<
>>
Prefs
   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/major.h>
  28#include <linux/types.h>
  29#include <linux/errno.h>
  30#include <linux/delay.h>        /* guess what */
  31#include <linux/fs.h>
  32#include <linux/mm.h>
  33#include <linux/init.h>
  34#include <linux/device.h>
  35#include <linux/smp_lock.h>
  36#include <linux/firmware.h>
  37#include <linux/platform_device.h>
  38#include <linux/uaccess.h>      /* For put_user and get_user */
  39
  40#include <asm/atarihw.h>
  41#include <asm/traps.h>
  42
  43#include <asm/dsp56k.h>
  44
  45/* minor devices */
  46#define DSP56K_DEV_56001        0    /* The only device so far */
  47
  48#define TIMEOUT    10   /* Host port timeout in number of tries */
  49#define MAXIO    2048   /* Maximum number of words before sleep */
  50#define DSP56K_MAX_BINARY_LENGTH (3*64*1024)
  51
  52#define DSP56K_TX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_TREQ
  53#define DSP56K_RX_INT_ON        dsp56k_host_interface.icr |=  DSP56K_ICR_RREQ
  54#define DSP56K_TX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_TREQ
  55#define DSP56K_RX_INT_OFF       dsp56k_host_interface.icr &= ~DSP56K_ICR_RREQ
  56
  57#define DSP56K_TRANSMIT         (dsp56k_host_interface.isr & DSP56K_ISR_TXDE)
  58#define DSP56K_RECEIVE          (dsp56k_host_interface.isr & DSP56K_ISR_RXDF)
  59
  60#define handshake(count, maxio, timeout, ENABLE, f) \
  61{ \
  62        long i, t, m; \
  63        while (count > 0) { \
  64                m = min_t(unsigned long, count, maxio); \
  65                for (i = 0; i < m; i++) { \
  66                        for (t = 0; t < timeout && !ENABLE; t++) \
  67                                msleep(20); \
  68                        if(!ENABLE) \
  69                                return -EIO; \
  70                        f; \
  71                } \
  72                count -= m; \
  73                if (m == maxio) msleep(20); \
  74        } \
  75}
  76
  77#define tx_wait(n) \
  78{ \
  79        int t; \
  80        for(t = 0; t < n && !DSP56K_TRANSMIT; t++) \
  81                msleep(10); \
  82        if(!DSP56K_TRANSMIT) { \
  83                return -EIO; \
  84        } \
  85}
  86
  87#define rx_wait(n) \
  88{ \
  89        int t; \
  90        for(t = 0; t < n && !DSP56K_RECEIVE; t++) \
  91                msleep(10); \
  92        if(!DSP56K_RECEIVE) { \
  93                return -EIO; \
  94        } \
  95}
  96
  97static struct dsp56k_device {
  98        unsigned long in_use;
  99        long maxio, timeout;
 100        int tx_wsize, rx_wsize;
 101} dsp56k;
 102
 103static struct class *dsp56k_class;
 104
 105static int dsp56k_reset(void)
 106{
 107        u_char status;
 108        
 109        /* Power down the DSP */
 110        sound_ym.rd_data_reg_sel = 14;
 111        status = sound_ym.rd_data_reg_sel & 0xef;
 112        sound_ym.wd_data = status;
 113        sound_ym.wd_data = status | 0x10;
 114  
 115        udelay(10);
 116  
 117        /* Power up the DSP */
 118        sound_ym.rd_data_reg_sel = 14;
 119        sound_ym.wd_data = sound_ym.rd_data_reg_sel & 0xef;
 120
 121        return 0;
 122}
 123
 124static int dsp56k_upload(u_char __user *bin, int len)
 125{
 126        struct platform_device *pdev;
 127        const struct firmware *fw;
 128        const char fw_name[] = "dsp56k/bootstrap.bin";
 129        int err;
 130        int i;
 131
 132        dsp56k_reset();
 133
 134        pdev = platform_device_register_simple("dsp56k", 0, NULL, 0);
 135        if (IS_ERR(pdev)) {
 136                printk(KERN_ERR "Failed to register device for \"%s\"\n",
 137                       fw_name);
 138                return -EINVAL;
 139        }
 140        err = request_firmware(&fw, fw_name, &pdev->dev);
 141        platform_device_unregister(pdev);
 142        if (err) {
 143                printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
 144                       fw_name, err);
 145                return err;
 146        }
 147        if (fw->size % 3) {
 148                printk(KERN_ERR "Bogus length %d in image \"%s\"\n",
 149                       fw->size, fw_name);
 150                release_firmware(fw);
 151                return -EINVAL;
 152        }
 153        for (i = 0; i < fw->size; i = i + 3) {
 154                /* tx_wait(10); */
 155                dsp56k_host_interface.data.b[1] = fw->data[i];
 156                dsp56k_host_interface.data.b[2] = fw->data[i + 1];
 157                dsp56k_host_interface.data.b[3] = fw->data[i + 2];
 158        }
 159        release_firmware(fw);
 160        for (; i < 512; i++) {
 161                /* tx_wait(10); */
 162                dsp56k_host_interface.data.b[1] = 0;
 163                dsp56k_host_interface.data.b[2] = 0;
 164                dsp56k_host_interface.data.b[3] = 0;
 165        }
 166  
 167        for (i = 0; i < len; i++) {
 168                tx_wait(10);
 169                get_user(dsp56k_host_interface.data.b[1], bin++);
 170                get_user(dsp56k_host_interface.data.b[2], bin++);
 171                get_user(dsp56k_host_interface.data.b[3], bin++);
 172        }
 173
 174        tx_wait(10);
 175        dsp56k_host_interface.data.l = 3;    /* Magic execute */
 176
 177        return 0;
 178}
 179
 180static ssize_t dsp56k_read(struct file *file, char __user *buf, size_t count,
 181                           loff_t *ppos)
 182{
 183        struct inode *inode = file->f_path.dentry->d_inode;
 184        int dev = iminor(inode) & 0x0f;
 185
 186        switch(dev)
 187        {
 188        case DSP56K_DEV_56001:
 189        {
 190
 191                long n;
 192
 193                /* Don't do anything if nothing is to be done */
 194                if (!count) return 0;
 195
 196                n = 0;
 197                switch (dsp56k.rx_wsize) {
 198                case 1:  /* 8 bit */
 199                {
 200                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 201                                  put_user(dsp56k_host_interface.data.b[3], buf+n++));
 202                        return n;
 203                }
 204                case 2:  /* 16 bit */
 205                {
 206                        short __user *data;
 207
 208                        count /= 2;
 209                        data = (short __user *) buf;
 210                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 211                                  put_user(dsp56k_host_interface.data.w[1], data+n++));
 212                        return 2*n;
 213                }
 214                case 3:  /* 24 bit */
 215                {
 216                        count /= 3;
 217                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 218                                  put_user(dsp56k_host_interface.data.b[1], buf+n++);
 219                                  put_user(dsp56k_host_interface.data.b[2], buf+n++);
 220                                  put_user(dsp56k_host_interface.data.b[3], buf+n++));
 221                        return 3*n;
 222                }
 223                case 4:  /* 32 bit */
 224                {
 225                        long __user *data;
 226
 227                        count /= 4;
 228                        data = (long __user *) buf;
 229                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_RECEIVE,
 230                                  put_user(dsp56k_host_interface.data.l, data+n++));
 231                        return 4*n;
 232                }
 233                }
 234                return -EFAULT;
 235        }
 236
 237        default:
 238                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 239                return -ENXIO;
 240        }
 241}
 242
 243static ssize_t dsp56k_write(struct file *file, const char __user *buf, size_t count,
 244                            loff_t *ppos)
 245{
 246        struct inode *inode = file->f_path.dentry->d_inode;
 247        int dev = iminor(inode) & 0x0f;
 248
 249        switch(dev)
 250        {
 251        case DSP56K_DEV_56001:
 252        {
 253                long n;
 254
 255                /* Don't do anything if nothing is to be done */
 256                if (!count) return 0;
 257
 258                n = 0;
 259                switch (dsp56k.tx_wsize) {
 260                case 1:  /* 8 bit */
 261                {
 262                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 263                                  get_user(dsp56k_host_interface.data.b[3], buf+n++));
 264                        return n;
 265                }
 266                case 2:  /* 16 bit */
 267                {
 268                        const short __user *data;
 269
 270                        count /= 2;
 271                        data = (const short __user *)buf;
 272                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 273                                  get_user(dsp56k_host_interface.data.w[1], data+n++));
 274                        return 2*n;
 275                }
 276                case 3:  /* 24 bit */
 277                {
 278                        count /= 3;
 279                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 280                                  get_user(dsp56k_host_interface.data.b[1], buf+n++);
 281                                  get_user(dsp56k_host_interface.data.b[2], buf+n++);
 282                                  get_user(dsp56k_host_interface.data.b[3], buf+n++));
 283                        return 3*n;
 284                }
 285                case 4:  /* 32 bit */
 286                {
 287                        const long __user *data;
 288
 289                        count /= 4;
 290                        data = (const long __user *)buf;
 291                        handshake(count, dsp56k.maxio, dsp56k.timeout, DSP56K_TRANSMIT,
 292                                  get_user(dsp56k_host_interface.data.l, data+n++));
 293                        return 4*n;
 294                }
 295                }
 296
 297                return -EFAULT;
 298        }
 299        default:
 300                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 301                return -ENXIO;
 302        }
 303}
 304
 305static long dsp56k_ioctl(struct file *file, unsigned int cmd,
 306                         unsigned long arg)
 307{
 308        int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
 309        void __user *argp = (void __user *)arg;
 310
 311        switch(dev)
 312        {
 313        case DSP56K_DEV_56001:
 314
 315                switch(cmd) {
 316                case DSP56K_UPLOAD:
 317                {
 318                        char __user *bin;
 319                        int r, len;
 320                        struct dsp56k_upload __user *binary = argp;
 321    
 322                        if(get_user(len, &binary->len) < 0)
 323                                return -EFAULT;
 324                        if(get_user(bin, &binary->bin) < 0)
 325                                return -EFAULT;
 326                
 327                        if (len == 0) {
 328                                return -EINVAL;      /* nothing to upload?!? */
 329                        }
 330                        if (len > DSP56K_MAX_BINARY_LENGTH) {
 331                                return -EINVAL;
 332                        }
 333                        lock_kernel();
 334                        r = dsp56k_upload(bin, len);
 335                        unlock_kernel();
 336                        if (r < 0) {
 337                                return r;
 338                        }
 339    
 340                        break;
 341                }
 342                case DSP56K_SET_TX_WSIZE:
 343                        if (arg > 4 || arg < 1)
 344                                return -EINVAL;
 345                        lock_kernel();
 346                        dsp56k.tx_wsize = (int) arg;
 347                        unlock_kernel();
 348                        break;
 349                case DSP56K_SET_RX_WSIZE:
 350                        if (arg > 4 || arg < 1)
 351                                return -EINVAL;
 352                        lock_kernel();
 353                        dsp56k.rx_wsize = (int) arg;
 354                        unlock_kernel();
 355                        break;
 356                case DSP56K_HOST_FLAGS:
 357                {
 358                        int dir, out, status;
 359                        struct dsp56k_host_flags __user *hf = argp;
 360    
 361                        if(get_user(dir, &hf->dir) < 0)
 362                                return -EFAULT;
 363                        if(get_user(out, &hf->out) < 0)
 364                                return -EFAULT;
 365
 366                        lock_kernel();
 367                        if ((dir & 0x1) && (out & 0x1))
 368                                dsp56k_host_interface.icr |= DSP56K_ICR_HF0;
 369                        else if (dir & 0x1)
 370                                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
 371                        if ((dir & 0x2) && (out & 0x2))
 372                                dsp56k_host_interface.icr |= DSP56K_ICR_HF1;
 373                        else if (dir & 0x2)
 374                                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
 375
 376                        status = 0;
 377                        if (dsp56k_host_interface.icr & DSP56K_ICR_HF0) status |= 0x1;
 378                        if (dsp56k_host_interface.icr & DSP56K_ICR_HF1) status |= 0x2;
 379                        if (dsp56k_host_interface.isr & DSP56K_ISR_HF2) status |= 0x4;
 380                        if (dsp56k_host_interface.isr & DSP56K_ISR_HF3) status |= 0x8;
 381                        unlock_kernel();
 382                        return put_user(status, &hf->status);
 383                }
 384                case DSP56K_HOST_CMD:
 385                        if (arg > 31 || arg < 0)
 386                                return -EINVAL;
 387                        lock_kernel();
 388                        dsp56k_host_interface.cvr = (u_char)((arg & DSP56K_CVR_HV_MASK) |
 389                                                             DSP56K_CVR_HC);
 390                        unlock_kernel();
 391                        break;
 392                default:
 393                        return -EINVAL;
 394                }
 395                return 0;
 396
 397        default:
 398                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 399                return -ENXIO;
 400        }
 401}
 402
 403/* As of 2.1.26 this should be dsp56k_poll,
 404 * but how do I then check device minor number?
 405 * Do I need this function at all???
 406 */
 407#if 0
 408static unsigned int dsp56k_poll(struct file *file, poll_table *wait)
 409{
 410        int dev = iminor(file->f_path.dentry->d_inode) & 0x0f;
 411
 412        switch(dev)
 413        {
 414        case DSP56K_DEV_56001:
 415                /* poll_wait(file, ???, wait); */
 416                return POLLIN | POLLRDNORM | POLLOUT;
 417
 418        default:
 419                printk("DSP56k driver: Unknown minor device: %d\n", dev);
 420                return 0;
 421        }
 422}
 423#endif
 424
 425static int dsp56k_open(struct inode *inode, struct file *file)
 426{
 427        int dev = iminor(inode) & 0x0f;
 428        int ret = 0;
 429
 430        lock_kernel();
 431        switch(dev)
 432        {
 433        case DSP56K_DEV_56001:
 434
 435                if (test_and_set_bit(0, &dsp56k.in_use)) {
 436                        ret = -EBUSY;
 437                        goto out;
 438                }
 439
 440                dsp56k.timeout = TIMEOUT;
 441                dsp56k.maxio = MAXIO;
 442                dsp56k.rx_wsize = dsp56k.tx_wsize = 4; 
 443
 444                DSP56K_TX_INT_OFF;
 445                DSP56K_RX_INT_OFF;
 446
 447                /* Zero host flags */
 448                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF0;
 449                dsp56k_host_interface.icr &= ~DSP56K_ICR_HF1;
 450
 451                break;
 452
 453        default:
 454                ret = -ENODEV;
 455        }
 456out:
 457        unlock_kernel();
 458        return ret;
 459}
 460
 461static int dsp56k_release(struct inode *inode, struct file *file)
 462{
 463        int dev = iminor(inode) & 0x0f;
 464
 465        switch(dev)
 466        {
 467        case DSP56K_DEV_56001:
 468                clear_bit(0, &dsp56k.in_use);
 469                break;
 470        default:
 471                printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 472                return -ENXIO;
 473        }
 474
 475        return 0;
 476}
 477
 478static const struct file_operations dsp56k_fops = {
 479        .owner          = THIS_MODULE,
 480        .read           = dsp56k_read,
 481        .write          = dsp56k_write,
 482        .unlocked_ioctl = dsp56k_ioctl,
 483        .open           = dsp56k_open,
 484        .release        = dsp56k_release,
 485};
 486
 487
 488/****** Init and module functions ******/
 489
 490static char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
 491
 492static int __init dsp56k_init_driver(void)
 493{
 494        int err = 0;
 495
 496        if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
 497                printk("DSP56k driver: Hardware not present\n");
 498                return -ENODEV;
 499        }
 500
 501        if(register_chrdev(DSP56K_MAJOR, "dsp56k", &dsp56k_fops)) {
 502                printk("DSP56k driver: Unable to register driver\n");
 503                return -ENODEV;
 504        }
 505        dsp56k_class = class_create(THIS_MODULE, "dsp56k");
 506        if (IS_ERR(dsp56k_class)) {
 507                err = PTR_ERR(dsp56k_class);
 508                goto out_chrdev;
 509        }
 510        device_create(dsp56k_class, NULL, MKDEV(DSP56K_MAJOR, 0), NULL,
 511                      "dsp56k");
 512
 513        printk(banner);
 514        goto out;
 515
 516out_chrdev:
 517        unregister_chrdev(DSP56K_MAJOR, "dsp56k");
 518out:
 519        return err;
 520}
 521module_init(dsp56k_init_driver);
 522
 523static void __exit dsp56k_cleanup_driver(void)
 524{
 525        device_destroy(dsp56k_class, MKDEV(DSP56K_MAJOR, 0));
 526        class_destroy(dsp56k_class);
 527        unregister_chrdev(DSP56K_MAJOR, "dsp56k");
 528}
 529module_exit(dsp56k_cleanup_driver);
 530
 531MODULE_LICENSE("GPL");
 532MODULE_FIRMWARE("dsp56k/bootstrap.bin");
 533
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.