blob: 0bb6037afba3622ae0792842d3ceadae97f2c773 [file] [log] [blame]
Brian Murphy1f21d2b2007-08-21 22:34:16 +02001/*
2 * Picvue PVC160206 display driver
3 *
4 * Brian Murphy <brian.murphy@eicon.com>
5 *
6 */
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/init.h>
10#include <linux/errno.h>
11
12#include <linux/proc_fs.h>
13#include <linux/interrupt.h>
14
15#include <linux/timer.h>
Daniel Walker8a39c522008-01-10 20:53:21 -080016#include <linux/mutex.h>
Brian Murphy1f21d2b2007-08-21 22:34:16 +020017
18#include "picvue.h"
19
Daniel Walker8a39c522008-01-10 20:53:21 -080020static DEFINE_MUTEX(pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +020021static char pvc_lines[PVC_NLINES][PVC_LINELEN+1];
22static int pvc_linedata[PVC_NLINES];
23static struct proc_dir_entry *pvc_display_dir;
24static char *pvc_linename[PVC_NLINES] = {"line1", "line2"};
25#define DISPLAY_DIR_NAME "display"
26static int scroll_dir, scroll_interval;
27
28static struct timer_list timer;
29
30static void pvc_display(unsigned long data)
31{
32 int i;
33
34 pvc_clear();
35 for (i = 0; i < PVC_NLINES; i++)
36 pvc_write_string(pvc_lines[i], 0, i);
37}
38
39static DECLARE_TASKLET(pvc_display_tasklet, &pvc_display, 0);
40
41static int pvc_proc_read_line(char *page, char **start,
42 off_t off, int count,
43 int *eof, void *data)
44{
45 char *origpage = page;
46 int lineno = *(int *)data;
47
48 if (lineno < 0 || lineno > PVC_NLINES) {
49 printk(KERN_WARNING "proc_read_line: invalid lineno %d\n", lineno);
50 return 0;
51 }
52
Daniel Walker8a39c522008-01-10 20:53:21 -080053 mutex_lock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +020054 page += sprintf(page, "%s\n", pvc_lines[lineno]);
Daniel Walker8a39c522008-01-10 20:53:21 -080055 mutex_unlock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +020056
57 return page - origpage;
58}
59
60static int pvc_proc_write_line(struct file *file, const char *buffer,
61 unsigned long count, void *data)
62{
63 int origcount = count;
64 int lineno = *(int *)data;
65
66 if (lineno < 0 || lineno > PVC_NLINES) {
67 printk(KERN_WARNING "proc_write_line: invalid lineno %d\n",
68 lineno);
69 return origcount;
70 }
71
72 if (count > PVC_LINELEN)
73 count = PVC_LINELEN;
74
75 if (buffer[count-1] == '\n')
76 count--;
77
Daniel Walker8a39c522008-01-10 20:53:21 -080078 mutex_lock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +020079 strncpy(pvc_lines[lineno], buffer, count);
80 pvc_lines[lineno][count] = '\0';
Daniel Walker8a39c522008-01-10 20:53:21 -080081 mutex_unlock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +020082
83 tasklet_schedule(&pvc_display_tasklet);
84
85 return origcount;
86}
87
88static int pvc_proc_write_scroll(struct file *file, const char *buffer,
89 unsigned long count, void *data)
90{
91 int origcount = count;
92 int cmd = simple_strtol(buffer, NULL, 10);
93
Daniel Walker8a39c522008-01-10 20:53:21 -080094 mutex_lock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +020095 if (scroll_interval != 0)
96 del_timer(&timer);
97
98 if (cmd == 0) {
99 scroll_dir = 0;
100 scroll_interval = 0;
101 } else {
102 if (cmd < 0) {
103 scroll_dir = -1;
104 scroll_interval = -cmd;
105 } else {
106 scroll_dir = 1;
107 scroll_interval = cmd;
108 }
109 add_timer(&timer);
110 }
Daniel Walker8a39c522008-01-10 20:53:21 -0800111 mutex_unlock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +0200112
113 return origcount;
114}
115
116static int pvc_proc_read_scroll(char *page, char **start,
117 off_t off, int count,
118 int *eof, void *data)
119{
120 char *origpage = page;
121
Daniel Walker8a39c522008-01-10 20:53:21 -0800122 mutex_lock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +0200123 page += sprintf(page, "%d\n", scroll_dir * scroll_interval);
Daniel Walker8a39c522008-01-10 20:53:21 -0800124 mutex_unlock(&pvc_mutex);
Brian Murphy1f21d2b2007-08-21 22:34:16 +0200125
126 return page - origpage;
127}
128
129
130void pvc_proc_timerfunc(unsigned long data)
131{
132 if (scroll_dir < 0)
133 pvc_move(DISPLAY|RIGHT);
134 else if (scroll_dir > 0)
135 pvc_move(DISPLAY|LEFT);
136
137 timer.expires = jiffies + scroll_interval;
138 add_timer(&timer);
139}
140
141static void pvc_proc_cleanup(void)
142{
143 int i;
144 for (i = 0; i < PVC_NLINES; i++)
145 remove_proc_entry(pvc_linename[i], pvc_display_dir);
146 remove_proc_entry("scroll", pvc_display_dir);
147 remove_proc_entry(DISPLAY_DIR_NAME, NULL);
148
149 del_timer(&timer);
150}
151
152static int __init pvc_proc_init(void)
153{
154 struct proc_dir_entry *proc_entry;
155 int i;
156
157 pvc_display_dir = proc_mkdir(DISPLAY_DIR_NAME, NULL);
158 if (pvc_display_dir == NULL)
159 goto error;
160
161 for (i = 0; i < PVC_NLINES; i++) {
162 strcpy(pvc_lines[i], "");
163 pvc_linedata[i] = i;
164 }
165 for (i = 0; i < PVC_NLINES; i++) {
166 proc_entry = create_proc_entry(pvc_linename[i], 0644,
167 pvc_display_dir);
168 if (proc_entry == NULL)
169 goto error;
170
171 proc_entry->read_proc = pvc_proc_read_line;
172 proc_entry->write_proc = pvc_proc_write_line;
173 proc_entry->data = &pvc_linedata[i];
174 }
175 proc_entry = create_proc_entry("scroll", 0644, pvc_display_dir);
176 if (proc_entry == NULL)
177 goto error;
178
179 proc_entry->write_proc = pvc_proc_write_scroll;
180 proc_entry->read_proc = pvc_proc_read_scroll;
181
182 init_timer(&timer);
183 timer.function = pvc_proc_timerfunc;
184
185 return 0;
186error:
187 pvc_proc_cleanup();
188 return -ENOMEM;
189}
190
191module_init(pvc_proc_init);
192module_exit(pvc_proc_cleanup);
193MODULE_LICENSE("GPL");