]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/isdn/gigaset/interface.c
TTY: isdn/gigaset, do not set tty->driver_data to NULL
[karo-tx-linux.git] / 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_flip.h>
17 #include <linux/module.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 = cs->mstate == MS_LOCKED;
32                 return 0;
33         }
34
35         if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
36                 cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS);
37                 cs->ops->baud_rate(cs, B115200);
38                 cs->ops->set_line_ctrl(cs, CS8);
39                 cs->control_state = TIOCM_DTR|TIOCM_RTS;
40         }
41
42         cs->waiting = 1;
43         if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
44                                NULL, cmd, NULL)) {
45                 cs->waiting = 0;
46                 return -ENOMEM;
47         }
48         gigaset_schedule_event(cs);
49
50         wait_event(cs->waitqueue, !cs->waiting);
51
52         if (cs->cmd_result >= 0) {
53                 *arg = cs->cmd_result;
54                 return 0;
55         }
56
57         return cs->cmd_result;
58 }
59
60 static int if_version(struct cardstate *cs, unsigned arg[4])
61 {
62         static const unsigned version[4] = GIG_VERSION;
63         static const unsigned compat[4] = GIG_COMPAT;
64         unsigned cmd = arg[0];
65
66         gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
67
68         switch (cmd) {
69         case GIGVER_DRIVER:
70                 memcpy(arg, version, sizeof version);
71                 return 0;
72         case GIGVER_COMPAT:
73                 memcpy(arg, compat, sizeof compat);
74                 return 0;
75         case GIGVER_FWBASE:
76                 cs->waiting = 1;
77                 if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
78                                        NULL, 0, arg)) {
79                         cs->waiting = 0;
80                         return -ENOMEM;
81                 }
82                 gigaset_schedule_event(cs);
83
84                 wait_event(cs->waitqueue, !cs->waiting);
85
86                 if (cs->cmd_result >= 0)
87                         return 0;
88
89                 return cs->cmd_result;
90         default:
91                 return -EINVAL;
92         }
93 }
94
95 static int if_config(struct cardstate *cs, int *arg)
96 {
97         gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
98
99         if (*arg != 1)
100                 return -EINVAL;
101
102         if (cs->mstate != MS_LOCKED)
103                 return -EBUSY;
104
105         if (!cs->connected) {
106                 pr_err("%s: not connected\n", __func__);
107                 return -ENODEV;
108         }
109
110         *arg = 0;
111         return gigaset_enterconfigmode(cs);
112 }
113
114 /*** the terminal driver ***/
115 /* stolen from usbserial and some other tty drivers */
116
117 static int  if_open(struct tty_struct *tty, struct file *filp);
118 static void if_close(struct tty_struct *tty, struct file *filp);
119 static int  if_ioctl(struct tty_struct *tty,
120                      unsigned int cmd, unsigned long arg);
121 static int  if_write_room(struct tty_struct *tty);
122 static int  if_chars_in_buffer(struct tty_struct *tty);
123 static void if_throttle(struct tty_struct *tty);
124 static void if_unthrottle(struct tty_struct *tty);
125 static void if_set_termios(struct tty_struct *tty, struct ktermios *old);
126 static int  if_tiocmget(struct tty_struct *tty);
127 static int  if_tiocmset(struct tty_struct *tty,
128                         unsigned int set, unsigned int clear);
129 static int  if_write(struct tty_struct *tty,
130                      const unsigned char *buf, int count);
131
132 static const struct tty_operations if_ops = {
133         .open =                 if_open,
134         .close =                if_close,
135         .ioctl =                if_ioctl,
136         .write =                if_write,
137         .write_room =           if_write_room,
138         .chars_in_buffer =      if_chars_in_buffer,
139         .set_termios =          if_set_termios,
140         .throttle =             if_throttle,
141         .unthrottle =           if_unthrottle,
142         .tiocmget =             if_tiocmget,
143         .tiocmset =             if_tiocmset,
144 };
145
146 static int if_open(struct tty_struct *tty, struct file *filp)
147 {
148         struct cardstate *cs;
149         unsigned long flags;
150
151         gig_dbg(DEBUG_IF, "%d+%d: %s()",
152                 tty->driver->minor_start, tty->index, __func__);
153
154         cs = gigaset_get_cs_by_tty(tty);
155         if (!cs || !try_module_get(cs->driver->owner))
156                 return -ENODEV;
157
158         if (mutex_lock_interruptible(&cs->mutex)) {
159                 module_put(cs->driver->owner);
160                 return -ERESTARTSYS;
161         }
162         tty->driver_data = cs;
163
164         ++cs->open_count;
165
166         if (cs->open_count == 1) {
167                 spin_lock_irqsave(&cs->lock, flags);
168                 cs->tty = tty;
169                 spin_unlock_irqrestore(&cs->lock, flags);
170                 tty->low_latency = 1;
171         }
172
173         mutex_unlock(&cs->mutex);
174         return 0;
175 }
176
177 static void if_close(struct tty_struct *tty, struct file *filp)
178 {
179         struct cardstate *cs = tty->driver_data;
180         unsigned long flags;
181
182         if (!cs) { /* happens if we didn't find cs in open */
183                 printk(KERN_DEBUG "%s: no cardstate\n", __func__);
184                 return;
185         }
186
187         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
188
189         mutex_lock(&cs->mutex);
190
191         if (!cs->connected)
192                 gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
193         else if (!cs->open_count)
194                 dev_warn(cs->dev, "%s: device not opened\n", __func__);
195         else {
196                 if (!--cs->open_count) {
197                         spin_lock_irqsave(&cs->lock, flags);
198                         cs->tty = NULL;
199                         spin_unlock_irqrestore(&cs->lock, flags);
200                 }
201         }
202
203         mutex_unlock(&cs->mutex);
204
205         module_put(cs->driver->owner);
206 }
207
208 static int if_ioctl(struct tty_struct *tty,
209                     unsigned int cmd, unsigned long arg)
210 {
211         struct cardstate *cs = tty->driver_data;
212         int retval = -ENODEV;
213         int int_arg;
214         unsigned char buf[6];
215         unsigned version[4];
216
217         gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
218
219         if (mutex_lock_interruptible(&cs->mutex))
220                 return -ERESTARTSYS;
221
222         if (!cs->connected) {
223                 gig_dbg(DEBUG_IF, "not connected");
224                 retval = -ENODEV;
225         } else {
226                 retval = 0;
227                 switch (cmd) {
228                 case GIGASET_REDIR:
229                         retval = get_user(int_arg, (int __user *) arg);
230                         if (retval >= 0)
231                                 retval = if_lock(cs, &int_arg);
232                         if (retval >= 0)
233                                 retval = put_user(int_arg, (int __user *) arg);
234                         break;
235                 case GIGASET_CONFIG:
236                         retval = get_user(int_arg, (int __user *) arg);
237                         if (retval >= 0)
238                                 retval = if_config(cs, &int_arg);
239                         if (retval >= 0)
240                                 retval = put_user(int_arg, (int __user *) arg);
241                         break;
242                 case GIGASET_BRKCHARS:
243                         retval = copy_from_user(&buf,
244                                         (const unsigned char __user *) arg, 6)
245                                 ? -EFAULT : 0;
246                         if (retval >= 0) {
247                                 gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
248                                                 6, (const unsigned char *) arg);
249                                 retval = cs->ops->brkchars(cs, buf);
250                         }
251                         break;
252                 case GIGASET_VERSION:
253                         retval = copy_from_user(version,
254                                         (unsigned __user *) arg, sizeof version)
255                                 ? -EFAULT : 0;
256                         if (retval >= 0)
257                                 retval = if_version(cs, version);
258                         if (retval >= 0)
259                                 retval = copy_to_user((unsigned __user *) arg,
260                                                       version, sizeof version)
261                                         ? -EFAULT : 0;
262                         break;
263                 default:
264                         gig_dbg(DEBUG_IF, "%s: arg not supported - 0x%04x",
265                                 __func__, cmd);
266                         retval = -ENOIOCTLCMD;
267                 }
268         }
269
270         mutex_unlock(&cs->mutex);
271
272         return retval;
273 }
274
275 static int if_tiocmget(struct tty_struct *tty)
276 {
277         struct cardstate *cs = tty->driver_data;
278         int retval;
279
280         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
281
282         if (mutex_lock_interruptible(&cs->mutex))
283                 return -ERESTARTSYS;
284
285         retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR);
286
287         mutex_unlock(&cs->mutex);
288
289         return retval;
290 }
291
292 static int if_tiocmset(struct tty_struct *tty,
293                        unsigned int set, unsigned int clear)
294 {
295         struct cardstate *cs = tty->driver_data;
296         int retval;
297         unsigned mc;
298
299         gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
300                 cs->minor_index, __func__, set, clear);
301
302         if (mutex_lock_interruptible(&cs->mutex))
303                 return -ERESTARTSYS;
304
305         if (!cs->connected) {
306                 gig_dbg(DEBUG_IF, "not connected");
307                 retval = -ENODEV;
308         } else {
309                 mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR);
310                 retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
311                 cs->control_state = mc;
312         }
313
314         mutex_unlock(&cs->mutex);
315
316         return retval;
317 }
318
319 static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
320 {
321         struct cardstate *cs = tty->driver_data;
322         struct cmdbuf_t *cb;
323         int retval;
324
325         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
326
327         if (mutex_lock_interruptible(&cs->mutex))
328                 return -ERESTARTSYS;
329
330         if (!cs->connected) {
331                 gig_dbg(DEBUG_IF, "not connected");
332                 retval = -ENODEV;
333                 goto done;
334         }
335         if (cs->mstate != MS_LOCKED) {
336                 dev_warn(cs->dev, "can't write to unlocked device\n");
337                 retval = -EBUSY;
338                 goto done;
339         }
340         if (count <= 0) {
341                 /* nothing to do */
342                 retval = 0;
343                 goto done;
344         }
345
346         cb = kmalloc(sizeof(struct cmdbuf_t) + count, GFP_KERNEL);
347         if (!cb) {
348                 dev_err(cs->dev, "%s: out of memory\n", __func__);
349                 retval = -ENOMEM;
350                 goto done;
351         }
352
353         memcpy(cb->buf, buf, count);
354         cb->len = count;
355         cb->offset = 0;
356         cb->next = NULL;
357         cb->wake_tasklet = &cs->if_wake_tasklet;
358         retval = cs->ops->write_cmd(cs, cb);
359 done:
360         mutex_unlock(&cs->mutex);
361         return retval;
362 }
363
364 static int if_write_room(struct tty_struct *tty)
365 {
366         struct cardstate *cs = tty->driver_data;
367         int retval = -ENODEV;
368
369         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
370
371         if (mutex_lock_interruptible(&cs->mutex))
372                 return -ERESTARTSYS;
373
374         if (!cs->connected) {
375                 gig_dbg(DEBUG_IF, "not connected");
376                 retval = -ENODEV;
377         } else if (cs->mstate != MS_LOCKED) {
378                 dev_warn(cs->dev, "can't write to unlocked device\n");
379                 retval = -EBUSY;
380         } else
381                 retval = cs->ops->write_room(cs);
382
383         mutex_unlock(&cs->mutex);
384
385         return retval;
386 }
387
388 static int if_chars_in_buffer(struct tty_struct *tty)
389 {
390         struct cardstate *cs = tty->driver_data;
391         int retval = 0;
392
393         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
394
395         mutex_lock(&cs->mutex);
396
397         if (!cs->connected)
398                 gig_dbg(DEBUG_IF, "not connected");
399         else if (cs->mstate != MS_LOCKED)
400                 dev_warn(cs->dev, "can't write to unlocked device\n");
401         else
402                 retval = cs->ops->chars_in_buffer(cs);
403
404         mutex_unlock(&cs->mutex);
405
406         return retval;
407 }
408
409 static void if_throttle(struct tty_struct *tty)
410 {
411         struct cardstate *cs = tty->driver_data;
412
413         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
414
415         mutex_lock(&cs->mutex);
416
417         if (!cs->connected)
418                 gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
419         else
420                 gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
421
422         mutex_unlock(&cs->mutex);
423 }
424
425 static void if_unthrottle(struct tty_struct *tty)
426 {
427         struct cardstate *cs = tty->driver_data;
428
429         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
430
431         mutex_lock(&cs->mutex);
432
433         if (!cs->connected)
434                 gig_dbg(DEBUG_IF, "not connected");     /* nothing to do */
435         else
436                 gig_dbg(DEBUG_IF, "%s: not implemented\n", __func__);
437
438         mutex_unlock(&cs->mutex);
439 }
440
441 static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
442 {
443         struct cardstate *cs = tty->driver_data;
444         unsigned int iflag;
445         unsigned int cflag;
446         unsigned int old_cflag;
447         unsigned int control_state, new_state;
448
449         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
450
451         mutex_lock(&cs->mutex);
452
453         if (!cs->connected) {
454                 gig_dbg(DEBUG_IF, "not connected");
455                 goto out;
456         }
457
458         iflag = tty->termios->c_iflag;
459         cflag = tty->termios->c_cflag;
460         old_cflag = old ? old->c_cflag : cflag;
461         gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
462                 cs->minor_index, iflag, cflag, old_cflag);
463
464         /* get a local copy of the current port settings */
465         control_state = cs->control_state;
466
467         /*
468          * Update baud rate.
469          * Do not attempt to cache old rates and skip settings,
470          * disconnects screw such tricks up completely.
471          * Premature optimization is the root of all evil.
472          */
473
474         /* reassert DTR and (maybe) RTS on transition from B0 */
475         if ((old_cflag & CBAUD) == B0) {
476                 new_state = control_state | TIOCM_DTR;
477                 /* don't set RTS if using hardware flow control */
478                 if (!(old_cflag & CRTSCTS))
479                         new_state |= TIOCM_RTS;
480                 gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
481                         cs->minor_index,
482                         (new_state & TIOCM_RTS) ? " only" : "/RTS");
483                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
484                 control_state = new_state;
485         }
486
487         cs->ops->baud_rate(cs, cflag & CBAUD);
488
489         if ((cflag & CBAUD) == B0) {
490                 /* Drop RTS and DTR */
491                 gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
492                 new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
493                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
494                 control_state = new_state;
495         }
496
497         /*
498          * Update line control register (LCR)
499          */
500
501         cs->ops->set_line_ctrl(cs, cflag);
502
503         /* save off the modified port settings */
504         cs->control_state = control_state;
505
506 out:
507         mutex_unlock(&cs->mutex);
508 }
509
510
511 /* wakeup tasklet for the write operation */
512 static void if_wake(unsigned long data)
513 {
514         struct cardstate *cs = (struct cardstate *) data;
515
516         if (cs->tty)
517                 tty_wakeup(cs->tty);
518 }
519
520 /*** interface to common ***/
521
522 void gigaset_if_init(struct cardstate *cs)
523 {
524         struct gigaset_driver *drv;
525
526         drv = cs->driver;
527         if (!drv->have_tty)
528                 return;
529
530         tasklet_init(&cs->if_wake_tasklet, if_wake, (unsigned long) cs);
531
532         mutex_lock(&cs->mutex);
533         cs->tty_dev = tty_register_device(drv->tty, cs->minor_index, NULL);
534
535         if (!IS_ERR(cs->tty_dev))
536                 dev_set_drvdata(cs->tty_dev, cs);
537         else {
538                 pr_warning("could not register device to the tty subsystem\n");
539                 cs->tty_dev = NULL;
540         }
541         mutex_unlock(&cs->mutex);
542 }
543
544 void gigaset_if_free(struct cardstate *cs)
545 {
546         struct gigaset_driver *drv;
547
548         drv = cs->driver;
549         if (!drv->have_tty)
550                 return;
551
552         tasklet_disable(&cs->if_wake_tasklet);
553         tasklet_kill(&cs->if_wake_tasklet);
554         cs->tty_dev = NULL;
555         tty_unregister_device(drv->tty, cs->minor_index);
556 }
557
558 /**
559  * gigaset_if_receive() - pass a received block of data to the tty device
560  * @cs:         device descriptor structure.
561  * @buffer:     received data.
562  * @len:        number of bytes received.
563  *
564  * Called by asyncdata/isocdata if a block of data received from the
565  * device must be sent to userspace through the ttyG* device.
566  */
567 void gigaset_if_receive(struct cardstate *cs,
568                         unsigned char *buffer, size_t len)
569 {
570         unsigned long flags;
571         struct tty_struct *tty;
572
573         spin_lock_irqsave(&cs->lock, flags);
574         tty = cs->tty;
575         if (tty == NULL)
576                 gig_dbg(DEBUG_IF, "receive on closed device");
577         else {
578                 tty_insert_flip_string(tty, buffer, len);
579                 tty_flip_buffer_push(tty);
580         }
581         spin_unlock_irqrestore(&cs->lock, flags);
582 }
583 EXPORT_SYMBOL_GPL(gigaset_if_receive);
584
585 /* gigaset_if_initdriver
586  * Initialize tty interface.
587  * parameters:
588  *      drv             Driver
589  *      procname        Name of the driver (e.g. for /proc/tty/drivers)
590  *      devname         Name of the device files (prefix without minor number)
591  */
592 void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
593                            const char *devname)
594 {
595         int ret;
596         struct tty_driver *tty;
597
598         drv->have_tty = 0;
599
600         drv->tty = tty = alloc_tty_driver(drv->minors);
601         if (tty == NULL)
602                 goto enomem;
603
604         tty->type =             TTY_DRIVER_TYPE_SERIAL,
605         tty->subtype =          SERIAL_TYPE_NORMAL,
606         tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
607
608         tty->driver_name =      procname;
609         tty->name =             devname;
610         tty->minor_start =      drv->minor;
611
612         tty->init_termios          = tty_std_termios;
613         tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
614         tty_set_operations(tty, &if_ops);
615
616         ret = tty_register_driver(tty);
617         if (ret < 0) {
618                 pr_err("error %d registering tty driver\n", ret);
619                 goto error;
620         }
621         gig_dbg(DEBUG_IF, "tty driver initialized");
622         drv->have_tty = 1;
623         return;
624
625 enomem:
626         pr_err("out of memory\n");
627 error:
628         if (drv->tty)
629                 put_tty_driver(drv->tty);
630 }
631
632 void gigaset_if_freedriver(struct gigaset_driver *drv)
633 {
634         if (!drv->have_tty)
635                 return;
636
637         drv->have_tty = 0;
638         tty_unregister_driver(drv->tty);
639         put_tty_driver(drv->tty);
640 }