rtlx.c 7.7 KB

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