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