msnd.c 8.2 KB

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