This commit was manufactured by cvs2svn to create tag
[linux-2.6.git] / arch / um / drivers / line.c
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/interrupt.h"
10 #include "linux/devfs_fs_kernel.h"
11 #include "asm/uaccess.h"
12 #include "chan_kern.h"
13 #include "irq_user.h"
14 #include "line.h"
15 #include "kern.h"
16 #include "user_util.h"
17 #include "kern_util.h"
18 #include "os.h"
19 #include "irq_kern.h"
20
21 #define LINE_BUFSIZE 4096
22
23 static irqreturn_t line_interrupt(int irq, void *data, struct pt_regs *unused)
24 {
25         struct line *dev = data;
26
27         if(dev->count > 0) 
28                 chan_interrupt(&dev->chan_list, &dev->task, dev->tty, irq, 
29                                dev);
30         return IRQ_HANDLED;
31 }
32
33 static void line_timer_cb(void *arg)
34 {
35         struct line *dev = arg;
36
37         line_interrupt(dev->driver->read_irq, dev, NULL);
38 }
39
40 static int write_room(struct line *dev)
41 {
42         int n;
43
44         if(dev->buffer == NULL) return(LINE_BUFSIZE - 1);
45
46         n = dev->head - dev->tail;
47         if(n <= 0) n = LINE_BUFSIZE + n;
48         return(n - 1);
49 }
50
51 static int buffer_data(struct line *line, const char *buf, int len)
52 {
53         int end, room;
54
55         if(line->buffer == NULL){
56                 line->buffer = kmalloc(LINE_BUFSIZE, GFP_ATOMIC);
57                 if(line->buffer == NULL){
58                         printk("buffer_data - atomic allocation failed\n");
59                         return(0);
60                 }
61                 line->head = line->buffer;
62                 line->tail = line->buffer;
63         }
64
65         room = write_room(line);
66         len = (len > room) ? room : len;
67
68         end = line->buffer + LINE_BUFSIZE - line->tail;
69         if(len < end){
70                 memcpy(line->tail, buf, len);
71                 line->tail += len;
72         }
73         else {
74                 memcpy(line->tail, buf, end);
75                 buf += end;
76                 len -= end;
77                 memcpy(line->buffer, buf, len);
78                 line->tail = line->buffer + len;
79         }
80
81         return(len);
82 }
83
84 static int flush_buffer(struct line *line)
85 {
86         int n, count;
87
88         if((line->buffer == NULL) || (line->head == line->tail)) return(1);
89
90         if(line->tail < line->head){
91                 count = line->buffer + LINE_BUFSIZE - line->head;
92                 n = write_chan(&line->chan_list, line->head, count,
93                                line->driver->write_irq);
94                 if(n < 0) return(n);
95                 if(n == count) line->head = line->buffer;
96                 else {
97                         line->head += n;
98                         return(0);
99                 }
100         }
101
102         count = line->tail - line->head;
103         n = write_chan(&line->chan_list, line->head, count, 
104                        line->driver->write_irq);
105         if(n < 0) return(n);
106
107         line->head += n;
108         return(line->head == line->tail);
109 }
110
111 int line_write(struct line *lines, struct tty_struct *tty, int from_user,
112                const char *buf, int len)
113 {
114         struct line *line;
115         char *new;
116         unsigned long flags;
117         int n, err, i, ret = 0;
118
119         if(tty->stopped) return 0;
120
121         if(from_user){
122                 new = kmalloc(len, GFP_KERNEL);
123                 if(new == NULL)
124                         return(0);
125                 n = copy_from_user(new, buf, len);
126                 buf = new;
127                 if(n == len){
128                         len = -EFAULT;
129                         goto out_free;
130                 }
131
132                 len -= n;
133         }
134
135         i = tty->index;
136         line = &lines[i];
137
138         down(&line->sem);
139         if(line->head != line->tail){
140                 local_irq_save(flags);
141                 ret += buffer_data(line, buf, len);
142                 err = flush_buffer(line);
143                 local_irq_restore(flags);
144                 if(err <= 0)
145                         goto out_up;
146         }
147         else {
148                 n = write_chan(&line->chan_list, buf, len, 
149                                line->driver->write_irq);
150                 if(n < 0){
151                         ret = n;
152                         goto out_up;
153                 }
154
155                 len -= n;
156                 ret += n;
157                 if(len > 0)
158                         ret += buffer_data(line, buf + n, len);
159         }
160  out_up:
161         up(&line->sem);
162  out_free:
163         if(from_user)
164                 kfree(buf);
165         return(ret);
166 }
167
168 static irqreturn_t line_write_interrupt(int irq, void *data, 
169                                         struct pt_regs *unused)
170 {
171         struct line *dev = data;
172         struct tty_struct *tty = dev->tty;
173         int err;
174
175         err = flush_buffer(dev);
176         if(err == 0) 
177                 return(IRQ_NONE);
178         else if(err < 0){
179                 dev->head = dev->buffer;
180                 dev->tail = dev->buffer;
181         }
182
183         if(tty == NULL) 
184                 return(IRQ_NONE);
185
186         if(test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
187            (tty->ldisc.write_wakeup != NULL))
188                 (tty->ldisc.write_wakeup)(tty);
189         
190         /* BLOCKING mode
191          * In blocking mode, everything sleeps on tty->write_wait.
192          * Sleeping in the console driver would break non-blocking
193          * writes.
194          */
195
196         if(waitqueue_active(&tty->write_wait))
197                 wake_up_interruptible(&tty->write_wait);
198         return(IRQ_HANDLED);
199 }
200
201 int line_setup_irq(int fd, int input, int output, void *data)
202 {
203         struct line *line = data;
204         struct line_driver *driver = line->driver;
205         int err = 0, flags = SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM;
206
207         if(input) err = um_request_irq(driver->read_irq, fd, IRQ_READ, 
208                                        line_interrupt, flags, 
209                                        driver->read_irq_name, line);
210         if(err) return(err);
211         if(output) err = um_request_irq(driver->write_irq, fd, IRQ_WRITE, 
212                                         line_write_interrupt, flags, 
213                                         driver->write_irq_name, line);
214         line->have_irq = 1;
215         return(err);
216 }
217
218 void line_disable(struct line *line, int current_irq)
219 {
220         if(!line->have_irq) return;
221
222         if(line->driver->read_irq == current_irq)
223                 free_irq_later(line->driver->read_irq, line);
224         else
225                 free_irq(line->driver->read_irq, line);
226
227         if(line->driver->write_irq == current_irq)
228                 free_irq_later(line->driver->write_irq, line);
229         else
230                 free_irq(line->driver->write_irq, line);
231
232         line->have_irq = 0;
233 }
234
235 int line_open(struct line *lines, struct tty_struct *tty,
236               struct chan_opts *opts)
237 {
238         struct line *line;
239         int n, err = 0;
240
241         if(tty == NULL) n = 0;
242         else n = tty->index;
243         line = &lines[n];
244
245         down(&line->sem);
246         if(line->count == 0){
247                 if(!line->valid){
248                         err = -ENODEV;
249                         goto out;
250                 }
251                 if(list_empty(&line->chan_list)){
252                         err = parse_chan_pair(line->init_str, &line->chan_list,
253                                               line->init_pri, n, opts);
254                         if(err) goto out;
255                         err = open_chan(&line->chan_list);
256                         if(err) goto out;
257                 }
258                 enable_chan(&line->chan_list, line);
259                 INIT_WORK(&line->task, line_timer_cb, line);
260         }
261
262         if(!line->sigio){
263                 chan_enable_winch(&line->chan_list, line);
264                 line->sigio = 1;
265         }
266
267         /* This is outside the if because the initial console is opened
268          * with tty == NULL
269          */
270         line->tty = tty;
271
272         if(tty != NULL){
273                 tty->driver_data = line;
274                 chan_window_size(&line->chan_list, &tty->winsize.ws_row, 
275                                  &tty->winsize.ws_col);
276         }
277
278         line->count++;
279  out:
280         up(&line->sem);
281         return(err);
282 }
283
284 void line_close(struct line *lines, struct tty_struct *tty)
285 {
286         struct line *line;
287         int n;
288
289         if(tty == NULL) n = 0;
290         else n = tty->index;
291         line = &lines[n];
292
293         down(&line->sem);
294         line->count--;
295
296         /* I don't like this, but I can't think of anything better.  What's
297          * going on is that the tty is in the process of being closed for
298          * the last time.  Its count hasn't been dropped yet, so it's still
299          * at 1.  This may happen when line->count != 0 because of the initial
300          * console open (without a tty) bumping it up to 1.
301          */
302         if((line->tty != NULL) && (line->tty->count == 1))
303                 line->tty = NULL;
304         if(line->count == 0)
305                 line_disable(line, -1);
306         up(&line->sem);
307 }
308
309 void close_lines(struct line *lines, int nlines)
310 {
311         int i;
312
313         for(i = 0; i < nlines; i++)
314                 close_chan(&lines[i].chan_list);
315 }
316
317 int line_setup(struct line *lines, int num, char *init, int all_allowed)
318 {
319         int i, n;
320         char *end;
321
322         if(*init == '=') n = -1;
323         else {
324                 n = simple_strtoul(init, &end, 0);
325                 if(*end != '='){
326                         printk(KERN_ERR "line_setup failed to parse \"%s\"\n", 
327                                init);
328                         return(0);
329                 }
330                 init = end;
331         }
332         init++;
333         if((n >= 0) && (n >= num)){
334                 printk("line_setup - %d out of range ((0 ... %d) allowed)\n",
335                        n, num);
336                 return(0);
337         }
338         else if(n >= 0){
339                 if(lines[n].count > 0){
340                         printk("line_setup - device %d is open\n", n);
341                         return(0);
342                 }
343                 if(lines[n].init_pri <= INIT_ONE){
344                         lines[n].init_pri = INIT_ONE;
345                         if(!strcmp(init, "none")) lines[n].valid = 0;
346                         else {
347                                 lines[n].init_str = init;
348                                 lines[n].valid = 1;
349                         }       
350                 }
351         }
352         else if(!all_allowed){
353                 printk("line_setup - can't configure all devices from "
354                        "mconsole\n");
355                 return(0);
356         }
357         else {
358                 for(i = 0; i < num; i++){
359                         if(lines[i].init_pri <= INIT_ALL){
360                                 lines[i].init_pri = INIT_ALL;
361                                 if(!strcmp(init, "none")) lines[i].valid = 0;
362                                 else {
363                                         lines[i].init_str = init;
364                                         lines[i].valid = 1;
365                                 }
366                         }
367                 }
368         }
369         return(1);
370 }
371
372 int line_config(struct line *lines, int num, char *str)
373 {
374         char *new = uml_strdup(str);
375
376         if(new == NULL){
377                 printk("line_config - uml_strdup failed\n");
378                 return(-ENOMEM);
379         }
380         return(!line_setup(lines, num, new, 0));
381 }
382
383 int line_get_config(char *name, struct line *lines, int num, char *str, 
384                     int size, char **error_out)
385 {
386         struct line *line;
387         char *end;
388         int dev, n = 0;
389
390         dev = simple_strtoul(name, &end, 0);
391         if((*end != '\0') || (end == name)){
392                 *error_out = "line_get_config failed to parse device number";
393                 return(0);
394         }
395
396         if((dev < 0) || (dev >= num)){
397                 *error_out = "device number of of range";
398                 return(0);
399         }
400
401         line = &lines[dev];
402
403         down(&line->sem);
404         if(!line->valid)
405                 CONFIG_CHUNK(str, size, n, "none", 1);
406         else if(line->count == 0)
407                 CONFIG_CHUNK(str, size, n, line->init_str, 1);
408         else n = chan_config_string(&line->chan_list, str, size, error_out);
409         up(&line->sem);
410
411         return(n);
412 }
413
414 int line_remove(struct line *lines, int num, char *str)
415 {
416         char config[sizeof("conxxxx=none\0")];
417
418         sprintf(config, "%s=none", str);
419         return(!line_setup(lines, num, config, 0));
420 }
421
422 int line_write_room(struct tty_struct *tty)
423 {
424         struct line *dev = tty->driver_data;
425
426         return(write_room(dev));
427 }
428
429 struct tty_driver *line_register_devfs(struct lines *set,
430                          struct line_driver *line_driver, 
431                          struct tty_operations *ops, struct line *lines,
432                          int nlines)
433 {
434         int err, i;
435         char *from, *to;
436         struct tty_driver *driver = alloc_tty_driver(nlines);
437
438         if (!driver)
439                 return NULL;
440
441         driver->driver_name = line_driver->name;
442         driver->name = line_driver->device_name;
443         driver->devfs_name = line_driver->devfs_name;
444         driver->major = line_driver->major;
445         driver->minor_start = line_driver->minor_start;
446         driver->type = line_driver->type;
447         driver->subtype = line_driver->subtype;
448         driver->flags = TTY_DRIVER_REAL_RAW;
449         driver->init_termios = tty_std_termios;
450         tty_set_operations(driver, ops);
451
452         if (tty_register_driver(driver))
453                 panic("line_register_devfs : Couldn't register driver\n");
454
455         from = line_driver->symlink_from;
456         to = line_driver->symlink_to;
457         err = devfs_mk_symlink(from, to);
458         if(err) printk("Symlink creation from /dev/%s to /dev/%s "
459                        "returned %d\n", from, to, err);
460
461         for(i = 0; i < nlines; i++){
462                 if(!lines[i].valid) 
463                         tty_unregister_device(driver, i);
464         }
465
466         mconsole_register_dev(&line_driver->mc);
467         return driver;
468 }
469
470 void lines_init(struct line *lines, int nlines)
471 {
472         struct line *line;
473         int i;
474
475         for(i = 0; i < nlines; i++){
476                 line = &lines[i];
477                 INIT_LIST_HEAD(&line->chan_list);
478                 sema_init(&line->sem, 1);
479                 if(line->init_str != NULL){
480                         line->init_str = uml_strdup(line->init_str);
481                         if(line->init_str == NULL)
482                                 printk("lines_init - uml_strdup returned "
483                                        "NULL\n");
484                 }
485         }
486 }
487
488 struct winch {
489         struct list_head list;
490         int fd;
491         int tty_fd;
492         int pid;
493         struct line *line;
494 };
495
496 irqreturn_t winch_interrupt(int irq, void *data, struct pt_regs *unused)
497 {
498         struct winch *winch = data;
499         struct tty_struct *tty;
500         int err;
501         char c;
502
503         if(winch->fd != -1){
504                 err = generic_read(winch->fd, &c, NULL);
505                 if(err < 0){
506                         if(err != -EAGAIN){
507                                 printk("winch_interrupt : read failed, "
508                                        "errno = %d\n", -err);
509                                 printk("fd %d is losing SIGWINCH support\n", 
510                                        winch->tty_fd);
511                                 return(IRQ_HANDLED);
512                         }
513                         goto out;
514                 }
515         }
516         tty = winch->line->tty;
517         if(tty != NULL){
518                 chan_window_size(&winch->line->chan_list, 
519                                  &tty->winsize.ws_row, 
520                                  &tty->winsize.ws_col);
521                 kill_pg(tty->pgrp, SIGWINCH, 1);
522         }
523  out:
524         if(winch->fd != -1)
525                 reactivate_fd(winch->fd, WINCH_IRQ);
526         return(IRQ_HANDLED);
527 }
528
529 DECLARE_MUTEX(winch_handler_sem);
530 LIST_HEAD(winch_handlers);
531
532 void register_winch_irq(int fd, int tty_fd, int pid, void *line)
533 {
534         struct winch *winch;
535
536         down(&winch_handler_sem);
537         winch = kmalloc(sizeof(*winch), GFP_KERNEL);
538         if(winch == NULL){
539                 printk("register_winch_irq - kmalloc failed\n");
540                 goto out;
541         }
542         *winch = ((struct winch) { .list        = LIST_HEAD_INIT(winch->list),
543                                    .fd          = fd,
544                                    .tty_fd      = tty_fd,
545                                    .pid         = pid,
546                                    .line        = line });
547         list_add(&winch->list, &winch_handlers);
548         if(um_request_irq(WINCH_IRQ, fd, IRQ_READ, winch_interrupt, 
549                           SA_INTERRUPT | SA_SHIRQ | SA_SAMPLE_RANDOM, 
550                           "winch", winch) < 0)
551                 printk("register_winch_irq - failed to register IRQ\n");
552  out:
553         up(&winch_handler_sem);
554 }
555
556 static void winch_cleanup(void)
557 {
558         struct list_head *ele;
559         struct winch *winch;
560
561         list_for_each(ele, &winch_handlers){
562                 winch = list_entry(ele, struct winch, list);
563                 if(winch->fd != -1){
564                         deactivate_fd(winch->fd, WINCH_IRQ);
565                         os_close_file(winch->fd);
566                 }
567                 if(winch->pid != -1) 
568                         os_kill_process(winch->pid, 1);
569         }
570 }
571
572 __uml_exitcall(winch_cleanup);
573
574 char *add_xterm_umid(char *base)
575 {
576         char *umid, *title;
577         int len;
578
579         umid = get_umid(1);
580         if(umid == NULL) return(base);
581         
582         len = strlen(base) + strlen(" ()") + strlen(umid) + 1;
583         title = kmalloc(len, GFP_KERNEL);
584         if(title == NULL){
585                 printk("Failed to allocate buffer for xterm title\n");
586                 return(base);
587         }
588
589         snprintf(title, len, "%s (%s)", base, umid);
590         return(title);
591 }
592
593 /*
594  * Overrides for Emacs so that we follow Linus's tabbing style.
595  * Emacs will notice this stuff at the end of the file and automatically
596  * adjust the settings for this buffer only.  This must remain at the end
597  * of the file.
598  * ---------------------------------------------------------------------------
599  * Local variables:
600  * c-file-style: "linux"
601  * End:
602  */