OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [uclinux/] [uClinux-2.0.x/] [drivers/] [isdn/] [isdn_tty.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 199 simons
/* $Id: isdn_tty.c,v 1.1.1.1 2001-09-10 07:44:18 simons Exp $
2
 
3
 * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
4
 *
5
 * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
6
 * Copyright 1995,96    by Thinking Objects Software GmbH Wuerzburg
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2, or (at your option)
11
 * any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
 *
22
 * $Log: not supported by cvs2svn $
23
 * Revision 1.1.1.1  2001/07/02 17:58:31  simons
24
 * Initial revision
25
 *
26
 * Revision 1.41.2.11  1998/11/05 22:12:12  fritz
27
 * Changed mail-address.
28
 *
29
 * Revision 1.41.2.10  1998/11/03 14:31:35  fritz
30
 * Reduced stack usage in various functions.
31
 * Adapted statemachine to work with certified HiSax.
32
 * Some fixes in callback handling.
33
 *
34
 * Revision 1.41.2.9  1998/10/25 15:48:32  fritz
35
 * Misc bugfixes and adaptions to new HiSax
36
 *
37
 * Revision 1.41.2.8  1998/08/22 16:43:07  armin
38
 * Added silence detection in audio receive mode (AT+VSD).
39
 *
40
 * Revision 1.41.2.7  1998/06/07 13:48:08  fritz
41
 * ABC cleanup
42
 *
43
 * Revision 1.41.2.5  1998/04/08 21:42:35  keil
44
 * Blocksize default 1024
45
 *
46
 * Revision 1.41.2.4  1998/03/19 17:58:55  detabc
47
 * remove 2 debug-messages (no longer needed) bug was fixed
48
 *
49
 * Revision 1.41.2.3  1998/03/07 23:35:20  detabc
50
 * added the abc-extension to the linux isdn-kernel
51
 * for kernel-version 2.0.xx
52
 * DO NOT USE FOR HIGHER KERNELS-VERSIONS
53
 * all source-lines are switched with the define  CONFIG_ISDN_WITH_ABC
54
 * (make config and answer ABC-Ext. Support (Compress,TCP-Keepalive ...) with yes
55
 *
56
 * you need also a modified isdnctrl-source the switch on the
57
 * features of the abc-extension
58
 *
59
 * please use carefully. more detail will be follow.
60
 * thanks
61
 *
62
 * Revision 1.41.2.2  1998/03/07 23:02:51  tsbogend
63
 * fixed kernel unaligned traps on Linux/Alpha
64
 *
65
 * Revision 1.41.2.1  1997/08/21 15:56:11  fritz
66
 * Synchronized 2.0.X branch with 2.0.31-pre7
67
 *
68
 * Revision 1.41  1997/05/27 15:17:31  fritz
69
 * Added changes for recent 2.1.x kernels:
70
 *   changed return type of isdn_close
71
 *   queue_task_* -> queue_task
72
 *   clear/set_bit -> test_and_... where apropriate.
73
 *   changed type of hard_header_cache parameter.
74
 *
75
 * Revision 1.40  1997/03/24 22:55:27  fritz
76
 * Added debug code for status callbacks.
77
 *
78
 * Revision 1.39  1997/03/21 18:25:56  fritz
79
 * Corrected CTS handling.
80
 *
81
 * Revision 1.38  1997/03/07 12:13:35  fritz
82
 * Bugfix: Send audio in adpcm format was broken.
83
 * Bugfix: CTS handling was wrong.
84
 *
85
 * Revision 1.37  1997/03/07 01:37:34  fritz
86
 * Bugfix: Did not compile with CONFIG_ISDN_AUDIO disabled.
87
 * Bugfix: isdn_tty_tint() did not handle lowlevel errors correctly.
88
 * Bugfix: conversion was wrong when sending ulaw audio.
89
 * Added proper ifdef's for CONFIG_ISDN_AUDIO
90
 *
91
 * Revision 1.36  1997/03/04 21:41:55  fritz
92
 * Fix: Excessive stack usage of isdn_tty_senddown()
93
 *      and isdn_tty_end_vrx() could lead to problems.
94
 *
95
 * Revision 1.35  1997/03/02 19:05:52  fritz
96
 * Bugfix: Avoid recursion.
97
 *
98
 * Revision 1.34  1997/03/02 14:29:22  fritz
99
 * More ttyI related cleanup.
100
 *
101
 * Revision 1.33  1997/02/28 02:32:45  fritz
102
 * Cleanup: Moved some tty related stuff from isdn_common.c
103
 *          to isdn_tty.c
104
 * Bugfix:  Bisync protocol did not behave like documented.
105
 *
106
 * Revision 1.32  1997/02/23 15:43:03  fritz
107
 * Small change in handling of incoming calls
108
 * documented in newest version of ttyI.4
109
 *
110
 * Revision 1.31  1997/02/21 13:05:57  fritz
111
 * Bugfix: Remote hangup did not set location-info on ttyI's
112
 *
113
 * Revision 1.30  1997/02/18 09:41:05  fritz
114
 * Added support for bitwise access to modem registers (ATSx.y=n, ATSx.y?).
115
 * Beautified output of AT&V.
116
 *
117
 * Revision 1.29  1997/02/16 12:11:51  fritz
118
 * Added S13,Bit4 option.
119
 *
120
 * Revision 1.28  1997/02/10 22:07:08  fritz
121
 * Added 2 modem registers for numbering plan and screening info.
122
 *
123
 * Revision 1.27  1997/02/10 21:31:14  fritz
124
 * Changed setup-interface (incoming and outgoing).
125
 *
126
 * Revision 1.26  1997/02/10 20:12:48  fritz
127
 * Changed interface for reporting incoming calls.
128
 *
129
 * Revision 1.25  1997/02/03 23:04:30  fritz
130
 * Reformatted according CodingStyle.
131
 * skb->free stuff replaced by macro.
132
 * Finished full-duplex audio.
133
 *
134
 * Revision 1.24  1997/01/14 01:32:42  fritz
135
 * Changed audio receive not to rely on skb->users and skb->lock.
136
 * Added ATI2 and related variables.
137
 * Started adding full-duplex audio capability.
138
 *
139
 * Revision 1.23  1996/10/22 23:14:02  fritz
140
 * Changes for compatibility to 2.0.X and 2.1.X kernels.
141
 *
142
 * Revision 1.22  1996/10/19 18:56:43  fritz
143
 * ATZ did not change the xmitbuf size.
144
 *
145
 * Revision 1.21  1996/06/24 17:40:28  fritz
146
 * Bugfix: Did not compile without CONFIG_ISDN_AUDIO
147
 *
148
 * Revision 1.20  1996/06/15 14:59:39  fritz
149
 * Fixed isdn_tty_tint() to handle partially sent
150
 * sk_buffs.
151
 *
152
 * Revision 1.19  1996/06/12 15:53:56  fritz
153
 * Bugfix: AT+VTX and AT+VRX could be executed without
154
 *         having a connection.
155
 *         Missing check for NULL tty in isdn_tty_flush_buffer().
156
 *
157
 * Revision 1.18  1996/06/07 11:17:33  tsbogend
158
 * added missing #ifdef CONFIG_ISDN_AUDIO to make compiling without
159
 * audio support possible
160
 *
161
 * Revision 1.17  1996/06/06 14:55:47  fritz
162
 * Changed to support DTMF decoding on audio playback also.
163
 * Bugfix: Added check for invalid info->isdn_driver in
164
 *         isdn_tty_senddown().
165
 * Clear ncarrier flag on last close() of a tty.
166
 *
167
 * Revision 1.16  1996/06/05 02:24:12  fritz
168
 * Added DTMF decoder for audio mode.
169
 *
170
 * Revision 1.15  1996/06/03 20:35:01  fritz
171
 * Fixed typos.
172
 *
173
 * Revision 1.14  1996/06/03 20:12:19  fritz
174
 * Fixed typos.
175
 * Added call to write_wakeup via isdn_tty_flush_buffer()
176
 * in isdn_tty_modem_hup().
177
 *
178
 * Revision 1.13  1996/05/31 01:33:29  fritz
179
 * Changed buffering due to bad performance with mgetty.
180
 * Now sk_buff is delayed allocated in isdn_tty_senddown
181
 * using xmit_buff like in standard serial driver.
182
 * Fixed module locking.
183
 * Added DLE-DC4 handling in voice mode.
184
 *
185
 * Revision 1.12  1996/05/19 01:34:40  fritz
186
 * Bugfix: ATS returned error.
187
 *         Register 20 made readonly.
188
 *
189
 * Revision 1.11  1996/05/18 01:37:03  fritz
190
 * Added spelling corrections and some minor changes
191
 * to stay in sync with kernel.
192
 *
193
 * Revision 1.10  1996/05/17 03:51:49  fritz
194
 * Changed DLE handling for audio receive.
195
 *
196
 * Revision 1.9  1996/05/11 21:52:07  fritz
197
 * Changed queue management to use sk_buffs.
198
 *
199
 * Revision 1.8  1996/05/10 08:49:43  fritz
200
 * Checkin before major changes of tty-code.
201
 *
202
 * Revision 1.7  1996/05/07 09:15:09  fritz
203
 * Reorganized and general cleanup.
204
 * Bugfixes:
205
 *  - Audio-transmit working now.
206
 *  - "NO CARRIER" now reported, when hanging up with DTR low.
207
 *  - Corrected CTS handling.
208
 *
209
 * Revision 1.6  1996/05/02 03:59:25  fritz
210
 * Bugfixes:
211
 *  - On dialout, layer-2 setup had been incomplete
212
 *    when using new auto-layer2 feature.
213
 *  - On hangup, "NO CARRIER" message sometimes missing.
214
 *
215
 * Revision 1.5  1996/04/30 21:05:25  fritz
216
 * Test commit
217
 *
218
 * Revision 1.4  1996/04/20 16:39:54  fritz
219
 * Changed all io to go through generic routines in isdn_common.c
220
 * Fixed a real ugly bug in modem-emulator: 'ATA' had been accepted
221
 * even when a call has been cancelled from the remote machine.
222
 *
223
 * Revision 1.3  1996/02/11 02:12:32  fritz
224
 * Bugfixes according to similar fixes in standard serial.c of kernel.
225
 *
226
 * Revision 1.2  1996/01/22 05:12:25  fritz
227
 * replaced my_atoi by simple_strtoul
228
 *
229
 * Revision 1.1  1996/01/09 04:13:18  fritz
230
 * Initial revision
231
 *
232
 */
233
#undef ISDN_TTY_STAT_DEBUG
234
 
235
#define __NO_VERSION__
236
#include <linux/config.h>
237
#include <linux/module.h>
238
#include <linux/isdn.h>
239
#include "isdn_common.h"
240
#include "isdn_tty.h"
241
#ifdef CONFIG_ISDN_AUDIO
242
#include "isdn_audio.h"
243
#define VBUF 0x3e0
244
#define VBUFX (VBUF/16)
245
#endif
246
 
247
 
248
/* Prototypes */
249
 
250
static int isdn_tty_edit_at(const char *, int, modem_info *, int);
251
static void isdn_tty_check_esc(const u_char *, u_char, int, int *, int *, int);
252
static void isdn_tty_modem_reset_regs(modem_info *, int);
253
static void isdn_tty_cmd_ATA(modem_info *);
254
static void isdn_tty_at_cout(char *, modem_info *);
255
static void isdn_tty_flush_buffer(struct tty_struct *);
256
static void isdn_tty_modem_result(int, modem_info *);
257
#ifdef CONFIG_ISDN_AUDIO
258
static int isdn_tty_countDLE(unsigned char *, int);
259
#endif
260
 
261
/* Leave this unchanged unless you know what you do! */
262
#define MODEM_PARANOIA_CHECK
263
#define MODEM_DO_RESTART
264
 
265
static char *isdn_ttyname_ttyI = "ttyI";
266
static char *isdn_ttyname_cui = "cui";
267
static int bit2si[8] =
268
{1, 5, 7, 7, 7, 7, 7, 7};
269
static int si2bit[8] =
270
{4, 1, 4, 4, 4, 4, 4, 4};
271
 
272
char *isdn_tty_revision = "$Revision: 1.1.1.1 $";
273
 
274
#define DLE 0x10
275
#define ETX 0x03
276
#define DC4 0x14
277
 
278
/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
279
 * to stuff incoming data directly into a tty's flip-buffer. This
280
 * is done to speed up tty-receiving if the receive-queue is empty.
281
 * This routine MUST be called with interrupts off.
282
 * Return:
283
 *  1 = Success
284
 *  0 = Failure, data has to be buffered and later processed by
285
 *      isdn_tty_readmodem().
286
 */
287
static int
288
isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
289
{
290
        int c;
291
        int len;
292
        struct tty_struct *tty;
293
 
294
        if (info->online) {
295
                if ((tty = info->tty)) {
296
                        if (info->mcr & UART_MCR_RTS) {
297
                                c = TTY_FLIPBUF_SIZE - tty->flip.count;
298
                                len = skb->len
299
#ifdef CONFIG_ISDN_AUDIO
300
                                        + ISDN_AUDIO_SKB_DLECOUNT(skb)
301
#endif
302
                                        ;
303
                                if (c >= len) {
304
#ifdef CONFIG_ISDN_AUDIO
305
                                        if (ISDN_AUDIO_SKB_DLECOUNT(skb))
306
                                                while (skb->len--) {
307
                                                        if (*skb->data == DLE)
308
                                                                tty_insert_flip_char(tty, DLE, 0);
309
                                                        tty_insert_flip_char(tty, *skb->data++, 0);
310
                                        } else {
311
#endif
312
                                                memcpy(tty->flip.char_buf_ptr,
313
                                                       skb->data, len);
314
                                                tty->flip.count += len;
315
                                                tty->flip.char_buf_ptr += len;
316
                                                memset(tty->flip.flag_buf_ptr, 0, len);
317
                                                tty->flip.flag_buf_ptr += len;
318
#ifdef CONFIG_ISDN_AUDIO
319
                                        }
320
#endif
321
                                        if (info->emu.mdmreg[12] & 128)
322
                                                tty->flip.flag_buf_ptr[len - 1] = 0xff;
323
                                        queue_task(&tty->flip.tqueue, &tq_timer);
324
                                        SET_SKB_FREE(skb);
325
                                        kfree_skb(skb, FREE_READ);
326
                                        return 1;
327
                                }
328
                        }
329
                }
330
        }
331
        return 0;
332
}
333
 
334
/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
335
 * It tries getting received data from the receive queue an stuff it into
336
 * the tty's flip-buffer.
337
 */
338
void
339
isdn_tty_readmodem(void)
340
{
341
        int resched = 0;
342
        int midx;
343
        int i;
344
        int c;
345
        int r;
346
        ulong flags;
347
        struct tty_struct *tty;
348
        modem_info *info;
349
 
350
        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
351
                if ((midx = dev->m_idx[i]) >= 0) {
352
                        info = &dev->mdm.info[midx];
353
                        if (info->online) {
354
                                r = 0;
355
#ifdef CONFIG_ISDN_AUDIO
356
                                isdn_audio_eval_dtmf(info);
357
                                if ((info->vonline & 1) && (info->emu.vpar[1]))
358
                                        isdn_audio_eval_silence(info);
359
#endif
360
                                if ((tty = info->tty)) {
361
                                        if (info->mcr & UART_MCR_RTS) {
362
                                                c = TTY_FLIPBUF_SIZE - tty->flip.count;
363
                                                if (c > 0) {
364
                                                        save_flags(flags);
365
                                                        cli();
366
                                                        r = isdn_readbchan(info->isdn_driver, info->isdn_channel,
367
                                                                           tty->flip.char_buf_ptr,
368
                                                                           tty->flip.flag_buf_ptr, c, 0);
369
                                                        /* CISCO AsyncPPP Hack */
370
                                                        if (!(info->emu.mdmreg[12] & 128))
371
                                                                memset(tty->flip.flag_buf_ptr, 0, r);
372
                                                        tty->flip.count += r;
373
                                                        tty->flip.flag_buf_ptr += r;
374
                                                        tty->flip.char_buf_ptr += r;
375
                                                        if (r)
376
                                                                queue_task(&tty->flip.tqueue, &tq_timer);
377
                                                        restore_flags(flags);
378
                                                }
379
                                        } else
380
                                                r = 1;
381
                                } else
382
                                        r = 1;
383
                                if (r) {
384
                                        info->rcvsched = 0;
385
                                        resched = 1;
386
                                } else
387
                                        info->rcvsched = 1;
388
                        }
389
                }
390
        }
391
        if (!resched)
392
                isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
393
}
394
 
395
int
396
isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
397
{
398
        ulong flags;
399
        int midx;
400
#ifdef CONFIG_ISDN_AUDIO
401
        int ifmt;
402
#endif
403
        modem_info *info;
404
 
405
        if ((midx = dev->m_idx[i]) < 0) {
406
                /* if midx is invalid, packet is not for tty */
407
                return 0;
408
        }
409
        info = &dev->mdm.info[midx];
410
#ifdef CONFIG_ISDN_AUDIO
411
        ifmt = 1;
412
 
413
        if (info->vonline)
414
                isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
415
        if ((info->vonline & 1) && (info->emu.vpar[1]))
416
                isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
417
#endif
418
        if ((info->online < 2)
419
#ifdef CONFIG_ISDN_AUDIO
420
            && (!(info->vonline & 1))
421
#endif
422
                ) {
423
                /* If Modem not listening, drop data */
424
                SET_SKB_FREE(skb);
425
                kfree_skb(skb, FREE_READ);
426
                return 1;
427
        }
428
        if (info->emu.mdmreg[13] & 2)
429
                /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
430
                if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
431
                        skb_pull(skb, 4);
432
#ifdef CONFIG_ISDN_AUDIO
433
        if (skb_headroom(skb) < sizeof(isdn_audio_skb)) {
434
                printk(KERN_WARNING
435
                       "isdn_audio: insufficient skb_headroom, dropping\n");
436
                SET_SKB_FREE(skb);
437
                kfree_skb(skb, FREE_READ);
438
                return 1;
439
        }
440
        ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
441
        ISDN_AUDIO_SKB_LOCK(skb) = 0;
442
        if (info->vonline & 1) {
443
                /* voice conversion/compression */
444
                switch (info->emu.vpar[3]) {
445
                        case 2:
446
                        case 3:
447
                        case 4:
448
                                /* adpcm
449
                                 * Since compressed data takes less
450
                                 * space, we can overwrite the buffer.
451
                                 */
452
                                skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
453
                                                                    ifmt,
454
                                                                    skb->data,
455
                                                                    skb->data,
456
                                                                    skb->len));
457
                                break;
458
                        case 5:
459
                                /* a-law */
460
                                if (!ifmt)
461
                                        isdn_audio_ulaw2alaw(skb->data, skb->len);
462
                                break;
463
                        case 6:
464
                                /* u-law */
465
                                if (ifmt)
466
                                        isdn_audio_alaw2ulaw(skb->data, skb->len);
467
                                break;
468
                }
469
                ISDN_AUDIO_SKB_DLECOUNT(skb) =
470
                        isdn_tty_countDLE(skb->data, skb->len);
471
        }
472
#endif
473
        /* Try to deliver directly via tty-flip-buf if queue is empty */
474
        save_flags(flags);
475
        cli();
476
        if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
477
                if (isdn_tty_try_read(info, skb)) {
478
                        restore_flags(flags);
479
                        return 1;
480
                }
481
        /* Direct deliver failed or queue wasn't empty.
482
         * Queue up for later dequeueing via timer-irq.
483
         */
484
        __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
485
        dev->drv[di]->rcvcount[channel] +=
486
                (skb->len
487
#ifdef CONFIG_ISDN_AUDIO
488
                 + ISDN_AUDIO_SKB_DLECOUNT(skb)
489
#endif
490
                        );
491
        restore_flags(flags);
492
        /* Schedule dequeuing */
493
        if ((dev->modempoll) && (info->rcvsched))
494
                isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
495
        return 1;
496
}
497
 
498
void
499
isdn_tty_cleanup_xmit(modem_info * info)
500
{
501
        struct sk_buff *skb;
502
        unsigned long flags;
503
 
504
        save_flags(flags);
505
        cli();
506
        if (skb_queue_len(&info->xmit_queue))
507
                while ((skb = skb_dequeue(&info->xmit_queue))) {
508
                        SET_SKB_FREE(skb);
509
                        kfree_skb(skb, FREE_WRITE);
510
                }
511
#ifdef CONFIG_ISDN_AUDIO
512
        if (skb_queue_len(&info->dtmf_queue))
513
                while ((skb = skb_dequeue(&info->dtmf_queue))) {
514
                        SET_SKB_FREE(skb);
515
                        kfree_skb(skb, FREE_WRITE);
516
                }
517
#endif
518
        restore_flags(flags);
519
}
520
 
521
static void
522
isdn_tty_tint(modem_info * info)
523
{
524
        struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
525
        int len,
526
         slen;
527
 
528
        if (!skb)
529
                return;
530
        len = skb->len;
531
        if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
532
                                           info->isdn_channel, skb)) == len) {
533
                struct tty_struct *tty = info->tty;
534
                info->send_outstanding++;
535
                info->msr |= UART_MSR_CTS;
536
                info->lsr |= UART_LSR_TEMT;
537
                if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
538
                    tty->ldisc.write_wakeup)
539
                        (tty->ldisc.write_wakeup) (tty);
540
                wake_up_interruptible(&tty->write_wait);
541
                return;
542
        }
543
        if (slen < 0) {
544
                /* Error: no channel, already shutdown, or wrong parameter */
545
                SET_SKB_FREE(skb);
546
                dev_kfree_skb(skb, FREE_WRITE);
547
                return;
548
        }
549
        if (slen)
550
                skb_pull(skb, slen);
551
        skb_queue_head(&info->xmit_queue, skb);
552
}
553
 
554
#ifdef CONFIG_ISDN_AUDIO
555
static int
556
isdn_tty_countDLE(unsigned char *buf, int len)
557
{
558
        int count = 0;
559
 
560
        while (len--)
561
                if (*buf++ == DLE)
562
                        count++;
563
        return count;
564
}
565
 
566
/* This routine is called from within isdn_tty_write() to perform
567
 * DLE-decoding when sending audio-data.
568
 */
569
static int
570
isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len)
571
{
572
        unsigned char *p = &info->xmit_buf[info->xmit_count];
573
        int count = 0;
574
 
575
        while (len > 0) {
576
                if (m->lastDLE) {
577
                        m->lastDLE = 0;
578
                        switch (*p) {
579
                                case DLE:
580
                                        /* Escape code */
581
                                        if (len > 1)
582
                                                memmove(p, p + 1, len - 1);
583
                                        p--;
584
                                        count++;
585
                                        break;
586
                                case ETX:
587
                                        /* End of data */
588
                                        info->vonline |= 4;
589
                                        return count;
590
                                case DC4:
591
                                        /* Abort RX */
592
                                        info->vonline &= ~1;
593
#ifdef ISDN_DEBUG_MODEM_VOICE
594
                                        printk(KERN_DEBUG
595
                                               "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",
596
                                               info->line);
597
#endif
598
                                        isdn_tty_at_cout("\020\003", info);
599
                                        if (!info->vonline) {
600
#ifdef ISDN_DEBUG_MODEM_VOICE
601
                                                printk(KERN_DEBUG
602
                                                       "DLEdown: send VCON on ttyI%d\n",
603
                                                       info->line);
604
#endif
605
                                                isdn_tty_at_cout("\r\nVCON\r\n", info);
606
                                        }
607
                                        /* Fall through */
608
                                case 'q':
609
                                case 's':
610
                                        /* Silence */
611
                                        if (len > 1)
612
                                                memmove(p, p + 1, len - 1);
613
                                        p--;
614
                                        break;
615
                        }
616
                } else {
617
                        if (*p == DLE)
618
                                m->lastDLE = 1;
619
                        else
620
                                count++;
621
                }
622
                p++;
623
                len--;
624
        }
625
        if (len < 0) {
626
                printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
627
                return 0;
628
        }
629
        return count;
630
}
631
 
632
/* This routine is called from within isdn_tty_write() when receiving
633
 * audio-data. It interrupts receiving, if an character other than
634
 * ^S or ^Q is sent.
635
 */
636
static int
637
isdn_tty_end_vrx(const char *buf, int c, int from_user)
638
{
639
        char ch;
640
 
641
        while (c--) {
642
                if (from_user)
643
                        GET_USER(ch, buf);
644
                else
645
                        ch = *buf;
646
                if ((ch != 0x11) && (ch != 0x13))
647
                        return 1;
648
                buf++;
649
        }
650
        return 0;
651
}
652
 
653
static int voice_cf[7] =
654
{0, 0, 4, 3, 2, 0, 0};
655
 
656
#endif                          /* CONFIG_ISDN_AUDIO */
657
 
658
/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
659
 * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
660
 * outgoing data from the tty's xmit-buffer, handles voice-decompression or
661
 * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
662
 */
663
static void
664
isdn_tty_senddown(modem_info * info)
665
{
666
        int buflen;
667
        int skb_res;
668
#ifdef CONFIG_ISDN_AUDIO
669
        int audio_len;
670
#endif
671
        struct sk_buff *skb;
672
        unsigned long flags;
673
 
674
#ifdef CONFIG_ISDN_AUDIO
675
        if (info->vonline & 4) {
676
                info->vonline &= ~6;
677
                if (!info->vonline) {
678
#ifdef ISDN_DEBUG_MODEM_VOICE
679
                        printk(KERN_DEBUG
680
                               "senddown: send VCON on ttyI%d\n",
681
                               info->line);
682
#endif
683
                        isdn_tty_at_cout("\r\nVCON\r\n", info);
684
                }
685
        }
686
#endif
687
        save_flags(flags);
688
        cli();
689
        if (!(buflen = info->xmit_count)) {
690
                restore_flags(flags);
691
                return;
692
        }
693
        if ((info->emu.mdmreg[12] & 0x10) != 0)
694
                info->msr &= ~UART_MSR_CTS;
695
        info->lsr &= ~UART_LSR_TEMT;
696
        if (info->isdn_driver < 0) {
697
                info->xmit_count = 0;
698
                restore_flags(flags);
699
                return;
700
        }
701
        skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
702
#ifdef CONFIG_ISDN_AUDIO
703
        if (info->vonline & 2)
704
                audio_len = buflen * voice_cf[info->emu.vpar[3]];
705
        else
706
                audio_len = 0;
707
        skb = dev_alloc_skb(skb_res + buflen + audio_len);
708
#else
709
        skb = dev_alloc_skb(skb_res + buflen);
710
#endif
711
        if (!skb) {
712
                restore_flags(flags);
713
                printk(KERN_WARNING
714
                       "isdn_tty: Out of memory in ttyI%d senddown\n",
715
                       info->line);
716
                return;
717
        }
718
        skb_reserve(skb, skb_res);
719
        memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
720
        info->xmit_count = 0;
721
        restore_flags(flags);
722
#ifdef CONFIG_ISDN_AUDIO
723
        if (info->vonline & 2) {
724
                /* For now, ifmt is fixed to 1 (alaw), since this
725
                 * is used with ISDN everywhere in the world, except
726
                 * US, Canada and Japan.
727
                 * Later, when US-ISDN protocols are implemented,
728
                 * this setting will depend on the D-channel protocol.
729
                 */
730
                int ifmt = 1;
731
 
732
                /* voice conversion/decompression */
733
                switch (info->emu.vpar[3]) {
734
                        case 2:
735
                        case 3:
736
                        case 4:
737
                                /* adpcm, compatible to ZyXel 1496 modem
738
                                 * with ROM revision 6.01
739
                                 */
740
                                audio_len = isdn_audio_adpcm2xlaw(info->adpcms,
741
                                                                  ifmt,
742
                                                                  skb->data,
743
                                                    skb_put(skb, audio_len),
744
                                                                  buflen);
745
                                skb_pull(skb, buflen);
746
                                skb_trim(skb, audio_len);
747
                                break;
748
                        case 5:
749
                                /* a-law */
750
                                if (!ifmt)
751
                                        isdn_audio_alaw2ulaw(skb->data,
752
                                                             buflen);
753
                                break;
754
                        case 6:
755
                                /* u-law */
756
                                if (ifmt)
757
                                        isdn_audio_ulaw2alaw(skb->data,
758
                                                             buflen);
759
                                break;
760
                }
761
        }
762
#endif                          /* CONFIG_ISDN_AUDIO */
763
        SET_SKB_FREE(skb);
764
        if (info->emu.mdmreg[13] & 2)
765
                /* Add T.70 simplified header */
766
                memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
767
        skb_queue_tail(&info->xmit_queue, skb);
768
}
769
 
770
/************************************************************
771
 *
772
 * Modem-functions
773
 *
774
 * mostly "stolen" from original Linux-serial.c and friends.
775
 *
776
 ************************************************************/
777
 
778
/* The next routine is called once from within timer-interrupt
779
 * triggered within isdn_tty_modem_ncarrier(). It calls
780
 * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
781
 * into the tty's flip-buffer.
782
 */
783
static void
784
isdn_tty_modem_do_ncarrier(unsigned long data)
785
{
786
        modem_info *info = (modem_info *) data;
787
        isdn_tty_modem_result(3, info);
788
}
789
 
790
/* Next routine is called, whenever the DTR-signal is raised.
791
 * It checks the ncarrier-flag, and triggers the above routine
792
 * when necessary. The ncarrier-flag is set, whenever DTR goes
793
 * low.
794
 */
795
static void
796
isdn_tty_modem_ncarrier(modem_info * info)
797
{
798
        if (info->ncarrier) {
799
                info->nc_timer.expires = jiffies + HZ;
800
                info->nc_timer.function = isdn_tty_modem_do_ncarrier;
801
                info->nc_timer.data = (unsigned long) info;
802
                add_timer(&info->nc_timer);
803
        }
804
}
805
 
806
/* isdn_tty_dial() performs dialing of a tty an the necessary
807
 * setup of the lower levels before that.
808
 */
809
static void
810
isdn_tty_dial(char *n, modem_info * info, atemu * m)
811
{
812
        int usg = ISDN_USAGE_MODEM;
813
        int si = 7;
814
        int l2 = m->mdmreg[14];
815
        isdn_ctrl cmd;
816
        ulong flags;
817
        int i;
818
        int j;
819
 
820
        for (j = 7; j >= 0; j--)
821
                if (m->mdmreg[18] & (1 << j)) {
822
                        si = bit2si[j];
823
                        break;
824
                }
825
#ifdef CONFIG_ISDN_AUDIO
826
        if (si == 1) {
827
                l2 = 4;
828
                usg = ISDN_USAGE_VOICE;
829
        }
830
#endif
831
        m->mdmreg[20] = si2bit[si];
832
        save_flags(flags);
833
        cli();
834
        i = isdn_get_free_channel(usg, l2, m->mdmreg[15], -1, -1);
835
        if (i < 0) {
836
                restore_flags(flags);
837
                isdn_tty_modem_result(6, info);
838
        } else {
839
                info->isdn_driver = dev->drvmap[i];
840
                info->isdn_channel = dev->chanmap[i];
841
                info->drv_index = i;
842
                dev->m_idx[i] = info->line;
843
                dev->usage[i] |= ISDN_USAGE_OUTGOING;
844
                info->last_dir = 1;
845
                strcpy(info->last_num, n);
846
                isdn_info_update();
847
                restore_flags(flags);
848
                cmd.driver = info->isdn_driver;
849
                cmd.arg = info->isdn_channel;
850
                cmd.command = ISDN_CMD_CLREAZ;
851
                isdn_command(&cmd);
852
                strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
853
                cmd.driver = info->isdn_driver;
854
                cmd.command = ISDN_CMD_SETEAZ;
855
                isdn_command(&cmd);
856
                cmd.driver = info->isdn_driver;
857
                cmd.command = ISDN_CMD_SETL2;
858
                info->last_l2 = l2;
859
                cmd.arg = info->isdn_channel + (l2 << 8);
860
                isdn_command(&cmd);
861
                cmd.driver = info->isdn_driver;
862
                cmd.command = ISDN_CMD_SETL3;
863
                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
864
                isdn_command(&cmd);
865
                cmd.driver = info->isdn_driver;
866
                cmd.arg = info->isdn_channel;
867
                sprintf(cmd.parm.setup.phone, "%s", n);
868
                sprintf(cmd.parm.setup.eazmsn, "%s",
869
                        isdn_map_eaz2msn(m->msn, info->isdn_driver));
870
                cmd.parm.setup.si1 = si;
871
                cmd.parm.setup.si2 = m->mdmreg[19];
872
                cmd.command = ISDN_CMD_DIAL;
873
                info->dialing = 1;
874
                strcpy(dev->num[i], n);
875
                isdn_info_update();
876
                isdn_command(&cmd);
877
        }
878
}
879
 
880
/* isdn_tty_hangup() disassociates a tty from the real
881
 * ISDN-line (hangup). The usage-status is cleared
882
 * and some cleanup is done also.
883
 */
884
static void
885
isdn_tty_modem_hup(modem_info * info, int local)
886
{
887
        isdn_ctrl cmd;
888
        int usage;
889
 
890
        if (!info)
891
                return;
892
#ifdef ISDN_DEBUG_MODEM_HUP
893
        printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
894
#endif
895
        info->rcvsched = 0;
896
        isdn_tty_flush_buffer(info->tty);
897
        if (info->online) {
898
                info->last_lhup = local;
899
                info->online = 0;
900
                /* NO CARRIER message */
901
                isdn_tty_modem_result(3, info);
902
        }
903
#ifdef CONFIG_ISDN_AUDIO
904
        info->vonline = 0;
905
        if (info->dtmf_state) {
906
                kfree(info->dtmf_state);
907
                info->dtmf_state = NULL;
908
        }
909
        if (info->adpcms) {
910
                kfree(info->adpcms);
911
                info->adpcms = NULL;
912
        }
913
        if (info->adpcmr) {
914
                kfree(info->adpcmr);
915
                info->adpcmr = NULL;
916
        }
917
#endif
918
        info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
919
        info->lsr |= UART_LSR_TEMT;
920
        if (info->isdn_driver >= 0) {
921
                if (local) {
922
                        cmd.driver = info->isdn_driver;
923
                        cmd.command = ISDN_CMD_HANGUP;
924
                        cmd.arg = info->isdn_channel;
925
                        isdn_command(&cmd);
926
                }
927
                isdn_all_eaz(info->isdn_driver, info->isdn_channel);
928
                info->emu.mdmreg[1] = 0;
929
                usage = (info->emu.mdmreg[20] == 1) ?
930
                    ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
931
                isdn_free_channel(info->isdn_driver, info->isdn_channel,
932
                                  usage);
933
        }
934
        info->isdn_driver = -1;
935
        info->isdn_channel = -1;
936
        if (info->drv_index >= 0) {
937
                dev->m_idx[info->drv_index] = -1;
938
                info->drv_index = -1;
939
        }
940
}
941
 
942
static inline int
943
isdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine)
944
{
945
#ifdef MODEM_PARANOIA_CHECK
946
        if (!info) {
947
                printk(KERN_WARNING "isdn_tty: null info_struct for (%d, %d) in %s\n",
948
                       MAJOR(device), MINOR(device), routine);
949
                return 1;
950
        }
951
        if (info->magic != ISDN_ASYNC_MAGIC) {
952
                printk(KERN_WARNING "isdn_tty: bad magic for modem struct (%d, %d) in %s\n",
953
                       MAJOR(device), MINOR(device), routine);
954
                return 1;
955
        }
956
#endif
957
        return 0;
958
}
959
 
960
/*
961
 * This routine is called to set the UART divisor registers to match
962
 * the specified baud rate for a serial port.
963
 */
964
static void
965
isdn_tty_change_speed(modem_info * info)
966
{
967
        uint cflag,
968
         cval,
969
         fcr,
970
         quot;
971
        int i;
972
 
973
        if (!info->tty || !info->tty->termios)
974
                return;
975
        cflag = info->tty->termios->c_cflag;
976
 
977
        quot = i = cflag & CBAUD;
978
        if (i & CBAUDEX) {
979
                i &= ~CBAUDEX;
980
                if (i < 1 || i > 2)
981
                        info->tty->termios->c_cflag &= ~CBAUDEX;
982
                else
983
                        i += 15;
984
        }
985
        if (quot) {
986
                info->mcr |= UART_MCR_DTR;
987
                isdn_tty_modem_ncarrier(info);
988
        } else {
989
                info->mcr &= ~UART_MCR_DTR;
990
                if (info->emu.mdmreg[13] & 4) {
991
#ifdef ISDN_DEBUG_MODEM_HUP
992
                        printk(KERN_DEBUG "Mhup in changespeed\n");
993
#endif
994
                        if (info->online)
995
                                info->ncarrier = 1;
996
                        isdn_tty_modem_reset_regs(info, 0);
997
                        isdn_tty_modem_hup(info, 1);
998
                }
999
                return;
1000
        }
1001
        /* byte size and parity */
1002
        cval = cflag & (CSIZE | CSTOPB);
1003
        cval >>= 4;
1004
        if (cflag & PARENB)
1005
                cval |= UART_LCR_PARITY;
1006
        if (!(cflag & PARODD))
1007
                cval |= UART_LCR_EPAR;
1008
        fcr = 0;
1009
 
1010
        /* CTS flow control flag and modem status interrupts */
1011
        if (cflag & CRTSCTS) {
1012
                info->flags |= ISDN_ASYNC_CTS_FLOW;
1013
        } else
1014
                info->flags &= ~ISDN_ASYNC_CTS_FLOW;
1015
        if (cflag & CLOCAL)
1016
                info->flags &= ~ISDN_ASYNC_CHECK_CD;
1017
        else {
1018
                info->flags |= ISDN_ASYNC_CHECK_CD;
1019
        }
1020
}
1021
 
1022
static int
1023
isdn_tty_startup(modem_info * info)
1024
{
1025
        ulong flags;
1026
 
1027
        if (info->flags & ISDN_ASYNC_INITIALIZED)
1028
                return 0;
1029
        save_flags(flags);
1030
        cli();
1031
        isdn_MOD_INC_USE_COUNT();
1032
#ifdef ISDN_DEBUG_MODEM_OPEN
1033
        printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
1034
#endif
1035
        /*
1036
         * Now, initialize the UART
1037
         */
1038
        info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
1039
        if (info->tty)
1040
                clear_bit(TTY_IO_ERROR, &info->tty->flags);
1041
        /*
1042
         * and set the speed of the serial port
1043
         */
1044
        isdn_tty_change_speed(info);
1045
 
1046
        info->flags |= ISDN_ASYNC_INITIALIZED;
1047
        info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
1048
        info->send_outstanding = 0;
1049
        restore_flags(flags);
1050
        return 0;
1051
}
1052
 
1053
/*
1054
 * This routine will shutdown a serial port; interrupts are disabled, and
1055
 * DTR is dropped if the hangup on close termio flag is on.
1056
 */
1057
static void
1058
isdn_tty_shutdown(modem_info * info)
1059
{
1060
        ulong flags;
1061
 
1062
        if (!(info->flags & ISDN_ASYNC_INITIALIZED))
1063
                return;
1064
#ifdef ISDN_DEBUG_MODEM_OPEN
1065
        printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
1066
#endif
1067
        save_flags(flags);
1068
        cli();                  /* Disable interrupts */
1069
        isdn_MOD_DEC_USE_COUNT();
1070
        info->msr &= ~UART_MSR_RI;
1071
        if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
1072
                info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
1073
                if (info->emu.mdmreg[13] & 4) {
1074
                        isdn_tty_modem_reset_regs(info, 0);
1075
#ifdef ISDN_DEBUG_MODEM_HUP
1076
                        printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
1077
#endif
1078
                        isdn_tty_modem_hup(info, 1);
1079
                }
1080
        }
1081
        if (info->tty)
1082
                set_bit(TTY_IO_ERROR, &info->tty->flags);
1083
 
1084
        info->flags &= ~ISDN_ASYNC_INITIALIZED;
1085
        restore_flags(flags);
1086
}
1087
 
1088
/* isdn_tty_write() is the main send-routine. It is called from the upper
1089
 * levels within the kernel to perform sending data. Depending on the
1090
 * online-flag it either directs output to the at-command-interpreter or
1091
 * to the lower level. Additional tasks done here:
1092
 *  - If online, check for escape-sequence (+++)
1093
 *  - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
1094
 *  - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
1095
 *  - If dialing, abort dial.
1096
 */
1097
static int
1098
isdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count)
1099
{
1100
        int c,
1101
         total = 0;
1102
        ulong flags;
1103
        modem_info *info = (modem_info *) tty->driver_data;
1104
 
1105
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write"))
1106
                return 0;
1107
        if (!tty)
1108
                return 0;
1109
        save_flags(flags);
1110
        cli();
1111
        while (1) {
1112
                c = MIN(count, info->xmit_size - info->xmit_count);
1113
                if (info->isdn_driver >= 0)
1114
                        c = MIN(c, dev->drv[info->isdn_driver]->maxbufsize);
1115
                if (c <= 0)
1116
                        break;
1117
                if ((info->online > 1)
1118
#ifdef CONFIG_ISDN_AUDIO
1119
                    || (info->vonline & 3)
1120
#endif
1121
                        ) {
1122
                        atemu *m = &info->emu;
1123
 
1124
#ifdef CONFIG_ISDN_AUDIO
1125
                        if (!info->vonline)
1126
#endif
1127
                                isdn_tty_check_esc(buf, m->mdmreg[2], c,
1128
                                                   &(m->pluscount),
1129
                                                   &(m->lastplus),
1130
                                                   from_user);
1131
                        if (from_user)
1132
                                copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c);
1133
                        else
1134
                                memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
1135
#ifdef CONFIG_ISDN_AUDIO
1136
                        if (info->vonline) {
1137
                                int cc = isdn_tty_handleDLEdown(info, m, c);
1138
                                if (info->vonline & 2) {
1139
                                        if (!cc) {
1140
                                                /* If DLE decoding results in zero-transmit, but
1141
                                                 * c originally was non-zero, do a wakeup.
1142
                                                 */
1143
                                                if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
1144
                                                 tty->ldisc.write_wakeup)
1145
                                                        (tty->ldisc.write_wakeup) (tty);
1146
                                                wake_up_interruptible(&tty->write_wait);
1147
                                                info->msr |= UART_MSR_CTS;
1148
                                                info->lsr |= UART_LSR_TEMT;
1149
                                        }
1150
                                        info->xmit_count += cc;
1151
                                }
1152
                                if ((info->vonline & 3) == 1) {
1153
                                        /* Do NOT handle Ctrl-Q or Ctrl-S
1154
                                         * when in full-duplex audio mode.
1155
                                         */
1156
                                        if (isdn_tty_end_vrx(buf, c, from_user)) {
1157
                                                info->vonline &= ~1;
1158
#ifdef ISDN_DEBUG_MODEM_VOICE
1159
                                                printk(KERN_DEBUG
1160
                                                       "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n",
1161
                                                       info->line);
1162
#endif
1163
                                                isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
1164
                                        }
1165
                                }
1166
                        } else
1167
#endif
1168
                                info->xmit_count += c;
1169
                        if (m->mdmreg[13] & 1) {
1170
                                isdn_tty_senddown(info);
1171
                                isdn_tty_tint(info);
1172
                        }
1173
                } else {
1174
                        info->msr |= UART_MSR_CTS;
1175
                        info->lsr |= UART_LSR_TEMT;
1176
                        if (info->dialing) {
1177
                                info->dialing = 0;
1178
#ifdef ISDN_DEBUG_MODEM_HUP
1179
                                printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
1180
#endif
1181
                                isdn_tty_modem_result(3, info);
1182
                                isdn_tty_modem_hup(info, 1);
1183
                        } else
1184
                                c = isdn_tty_edit_at(buf, c, info, from_user);
1185
                }
1186
                buf += c;
1187
                count -= c;
1188
                total += c;
1189
        }
1190
        if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
1191
                isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
1192
        restore_flags(flags);
1193
        return total;
1194
}
1195
 
1196
static int
1197
isdn_tty_write_room(struct tty_struct *tty)
1198
{
1199
        modem_info *info = (modem_info *) tty->driver_data;
1200
        int ret;
1201
 
1202
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room"))
1203
                return 0;
1204
        if (!info->online)
1205
                return info->xmit_size;
1206
        ret = info->xmit_size - info->xmit_count;
1207
        return (ret < 0) ? 0 : ret;
1208
}
1209
 
1210
static int
1211
isdn_tty_chars_in_buffer(struct tty_struct *tty)
1212
{
1213
        modem_info *info = (modem_info *) tty->driver_data;
1214
 
1215
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer"))
1216
                return 0;
1217
        if (!info->online)
1218
                return 0;
1219
        return (info->xmit_count);
1220
}
1221
 
1222
static void
1223
isdn_tty_flush_buffer(struct tty_struct *tty)
1224
{
1225
        modem_info *info;
1226
        unsigned long flags;
1227
 
1228
        save_flags(flags);
1229
        cli();
1230
        if (!tty) {
1231
                restore_flags(flags);
1232
                return;
1233
        }
1234
        info = (modem_info *) tty->driver_data;
1235
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) {
1236
                restore_flags(flags);
1237
                return;
1238
        }
1239
        isdn_tty_cleanup_xmit(info);
1240
        info->xmit_count = 0;
1241
        restore_flags(flags);
1242
        wake_up_interruptible(&tty->write_wait);
1243
        if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
1244
            tty->ldisc.write_wakeup)
1245
                (tty->ldisc.write_wakeup) (tty);
1246
}
1247
 
1248
static void
1249
isdn_tty_flush_chars(struct tty_struct *tty)
1250
{
1251
        modem_info *info = (modem_info *) tty->driver_data;
1252
 
1253
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars"))
1254
                return;
1255
        if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue)))
1256
                isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
1257
}
1258
 
1259
/*
1260
 * ------------------------------------------------------------
1261
 * isdn_tty_throttle()
1262
 *
1263
 * This routine is called by the upper-layer tty layer to signal that
1264
 * incoming characters should be throttled.
1265
 * ------------------------------------------------------------
1266
 */
1267
static void
1268
isdn_tty_throttle(struct tty_struct *tty)
1269
{
1270
        modem_info *info = (modem_info *) tty->driver_data;
1271
 
1272
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_throttle"))
1273
                return;
1274
        if (I_IXOFF(tty))
1275
                info->x_char = STOP_CHAR(tty);
1276
        info->mcr &= ~UART_MCR_RTS;
1277
}
1278
 
1279
static void
1280
isdn_tty_unthrottle(struct tty_struct *tty)
1281
{
1282
        modem_info *info = (modem_info *) tty->driver_data;
1283
 
1284
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_unthrottle"))
1285
                return;
1286
        if (I_IXOFF(tty)) {
1287
                if (info->x_char)
1288
                        info->x_char = 0;
1289
                else
1290
                        info->x_char = START_CHAR(tty);
1291
        }
1292
        info->mcr |= UART_MCR_RTS;
1293
}
1294
 
1295
/*
1296
 * ------------------------------------------------------------
1297
 * isdn_tty_ioctl() and friends
1298
 * ------------------------------------------------------------
1299
 */
1300
 
1301
/*
1302
 * isdn_tty_get_lsr_info - get line status register info
1303
 *
1304
 * Purpose: Let user call ioctl() to get info when the UART physically
1305
 *          is emptied.  On bus types like RS485, the transmitter must
1306
 *          release the bus after transmitting. This must be done when
1307
 *          the transmit shift register is empty, not be done when the
1308
 *          transmit holding register is empty.  This functionality
1309
 *          allows RS485 driver to be written in user space.
1310
 */
1311
static int
1312
isdn_tty_get_lsr_info(modem_info * info, uint * value)
1313
{
1314
        u_char status;
1315
        uint result;
1316
        ulong flags;
1317
 
1318
        save_flags(flags);
1319
        cli();
1320
        status = info->lsr;
1321
        restore_flags(flags);
1322
        result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
1323
        put_user(result, (uint *) value);
1324
        return 0;
1325
}
1326
 
1327
 
1328
static int
1329
isdn_tty_get_modem_info(modem_info * info, uint * value)
1330
{
1331
        u_char control,
1332
         status;
1333
        uint result;
1334
        ulong flags;
1335
 
1336
        control = info->mcr;
1337
        save_flags(flags);
1338
        cli();
1339
        status = info->msr;
1340
        restore_flags(flags);
1341
        result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
1342
            | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
1343
            | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
1344
            | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
1345
            | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
1346
            | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
1347
        put_user(result, (uint *) value);
1348
        return 0;
1349
}
1350
 
1351
static int
1352
isdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value)
1353
{
1354
        uint arg;
1355
        int pre_dtr;
1356
 
1357
        GET_USER(arg, (uint *) value);
1358
        switch (cmd) {
1359
                case TIOCMBIS:
1360
#ifdef ISDN_DEBUG_MODEM_IOCTL
1361
                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);
1362
#endif
1363
                        if (arg & TIOCM_RTS) {
1364
                                info->mcr |= UART_MCR_RTS;
1365
                        }
1366
                        if (arg & TIOCM_DTR) {
1367
                                info->mcr |= UART_MCR_DTR;
1368
                                isdn_tty_modem_ncarrier(info);
1369
                        }
1370
                        break;
1371
                case TIOCMBIC:
1372
#ifdef ISDN_DEBUG_MODEM_IOCTL
1373
                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);
1374
#endif
1375
                        if (arg & TIOCM_RTS) {
1376
                                info->mcr &= ~UART_MCR_RTS;
1377
                        }
1378
                        if (arg & TIOCM_DTR) {
1379
                                info->mcr &= ~UART_MCR_DTR;
1380
                                if (info->emu.mdmreg[13] & 4) {
1381
                                        isdn_tty_modem_reset_regs(info, 0);
1382
#ifdef ISDN_DEBUG_MODEM_HUP
1383
                                        printk(KERN_DEBUG "Mhup in TIOCMBIC\n");
1384
#endif
1385
                                        if (info->online)
1386
                                                info->ncarrier = 1;
1387
                                        isdn_tty_modem_hup(info, 1);
1388
                                }
1389
                        }
1390
                        break;
1391
                case TIOCMSET:
1392
#ifdef ISDN_DEBUG_MODEM_IOCTL
1393
                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);
1394
#endif
1395
                        pre_dtr = (info->mcr & UART_MCR_DTR);
1396
                        info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR))
1397
                                 | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
1398
                               | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
1399
                        if (pre_dtr |= (info->mcr & UART_MCR_DTR)) {
1400
                                if (!(info->mcr & UART_MCR_DTR)) {
1401
                                        if (info->emu.mdmreg[13] & 4) {
1402
                                                isdn_tty_modem_reset_regs(info, 0);
1403
#ifdef ISDN_DEBUG_MODEM_HUP
1404
                                                printk(KERN_DEBUG "Mhup in TIOCMSET\n");
1405
#endif
1406
                                                if (info->online)
1407
                                                        info->ncarrier = 1;
1408
                                                isdn_tty_modem_hup(info, 1);
1409
                                        }
1410
                                } else
1411
                                        isdn_tty_modem_ncarrier(info);
1412
                        }
1413
                        break;
1414
                default:
1415
                        return -EINVAL;
1416
        }
1417
        return 0;
1418
}
1419
 
1420
static int
1421
isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
1422
               uint cmd, ulong arg)
1423
{
1424
        modem_info *info = (modem_info *) tty->driver_data;
1425
        int error;
1426
        int retval;
1427
 
1428
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_ioctl"))
1429
                return -ENODEV;
1430
        if (tty->flags & (1 << TTY_IO_ERROR))
1431
                return -EIO;
1432
        switch (cmd) {
1433
                case TCSBRK:   /* SVID version: non-zero arg --> no break */
1434
#ifdef ISDN_DEBUG_MODEM_IOCTL
1435
                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
1436
#endif
1437
                        retval = tty_check_change(tty);
1438
                        if (retval)
1439
                                return retval;
1440
                        tty_wait_until_sent(tty, 0);
1441
                        return 0;
1442
                case TCSBRKP:  /* support for POSIX tcsendbreak() */
1443
#ifdef ISDN_DEBUG_MODEM_IOCTL
1444
                        printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
1445
#endif
1446
                        retval = tty_check_change(tty);
1447
                        if (retval)
1448
                                return retval;
1449
                        tty_wait_until_sent(tty, 0);
1450
                        return 0;
1451
                case TIOCGSOFTCAR:
1452
#ifdef ISDN_DEBUG_MODEM_IOCTL
1453
                        printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line);
1454
#endif
1455
                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long));
1456
                        if (error)
1457
                                return error;
1458
                        put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg);
1459
                        return 0;
1460
                case TIOCSSOFTCAR:
1461
#ifdef ISDN_DEBUG_MODEM_IOCTL
1462
                        printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line);
1463
#endif
1464
                        error = verify_area(VERIFY_READ, (void *) arg, sizeof(long));
1465
                        if (error)
1466
                                return error;
1467
                        GET_USER(arg, (ulong *) arg);
1468
                        tty->termios->c_cflag =
1469
                            ((tty->termios->c_cflag & ~CLOCAL) |
1470
                             (arg ? CLOCAL : 0));
1471
                        return 0;
1472
                case TIOCMGET:
1473
#ifdef ISDN_DEBUG_MODEM_IOCTL
1474
                        printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
1475
#endif
1476
                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
1477
                        if (error)
1478
                                return error;
1479
                        return isdn_tty_get_modem_info(info, (uint *) arg);
1480
                case TIOCMBIS:
1481
                case TIOCMBIC:
1482
                case TIOCMSET:
1483
                        error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint));
1484
                        if (error)
1485
                                return error;
1486
                        return isdn_tty_set_modem_info(info, cmd, (uint *) arg);
1487
                case TIOCSERGETLSR:     /* Get line status register */
1488
#ifdef ISDN_DEBUG_MODEM_IOCTL
1489
                        printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
1490
#endif
1491
                        error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint));
1492
                        if (error)
1493
                                return error;
1494
                        else
1495
                                return isdn_tty_get_lsr_info(info, (uint *) arg);
1496
                default:
1497
#ifdef ISDN_DEBUG_MODEM_IOCTL
1498
                        printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
1499
#endif
1500
                        return -ENOIOCTLCMD;
1501
        }
1502
        return 0;
1503
}
1504
 
1505
static void
1506
isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios)
1507
{
1508
        modem_info *info = (modem_info *) tty->driver_data;
1509
 
1510
        if (!old_termios)
1511
                isdn_tty_change_speed(info);
1512
        else {
1513
                if (tty->termios->c_cflag == old_termios->c_cflag)
1514
                        return;
1515
                isdn_tty_change_speed(info);
1516
                if ((old_termios->c_cflag & CRTSCTS) &&
1517
                    !(tty->termios->c_cflag & CRTSCTS)) {
1518
                        tty->hw_stopped = 0;
1519
                }
1520
        }
1521
}
1522
 
1523
/*
1524
 * ------------------------------------------------------------
1525
 * isdn_tty_open() and friends
1526
 * ------------------------------------------------------------
1527
 */
1528
static int
1529
isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
1530
{
1531
        struct wait_queue wait =
1532
        {current, NULL};
1533
        int do_clocal = 0;
1534
        unsigned long flags;
1535
        int retval;
1536
 
1537
        /*
1538
         * If the device is in the middle of being closed, then block
1539
         * until it's done, and then try again.
1540
         */
1541
        if (tty_hung_up_p(filp) ||
1542
            (info->flags & ISDN_ASYNC_CLOSING)) {
1543
                if (info->flags & ISDN_ASYNC_CLOSING)
1544
                        interruptible_sleep_on(&info->close_wait);
1545
#ifdef MODEM_DO_RESTART
1546
                if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
1547
                        return -EAGAIN;
1548
                else
1549
                        return -ERESTARTSYS;
1550
#else
1551
                return -EAGAIN;
1552
#endif
1553
        }
1554
        /*
1555
         * If this is a callout device, then just make sure the normal
1556
         * device isn't being used.
1557
         */
1558
        if (tty->driver.subtype == ISDN_SERIAL_TYPE_CALLOUT) {
1559
                if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
1560
                        return -EBUSY;
1561
                if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
1562
                    (info->flags & ISDN_ASYNC_SESSION_LOCKOUT) &&
1563
                    (info->session != current->session))
1564
                        return -EBUSY;
1565
                if ((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
1566
                    (info->flags & ISDN_ASYNC_PGRP_LOCKOUT) &&
1567
                    (info->pgrp != current->pgrp))
1568
                        return -EBUSY;
1569
                info->flags |= ISDN_ASYNC_CALLOUT_ACTIVE;
1570
                return 0;
1571
        }
1572
        /*
1573
         * If non-blocking mode is set, then make the check up front
1574
         * and then exit.
1575
         */
1576
        if ((filp->f_flags & O_NONBLOCK) ||
1577
            (tty->flags & (1 << TTY_IO_ERROR))) {
1578
                if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
1579
                        return -EBUSY;
1580
                info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
1581
                return 0;
1582
        }
1583
        if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
1584
                if (info->normal_termios.c_cflag & CLOCAL)
1585
                        do_clocal = 1;
1586
        } else {
1587
                if (tty->termios->c_cflag & CLOCAL)
1588
                        do_clocal = 1;
1589
        }
1590
        /*
1591
         * Block waiting for the carrier detect and the line to become
1592
         * free (i.e., not in use by the callout).  While we are in
1593
         * this loop, info->count is dropped by one, so that
1594
         * isdn_tty_close() knows when to free things.  We restore it upon
1595
         * exit, either normal or abnormal.
1596
         */
1597
        retval = 0;
1598
        add_wait_queue(&info->open_wait, &wait);
1599
#ifdef ISDN_DEBUG_MODEM_OPEN
1600
        printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
1601
               info->line, info->count);
1602
#endif
1603
        save_flags(flags);
1604
        cli();
1605
        if (!(tty_hung_up_p(filp)))
1606
                info->count--;
1607
        restore_flags(flags);
1608
        info->blocked_open++;
1609
        while (1) {
1610
                current->state = TASK_INTERRUPTIBLE;
1611
                if (tty_hung_up_p(filp) ||
1612
                    !(info->flags & ISDN_ASYNC_INITIALIZED)) {
1613
#ifdef MODEM_DO_RESTART
1614
                        if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
1615
                                retval = -EAGAIN;
1616
                        else
1617
                                retval = -ERESTARTSYS;
1618
#else
1619
                        retval = -EAGAIN;
1620
#endif
1621
                        break;
1622
                }
1623
                if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
1624
                    !(info->flags & ISDN_ASYNC_CLOSING) &&
1625
                    (do_clocal || (info->msr & UART_MSR_DCD))) {
1626
                        break;
1627
                }
1628
                if (current->signal & ~current->blocked) {
1629
                        retval = -ERESTARTSYS;
1630
                        break;
1631
                }
1632
#ifdef ISDN_DEBUG_MODEM_OPEN
1633
                printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
1634
                       info->line, info->count);
1635
#endif
1636
                schedule();
1637
        }
1638
        current->state = TASK_RUNNING;
1639
        remove_wait_queue(&info->open_wait, &wait);
1640
        if (!tty_hung_up_p(filp))
1641
                info->count++;
1642
        info->blocked_open--;
1643
#ifdef ISDN_DEBUG_MODEM_OPEN
1644
        printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
1645
               info->line, info->count);
1646
#endif
1647
        if (retval)
1648
                return retval;
1649
        info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
1650
        return 0;
1651
}
1652
 
1653
/*
1654
 * This routine is called whenever a serial port is opened.  It
1655
 * enables interrupts for a serial port, linking in its async structure into
1656
 * the IRQ chain.   It also performs the serial-specific
1657
 * initialization for the tty structure.
1658
 */
1659
static int
1660
isdn_tty_open(struct tty_struct *tty, struct file *filp)
1661
{
1662
        modem_info *info;
1663
        int retval,
1664
         line;
1665
 
1666
        line = MINOR(tty->device) - tty->driver.minor_start;
1667
        if (line < 0 || line > ISDN_MAX_CHANNELS)
1668
                return -ENODEV;
1669
        info = &dev->mdm.info[line];
1670
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_open"))
1671
                return -ENODEV;
1672
#ifdef ISDN_DEBUG_MODEM_OPEN
1673
        printk(KERN_DEBUG "isdn_tty_open %s%d, count = %d\n", tty->driver.name,
1674
               info->line, info->count);
1675
#endif
1676
        info->count++;
1677
        tty->driver_data = info;
1678
        info->tty = tty;
1679
        /*
1680
         * Start up serial port
1681
         */
1682
        retval = isdn_tty_startup(info);
1683
        if (retval) {
1684
#ifdef ISDN_DEBUG_MODEM_OPEN
1685
                printk(KERN_DEBUG "isdn_tty_open return after startup\n");
1686
#endif
1687
                return retval;
1688
        }
1689
        retval = isdn_tty_block_til_ready(tty, filp, info);
1690
        if (retval) {
1691
#ifdef ISDN_DEBUG_MODEM_OPEN
1692
                printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
1693
#endif
1694
                return retval;
1695
        }
1696
        if ((info->count == 1) && (info->flags & ISDN_ASYNC_SPLIT_TERMIOS)) {
1697
                if (tty->driver.subtype == ISDN_SERIAL_TYPE_NORMAL)
1698
                        *tty->termios = info->normal_termios;
1699
                else
1700
                        *tty->termios = info->callout_termios;
1701
                isdn_tty_change_speed(info);
1702
        }
1703
        info->session = current->session;
1704
        info->pgrp = current->pgrp;
1705
#ifdef ISDN_DEBUG_MODEM_OPEN
1706
        printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
1707
#endif
1708
        dev->modempoll++;
1709
#ifdef ISDN_DEBUG_MODEM_OPEN
1710
        printk(KERN_DEBUG "isdn_tty_open normal exit\n");
1711
#endif
1712
        return 0;
1713
}
1714
 
1715
static void
1716
isdn_tty_close(struct tty_struct *tty, struct file *filp)
1717
{
1718
        modem_info *info = (modem_info *) tty->driver_data;
1719
        ulong flags;
1720
        ulong timeout;
1721
 
1722
        if (!info || isdn_tty_paranoia_check(info, tty->device, "isdn_tty_close"))
1723
                return;
1724
        save_flags(flags);
1725
        cli();
1726
        if (tty_hung_up_p(filp)) {
1727
                restore_flags(flags);
1728
#ifdef ISDN_DEBUG_MODEM_OPEN
1729
                printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
1730
#endif
1731
                return;
1732
        }
1733
        if ((tty->count == 1) && (info->count != 1)) {
1734
                /*
1735
                 * Uh, oh.  tty->count is 1, which means that the tty
1736
                 * structure will be freed.  Info->count should always
1737
                 * be one in these conditions.  If it's greater than
1738
                 * one, we've got real problems, since it means the
1739
                 * serial port won't be shutdown.
1740
                 */
1741
                printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
1742
                       "info->count is %d\n", info->count);
1743
                info->count = 1;
1744
        }
1745
        if (--info->count < 0) {
1746
                printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
1747
                       info->line, info->count);
1748
                info->count = 0;
1749
        }
1750
        if (info->count) {
1751
                restore_flags(flags);
1752
#ifdef ISDN_DEBUG_MODEM_OPEN
1753
                printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
1754
#endif
1755
                return;
1756
        }
1757
        info->flags |= ISDN_ASYNC_CLOSING;
1758
        /*
1759
         * Save the termios structure, since this port may have
1760
         * separate termios for callout and dialin.
1761
         */
1762
        if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
1763
                info->normal_termios = *tty->termios;
1764
        if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
1765
                info->callout_termios = *tty->termios;
1766
 
1767
        tty->closing = 1;
1768
        /*
1769
         * At this point we stop accepting input.  To do this, we
1770
         * disable the receive line status interrupts, and tell the
1771
         * interrupt driver to stop checking the data ready bit in the
1772
         * line status register.
1773
         */
1774
        if (info->flags & ISDN_ASYNC_INITIALIZED) {
1775
                tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
1776
                /*
1777
                 * Before we drop DTR, make sure the UART transmitter
1778
                 * has completely drained; this is especially
1779
                 * important if there is a transmit FIFO!
1780
                 */
1781
                timeout = jiffies + HZ;
1782
                while (!(info->lsr & UART_LSR_TEMT)) {
1783
                        current->state = TASK_INTERRUPTIBLE;
1784
                        current->timeout = jiffies + 20;
1785
                        schedule();
1786
                        if (jiffies > timeout)
1787
                                break;
1788
                }
1789
        }
1790
        dev->modempoll--;
1791
        isdn_tty_shutdown(info);
1792
        if (tty->driver.flush_buffer)
1793
                tty->driver.flush_buffer(tty);
1794
        if (tty->ldisc.flush_buffer)
1795
                tty->ldisc.flush_buffer(tty);
1796
        info->tty = 0;
1797
        info->ncarrier = 0;
1798
        tty->closing = 0;
1799
        if (info->blocked_open) {
1800
                current->state = TASK_INTERRUPTIBLE;
1801
                current->timeout = jiffies + 50;
1802
                schedule();
1803
                wake_up_interruptible(&info->open_wait);
1804
        }
1805
        info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE |
1806
                         ISDN_ASYNC_CLOSING);
1807
        wake_up_interruptible(&info->close_wait);
1808
        restore_flags(flags);
1809
#ifdef ISDN_DEBUG_MODEM_OPEN
1810
        printk(KERN_DEBUG "isdn_tty_close normal exit\n");
1811
#endif
1812
}
1813
 
1814
/*
1815
 * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
1816
 */
1817
static void
1818
isdn_tty_hangup(struct tty_struct *tty)
1819
{
1820
        modem_info *info = (modem_info *) tty->driver_data;
1821
 
1822
        if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_hangup"))
1823
                return;
1824
        isdn_tty_shutdown(info);
1825
        info->count = 0;
1826
        info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
1827
        info->tty = 0;
1828
        wake_up_interruptible(&info->open_wait);
1829
}
1830
 
1831
/* This routine initializes all emulator-data.
1832
 */
1833
static void
1834
isdn_tty_reset_profile(atemu * m)
1835
{
1836
        m->profile[0] = 0;
1837
        m->profile[1] = 0;
1838
        m->profile[2] = 43;
1839
        m->profile[3] = 13;
1840
        m->profile[4] = 10;
1841
        m->profile[5] = 8;
1842
        m->profile[6] = 3;
1843
        m->profile[7] = 60;
1844
        m->profile[8] = 2;
1845
        m->profile[9] = 6;
1846
        m->profile[10] = 7;
1847
        m->profile[11] = 70;
1848
        m->profile[12] = 0x45;
1849
        m->profile[13] = 4;
1850
        m->profile[14] = ISDN_PROTO_L2_X75I;
1851
        m->profile[15] = ISDN_PROTO_L3_TRANS;
1852
        m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
1853
        m->profile[17] = ISDN_MODEM_WINSIZE;
1854
        m->profile[18] = 4;
1855
        m->profile[19] = 0;
1856
        m->profile[20] = 0;
1857
        m->pmsn[0] = '\0';
1858
}
1859
 
1860
#ifdef CONFIG_ISDN_AUDIO
1861
static void
1862
isdn_tty_modem_reset_vpar(atemu * m)
1863
{
1864
        m->vpar[0] = 2;         /* Voice-device            (2 = phone line) */
1865
        m->vpar[1] = 0;         /* Silence detection level (0 = none      ) */
1866
        m->vpar[2] = 70;        /* Silence interval        (7 sec.        ) */
1867
        m->vpar[3] = 2;         /* Compression type        (1 = ADPCM-2   ) */
1868
}
1869
#endif
1870
 
1871
static void
1872
isdn_tty_modem_reset_regs(modem_info * info, int force)
1873
{
1874
        atemu *m = &info->emu;
1875
        if ((m->mdmreg[12] & 32) || force) {
1876
                memcpy(m->mdmreg, m->profile, ISDN_MODEM_ANZREG);
1877
                memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
1878
                info->xmit_size = m->mdmreg[16] * 16;
1879
        }
1880
#ifdef CONFIG_ISDN_AUDIO
1881
        isdn_tty_modem_reset_vpar(m);
1882
#endif
1883
        m->mdmcmdl = 0;
1884
}
1885
 
1886
static void
1887
modem_write_profile(atemu * m)
1888
{
1889
        memcpy(m->profile, m->mdmreg, ISDN_MODEM_ANZREG);
1890
        memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
1891
        if (dev->profd)
1892
                send_sig(SIGIO, dev->profd, 1);
1893
}
1894
 
1895
int
1896
isdn_tty_modem_init(void)
1897
{
1898
        modem *m;
1899
        int i;
1900
        modem_info *info;
1901
 
1902
        m = &dev->mdm;
1903
        memset(&m->tty_modem, 0, sizeof(struct tty_driver));
1904
        m->tty_modem.magic = TTY_DRIVER_MAGIC;
1905
        m->tty_modem.name = isdn_ttyname_ttyI;
1906
        m->tty_modem.major = ISDN_TTY_MAJOR;
1907
        m->tty_modem.minor_start = 0;
1908
        m->tty_modem.num = ISDN_MAX_CHANNELS;
1909
        m->tty_modem.type = TTY_DRIVER_TYPE_SERIAL;
1910
        m->tty_modem.subtype = ISDN_SERIAL_TYPE_NORMAL;
1911
        m->tty_modem.init_termios = tty_std_termios;
1912
        m->tty_modem.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
1913
        m->tty_modem.flags = TTY_DRIVER_REAL_RAW;
1914
        m->tty_modem.refcount = &m->refcount;
1915
        m->tty_modem.table = m->modem_table;
1916
        m->tty_modem.termios = m->modem_termios;
1917
        m->tty_modem.termios_locked = m->modem_termios_locked;
1918
        m->tty_modem.open = isdn_tty_open;
1919
        m->tty_modem.close = isdn_tty_close;
1920
        m->tty_modem.write = isdn_tty_write;
1921
        m->tty_modem.put_char = NULL;
1922
        m->tty_modem.flush_chars = isdn_tty_flush_chars;
1923
        m->tty_modem.write_room = isdn_tty_write_room;
1924
        m->tty_modem.chars_in_buffer = isdn_tty_chars_in_buffer;
1925
        m->tty_modem.flush_buffer = isdn_tty_flush_buffer;
1926
        m->tty_modem.ioctl = isdn_tty_ioctl;
1927
        m->tty_modem.throttle = isdn_tty_throttle;
1928
        m->tty_modem.unthrottle = isdn_tty_unthrottle;
1929
        m->tty_modem.set_termios = isdn_tty_set_termios;
1930
        m->tty_modem.stop = NULL;
1931
        m->tty_modem.start = NULL;
1932
        m->tty_modem.hangup = isdn_tty_hangup;
1933
        /*
1934
         * The callout device is just like normal device except for
1935
         * major number and the subtype code.
1936
         */
1937
        m->cua_modem = m->tty_modem;
1938
        m->cua_modem.name = isdn_ttyname_cui;
1939
        m->cua_modem.major = ISDN_TTYAUX_MAJOR;
1940
        m->tty_modem.minor_start = 0;
1941
        m->cua_modem.subtype = ISDN_SERIAL_TYPE_CALLOUT;
1942
 
1943
        if (tty_register_driver(&m->tty_modem)) {
1944
                printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n");
1945
                return -1;
1946
        }
1947
        if (tty_register_driver(&m->cua_modem)) {
1948
                printk(KERN_WARNING "isdn_tty: Couldn't register modem-callout-device\n");
1949
                return -2;
1950
        }
1951
        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
1952
                info = &m->info[i];
1953
                sprintf(info->last_cause, "0000");
1954
                sprintf(info->last_num, "none");
1955
                info->last_dir = 0;
1956
                info->last_lhup = 1;
1957
                info->last_l2 = 0;
1958
                info->last_si = 0;
1959
                isdn_tty_reset_profile(&info->emu);
1960
                isdn_tty_modem_reset_regs(info, 1);
1961
                info->magic = ISDN_ASYNC_MAGIC;
1962
                info->line = i;
1963
                info->tty = 0;
1964
                info->x_char = 0;
1965
                info->count = 0;
1966
                info->blocked_open = 0;
1967
                info->callout_termios = m->cua_modem.init_termios;
1968
                info->normal_termios = m->tty_modem.init_termios;
1969
                info->open_wait = 0;
1970
                info->close_wait = 0;
1971
                info->isdn_driver = -1;
1972
                info->isdn_channel = -1;
1973
                info->drv_index = -1;
1974
                info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
1975
                skb_queue_head_init(&info->xmit_queue);
1976
#ifdef CONFIG_ISDN_AUDIO
1977
                skb_queue_head_init(&info->dtmf_queue);
1978
#endif
1979
                if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
1980
                        printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
1981
                        return -3;
1982
                }
1983
                /* Make room for T.70 header */
1984
                info->xmit_buf += 4;
1985
        }
1986
        return 0;
1987
}
1988
 
1989
/*
1990
 * An incoming call-request has arrived.
1991
 * Search the tty-devices for an appropriate device and bind
1992
 * it to the ISDN-Channel.
1993
 * Return Index to dev->mdm or -1 if none found.
1994
 */
1995
int
1996
isdn_tty_find_icall(int di, isdn_ctrl *c)
1997
{
1998
        char *eaz;
1999
        int ch = c->arg;
2000
        setup_parm *setup = &c->parm.setup;
2001
        int i;
2002
        int idx;
2003
        int si1;
2004
        int si2;
2005
        char nr[32];
2006
        ulong flags;
2007
 
2008
        save_flags(flags);
2009
        cli();
2010
        if (!setup->phone[0]) {
2011
                nr[0] = '0';
2012
                nr[1] = '\0';
2013
                printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
2014
        } else
2015
                strcpy(nr, setup->phone);
2016
        si1 = (int) setup->si1;
2017
        si2 = (int) setup->si2;
2018
        if (!setup->eazmsn[0]) {
2019
                printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
2020
                eaz = "0";
2021
        } else
2022
                eaz = setup->eazmsn;
2023
#ifdef ISDN_DEBUG_MODEM_ICALL
2024
        printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
2025
#endif
2026
        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
2027
                modem_info *info = &dev->mdm.info[i];
2028
#ifdef ISDN_DEBUG_MODEM_ICALL
2029
                printk(KERN_DEBUG "m_fi: i=%d msn=%s mmsn=%s mreg18=%d mreg19=%d\n", i,
2030
                       info->emu.msn, isdn_map_eaz2msn(info->emu.msn, di),
2031
                       info->emu.mdmreg[18], info->emu.mdmreg[19]);
2032
#endif
2033
                if ((!strcmp(isdn_map_eaz2msn(info->emu.msn, di),
2034
                             eaz)) &&   /* EAZ is matching      */
2035
                    (info->emu.mdmreg[18] & si2bit[si1]) &&     /* SI1 is matching      */
2036
                    ((info->emu.mdmreg[19] == si2) || !si2)) {  /* SI2 is matching or 0 */
2037
                        idx = isdn_dc2minor(di, ch);
2038
#ifdef ISDN_DEBUG_MODEM_ICALL
2039
                        printk(KERN_DEBUG "m_fi: match1\n");
2040
                        printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
2041
                               info->flags, info->isdn_driver, info->isdn_channel,
2042
                               dev->usage[idx]);
2043
#endif
2044
                        if ((info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
2045
                            (info->isdn_driver == -1) &&
2046
                            (info->isdn_channel == -1) &&
2047
                            (USG_NONE(dev->usage[idx]))) {
2048
                                info->isdn_driver = di;
2049
                                info->isdn_channel = ch;
2050
                                info->drv_index = idx;
2051
                                dev->m_idx[idx] = info->line;
2052
                                dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
2053
                                dev->usage[idx] |= (si1 == 1) ? ISDN_USAGE_VOICE : ISDN_USAGE_MODEM;
2054
                                strcpy(dev->num[idx], nr);
2055
                                info->emu.mdmreg[20] = si2bit[si1];
2056
                                info->emu.mdmreg[21] = setup->plan;
2057
                                info->emu.mdmreg[22] = setup->screen;
2058
                                isdn_info_update();
2059
                                restore_flags(flags);
2060
                                printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
2061
                                       info->line);
2062
                                info->msr |= UART_MSR_RI;
2063
                                isdn_tty_modem_result(2, info);
2064
                                isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
2065
                                return info->line;
2066
                        }
2067
                }
2068
        }
2069
        printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
2070
               (dev->drv[di]->flags & DRV_FLAG_REJBUS) ? "rejected" : "ignored");
2071
        restore_flags(flags);
2072
        return -1;
2073
}
2074
 
2075
#define TTY_IS_ACTIVE(info) \
2076
        (info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE))
2077
 
2078
int
2079
isdn_tty_stat_callback(int i, isdn_ctrl * c)
2080
{
2081
        int mi;
2082
        modem_info *info;
2083
 
2084
        if (i < 0)
2085
                return 0;
2086
        if ((mi = dev->m_idx[i]) >= 0) {
2087
                info = &dev->mdm.info[mi];
2088
                switch (c->command) {
2089
                        case ISDN_STAT_BSENT:
2090
#ifdef ISDN_TTY_STAT_DEBUG
2091
                                printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
2092
#endif
2093
                                if ((info->isdn_driver == c->driver) &&
2094
                                    (info->isdn_channel == c->arg)) {
2095
                                        info->msr |= UART_MSR_CTS;
2096
                                        if (info->send_outstanding)
2097
                                                if (!(--info->send_outstanding))
2098
                                                        info->lsr |= UART_LSR_TEMT;
2099
                                        isdn_tty_tint(info);
2100
                                        return 1;
2101
                                }
2102
                                break;
2103
                        case ISDN_STAT_CAUSE:
2104
#ifdef ISDN_TTY_STAT_DEBUG
2105
                                printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line);
2106
#endif
2107
                                /* Signal cause to tty-device */
2108
                                strncpy(info->last_cause, c->parm.num, 5);
2109
                                return 1;
2110
                        case ISDN_STAT_DCONN:
2111
#ifdef ISDN_TTY_STAT_DEBUG
2112
                                printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
2113
#endif
2114
                                if (TTY_IS_ACTIVE(info)) {
2115
                                        if (info->dialing == 1) {
2116
                                                info->dialing = 2;
2117
                                                return 1;
2118
                                        }
2119
                                }
2120
                                break;
2121
                        case ISDN_STAT_DHUP:
2122
#ifdef ISDN_TTY_STAT_DEBUG
2123
                                printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
2124
#endif
2125
                                if (TTY_IS_ACTIVE(info)) {
2126
                                        if (info->dialing == 1) {
2127
                                                info->dialing = 0;
2128
                                                isdn_tty_modem_result(7, info);
2129
                                        }
2130
#ifdef ISDN_DEBUG_MODEM_HUP
2131
                                        printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
2132
#endif
2133
                                        isdn_tty_modem_hup(info, 0);
2134
                                        return 1;
2135
                                }
2136
                                break;
2137
                        case ISDN_STAT_BCONN:
2138
#ifdef ISDN_TTY_STAT_DEBUG
2139
                                printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
2140
#endif
2141
                                /* Schedule CONNECT-Message to any tty
2142
                                 * waiting for it and
2143
                                 * set DCD-bit of its modem-status.
2144
                                 */
2145
                                if (TTY_IS_ACTIVE(info)) {
2146
                                        info->msr |= UART_MSR_DCD;
2147
                                        if (info->dialing) {
2148
                                                info->dialing = 0;
2149
                                                info->last_dir = 1;
2150
                                        } else
2151
                                                info->last_dir = 0;
2152
                                        info->rcvsched = 1;
2153
                                        if (USG_MODEM(dev->usage[i]))
2154
                                                isdn_tty_modem_result(5, info);
2155
                                        if (USG_VOICE(dev->usage[i]))
2156
                                                isdn_tty_modem_result(11, info);
2157
                                        return 1;
2158
                                }
2159
                                break;
2160
                        case ISDN_STAT_BHUP:
2161
#ifdef ISDN_TTY_STAT_DEBUG
2162
                                printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line);
2163
#endif
2164
                                if (TTY_IS_ACTIVE(info)) {
2165
#ifdef ISDN_DEBUG_MODEM_HUP
2166
                                        printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
2167
#endif
2168
                                        isdn_tty_modem_hup(info, 0);
2169
                                        return 1;
2170
                                }
2171
                                break;
2172
                        case ISDN_STAT_NODCH:
2173
#ifdef ISDN_TTY_STAT_DEBUG
2174
                                printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line);
2175
#endif
2176
                                if (TTY_IS_ACTIVE(info)) {
2177
                                        if (info->dialing) {
2178
                                                info->dialing = 0;
2179
                                                info->last_l2 = -1;
2180
                                                info->last_si = 0;
2181
                                                sprintf(info->last_cause, "0000");
2182
                                                isdn_tty_modem_result(6, info);
2183
                                        }
2184
                                        info->msr &= ~UART_MSR_DCD;
2185
                                        if (info->online) {
2186
                                                isdn_tty_modem_result(3, info);
2187
                                                info->online = 0;
2188
                                        }
2189
                                        return 1;
2190
                                }
2191
                                break;
2192
                        case ISDN_STAT_UNLOAD:
2193
#ifdef ISDN_TTY_STAT_DEBUG
2194
                                printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line);
2195
#endif
2196
                                for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
2197
                                        info = &dev->mdm.info[i];
2198
                                        if (info->isdn_driver == c->driver) {
2199
                                                if (info->online)
2200
                                                        isdn_tty_modem_hup(info, 1);
2201
                                        }
2202
                                }
2203
                                return 1;
2204
                }
2205
        }
2206
        return 0;
2207
}
2208
 
2209
/*********************************************************************
2210
 Modem-Emulator-Routines
2211
 *********************************************************************/
2212
 
2213
#define cmdchar(c) ((c>' ')&&(c<=0x7f))
2214
 
2215
/*
2216
 * Put a message from the AT-emulator into receive-buffer of tty,
2217
 * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
2218
 */
2219
static void
2220
isdn_tty_at_cout(char *msg, modem_info * info)
2221
{
2222
        struct tty_struct *tty;
2223
        atemu *m = &info->emu;
2224
        char *p;
2225
        char c;
2226
        ulong flags;
2227
 
2228
        if (!msg) {
2229
                printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
2230
                return;
2231
        }
2232
        save_flags(flags);
2233
        cli();
2234
        tty = info->tty;
2235
        for (p = msg; *p; p++) {
2236
                switch (*p) {
2237
                        case '\r':
2238
                                c = m->mdmreg[3];
2239
                                break;
2240
                        case '\n':
2241
                                c = m->mdmreg[4];
2242
                                break;
2243
                        case '\b':
2244
                                c = m->mdmreg[5];
2245
                                break;
2246
                        default:
2247
                                c = *p;
2248
                }
2249
                if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
2250
                        restore_flags(flags);
2251
                        return;
2252
                }
2253
                if (tty->flip.count >= TTY_FLIPBUF_SIZE)
2254
                        break;
2255
                tty_insert_flip_char(tty, c, 0);
2256
        }
2257
        restore_flags(flags);
2258
        queue_task(&tty->flip.tqueue, &tq_timer);
2259
}
2260
 
2261
/*
2262
 * Perform ATH Hangup
2263
 */
2264
static void
2265
isdn_tty_on_hook(modem_info * info)
2266
{
2267
        if (info->isdn_channel >= 0) {
2268
#ifdef ISDN_DEBUG_MODEM_HUP
2269
                printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
2270
#endif
2271
                isdn_tty_modem_hup(info, 1);
2272
        }
2273
}
2274
 
2275
static void
2276
isdn_tty_off_hook(void)
2277
{
2278
        printk(KERN_DEBUG "isdn_tty_off_hook\n");
2279
}
2280
 
2281
#define PLUSWAIT1 (HZ/2)        /* 0.5 sec. */
2282
#define PLUSWAIT2 (HZ*3/2)      /* 1.5 sec */
2283
 
2284
/*
2285
 * Check Buffer for Modem-escape-sequence, activate timer-callback to
2286
 * isdn_tty_modem_escape() if sequence found.
2287
 *
2288
 * Parameters:
2289
 *   p          pointer to databuffer
2290
 *   plus       escape-character
2291
 *   count      length of buffer
2292
 *   pluscount  count of valid escape-characters so far
2293
 *   lastplus   timestamp of last character
2294
 */
2295
static void
2296
isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
2297
                   int *lastplus, int from_user)
2298
{
2299
        char cbuf[3];
2300
 
2301
        if (plus > 127)
2302
                return;
2303
        if (count > 3) {
2304
                p += count - 3;
2305
                count = 3;
2306
                *pluscount = 0;
2307
        }
2308
        if (from_user) {
2309
                copy_from_user(cbuf, p, count);
2310
                p = cbuf;
2311
        }
2312
        while (count > 0) {
2313
                if (*(p++) == plus) {
2314
                        if ((*pluscount)++) {
2315
                                /* Time since last '+' > 0.5 sec. ? */
2316
                                if ((jiffies - *lastplus) > PLUSWAIT1)
2317
                                        *pluscount = 1;
2318
                        } else {
2319
                                /* Time since last non-'+' < 1.5 sec. ? */
2320
                                if ((jiffies - *lastplus) < PLUSWAIT2)
2321
                                        *pluscount = 0;
2322
                        }
2323
                        if ((*pluscount == 3) && (count = 1))
2324
                                isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
2325
                        if (*pluscount > 3)
2326
                                *pluscount = 1;
2327
                } else
2328
                        *pluscount = 0;
2329
                *lastplus = jiffies;
2330
                count--;
2331
        }
2332
}
2333
 
2334
/*
2335
 * Return result of AT-emulator to tty-receive-buffer, depending on
2336
 * modem-register 12, bit 0 and 1.
2337
 * For CONNECT-messages also switch to online-mode.
2338
 * For RING-message handle auto-ATA if register 0 != 0
2339
 */
2340
static void
2341
isdn_tty_modem_result(int code, modem_info * info)
2342
{
2343
        atemu *m = &info->emu;
2344
        static char *msg[] =
2345
        {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
2346
         "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
2347
         "RINGING", "NO MSN/EAZ", "VCON"};
2348
        ulong flags;
2349
        char s[10];
2350
 
2351
        switch (code) {
2352
                case 2:
2353
                        m->mdmreg[1]++; /* RING */
2354
                        if (m->mdmreg[1] == m->mdmreg[0])
2355
                                /* Automatically accept incoming call */
2356
                                isdn_tty_cmd_ATA(info);
2357
                        break;
2358
                case 3:
2359
                        /* NO CARRIER */
2360
#ifdef ISDN_DEBUG_MODEM_HUP
2361
                        printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
2362
                               (info->flags & ISDN_ASYNC_CLOSING),
2363
                               (!info->tty));
2364
#endif
2365
                        save_flags(flags);
2366
                        cli();
2367
                        m->mdmreg[1] = 0;
2368
                        del_timer(&info->nc_timer);
2369
                        info->ncarrier = 0;
2370
                        if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
2371
                                restore_flags(flags);
2372
                                return;
2373
                        }
2374
                        restore_flags(flags);
2375
#ifdef CONFIG_ISDN_AUDIO
2376
                        if (info->vonline & 1) {
2377
#ifdef ISDN_DEBUG_MODEM_VOICE
2378
                                printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n",
2379
                                       info->line);
2380
#endif
2381
                                /* voice-recording, add DLE-ETX */
2382
                                isdn_tty_at_cout("\020\003", info);
2383
                        }
2384
                        if (info->vonline & 2) {
2385
#ifdef ISDN_DEBUG_MODEM_VOICE
2386
                                printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n",
2387
                                       info->line);
2388
#endif
2389
                                /* voice-playing, add DLE-DC4 */
2390
                                isdn_tty_at_cout("\020\024", info);
2391
                        }
2392
#endif
2393
                        break;
2394
                case 1:
2395
                case 5:
2396
                        sprintf(info->last_cause, "0000");
2397
                        if (!info->online)
2398
                                info->online = 2;
2399
                        break;
2400
                case 11:
2401
#ifdef ISDN_DEBUG_MODEM_VOICE
2402
                        printk(KERN_DEBUG "res3: send VCON on ttyI%d\n",
2403
                               info->line);
2404
#endif
2405
                        sprintf(info->last_cause, "0000");
2406
                        if (!info->online)
2407
                                info->online = 1;
2408
                        break;
2409
        }
2410
        if (m->mdmreg[12] & 1) {
2411
                /* Show results */
2412
                isdn_tty_at_cout("\r\n", info);
2413
                if (m->mdmreg[12] & 2) {
2414
                        /* Show numeric results */
2415
                        sprintf(s, "%d", code);
2416
                        isdn_tty_at_cout(s, info);
2417
                } else {
2418
                        if ((code == 2) && (!(m->mdmreg[13] & 16))) {
2419
                                isdn_tty_at_cout("CALLER NUMBER: ", info);
2420
                                isdn_tty_at_cout(dev->num[info->drv_index], info);
2421
                                isdn_tty_at_cout("\r\n", info);
2422
                        }
2423
                        isdn_tty_at_cout(msg[code], info);
2424
                        switch (code) {
2425
                                case 2:
2426
                                        /* Print CID only once, _after_ 1.st RING */
2427
                                        if ((m->mdmreg[13] & 16) && (m->mdmreg[1] == 1)) {
2428
                                                isdn_tty_at_cout("\r\n", info);
2429
                                                isdn_tty_at_cout("CALLER NUMBER: ", info);
2430
                                                isdn_tty_at_cout(dev->num[info->drv_index], info);
2431
                                        }
2432
                                        break;
2433
                                case 3:
2434
                                case 6:
2435
                                case 7:
2436
                                case 8:
2437
                                        m->mdmreg[1] = 0;
2438
                                        /* Append Cause-Message if enabled */
2439
                                        if (m->mdmreg[13] & 8) {
2440
                                                sprintf(s, "/%s", info->last_cause);
2441
                                                isdn_tty_at_cout(s, info);
2442
                                        }
2443
                                        break;
2444
                                case 5:
2445
                                        /* Append Protocol to CONNECT message */
2446
                                        isdn_tty_at_cout((m->mdmreg[14] != 3) ? "/X.75" : "/HDLC", info);
2447
                                        if (m->mdmreg[13] & 2)
2448
                                                isdn_tty_at_cout("/T.70", info);
2449
                                        break;
2450
                        }
2451
                }
2452
                isdn_tty_at_cout("\r\n", info);
2453
        }
2454
        if (code == 3) {
2455
                save_flags(flags);
2456
                cli();
2457
                if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
2458
                        restore_flags(flags);
2459
                        return;
2460
                }
2461
                if (info->tty->ldisc.flush_buffer)
2462
                        info->tty->ldisc.flush_buffer(info->tty);
2463
                if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
2464
                    (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
2465
                       (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
2466
                        tty_hangup(info->tty);
2467
                }
2468
                restore_flags(flags);
2469
        }
2470
}
2471
 
2472
/*
2473
 * Display a modem-register-value.
2474
 */
2475
static void
2476
isdn_tty_show_profile(int ridx, modem_info * info)
2477
{
2478
        char v[6];
2479
 
2480
        sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
2481
        isdn_tty_at_cout(v, info);
2482
}
2483
 
2484
/*
2485
 * Get MSN-string from char-pointer, set pointer to end of number
2486
 */
2487
static void
2488
isdn_tty_get_msnstr(char *n, char **p)
2489
{
2490
        while ((*p[0] >= '0' && *p[0] <= '9') || (*p[0] == ','))
2491
                *n++ = *p[0]++;
2492
        *n = '\0';
2493
}
2494
 
2495
/*
2496
 * Get phone-number from modem-commandbuffer
2497
 */
2498
static void
2499
isdn_tty_getdial(char *p, char *q, int max)
2500
{
2501
        int first = 1;
2502
 
2503
        max--;
2504
        while (strchr("0123456789,#.*WPTS-", *p) && *p && (max > 0)) {
2505
                if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first)) {
2506
                        *q++ = *p;
2507
                        max--;
2508
                }
2509
                p++;
2510
                first = 0;
2511
        }
2512
        *q = 0;
2513
}
2514
 
2515
#define PARSE_ERROR { isdn_tty_modem_result(4, info); return; }
2516
#define PARSE_ERROR1 { isdn_tty_modem_result(4, info); return 1; }
2517
 
2518
static void
2519
isdn_tty_report(modem_info * info)
2520
{
2521
        atemu *m = &info->emu;
2522
        char s[80];
2523
 
2524
        isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
2525
        sprintf(s, "    Remote Number:    %s\r\n", info->last_num);
2526
        isdn_tty_at_cout(s, info);
2527
        sprintf(s, "    Direction:        %s\r\n", info->last_dir ? "outgoing" : "incoming");
2528
        isdn_tty_at_cout(s, info);
2529
        isdn_tty_at_cout("    Layer-2 Protocol: ", info);
2530
        switch (info->last_l2) {
2531
                case ISDN_PROTO_L2_X75I:
2532
                        isdn_tty_at_cout("x75i", info);
2533
                        break;
2534
                case ISDN_PROTO_L2_X75UI:
2535
                        isdn_tty_at_cout("x75ui", info);
2536
                        break;
2537
                case ISDN_PROTO_L2_X75BUI:
2538
                        isdn_tty_at_cout("x75bui", info);
2539
                        break;
2540
                case ISDN_PROTO_L2_HDLC:
2541
                        isdn_tty_at_cout("hdlc", info);
2542
                        break;
2543
                case ISDN_PROTO_L2_TRANS:
2544
                        isdn_tty_at_cout("transparent", info);
2545
                        break;
2546
                default:
2547
                        isdn_tty_at_cout("unknown", info);
2548
                        break;
2549
        }
2550
        isdn_tty_at_cout((m->mdmreg[13] & 2) ? "/t.70\r\n" : "\r\n", info);
2551
        isdn_tty_at_cout("    Service:          ", info);
2552
        switch (info->last_si) {
2553
                case 1:
2554
                        isdn_tty_at_cout("audio\r\n", info);
2555
                        break;
2556
                case 5:
2557
                        isdn_tty_at_cout("btx\r\n", info);
2558
                        break;
2559
                case 7:
2560
                        isdn_tty_at_cout("data\r\n", info);
2561
                        break;
2562
                default:
2563
                        sprintf(s, "%d\r\n", info->last_si);
2564
                        isdn_tty_at_cout(s, info);
2565
                        break;
2566
        }
2567
        sprintf(s, "    Hangup location:  %s\r\n", info->last_lhup ? "local" : "remote");
2568
        isdn_tty_at_cout(s, info);
2569
        sprintf(s, "    Last cause:       %s\r\n", info->last_cause);
2570
        isdn_tty_at_cout(s, info);
2571
}
2572
 
2573
/*
2574
 * Parse AT&.. commands.
2575
 */
2576
static int
2577
isdn_tty_cmd_ATand(char **p, modem_info * info)
2578
{
2579
        atemu *m = &info->emu;
2580
        int i;
2581
        char rb[100];
2582
 
2583
        switch (*p[0]) {
2584
                case 'B':
2585
                        /* &B - Set Buffersize */
2586
                        p[0]++;
2587
                        i = isdn_getnum(p);
2588
                        if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
2589
                                PARSE_ERROR1;
2590
#ifdef CONFIG_ISDN_AUDIO
2591
                        if ((m->mdmreg[18] & 1) && (i > VBUF))
2592
                                PARSE_ERROR1;
2593
#endif
2594
                        m->mdmreg[16] = i / 16;
2595
                        info->xmit_size = m->mdmreg[16] * 16;
2596
                        break;
2597
                case 'D':
2598
                        /* &D - Set DCD-Low-behavior */
2599
                        p[0]++;
2600
                        switch (isdn_getnum(p)) {
2601
                                case 0:
2602
                                        m->mdmreg[13] &= ~4;
2603
                                        m->mdmreg[12] &= ~32;
2604
                                        break;
2605
                                case 2:
2606
                                        m->mdmreg[13] |= 4;
2607
                                        m->mdmreg[12] &= ~32;
2608
                                        break;
2609
                                case 3:
2610
                                        m->mdmreg[13] |= 4;
2611
                                        m->mdmreg[12] |= 32;
2612
                                        break;
2613
                                default:
2614
                                        PARSE_ERROR1
2615
                        }
2616
                        break;
2617
                case 'E':
2618
                        /* &E -Set EAZ/MSN */
2619
                        p[0]++;
2620
                        isdn_tty_get_msnstr(m->msn, p);
2621
                        break;
2622
                case 'F':
2623
                        /* &F -Set Factory-Defaults */
2624
                        p[0]++;
2625
                        isdn_tty_reset_profile(m);
2626
                        isdn_tty_modem_reset_regs(info, 1);
2627
                        break;
2628
                case 'S':
2629
                        /* &S - Set Windowsize */
2630
                        p[0]++;
2631
                        i = isdn_getnum(p);
2632
                        if ((i > 0) && (i < 9))
2633
                                m->mdmreg[17] = i;
2634
                        else
2635
                                PARSE_ERROR1;
2636
                        break;
2637
                case 'V':
2638
                        /* &V - Show registers */
2639
                        p[0]++;
2640
                        isdn_tty_at_cout("\r\n", info);
2641
                        for (i = 0; i < ISDN_MODEM_ANZREG; i++) {
2642
                                sprintf(rb, "S%02d=%03d%s", i,
2643
                                        m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
2644
                                isdn_tty_at_cout(rb, info);
2645
                        }
2646
                        sprintf(rb, "\r\nEAZ/MSN: %s\r\n",
2647
                                strlen(m->msn) ? m->msn : "None");
2648
                        isdn_tty_at_cout(rb, info);
2649
                        break;
2650
                case 'W':
2651
                        /* &W - Write Profile */
2652
                        p[0]++;
2653
                        switch (*p[0]) {
2654
                                case '0':
2655
                                        p[0]++;
2656
                                        modem_write_profile(m);
2657
                                        break;
2658
                                default:
2659
                                        PARSE_ERROR1;
2660
                        }
2661
                        break;
2662
                case 'X':
2663
                        /* &X - Switch to BTX-Mode */
2664
                        p[0]++;
2665
                        switch (isdn_getnum(p)) {
2666
                                case 0:
2667
                                        m->mdmreg[13] &= ~2;
2668
                                        info->xmit_size = m->mdmreg[16] * 16;
2669
                                        break;
2670
                                case 1:
2671
                                        m->mdmreg[13] |= 2;
2672
                                        m->mdmreg[14] = 0;
2673
                                        info->xmit_size = 112;
2674
                                        m->mdmreg[18] = 4;
2675
                                        m->mdmreg[19] = 0;
2676
                                        break;
2677
                                default:
2678
                                        PARSE_ERROR1;
2679
                        }
2680
                        break;
2681
                default:
2682
                        PARSE_ERROR1;
2683
        }
2684
        return 0;
2685
}
2686
 
2687
static int
2688
isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m)
2689
{
2690
        /* Some plausibility checks */
2691
        switch (mreg) {
2692
                case 14:
2693
                        if (mval > ISDN_PROTO_L2_TRANS)
2694
                                return 1;
2695
                        break;
2696
                case 16:
2697
                        if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
2698
                                return 1;
2699
#ifdef CONFIG_ISDN_AUDIO
2700
                        if ((m->mdmreg[18] & 1) && (mval > VBUFX))
2701
                                return 1;
2702
#endif
2703
                        info->xmit_size = mval * 16;
2704
                        break;
2705
                case 20:
2706
                case 21:
2707
                case 22:
2708
                        /* readonly registers */
2709
                        return 1;
2710
        }
2711
        return 0;
2712
}
2713
 
2714
/*
2715
 * Perform ATS command
2716
 */
2717
static int
2718
isdn_tty_cmd_ATS(char **p, modem_info * info)
2719
{
2720
        atemu *m = &info->emu;
2721
        int bitpos;
2722
        int mreg;
2723
        int mval;
2724
        int bval;
2725
 
2726
        mreg = isdn_getnum(p);
2727
        if (mreg < 0 || mreg > ISDN_MODEM_ANZREG)
2728
                PARSE_ERROR1;
2729
        switch (*p[0]) {
2730
                case '=':
2731
                        p[0]++;
2732
                        mval = isdn_getnum(p);
2733
                        if (mval < 0 || mval > 255)
2734
                                PARSE_ERROR1;
2735
                        if (isdn_tty_check_ats(mreg, mval, info, m))
2736
                                PARSE_ERROR1;
2737
                        m->mdmreg[mreg] = mval;
2738
                        break;
2739
                case '.':
2740
                        /* Set/Clear a single bit */
2741
                        p[0]++;
2742
                        bitpos = isdn_getnum(p);
2743
                        if ((bitpos < 0) || (bitpos > 7))
2744
                                PARSE_ERROR1;
2745
                        switch (*p[0]) {
2746
                                case '=':
2747
                                        p[0]++;
2748
                                        bval = isdn_getnum(p);
2749
                                        if (bval < 0 || bval > 1)
2750
                                                PARSE_ERROR1;
2751
                                        if (bval)
2752
                                                mval = m->mdmreg[mreg] | (1 << bitpos);
2753
                                        else
2754
                                                mval = m->mdmreg[mreg] & ~(1 << bitpos);
2755
                                        if (isdn_tty_check_ats(mreg, mval, info, m))
2756
                                                PARSE_ERROR1;
2757
                                        m->mdmreg[mreg] = mval;
2758
                                        break;
2759
                                case '?':
2760
                                        p[0]++;
2761
                                        isdn_tty_at_cout("\r\n", info);
2762
                                        isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0",
2763
                                                         info);
2764
                                        break;
2765
                                default:
2766
                                        PARSE_ERROR1;
2767
                        }
2768
                        break;
2769
                case '?':
2770
                        p[0]++;
2771
                        isdn_tty_show_profile(mreg, info);
2772
                        break;
2773
                default:
2774
                        PARSE_ERROR1;
2775
                        break;
2776
        }
2777
        return 0;
2778
}
2779
 
2780
/*
2781
 * Perform ATA command
2782
 */
2783
static void
2784
isdn_tty_cmd_ATA(modem_info * info)
2785
{
2786
        atemu *m = &info->emu;
2787
        isdn_ctrl cmd;
2788
        int l2;
2789
 
2790
        if (info->msr & UART_MSR_RI) {
2791
                /* Accept incoming call */
2792
                info->last_dir = 0;
2793
                strcpy(info->last_num, dev->num[info->drv_index]);
2794
                m->mdmreg[1] = 0;
2795
                info->msr &= ~UART_MSR_RI;
2796
                l2 = m->mdmreg[14];
2797
#ifdef CONFIG_ISDN_AUDIO
2798
                /* If more than one bit set in reg18, autoselect Layer2 */
2799
                if ((m->mdmreg[18] & m->mdmreg[20]) != m->mdmreg[18]) {
2800
                        if (m->mdmreg[20] == 1)
2801
                                l2 = 4;
2802
                        else
2803
                                l2 = 0;
2804
                }
2805
#endif
2806
                cmd.driver = info->isdn_driver;
2807
                cmd.command = ISDN_CMD_SETL2;
2808
                cmd.arg = info->isdn_channel + (l2 << 8);
2809
                info->last_l2 = l2;
2810
                isdn_command(&cmd);
2811
                cmd.driver = info->isdn_driver;
2812
                cmd.command = ISDN_CMD_SETL3;
2813
                cmd.arg = info->isdn_channel + (m->mdmreg[15] << 8);
2814
                isdn_command(&cmd);
2815
                cmd.driver = info->isdn_driver;
2816
                cmd.arg = info->isdn_channel;
2817
                cmd.command = ISDN_CMD_ACCEPTD;
2818
                isdn_command(&cmd);
2819
        } else
2820
                isdn_tty_modem_result(8, info);
2821
}
2822
 
2823
#ifdef CONFIG_ISDN_AUDIO
2824
/*
2825
 * Parse AT+F.. commands
2826
 */
2827
static int
2828
isdn_tty_cmd_PLUSF(char **p, modem_info * info)
2829
{
2830
        atemu *m = &info->emu;
2831
        int par;
2832
        char rs[20];
2833
 
2834
        if (!strncmp(p[0], "CLASS", 5)) {
2835
                p[0] += 5;
2836
                switch (*p[0]) {
2837
                        case '?':
2838
                                p[0]++;
2839
                                sprintf(rs, "\r\n%d",
2840
                                        (m->mdmreg[18] & 1) ? 8 : 0);
2841
                                isdn_tty_at_cout(rs, info);
2842
                                break;
2843
                        case '=':
2844
                                p[0]++;
2845
                                switch (*p[0]) {
2846
                                        case '0':
2847
                                                p[0]++;
2848
                                                m->mdmreg[18] = 4;
2849
                                                info->xmit_size =
2850
                                                    m->mdmreg[16] * 16;
2851
                                                break;
2852
                                        case '8':
2853
                                                p[0]++;
2854
                                                m->mdmreg[18] = 5;
2855
                                                info->xmit_size = VBUF;
2856
                                                break;
2857
                                        case '?':
2858
                                                p[0]++;
2859
                                                isdn_tty_at_cout("\r\n0,8",
2860
                                                                 info);
2861
                                                break;
2862
                                        default:
2863
                                                PARSE_ERROR1;
2864
                                }
2865
                                break;
2866
                        default:
2867
                                PARSE_ERROR1;
2868
                }
2869
                return 0;
2870
        }
2871
        if (!strncmp(p[0], "AA", 2)) {
2872
                p[0] += 2;
2873
                switch (*p[0]) {
2874
                        case '?':
2875
                                p[0]++;
2876
                                sprintf(rs, "\r\n%d",
2877
                                        m->mdmreg[0]);
2878
                                isdn_tty_at_cout(rs, info);
2879
                                break;
2880
                        case '=':
2881
                                p[0]++;
2882
                                par = isdn_getnum(p);
2883
                                if ((par < 0) || (par > 255))
2884
                                        PARSE_ERROR1;
2885
                                m->mdmreg[0] = par;
2886
                                break;
2887
                        default:
2888
                                PARSE_ERROR1;
2889
                }
2890
                return 0;
2891
        }
2892
        PARSE_ERROR1;
2893
}
2894
 
2895
/*
2896
 * Parse AT+V.. commands
2897
 */
2898
static int
2899
isdn_tty_cmd_PLUSV(char **p, modem_info * info)
2900
{
2901
        atemu *m = &info->emu;
2902
        static char *vcmd[] =
2903
        {"NH", "IP", "LS", "RX", "SD", "SM", "TX", NULL};
2904
        int i;
2905
        int par1;
2906
        int par2;
2907
        char rs[20];
2908
 
2909
        i = 0;
2910
        while (vcmd[i]) {
2911
                if (!strncmp(vcmd[i], p[0], 2)) {
2912
                        p[0] += 2;
2913
                        break;
2914
                }
2915
                i++;
2916
        }
2917
        switch (i) {
2918
                case 0:
2919
                        /* AT+VNH - Auto hangup feature */
2920
                        switch (*p[0]) {
2921
                                case '?':
2922
                                        p[0]++;
2923
                                        isdn_tty_at_cout("\r\n1", info);
2924
                                        break;
2925
                                case '=':
2926
                                        p[0]++;
2927
                                        switch (*p[0]) {
2928
                                                case '1':
2929
                                                        p[0]++;
2930
                                                        break;
2931
                                                case '?':
2932
                                                        p[0]++;
2933
                                                        isdn_tty_at_cout("\r\n1", info);
2934
                                                        break;
2935
                                                default:
2936
                                                        PARSE_ERROR1;
2937
                                        }
2938
                                        break;
2939
                                default:
2940
                                        PARSE_ERROR1;
2941
                        }
2942
                        break;
2943
                case 1:
2944
                        /* AT+VIP - Reset all voice parameters */
2945
                        isdn_tty_modem_reset_vpar(m);
2946
                        break;
2947
                case 2:
2948
                        /* AT+VLS - Select device, accept incoming call */
2949
                        switch (*p[0]) {
2950
                                case '?':
2951
                                        p[0]++;
2952
                                        sprintf(rs, "\r\n%d", m->vpar[0]);
2953
                                        isdn_tty_at_cout(rs, info);
2954
                                        break;
2955
                                case '=':
2956
                                        p[0]++;
2957
                                        switch (*p[0]) {
2958
                                                case '0':
2959
                                                        p[0]++;
2960
                                                        m->vpar[0] = 0;
2961
                                                        break;
2962
                                                case '2':
2963
                                                        p[0]++;
2964
                                                        m->vpar[0] = 2;
2965
                                                        break;
2966
                                                case '?':
2967
                                                        p[0]++;
2968
                                                        isdn_tty_at_cout("\r\n0,2", info);
2969
                                                        break;
2970
                                                default:
2971
                                                        PARSE_ERROR1;
2972
                                        }
2973
                                        break;
2974
                                default:
2975
                                        PARSE_ERROR1;
2976
                        }
2977
                        break;
2978
                case 3:
2979
                        /* AT+VRX - Start recording */
2980
                        if (!m->vpar[0])
2981
                                PARSE_ERROR1;
2982
                        if (info->online != 1) {
2983
                                isdn_tty_modem_result(8, info);
2984
                                return 1;
2985
                        }
2986
                        info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
2987
                        if (!info->dtmf_state) {
2988
                                printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
2989
                                PARSE_ERROR1;
2990
                        }
2991
                        info->silence_state = isdn_audio_silence_init(info->silence_state);
2992
                        if (!info->silence_state) {
2993
                                printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
2994
                                PARSE_ERROR1;
2995
                        }
2996
                        if (m->vpar[3] < 5) {
2997
                                info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
2998
                                if (!info->adpcmr) {
2999
                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
3000
                                        PARSE_ERROR1;
3001
                                }
3002
                        }
3003
#ifdef ISDN_DEBUG_AT
3004
                        printk(KERN_DEBUG "AT: +VRX\n");
3005
#endif
3006
                        info->vonline |= 1;
3007
                        isdn_tty_modem_result(1, info);
3008
                        return 0;
3009
                        break;
3010
                case 4:
3011
                        /* AT+VSD - Silence detection */
3012
                        switch (*p[0]) {
3013
                                case '?':
3014
                                        p[0]++;
3015
                                        sprintf(rs, "\r\n<%d>,<%d>",
3016
                                                m->vpar[1],
3017
                                                m->vpar[2]);
3018
                                        isdn_tty_at_cout(rs, info);
3019
                                        break;
3020
                                case '=':
3021
                                        p[0]++;
3022
                                        if ((*p[0]>='0') && (*p[0]<='9')) {
3023
                                                par1 = isdn_getnum(p);
3024
                                                if ((par1 < 0) || (par1 > 31))
3025
                                                        PARSE_ERROR1;
3026
                                                if (*p[0] != ',')
3027
                                                        PARSE_ERROR1;
3028
                                                p[0]++;
3029
                                                par2 = isdn_getnum(p);
3030
                                                if ((par2 < 0) || (par2 > 255))
3031
                                                        PARSE_ERROR1;
3032
                                                m->vpar[1] = par1;
3033
                                                m->vpar[2] = par2;
3034
                                                break;
3035
                                        } else
3036
                                        if (*p[0] == '?') {
3037
                                                p[0]++;
3038
                                                isdn_tty_at_cout("\r\n<0-31>,<0-255>",
3039
                                                           info);
3040
                                                break;
3041
                                        } else
3042
                                        PARSE_ERROR1;
3043
                                        break;
3044
                                default:
3045
                                        PARSE_ERROR1;
3046
                        }
3047
                        break;
3048
                case 5:
3049
                        /* AT+VSM - Select compression */
3050
                        switch (*p[0]) {
3051
                                case '?':
3052
                                        p[0]++;
3053
                                        sprintf(rs, "\r\n<%d>,<%d><8000>",
3054
                                                m->vpar[3],
3055
                                                m->vpar[1]);
3056
                                        isdn_tty_at_cout(rs, info);
3057
                                        break;
3058
                                case '=':
3059
                                        p[0]++;
3060
                                        switch (*p[0]) {
3061
                                                case '2':
3062
                                                case '3':
3063
                                                case '4':
3064
                                                case '5':
3065
                                                case '6':
3066
                                                        par1 = isdn_getnum(p);
3067
                                                        if ((par1 < 2) || (par1 > 6))
3068
                                                                PARSE_ERROR1;
3069
                                                        m->vpar[3] = par1;
3070
                                                        break;
3071
                                                case '?':
3072
                                                        p[0]++;
3073
                                                        isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
3074
                                                                   info);
3075
                                                        isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
3076
                                                                   info);
3077
                                                        isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
3078
                                                                   info);
3079
                                                        isdn_tty_at_cout("5;ALAW;8;0;(8000)",
3080
                                                                   info);
3081
                                                        isdn_tty_at_cout("6;ULAW;8;0;(8000)",
3082
                                                                   info);
3083
                                                        break;
3084
                                                default:
3085
                                                        PARSE_ERROR1;
3086
                                        }
3087
                                        break;
3088
                                default:
3089
                                        PARSE_ERROR1;
3090
                        }
3091
                        break;
3092
                case 6:
3093
                        /* AT+VTX - Start sending */
3094
                        if (!m->vpar[0])
3095
                                PARSE_ERROR1;
3096
                        if (info->online != 1) {
3097
                                isdn_tty_modem_result(8, info);
3098
                                return 1;
3099
                        }
3100
                        info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
3101
                        if (!info->dtmf_state) {
3102
                                printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
3103
                                PARSE_ERROR1;
3104
                        }
3105
                        if (m->vpar[3] < 5) {
3106
                                info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
3107
                                if (!info->adpcms) {
3108
                                        printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
3109
                                        PARSE_ERROR1;
3110
                                }
3111
                        }
3112
#ifdef ISDN_DEBUG_AT
3113
                        printk(KERN_DEBUG "AT: +VTX\n");
3114
#endif
3115
                        m->lastDLE = 0;
3116
                        info->vonline |= 2;
3117
                        isdn_tty_modem_result(1, info);
3118
                        return 0;
3119
                        break;
3120
                default:
3121
                        PARSE_ERROR1;
3122
        }
3123
        return 0;
3124
}
3125
#endif                          /* CONFIG_ISDN_AUDIO */
3126
 
3127
/*
3128
 * Parse and perform an AT-command-line.
3129
 */
3130
static void
3131
isdn_tty_parse_at(modem_info * info)
3132
{
3133
        atemu *m = &info->emu;
3134
        char *p;
3135
        char ds[40];
3136
 
3137
#ifdef ISDN_DEBUG_AT
3138
        printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
3139
#endif
3140
        for (p = &m->mdmcmd[2]; *p;) {
3141
                switch (*p) {
3142
                        case 'A':
3143
                                /* A - Accept incoming call */
3144
                                p++;
3145
                                isdn_tty_cmd_ATA(info);
3146
                                return;
3147
                                break;
3148
                        case 'D':
3149
                                /* D - Dial */
3150
                                isdn_tty_getdial(++p, ds,sizeof(ds));
3151
                                p += strlen(p);
3152
                                if (!strlen(m->msn))
3153
                                        isdn_tty_modem_result(10, info);
3154
                                else if (strlen(ds))
3155
                                        isdn_tty_dial(ds, info, m);
3156
                                else
3157
                                        PARSE_ERROR;
3158
                                return;
3159
                        case 'E':
3160
                                /* E - Turn Echo on/off */
3161
                                p++;
3162
                                switch (isdn_getnum(&p)) {
3163
                                        case 0:
3164
                                                m->mdmreg[12] &= ~4;
3165
                                                break;
3166
                                        case 1:
3167
                                                m->mdmreg[12] |= 4;
3168
                                                break;
3169
                                        default:
3170
                                                PARSE_ERROR;
3171
                                }
3172
                                break;
3173
                        case 'H':
3174
                                /* H - On/Off-hook */
3175
                                p++;
3176
                                switch (*p) {
3177
                                        case '0':
3178
                                                p++;
3179
                                                isdn_tty_on_hook(info);
3180
                                                break;
3181
                                        case '1':
3182
                                                p++;
3183
                                                isdn_tty_off_hook();
3184
                                                break;
3185
                                        default:
3186
                                                isdn_tty_on_hook(info);
3187
                                                break;
3188
                                }
3189
                                break;
3190
                        case 'I':
3191
                                /* I - Information */
3192
                                p++;
3193
                                isdn_tty_at_cout("\r\nLinux ISDN", info);
3194
                                switch (*p) {
3195
                                        case '0':
3196
                                        case '1':
3197
                                                p++;
3198
                                                break;
3199
                                        case '2':
3200
                                                p++;
3201
                                                isdn_tty_report(info);
3202
                                                break;
3203
                                        default:
3204
                                }
3205
                                break;
3206
                        case 'O':
3207
                                /* O - Go online */
3208
                                p++;
3209
                                if (info->msr & UART_MSR_DCD)
3210
                                        /* if B-Channel is up */
3211
                                        isdn_tty_modem_result(5, info);
3212
                                else
3213
                                        isdn_tty_modem_result(3, info);
3214
                                return;
3215
                        case 'Q':
3216
                                /* Q - Turn Emulator messages on/off */
3217
                                p++;
3218
                                switch (isdn_getnum(&p)) {
3219
                                        case 0:
3220
                                                m->mdmreg[12] |= 1;
3221
                                                break;
3222
                                        case 1:
3223
                                                m->mdmreg[12] &= ~1;
3224
                                                break;
3225
                                        default:
3226
                                                PARSE_ERROR;
3227
                                }
3228
                                break;
3229
                        case 'S':
3230
                                /* S - Set/Get Register */
3231
                                p++;
3232
                                if (isdn_tty_cmd_ATS(&p, info))
3233
                                        return;
3234
                                break;
3235
                        case 'V':
3236
                                /* V - Numeric or ASCII Emulator-messages */
3237
                                p++;
3238
                                switch (isdn_getnum(&p)) {
3239
                                        case 0:
3240
                                                m->mdmreg[12] |= 2;
3241
                                                break;
3242
                                        case 1:
3243
                                                m->mdmreg[12] &= ~2;
3244
                                                break;
3245
                                        default:
3246
                                                PARSE_ERROR;
3247
                                }
3248
                                break;
3249
                        case 'Z':
3250
                                /* Z - Load Registers from Profile */
3251
                                p++;
3252
                                isdn_tty_modem_reset_regs(info, 1);
3253
                                break;
3254
#ifdef CONFIG_ISDN_AUDIO
3255
                        case '+':
3256
                                p++;
3257
                                switch (*p) {
3258
                                        case 'F':
3259
                                                p++;
3260
                                                if (isdn_tty_cmd_PLUSF(&p, info))
3261
                                                        return;
3262
                                                break;
3263
                                        case 'V':
3264
                                                if (!(m->mdmreg[18] & 1))
3265
                                                        PARSE_ERROR;
3266
                                                p++;
3267
                                                if (isdn_tty_cmd_PLUSV(&p, info))
3268
                                                        return;
3269
                                                break;
3270
                                        default:
3271
                                                PARSE_ERROR;
3272
                                }
3273
                                break;
3274
#endif                          /* CONFIG_ISDN_AUDIO */
3275
                        case '&':
3276
                                p++;
3277
                                if (isdn_tty_cmd_ATand(&p, info))
3278
                                        return;
3279
                                break;
3280
                        default:
3281
                                PARSE_ERROR;
3282
                }
3283
        }
3284
#ifdef CONFIG_ISDN_AUDIO
3285
        if (!info->vonline)
3286
#endif
3287
                isdn_tty_modem_result(0, info);
3288
}
3289
 
3290
/* Need own toupper() because standard-toupper is not available
3291
 * within modules.
3292
 */
3293
#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
3294
 
3295
/*
3296
 * Perform line-editing of AT-commands
3297
 *
3298
 * Parameters:
3299
 *   p        inputbuffer
3300
 *   count    length of buffer
3301
 *   channel  index to line (minor-device)
3302
 *   user     flag: buffer is in userspace
3303
 */
3304
static int
3305
isdn_tty_edit_at(const char *p, int count, modem_info * info, int user)
3306
{
3307
        atemu *m = &info->emu;
3308
        int total = 0;
3309
        u_char c;
3310
        char eb[2];
3311
        int cnt;
3312
 
3313
        for (cnt = count; cnt > 0; p++, cnt--) {
3314
                if (user)
3315
                        GET_USER(c, p);
3316
                else
3317
                        c = *p;
3318
                total++;
3319
                if (c == m->mdmreg[3] || c == m->mdmreg[4]) {
3320
                        /* Separator (CR oder LF) */
3321
                        m->mdmcmd[m->mdmcmdl] = 0;
3322
                        if (m->mdmreg[12] & 4) {
3323
                                eb[0] = c;
3324
                                eb[1] = 0;
3325
                                isdn_tty_at_cout(eb, info);
3326
                        }
3327
                        if (m->mdmcmdl >= 2)
3328
                                isdn_tty_parse_at(info);
3329
                        m->mdmcmdl = 0;
3330
                        continue;
3331
                }
3332
                if (c == m->mdmreg[5] && m->mdmreg[5] < 128) {
3333
                        /* Backspace-Funktion */
3334
                        if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
3335
                                if (m->mdmcmdl)
3336
                                        m->mdmcmdl--;
3337
                                if (m->mdmreg[12] & 4)
3338
                                        isdn_tty_at_cout("\b", info);
3339
                        }
3340
                        continue;
3341
                }
3342
                if (cmdchar(c)) {
3343
                        if (m->mdmreg[12] & 4) {
3344
                                eb[0] = c;
3345
                                eb[1] = 0;
3346
                                isdn_tty_at_cout(eb, info);
3347
                        }
3348
                        if (m->mdmcmdl < 255) {
3349
                                c = my_toupper(c);
3350
                                switch (m->mdmcmdl) {
3351
                                        case 0:
3352
                                                if (c == 'A')
3353
                                                        m->mdmcmd[m->mdmcmdl++] = c;
3354
                                                break;
3355
                                        case 1:
3356
                                                if (c == 'T')
3357
                                                        m->mdmcmd[m->mdmcmdl++] = c;
3358
                                                break;
3359
                                        default:
3360
                                                m->mdmcmd[m->mdmcmdl++] = c;
3361
                                }
3362
                        }
3363
                }
3364
        }
3365
        return total;
3366
}
3367
 
3368
/*
3369
 * Switch all modem-channels who are online and got a valid
3370
 * escape-sequence 1.5 seconds ago, to command-mode.
3371
 * This function is called every second via timer-interrupt from within
3372
 * timer-dispatcher isdn_timer_function()
3373
 */
3374
void
3375
isdn_tty_modem_escape(void)
3376
{
3377
        int ton = 0;
3378
        int i;
3379
        int midx;
3380
 
3381
        for (i = 0; i < ISDN_MAX_CHANNELS; i++)
3382
                if (USG_MODEM(dev->usage[i]))
3383
                        if ((midx = dev->m_idx[i]) >= 0) {
3384
                                modem_info *info = &dev->mdm.info[midx];
3385
                                if (info->online) {
3386
                                        ton = 1;
3387
                                        if ((info->emu.pluscount == 3) &&
3388
                                            ((jiffies - info->emu.lastplus) > PLUSWAIT2)) {
3389
                                                info->emu.pluscount = 0;
3390
                                                info->online = 0;
3391
                                                isdn_tty_modem_result(0, info);
3392
                                        }
3393
                                }
3394
                        }
3395
        isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
3396
}
3397
 
3398
/*
3399
 * Put a RING-message to all modem-channels who have the RI-bit set.
3400
 * This function is called every second via timer-interrupt from within
3401
 * timer-dispatcher isdn_timer_function()
3402
 */
3403
void
3404
isdn_tty_modem_ring(void)
3405
{
3406
        int ton = 0;
3407
        int i;
3408
 
3409
        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
3410
                modem_info *info = &dev->mdm.info[i];
3411
                if (info->msr & UART_MSR_RI) {
3412
                        ton = 1;
3413
                        isdn_tty_modem_result(2, info);
3414
                }
3415
        }
3416
        isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
3417
}
3418
 
3419
/*
3420
 * For all online tty's, try sending data to
3421
 * the lower levels.
3422
 */
3423
void
3424
isdn_tty_modem_xmit(void)
3425
{
3426
        int ton = 1;
3427
        int i;
3428
 
3429
        for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
3430
                modem_info *info = &dev->mdm.info[i];
3431
                if (info->online) {
3432
                        ton = 1;
3433
                        isdn_tty_senddown(info);
3434
                        isdn_tty_tint(info);
3435
                }
3436
        }
3437
        isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
3438
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.