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