Merge git://git.infradead.org/hdrinstall-2.6
[linux-2.6] / drivers / isdn / gigaset / interface.c
1 /*
2  * interface to user space for the gigaset driver
3  *
4  * Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de>
5  *
6  * =====================================================================
7  *    This program is free software; you can redistribute it and/or
8  *    modify it under the terms of the GNU General Public License as
9  *    published by the Free Software Foundation; either version 2 of
10  *    the License, or (at your option) any later version.
11  * =====================================================================
12  */
13
14 #include "gigaset.h"
15 #include <linux/gigaset_dev.h>
16 #include <linux/tty.h>
17 #include <linux/tty_flip.h>
18
19 /*** our ioctls ***/
20
21 static int if_lock(struct cardstate *cs, int *arg)
22 {
23         int cmd = *arg;
24
25         gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
26
27         if (cmd > 1)
28                 return -EINVAL;
29
30         if (cmd < 0) {
31                 *arg = atomic_read(&cs->mstate) == MS_LOCKED; //FIXME remove?
32                 return 0;
33         }
34
35         if (!cmd && atomic_read(&cs->mstate) == MS_LOCKED
36             && cs->connected) {
37                 cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS);
38                 cs->ops->baud_rate(cs, B115200);
39                 cs->ops->set_line_ctrl(cs, CS8);
40                 cs->control_state = TIOCM_DTR|TIOCM_RTS;
41         }
42
43         cs->waiting = 1;
44         if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
45                                NULL, cmd, NULL)) {
46                 cs->waiting = 0;
47                 return -ENOMEM;
48         }
49
50         gig_dbg(DEBUG_CMD, "scheduling IF_LOCK");
51         gigaset_schedule_event(cs);
52
53         wait_event(cs->waitqueue, !cs->waiting);
54
55         if (cs->cmd_result >= 0) {
56                 *arg = cs->cmd_result;
57                 return 0;
58         }
59
60         return cs->cmd_result;
61 }
62
63 static int if_version(struct cardstate *cs, unsigned arg[4])
64 {
65         static const unsigned version[4] = GIG_VERSION;
66         static const unsigned compat[4] = GIG_COMPAT;
67         unsigned cmd = arg[0];
68
69         gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
70
71         switch (cmd) {
72         case GIGVER_DRIVER:
73                 memcpy(arg, version, sizeof version);
74                 return 0;
75         case GIGVER_COMPAT:
76                 memcpy(arg, compat, sizeof compat);
77                 return 0;
78         case GIGVER_FWBASE:
79                 cs->waiting = 1;
80                 if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
81                                        NULL, 0, arg)) {
82                         cs->waiting = 0;
83                         return -ENOMEM;
84                 }
85
86                 gig_dbg(DEBUG_CMD, "scheduling IF_VER");
87                 gigaset_schedule_event(cs);
88
89                 wait_event(cs->waitqueue, !cs->waiting);
90
91                 if (cs->cmd_result >= 0)
92                         return 0;
93
94                 return cs->cmd_result;
95         default:
96                 return -EINVAL;
97         }
98 }
99
100 static int if_config(struct cardstate *cs, int *arg)
101 {
102         gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
103
104         if (*arg != 1)
105                 return -EINVAL;
106
107         if (atomic_read(&cs->mstate) != MS_LOCKED)
108                 return -EBUSY;
109
110         if (!cs->connected) {
111                 err("not connected!");
112                 return -ENODEV;
113         }
114
115         *arg = 0;
116         return gigaset_enterconfigmode(cs);
117 }
118
119 /*** the terminal driver ***/
120 /* stolen from usbserial and some other tty drivers */
121
122 static int  if_open(struct tty_struct *tty, struct file *filp);
123 static void if_close(struct tty_struct *tty, struct file *filp);
124 static int  if_ioctl(struct tty_struct *tty, struct file *file,
125                      unsigned int cmd, unsigned long arg);
126 static int  if_write_room(struct tty_struct *tty);
127 static int  if_chars_in_buffer(struct tty_struct *tty);
128 static void if_throttle(struct tty_struct *tty);
129 static void if_unthrottle(struct tty_struct *tty);
130 static void if_set_termios(struct tty_struct *tty, struct termios *old);
131 static int  if_tiocmget(struct tty_struct *tty, struct file *file);
132 static int  if_tiocmset(struct tty_struct *tty, struct file *file,
133                         unsigned int set, unsigned int clear);
134 static int  if_write(struct tty_struct *tty,
135                      const unsigned char *buf, int count);
136
137 static struct tty_operations if_ops = {
138         .open =                 if_open,
139         .close =                if_close,
140         .ioctl =                if_ioctl,
141         .write =                if_write,
142         .write_room =           if_write_room,
143         .chars_in_buffer =      if_chars_in_buffer,
144         .set_termios =          if_set_termios,
145         .throttle =             if_throttle,
146         .unthrottle =           if_unthrottle,
147 #if 0
148         .break_ctl =            serial_break,
149 #endif
150         .tiocmget =             if_tiocmget,
151         .tiocmset =             if_tiocmset,
152 };
153
154 static int if_open(struct tty_struct *tty, struct file *filp)
155 {
156         struct cardstate *cs;
157         unsigned long flags;
158
159         gig_dbg(DEBUG_IF, "%d+%d: %s()",
160                 tty->driver->minor_start, tty->index, __func__);
161
162         tty->driver_data = NULL;
163
164         cs = gigaset_get_cs_by_tty(tty);
165         if (!cs)
166                 return -ENODEV;
167
168         if (mutex_lock_interruptible(&cs->mutex))
169                 return -ERESTARTSYS; // FIXME -EINTR?
170         tty->driver_data = cs;
171
172         ++cs->open_count;
173
174         if (cs->open_count == 1) {
175                 spin_lock_irqsave(&cs->lock, flags);
176                 cs->tty = tty;
177                 spin_unlock_irqrestore(&cs->lock, flags);
178                 tty->low_latency = 1; //FIXME test
179         }
180
181         mutex_unlock(&cs->mutex);
182         return 0;
183 }
184
185 static void if_close(struct tty_struct *tty, struct file *filp)
186 {
187         struct cardstate *cs;
188         unsigned long flags;
189
190         cs = (struct cardstate *) tty->driver_data;
191         if (!cs) {
192                 err("cs==NULL in %s", __func__);
193                 return;
194         }
195
196         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
197
198         mutex_lock(&cs->mutex);
199
200         if (!cs->open_count)
201                 warn("%s: device not opened", __func__);
202         else {
203                 if (!--cs->open_count) {
204                         spin_lock_irqsave(&cs->lock, flags);
205                         cs->tty = NULL;
206                         spin_unlock_irqrestore(&cs->lock, flags);
207                 }
208         }
209
210         mutex_unlock(&cs->mutex);
211 }
212
213 static int if_ioctl(struct tty_struct *tty, struct file *file,
214                     unsigned int cmd, unsigned long arg)
215 {
216         struct cardstate *cs;
217         int retval = -ENODEV;
218         int int_arg;
219         unsigned char buf[6];
220         unsigned version[4];
221
222         cs = (struct cardstate *) tty->driver_data;
223         if (!cs) {
224                 err("cs==NULL in %s", __func__);
225                 return -ENODEV;
226         }
227
228         gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
229
230         if (mutex_lock_interruptible(&cs->mutex))
231                 return -ERESTARTSYS; // FIXME -EINTR?
232
233         if (!cs->open_count)
234                 warn("%s: device not opened", __func__);
235         else {
236                 retval = 0;
237                 switch (cmd) {
238                 case GIGASET_REDIR:
239                         retval = get_user(int_arg, (int __user *) arg);
240                         if (retval >= 0)
241                                 retval = if_lock(cs, &int_arg);
242                         if (retval >= 0)
243                                 retval = put_user(int_arg, (int __user *) arg);
244                         break;
245                 case GIGASET_CONFIG:
246                         retval = get_user(int_arg, (int __user *) arg);
247                         if (retval >= 0)
248                                 retval = if_config(cs, &int_arg);
249                         if (retval >= 0)
250                                 retval = put_user(int_arg, (int __user *) arg);
251                         break;
252                 case GIGASET_BRKCHARS:
253                         //FIXME test if MS_LOCKED
254                         if (!cs->connected) {
255                                 gig_dbg(DEBUG_ANY,
256                                     "can't communicate with unplugged device");
257                                 retval = -ENODEV;
258                                 break;
259                         }
260                         retval = copy_from_user(&buf,
261                                         (const unsigned char __user *) arg, 6)
262                                 ? -EFAULT : 0;
263                         if (retval >= 0) {
264                                 gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
265                                                 6, (const unsigned char *) arg);
266                                 retval = cs->ops->brkchars(cs, buf);
267                         }
268                         break;
269                 case GIGASET_VERSION:
270                         retval = copy_from_user(version,
271                                         (unsigned __user *) arg, sizeof version)
272                                 ? -EFAULT : 0;
273                         if (retval >= 0)
274                                 retval = if_version(cs, version);
275                         if (retval >= 0)
276                                 retval = copy_to_user((unsigned __user *) arg,
277                                                       version, sizeof version)
278                                         ? -EFAULT : 0;
279                         break;
280                 default:
281                         gig_dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x",
282                                 __func__, cmd);
283                         retval = -ENOIOCTLCMD;
284                 }
285         }
286
287         mutex_unlock(&cs->mutex);
288
289         return retval;
290 }
291
292 static int if_tiocmget(struct tty_struct *tty, struct file *file)
293 {
294         struct cardstate *cs;
295         int retval;
296
297         cs = (struct cardstate *) tty->driver_data;
298         if (!cs) {
299                 err("cs==NULL in %s", __func__);
300                 return -ENODEV;
301         }
302
303         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
304
305         if (mutex_lock_interruptible(&cs->mutex))
306                 return -ERESTARTSYS; // FIXME -EINTR?
307
308         // FIXME read from device?
309         retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR);
310
311         mutex_unlock(&cs->mutex);
312
313         return retval;
314 }
315
316 static int if_tiocmset(struct tty_struct *tty, struct file *file,
317                        unsigned int set, unsigned int clear)
318 {
319         struct cardstate *cs;
320         int retval;
321         unsigned mc;
322
323         cs = (struct cardstate *) tty->driver_data;
324         if (!cs) {
325                 err("cs==NULL in %s", __func__);
326                 return -ENODEV;
327         }
328
329         gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
330                 cs->minor_index, __func__, set, clear);
331
332         if (mutex_lock_interruptible(&cs->mutex))
333                 return -ERESTARTSYS; // FIXME -EINTR?
334
335         if (!cs->connected) {
336                 gig_dbg(DEBUG_ANY, "can't communicate with unplugged device");
337                 retval = -ENODEV;
338         } else {
339                 mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR);
340                 retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
341                 cs->control_state = mc;
342         }
343
344         mutex_unlock(&cs->mutex);
345
346         return retval;
347 }
348
349 static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
350 {
351         struct cardstate *cs;
352         int retval = -ENODEV;
353
354         cs = (struct cardstate *) tty->driver_data;
355         if (!cs) {
356                 err("cs==NULL in %s", __func__);
357                 return -ENODEV;
358         }
359
360         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
361
362         if (mutex_lock_interruptible(&cs->mutex))
363                 return -ERESTARTSYS; // FIXME -EINTR?
364
365         if (!cs->open_count)
366                 warn("%s: device not opened", __func__);
367         else if (atomic_read(&cs->mstate) != MS_LOCKED) {
368                 warn("can't write to unlocked device");
369                 retval = -EBUSY;
370         } else if (!cs->connected) {
371                 gig_dbg(DEBUG_ANY, "can't write to unplugged device");
372                 retval = -EBUSY; //FIXME
373         } else {
374                 retval = cs->ops->write_cmd(cs, buf, count,
375                                             &cs->if_wake_tasklet);
376         }
377
378         mutex_unlock(&cs->mutex);
379
380         return retval;
381 }
382
383 static int if_write_room(struct tty_struct *tty)
384 {
385         struct cardstate *cs;
386         int retval = -ENODEV;
387
388         cs = (struct cardstate *) tty->driver_data;
389         if (!cs) {
390                 err("cs==NULL in %s", __func__);
391                 return -ENODEV;
392         }
393
394         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
395
396         if (mutex_lock_interruptible(&cs->mutex))
397                 return -ERESTARTSYS; // FIXME -EINTR?
398
399         if (!cs->open_count)
400                 warn("%s: device not opened", __func__);
401         else if (atomic_read(&cs->mstate) != MS_LOCKED) {
402                 warn("can't write to unlocked device");
403                 retval = -EBUSY; //FIXME
404         } else if (!cs->connected) {
405                 gig_dbg(DEBUG_ANY, "can't write to unplugged device");
406                 retval = -EBUSY; //FIXME
407         } else
408                 retval = cs->ops->write_room(cs);
409
410         mutex_unlock(&cs->mutex);
411
412         return retval;
413 }
414
415 static int if_chars_in_buffer(struct tty_struct *tty)
416 {
417         struct cardstate *cs;
418         int retval = -ENODEV;
419
420         cs = (struct cardstate *) tty->driver_data;
421         if (!cs) {
422                 err("cs==NULL in %s", __func__);
423                 return -ENODEV;
424         }
425
426         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
427
428         if (mutex_lock_interruptible(&cs->mutex))
429                 return -ERESTARTSYS; // FIXME -EINTR?
430
431         if (!cs->open_count)
432                 warn("%s: device not opened", __func__);
433         else if (atomic_read(&cs->mstate) != MS_LOCKED) {
434                 warn("can't write to unlocked device");
435                 retval = -EBUSY;
436         } else if (!cs->connected) {
437                 gig_dbg(DEBUG_ANY, "can't write to unplugged device");
438                 retval = -EBUSY; //FIXME
439         } else
440                 retval = cs->ops->chars_in_buffer(cs);
441
442         mutex_unlock(&cs->mutex);
443
444         return retval;
445 }
446
447 static void if_throttle(struct tty_struct *tty)
448 {
449         struct cardstate *cs;
450
451         cs = (struct cardstate *) tty->driver_data;
452         if (!cs) {
453                 err("cs==NULL in %s", __func__);
454                 return;
455         }
456
457         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
458
459         mutex_lock(&cs->mutex);
460
461         if (!cs->open_count)
462                 warn("%s: device not opened", __func__);
463         else {
464                 //FIXME
465         }
466
467         mutex_unlock(&cs->mutex);
468 }
469
470 static void if_unthrottle(struct tty_struct *tty)
471 {
472         struct cardstate *cs;
473
474         cs = (struct cardstate *) tty->driver_data;
475         if (!cs) {
476                 err("cs==NULL in %s", __func__);
477                 return;
478         }
479
480         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
481
482         mutex_lock(&cs->mutex);
483
484         if (!cs->open_count)
485                 warn("%s: device not opened", __func__);
486         else {
487                 //FIXME
488         }
489
490         mutex_unlock(&cs->mutex);
491 }
492
493 static void if_set_termios(struct tty_struct *tty, struct termios *old)
494 {
495         struct cardstate *cs;
496         unsigned int iflag;
497         unsigned int cflag;
498         unsigned int old_cflag;
499         unsigned int control_state, new_state;
500
501         cs = (struct cardstate *) tty->driver_data;
502         if (!cs) {
503                 err("cs==NULL in %s", __func__);
504                 return;
505         }
506
507         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
508
509         mutex_lock(&cs->mutex);
510
511         if (!cs->open_count) {
512                 warn("%s: device not opened", __func__);
513                 goto out;
514         }
515
516         if (!cs->connected) {
517                 gig_dbg(DEBUG_ANY, "can't communicate with unplugged device");
518                 goto out;
519         }
520
521         // stolen from mct_u232.c
522         iflag = tty->termios->c_iflag;
523         cflag = tty->termios->c_cflag;
524         old_cflag = old ? old->c_cflag : cflag; //FIXME?
525         gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
526                 cs->minor_index, iflag, cflag, old_cflag);
527
528         /* get a local copy of the current port settings */
529         control_state = cs->control_state;
530
531         /*
532          * Update baud rate.
533          * Do not attempt to cache old rates and skip settings,
534          * disconnects screw such tricks up completely.
535          * Premature optimization is the root of all evil.
536          */
537
538         /* reassert DTR and (maybe) RTS on transition from B0 */
539         if ((old_cflag & CBAUD) == B0) {
540                 new_state = control_state | TIOCM_DTR;
541                 /* don't set RTS if using hardware flow control */
542                 if (!(old_cflag & CRTSCTS))
543                         new_state |= TIOCM_RTS;
544                 gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
545                         cs->minor_index,
546                         (new_state & TIOCM_RTS) ? " only" : "/RTS");
547                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
548                 control_state = new_state;
549         }
550
551         cs->ops->baud_rate(cs, cflag & CBAUD);
552
553         if ((cflag & CBAUD) == B0) {
554                 /* Drop RTS and DTR */
555                 gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
556                 new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
557                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
558                 control_state = new_state;
559         }
560
561         /*
562          * Update line control register (LCR)
563          */
564
565         cs->ops->set_line_ctrl(cs, cflag);
566
567 #if 0
568         //FIXME this hangs M101 [ts 2005-03-09]
569         //FIXME do we need this?
570         /*
571          * Set flow control: well, I do not really now how to handle DTR/RTS.
572          * Just do what we have seen with SniffUSB on Win98.
573          */
574         /* Drop DTR/RTS if no flow control otherwise assert */
575         gig_dbg(DEBUG_IF, "%u: control_state %x",
576                 cs->minor_index, control_state);
577         new_state = control_state;
578         if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
579                 new_state |= TIOCM_DTR | TIOCM_RTS;
580         else
581                 new_state &= ~(TIOCM_DTR | TIOCM_RTS);
582         if (new_state != control_state) {
583                 gig_dbg(DEBUG_IF, "%u: new_state %x",
584                         cs->minor_index, new_state);
585                 gigaset_set_modem_ctrl(cs, control_state, new_state);
586                 control_state = new_state;
587         }
588 #endif
589
590         /* save off the modified port settings */
591         cs->control_state = control_state;
592
593 out:
594         mutex_unlock(&cs->mutex);
595 }
596
597
598 /* wakeup tasklet for the write operation */
599 static void if_wake(unsigned long data)
600 {
601         struct cardstate *cs = (struct cardstate *) data;
602         struct tty_struct *tty;
603
604         tty = cs->tty;
605         if (!tty)
606                 return;
607
608         if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
609             tty->ldisc.write_wakeup) {
610                 gig_dbg(DEBUG_IF, "write wakeup call");
611                 tty->ldisc.write_wakeup(tty);
612         }
613
614         wake_up_interruptible(&tty->write_wait);
615 }
616
617 /*** interface to common ***/
618
619 void gigaset_if_init(struct cardstate *cs)
620 {
621         struct gigaset_driver *drv;
622
623         drv = cs->driver;
624         if (!drv->have_tty)
625                 return;
626
627         tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs);
628         cs->class = tty_register_device(drv->tty, cs->minor_index, NULL);
629
630         if (!IS_ERR(cs->class))
631                 class_set_devdata(cs->class, cs);
632         else {
633                 warn("could not register device to the tty subsystem");
634                 cs->class = NULL;
635         }
636 }
637
638 void gigaset_if_free(struct cardstate *cs)
639 {
640         struct gigaset_driver *drv;
641
642         drv = cs->driver;
643         if (!drv->have_tty)
644                 return;
645
646         tasklet_disable(&cs->if_wake_tasklet);
647         tasklet_kill(&cs->if_wake_tasklet);
648         cs->class = NULL;
649         tty_unregister_device(drv->tty, cs->minor_index);
650 }
651
652 void gigaset_if_receive(struct cardstate *cs,
653                         unsigned char *buffer, size_t len)
654 {
655         unsigned long flags;
656         struct tty_struct *tty;
657
658         spin_lock_irqsave(&cs->lock, flags);
659         if ((tty = cs->tty) == NULL)
660                 gig_dbg(DEBUG_ANY, "receive on closed device");
661         else {
662                 tty_buffer_request_room(tty, len);
663                 tty_insert_flip_string(tty, buffer, len);
664                 tty_flip_buffer_push(tty);
665         }
666         spin_unlock_irqrestore(&cs->lock, flags);
667 }
668 EXPORT_SYMBOL_GPL(gigaset_if_receive);
669
670 /* gigaset_if_initdriver
671  * Initialize tty interface.
672  * parameters:
673  *      drv             Driver
674  *      procname        Name of the driver (e.g. for /proc/tty/drivers)
675  *      devname         Name of the device files (prefix without minor number)
676  */
677 void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
678                            const char *devname)
679 {
680         unsigned minors = drv->minors;
681         int ret;
682         struct tty_driver *tty;
683
684         drv->have_tty = 0;
685
686         if ((drv->tty = alloc_tty_driver(minors)) == NULL)
687                 goto enomem;
688         tty = drv->tty;
689
690         tty->magic =            TTY_DRIVER_MAGIC,
691         tty->major =            GIG_MAJOR,
692         tty->type =             TTY_DRIVER_TYPE_SERIAL,
693         tty->subtype =          SERIAL_TYPE_NORMAL,
694         tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
695
696         tty->driver_name =      procname;
697         tty->name =             devname;
698         tty->minor_start =      drv->minor;
699         tty->num =              drv->minors;
700
701         tty->owner =            THIS_MODULE;
702
703         tty->init_termios          = tty_std_termios; //FIXME
704         tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME
705         tty_set_operations(tty, &if_ops);
706
707         ret = tty_register_driver(tty);
708         if (ret < 0) {
709                 warn("failed to register tty driver (error %d)", ret);
710                 goto error;
711         }
712         gig_dbg(DEBUG_IF, "tty driver initialized");
713         drv->have_tty = 1;
714         return;
715
716 enomem:
717         warn("could not allocate tty structures");
718 error:
719         if (drv->tty)
720                 put_tty_driver(drv->tty);
721 }
722
723 void gigaset_if_freedriver(struct gigaset_driver *drv)
724 {
725         if (!drv->have_tty)
726                 return;
727
728         drv->have_tty = 0;
729         tty_unregister_driver(drv->tty);
730         put_tty_driver(drv->tty);
731 }