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