linux/sound/oss/msnd.c
<<
>>
Prefs
   1/*********************************************************************
   2 *
   3 * msnd.c - Driver Base
   4 *
   5 * Turtle Beach MultiSound Sound Card Driver for Linux
   6 *
   7 * Copyright (C) 1998 Andrew Veliath
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22 *
  23 ********************************************************************/
  24
  25#include <linux/module.h>
  26#include <linux/kernel.h>
  27#include <linux/vmalloc.h>
  28#include <linux/types.h>
  29#include <linux/delay.h>
  30#include <linux/mm.h>
  31#include <linux/init.h>
  32#include <linux/interrupt.h>
  33
  34#include <asm/io.h>
  35#include <asm/uaccess.h>
  36#include <linux/spinlock.h>
  37#include <asm/irq.h>
  38#include "msnd.h"
  39
  40#define LOGNAME                 "msnd"
  41
  42#define MSND_MAX_DEVS           4
  43
  44static multisound_dev_t         *devs[MSND_MAX_DEVS];
  45static int                      num_devs;
  46
  47int msnd_register(multisound_dev_t *dev)
  48{
  49        int i;
  50
  51        for (i = 0; i < MSND_MAX_DEVS; ++i)
  52                if (devs[i] == NULL)
  53                        break;
  54
  55        if (i == MSND_MAX_DEVS)
  56                return -ENOMEM;
  57
  58        devs[i] = dev;
  59        ++num_devs;
  60        return 0;
  61}
  62
  63void msnd_unregister(multisound_dev_t *dev)
  64{
  65        int i;
  66
  67        for (i = 0; i < MSND_MAX_DEVS; ++i)
  68                if (devs[i] == dev)
  69                        break;
  70
  71        if (i == MSND_MAX_DEVS) {
  72                printk(KERN_WARNING LOGNAME ": Unregistering unknown device\n");
  73                return;
  74        }
  75
  76        devs[i] = NULL;
  77        --num_devs;
  78}
  79
  80void msnd_init_queue(void __iomem *base, int start, int size)
  81{
  82        writew(PCTODSP_BASED(start), base + JQS_wStart);
  83        writew(PCTODSP_OFFSET(size) - 1, base + JQS_wSize);
  84        writew(0, base + JQS_wHead);
  85        writew(0, base + JQS_wTail);
  86}
  87
  88void msnd_fifo_init(msnd_fifo *f)
  89{
  90        f->data = NULL;
  91}
  92
  93void msnd_fifo_free(msnd_fifo *f)
  94{
  95        vfree(f->data);
  96        f->data = NULL;
  97}
  98
  99int msnd_fifo_alloc(msnd_fifo *f, size_t n)
 100{
 101        msnd_fifo_free(f);
 102        f->data = vmalloc(n);
 103        f->n = n;
 104        f->tail = 0;
 105        f->head = 0;
 106        f->len = 0;
 107
 108        if (!f->data)
 109                return -ENOMEM;
 110
 111        return 0;
 112}
 113
 114void msnd_fifo_make_empty(msnd_fifo *f)
 115{
 116        f->len = f->tail = f->head = 0;
 117}
 118
 119int msnd_fifo_write_io(msnd_fifo *f, char __iomem *buf, size_t len)
 120{
 121        int count = 0;
 122
 123        while ((count < len) && (f->len != f->n)) {
 124
 125                int nwritten;
 126
 127                if (f->head <= f->tail) {
 128                        nwritten = len - count;
 129                        if (nwritten > f->n - f->tail)
 130                                nwritten = f->n - f->tail;
 131                }
 132                else {
 133                        nwritten = f->head - f->tail;
 134                        if (nwritten > len - count)
 135                                nwritten = len - count;
 136                }
 137
 138                memcpy_fromio(f->data + f->tail, buf, nwritten);
 139
 140                count += nwritten;
 141                buf += nwritten;
 142                f->len += nwritten;
 143                f->tail += nwritten;
 144                f->tail %= f->n;
 145        }
 146
 147        return count;
 148}
 149
 150int msnd_fifo_write(msnd_fifo *f, const char *buf, size_t len)
 151{
 152        int count = 0;
 153
 154        while ((count < len) && (f->len != f->n)) {
 155
 156                int nwritten;
 157
 158                if (f->head <= f->tail) {
 159                        nwritten = len - count;
 160                        if (nwritten > f->n - f->tail)
 161                                nwritten = f->n - f->tail;
 162                }
 163                else {
 164                        nwritten = f->head - f->tail;
 165                        if (nwritten > len - count)
 166                                nwritten = len - count;
 167                }
 168
 169                memcpy(f->data + f->tail, buf, nwritten);
 170
 171                count += nwritten;
 172                buf += nwritten;
 173                f->len += nwritten;
 174                f->tail += nwritten;
 175                f->tail %= f->n;
 176        }
 177
 178        return count;
 179}
 180
 181int msnd_fifo_read_io(msnd_fifo *f, char __iomem *buf, size_t len)
 182{
 183        int count = 0;
 184
 185        while ((count < len) && (f->len > 0)) {
 186
 187                int nread;
 188
 189                if (f->tail <= f->head) {
 190                        nread = len - count;
 191                        if (nread > f->n - f->head)
 192                                nread = f->n - f->head;
 193                }
 194                else {
 195                        nread = f->tail - f->head;
 196                        if (nread > len - count)
 197                                nread = len - count;
 198                }
 199
 200                memcpy_toio(buf, f->data + f->head, nread);
 201
 202                count += nread;
 203                buf += nread;
 204                f->len -= nread;
 205                f->head += nread;
 206                f->head %= f->n;
 207        }
 208
 209        return count;
 210}
 211
 212int msnd_fifo_read(msnd_fifo *f, char *buf, size_t len)
 213{
 214        int count = 0;
 215
 216        while ((count < len) && (f->len > 0)) {
 217
 218                int nread;
 219
 220                if (f->tail <= f->head) {
 221                        nread = len - count;
 222                        if (nread > f->n - f->head)
 223                                nread = f->n - f->head;
 224                }
 225                else {
 226                        nread = f->tail - f->head;
 227                        if (nread > len - count)
 228                                nread = len - count;
 229                }
 230
 231                memcpy(buf, f->data + f->head, nread);
 232
 233                count += nread;
 234                buf += nread;
 235                f->len -= nread;
 236                f->head += nread;
 237                f->head %= f->n;
 238        }
 239
 240        return count;
 241}
 242
 243static int msnd_wait_TXDE(multisound_dev_t *dev)
 244{
 245        register unsigned int io = dev->io;
 246        register int timeout = 1000;
 247    
 248        while(timeout-- > 0)
 249                if (msnd_inb(io + HP_ISR) & HPISR_TXDE)
 250                        return 0;
 251
 252        return -EIO;
 253}
 254
 255static int msnd_wait_HC0(multisound_dev_t *dev)
 256{
 257        register unsigned int io = dev->io;
 258        register int timeout = 1000;
 259
 260        while(timeout-- > 0)
 261                if (!(msnd_inb(io + HP_CVR) & HPCVR_HC))
 262                        return 0;
 263
 264        return -EIO;
 265}
 266
 267int msnd_send_dsp_cmd(multisound_dev_t *dev, BYTE cmd)
 268{
 269        unsigned long flags;
 270
 271        spin_lock_irqsave(&dev->lock, flags);
 272        if (msnd_wait_HC0(dev) == 0) {
 273                msnd_outb(cmd, dev->io + HP_CVR);
 274                spin_unlock_irqrestore(&dev->lock, flags);
 275                return 0;
 276        }
 277        spin_unlock_irqrestore(&dev->lock, flags);
 278
 279        printk(KERN_DEBUG LOGNAME ": Send DSP command timeout\n");
 280
 281        return -EIO;
 282}
 283
 284int msnd_send_word(multisound_dev_t *dev, unsigned char high,
 285                   unsigned char mid, unsigned char low)
 286{
 287        register unsigned int io = dev->io;
 288
 289        if (msnd_wait_TXDE(dev) == 0) {
 290                msnd_outb(high, io + HP_TXH);
 291                msnd_outb(mid, io + HP_TXM);
 292                msnd_outb(low, io + HP_TXL);
 293                return 0;
 294        }
 295
 296        printk(KERN_DEBUG LOGNAME ": Send host word timeout\n");
 297
 298        return -EIO;
 299}
 300
 301int msnd_upload_host(multisound_dev_t *dev, char *bin, int len)
 302{
 303        int i;
 304
 305        if (len % 3 != 0) {
 306                printk(KERN_WARNING LOGNAME ": Upload host data not multiple of 3!\n");         
 307                return -EINVAL;
 308        }
 309
 310        for (i = 0; i < len; i += 3)
 311                if (msnd_send_word(dev, bin[i], bin[i + 1], bin[i + 2]) != 0)
 312                        return -EIO;
 313
 314        msnd_inb(dev->io + HP_RXL);
 315        msnd_inb(dev->io + HP_CVR);
 316
 317        return 0;
 318}
 319
 320int msnd_enable_irq(multisound_dev_t *dev)
 321{
 322        unsigned long flags;
 323
 324        if (dev->irq_ref++)
 325                return 0;
 326
 327        printk(KERN_DEBUG LOGNAME ": Enabling IRQ\n");
 328
 329        spin_lock_irqsave(&dev->lock, flags);
 330        if (msnd_wait_TXDE(dev) == 0) {
 331                msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_TREQ, dev->io + HP_ICR);
 332                if (dev->type == msndClassic)
 333                        msnd_outb(dev->irqid, dev->io + HP_IRQM);
 334                msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_TREQ, dev->io + HP_ICR);
 335                msnd_outb(msnd_inb(dev->io + HP_ICR) | HPICR_RREQ, dev->io + HP_ICR);
 336                enable_irq(dev->irq);
 337                msnd_init_queue(dev->DSPQ, dev->dspq_data_buff, dev->dspq_buff_size);
 338                spin_unlock_irqrestore(&dev->lock, flags);
 339                return 0;
 340        }
 341        spin_unlock_irqrestore(&dev->lock, flags);
 342
 343        printk(KERN_DEBUG LOGNAME ": Enable IRQ failed\n");
 344
 345        return -EIO;
 346}
 347
 348int msnd_disable_irq(multisound_dev_t *dev)
 349{
 350        unsigned long flags;
 351
 352        if (--dev->irq_ref > 0)
 353                return 0;
 354
 355        if (dev->irq_ref < 0)
 356                printk(KERN_DEBUG LOGNAME ": IRQ ref count is %d\n", dev->irq_ref);
 357
 358        printk(KERN_DEBUG LOGNAME ": Disabling IRQ\n");
 359
 360        spin_lock_irqsave(&dev->lock, flags);
 361        if (msnd_wait_TXDE(dev) == 0) {
 362                msnd_outb(msnd_inb(dev->io + HP_ICR) & ~HPICR_RREQ, dev->io + HP_ICR);
 363                if (dev->type == msndClassic)
 364                        msnd_outb(HPIRQ_NONE, dev->io + HP_IRQM);
 365                disable_irq(dev->irq);
 366                spin_unlock_irqrestore(&dev->lock, flags);
 367                return 0;
 368        }
 369        spin_unlock_irqrestore(&dev->lock, flags);
 370
 371        printk(KERN_DEBUG LOGNAME ": Disable IRQ failed\n");
 372
 373        return -EIO;
 374}
 375
 376#ifndef LINUX20
 377EXPORT_SYMBOL(msnd_register);
 378EXPORT_SYMBOL(msnd_unregister);
 379
 380EXPORT_SYMBOL(msnd_init_queue);
 381
 382EXPORT_SYMBOL(msnd_fifo_init);
 383EXPORT_SYMBOL(msnd_fifo_free);
 384EXPORT_SYMBOL(msnd_fifo_alloc);
 385EXPORT_SYMBOL(msnd_fifo_make_empty);
 386EXPORT_SYMBOL(msnd_fifo_write_io);
 387EXPORT_SYMBOL(msnd_fifo_read_io);
 388EXPORT_SYMBOL(msnd_fifo_write);
 389EXPORT_SYMBOL(msnd_fifo_read);
 390
 391EXPORT_SYMBOL(msnd_send_dsp_cmd);
 392EXPORT_SYMBOL(msnd_send_word);
 393EXPORT_SYMBOL(msnd_upload_host);
 394
 395EXPORT_SYMBOL(msnd_enable_irq);
 396EXPORT_SYMBOL(msnd_disable_irq);
 397#endif
 398
 399#ifdef MODULE
 400MODULE_AUTHOR                           ("Andrew Veliath <andrewtv@usa.net>");
 401MODULE_DESCRIPTION                      ("Turtle Beach MultiSound Driver Base");
 402MODULE_LICENSE("GPL");
 403
 404
 405int init_module(void)
 406{
 407        return 0;
 408}
 409
 410void cleanup_module(void)
 411{
 412}
 413#endif
 414