1 /*
2  *	linux/arch/alpha/kernel/srmcons.c
3  *
4  * Callback based driver for SRM Console console device.
5  * (TTY driver and console driver)
6  */
7 
8 #include <linux/config.h>
9 #include <linux/kernel.h>
10 #include <linux/init.h>
11 #include <linux/console.h>
12 #include <linux/delay.h>
13 #include <linux/mm.h>
14 #include <linux/slab.h>
15 #include <linux/spinlock.h>
16 #include <linux/timer.h>
17 #include <linux/tty.h>
18 #include <linux/tty_driver.h>
19 #include <linux/tty_flip.h>
20 
21 #include <asm/console.h>
22 #include <asm/uaccess.h>
23 
24 
25 static spinlock_t srmcons_callback_lock = SPIN_LOCK_UNLOCKED;
26 static int srm_is_registered_console = 0;
27 
28 /*
29  * The TTY driver
30  */
31 #define MAX_SRM_CONSOLE_DEVICES 1	/* only support 1 console device */
32 
33 static int srmcons_refcount;
34 static struct tty_struct *srmcons_table[MAX_SRM_CONSOLE_DEVICES];
35 static struct termios *srmcons_termios[MAX_SRM_CONSOLE_DEVICES];
36 static struct termios *srmcons_termios_locked[MAX_SRM_CONSOLE_DEVICES];
37 
38 struct srmcons_private {
39 	struct tty_struct *tty;
40 	struct timer_list timer;
41 	spinlock_t lock;
42 };
43 
44 typedef union _srmcons_result {
45 	struct {
46 		unsigned long c :61;
47 		unsigned long status :3;
48 	} bits;
49 	long as_long;
50 } srmcons_result;
51 
52 /* called with callback_lock held */
53 static int
srmcons_do_receive_chars(struct tty_struct * tty)54 srmcons_do_receive_chars(struct tty_struct *tty)
55 {
56 	srmcons_result result;
57 	int count = 0, loops = 0;
58 
59 	do {
60 		result.as_long = callback_getc(0);
61 		if (result.bits.status < 2) {
62 			tty_insert_flip_char(tty, (char)result.bits.c, 0);
63 			count++;
64 		}
65 	} while((result.bits.status & 1) && (++loops < 10));
66 
67 	if (count)
68 		tty_schedule_flip(tty);
69 
70 	return count;
71 }
72 
73 static void
srmcons_receive_chars(unsigned long data)74 srmcons_receive_chars(unsigned long data)
75 {
76 	struct srmcons_private *srmconsp = (struct srmcons_private *)data;
77 	unsigned long flags;
78 	int incr = 10;
79 
80 	local_irq_save(flags);
81 	if (spin_trylock(&srmcons_callback_lock)) {
82 		if (!srmcons_do_receive_chars(srmconsp->tty))
83 			incr = 100;
84 		spin_unlock(&srmcons_callback_lock);
85 	}
86 
87 	spin_lock(&srmconsp->lock);
88 	if (srmconsp->tty) {
89 		srmconsp->timer.expires = jiffies + incr;
90 		add_timer(&srmconsp->timer);
91 	}
92 	spin_unlock(&srmconsp->lock);
93 
94 	local_irq_restore(flags);
95 }
96 
97 /* called with callback_lock held */
98 static int
srmcons_do_write(struct tty_struct * tty,const unsigned char * buf,int count)99 srmcons_do_write(struct tty_struct *tty, const unsigned char *buf, int count)
100 {
101 	unsigned char *str_cr = "\r";
102 	long c, remaining = count;
103 	srmcons_result result;
104 	unsigned char *cur;
105 	int need_cr;
106 
107 	for (cur = (unsigned char *)buf; remaining > 0; ) {
108 		need_cr = 0;
109 		/*
110 		 * Break it up into reasonable size chunks to allow a chance
111 		 * for input to get in
112 		 */
113 		for (c = 0; c < min_t(long, 128L, remaining) && !need_cr; c++)
114 			if (cur[c] == '\n')
115 				need_cr = 1;
116 
117 		while (c > 0) {
118 			result.as_long = callback_puts(0, cur, c);
119 			c -= result.bits.c;
120 			remaining -= result.bits.c;
121 			cur += result.bits.c;
122 
123 			/*
124 			 * Check for pending input iff a tty was provided
125 			 */
126 			if (tty)
127 				srmcons_do_receive_chars(tty);
128 		}
129 
130 		while (need_cr) {
131 			result.as_long = callback_puts(0, str_cr, 1);
132 			if (result.bits.c > 0)
133 				need_cr = 0;
134 		}
135 	}
136 	return count;
137 }
138 
139 static int
srmcons_write(struct tty_struct * tty,int from_user,const unsigned char * buf,int count)140 srmcons_write(struct tty_struct *tty, int from_user,
141 	      const unsigned char *buf, int count)
142 {
143 	unsigned long flags;
144 
145 	if (from_user) {
146 		unsigned char tmp[512];
147 		int ret = 0;
148 		size_t c;
149 
150 		while ((c = count) > 0) {
151 			if (c > sizeof(tmp))
152 				c = sizeof(tmp);
153 
154 			c -= copy_from_user(tmp, buf, c);
155 
156 			if (!c) {
157 				printk("%s: EFAULT (count %d)\n",
158 				       __FUNCTION__, count);
159 				return -EFAULT;
160 			}
161 
162 			spin_lock_irqsave(&srmcons_callback_lock, flags);
163 			srmcons_do_write(tty, tmp, c);
164 			spin_unlock_irqrestore(&srmcons_callback_lock, flags);
165 
166 			buf += c;
167 			count -= c;
168 			ret += c;
169 		}
170 
171 		return ret;
172 	}
173 
174 	spin_lock_irqsave(&srmcons_callback_lock, flags);
175 	srmcons_do_write(tty, buf, count);
176 	spin_unlock_irqrestore(&srmcons_callback_lock, flags);
177 
178 	return count;
179 }
180 
181 static int
srmcons_write_room(struct tty_struct * tty)182 srmcons_write_room(struct tty_struct *tty)
183 {
184 	return 512;
185 }
186 
187 static int
srmcons_chars_in_buffer(struct tty_struct * tty)188 srmcons_chars_in_buffer(struct tty_struct *tty)
189 {
190 	return 0;
191 }
192 
193 static int
srmcons_get_private_struct(struct srmcons_private ** ps)194 srmcons_get_private_struct(struct srmcons_private **ps)
195 {
196 	static struct srmcons_private *srmconsp = NULL;
197 	static spinlock_t srmconsp_lock = SPIN_LOCK_UNLOCKED;
198 	unsigned long flags;
199 	int retval = 0;
200 
201 	spin_lock_irqsave(&srmconsp_lock, flags);
202 
203 	do {
204 		if (srmconsp != NULL) {
205 			*ps = srmconsp;
206 			break;
207 		}
208 
209 		srmconsp = kmalloc(sizeof(*srmconsp), GFP_KERNEL);
210 		if (srmconsp == NULL) {
211 			retval = -ENOMEM;
212 			break;
213 		}
214 
215 		srmconsp->tty = NULL;
216 		srmconsp->lock = SPIN_LOCK_UNLOCKED;
217 		init_timer(&srmconsp->timer);
218 
219 		*ps = srmconsp;
220 	} while(0);
221 
222 	spin_unlock_irqrestore(&srmconsp_lock, flags);
223 
224 	return retval;
225 }
226 
227 static int
srmcons_open(struct tty_struct * tty,struct file * filp)228 srmcons_open(struct tty_struct *tty, struct file *filp)
229 {
230 	struct srmcons_private *srmconsp;
231 	unsigned long flags;
232 	int retval;
233 
234 	retval = srmcons_get_private_struct(&srmconsp);
235 	if (retval)
236 		return retval;
237 
238 	spin_lock_irqsave(&srmconsp->lock, flags);
239 
240 	if (!srmconsp->tty) {
241 		tty->driver_data = srmconsp;
242 
243 		srmconsp->tty = tty;
244 		srmconsp->timer.function = srmcons_receive_chars;
245 		srmconsp->timer.data = (unsigned long)srmconsp;
246 		srmconsp->timer.expires = jiffies + 10;
247 		add_timer(&srmconsp->timer);
248 	}
249 
250 	spin_unlock_irqrestore(&srmconsp->lock, flags);
251 
252 	return 0;
253 }
254 
255 static void
srmcons_close(struct tty_struct * tty,struct file * filp)256 srmcons_close(struct tty_struct *tty, struct file *filp)
257 {
258 	struct srmcons_private *srmconsp = tty->driver_data;
259 	unsigned long flags;
260 
261 	spin_lock_irqsave(&srmconsp->lock, flags);
262 
263 	if (tty->count == 1) {
264 		srmconsp->tty = NULL;
265 		del_timer(&srmconsp->timer);
266 	}
267 
268 	spin_unlock_irqrestore(&srmconsp->lock, flags);
269 }
270 
271 
272 static struct tty_driver srmcons_driver = {
273 	.driver_name	= "srm",
274 	.name		= "srm",
275 	.magic		= TTY_DRIVER_MAGIC,
276 	.major		= 0, 	/* dynamic */
277 	.minor_start	= 0,
278 	.num		= MAX_SRM_CONSOLE_DEVICES,
279 	.type		= TTY_DRIVER_TYPE_SYSTEM,
280 	.subtype	= SYSTEM_TYPE_SYSCONS,
281 
282 	.table		= srmcons_table,
283 	.termios	= srmcons_termios,
284 	.termios_locked	= srmcons_termios_locked,
285 	.refcount	= &srmcons_refcount,
286 
287 	.open		= srmcons_open,
288 	.close		= srmcons_close,
289 	.write		= srmcons_write,
290 	.write_room	= srmcons_write_room,
291 	.chars_in_buffer= srmcons_chars_in_buffer,
292 };
293 
294 static int __init
srmcons_init(void)295 srmcons_init(void)
296 {
297 	if (srm_is_registered_console) {
298 		srmcons_driver.init_termios = tty_std_termios;
299 		return tty_register_driver(&srmcons_driver);
300 	}
301 
302 	return -ENODEV;
303 }
304 
305 module_init(srmcons_init);
306 
307 
308 /*
309  * The console driver
310  */
311 static void
srm_console_write(struct console * co,const char * s,unsigned count)312 srm_console_write(struct console *co, const char *s, unsigned count)
313 {
314 	unsigned long flags;
315 
316 	spin_lock_irqsave(&srmcons_callback_lock, flags);
317 	srmcons_do_write(NULL, s, count);
318 	spin_unlock_irqrestore(&srmcons_callback_lock, flags);
319 }
320 
321 static kdev_t
srm_console_device(struct console * co)322 srm_console_device(struct console *co)
323 {
324 	return mk_kdev(srmcons_driver.major,
325 		       srmcons_driver.minor_start + co->index);
326 }
327 
328 static int __init
srm_console_setup(struct console * co,char * options)329 srm_console_setup(struct console *co, char *options)
330 {
331 	return 0;
332 }
333 
334 static struct console srmcons = {
335 	.name		= "srm",
336 	.write		= srm_console_write,
337 	.device		= srm_console_device,
338 	.setup		= srm_console_setup,
339 	.flags		= CON_PRINTBUFFER,
340 	.index		= -1,
341 };
342 
343 void __init
register_srm_console(void)344 register_srm_console(void)
345 {
346 	if (!srm_is_registered_console) {
347 		callback_open_console();
348 		register_console(&srmcons);
349 		srm_is_registered_console = 1;
350 	}
351 }
352 
353 void __init
unregister_srm_console(void)354 unregister_srm_console(void)
355 {
356 	if (srm_is_registered_console) {
357 		callback_close_console();
358 		unregister_console(&srmcons);
359 		srm_is_registered_console = 0;
360 	}
361 }
362