1/* 2 * Picvue PVC160206 display driver 3 * 4 * Brian Murphy <brian.murphy@eicon.com> 5 * 6 */ 7#include <linux/bug.h> 8#include <linux/kernel.h> 9#include <linux/module.h> 10#include <linux/init.h> 11#include <linux/errno.h> 12 13#include <linux/proc_fs.h> 14#include <linux/seq_file.h> 15#include <linux/interrupt.h> 16 17#include <linux/timer.h> 18#include <linux/mutex.h> 19 20#include "picvue.h" 21 22static DEFINE_MUTEX(pvc_mutex); 23static char pvc_lines[PVC_NLINES][PVC_LINELEN+1]; 24static int pvc_linedata[PVC_NLINES]; 25static struct proc_dir_entry *pvc_display_dir; 26static char *pvc_linename[PVC_NLINES] = {"line1", "line2"}; 27#define DISPLAY_DIR_NAME "display" 28static int scroll_dir, scroll_interval; 29 30static struct timer_list timer; 31 32static void pvc_display(unsigned long data) 33{ 34 int i; 35 36 pvc_clear(); 37 for (i = 0; i < PVC_NLINES; i++) 38 pvc_write_string(pvc_lines[i], 0, i); 39} 40 41static DECLARE_TASKLET(pvc_display_tasklet, &pvc_display, 0); 42 43static int pvc_line_proc_show(struct seq_file *m, void *v) 44{ 45 int lineno = *(int *)m->private; 46 47 if (lineno < 0 || lineno > PVC_NLINES) { 48 printk(KERN_WARNING "proc_read_line: invalid lineno %d\n", lineno); 49 return 0; 50 } 51 52 mutex_lock(&pvc_mutex); 53 seq_printf(m, "%s\n", pvc_lines[lineno]); 54 mutex_unlock(&pvc_mutex); 55 56 return 0; 57} 58 59static int pvc_line_proc_open(struct inode *inode, struct file *file) 60{ 61 return single_open(file, pvc_line_proc_show, PDE(inode)->data); 62} 63 64static ssize_t pvc_line_proc_write(struct file *file, const char __user *buf, 65 size_t count, loff_t *pos) 66{ 67 int lineno = *(int *)PDE(file->f_path.dentry->d_inode)->data; 68 char kbuf[PVC_LINELEN]; 69 size_t len; 70 71 BUG_ON(lineno < 0 || lineno > PVC_NLINES); 72 73 len = min(count, sizeof(kbuf) - 1); 74 if (copy_from_user(kbuf, buf, len)) 75 return -EFAULT; 76 kbuf[len] = '\0'; 77 78 if (len > 0 && kbuf[len - 1] == '\n') 79 len--; 80 81 mutex_lock(&pvc_mutex); 82 strncpy(pvc_lines[lineno], kbuf, len); 83 pvc_lines[lineno][len] = '\0'; 84 mutex_unlock(&pvc_mutex); 85 86 tasklet_schedule(&pvc_display_tasklet); 87 88 return count; 89} 90 91static const struct file_operations pvc_line_proc_fops = { 92 .owner = THIS_MODULE, 93 .open = pvc_line_proc_open, 94 .read = seq_read, 95 .llseek = seq_lseek, 96 .release = single_release, 97 .write = pvc_line_proc_write, 98}; 99 100static ssize_t pvc_scroll_proc_write(struct file *file, const char __user *buf, 101 size_t count, loff_t *pos) 102{ 103 char kbuf[42]; 104 size_t len; 105 int cmd; 106 107 len = min(count, sizeof(kbuf) - 1); 108 if (copy_from_user(kbuf, buf, len)) 109 return -EFAULT; 110 kbuf[len] = '\0'; 111 112 cmd = simple_strtol(kbuf, NULL, 10); 113 114 mutex_lock(&pvc_mutex); 115 if (scroll_interval != 0) 116 del_timer(&timer); 117 118 if (cmd == 0) { 119 scroll_dir = 0; 120 scroll_interval = 0; 121 } else { 122 if (cmd < 0) { 123 scroll_dir = -1; 124 scroll_interval = -cmd; 125 } else { 126 scroll_dir = 1; 127 scroll_interval = cmd; 128 } 129 add_timer(&timer); 130 } 131 mutex_unlock(&pvc_mutex); 132 133 return count; 134} 135 136static int pvc_scroll_proc_show(struct seq_file *m, void *v) 137{ 138 mutex_lock(&pvc_mutex); 139 seq_printf(m, "%d\n", scroll_dir * scroll_interval); 140 mutex_unlock(&pvc_mutex); 141 142 return 0; 143} 144 145static int pvc_scroll_proc_open(struct inode *inode, struct file *file) 146{ 147 return single_open(file, pvc_scroll_proc_show, NULL); 148} 149 150static const struct file_operations pvc_scroll_proc_fops = { 151 .owner = THIS_MODULE, 152 .open = pvc_scroll_proc_open, 153 .read = seq_read, 154 .llseek = seq_lseek, 155 .release = single_release, 156 .write = pvc_scroll_proc_write, 157}; 158 159void pvc_proc_timerfunc(unsigned long data) 160{ 161 if (scroll_dir < 0) 162 pvc_move(DISPLAY|RIGHT); 163 else if (scroll_dir > 0) 164 pvc_move(DISPLAY|LEFT); 165 166 timer.expires = jiffies + scroll_interval; 167 add_timer(&timer); 168} 169 170static void pvc_proc_cleanup(void) 171{ 172 int i; 173 for (i = 0; i < PVC_NLINES; i++) 174 remove_proc_entry(pvc_linename[i], pvc_display_dir); 175 remove_proc_entry("scroll", pvc_display_dir); 176 remove_proc_entry(DISPLAY_DIR_NAME, NULL); 177 178 del_timer(&timer); 179} 180 181static int __init pvc_proc_init(void) 182{ 183 struct proc_dir_entry *proc_entry; 184 int i; 185 186 pvc_display_dir = proc_mkdir(DISPLAY_DIR_NAME, NULL); 187 if (pvc_display_dir == NULL) 188 goto error; 189 190 for (i = 0; i < PVC_NLINES; i++) { 191 strcpy(pvc_lines[i], ""); 192 pvc_linedata[i] = i; 193 } 194 for (i = 0; i < PVC_NLINES; i++) { 195 proc_entry = proc_create_data(pvc_linename[i], 0644, pvc_display_dir, 196 &pvc_line_proc_fops, &pvc_linedata[i]); 197 if (proc_entry == NULL) 198 goto error; 199 } 200 proc_entry = proc_create("scroll", 0644, pvc_display_dir, 201 &pvc_scroll_proc_fops); 202 if (proc_entry == NULL) 203 goto error; 204 205 init_timer(&timer); 206 timer.function = pvc_proc_timerfunc; 207 208 return 0; 209error: 210 pvc_proc_cleanup(); 211 return -ENOMEM; 212} 213 214module_init(pvc_proc_init); 215module_exit(pvc_proc_cleanup); 216MODULE_LICENSE("GPL"); 217