[PATCH] isdn4linux: Siemens Gigaset drivers: logging usage
[powerpc.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.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             && atomic_read(&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         *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, struct file *file,
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 termios *old);
126 static int  if_tiocmget(struct tty_struct *tty, struct file *file);
127 static int  if_tiocmset(struct tty_struct *tty, struct file *file,
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 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 #if 0
143         .break_ctl =            serial_break,
144 #endif
145         .tiocmget =             if_tiocmget,
146         .tiocmset =             if_tiocmset,
147 };
148
149 static int if_open(struct tty_struct *tty, struct file *filp)
150 {
151         struct cardstate *cs;
152         unsigned long flags;
153
154         gig_dbg(DEBUG_IF, "%d+%d: %s()",
155                 tty->driver->minor_start, tty->index, __func__);
156
157         tty->driver_data = NULL;
158
159         cs = gigaset_get_cs_by_tty(tty);
160         if (!cs)
161                 return -ENODEV;
162
163         if (down_interruptible(&cs->sem))
164                 return -ERESTARTSYS; // FIXME -EINTR?
165         tty->driver_data = cs;
166
167         ++cs->open_count;
168
169         if (cs->open_count == 1) {
170                 spin_lock_irqsave(&cs->lock, flags);
171                 cs->tty = tty;
172                 spin_unlock_irqrestore(&cs->lock, flags);
173                 tty->low_latency = 1; //FIXME test
174         }
175
176         up(&cs->sem);
177         return 0;
178 }
179
180 static void if_close(struct tty_struct *tty, struct file *filp)
181 {
182         struct cardstate *cs;
183         unsigned long flags;
184
185         cs = (struct cardstate *) tty->driver_data;
186         if (!cs) {
187                 err("cs==NULL in %s", __func__);
188                 return;
189         }
190
191         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
192
193         down(&cs->sem);
194
195         if (!cs->open_count)
196                 warn("%s: device not opened", __func__);
197         else {
198                 if (!--cs->open_count) {
199                         spin_lock_irqsave(&cs->lock, flags);
200                         cs->tty = NULL;
201                         spin_unlock_irqrestore(&cs->lock, flags);
202                 }
203         }
204
205         up(&cs->sem);
206 }
207
208 static int if_ioctl(struct tty_struct *tty, struct file *file,
209                     unsigned int cmd, unsigned long arg)
210 {
211         struct cardstate *cs;
212         int retval = -ENODEV;
213         int int_arg;
214         unsigned char buf[6];
215         unsigned version[4];
216
217         cs = (struct cardstate *) tty->driver_data;
218         if (!cs) {
219                 err("cs==NULL in %s", __func__);
220                 return -ENODEV;
221         }
222
223         gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
224
225         if (down_interruptible(&cs->sem))
226                 return -ERESTARTSYS; // FIXME -EINTR?
227
228         if (!cs->open_count)
229                 warn("%s: device not opened", __func__);
230         else {
231                 retval = 0;
232                 switch (cmd) {
233                 case GIGASET_REDIR:
234                         retval = get_user(int_arg, (int __user *) arg);
235                         if (retval >= 0)
236                                 retval = if_lock(cs, &int_arg);
237                         if (retval >= 0)
238                                 retval = put_user(int_arg, (int __user *) arg);
239                         break;
240                 case GIGASET_CONFIG:
241                         retval = get_user(int_arg, (int __user *) arg);
242                         if (retval >= 0)
243                                 retval = if_config(cs, &int_arg);
244                         if (retval >= 0)
245                                 retval = put_user(int_arg, (int __user *) arg);
246                         break;
247                 case GIGASET_BRKCHARS:
248                         //FIXME test if MS_LOCKED
249                         gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
250                                            6, (const unsigned char *) arg, 1);
251                         if (!atomic_read(&cs->connected)) {
252                                 gig_dbg(DEBUG_ANY,
253                                     "can't communicate with unplugged device");
254                                 retval = -ENODEV;
255                                 break;
256                         }
257                         retval = copy_from_user(&buf,
258                                         (const unsigned char __user *) arg, 6)
259                                 ? -EFAULT : 0;
260                         if (retval >= 0)
261                                 retval = cs->ops->brkchars(cs, buf);
262                         break;
263                 case GIGASET_VERSION:
264                         retval = copy_from_user(version,
265                                         (unsigned __user *) arg, sizeof version)
266                                 ? -EFAULT : 0;
267                         if (retval >= 0)
268                                 retval = if_version(cs, version);
269                         if (retval >= 0)
270                                 retval = copy_to_user((unsigned __user *) arg,
271                                                       version, sizeof version)
272                                         ? -EFAULT : 0;
273                         break;
274                 default:
275                         gig_dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x",
276                                 __func__, cmd);
277                         retval = -ENOIOCTLCMD;
278                 }
279         }
280
281         up(&cs->sem);
282
283         return retval;
284 }
285
286 static int if_tiocmget(struct tty_struct *tty, struct file *file)
287 {
288         struct cardstate *cs;
289         int retval;
290
291         cs = (struct cardstate *) tty->driver_data;
292         if (!cs) {
293                 err("cs==NULL in %s", __func__);
294                 return -ENODEV;
295         }
296
297         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
298
299         if (down_interruptible(&cs->sem))
300                 return -ERESTARTSYS; // FIXME -EINTR?
301
302         // FIXME read from device?
303         retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR);
304
305         up(&cs->sem);
306
307         return retval;
308 }
309
310 static int if_tiocmset(struct tty_struct *tty, struct file *file,
311                        unsigned int set, unsigned int clear)
312 {
313         struct cardstate *cs;
314         int retval;
315         unsigned mc;
316
317         cs = (struct cardstate *) tty->driver_data;
318         if (!cs) {
319                 err("cs==NULL in %s", __func__);
320                 return -ENODEV;
321         }
322
323         gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
324                 cs->minor_index, __func__, set, clear);
325
326         if (down_interruptible(&cs->sem))
327                 return -ERESTARTSYS; // FIXME -EINTR?
328
329         if (!atomic_read(&cs->connected)) {
330                 gig_dbg(DEBUG_ANY, "can't communicate with unplugged device");
331                 retval = -ENODEV;
332         } else {
333                 mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR);
334                 retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
335                 cs->control_state = mc;
336         }
337
338         up(&cs->sem);
339
340         return retval;
341 }
342
343 static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
344 {
345         struct cardstate *cs;
346         int retval = -ENODEV;
347
348         cs = (struct cardstate *) tty->driver_data;
349         if (!cs) {
350                 err("cs==NULL in %s", __func__);
351                 return -ENODEV;
352         }
353
354         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
355
356         if (down_interruptible(&cs->sem))
357                 return -ERESTARTSYS; // FIXME -EINTR?
358
359         if (!cs->open_count)
360                 warn("%s: device not opened", __func__);
361         else if (atomic_read(&cs->mstate) != MS_LOCKED) {
362                 warn("can't write to unlocked device");
363                 retval = -EBUSY;
364         } else if (!atomic_read(&cs->connected)) {
365                 gig_dbg(DEBUG_ANY, "can't write to unplugged device");
366                 retval = -EBUSY; //FIXME
367         } else {
368                 retval = cs->ops->write_cmd(cs, buf, count,
369                                             &cs->if_wake_tasklet);
370         }
371
372         up(&cs->sem);
373
374         return retval;
375 }
376
377 static int if_write_room(struct tty_struct *tty)
378 {
379         struct cardstate *cs;
380         int retval = -ENODEV;
381
382         cs = (struct cardstate *) tty->driver_data;
383         if (!cs) {
384                 err("cs==NULL in %s", __func__);
385                 return -ENODEV;
386         }
387
388         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
389
390         if (down_interruptible(&cs->sem))
391                 return -ERESTARTSYS; // FIXME -EINTR?
392
393         if (!cs->open_count)
394                 warn("%s: device not opened", __func__);
395         else if (atomic_read(&cs->mstate) != MS_LOCKED) {
396                 warn("can't write to unlocked device");
397                 retval = -EBUSY; //FIXME
398         } else if (!atomic_read(&cs->connected)) {
399                 gig_dbg(DEBUG_ANY, "can't write to unplugged device");
400                 retval = -EBUSY; //FIXME
401         } else
402                 retval = cs->ops->write_room(cs);
403
404         up(&cs->sem);
405
406         return retval;
407 }
408
409 static int if_chars_in_buffer(struct tty_struct *tty)
410 {
411         struct cardstate *cs;
412         int retval = -ENODEV;
413
414         cs = (struct cardstate *) tty->driver_data;
415         if (!cs) {
416                 err("cs==NULL in %s", __func__);
417                 return -ENODEV;
418         }
419
420         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
421
422         if (down_interruptible(&cs->sem))
423                 return -ERESTARTSYS; // FIXME -EINTR?
424
425         if (!cs->open_count)
426                 warn("%s: device not opened", __func__);
427         else if (atomic_read(&cs->mstate) != MS_LOCKED) {
428                 warn("can't write to unlocked device");
429                 retval = -EBUSY;
430         } else if (!atomic_read(&cs->connected)) {
431                 gig_dbg(DEBUG_ANY, "can't write to unplugged device");
432                 retval = -EBUSY; //FIXME
433         } else
434                 retval = cs->ops->chars_in_buffer(cs);
435
436         up(&cs->sem);
437
438         return retval;
439 }
440
441 static void if_throttle(struct tty_struct *tty)
442 {
443         struct cardstate *cs;
444
445         cs = (struct cardstate *) tty->driver_data;
446         if (!cs) {
447                 err("cs==NULL in %s", __func__);
448                 return;
449         }
450
451         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
452
453         down(&cs->sem);
454
455         if (!cs->open_count)
456                 warn("%s: device not opened", __func__);
457         else {
458                 //FIXME
459         }
460
461         up(&cs->sem);
462 }
463
464 static void if_unthrottle(struct tty_struct *tty)
465 {
466         struct cardstate *cs;
467
468         cs = (struct cardstate *) tty->driver_data;
469         if (!cs) {
470                 err("cs==NULL in %s", __func__);
471                 return;
472         }
473
474         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
475
476         down(&cs->sem);
477
478         if (!cs->open_count)
479                 warn("%s: device not opened", __func__);
480         else {
481                 //FIXME
482         }
483
484         up(&cs->sem);
485 }
486
487 static void if_set_termios(struct tty_struct *tty, struct termios *old)
488 {
489         struct cardstate *cs;
490         unsigned int iflag;
491         unsigned int cflag;
492         unsigned int old_cflag;
493         unsigned int control_state, new_state;
494
495         cs = (struct cardstate *) tty->driver_data;
496         if (!cs) {
497                 err("cs==NULL in %s", __func__);
498                 return;
499         }
500
501         gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
502
503         down(&cs->sem);
504
505         if (!cs->open_count) {
506                 warn("%s: device not opened", __func__);
507                 goto out;
508         }
509
510         if (!atomic_read(&cs->connected)) {
511                 gig_dbg(DEBUG_ANY, "can't communicate with unplugged device");
512                 goto out;
513         }
514
515         // stolen from mct_u232.c
516         iflag = tty->termios->c_iflag;
517         cflag = tty->termios->c_cflag;
518         old_cflag = old ? old->c_cflag : cflag; //FIXME?
519         gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
520                 cs->minor_index, iflag, cflag, old_cflag);
521
522         /* get a local copy of the current port settings */
523         control_state = cs->control_state;
524
525         /*
526          * Update baud rate.
527          * Do not attempt to cache old rates and skip settings,
528          * disconnects screw such tricks up completely.
529          * Premature optimization is the root of all evil.
530          */
531
532         /* reassert DTR and (maybe) RTS on transition from B0 */
533         if ((old_cflag & CBAUD) == B0) {
534                 new_state = control_state | TIOCM_DTR;
535                 /* don't set RTS if using hardware flow control */
536                 if (!(old_cflag & CRTSCTS))
537                         new_state |= TIOCM_RTS;
538                 gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
539                         cs->minor_index,
540                         (new_state & TIOCM_RTS) ? " only" : "/RTS");
541                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
542                 control_state = new_state;
543         }
544
545         cs->ops->baud_rate(cs, cflag & CBAUD);
546
547         if ((cflag & CBAUD) == B0) {
548                 /* Drop RTS and DTR */
549                 gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
550                 new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
551                 cs->ops->set_modem_ctrl(cs, control_state, new_state);
552                 control_state = new_state;
553         }
554
555         /*
556          * Update line control register (LCR)
557          */
558
559         cs->ops->set_line_ctrl(cs, cflag);
560
561 #if 0
562         //FIXME this hangs M101 [ts 2005-03-09]
563         //FIXME do we need this?
564         /*
565          * Set flow control: well, I do not really now how to handle DTR/RTS.
566          * Just do what we have seen with SniffUSB on Win98.
567          */
568         /* Drop DTR/RTS if no flow control otherwise assert */
569         gig_dbg(DEBUG_IF, "%u: control_state %x",
570                 cs->minor_index, control_state);
571         new_state = control_state;
572         if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
573                 new_state |= TIOCM_DTR | TIOCM_RTS;
574         else
575                 new_state &= ~(TIOCM_DTR | TIOCM_RTS);
576         if (new_state != control_state) {
577                 gig_dbg(DEBUG_IF, "%u: new_state %x",
578                         cs->minor_index, new_state);
579                 gigaset_set_modem_ctrl(cs, control_state, new_state);
580                 control_state = new_state;
581         }
582 #endif
583
584         /* save off the modified port settings */
585         cs->control_state = control_state;
586
587 out:
588         up(&cs->sem);
589 }
590
591
592 /* wakeup tasklet for the write operation */
593 static void if_wake(unsigned long data)
594 {
595         struct cardstate *cs = (struct cardstate *) data;
596         struct tty_struct *tty;
597
598         tty = cs->tty;
599         if (!tty)
600                 return;
601
602         if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
603             tty->ldisc.write_wakeup) {
604                 gig_dbg(DEBUG_IF, "write wakeup call");
605                 tty->ldisc.write_wakeup(tty);
606         }
607
608         wake_up_interruptible(&tty->write_wait);
609 }
610
611 /*** interface to common ***/
612
613 void gigaset_if_init(struct cardstate *cs)
614 {
615         struct gigaset_driver *drv;
616
617         drv = cs->driver;
618         if (!drv->have_tty)
619                 return;
620
621         tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs);
622         tty_register_device(drv->tty, cs->minor_index, NULL);
623 }
624
625 void gigaset_if_free(struct cardstate *cs)
626 {
627         struct gigaset_driver *drv;
628
629         drv = cs->driver;
630         if (!drv->have_tty)
631                 return;
632
633         tasklet_disable(&cs->if_wake_tasklet);
634         tasklet_kill(&cs->if_wake_tasklet);
635         tty_unregister_device(drv->tty, cs->minor_index);
636 }
637
638 void gigaset_if_receive(struct cardstate *cs,
639                         unsigned char *buffer, size_t len)
640 {
641         unsigned long flags;
642         struct tty_struct *tty;
643
644         spin_lock_irqsave(&cs->lock, flags);
645         if ((tty = cs->tty) == NULL)
646                 gig_dbg(DEBUG_ANY, "receive on closed device");
647         else {
648                 tty_buffer_request_room(tty, len);
649                 tty_insert_flip_string(tty, buffer, len);
650                 tty_flip_buffer_push(tty);
651         }
652         spin_unlock_irqrestore(&cs->lock, flags);
653 }
654 EXPORT_SYMBOL_GPL(gigaset_if_receive);
655
656 /* gigaset_if_initdriver
657  * Initialize tty interface.
658  * parameters:
659  *      drv             Driver
660  *      procname        Name of the driver (e.g. for /proc/tty/drivers)
661  *      devname         Name of the device files (prefix without minor number)
662  *      devfsname       Devfs name of the device files without %d
663  */
664 void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
665                            const char *devname, const char *devfsname)
666 {
667         unsigned minors = drv->minors;
668         int ret;
669         struct tty_driver *tty;
670
671         drv->have_tty = 0;
672
673         if ((drv->tty = alloc_tty_driver(minors)) == NULL)
674                 goto enomem;
675         tty = drv->tty;
676
677         tty->magic =            TTY_DRIVER_MAGIC,
678         tty->major =            GIG_MAJOR,
679         tty->type =             TTY_DRIVER_TYPE_SERIAL,
680         tty->subtype =          SERIAL_TYPE_NORMAL,
681         tty->flags =            TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
682
683         tty->driver_name =      procname;
684         tty->name =             devname;
685         tty->minor_start =      drv->minor;
686         tty->num =              drv->minors;
687
688         tty->owner =            THIS_MODULE;
689         tty->devfs_name =       devfsname;
690
691         tty->init_termios          = tty_std_termios; //FIXME
692         tty->init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME
693         tty_set_operations(tty, &if_ops);
694
695         ret = tty_register_driver(tty);
696         if (ret < 0) {
697                 warn("failed to register tty driver (error %d)", ret);
698                 goto error;
699         }
700         gig_dbg(DEBUG_IF, "tty driver initialized");
701         drv->have_tty = 1;
702         return;
703
704 enomem:
705         warn("could not allocate tty structures");
706 error:
707         if (drv->tty)
708                 put_tty_driver(drv->tty);
709 }
710
711 void gigaset_if_freedriver(struct gigaset_driver *drv)
712 {
713         if (!drv->have_tty)
714                 return;
715
716         drv->have_tty = 0;
717         tty_unregister_driver(drv->tty);
718         put_tty_driver(drv->tty);
719 }