Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[linux-2.6] / arch / mips / lasat / picvue_proc.c
1 /*
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>
16
17 #include "picvue.h"
18
19 static char pvc_lines[PVC_NLINES][PVC_LINELEN+1];
20 static int pvc_linedata[PVC_NLINES];
21 static struct proc_dir_entry *pvc_display_dir;
22 static char *pvc_linename[PVC_NLINES] = {"line1", "line2"};
23 #define DISPLAY_DIR_NAME "display"
24 static int scroll_dir, scroll_interval;
25
26 static struct timer_list timer;
27
28 static void pvc_display(unsigned long data)
29 {
30         int i;
31
32         pvc_clear();
33         for (i = 0; i < PVC_NLINES; i++)
34                 pvc_write_string(pvc_lines[i], 0, i);
35 }
36
37 static DECLARE_TASKLET(pvc_display_tasklet, &pvc_display, 0);
38
39 static int pvc_proc_read_line(char *page, char **start,
40                              off_t off, int count,
41                              int *eof, void *data)
42 {
43         char *origpage = page;
44         int lineno = *(int *)data;
45
46         if (lineno < 0 || lineno > PVC_NLINES) {
47                 printk(KERN_WARNING "proc_read_line: invalid lineno %d\n", lineno);
48                 return 0;
49         }
50
51         down(&pvc_sem);
52         page += sprintf(page, "%s\n", pvc_lines[lineno]);
53         up(&pvc_sem);
54
55         return page - origpage;
56 }
57
58 static int pvc_proc_write_line(struct file *file, const char *buffer,
59                            unsigned long count, void *data)
60 {
61         int origcount = count;
62         int lineno = *(int *)data;
63
64         if (lineno < 0 || lineno > PVC_NLINES) {
65                 printk(KERN_WARNING "proc_write_line: invalid lineno %d\n",
66                        lineno);
67                 return origcount;
68         }
69
70         if (count > PVC_LINELEN)
71                 count = PVC_LINELEN;
72
73         if (buffer[count-1] == '\n')
74                 count--;
75
76         down(&pvc_sem);
77         strncpy(pvc_lines[lineno], buffer, count);
78         pvc_lines[lineno][count] = '\0';
79         up(&pvc_sem);
80
81         tasklet_schedule(&pvc_display_tasklet);
82
83         return origcount;
84 }
85
86 static int pvc_proc_write_scroll(struct file *file, const char *buffer,
87                            unsigned long count, void *data)
88 {
89         int origcount = count;
90         int cmd = simple_strtol(buffer, NULL, 10);
91
92         down(&pvc_sem);
93         if (scroll_interval != 0)
94                 del_timer(&timer);
95
96         if (cmd == 0) {
97                 scroll_dir = 0;
98                 scroll_interval = 0;
99         } else {
100                 if (cmd < 0) {
101                         scroll_dir = -1;
102                         scroll_interval = -cmd;
103                 } else {
104                         scroll_dir = 1;
105                         scroll_interval = cmd;
106                 }
107                 add_timer(&timer);
108         }
109         up(&pvc_sem);
110
111         return origcount;
112 }
113
114 static int pvc_proc_read_scroll(char *page, char **start,
115                              off_t off, int count,
116                              int *eof, void *data)
117 {
118         char *origpage = page;
119
120         down(&pvc_sem);
121         page += sprintf(page, "%d\n", scroll_dir * scroll_interval);
122         up(&pvc_sem);
123
124         return page - origpage;
125 }
126
127
128 void pvc_proc_timerfunc(unsigned long data)
129 {
130         if (scroll_dir < 0)
131                 pvc_move(DISPLAY|RIGHT);
132         else if (scroll_dir > 0)
133                 pvc_move(DISPLAY|LEFT);
134
135         timer.expires = jiffies + scroll_interval;
136         add_timer(&timer);
137 }
138
139 static void pvc_proc_cleanup(void)
140 {
141         int i;
142         for (i = 0; i < PVC_NLINES; i++)
143                 remove_proc_entry(pvc_linename[i], pvc_display_dir);
144         remove_proc_entry("scroll", pvc_display_dir);
145         remove_proc_entry(DISPLAY_DIR_NAME, NULL);
146
147         del_timer(&timer);
148 }
149
150 static int __init pvc_proc_init(void)
151 {
152         struct proc_dir_entry *proc_entry;
153         int i;
154
155         pvc_display_dir = proc_mkdir(DISPLAY_DIR_NAME, NULL);
156         if (pvc_display_dir == NULL)
157                 goto error;
158
159         for (i = 0; i < PVC_NLINES; i++) {
160                 strcpy(pvc_lines[i], "");
161                 pvc_linedata[i] = i;
162         }
163         for (i = 0; i < PVC_NLINES; i++) {
164                 proc_entry = create_proc_entry(pvc_linename[i], 0644,
165                                                pvc_display_dir);
166                 if (proc_entry == NULL)
167                         goto error;
168
169                 proc_entry->read_proc = pvc_proc_read_line;
170                 proc_entry->write_proc = pvc_proc_write_line;
171                 proc_entry->data = &pvc_linedata[i];
172         }
173         proc_entry = create_proc_entry("scroll", 0644, pvc_display_dir);
174         if (proc_entry == NULL)
175                 goto error;
176
177         proc_entry->write_proc = pvc_proc_write_scroll;
178         proc_entry->read_proc = pvc_proc_read_scroll;
179
180         init_timer(&timer);
181         timer.function = pvc_proc_timerfunc;
182
183         return 0;
184 error:
185         pvc_proc_cleanup();
186         return -ENOMEM;
187 }
188
189 module_init(pvc_proc_init);
190 module_exit(pvc_proc_cleanup);
191 MODULE_LICENSE("GPL");