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