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