diff options
Diffstat (limited to 'arch/mips/lasat/picvue_proc.c')
-rw-r--r-- | arch/mips/lasat/picvue_proc.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/arch/mips/lasat/picvue_proc.c b/arch/mips/lasat/picvue_proc.c new file mode 100644 index 000000000..5d89e1ec5 --- /dev/null +++ b/arch/mips/lasat/picvue_proc.c @@ -0,0 +1,210 @@ +/* + * Picvue PVC160206 display driver + * + * Brian Murphy <brian.murphy@eicon.com> + * + */ +#include <linux/bug.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/errno.h> + +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> + +#include <linux/timer.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> + +#include "picvue.h" + +static DEFINE_MUTEX(pvc_mutex); +static char pvc_lines[PVC_NLINES][PVC_LINELEN+1]; +static int pvc_linedata[PVC_NLINES]; +static char *pvc_linename[PVC_NLINES] = {"line1", "line2"}; +#define DISPLAY_DIR_NAME "display" +static int scroll_dir, scroll_interval; + +static struct timer_list timer; + +static void pvc_display(unsigned long data) +{ + int i; + + pvc_clear(); + for (i = 0; i < PVC_NLINES; i++) + pvc_write_string(pvc_lines[i], 0, i); +} + +static DECLARE_TASKLET(pvc_display_tasklet, &pvc_display, 0); + +static int pvc_line_proc_show(struct seq_file *m, void *v) +{ + int lineno = *(int *)m->private; + + if (lineno < 0 || lineno >= PVC_NLINES) { + printk(KERN_WARNING "proc_read_line: invalid lineno %d\n", lineno); + return 0; + } + + mutex_lock(&pvc_mutex); + seq_printf(m, "%s\n", pvc_lines[lineno]); + mutex_unlock(&pvc_mutex); + + return 0; +} + +static int pvc_line_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pvc_line_proc_show, PDE_DATA(inode)); +} + +static ssize_t pvc_line_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + int lineno = *(int *)PDE_DATA(file_inode(file)); + char kbuf[PVC_LINELEN]; + size_t len; + + BUG_ON(lineno < 0 || lineno >= PVC_NLINES); + + len = min(count, sizeof(kbuf) - 1); + if (copy_from_user(kbuf, buf, len)) + return -EFAULT; + kbuf[len] = '\0'; + + if (len > 0 && kbuf[len - 1] == '\n') + len--; + + mutex_lock(&pvc_mutex); + strncpy(pvc_lines[lineno], kbuf, len); + pvc_lines[lineno][len] = '\0'; + mutex_unlock(&pvc_mutex); + + tasklet_schedule(&pvc_display_tasklet); + + return count; +} + +static const struct file_operations pvc_line_proc_fops = { + .owner = THIS_MODULE, + .open = pvc_line_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = pvc_line_proc_write, +}; + +static ssize_t pvc_scroll_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + char kbuf[42]; + size_t len; + int cmd; + + len = min(count, sizeof(kbuf) - 1); + if (copy_from_user(kbuf, buf, len)) + return -EFAULT; + kbuf[len] = '\0'; + + cmd = simple_strtol(kbuf, NULL, 10); + + mutex_lock(&pvc_mutex); + if (scroll_interval != 0) + del_timer(&timer); + + if (cmd == 0) { + scroll_dir = 0; + scroll_interval = 0; + } else { + if (cmd < 0) { + scroll_dir = -1; + scroll_interval = -cmd; + } else { + scroll_dir = 1; + scroll_interval = cmd; + } + add_timer(&timer); + } + mutex_unlock(&pvc_mutex); + + return count; +} + +static int pvc_scroll_proc_show(struct seq_file *m, void *v) +{ + mutex_lock(&pvc_mutex); + seq_printf(m, "%d\n", scroll_dir * scroll_interval); + mutex_unlock(&pvc_mutex); + + return 0; +} + +static int pvc_scroll_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, pvc_scroll_proc_show, NULL); +} + +static const struct file_operations pvc_scroll_proc_fops = { + .owner = THIS_MODULE, + .open = pvc_scroll_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = pvc_scroll_proc_write, +}; + +void pvc_proc_timerfunc(struct timer_list *unused) +{ + if (scroll_dir < 0) + pvc_move(DISPLAY|RIGHT); + else if (scroll_dir > 0) + pvc_move(DISPLAY|LEFT); + + timer.expires = jiffies + scroll_interval; + add_timer(&timer); +} + +static void pvc_proc_cleanup(void) +{ + remove_proc_subtree(DISPLAY_DIR_NAME, NULL); + del_timer_sync(&timer); +} + +static int __init pvc_proc_init(void) +{ + struct proc_dir_entry *dir, *proc_entry; + int i; + + dir = proc_mkdir(DISPLAY_DIR_NAME, NULL); + if (dir == NULL) + goto error; + + for (i = 0; i < PVC_NLINES; i++) { + strcpy(pvc_lines[i], ""); + pvc_linedata[i] = i; + } + for (i = 0; i < PVC_NLINES; i++) { + proc_entry = proc_create_data(pvc_linename[i], 0644, dir, + &pvc_line_proc_fops, &pvc_linedata[i]); + if (proc_entry == NULL) + goto error; + } + proc_entry = proc_create("scroll", 0644, dir, + &pvc_scroll_proc_fops); + if (proc_entry == NULL) + goto error; + + timer_setup(&timer, pvc_proc_timerfunc, 0); + + return 0; +error: + pvc_proc_cleanup(); + return -ENOMEM; +} + +module_init(pvc_proc_init); +module_exit(pvc_proc_cleanup); +MODULE_LICENSE("GPL"); |