PageRenderTime 84ms CodeModel.GetById 13ms app.highlight 64ms RepoModel.GetById 2ms app.codeStats 0ms

/arch/um/drivers/line.c

https://bitbucket.org/evzijst/gittest
C | 681 lines | 569 code | 88 blank | 24 comment | 132 complexity | 38fc33a086b1115e9631fa1e81003820 MD5 | raw file
  1/* 
  2 * Copyright (C) 2001, 2002 Jeff Dike (jdike@karaya.com)
  3 * Licensed under the GPL
  4 */
  5
  6#include "linux/sched.h"
  7#include "linux/slab.h"
  8#include "linux/list.h"
  9#include "linux/kd.h"
 10#include "linux/interrupt.h"
 11#include "linux/devfs_fs_kernel.h"
 12#include "asm/uaccess.h"
 13#include "chan_kern.h"
 14#include "irq_user.h"
 15#include "line.h"
 16#include "kern.h"
 17#include "user_util.h"
 18#include "kern_util.h"
 19#include "os.h"
 20#include "irq_kern.h"
 21
 22#define LINE_BUFSIZE 4096
 23
 24static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused)
 25{
 26	struct tty_struct *tty = data;
 27	struct line *line = tty->driver_data;
 28
 29	if (line)
 30		chan_interrupt(&line->chan_list, &line->task, tty, irq);
 31	return IRQ_HANDLED;
 32}
 33
 34static void line_timer_cb(void *arg)
 35{
 36	struct tty_struct *tty = arg;
 37	struct line *line = tty->driver_data;
 38
 39	line_interrupt(line->driver->read_irq, arg, NULL);
 40}
 41
 42static int write_room(struct line *dev)
 43{
 44	int n;
 45
 46	if (dev->buffer == NULL)
 47		return (LINE_BUFSIZE - 1);
 48
 49	n = dev->head - dev->tail;
 50	if (n <= 0)
 51		n = LINE_BUFSIZE + n;
 52	return (n - 1);
 53}
 54
 55static int buffer_data(struct line *line, const char *buf, int len)
 56{
 57	int end, room;
 58
 59	if(line->buffer == NULL){
 60		line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC);
 61		if (line->buffer == NULL) {
 62			printk("buffer_data - atomic allocation failed\n");
 63			return(0);
 64		}
 65		line->head = line->buffer;
 66		line->tail = line->buffer;
 67	}
 68
 69	room = write_room(line);
 70	len = (len > room) ? room : len;
 71
 72	end = line->buffer + LINE_BUFSIZE - line->tail;
 73	if(len < end){
 74		memcpy(line->tail, buf, len);
 75		line->tail += len;
 76	}
 77	else {
 78		memcpy(line->tail, buf, end);
 79		buf += end;
 80		memcpy(line->buffer, buf, len - end);
 81		line->tail = line->buffer + len - end;
 82	}
 83
 84	return(len);
 85}
 86
 87static int flush_buffer(struct line *line)
 88{
 89	int n, count;
 90
 91	if ((line->buffer == NULL) || (line->head == line->tail))
 92		return(1);
 93
 94	if (line->tail < line->head) {
 95		count = line->buffer + LINE_BUFSIZE - line->head;
 96		n = write_chan(&line->chan_list, line->head, count,
 97			       line->driver->write_irq);
 98		if (n < 0)
 99			return(n);
100		if (n == count)
101			line->head = line->buffer;
102		else {
103			line->head += n;
104			return(0);
105		}
106	}
107
108	count = line->tail - line->head;
109	n = write_chan(&line->chan_list, line->head, count, 
110		       line->driver->write_irq);
111	if(n < 0) return(n);
112
113	line->head += n;
114	return(line->head == line->tail);
115}
116
117int line_write(struct tty_struct *tty, const unsigned char *buf, int len)
118{
119	struct line *line = tty->driver_data;
120	unsigned long flags;
121	int n, err, ret = 0;
122
123	if(tty->stopped) return 0;
124
125	down(&line->sem);
126	if(line->head != line->tail){
127		local_irq_save(flags);
128		ret = buffer_data(line, buf, len);
129		err = flush_buffer(line);
130		local_irq_restore(flags);
131		if(err <= 0 && (err != -EAGAIN || !ret))
132			ret = err;
133	}
134	else {
135		n = write_chan(&line->chan_list, buf, len, 
136			       line->driver->write_irq);
137		if(n < 0){
138			ret = n;
139			goto out_up;
140		}
141
142		len -= n;
143		ret += n;
144		if(len > 0)
145			ret += buffer_data(line, buf + n, len);
146	}
147 out_up:
148	up(&line->sem);
149	return(ret);
150}
151
152void line_put_char(struct tty_struct *tty, unsigned char ch)
153{
154	line_write(tty, &ch, sizeof(ch));
155}
156
157void line_set_termios(struct tty_struct *tty, struct termios * old)
158{
159	/* nothing */
160}
161
162int line_chars_in_buffer(struct tty_struct *tty)
163{
164	return 0;
165}
166
167static struct {
168	int  cmd;
169	char *level;
170	char *name;
171} tty_ioctls[] = {
172	/* don't print these, they flood the log ... */
173	{ TCGETS,      NULL,       "TCGETS"      },
174        { TCSETS,      NULL,       "TCSETS"      },
175        { TCSETSW,     NULL,       "TCSETSW"     },
176        { TCFLSH,      NULL,       "TCFLSH"      },
177        { TCSBRK,      NULL,       "TCSBRK"      },
178
179	/* general tty stuff */
180        { TCSETSF,     KERN_DEBUG, "TCSETSF"     },
181        { TCGETA,      KERN_DEBUG, "TCGETA"      },
182        { TIOCMGET,    KERN_DEBUG, "TIOCMGET"    },
183        { TCSBRKP,     KERN_DEBUG, "TCSBRKP"     },
184        { TIOCMSET,    KERN_DEBUG, "TIOCMSET"    },
185
186	/* linux-specific ones */
187	{ TIOCLINUX,   KERN_INFO,  "TIOCLINUX"   },
188	{ KDGKBMODE,   KERN_INFO,  "KDGKBMODE"   },
189	{ KDGKBTYPE,   KERN_INFO,  "KDGKBTYPE"   },
190	{ KDSIGACCEPT, KERN_INFO,  "KDSIGACCEPT" },
191};
192
193int line_ioctl(struct tty_struct *tty, struct file * file,
194	       unsigned int cmd, unsigned long arg)
195{
196	int ret;
197	int i;
198
199	ret = 0;
200	switch(cmd) {
201#ifdef TIOCGETP
202	case TIOCGETP:
203	case TIOCSETP:
204	case TIOCSETN:
205#endif
206#ifdef TIOCGETC
207	case TIOCGETC:
208	case TIOCSETC:
209#endif
210#ifdef TIOCGLTC
211	case TIOCGLTC:
212	case TIOCSLTC:
213#endif
214	case TCGETS:
215	case TCSETSF:
216	case TCSETSW:
217	case TCSETS:
218	case TCGETA:
219	case TCSETAF:
220	case TCSETAW:
221	case TCSETA:
222	case TCXONC:
223	case TCFLSH:
224	case TIOCOUTQ:
225	case TIOCINQ:
226	case TIOCGLCKTRMIOS:
227	case TIOCSLCKTRMIOS:
228	case TIOCPKT:
229	case TIOCGSOFTCAR:
230	case TIOCSSOFTCAR:
231		return -ENOIOCTLCMD;
232#if 0
233	case TCwhatever:
234		/* do something */
235		break;
236#endif
237	default:
238		for (i = 0; i < ARRAY_SIZE(tty_ioctls); i++)
239			if (cmd == tty_ioctls[i].cmd)
240				break;
241		if (i < ARRAY_SIZE(tty_ioctls)) {
242			if (NULL != tty_ioctls[i].level)
243				printk("%s%s: %s: ioctl %s called\n",
244				       tty_ioctls[i].level, __FUNCTION__,
245				       tty->name, tty_ioctls[i].name);
246		} else {
247			printk(KERN_ERR "%s: %s: unknown ioctl: 0x%x\n",
248			       __FUNCTION__, tty->name, cmd);
249		}
250		ret = -ENOIOCTLCMD;
251		break;
252	}
253	return(ret);
254}
255
256static irqreturn_t line_write_interrupt(int irq, void *data,
257					struct pt_regs *unused)
258{
259	struct tty_struct *tty = data;
260	struct line *line = tty->driver_data;
261	int err;
262
263	err = flush_buffer(line);
264	if(err == 0)
265		return(IRQ_NONE);
266	else if(err < 0){
267		line->head = line->buffer;
268		line->tail = line->buffer;
269	}
270
271	if(tty == NULL)
272		return(IRQ_NONE);
273
274	if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
275	   (tty->ldisc.write_wakeup != NULL))
276		(tty->ldisc.write_wakeup)(tty);
277	
278	/* BLOCKING mode
279	 * In blocking mode, everything sleeps on tty->write_wait.
280	 * Sleeping in the console driver would break non-blocking
281	 * writes.
282	 */
283
284	if(waitqueue_active(&tty->write_wait))
285		wake_up_interruptible(&tty->write_wait);
286	return(IRQ_HANDLED);
287}
288
289int line_setup_irq(int fd, int input, int output, struct tty_struct *tty)
290{
291	struct line *line = tty->driver_data;
292	struct line_driver *driver = line->driver;
293	int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM;
294
295	if(input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, 
296				       line_interrupt, flags, 
297				       driver->read_irq_name, tty);
298	if(err) return(err);
299	if(output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, 
300					line_write_interrupt, flags, 
301					driver->write_irq_name, tty);
302	line->have_irq = 1;
303	return(err);
304}
305
306void line_disable(struct tty_struct *tty, int current_irq)
307{
308	struct line *line = tty->driver_data;
309
310	if(!line->have_irq)
311		return;
312
313	if(line->driver->read_irq == current_irq)
314		free_irq_later(line->driver->read_irq, tty);
315	else {
316		free_irq_by_irq_and_dev(line->driver->read_irq, tty);
317		free_irq(line->driver->read_irq, tty);
318	}
319
320	if(line->driver->write_irq == current_irq)
321		free_irq_later(line->driver->write_irq, tty);
322	else {
323		free_irq_by_irq_and_dev(line->driver->write_irq, tty);
324		free_irq(line->driver->write_irq, tty);
325	}
326
327	line->have_irq = 0;
328}
329
330int line_open(struct line *lines, struct tty_struct *tty,
331	      struct chan_opts *opts)
332{
333	struct line *line;
334	int err = 0;
335
336	line = &lines[tty->index];
337	tty->driver_data = line;
338
339	down(&line->sem);
340	if (tty->count == 1) {
341		if (!line->valid) {
342			err = -ENODEV;
343			goto out;
344		}
345		if (list_empty(&line->chan_list)) {
346			err = parse_chan_pair(line->init_str, &line->chan_list,
347					      line->init_pri, tty->index, opts);
348			if(err) goto out;
349			err = open_chan(&line->chan_list);
350			if(err) goto out;
351		}
352		enable_chan(&line->chan_list, tty);
353		INIT_WORK(&line->task, line_timer_cb, tty);
354	}
355
356	if(!line->sigio){
357		chan_enable_winch(&line->chan_list, tty);
358		line->sigio = 1;
359	}
360	chan_window_size(&line->chan_list, &tty->winsize.ws_row,
361			 &tty->winsize.ws_col);
362	line->count++;
363
364out:
365	up(&line->sem);
366	return(err);
367}
368
369void line_close(struct tty_struct *tty, struct file * filp)
370{
371	struct line *line = tty->driver_data;
372
373	down(&line->sem);
374	line->count--;
375	if (tty->count == 1) {
376		line_disable(tty, -1);
377		tty->driver_data = NULL;
378	}
379	up(&line->sem);
380}
381
382void close_lines(struct line *lines, int nlines)
383{
384	int i;
385
386	for(i = 0; i < nlines; i++)
387		close_chan(&lines[i].chan_list);
388}
389
390int line_setup(struct line *lines, int num, char *init, int all_allowed)
391{
392	int i, n;
393	char *end;
394
395	if(*init == '=') n = -1;
396	else {
397		n = simple_strtoul(init, &end, 0);
398		if(*end != '='){
399			printk(KERN_ERR "line_setup failed to parse \"%s\"\n", 
400			       init);
401			return(0);
402		}
403		init = end;
404	}
405	init++;
406	if((n >= 0) && (n >= num)){
407		printk("line_setup - %d out of range ((0 ... %d) allowed)\n",
408		       n, num - 1);
409		return(0);
410	}
411	else if (n >= 0){
412		if (lines[n].count > 0) {
413			printk("line_setup - device %d is open\n", n);
414			return(0);
415		}
416		if (lines[n].init_pri <= INIT_ONE){
417			lines[n].init_pri = INIT_ONE;
418			if (!strcmp(init, "none"))
419				lines[n].valid = 0;
420			else {
421				lines[n].init_str = init;
422				lines[n].valid = 1;
423			}	
424		}
425	}
426	else if(!all_allowed){
427		printk("line_setup - can't configure all devices from "
428		       "mconsole\n");
429		return(0);
430	}
431	else {
432		for(i = 0; i < num; i++){
433			if(lines[i].init_pri <= INIT_ALL){
434				lines[i].init_pri = INIT_ALL;
435				if(!strcmp(init, "none")) lines[i].valid = 0;
436				else {
437					lines[i].init_str = init;
438					lines[i].valid = 1;
439				}
440			}
441		}
442	}
443	return(1);
444}
445
446int line_config(struct line *lines, int num, char *str)
447{
448	char *new = uml_strdup(str);
449
450	if(new == NULL){
451		printk("line_config - uml_strdup failed\n");
452		return(-ENOMEM);
453	}
454	return(!line_setup(lines, num, new, 0));
455}
456
457int line_get_config(char *name, struct line *lines, int num, char *str, 
458		    int size, char **error_out)
459{
460	struct line *line;
461	char *end;
462	int dev, n = 0;
463
464	dev = simple_strtoul(name, &end, 0);
465	if((*end != '\0') || (end == name)){
466		*error_out = "line_get_config failed to parse device number";
467		return(0);
468	}
469
470	if((dev < 0) || (dev >= num)){
471		*error_out = "device number of of range";
472		return(0);
473	}
474
475	line = &lines[dev];
476
477	down(&line->sem);
478	if(!line->valid)
479		CONFIG_CHUNK(str, size, n, "none", 1);
480	else if(line->count == 0)
481		CONFIG_CHUNK(str, size, n, line->init_str, 1);
482	else n = chan_config_string(&line->chan_list, str, size, error_out);
483	up(&line->sem);
484
485	return(n);
486}
487
488int line_remove(struct line *lines, int num, char *str)
489{
490	char config[sizeof("conxxxx=none\0")];
491
492	sprintf(config, "%s=none", str);
493	return(!line_setup(lines, num, config, 0));
494}
495
496int line_write_room(struct tty_struct *tty)
497{
498	struct line *dev = tty->driver_data;
499	int room;
500
501	if (tty->stopped)
502		return 0;
503	room = write_room(dev);
504	if (0 == room)
505		printk(KERN_DEBUG "%s: %s: no room left in buffer\n",
506		       __FUNCTION__,tty->name);
507	return room;
508}
509
510struct tty_driver *line_register_devfs(struct lines *set,
511			 struct line_driver *line_driver, 
512			 struct tty_operations *ops, struct line *lines,
513			 int nlines)
514{
515	int i;
516	struct tty_driver *driver = alloc_tty_driver(nlines);
517
518	if (!driver)
519		return NULL;
520
521	driver->driver_name = line_driver->name;
522	driver->name = line_driver->device_name;
523	driver->devfs_name = line_driver->devfs_name;
524	driver->major = line_driver->major;
525	driver->minor_start = line_driver->minor_start;
526	driver->type = line_driver->type;
527	driver->subtype = line_driver->subtype;
528	driver->flags = TTY_DRIVER_REAL_RAW;
529	driver->init_termios = tty_std_termios;
530	tty_set_operations(driver, ops);
531
532	if (tty_register_driver(driver)) {
533		printk("%s: can't register %s driver\n",
534		       __FUNCTION__,line_driver->name);
535		put_tty_driver(driver);
536		return NULL;
537	}
538
539	for(i = 0; i < nlines; i++){
540		if(!lines[i].valid) 
541			tty_unregister_device(driver, i);
542	}
543
544	mconsole_register_dev(&line_driver->mc);
545	return driver;
546}
547
548void lines_init(struct line *lines, int nlines)
549{
550	struct line *line;
551	int i;
552
553	for(i = 0; i < nlines; i++){
554		line = &lines[i];
555		INIT_LIST_HEAD(&line->chan_list);
556		sema_init(&line->sem, 1);
557		if(line->init_str != NULL){
558			line->init_str = uml_strdup(line->init_str);
559			if(line->init_str == NULL)
560				printk("lines_init - uml_strdup returned "
561				       "NULL\n");
562		}
563	}
564}
565
566struct winch {
567	struct list_head list;
568	int fd;
569	int tty_fd;
570	int pid;
571	struct tty_struct *tty;
572};
573
574irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused)
575{
576	struct winch *winch = data;
577	struct tty_struct *tty;
578	struct line *line;
579	int err;
580	char c;
581
582	if(winch->fd != -1){
583		err = generic_read(winch->fd, &c, NULL);
584		if(err < 0){
585			if(err != -EAGAIN){
586				printk("winch_interrupt : read failed, "
587				       "errno = %d\n", -err);
588				printk("fd %d is losing SIGWINCH support\n",
589				       winch->tty_fd);
590				return(IRQ_HANDLED);
591			}
592			goto out;
593		}
594	}
595	tty  = winch->tty;
596	if (tty != NULL) {
597		line = tty->driver_data;
598		chan_window_size(&line->chan_list,
599				 &tty->winsize.ws_row, 
600				 &tty->winsize.ws_col);
601		kill_pg(tty->pgrp, SIGWINCH, 1);
602	}
603 out:
604	if(winch->fd != -1)
605		reactivate_fd(winch->fd, WINCH_IRQ);
606	return(IRQ_HANDLED);
607}
608
609DECLARE_MUTEX(winch_handler_sem);
610LIST_HEAD(winch_handlers);
611
612void register_winch_irq(int fd, int tty_fd, int pid, struct tty_struct *tty)
613{
614	struct winch *winch;
615
616	down(&winch_handler_sem);
617	winch = kmalloc(sizeof(*winch), GFP_KERNEL);
618	if (winch == NULL) {
619		printk("register_winch_irq - kmalloc failed\n");
620		goto out;
621	}
622	*winch = ((struct winch) { .list  	= LIST_HEAD_INIT(winch->list),
623				   .fd  	= fd,
624				   .tty_fd 	= tty_fd,
625				   .pid  	= pid,
626				   .tty 	= tty });
627	list_add(&winch->list, &winch_handlers);
628	if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, 
629			  SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
630			  "winch", winch) < 0)
631		printk("register_winch_irq - failed to register IRQ\n");
632 out:
633	up(&winch_handler_sem);
634}
635
636static void winch_cleanup(void)
637{
638	struct list_head *ele;
639	struct winch *winch;
640
641	list_for_each(ele, &winch_handlers){
642		winch = list_entry(ele, struct winch, list);
643		if(winch->fd != -1){
644			deactivate_fd(winch->fd, WINCH_IRQ);
645			os_close_file(winch->fd);
646		}
647		if(winch->pid != -1) 
648			os_kill_process(winch->pid, 1);
649	}
650}
651__uml_exitcall(winch_cleanup);
652
653char *add_xterm_umid(char *base)
654{
655	char *umid, *title;
656	int len;
657
658	umid = get_umid(1);
659	if(umid == NULL) return(base);
660	
661	len = strlen(base) + strlen(" ()") + strlen(umid) + 1;
662	title = kmalloc(len, GFP_KERNEL);
663	if(title == NULL){
664		printk("Failed to allocate buffer for xterm title\n");
665		return(base);
666	}
667
668	snprintf(title, len, "%s (%s)", base, umid);
669	return(title);
670}
671
672/*
673 * Overrides for Emacs so that we follow Linus's tabbing style.
674 * Emacs will notice this stuff at the end of the file and automatically
675 * adjust the settings for this buffer only.  This must remain at the end
676 * of the file.
677 * ---------------------------------------------------------------------------
678 * Local variables:
679 * c-file-style: "linux"
680 * End:
681 */