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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [isdn/] [isdn_tty.c] - Blame information for rev 1777

Go to most recent revision | Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

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