rtlx.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /*
  2. * Copyright (C) 2005 MIPS Technologies, Inc. All rights reserved.
  3. * Copyright (C) 2005, 06 Ralf Baechle (ralf@linux-mips.org)
  4. *
  5. * This program is free software; you can distribute it and/or modify it
  6. * under the terms of the GNU General Public License (Version 2) as
  7. * published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  12. * for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along
  15. * with this program; if not, write to the Free Software Foundation, Inc.,
  16. * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
  17. *
  18. */
  19. #include <linux/kernel.h>
  20. #include <linux/module.h>
  21. #include <linux/fs.h>
  22. #include <linux/init.h>
  23. #include <linux/interrupt.h>
  24. #include <linux/irq.h>
  25. #include <linux/poll.h>
  26. #include <linux/sched.h>
  27. #include <linux/wait.h>
  28. #include <asm/mipsmtregs.h>
  29. #include <asm/bitops.h>
  30. #include <asm/cpu.h>
  31. #include <asm/processor.h>
  32. #include <asm/rtlx.h>
  33. #include <asm/uaccess.h>
  34. #define RTLX_TARG_VPE 1
  35. static struct rtlx_info *rtlx;
  36. static int major;
  37. static char module_name[] = "rtlx";
  38. static struct irqaction irq;
  39. static int irq_num;
  40. static inline int spacefree(int read, int write, int size)
  41. {
  42. if (read == write) {
  43. /*
  44. * never fill the buffer completely, so indexes are always
  45. * equal if empty and only empty, or !equal if data available
  46. */
  47. return size - 1;
  48. }
  49. return ((read + size - write) % size) - 1;
  50. }
  51. static struct chan_waitqueues {
  52. wait_queue_head_t rt_queue;
  53. wait_queue_head_t lx_queue;
  54. } channel_wqs[RTLX_CHANNELS];
  55. extern void *vpe_get_shared(int index);
  56. static void rtlx_dispatch(struct pt_regs *regs)
  57. {
  58. do_IRQ(MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ, regs);
  59. }
  60. static irqreturn_t rtlx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  61. {
  62. int i;
  63. for (i = 0; i < RTLX_CHANNELS; i++) {
  64. struct rtlx_channel *chan = &rtlx->channel[i];
  65. if (chan->lx_read != chan->lx_write)
  66. wake_up_interruptible(&channel_wqs[i].lx_queue);
  67. }
  68. return IRQ_HANDLED;
  69. }
  70. /* call when we have the address of the shared structure from the SP side. */
  71. static int rtlx_init(struct rtlx_info *rtlxi)
  72. {
  73. int i;
  74. if (rtlxi->id != RTLX_ID) {
  75. printk(KERN_WARNING "no valid RTLX id at 0x%p\n", rtlxi);
  76. return -ENOEXEC;
  77. }
  78. /* initialise the wait queues */
  79. for (i = 0; i < RTLX_CHANNELS; i++) {
  80. init_waitqueue_head(&channel_wqs[i].rt_queue);
  81. init_waitqueue_head(&channel_wqs[i].lx_queue);
  82. }
  83. /* set up for interrupt handling */
  84. memset(&irq, 0, sizeof(struct irqaction));
  85. if (cpu_has_vint)
  86. set_vi_handler(MIPS_CPU_RTLX_IRQ, rtlx_dispatch);
  87. irq_num = MIPSCPU_INT_BASE + MIPS_CPU_RTLX_IRQ;
  88. irq.handler = rtlx_interrupt;
  89. irq.flags = SA_INTERRUPT;
  90. irq.name = "RTLX";
  91. irq.dev_id = rtlx;
  92. setup_irq(irq_num, &irq);
  93. rtlx = rtlxi;
  94. return 0;
  95. }
  96. /* only allow one open process at a time to open each channel */
  97. static int rtlx_open(struct inode *inode, struct file *filp)
  98. {
  99. int minor, ret;
  100. struct rtlx_channel *chan;
  101. /* assume only 1 device at the mo. */
  102. minor = MINOR(inode->i_rdev);
  103. if (rtlx == NULL) {
  104. struct rtlx_info **p;
  105. if( (p = vpe_get_shared(RTLX_TARG_VPE)) == NULL) {
  106. printk(KERN_ERR "vpe_get_shared is NULL. "
  107. "Has an SP program been loaded?\n");
  108. return -EFAULT;
  109. }
  110. if (*p == NULL) {
  111. printk(KERN_ERR "vpe_shared %p %p\n", p, *p);
  112. return -EFAULT;
  113. }
  114. if ((ret = rtlx_init(*p)) < 0)
  115. return ret;
  116. }
  117. chan = &rtlx->channel[minor];
  118. if (test_and_set_bit(RTLX_STATE_OPENED, &chan->lx_state))
  119. return -EBUSY;
  120. return 0;
  121. }
  122. static int rtlx_release(struct inode *inode, struct file *filp)
  123. {
  124. int minor = MINOR(inode->i_rdev);
  125. clear_bit(RTLX_STATE_OPENED, &rtlx->channel[minor].lx_state);
  126. smp_mb__after_clear_bit();
  127. return 0;
  128. }
  129. static unsigned int rtlx_poll(struct file *file, poll_table * wait)
  130. {
  131. int minor;
  132. unsigned int mask = 0;
  133. struct rtlx_channel *chan;
  134. minor = MINOR(file->f_dentry->d_inode->i_rdev);
  135. chan = &rtlx->channel[minor];
  136. poll_wait(file, &channel_wqs[minor].rt_queue, wait);
  137. poll_wait(file, &channel_wqs[minor].lx_queue, wait);
  138. /* data available to read? */
  139. if (chan->lx_read != chan->lx_write)
  140. mask |= POLLIN | POLLRDNORM;
  141. /* space to write */
  142. if (spacefree(chan->rt_read, chan->rt_write, chan->buffer_size))
  143. mask |= POLLOUT | POLLWRNORM;
  144. return mask;
  145. }
  146. static ssize_t rtlx_read(struct file *file, char __user * buffer, size_t count,
  147. loff_t * ppos)
  148. {
  149. unsigned long failed;
  150. size_t fl = 0L;
  151. int minor;
  152. struct rtlx_channel *lx;
  153. DECLARE_WAITQUEUE(wait, current);
  154. minor = MINOR(file->f_dentry->d_inode->i_rdev);
  155. lx = &rtlx->channel[minor];
  156. /* data available? */
  157. if (lx->lx_write == lx->lx_read) {
  158. if (file->f_flags & O_NONBLOCK)
  159. return 0; /* -EAGAIN makes cat whinge */
  160. /* go to sleep */
  161. add_wait_queue(&channel_wqs[minor].lx_queue, &wait);
  162. set_current_state(TASK_INTERRUPTIBLE);
  163. while (lx->lx_write == lx->lx_read)
  164. schedule();
  165. set_current_state(TASK_RUNNING);
  166. remove_wait_queue(&channel_wqs[minor].lx_queue, &wait);
  167. /* back running */
  168. }
  169. /* find out how much in total */
  170. count = min(count,
  171. (size_t)(lx->lx_write + lx->buffer_size - lx->lx_read) % lx->buffer_size);
  172. /* then how much from the read pointer onwards */
  173. fl = min(count, (size_t)lx->buffer_size - lx->lx_read);
  174. failed = copy_to_user (buffer, &lx->lx_buffer[lx->lx_read], fl);
  175. if (failed) {
  176. count = fl - failed;
  177. goto out;
  178. }
  179. /* and if there is anything left at the beginning of the buffer */
  180. if (count - fl) {
  181. failed = copy_to_user (buffer + fl, lx->lx_buffer, count - fl);
  182. if (failed) {
  183. count -= failed;
  184. goto out;
  185. }
  186. }
  187. out:
  188. /* update the index */
  189. lx->lx_read += count;
  190. lx->lx_read %= lx->buffer_size;
  191. return count;
  192. }
  193. static ssize_t rtlx_write(struct file *file, const char __user * buffer,
  194. size_t count, loff_t * ppos)
  195. {
  196. unsigned long failed;
  197. int minor;
  198. struct rtlx_channel *rt;
  199. size_t fl;
  200. DECLARE_WAITQUEUE(wait, current);
  201. minor = MINOR(file->f_dentry->d_inode->i_rdev);
  202. rt = &rtlx->channel[minor];
  203. /* any space left... */
  204. if (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size)) {
  205. if (file->f_flags & O_NONBLOCK)
  206. return -EAGAIN;
  207. add_wait_queue(&channel_wqs[minor].rt_queue, &wait);
  208. set_current_state(TASK_INTERRUPTIBLE);
  209. while (!spacefree(rt->rt_read, rt->rt_write, rt->buffer_size))
  210. schedule();
  211. set_current_state(TASK_RUNNING);
  212. remove_wait_queue(&channel_wqs[minor].rt_queue, &wait);
  213. }
  214. /* total number of bytes to copy */
  215. count = min(count, (size_t)spacefree(rt->rt_read, rt->rt_write, rt->buffer_size) );
  216. /* first bit from write pointer to the end of the buffer, or count */
  217. fl = min(count, (size_t) rt->buffer_size - rt->rt_write);
  218. failed = copy_from_user(&rt->rt_buffer[rt->rt_write], buffer, fl);
  219. if (failed) {
  220. count = fl - failed;
  221. goto out;
  222. }
  223. /* if there's any left copy to the beginning of the buffer */
  224. if (count - fl) {
  225. failed = copy_from_user(rt->rt_buffer, buffer + fl, count - fl);
  226. if (failed) {
  227. count -= failed;
  228. goto out;
  229. }
  230. }
  231. out:
  232. rt->rt_write += count;
  233. rt->rt_write %= rt->buffer_size;
  234. return count;
  235. }
  236. static struct file_operations rtlx_fops = {
  237. .owner = THIS_MODULE,
  238. .open = rtlx_open,
  239. .release = rtlx_release,
  240. .write = rtlx_write,
  241. .read = rtlx_read,
  242. .poll = rtlx_poll
  243. };
  244. static char register_chrdev_failed[] __initdata =
  245. KERN_ERR "rtlx_module_init: unable to register device\n";
  246. static int __init rtlx_module_init(void)
  247. {
  248. major = register_chrdev(0, module_name, &rtlx_fops);
  249. if (major < 0) {
  250. printk(register_chrdev_failed);
  251. return major;
  252. }
  253. return 0;
  254. }
  255. static void __exit rtlx_module_exit(void)
  256. {
  257. unregister_chrdev(major, module_name);
  258. }
  259. module_init(rtlx_module_init);
  260. module_exit(rtlx_module_exit);
  261. MODULE_DESCRIPTION("MIPS RTLX");
  262. MODULE_AUTHOR("Elizabeth Clarke, MIPS Technologies, Inc.");
  263. MODULE_LICENSE("GPL");