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