/arch/mips/lasat/picvue_proc.c

http://github.com/mirrors/linux · C · 208 lines · 161 code · 40 blank · 7 comment · 27 complexity · 263f831c90afe9ebbdc5324766c42f6f MD5 · raw file

  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Picvue PVC160206 display driver
  4. *
  5. * Brian Murphy <brian.murphy@eicon.com>
  6. *
  7. */
  8. #include <linux/bug.h>
  9. #include <linux/kernel.h>
  10. #include <linux/module.h>
  11. #include <linux/init.h>
  12. #include <linux/errno.h>
  13. #include <linux/proc_fs.h>
  14. #include <linux/seq_file.h>
  15. #include <linux/interrupt.h>
  16. #include <linux/timer.h>
  17. #include <linux/mutex.h>
  18. #include <linux/uaccess.h>
  19. #include "picvue.h"
  20. static DEFINE_MUTEX(pvc_mutex);
  21. static char pvc_lines[PVC_NLINES][PVC_LINELEN+1];
  22. static int pvc_linedata[PVC_NLINES];
  23. static char *pvc_linename[PVC_NLINES] = {"line1", "line2"};
  24. #define DISPLAY_DIR_NAME "display"
  25. static int scroll_dir, scroll_interval;
  26. static struct timer_list timer;
  27. static void pvc_display(unsigned long data)
  28. {
  29. int i;
  30. pvc_clear();
  31. for (i = 0; i < PVC_NLINES; i++)
  32. pvc_write_string(pvc_lines[i], 0, i);
  33. }
  34. static DECLARE_TASKLET(pvc_display_tasklet, &pvc_display, 0);
  35. static int pvc_line_proc_show(struct seq_file *m, void *v)
  36. {
  37. int lineno = *(int *)m->private;
  38. if (lineno < 0 || lineno >= PVC_NLINES) {
  39. printk(KERN_WARNING "proc_read_line: invalid lineno %d\n", lineno);
  40. return 0;
  41. }
  42. mutex_lock(&pvc_mutex);
  43. seq_printf(m, "%s\n", pvc_lines[lineno]);
  44. mutex_unlock(&pvc_mutex);
  45. return 0;
  46. }
  47. static int pvc_line_proc_open(struct inode *inode, struct file *file)
  48. {
  49. return single_open(file, pvc_line_proc_show, PDE_DATA(inode));
  50. }
  51. static ssize_t pvc_line_proc_write(struct file *file, const char __user *buf,
  52. size_t count, loff_t *pos)
  53. {
  54. int lineno = *(int *)PDE_DATA(file_inode(file));
  55. char kbuf[PVC_LINELEN];
  56. size_t len;
  57. BUG_ON(lineno < 0 || lineno >= PVC_NLINES);
  58. len = min(count, sizeof(kbuf) - 1);
  59. if (copy_from_user(kbuf, buf, len))
  60. return -EFAULT;
  61. kbuf[len] = '\0';
  62. if (len > 0 && kbuf[len - 1] == '\n')
  63. len--;
  64. mutex_lock(&pvc_mutex);
  65. strncpy(pvc_lines[lineno], kbuf, len);
  66. pvc_lines[lineno][len] = '\0';
  67. mutex_unlock(&pvc_mutex);
  68. tasklet_schedule(&pvc_display_tasklet);
  69. return count;
  70. }
  71. static const struct proc_ops pvc_line_proc_ops = {
  72. .proc_open = pvc_line_proc_open,
  73. .proc_read = seq_read,
  74. .proc_lseek = seq_lseek,
  75. .proc_release = single_release,
  76. .proc_write = pvc_line_proc_write,
  77. };
  78. static ssize_t pvc_scroll_proc_write(struct file *file, const char __user *buf,
  79. size_t count, loff_t *pos)
  80. {
  81. char kbuf[42];
  82. size_t len;
  83. int cmd;
  84. len = min(count, sizeof(kbuf) - 1);
  85. if (copy_from_user(kbuf, buf, len))
  86. return -EFAULT;
  87. kbuf[len] = '\0';
  88. cmd = simple_strtol(kbuf, NULL, 10);
  89. mutex_lock(&pvc_mutex);
  90. if (scroll_interval != 0)
  91. del_timer(&timer);
  92. if (cmd == 0) {
  93. scroll_dir = 0;
  94. scroll_interval = 0;
  95. } else {
  96. if (cmd < 0) {
  97. scroll_dir = -1;
  98. scroll_interval = -cmd;
  99. } else {
  100. scroll_dir = 1;
  101. scroll_interval = cmd;
  102. }
  103. add_timer(&timer);
  104. }
  105. mutex_unlock(&pvc_mutex);
  106. return count;
  107. }
  108. static int pvc_scroll_proc_show(struct seq_file *m, void *v)
  109. {
  110. mutex_lock(&pvc_mutex);
  111. seq_printf(m, "%d\n", scroll_dir * scroll_interval);
  112. mutex_unlock(&pvc_mutex);
  113. return 0;
  114. }
  115. static int pvc_scroll_proc_open(struct inode *inode, struct file *file)
  116. {
  117. return single_open(file, pvc_scroll_proc_show, NULL);
  118. }
  119. static const struct proc_ops pvc_scroll_proc_ops = {
  120. .proc_open = pvc_scroll_proc_open,
  121. .proc_read = seq_read,
  122. .proc_lseek = seq_lseek,
  123. .proc_release = single_release,
  124. .proc_write = pvc_scroll_proc_write,
  125. };
  126. void pvc_proc_timerfunc(struct timer_list *unused)
  127. {
  128. if (scroll_dir < 0)
  129. pvc_move(DISPLAY|RIGHT);
  130. else if (scroll_dir > 0)
  131. pvc_move(DISPLAY|LEFT);
  132. timer.expires = jiffies + scroll_interval;
  133. add_timer(&timer);
  134. }
  135. static void pvc_proc_cleanup(void)
  136. {
  137. remove_proc_subtree(DISPLAY_DIR_NAME, NULL);
  138. del_timer_sync(&timer);
  139. }
  140. static int __init pvc_proc_init(void)
  141. {
  142. struct proc_dir_entry *dir, *proc_entry;
  143. int i;
  144. dir = proc_mkdir(DISPLAY_DIR_NAME, NULL);
  145. if (dir == NULL)
  146. goto error;
  147. for (i = 0; i < PVC_NLINES; i++) {
  148. strcpy(pvc_lines[i], "");
  149. pvc_linedata[i] = i;
  150. }
  151. for (i = 0; i < PVC_NLINES; i++) {
  152. proc_entry = proc_create_data(pvc_linename[i], 0644, dir,
  153. &pvc_line_proc_ops, &pvc_linedata[i]);
  154. if (proc_entry == NULL)
  155. goto error;
  156. }
  157. proc_entry = proc_create("scroll", 0644, dir, &pvc_scroll_proc_ops);
  158. if (proc_entry == NULL)
  159. goto error;
  160. timer_setup(&timer, pvc_proc_timerfunc, 0);
  161. return 0;
  162. error:
  163. pvc_proc_cleanup();
  164. return -ENOMEM;
  165. }
  166. module_init(pvc_proc_init);
  167. module_exit(pvc_proc_cleanup);
  168. MODULE_LICENSE("GPL");