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