blob: bcf4bbe693bc249ee6b323a93ae0765c364808b7 [file] [log] [blame]
Jiri Slaby1ca67112012-04-02 13:53:48 +02001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
3 *
4 * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
5 * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
6 *
7 * This software may be used and distributed according to the terms
8 * of the GNU General Public License, incorporated herein by reference.
9 *
10 */
11#undef ISDN_TTY_STAT_DEBUG
12
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include <linux/isdn.h>
Jiri Slaby6776a2f2012-04-02 13:53:50 +020014#include <linux/serial.h> /* ASYNC_* flags */
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090015#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/delay.h>
Arnd Bergmann72250d42010-09-14 09:35:04 +000017#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include "isdn_common.h"
19#include "isdn_tty.h"
20#ifdef CONFIG_ISDN_AUDIO
21#include "isdn_audio.h"
22#define VBUF 0x3e0
23#define VBUFX (VBUF/16)
24#endif
25
26#define FIX_FILE_TRANSFER
27#define DUMMY_HAYES_AT
28
29/* Prototypes */
30
Arnd Bergmann72250d42010-09-14 09:35:04 +000031static DEFINE_MUTEX(modem_info_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070032static int isdn_tty_edit_at(const char *, int, modem_info *);
33static void isdn_tty_check_esc(const u_char *, u_char, int, int *, u_long *);
34static void isdn_tty_modem_reset_regs(modem_info *, int);
35static void isdn_tty_cmd_ATA(modem_info *);
36static void isdn_tty_flush_buffer(struct tty_struct *);
37static void isdn_tty_modem_result(int, modem_info *);
38#ifdef CONFIG_ISDN_AUDIO
39static int isdn_tty_countDLE(unsigned char *, int);
40#endif
41
42/* Leave this unchanged unless you know what you do! */
43#define MODEM_PARANOIA_CHECK
44#define MODEM_DO_RESTART
45
46static int bit2si[8] =
47{1, 5, 7, 7, 7, 7, 7, 7};
48static int si2bit[8] =
49{4, 1, 4, 4, 4, 4, 4, 4};
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
52 * to stuff incoming data directly into a tty's flip-buffer. This
53 * is done to speed up tty-receiving if the receive-queue is empty.
54 * This routine MUST be called with interrupts off.
55 * Return:
56 * 1 = Success
57 * 0 = Failure, data has to be buffered and later processed by
58 * isdn_tty_readmodem().
59 */
60static int
Joe Perches475be4d2012-02-19 19:52:38 -080061isdn_tty_try_read(modem_info *info, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062{
63 int c;
64 int len;
65 struct tty_struct *tty;
Alan Cox33f0f882006-01-09 20:54:13 -080066 char last;
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
68 if (info->online) {
69 if ((tty = info->tty)) {
70 if (info->mcr & UART_MCR_RTS) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070071 len = skb->len
72#ifdef CONFIG_ISDN_AUDIO
73 + ISDN_AUDIO_SKB_DLECOUNT(skb)
74#endif
75 ;
Alan Cox33f0f882006-01-09 20:54:13 -080076
77 c = tty_buffer_request_room(tty, len);
Linus Torvalds1da177e2005-04-16 15:20:36 -070078 if (c >= len) {
79#ifdef CONFIG_ISDN_AUDIO
Alan Cox33f0f882006-01-09 20:54:13 -080080 if (ISDN_AUDIO_SKB_DLECOUNT(skb)) {
81 int l = skb->len;
82 unsigned char *dp = skb->data;
83 while (--l) {
Karsten Keilca6f8792006-06-27 13:01:27 +020084 if (*dp == DLE)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 tty_insert_flip_char(tty, DLE, 0);
Alan Cox33f0f882006-01-09 20:54:13 -080086 tty_insert_flip_char(tty, *dp++, 0);
87 }
Matthias Goebl7fde4d72008-01-04 03:45:28 -080088 if (*dp == DLE)
89 tty_insert_flip_char(tty, DLE, 0);
Alan Cox33f0f882006-01-09 20:54:13 -080090 last = *dp;
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 } else {
92#endif
Joe Perches475be4d2012-02-19 19:52:38 -080093 if (len > 1)
Alan Cox33f0f882006-01-09 20:54:13 -080094 tty_insert_flip_string(tty, skb->data, len - 1);
95 last = skb->data[len - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -070096#ifdef CONFIG_ISDN_AUDIO
97 }
98#endif
99 if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)
Alan Cox33f0f882006-01-09 20:54:13 -0800100 tty_insert_flip_char(tty, last, 0xFF);
101 else
102 tty_insert_flip_char(tty, last, TTY_NORMAL);
103 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104 kfree_skb(skb);
105 return 1;
106 }
107 }
108 }
109 }
110 return 0;
111}
112
113/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
114 * It tries getting received data from the receive queue an stuff it into
115 * the tty's flip-buffer.
116 */
117void
118isdn_tty_readmodem(void)
119{
120 int resched = 0;
121 int midx;
122 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 int r;
124 struct tty_struct *tty;
125 modem_info *info;
126
127 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
128 if ((midx = dev->m_idx[i]) >= 0) {
129 info = &dev->mdm.info[midx];
130 if (info->online) {
131 r = 0;
132#ifdef CONFIG_ISDN_AUDIO
133 isdn_audio_eval_dtmf(info);
134 if ((info->vonline & 1) && (info->emu.vpar[1]))
135 isdn_audio_eval_silence(info);
136#endif
137 if ((tty = info->tty)) {
138 if (info->mcr & UART_MCR_RTS) {
Alan Cox33f0f882006-01-09 20:54:13 -0800139 /* CISCO AsyncPPP Hack */
140 if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP))
141 r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 0);
142 else
143 r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 1);
144 if (r)
145 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 } else
147 r = 1;
148 } else
149 r = 1;
150 if (r) {
151 info->rcvsched = 0;
152 resched = 1;
153 } else
154 info->rcvsched = 1;
155 }
156 }
157 }
158 if (!resched)
159 isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
160}
161
162int
163isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
164{
165 ulong flags;
166 int midx;
167#ifdef CONFIG_ISDN_AUDIO
168 int ifmt;
169#endif
170 modem_info *info;
171
172 if ((midx = dev->m_idx[i]) < 0) {
173 /* if midx is invalid, packet is not for tty */
174 return 0;
175 }
176 info = &dev->mdm.info[midx];
177#ifdef CONFIG_ISDN_AUDIO
178 ifmt = 1;
Joe Perches475be4d2012-02-19 19:52:38 -0800179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 if ((info->vonline) && (!info->emu.vpar[4]))
181 isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
182 if ((info->vonline & 1) && (info->emu.vpar[1]))
183 isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
184#endif
185 if ((info->online < 2)
186#ifdef CONFIG_ISDN_AUDIO
187 && (!(info->vonline & 1))
188#endif
189 ) {
190 /* If Modem not listening, drop data */
191 kfree_skb(skb);
192 return 1;
193 }
194 if (info->emu.mdmreg[REG_T70] & BIT_T70) {
195 if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) {
196 /* T.70 decoding: throw away the T.70 header (2 or 4 bytes) */
197 if (skb->data[0] == 3) /* pure data packet -> 4 byte headers */
198 skb_pull(skb, 4);
199 else
200 if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr */
201 skb_pull(skb, 2);
202 } else
203 /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
204 if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
205 skb_pull(skb, 4);
206 }
207#ifdef CONFIG_ISDN_AUDIO
208 ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
209 ISDN_AUDIO_SKB_LOCK(skb) = 0;
210 if (info->vonline & 1) {
211 /* voice conversion/compression */
212 switch (info->emu.vpar[3]) {
Joe Perches475be4d2012-02-19 19:52:38 -0800213 case 2:
214 case 3:
215 case 4:
216 /* adpcm
217 * Since compressed data takes less
218 * space, we can overwrite the buffer.
219 */
220 skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
221 ifmt,
222 skb->data,
223 skb->data,
224 skb->len));
225 break;
226 case 5:
227 /* a-law */
228 if (!ifmt)
229 isdn_audio_ulaw2alaw(skb->data, skb->len);
230 break;
231 case 6:
232 /* u-law */
233 if (ifmt)
234 isdn_audio_alaw2ulaw(skb->data, skb->len);
235 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 }
237 ISDN_AUDIO_SKB_DLECOUNT(skb) =
238 isdn_tty_countDLE(skb->data, skb->len);
239 }
240#ifdef CONFIG_ISDN_TTY_FAX
241 else {
242 if (info->faxonline & 2) {
243 isdn_tty_fax_bitorder(info, skb);
244 ISDN_AUDIO_SKB_DLECOUNT(skb) =
245 isdn_tty_countDLE(skb->data, skb->len);
246 }
247 }
248#endif
249#endif
Alan Cox33f0f882006-01-09 20:54:13 -0800250 /* Try to deliver directly via tty-buf if queue is empty */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 spin_lock_irqsave(&info->readlock, flags);
252 if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
253 if (isdn_tty_try_read(info, skb)) {
254 spin_unlock_irqrestore(&info->readlock, flags);
255 return 1;
256 }
257 /* Direct deliver failed or queue wasn't empty.
258 * Queue up for later dequeueing via timer-irq.
259 */
260 __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
261 dev->drv[di]->rcvcount[channel] +=
262 (skb->len
263#ifdef CONFIG_ISDN_AUDIO
264 + ISDN_AUDIO_SKB_DLECOUNT(skb)
265#endif
266 );
267 spin_unlock_irqrestore(&info->readlock, flags);
268 /* Schedule dequeuing */
269 if ((dev->modempoll) && (info->rcvsched))
270 isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
271 return 1;
272}
273
Adrian Bunk3e206b02005-06-25 14:58:35 -0700274static void
Joe Perches475be4d2012-02-19 19:52:38 -0800275isdn_tty_cleanup_xmit(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
277 skb_queue_purge(&info->xmit_queue);
278#ifdef CONFIG_ISDN_AUDIO
279 skb_queue_purge(&info->dtmf_queue);
280#endif
281}
282
283static void
Joe Perches475be4d2012-02-19 19:52:38 -0800284isdn_tty_tint(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
287 int len, slen;
288
289 if (!skb)
290 return;
291 len = skb->len;
292 if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
293 info->isdn_channel, 1, skb)) == len) {
294 struct tty_struct *tty = info->tty;
295 info->send_outstanding++;
296 info->msr &= ~UART_MSR_CTS;
297 info->lsr &= ~UART_LSR_TEMT;
298 tty_wakeup(tty);
299 return;
300 }
301 if (slen < 0) {
302 /* Error: no channel, already shutdown, or wrong parameter */
303 dev_kfree_skb(skb);
304 return;
305 }
306 skb_queue_head(&info->xmit_queue, skb);
307}
308
309#ifdef CONFIG_ISDN_AUDIO
310static int
311isdn_tty_countDLE(unsigned char *buf, int len)
312{
313 int count = 0;
314
315 while (len--)
316 if (*buf++ == DLE)
317 count++;
318 return count;
319}
320
321/* This routine is called from within isdn_tty_write() to perform
322 * DLE-decoding when sending audio-data.
323 */
324static int
Joe Perches475be4d2012-02-19 19:52:38 -0800325isdn_tty_handleDLEdown(modem_info *info, atemu *m, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326{
327 unsigned char *p = &info->xmit_buf[info->xmit_count];
328 int count = 0;
329
330 while (len > 0) {
331 if (m->lastDLE) {
332 m->lastDLE = 0;
333 switch (*p) {
Joe Perches475be4d2012-02-19 19:52:38 -0800334 case DLE:
335 /* Escape code */
336 if (len > 1)
337 memmove(p, p + 1, len - 1);
338 p--;
339 count++;
340 break;
341 case ETX:
342 /* End of data */
343 info->vonline |= 4;
344 return count;
345 case DC4:
346 /* Abort RX */
347 info->vonline &= ~1;
348#ifdef ISDN_DEBUG_MODEM_VOICE
349 printk(KERN_DEBUG
350 "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",
351 info->line);
352#endif
353 isdn_tty_at_cout("\020\003", info);
354 if (!info->vonline) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355#ifdef ISDN_DEBUG_MODEM_VOICE
356 printk(KERN_DEBUG
Joe Perches475be4d2012-02-19 19:52:38 -0800357 "DLEdown: send VCON on ttyI%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 info->line);
359#endif
Joe Perches475be4d2012-02-19 19:52:38 -0800360 isdn_tty_at_cout("\r\nVCON\r\n", info);
361 }
362 /* Fall through */
363 case 'q':
364 case 's':
365 /* Silence */
366 if (len > 1)
367 memmove(p, p + 1, len - 1);
368 p--;
369 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 }
371 } else {
372 if (*p == DLE)
373 m->lastDLE = 1;
374 else
375 count++;
376 }
377 p++;
378 len--;
379 }
380 if (len < 0) {
381 printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
382 return 0;
383 }
384 return count;
385}
386
387/* This routine is called from within isdn_tty_write() when receiving
388 * audio-data. It interrupts receiving, if an character other than
389 * ^S or ^Q is sent.
390 */
391static int
392isdn_tty_end_vrx(const char *buf, int c)
393{
394 char ch;
395
396 while (c--) {
397 ch = *buf;
398 if ((ch != 0x11) && (ch != 0x13))
399 return 1;
400 buf++;
401 }
402 return 0;
403}
404
405static int voice_cf[7] =
406{0, 0, 4, 3, 2, 0, 0};
407
408#endif /* CONFIG_ISDN_AUDIO */
409
410/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
411 * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
412 * outgoing data from the tty's xmit-buffer, handles voice-decompression or
413 * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
414 */
415static void
Joe Perches475be4d2012-02-19 19:52:38 -0800416isdn_tty_senddown(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
418 int buflen;
419 int skb_res;
420#ifdef CONFIG_ISDN_AUDIO
421 int audio_len;
422#endif
423 struct sk_buff *skb;
424
425#ifdef CONFIG_ISDN_AUDIO
426 if (info->vonline & 4) {
427 info->vonline &= ~6;
428 if (!info->vonline) {
429#ifdef ISDN_DEBUG_MODEM_VOICE
430 printk(KERN_DEBUG
431 "senddown: send VCON on ttyI%d\n",
432 info->line);
433#endif
434 isdn_tty_at_cout("\r\nVCON\r\n", info);
435 }
436 }
437#endif
438 if (!(buflen = info->xmit_count))
439 return;
Joe Perches475be4d2012-02-19 19:52:38 -0800440 if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 info->msr &= ~UART_MSR_CTS;
Joe Perches475be4d2012-02-19 19:52:38 -0800442 info->lsr &= ~UART_LSR_TEMT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 /* info->xmit_count is modified here and in isdn_tty_write().
444 * So we return here if isdn_tty_write() is in the
445 * critical section.
446 */
447 atomic_inc(&info->xmit_lock);
448 if (!(atomic_dec_and_test(&info->xmit_lock)))
449 return;
450 if (info->isdn_driver < 0) {
451 info->xmit_count = 0;
452 return;
453 }
454 skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
455#ifdef CONFIG_ISDN_AUDIO
456 if (info->vonline & 2)
457 audio_len = buflen * voice_cf[info->emu.vpar[3]];
458 else
459 audio_len = 0;
460 skb = dev_alloc_skb(skb_res + buflen + audio_len);
461#else
462 skb = dev_alloc_skb(skb_res + buflen);
463#endif
464 if (!skb) {
465 printk(KERN_WARNING
466 "isdn_tty: Out of memory in ttyI%d senddown\n",
467 info->line);
468 return;
469 }
470 skb_reserve(skb, skb_res);
471 memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
472 info->xmit_count = 0;
473#ifdef CONFIG_ISDN_AUDIO
474 if (info->vonline & 2) {
475 /* For now, ifmt is fixed to 1 (alaw), since this
476 * is used with ISDN everywhere in the world, except
477 * US, Canada and Japan.
478 * Later, when US-ISDN protocols are implemented,
479 * this setting will depend on the D-channel protocol.
480 */
481 int ifmt = 1;
482
483 /* voice conversion/decompression */
484 switch (info->emu.vpar[3]) {
Joe Perches475be4d2012-02-19 19:52:38 -0800485 case 2:
486 case 3:
487 case 4:
488 /* adpcm, compatible to ZyXel 1496 modem
489 * with ROM revision 6.01
490 */
491 audio_len = isdn_audio_adpcm2xlaw(info->adpcms,
492 ifmt,
493 skb->data,
494 skb_put(skb, audio_len),
495 buflen);
496 skb_pull(skb, buflen);
497 skb_trim(skb, audio_len);
498 break;
499 case 5:
500 /* a-law */
501 if (!ifmt)
502 isdn_audio_alaw2ulaw(skb->data,
503 buflen);
504 break;
505 case 6:
506 /* u-law */
507 if (ifmt)
508 isdn_audio_ulaw2alaw(skb->data,
509 buflen);
510 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 }
512 }
513#endif /* CONFIG_ISDN_AUDIO */
514 if (info->emu.mdmreg[REG_T70] & BIT_T70) {
515 /* Add T.70 simplified header */
516 if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT)
517 memcpy(skb_push(skb, 2), "\1\0", 2);
518 else
519 memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
520 }
521 skb_queue_tail(&info->xmit_queue, skb);
522}
523
524/************************************************************
525 *
526 * Modem-functions
527 *
528 * mostly "stolen" from original Linux-serial.c and friends.
529 *
530 ************************************************************/
531
532/* The next routine is called once from within timer-interrupt
533 * triggered within isdn_tty_modem_ncarrier(). It calls
534 * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
Alan Cox33f0f882006-01-09 20:54:13 -0800535 * into the tty's buffer.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 */
537static void
538isdn_tty_modem_do_ncarrier(unsigned long data)
539{
540 modem_info *info = (modem_info *) data;
541 isdn_tty_modem_result(RESULT_NO_CARRIER, info);
542}
543
544/* Next routine is called, whenever the DTR-signal is raised.
545 * It checks the ncarrier-flag, and triggers the above routine
546 * when necessary. The ncarrier-flag is set, whenever DTR goes
547 * low.
548 */
549static void
Joe Perches475be4d2012-02-19 19:52:38 -0800550isdn_tty_modem_ncarrier(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551{
552 if (info->ncarrier) {
553 info->nc_timer.expires = jiffies + HZ;
554 add_timer(&info->nc_timer);
555 }
556}
557
558/*
559 * return the usage calculated by si and layer 2 protocol
560 */
Adrian Bunk3e206b02005-06-25 14:58:35 -0700561static int
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562isdn_calc_usage(int si, int l2)
563{
564 int usg = ISDN_USAGE_MODEM;
565
566#ifdef CONFIG_ISDN_AUDIO
567 if (si == 1) {
Joe Perches475be4d2012-02-19 19:52:38 -0800568 switch (l2) {
569 case ISDN_PROTO_L2_MODEM:
570 usg = ISDN_USAGE_MODEM;
571 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572#ifdef CONFIG_ISDN_TTY_FAX
Joe Perches475be4d2012-02-19 19:52:38 -0800573 case ISDN_PROTO_L2_FAX:
574 usg = ISDN_USAGE_FAX;
575 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576#endif
Joe Perches475be4d2012-02-19 19:52:38 -0800577 case ISDN_PROTO_L2_TRANS:
578 default:
579 usg = ISDN_USAGE_VOICE;
580 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 }
582 }
583#endif
Joe Perches475be4d2012-02-19 19:52:38 -0800584 return (usg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585}
586
587/* isdn_tty_dial() performs dialing of a tty an the necessary
588 * setup of the lower levels before that.
589 */
590static void
Joe Perches475be4d2012-02-19 19:52:38 -0800591isdn_tty_dial(char *n, modem_info *info, atemu *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592{
593 int usg = ISDN_USAGE_MODEM;
594 int si = 7;
595 int l2 = m->mdmreg[REG_L2PROT];
596 u_long flags;
597 isdn_ctrl cmd;
598 int i;
599 int j;
600
601 for (j = 7; j >= 0; j--)
602 if (m->mdmreg[REG_SI1] & (1 << j)) {
603 si = bit2si[j];
604 break;
605 }
606 usg = isdn_calc_usage(si, l2);
607#ifdef CONFIG_ISDN_AUDIO
Joe Perches475be4d2012-02-19 19:52:38 -0800608 if ((si == 1) &&
609 (l2 != ISDN_PROTO_L2_MODEM)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610#ifdef CONFIG_ISDN_TTY_FAX
Joe Perches475be4d2012-02-19 19:52:38 -0800611 && (l2 != ISDN_PROTO_L2_FAX)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612#endif
613 ) {
614 l2 = ISDN_PROTO_L2_TRANS;
615 usg = ISDN_USAGE_VOICE;
616 }
617#endif
618 m->mdmreg[REG_SI1I] = si2bit[si];
619 spin_lock_irqsave(&dev->lock, flags);
620 i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
621 if (i < 0) {
622 spin_unlock_irqrestore(&dev->lock, flags);
623 isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
624 } else {
625 info->isdn_driver = dev->drvmap[i];
626 info->isdn_channel = dev->chanmap[i];
627 info->drv_index = i;
628 dev->m_idx[i] = info->line;
629 dev->usage[i] |= ISDN_USAGE_OUTGOING;
630 info->last_dir = 1;
631 strcpy(info->last_num, n);
632 isdn_info_update();
633 spin_unlock_irqrestore(&dev->lock, flags);
634 cmd.driver = info->isdn_driver;
635 cmd.arg = info->isdn_channel;
636 cmd.command = ISDN_CMD_CLREAZ;
637 isdn_command(&cmd);
638 strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
639 cmd.driver = info->isdn_driver;
640 cmd.command = ISDN_CMD_SETEAZ;
641 isdn_command(&cmd);
642 cmd.driver = info->isdn_driver;
643 cmd.command = ISDN_CMD_SETL2;
644 info->last_l2 = l2;
645 cmd.arg = info->isdn_channel + (l2 << 8);
646 isdn_command(&cmd);
647 cmd.driver = info->isdn_driver;
648 cmd.command = ISDN_CMD_SETL3;
649 cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
650#ifdef CONFIG_ISDN_TTY_FAX
651 if (l2 == ISDN_PROTO_L2_FAX) {
652 cmd.parm.fax = info->fax;
653 info->fax->direction = ISDN_TTY_FAX_CONN_OUT;
654 }
655#endif
656 isdn_command(&cmd);
657 cmd.driver = info->isdn_driver;
658 cmd.arg = info->isdn_channel;
659 sprintf(cmd.parm.setup.phone, "%s", n);
660 sprintf(cmd.parm.setup.eazmsn, "%s",
661 isdn_map_eaz2msn(m->msn, info->isdn_driver));
662 cmd.parm.setup.si1 = si;
663 cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
664 cmd.command = ISDN_CMD_DIAL;
665 info->dialing = 1;
666 info->emu.carrierwait = 0;
667 strcpy(dev->num[i], n);
668 isdn_info_update();
669 isdn_command(&cmd);
670 isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
671 }
672}
673
674/* isdn_tty_hangup() disassociates a tty from the real
675 * ISDN-line (hangup). The usage-status is cleared
676 * and some cleanup is done also.
677 */
678void
Joe Perches475be4d2012-02-19 19:52:38 -0800679isdn_tty_modem_hup(modem_info *info, int local)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680{
681 isdn_ctrl cmd;
682 int di, ch;
683
684 if (!info)
685 return;
686
687 di = info->isdn_driver;
688 ch = info->isdn_channel;
689 if (di < 0 || ch < 0)
690 return;
691
692 info->isdn_driver = -1;
693 info->isdn_channel = -1;
694
695#ifdef ISDN_DEBUG_MODEM_HUP
696 printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
697#endif
698 info->rcvsched = 0;
699 isdn_tty_flush_buffer(info->tty);
700 if (info->online) {
701 info->last_lhup = local;
702 info->online = 0;
703 isdn_tty_modem_result(RESULT_NO_CARRIER, info);
704 }
705#ifdef CONFIG_ISDN_AUDIO
706 info->vonline = 0;
707#ifdef CONFIG_ISDN_TTY_FAX
708 info->faxonline = 0;
709 info->fax->phase = ISDN_FAX_PHASE_IDLE;
710#endif
711 info->emu.vpar[4] = 0;
712 info->emu.vpar[5] = 8;
Jesper Juhl3c7208f2005-11-07 01:01:29 -0800713 kfree(info->dtmf_state);
714 info->dtmf_state = NULL;
715 kfree(info->silence_state);
716 info->silence_state = NULL;
717 kfree(info->adpcms);
718 info->adpcms = NULL;
719 kfree(info->adpcmr);
720 info->adpcmr = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721#endif
722 if ((info->msr & UART_MSR_RI) &&
Joe Perches475be4d2012-02-19 19:52:38 -0800723 (info->emu.mdmreg[REG_RUNG] & BIT_RUNG))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 isdn_tty_modem_result(RESULT_RUNG, info);
725 info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
726 info->lsr |= UART_LSR_TEMT;
727
728 if (local) {
729 cmd.driver = di;
730 cmd.command = ISDN_CMD_HANGUP;
731 cmd.arg = ch;
732 isdn_command(&cmd);
733 }
734
735 isdn_all_eaz(di, ch);
736 info->emu.mdmreg[REG_RINGCNT] = 0;
737 isdn_free_channel(di, ch, 0);
738
739 if (info->drv_index >= 0) {
740 dev->m_idx[info->drv_index] = -1;
741 info->drv_index = -1;
742 }
743}
744
745/*
Joe Perches475be4d2012-02-19 19:52:38 -0800746 * Begin of a CAPI like interface, currently used only for
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 * supplementary service (CAPI 2.0 part III)
748 */
749#include <linux/isdn/capicmd.h>
Paul Gortmaker07a97fe2011-08-30 12:08:51 -0400750#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751
752int
753isdn_tty_capi_facility(capi_msg *cm) {
Joe Perches475be4d2012-02-19 19:52:38 -0800754 return (-1); /* dummy */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755}
756
757/* isdn_tty_suspend() tries to suspend the current tty connection
758 */
759static void
Joe Perches475be4d2012-02-19 19:52:38 -0800760isdn_tty_suspend(char *id, modem_info *info, atemu *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761{
762 isdn_ctrl cmd;
Joe Perches475be4d2012-02-19 19:52:38 -0800763
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 int l;
765
766 if (!info)
767 return;
768
769#ifdef ISDN_DEBUG_MODEM_SERVICES
770 printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
771#endif
772 l = strlen(id);
773 if ((info->isdn_driver >= 0)) {
Joe Perches475be4d2012-02-19 19:52:38 -0800774 cmd.parm.cmsg.Length = l + 18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 cmd.parm.cmsg.Command = CAPI_FACILITY;
776 cmd.parm.cmsg.Subcommand = CAPI_REQ;
777 cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
778 cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
779 cmd.parm.cmsg.para[1] = 0;
780 cmd.parm.cmsg.para[2] = l + 3;
781 cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
782 cmd.parm.cmsg.para[4] = 0;
783 cmd.parm.cmsg.para[5] = l;
784 strncpy(&cmd.parm.cmsg.para[6], id, l);
785 cmd.command = CAPI_PUT_MESSAGE;
786 cmd.driver = info->isdn_driver;
787 cmd.arg = info->isdn_channel;
788 isdn_command(&cmd);
789 }
790}
791
792/* isdn_tty_resume() tries to resume a suspended call
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300793 * setup of the lower levels before that. unfortunately here is no
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 * checking for compatibility of used protocols implemented by Q931
795 * It does the same things like isdn_tty_dial, the last command
796 * is different, may be we can merge it.
797 */
798
799static void
Joe Perches475be4d2012-02-19 19:52:38 -0800800isdn_tty_resume(char *id, modem_info *info, atemu *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801{
802 int usg = ISDN_USAGE_MODEM;
803 int si = 7;
804 int l2 = m->mdmreg[REG_L2PROT];
805 isdn_ctrl cmd;
806 ulong flags;
807 int i;
808 int j;
809 int l;
810
811 l = strlen(id);
812 for (j = 7; j >= 0; j--)
813 if (m->mdmreg[REG_SI1] & (1 << j)) {
814 si = bit2si[j];
815 break;
816 }
817 usg = isdn_calc_usage(si, l2);
818#ifdef CONFIG_ISDN_AUDIO
Joe Perches475be4d2012-02-19 19:52:38 -0800819 if ((si == 1) &&
820 (l2 != ISDN_PROTO_L2_MODEM)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821#ifdef CONFIG_ISDN_TTY_FAX
Joe Perches475be4d2012-02-19 19:52:38 -0800822 && (l2 != ISDN_PROTO_L2_FAX)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823#endif
824 ) {
825 l2 = ISDN_PROTO_L2_TRANS;
826 usg = ISDN_USAGE_VOICE;
827 }
828#endif
829 m->mdmreg[REG_SI1I] = si2bit[si];
830 spin_lock_irqsave(&dev->lock, flags);
831 i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
832 if (i < 0) {
833 spin_unlock_irqrestore(&dev->lock, flags);
834 isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
835 } else {
836 info->isdn_driver = dev->drvmap[i];
837 info->isdn_channel = dev->chanmap[i];
838 info->drv_index = i;
839 dev->m_idx[i] = info->line;
840 dev->usage[i] |= ISDN_USAGE_OUTGOING;
841 info->last_dir = 1;
842// strcpy(info->last_num, n);
843 isdn_info_update();
844 spin_unlock_irqrestore(&dev->lock, flags);
845 cmd.driver = info->isdn_driver;
846 cmd.arg = info->isdn_channel;
847 cmd.command = ISDN_CMD_CLREAZ;
848 isdn_command(&cmd);
849 strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
850 cmd.driver = info->isdn_driver;
851 cmd.command = ISDN_CMD_SETEAZ;
852 isdn_command(&cmd);
853 cmd.driver = info->isdn_driver;
854 cmd.command = ISDN_CMD_SETL2;
855 info->last_l2 = l2;
856 cmd.arg = info->isdn_channel + (l2 << 8);
857 isdn_command(&cmd);
858 cmd.driver = info->isdn_driver;
859 cmd.command = ISDN_CMD_SETL3;
860 cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
861 isdn_command(&cmd);
862 cmd.driver = info->isdn_driver;
863 cmd.arg = info->isdn_channel;
Joe Perches475be4d2012-02-19 19:52:38 -0800864 cmd.parm.cmsg.Length = l + 18;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865 cmd.parm.cmsg.Command = CAPI_FACILITY;
866 cmd.parm.cmsg.Subcommand = CAPI_REQ;
867 cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
868 cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
869 cmd.parm.cmsg.para[1] = 0;
Joe Perches475be4d2012-02-19 19:52:38 -0800870 cmd.parm.cmsg.para[2] = l + 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
872 cmd.parm.cmsg.para[4] = 0;
873 cmd.parm.cmsg.para[5] = l;
874 strncpy(&cmd.parm.cmsg.para[6], id, l);
Joe Perches475be4d2012-02-19 19:52:38 -0800875 cmd.command = CAPI_PUT_MESSAGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 info->dialing = 1;
877// strcpy(dev->num[i], n);
878 isdn_info_update();
879 isdn_command(&cmd);
880 isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
881 }
882}
883
884/* isdn_tty_send_msg() sends a message to a HL driver
885 * This is used for hybrid modem cards to send AT commands to it
886 */
887
888static void
Joe Perches475be4d2012-02-19 19:52:38 -0800889isdn_tty_send_msg(modem_info *info, atemu *m, char *msg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890{
891 int usg = ISDN_USAGE_MODEM;
892 int si = 7;
893 int l2 = m->mdmreg[REG_L2PROT];
894 isdn_ctrl cmd;
895 ulong flags;
896 int i;
897 int j;
898 int l;
899
900 l = strlen(msg);
901 if (!l) {
902 isdn_tty_modem_result(RESULT_ERROR, info);
903 return;
904 }
905 for (j = 7; j >= 0; j--)
906 if (m->mdmreg[REG_SI1] & (1 << j)) {
907 si = bit2si[j];
908 break;
909 }
910 usg = isdn_calc_usage(si, l2);
911#ifdef CONFIG_ISDN_AUDIO
Joe Perches475be4d2012-02-19 19:52:38 -0800912 if ((si == 1) &&
913 (l2 != ISDN_PROTO_L2_MODEM)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914#ifdef CONFIG_ISDN_TTY_FAX
Joe Perches475be4d2012-02-19 19:52:38 -0800915 && (l2 != ISDN_PROTO_L2_FAX)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916#endif
917 ) {
918 l2 = ISDN_PROTO_L2_TRANS;
919 usg = ISDN_USAGE_VOICE;
920 }
921#endif
922 m->mdmreg[REG_SI1I] = si2bit[si];
923 spin_lock_irqsave(&dev->lock, flags);
924 i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
925 if (i < 0) {
926 spin_unlock_irqrestore(&dev->lock, flags);
927 isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
928 } else {
929 info->isdn_driver = dev->drvmap[i];
930 info->isdn_channel = dev->chanmap[i];
931 info->drv_index = i;
932 dev->m_idx[i] = info->line;
933 dev->usage[i] |= ISDN_USAGE_OUTGOING;
934 info->last_dir = 1;
935 isdn_info_update();
936 spin_unlock_irqrestore(&dev->lock, flags);
937 cmd.driver = info->isdn_driver;
938 cmd.arg = info->isdn_channel;
939 cmd.command = ISDN_CMD_CLREAZ;
940 isdn_command(&cmd);
941 strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
942 cmd.driver = info->isdn_driver;
943 cmd.command = ISDN_CMD_SETEAZ;
944 isdn_command(&cmd);
945 cmd.driver = info->isdn_driver;
946 cmd.command = ISDN_CMD_SETL2;
947 info->last_l2 = l2;
948 cmd.arg = info->isdn_channel + (l2 << 8);
949 isdn_command(&cmd);
950 cmd.driver = info->isdn_driver;
951 cmd.command = ISDN_CMD_SETL3;
952 cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
953 isdn_command(&cmd);
954 cmd.driver = info->isdn_driver;
955 cmd.arg = info->isdn_channel;
Joe Perches475be4d2012-02-19 19:52:38 -0800956 cmd.parm.cmsg.Length = l + 14;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
958 cmd.parm.cmsg.Subcommand = CAPI_REQ;
959 cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
Joe Perches475be4d2012-02-19 19:52:38 -0800960 cmd.parm.cmsg.para[0] = l + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961 strncpy(&cmd.parm.cmsg.para[1], msg, l);
Joe Perches475be4d2012-02-19 19:52:38 -0800962 cmd.parm.cmsg.para[l + 1] = 0xd;
963 cmd.command = CAPI_PUT_MESSAGE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964/* info->dialing = 1;
965 strcpy(dev->num[i], n);
966 isdn_info_update();
967*/
968 isdn_command(&cmd);
969 }
970}
971
972static inline int
973isdn_tty_paranoia_check(modem_info *info, char *name, const char *routine)
974{
975#ifdef MODEM_PARANOIA_CHECK
976 if (!info) {
977 printk(KERN_WARNING "isdn_tty: null info_struct for %s in %s\n",
Joe Perches475be4d2012-02-19 19:52:38 -0800978 name, routine);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 return 1;
980 }
981 if (info->magic != ISDN_ASYNC_MAGIC) {
982 printk(KERN_WARNING "isdn_tty: bad magic for modem struct %s in %s\n",
983 name, routine);
984 return 1;
985 }
986#endif
987 return 0;
988}
989
990/*
991 * This routine is called to set the UART divisor registers to match
992 * the specified baud rate for a serial port.
993 */
994static void
Joe Perches475be4d2012-02-19 19:52:38 -0800995isdn_tty_change_speed(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700996{
997 uint cflag,
Joe Perches475be4d2012-02-19 19:52:38 -0800998 cval,
999 quot;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 int i;
1001
1002 if (!info->tty || !info->tty->termios)
1003 return;
1004 cflag = info->tty->termios->c_cflag;
1005
1006 quot = i = cflag & CBAUD;
1007 if (i & CBAUDEX) {
1008 i &= ~CBAUDEX;
1009 if (i < 1 || i > 2)
1010 info->tty->termios->c_cflag &= ~CBAUDEX;
1011 else
1012 i += 15;
1013 }
1014 if (quot) {
1015 info->mcr |= UART_MCR_DTR;
1016 isdn_tty_modem_ncarrier(info);
1017 } else {
1018 info->mcr &= ~UART_MCR_DTR;
1019 if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
1020#ifdef ISDN_DEBUG_MODEM_HUP
1021 printk(KERN_DEBUG "Mhup in changespeed\n");
1022#endif
1023 if (info->online)
1024 info->ncarrier = 1;
1025 isdn_tty_modem_reset_regs(info, 0);
1026 isdn_tty_modem_hup(info, 1);
1027 }
1028 return;
1029 }
1030 /* byte size and parity */
1031 cval = cflag & (CSIZE | CSTOPB);
1032 cval >>= 4;
1033 if (cflag & PARENB)
1034 cval |= UART_LCR_PARITY;
1035 if (!(cflag & PARODD))
1036 cval |= UART_LCR_EPAR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001037
1038 /* CTS flow control flag and modem status interrupts */
1039 if (cflag & CRTSCTS) {
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001040 info->flags |= ASYNC_CTS_FLOW;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 } else
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001042 info->flags &= ~ASYNC_CTS_FLOW;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001043 if (cflag & CLOCAL)
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001044 info->flags &= ~ASYNC_CHECK_CD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 else {
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001046 info->flags |= ASYNC_CHECK_CD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 }
1048}
1049
1050static int
Joe Perches475be4d2012-02-19 19:52:38 -08001051isdn_tty_startup(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052{
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001053 if (info->flags & ASYNC_INITIALIZED)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054 return 0;
1055 isdn_lock_drivers();
1056#ifdef ISDN_DEBUG_MODEM_OPEN
1057 printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
1058#endif
1059 /*
1060 * Now, initialize the UART
1061 */
1062 info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
1063 if (info->tty)
1064 clear_bit(TTY_IO_ERROR, &info->tty->flags);
1065 /*
1066 * and set the speed of the serial port
1067 */
1068 isdn_tty_change_speed(info);
1069
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001070 info->flags |= ASYNC_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
1072 info->send_outstanding = 0;
1073 return 0;
1074}
1075
1076/*
1077 * This routine will shutdown a serial port; interrupts are disabled, and
1078 * DTR is dropped if the hangup on close termio flag is on.
1079 */
1080static void
Joe Perches475be4d2012-02-19 19:52:38 -08001081isdn_tty_shutdown(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082{
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001083 if (!(info->flags & ASYNC_INITIALIZED))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001084 return;
1085#ifdef ISDN_DEBUG_MODEM_OPEN
1086 printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
1087#endif
1088 isdn_unlock_drivers();
1089 info->msr &= ~UART_MSR_RI;
1090 if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
1091 info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
1092 if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
1093 isdn_tty_modem_reset_regs(info, 0);
1094#ifdef ISDN_DEBUG_MODEM_HUP
1095 printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
1096#endif
1097 isdn_tty_modem_hup(info, 1);
1098 }
1099 }
1100 if (info->tty)
1101 set_bit(TTY_IO_ERROR, &info->tty->flags);
1102
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001103 info->flags &= ~ASYNC_INITIALIZED;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001104}
1105
1106/* isdn_tty_write() is the main send-routine. It is called from the upper
1107 * levels within the kernel to perform sending data. Depending on the
1108 * online-flag it either directs output to the at-command-interpreter or
1109 * to the lower level. Additional tasks done here:
1110 * - If online, check for escape-sequence (+++)
1111 * - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
1112 * - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
1113 * - If dialing, abort dial.
1114 */
1115static int
Joe Perches475be4d2012-02-19 19:52:38 -08001116isdn_tty_write(struct tty_struct *tty, const u_char *buf, int count)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117{
1118 int c;
1119 int total = 0;
1120 modem_info *info = (modem_info *) tty->driver_data;
1121 atemu *m = &info->emu;
1122
1123 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write"))
1124 return 0;
1125 /* See isdn_tty_senddown() */
1126 atomic_inc(&info->xmit_lock);
1127 while (1) {
1128 c = count;
1129 if (c > info->xmit_size - info->xmit_count)
1130 c = info->xmit_size - info->xmit_count;
1131 if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize)
1132 c = dev->drv[info->isdn_driver]->maxbufsize;
1133 if (c <= 0)
1134 break;
1135 if ((info->online > 1)
1136#ifdef CONFIG_ISDN_AUDIO
1137 || (info->vonline & 3)
1138#endif
1139 ) {
1140#ifdef CONFIG_ISDN_AUDIO
1141 if (!info->vonline)
1142#endif
1143 isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c,
1144 &(m->pluscount),
1145 &(m->lastplus));
1146 memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
1147#ifdef CONFIG_ISDN_AUDIO
1148 if (info->vonline) {
1149 int cc = isdn_tty_handleDLEdown(info, m, c);
1150 if (info->vonline & 2) {
1151 if (!cc) {
1152 /* If DLE decoding results in zero-transmit, but
1153 * c originally was non-zero, do a wakeup.
1154 */
1155 tty_wakeup(tty);
1156 info->msr |= UART_MSR_CTS;
1157 info->lsr |= UART_LSR_TEMT;
1158 }
1159 info->xmit_count += cc;
1160 }
1161 if ((info->vonline & 3) == 1) {
1162 /* Do NOT handle Ctrl-Q or Ctrl-S
1163 * when in full-duplex audio mode.
1164 */
1165 if (isdn_tty_end_vrx(buf, c)) {
1166 info->vonline &= ~1;
1167#ifdef ISDN_DEBUG_MODEM_VOICE
1168 printk(KERN_DEBUG
1169 "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n",
1170 info->line);
1171#endif
1172 isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
1173 }
1174 }
1175 } else
Joe Perches475be4d2012-02-19 19:52:38 -08001176 if (TTY_IS_FCLASS1(info)) {
1177 int cc = isdn_tty_handleDLEdown(info, m, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
Joe Perches475be4d2012-02-19 19:52:38 -08001179 if (info->vonline & 4) { /* ETX seen */
1180 isdn_ctrl c;
1181
1182 c.command = ISDN_CMD_FAXCMD;
1183 c.driver = info->isdn_driver;
1184 c.arg = info->isdn_channel;
1185 c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL;
1186 c.parm.aux.subcmd = ETX;
1187 isdn_command(&c);
1188 }
1189 info->vonline = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190#ifdef ISDN_DEBUG_MODEM_VOICE
Joe Perches475be4d2012-02-19 19:52:38 -08001191 printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192#endif
Joe Perches475be4d2012-02-19 19:52:38 -08001193 info->xmit_count += cc;
1194 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195#endif
Joe Perches475be4d2012-02-19 19:52:38 -08001196 info->xmit_count += c;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 } else {
1198 info->msr |= UART_MSR_CTS;
1199 info->lsr |= UART_LSR_TEMT;
1200 if (info->dialing) {
1201 info->dialing = 0;
1202#ifdef ISDN_DEBUG_MODEM_HUP
1203 printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
1204#endif
1205 isdn_tty_modem_result(RESULT_NO_CARRIER, info);
1206 isdn_tty_modem_hup(info, 1);
1207 } else
1208 c = isdn_tty_edit_at(buf, c, info);
1209 }
1210 buf += c;
1211 count -= c;
1212 total += c;
1213 }
1214 atomic_dec(&info->xmit_lock);
David S. Millerb03efcf2005-07-08 14:57:23 -07001215 if ((info->xmit_count) || !skb_queue_empty(&info->xmit_queue)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 if (m->mdmreg[REG_DXMT] & BIT_DXMT) {
1217 isdn_tty_senddown(info);
1218 isdn_tty_tint(info);
1219 }
1220 isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
1221 }
1222 return total;
1223}
1224
1225static int
1226isdn_tty_write_room(struct tty_struct *tty)
1227{
1228 modem_info *info = (modem_info *) tty->driver_data;
1229 int ret;
1230
1231 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write_room"))
1232 return 0;
1233 if (!info->online)
1234 return info->xmit_size;
1235 ret = info->xmit_size - info->xmit_count;
1236 return (ret < 0) ? 0 : ret;
1237}
1238
1239static int
1240isdn_tty_chars_in_buffer(struct tty_struct *tty)
1241{
1242 modem_info *info = (modem_info *) tty->driver_data;
1243
1244 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_chars_in_buffer"))
1245 return 0;
1246 if (!info->online)
1247 return 0;
1248 return (info->xmit_count);
1249}
1250
1251static void
1252isdn_tty_flush_buffer(struct tty_struct *tty)
1253{
1254 modem_info *info;
1255
1256 if (!tty) {
1257 return;
1258 }
1259 info = (modem_info *) tty->driver_data;
1260 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_buffer")) {
1261 return;
1262 }
1263 isdn_tty_cleanup_xmit(info);
1264 info->xmit_count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 tty_wakeup(tty);
1266}
1267
1268static void
1269isdn_tty_flush_chars(struct tty_struct *tty)
1270{
1271 modem_info *info = (modem_info *) tty->driver_data;
1272
1273 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_chars"))
1274 return;
David S. Millerb03efcf2005-07-08 14:57:23 -07001275 if ((info->xmit_count) || !skb_queue_empty(&info->xmit_queue))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
1277}
1278
1279/*
1280 * ------------------------------------------------------------
1281 * isdn_tty_throttle()
1282 *
1283 * This routine is called by the upper-layer tty layer to signal that
1284 * incoming characters should be throttled.
1285 * ------------------------------------------------------------
1286 */
1287static void
1288isdn_tty_throttle(struct tty_struct *tty)
1289{
1290 modem_info *info = (modem_info *) tty->driver_data;
1291
1292 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_throttle"))
1293 return;
1294 if (I_IXOFF(tty))
1295 info->x_char = STOP_CHAR(tty);
1296 info->mcr &= ~UART_MCR_RTS;
1297}
1298
1299static void
1300isdn_tty_unthrottle(struct tty_struct *tty)
1301{
1302 modem_info *info = (modem_info *) tty->driver_data;
1303
1304 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_unthrottle"))
1305 return;
1306 if (I_IXOFF(tty)) {
1307 if (info->x_char)
1308 info->x_char = 0;
1309 else
1310 info->x_char = START_CHAR(tty);
1311 }
1312 info->mcr |= UART_MCR_RTS;
1313}
1314
1315/*
1316 * ------------------------------------------------------------
1317 * isdn_tty_ioctl() and friends
1318 * ------------------------------------------------------------
1319 */
1320
1321/*
1322 * isdn_tty_get_lsr_info - get line status register info
1323 *
1324 * Purpose: Let user call ioctl() to get info when the UART physically
1325 * is emptied. On bus types like RS485, the transmitter must
1326 * release the bus after transmitting. This must be done when
1327 * the transmit shift register is empty, not be done when the
1328 * transmit holding register is empty. This functionality
1329 * allows RS485 driver to be written in user space.
1330 */
1331static int
Joe Perches475be4d2012-02-19 19:52:38 -08001332isdn_tty_get_lsr_info(modem_info *info, uint __user *value)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333{
1334 u_char status;
1335 uint result;
1336
1337 status = info->lsr;
1338 result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
1339 return put_user(result, value);
1340}
1341
1342
1343static int
Alan Cox60b33c12011-02-14 16:26:14 +00001344isdn_tty_tiocmget(struct tty_struct *tty)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345{
1346 modem_info *info = (modem_info *) tty->driver_data;
1347 u_char control, status;
1348
Harvey Harrison156f1ed2008-04-28 02:14:40 -07001349 if (isdn_tty_paranoia_check(info, tty->name, __func__))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 return -ENODEV;
1351 if (tty->flags & (1 << TTY_IO_ERROR))
1352 return -EIO;
1353
Arnd Bergmann72250d42010-09-14 09:35:04 +00001354 mutex_lock(&modem_info_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001355#ifdef ISDN_DEBUG_MODEM_IOCTL
1356 printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
1357#endif
1358
1359 control = info->mcr;
1360 status = info->msr;
Arnd Bergmann72250d42010-09-14 09:35:04 +00001361 mutex_unlock(&modem_info_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362 return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
Joe Perches475be4d2012-02-19 19:52:38 -08001363 | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
1364 | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
1365 | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
1366 | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
1367 | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001368}
1369
1370static int
Alan Cox20b9d172011-02-14 16:26:50 +00001371isdn_tty_tiocmset(struct tty_struct *tty,
Joe Perches475be4d2012-02-19 19:52:38 -08001372 unsigned int set, unsigned int clear)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373{
1374 modem_info *info = (modem_info *) tty->driver_data;
1375
Harvey Harrison156f1ed2008-04-28 02:14:40 -07001376 if (isdn_tty_paranoia_check(info, tty->name, __func__))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377 return -ENODEV;
1378 if (tty->flags & (1 << TTY_IO_ERROR))
1379 return -EIO;
1380
1381#ifdef ISDN_DEBUG_MODEM_IOCTL
1382 printk(KERN_DEBUG "ttyI%d ioctl TIOCMxxx: %x %x\n", info->line, set, clear);
1383#endif
1384
Arnd Bergmann72250d42010-09-14 09:35:04 +00001385 mutex_lock(&modem_info_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 if (set & TIOCM_RTS)
1387 info->mcr |= UART_MCR_RTS;
1388 if (set & TIOCM_DTR) {
1389 info->mcr |= UART_MCR_DTR;
1390 isdn_tty_modem_ncarrier(info);
1391 }
1392
1393 if (clear & TIOCM_RTS)
1394 info->mcr &= ~UART_MCR_RTS;
1395 if (clear & TIOCM_DTR) {
1396 info->mcr &= ~UART_MCR_DTR;
1397 if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
1398 isdn_tty_modem_reset_regs(info, 0);
1399#ifdef ISDN_DEBUG_MODEM_HUP
1400 printk(KERN_DEBUG "Mhup in TIOCMSET\n");
1401#endif
1402 if (info->online)
1403 info->ncarrier = 1;
1404 isdn_tty_modem_hup(info, 1);
1405 }
1406 }
Arnd Bergmann72250d42010-09-14 09:35:04 +00001407 mutex_unlock(&modem_info_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408 return 0;
1409}
1410
1411static int
Alan Cox6caa76b2011-02-14 16:27:22 +00001412isdn_tty_ioctl(struct tty_struct *tty, uint cmd, ulong arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413{
1414 modem_info *info = (modem_info *) tty->driver_data;
1415 int retval;
1416
1417 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_ioctl"))
1418 return -ENODEV;
1419 if (tty->flags & (1 << TTY_IO_ERROR))
1420 return -EIO;
1421 switch (cmd) {
Joe Perches475be4d2012-02-19 19:52:38 -08001422 case TCSBRK: /* SVID version: non-zero arg --> no break */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001423#ifdef ISDN_DEBUG_MODEM_IOCTL
Joe Perches475be4d2012-02-19 19:52:38 -08001424 printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425#endif
Joe Perches475be4d2012-02-19 19:52:38 -08001426 retval = tty_check_change(tty);
1427 if (retval)
1428 return retval;
1429 tty_wait_until_sent(tty, 0);
1430 return 0;
1431 case TCSBRKP: /* support for POSIX tcsendbreak() */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432#ifdef ISDN_DEBUG_MODEM_IOCTL
Joe Perches475be4d2012-02-19 19:52:38 -08001433 printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434#endif
Joe Perches475be4d2012-02-19 19:52:38 -08001435 retval = tty_check_change(tty);
1436 if (retval)
1437 return retval;
1438 tty_wait_until_sent(tty, 0);
1439 return 0;
1440 case TIOCSERGETLSR: /* Get line status register */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441#ifdef ISDN_DEBUG_MODEM_IOCTL
Joe Perches475be4d2012-02-19 19:52:38 -08001442 printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443#endif
Joe Perches475be4d2012-02-19 19:52:38 -08001444 return isdn_tty_get_lsr_info(info, (uint __user *) arg);
1445 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446#ifdef ISDN_DEBUG_MODEM_IOCTL
Joe Perches475be4d2012-02-19 19:52:38 -08001447 printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448#endif
Joe Perches475be4d2012-02-19 19:52:38 -08001449 return -ENOIOCTLCMD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 }
1451 return 0;
1452}
1453
1454static void
Alan Cox606d0992006-12-08 02:38:45 -08001455isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456{
1457 modem_info *info = (modem_info *) tty->driver_data;
1458
1459 if (!old_termios)
1460 isdn_tty_change_speed(info);
1461 else {
Alan Cox6e4d3762008-04-30 00:53:27 -07001462 if (tty->termios->c_cflag == old_termios->c_cflag &&
1463 tty->termios->c_ispeed == old_termios->c_ispeed &&
1464 tty->termios->c_ospeed == old_termios->c_ospeed)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 return;
1466 isdn_tty_change_speed(info);
1467 if ((old_termios->c_cflag & CRTSCTS) &&
Alan Cox6e4d3762008-04-30 00:53:27 -07001468 !(tty->termios->c_cflag & CRTSCTS))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 tty->hw_stopped = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470 }
1471}
1472
1473/*
1474 * ------------------------------------------------------------
1475 * isdn_tty_open() and friends
1476 * ------------------------------------------------------------
1477 */
1478static int
Joe Perches475be4d2012-02-19 19:52:38 -08001479isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001480{
1481 DECLARE_WAITQUEUE(wait, NULL);
1482 int do_clocal = 0;
1483 int retval;
1484
1485 /*
1486 * If the device is in the middle of being closed, then block
1487 * until it's done, and then try again.
1488 */
1489 if (tty_hung_up_p(filp) ||
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001490 (info->flags & ASYNC_CLOSING)) {
1491 if (info->flags & ASYNC_CLOSING)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 interruptible_sleep_on(&info->close_wait);
1493#ifdef MODEM_DO_RESTART
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001494 if (info->flags & ASYNC_HUP_NOTIFY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 return -EAGAIN;
1496 else
1497 return -ERESTARTSYS;
1498#else
1499 return -EAGAIN;
1500#endif
1501 }
1502 /*
1503 * If non-blocking mode is set, then make the check up front
1504 * and then exit.
1505 */
1506 if ((filp->f_flags & O_NONBLOCK) ||
1507 (tty->flags & (1 << TTY_IO_ERROR))) {
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001508 info->flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 return 0;
1510 }
Jiri Slaby05eb48b2012-04-02 13:53:49 +02001511 if (tty->termios->c_cflag & CLOCAL)
1512 do_clocal = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 /*
1514 * Block waiting for the carrier detect and the line to become
1515 * free (i.e., not in use by the callout). While we are in
1516 * this loop, info->count is dropped by one, so that
1517 * isdn_tty_close() knows when to free things. We restore it upon
1518 * exit, either normal or abnormal.
1519 */
1520 retval = 0;
1521 add_wait_queue(&info->open_wait, &wait);
1522#ifdef ISDN_DEBUG_MODEM_OPEN
1523 printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
1524 info->line, info->count);
1525#endif
1526 if (!(tty_hung_up_p(filp)))
1527 info->count--;
1528 info->blocked_open++;
1529 while (1) {
1530 set_current_state(TASK_INTERRUPTIBLE);
1531 if (tty_hung_up_p(filp) ||
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001532 !(info->flags & ASYNC_INITIALIZED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533#ifdef MODEM_DO_RESTART
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001534 if (info->flags & ASYNC_HUP_NOTIFY)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 retval = -EAGAIN;
1536 else
1537 retval = -ERESTARTSYS;
1538#else
1539 retval = -EAGAIN;
1540#endif
1541 break;
1542 }
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001543 if (!(info->flags & ASYNC_CLOSING) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 (do_clocal || (info->msr & UART_MSR_DCD))) {
1545 break;
1546 }
1547 if (signal_pending(current)) {
1548 retval = -ERESTARTSYS;
1549 break;
1550 }
1551#ifdef ISDN_DEBUG_MODEM_OPEN
1552 printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
1553 info->line, info->count);
1554#endif
1555 schedule();
1556 }
1557 current->state = TASK_RUNNING;
1558 remove_wait_queue(&info->open_wait, &wait);
1559 if (!tty_hung_up_p(filp))
1560 info->count++;
1561 info->blocked_open--;
1562#ifdef ISDN_DEBUG_MODEM_OPEN
1563 printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
1564 info->line, info->count);
1565#endif
1566 if (retval)
1567 return retval;
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001568 info->flags |= ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001569 return 0;
1570}
1571
1572/*
1573 * This routine is called whenever a serial port is opened. It
1574 * enables interrupts for a serial port, linking in its async structure into
1575 * the IRQ chain. It also performs the serial-specific
1576 * initialization for the tty structure.
1577 */
1578static int
1579isdn_tty_open(struct tty_struct *tty, struct file *filp)
1580{
1581 modem_info *info;
Jiri Slaby410235f2012-03-05 14:52:01 +01001582 int retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
Jiri Slaby410235f2012-03-05 14:52:01 +01001584 info = &dev->mdm.info[tty->index];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001585 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open"))
1586 return -ENODEV;
1587 if (!try_module_get(info->owner)) {
Harvey Harrison156f1ed2008-04-28 02:14:40 -07001588 printk(KERN_WARNING "%s: cannot reserve module\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 return -ENODEV;
1590 }
1591#ifdef ISDN_DEBUG_MODEM_OPEN
Joe Perches475be4d2012-02-19 19:52:38 -08001592 printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593 info->count);
1594#endif
1595 info->count++;
1596 tty->driver_data = info;
1597 info->tty = tty;
1598 /*
1599 * Start up serial port
1600 */
1601 retval = isdn_tty_startup(info);
1602 if (retval) {
1603#ifdef ISDN_DEBUG_MODEM_OPEN
1604 printk(KERN_DEBUG "isdn_tty_open return after startup\n");
1605#endif
1606 module_put(info->owner);
1607 return retval;
1608 }
1609 retval = isdn_tty_block_til_ready(tty, filp, info);
1610 if (retval) {
1611#ifdef ISDN_DEBUG_MODEM_OPEN
1612 printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
1613#endif
1614 module_put(info->owner);
1615 return retval;
1616 }
1617#ifdef ISDN_DEBUG_MODEM_OPEN
1618 printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
1619#endif
1620 dev->modempoll++;
1621#ifdef ISDN_DEBUG_MODEM_OPEN
1622 printk(KERN_DEBUG "isdn_tty_open normal exit\n");
1623#endif
1624 return 0;
1625}
1626
1627static void
1628isdn_tty_close(struct tty_struct *tty, struct file *filp)
1629{
1630 modem_info *info = (modem_info *) tty->driver_data;
1631 ulong timeout;
1632
1633 if (!info || isdn_tty_paranoia_check(info, tty->name, "isdn_tty_close"))
1634 return;
1635 if (tty_hung_up_p(filp)) {
1636#ifdef ISDN_DEBUG_MODEM_OPEN
1637 printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
1638#endif
1639 return;
1640 }
1641 if ((tty->count == 1) && (info->count != 1)) {
1642 /*
1643 * Uh, oh. tty->count is 1, which means that the tty
1644 * structure will be freed. Info->count should always
1645 * be one in these conditions. If it's greater than
1646 * one, we've got real problems, since it means the
1647 * serial port won't be shutdown.
1648 */
1649 printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
1650 "info->count is %d\n", info->count);
1651 info->count = 1;
1652 }
1653 if (--info->count < 0) {
1654 printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
1655 info->line, info->count);
1656 info->count = 0;
1657 }
1658 if (info->count) {
1659#ifdef ISDN_DEBUG_MODEM_OPEN
1660 printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
1661#endif
Karsten Keil7cb94782006-03-06 15:42:39 -08001662 module_put(info->owner);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663 return;
1664 }
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001665 info->flags |= ASYNC_CLOSING;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666
1667 tty->closing = 1;
1668 /*
1669 * At this point we stop accepting input. To do this, we
1670 * disable the receive line status interrupts, and tell the
1671 * interrupt driver to stop checking the data ready bit in the
1672 * line status register.
1673 */
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001674 if (info->flags & ASYNC_INITIALIZED) {
Jiri Slaby0b058352011-08-25 15:12:08 +02001675 tty_wait_until_sent_from_close(tty, 3000); /* 30 seconds timeout */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001676 /*
1677 * Before we drop DTR, make sure the UART transmitter
1678 * has completely drained; this is especially
1679 * important if there is a transmit FIFO!
1680 */
1681 timeout = jiffies + HZ;
1682 while (!(info->lsr & UART_LSR_TEMT)) {
Nishanth Aravamudan24763c42005-11-07 01:01:16 -08001683 schedule_timeout_interruptible(20);
Joe Perches475be4d2012-02-19 19:52:38 -08001684 if (time_after(jiffies, timeout))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685 break;
1686 }
1687 }
1688 dev->modempoll--;
1689 isdn_tty_shutdown(info);
Alan Cox978e5952008-04-30 00:53:59 -07001690 isdn_tty_flush_buffer(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691 tty_ldisc_flush(tty);
1692 info->tty = NULL;
1693 info->ncarrier = 0;
1694 tty->closing = 0;
1695 module_put(info->owner);
1696 if (info->blocked_open) {
1697 msleep_interruptible(500);
1698 wake_up_interruptible(&info->open_wait);
1699 }
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001700 info->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 wake_up_interruptible(&info->close_wait);
1702#ifdef ISDN_DEBUG_MODEM_OPEN
1703 printk(KERN_DEBUG "isdn_tty_close normal exit\n");
1704#endif
1705}
1706
1707/*
1708 * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
1709 */
1710static void
1711isdn_tty_hangup(struct tty_struct *tty)
1712{
1713 modem_info *info = (modem_info *) tty->driver_data;
1714
1715 if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup"))
1716 return;
1717 isdn_tty_shutdown(info);
1718 info->count = 0;
Jiri Slaby6776a2f2012-04-02 13:53:50 +02001719 info->flags &= ~ASYNC_NORMAL_ACTIVE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 info->tty = NULL;
1721 wake_up_interruptible(&info->open_wait);
1722}
1723
1724/* This routine initializes all emulator-data.
1725 */
1726static void
Joe Perches475be4d2012-02-19 19:52:38 -08001727isdn_tty_reset_profile(atemu *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728{
1729 m->profile[0] = 0;
1730 m->profile[1] = 0;
1731 m->profile[2] = 43;
1732 m->profile[3] = 13;
1733 m->profile[4] = 10;
1734 m->profile[5] = 8;
1735 m->profile[6] = 3;
1736 m->profile[7] = 60;
1737 m->profile[8] = 2;
1738 m->profile[9] = 6;
1739 m->profile[10] = 7;
1740 m->profile[11] = 70;
1741 m->profile[12] = 0x45;
1742 m->profile[13] = 4;
1743 m->profile[14] = ISDN_PROTO_L2_X75I;
1744 m->profile[15] = ISDN_PROTO_L3_TRANS;
1745 m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
1746 m->profile[17] = ISDN_MODEM_WINSIZE;
1747 m->profile[18] = 4;
1748 m->profile[19] = 0;
1749 m->profile[20] = 0;
1750 m->profile[23] = 0;
1751 m->pmsn[0] = '\0';
1752 m->plmsn[0] = '\0';
1753}
1754
1755#ifdef CONFIG_ISDN_AUDIO
1756static void
Joe Perches475be4d2012-02-19 19:52:38 -08001757isdn_tty_modem_reset_vpar(atemu *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758{
1759 m->vpar[0] = 2; /* Voice-device (2 = phone line) */
1760 m->vpar[1] = 0; /* Silence detection level (0 = none ) */
1761 m->vpar[2] = 70; /* Silence interval (7 sec. ) */
1762 m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */
1763 m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */
1764 m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */
1765}
1766#endif
1767
1768#ifdef CONFIG_ISDN_TTY_FAX
1769static void
Joe Perches475be4d2012-02-19 19:52:38 -08001770isdn_tty_modem_reset_faxpar(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001771{
1772 T30_s *f = info->fax;
1773
1774 f->code = 0;
1775 f->phase = ISDN_FAX_PHASE_IDLE;
1776 f->direction = 0;
1777 f->resolution = 1; /* fine */
1778 f->rate = 5; /* 14400 bit/s */
1779 f->width = 0;
1780 f->length = 0;
1781 f->compression = 0;
1782 f->ecm = 0;
1783 f->binary = 0;
1784 f->scantime = 0;
1785 memset(&f->id[0], 32, FAXIDLEN - 1);
1786 f->id[FAXIDLEN - 1] = 0;
1787 f->badlin = 0;
1788 f->badmul = 0;
1789 f->bor = 0;
1790 f->nbc = 0;
1791 f->cq = 0;
1792 f->cr = 0;
1793 f->ctcrty = 0;
1794 f->minsp = 0;
1795 f->phcto = 30;
1796 f->rel = 0;
1797 memset(&f->pollid[0], 32, FAXIDLEN - 1);
1798 f->pollid[FAXIDLEN - 1] = 0;
1799}
1800#endif
1801
1802static void
Joe Perches475be4d2012-02-19 19:52:38 -08001803isdn_tty_modem_reset_regs(modem_info *info, int force)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804{
1805 atemu *m = &info->emu;
1806 if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
1807 memcpy(m->mdmreg, m->profile, ISDN_MODEM_NUMREG);
1808 memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
1809 memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
1810 info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
1811 }
1812#ifdef CONFIG_ISDN_AUDIO
1813 isdn_tty_modem_reset_vpar(m);
1814#endif
1815#ifdef CONFIG_ISDN_TTY_FAX
1816 isdn_tty_modem_reset_faxpar(info);
1817#endif
1818 m->mdmcmdl = 0;
1819}
1820
1821static void
Joe Perches475be4d2012-02-19 19:52:38 -08001822modem_write_profile(atemu *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823{
1824 memcpy(m->profile, m->mdmreg, ISDN_MODEM_NUMREG);
1825 memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
1826 memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
1827 if (dev->profd)
1828 send_sig(SIGIO, dev->profd, 1);
1829}
1830
Jeff Dikeb68e31d2006-10-02 02:17:18 -07001831static const struct tty_operations modem_ops = {
Joe Perches475be4d2012-02-19 19:52:38 -08001832 .open = isdn_tty_open,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 .close = isdn_tty_close,
1834 .write = isdn_tty_write,
1835 .flush_chars = isdn_tty_flush_chars,
1836 .write_room = isdn_tty_write_room,
1837 .chars_in_buffer = isdn_tty_chars_in_buffer,
1838 .flush_buffer = isdn_tty_flush_buffer,
1839 .ioctl = isdn_tty_ioctl,
1840 .throttle = isdn_tty_throttle,
1841 .unthrottle = isdn_tty_unthrottle,
1842 .set_termios = isdn_tty_set_termios,
1843 .hangup = isdn_tty_hangup,
1844 .tiocmget = isdn_tty_tiocmget,
1845 .tiocmset = isdn_tty_tiocmset,
1846};
1847
1848int
1849isdn_tty_modem_init(void)
1850{
1851 isdn_modem_t *m;
1852 int i, retval;
1853 modem_info *info;
1854
1855 m = &dev->mdm;
1856 m->tty_modem = alloc_tty_driver(ISDN_MAX_CHANNELS);
1857 if (!m->tty_modem)
1858 return -ENOMEM;
1859 m->tty_modem->name = "ttyI";
Linus Torvalds1da177e2005-04-16 15:20:36 -07001860 m->tty_modem->major = ISDN_TTY_MAJOR;
1861 m->tty_modem->minor_start = 0;
1862 m->tty_modem->type = TTY_DRIVER_TYPE_SERIAL;
1863 m->tty_modem->subtype = SERIAL_TYPE_NORMAL;
1864 m->tty_modem->init_termios = tty_std_termios;
1865 m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
Greg Kroah-Hartman331b8312005-06-20 21:15:16 -07001866 m->tty_modem->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001867 m->tty_modem->driver_name = "isdn_tty";
1868 tty_set_operations(m->tty_modem, &modem_ops);
1869 retval = tty_register_driver(m->tty_modem);
1870 if (retval) {
1871 printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n");
1872 goto err;
1873 }
1874 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
1875 info = &m->info[i];
1876#ifdef CONFIG_ISDN_TTY_FAX
1877 if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) {
1878 printk(KERN_ERR "Could not allocate fax t30-buffer\n");
1879 retval = -ENOMEM;
1880 goto err_unregister;
1881 }
1882#endif
1883#ifdef MODULE
1884 info->owner = THIS_MODULE;
1885#endif
1886 spin_lock_init(&info->readlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887 sprintf(info->last_cause, "0000");
1888 sprintf(info->last_num, "none");
1889 info->last_dir = 0;
1890 info->last_lhup = 1;
1891 info->last_l2 = -1;
1892 info->last_si = 0;
1893 isdn_tty_reset_profile(&info->emu);
1894 isdn_tty_modem_reset_regs(info, 1);
1895 info->magic = ISDN_ASYNC_MAGIC;
1896 info->line = i;
1897 info->tty = NULL;
1898 info->x_char = 0;
1899 info->count = 0;
1900 info->blocked_open = 0;
1901 init_waitqueue_head(&info->open_wait);
1902 init_waitqueue_head(&info->close_wait);
1903 info->isdn_driver = -1;
1904 info->isdn_channel = -1;
1905 info->drv_index = -1;
1906 info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
1907 init_timer(&info->nc_timer);
1908 info->nc_timer.function = isdn_tty_modem_do_ncarrier;
1909 info->nc_timer.data = (unsigned long) info;
1910 skb_queue_head_init(&info->xmit_queue);
1911#ifdef CONFIG_ISDN_AUDIO
1912 skb_queue_head_init(&info->dtmf_queue);
1913#endif
1914 if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
1915 printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
1916 retval = -ENOMEM;
1917 goto err_unregister;
1918 }
1919 /* Make room for T.70 header */
1920 info->xmit_buf += 4;
1921 }
1922 return 0;
1923err_unregister:
1924 for (i--; i >= 0; i--) {
1925 info = &m->info[i];
1926#ifdef CONFIG_ISDN_TTY_FAX
1927 kfree(info->fax);
1928#endif
1929 kfree(info->xmit_buf - 4);
1930 }
1931 tty_unregister_driver(m->tty_modem);
Joe Perches475be4d2012-02-19 19:52:38 -08001932err:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933 put_tty_driver(m->tty_modem);
1934 m->tty_modem = NULL;
1935 return retval;
1936}
1937
1938void
1939isdn_tty_exit(void)
1940{
1941 modem_info *info;
1942 int i;
1943
1944 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
1945 info = &dev->mdm.info[i];
1946 isdn_tty_cleanup_xmit(info);
1947#ifdef CONFIG_ISDN_TTY_FAX
1948 kfree(info->fax);
1949#endif
1950 kfree(info->xmit_buf - 4);
1951 }
1952 tty_unregister_driver(dev->mdm.tty_modem);
1953 put_tty_driver(dev->mdm.tty_modem);
1954 dev->mdm.tty_modem = NULL;
1955}
1956
1957
1958/*
1959 * isdn_tty_match_icall(char *MSN, atemu *tty_emulator, int dev_idx)
1960 * match the MSN against the MSNs (glob patterns) defined for tty_emulator,
1961 * and return 0 for match, 1 for no match, 2 if MSN could match if longer.
1962 */
1963
1964static int
1965isdn_tty_match_icall(char *cid, atemu *emu, int di)
1966{
1967#ifdef ISDN_DEBUG_MODEM_ICALL
1968 printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
1969 emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
1970 emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
1971#endif
1972 if (strlen(emu->lmsn)) {
1973 char *p = emu->lmsn;
1974 char *q;
1975 int tmp;
1976 int ret = 0;
1977
1978 while (1) {
1979 if ((q = strchr(p, ';')))
1980 *q = '\0';
1981 if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret)
1982 ret = tmp;
1983#ifdef ISDN_DEBUG_MODEM_ICALL
1984 printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
1985 p, isdn_map_eaz2msn(emu->msn, di), tmp);
1986#endif
1987 if (q) {
1988 *q = ';';
1989 p = q;
1990 p++;
1991 }
1992 if (!tmp)
1993 return 0;
1994 if (!q)
1995 break;
1996 }
1997 return ret;
1998 } else {
1999 int tmp;
2000 tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di));
2001#ifdef ISDN_DEBUG_MODEM_ICALL
Joe Perches475be4d2012-02-19 19:52:38 -08002002 printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
2003 isdn_map_eaz2msn(emu->msn, di), tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004#endif
2005 return tmp;
2006 }
2007}
2008
2009/*
2010 * An incoming call-request has arrived.
2011 * Search the tty-devices for an appropriate device and bind
2012 * it to the ISDN-Channel.
2013 * Return:
2014 *
2015 * 0 = No matching device found.
2016 * 1 = A matching device found.
2017 * 3 = No match found, but eventually would match, if
2018 * CID is longer.
2019 */
2020int
2021isdn_tty_find_icall(int di, int ch, setup_parm *setup)
2022{
2023 char *eaz;
2024 int i;
2025 int wret;
2026 int idx;
2027 int si1;
2028 int si2;
2029 char *nr;
2030 ulong flags;
2031
2032 if (!setup->phone[0]) {
2033 nr = "0";
2034 printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
2035 } else
2036 nr = setup->phone;
2037 si1 = (int) setup->si1;
2038 si2 = (int) setup->si2;
2039 if (!setup->eazmsn[0]) {
2040 printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
2041 eaz = "0";
2042 } else
2043 eaz = setup->eazmsn;
2044#ifdef ISDN_DEBUG_MODEM_ICALL
2045 printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
2046#endif
2047 wret = 0;
2048 spin_lock_irqsave(&dev->lock, flags);
2049 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
2050 modem_info *info = &dev->mdm.info[i];
2051
Joe Perches475be4d2012-02-19 19:52:38 -08002052 if (info->count == 0)
2053 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054 if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */
2055 (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */
2056 idx = isdn_dc2minor(di, ch);
2057#ifdef ISDN_DEBUG_MODEM_ICALL
2058 printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret);
2059 printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
2060 info->flags, info->isdn_driver, info->isdn_channel,
2061 dev->usage[idx]);
2062#endif
2063 if (
2064#ifndef FIX_FILE_TRANSFER
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002065 (info->flags & ASYNC_NORMAL_ACTIVE) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066#endif
2067 (info->isdn_driver == -1) &&
2068 (info->isdn_channel == -1) &&
2069 (USG_NONE(dev->usage[idx]))) {
2070 int matchret;
2071
2072 if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
2073 wret = matchret;
2074 if (!matchret) { /* EAZ is matching */
2075 info->isdn_driver = di;
2076 info->isdn_channel = ch;
2077 info->drv_index = idx;
2078 dev->m_idx[idx] = info->line;
2079 dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
Joe Perches475be4d2012-02-19 19:52:38 -08002080 dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081 strcpy(dev->num[idx], nr);
2082 strcpy(info->emu.cpn, eaz);
2083 info->emu.mdmreg[REG_SI1I] = si2bit[si1];
2084 info->emu.mdmreg[REG_PLAN] = setup->plan;
2085 info->emu.mdmreg[REG_SCREEN] = setup->screen;
2086 isdn_info_update();
2087 spin_unlock_irqrestore(&dev->lock, flags);
2088 printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
2089 info->line);
2090 info->msr |= UART_MSR_RI;
2091 isdn_tty_modem_result(RESULT_RING, info);
2092 isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
2093 return 1;
2094 }
2095 }
2096 }
2097 }
2098 spin_unlock_irqrestore(&dev->lock, flags);
2099 printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
Joe Perches475be4d2012-02-19 19:52:38 -08002100 ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2)) ? "rejected" : "ignored");
2101 return (wret == 2) ? 3 : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002102}
2103
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002104#define TTY_IS_ACTIVE(info) (info->flags & ASYNC_NORMAL_ACTIVE)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105
2106int
2107isdn_tty_stat_callback(int i, isdn_ctrl *c)
2108{
2109 int mi;
2110 modem_info *info;
2111 char *e;
2112
2113 if (i < 0)
2114 return 0;
2115 if ((mi = dev->m_idx[i]) >= 0) {
2116 info = &dev->mdm.info[mi];
2117 switch (c->command) {
Joe Perches475be4d2012-02-19 19:52:38 -08002118 case ISDN_STAT_CINF:
2119 printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
2120 info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
2121 if (e == (char *)c->parm.num)
2122 info->emu.charge = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002123
Joe Perches475be4d2012-02-19 19:52:38 -08002124 break;
2125 case ISDN_STAT_BSENT:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002126#ifdef ISDN_TTY_STAT_DEBUG
Joe Perches475be4d2012-02-19 19:52:38 -08002127 printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128#endif
Joe Perches475be4d2012-02-19 19:52:38 -08002129 if ((info->isdn_driver == c->driver) &&
2130 (info->isdn_channel == c->arg)) {
2131 info->msr |= UART_MSR_CTS;
2132 if (info->send_outstanding)
2133 if (!(--info->send_outstanding))
2134 info->lsr |= UART_LSR_TEMT;
2135 isdn_tty_tint(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002136 return 1;
Joe Perches475be4d2012-02-19 19:52:38 -08002137 }
2138 break;
2139 case ISDN_STAT_CAUSE:
2140#ifdef ISDN_TTY_STAT_DEBUG
2141 printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line);
2142#endif
2143 /* Signal cause to tty-device */
2144 strncpy(info->last_cause, c->parm.num, 5);
2145 return 1;
2146 case ISDN_STAT_DISPLAY:
2147#ifdef ISDN_TTY_STAT_DEBUG
2148 printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line);
2149#endif
2150 /* Signal display to tty-device */
2151 if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) &&
2152 !(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) {
2153 isdn_tty_at_cout("\r\n", info);
2154 isdn_tty_at_cout("DISPLAY: ", info);
2155 isdn_tty_at_cout(c->parm.display, info);
2156 isdn_tty_at_cout("\r\n", info);
2157 }
2158 return 1;
2159 case ISDN_STAT_DCONN:
2160#ifdef ISDN_TTY_STAT_DEBUG
2161 printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
2162#endif
2163 if (TTY_IS_ACTIVE(info)) {
2164 if (info->dialing == 1) {
2165 info->dialing = 2;
2166 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 }
Joe Perches475be4d2012-02-19 19:52:38 -08002168 }
2169 break;
2170 case ISDN_STAT_DHUP:
2171#ifdef ISDN_TTY_STAT_DEBUG
2172 printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
2173#endif
2174 if (TTY_IS_ACTIVE(info)) {
2175 if (info->dialing == 1)
2176 isdn_tty_modem_result(RESULT_BUSY, info);
2177 if (info->dialing > 1)
2178 isdn_tty_modem_result(RESULT_NO_CARRIER, info);
2179 info->dialing = 0;
2180#ifdef ISDN_DEBUG_MODEM_HUP
2181 printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
2182#endif
2183 isdn_tty_modem_hup(info, 0);
2184 return 1;
2185 }
2186 break;
2187 case ISDN_STAT_BCONN:
2188#ifdef ISDN_TTY_STAT_DEBUG
2189 printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
2190#endif
2191 /* Wake up any processes waiting
2192 * for incoming call of this device when
2193 * DCD follow the state of incoming carrier
2194 */
2195 if (info->blocked_open &&
2196 (info->emu.mdmreg[REG_DCD] & BIT_DCD)) {
2197 wake_up_interruptible(&info->open_wait);
2198 }
2199
2200 /* Schedule CONNECT-Message to any tty
2201 * waiting for it and
2202 * set DCD-bit of its modem-status.
2203 */
2204 if (TTY_IS_ACTIVE(info) ||
2205 (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) {
2206 info->msr |= UART_MSR_DCD;
2207 info->emu.charge = 0;
2208 if (info->dialing & 0xf)
2209 info->last_dir = 1;
2210 else
2211 info->last_dir = 0;
2212 info->dialing = 0;
2213 info->rcvsched = 1;
2214 if (USG_MODEM(dev->usage[i])) {
2215 if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
2216 strcpy(info->emu.connmsg, c->parm.num);
2217 isdn_tty_modem_result(RESULT_CONNECT, info);
2218 } else
2219 isdn_tty_modem_result(RESULT_CONNECT64000, info);
2220 }
2221 if (USG_VOICE(dev->usage[i]))
2222 isdn_tty_modem_result(RESULT_VCON, info);
2223 return 1;
2224 }
2225 break;
2226 case ISDN_STAT_BHUP:
2227#ifdef ISDN_TTY_STAT_DEBUG
2228 printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line);
2229#endif
2230 if (TTY_IS_ACTIVE(info)) {
2231#ifdef ISDN_DEBUG_MODEM_HUP
2232 printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
2233#endif
2234 isdn_tty_modem_hup(info, 0);
2235 return 1;
2236 }
2237 break;
2238 case ISDN_STAT_NODCH:
2239#ifdef ISDN_TTY_STAT_DEBUG
2240 printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line);
2241#endif
2242 if (TTY_IS_ACTIVE(info)) {
2243 if (info->dialing) {
2244 info->dialing = 0;
2245 info->last_l2 = -1;
2246 info->last_si = 0;
2247 sprintf(info->last_cause, "0000");
2248 isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
2249 }
2250 isdn_tty_modem_hup(info, 0);
2251 return 1;
2252 }
2253 break;
2254 case ISDN_STAT_UNLOAD:
2255#ifdef ISDN_TTY_STAT_DEBUG
2256 printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line);
2257#endif
2258 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
2259 info = &dev->mdm.info[i];
2260 if (info->isdn_driver == c->driver) {
2261 if (info->online)
2262 isdn_tty_modem_hup(info, 1);
2263 }
2264 }
2265 return 1;
2266#ifdef CONFIG_ISDN_TTY_FAX
2267 case ISDN_STAT_FAXIND:
2268 if (TTY_IS_ACTIVE(info)) {
2269 isdn_tty_fax_command(info, c);
2270 }
2271 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272#endif
2273#ifdef CONFIG_ISDN_AUDIO
Joe Perches475be4d2012-02-19 19:52:38 -08002274 case ISDN_STAT_AUDIO:
2275 if (TTY_IS_ACTIVE(info)) {
2276 switch (c->parm.num[0]) {
2277 case ISDN_AUDIO_DTMF:
2278 if (info->vonline) {
2279 isdn_audio_put_dle_code(info,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 c->parm.num[1]);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 }
Joe Perches475be4d2012-02-19 19:52:38 -08002282 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 }
Joe Perches475be4d2012-02-19 19:52:38 -08002284 }
2285 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286#endif
2287 }
2288 }
2289 return 0;
2290}
2291
2292/*********************************************************************
2293 Modem-Emulator-Routines
Joe Perches475be4d2012-02-19 19:52:38 -08002294*********************************************************************/
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295
Joe Perches475be4d2012-02-19 19:52:38 -08002296#define cmdchar(c) ((c >= ' ') && (c <= 0x7f))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297
2298/*
2299 * Put a message from the AT-emulator into receive-buffer of tty,
2300 * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
2301 */
2302void
Joe Perches475be4d2012-02-19 19:52:38 -08002303isdn_tty_at_cout(char *msg, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002304{
2305 struct tty_struct *tty;
2306 atemu *m = &info->emu;
2307 char *p;
2308 char c;
2309 u_long flags;
2310 struct sk_buff *skb = NULL;
2311 char *sp = NULL;
Adrian Bunk1f4d4a82006-03-25 03:08:06 -08002312 int l;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313
2314 if (!msg) {
2315 printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
2316 return;
2317 }
Adrian Bunk1f4d4a82006-03-25 03:08:06 -08002318
2319 l = strlen(msg);
2320
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 spin_lock_irqsave(&info->readlock, flags);
2322 tty = info->tty;
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002323 if ((info->flags & ASYNC_CLOSING) || (!tty)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002324 spin_unlock_irqrestore(&info->readlock, flags);
2325 return;
2326 }
2327
Alan Cox33f0f882006-01-09 20:54:13 -08002328 /* use queue instead of direct, if online and */
2329 /* data is in queue or buffer is full */
Karsten Keil61b9a262006-02-14 13:53:06 -08002330 if (info->online && ((tty_buffer_request_room(tty, l) < l) ||
Joe Perches475be4d2012-02-19 19:52:38 -08002331 !skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel]))) {
Alan Cox33f0f882006-01-09 20:54:13 -08002332 skb = alloc_skb(l, GFP_ATOMIC);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 if (!skb) {
2334 spin_unlock_irqrestore(&info->readlock, flags);
2335 return;
2336 }
Alan Cox33f0f882006-01-09 20:54:13 -08002337 sp = skb_put(skb, l);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338#ifdef CONFIG_ISDN_AUDIO
2339 ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
2340 ISDN_AUDIO_SKB_LOCK(skb) = 0;
2341#endif
2342 }
2343
2344 for (p = msg; *p; p++) {
2345 switch (*p) {
Joe Perches475be4d2012-02-19 19:52:38 -08002346 case '\r':
2347 c = m->mdmreg[REG_CR];
2348 break;
2349 case '\n':
2350 c = m->mdmreg[REG_LF];
2351 break;
2352 case '\b':
2353 c = m->mdmreg[REG_BS];
2354 break;
2355 default:
2356 c = *p;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357 }
2358 if (skb) {
2359 *sp++ = c;
2360 } else {
Joe Perches475be4d2012-02-19 19:52:38 -08002361 if (tty_insert_flip_char(tty, c, TTY_NORMAL) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363 }
2364 }
2365 if (skb) {
2366 __skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb);
2367 dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len;
2368 spin_unlock_irqrestore(&info->readlock, flags);
2369 /* Schedule dequeuing */
Alan Cox33f0f882006-01-09 20:54:13 -08002370 if (dev->modempoll && info->rcvsched)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371 isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
2372
2373 } else {
2374 spin_unlock_irqrestore(&info->readlock, flags);
Alan Cox33f0f882006-01-09 20:54:13 -08002375 tty_flip_buffer_push(tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 }
2377}
2378
2379/*
2380 * Perform ATH Hangup
2381 */
2382static void
Joe Perches475be4d2012-02-19 19:52:38 -08002383isdn_tty_on_hook(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384{
2385 if (info->isdn_channel >= 0) {
2386#ifdef ISDN_DEBUG_MODEM_HUP
2387 printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
2388#endif
2389 isdn_tty_modem_hup(info, 1);
2390 }
2391}
2392
2393static void
2394isdn_tty_off_hook(void)
2395{
2396 printk(KERN_DEBUG "isdn_tty_off_hook\n");
2397}
2398
Joe Perches475be4d2012-02-19 19:52:38 -08002399#define PLUSWAIT1 (HZ / 2) /* 0.5 sec. */
2400#define PLUSWAIT2 (HZ * 3 / 2) /* 1.5 sec */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401
2402/*
2403 * Check Buffer for Modem-escape-sequence, activate timer-callback to
2404 * isdn_tty_modem_escape() if sequence found.
2405 *
2406 * Parameters:
2407 * p pointer to databuffer
2408 * plus escape-character
2409 * count length of buffer
2410 * pluscount count of valid escape-characters so far
2411 * lastplus timestamp of last character
2412 */
2413static void
Joe Perches475be4d2012-02-19 19:52:38 -08002414isdn_tty_check_esc(const u_char *p, u_char plus, int count, int *pluscount,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002415 u_long *lastplus)
2416{
2417 if (plus > 127)
2418 return;
2419 if (count > 3) {
2420 p += count - 3;
2421 count = 3;
2422 *pluscount = 0;
2423 }
2424 while (count > 0) {
2425 if (*(p++) == plus) {
2426 if ((*pluscount)++) {
2427 /* Time since last '+' > 0.5 sec. ? */
2428 if (time_after(jiffies, *lastplus + PLUSWAIT1))
2429 *pluscount = 1;
2430 } else {
2431 /* Time since last non-'+' < 1.5 sec. ? */
2432 if (time_before(jiffies, *lastplus + PLUSWAIT2))
2433 *pluscount = 0;
2434 }
2435 if ((*pluscount == 3) && (count == 1))
2436 isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
2437 if (*pluscount > 3)
2438 *pluscount = 1;
2439 } else
2440 *pluscount = 0;
2441 *lastplus = jiffies;
2442 count--;
2443 }
2444}
2445
2446/*
2447 * Return result of AT-emulator to tty-receive-buffer, depending on
2448 * modem-register 12, bit 0 and 1.
2449 * For CONNECT-messages also switch to online-mode.
2450 * For RING-message handle auto-ATA if register 0 != 0
2451 */
2452
2453static void
Joe Perches475be4d2012-02-19 19:52:38 -08002454isdn_tty_modem_result(int code, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002455{
2456 atemu *m = &info->emu;
2457 static char *msg[] =
Joe Perches475be4d2012-02-19 19:52:38 -08002458 {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
2459 "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
2460 "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
2461 char s[ISDN_MSNLEN + 10];
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462
2463 switch (code) {
Joe Perches475be4d2012-02-19 19:52:38 -08002464 case RESULT_RING:
2465 m->mdmreg[REG_RINGCNT]++;
2466 if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA])
2467 /* Automatically accept incoming call */
2468 isdn_tty_cmd_ATA(info);
2469 break;
2470 case RESULT_NO_CARRIER:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002471#ifdef ISDN_DEBUG_MODEM_HUP
Joe Perches475be4d2012-02-19 19:52:38 -08002472 printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002473 (info->flags & ASYNC_CLOSING),
Joe Perches475be4d2012-02-19 19:52:38 -08002474 (!info->tty));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475#endif
Joe Perches475be4d2012-02-19 19:52:38 -08002476 m->mdmreg[REG_RINGCNT] = 0;
2477 del_timer(&info->nc_timer);
2478 info->ncarrier = 0;
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002479 if ((info->flags & ASYNC_CLOSING) || (!info->tty))
Joe Perches475be4d2012-02-19 19:52:38 -08002480 return;
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002481
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482#ifdef CONFIG_ISDN_AUDIO
Joe Perches475be4d2012-02-19 19:52:38 -08002483 if (info->vonline & 1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002484#ifdef ISDN_DEBUG_MODEM_VOICE
Joe Perches475be4d2012-02-19 19:52:38 -08002485 printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002486 info->line);
2487#endif
Joe Perches475be4d2012-02-19 19:52:38 -08002488 /* voice-recording, add DLE-ETX */
2489 isdn_tty_at_cout("\020\003", info);
2490 }
2491 if (info->vonline & 2) {
2492#ifdef ISDN_DEBUG_MODEM_VOICE
2493 printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n",
2494 info->line);
2495#endif
2496 /* voice-playing, add DLE-DC4 */
2497 isdn_tty_at_cout("\020\024", info);
2498 }
2499#endif
2500 break;
2501 case RESULT_CONNECT:
2502 case RESULT_CONNECT64000:
2503 sprintf(info->last_cause, "0000");
2504 if (!info->online)
2505 info->online = 2;
2506 break;
2507 case RESULT_VCON:
2508#ifdef ISDN_DEBUG_MODEM_VOICE
2509 printk(KERN_DEBUG "res3: send VCON on ttyI%d\n",
2510 info->line);
2511#endif
2512 sprintf(info->last_cause, "0000");
2513 if (!info->online)
2514 info->online = 1;
2515 break;
2516 } /* switch (code) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002517
2518 if (m->mdmreg[REG_RESP] & BIT_RESP) {
2519 /* Show results */
2520 if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) {
2521 /* Show numeric results only */
2522 sprintf(s, "\r\n%d\r\n", code);
2523 isdn_tty_at_cout(s, info);
2524 } else {
2525 if (code == RESULT_RING) {
Joe Perches475be4d2012-02-19 19:52:38 -08002526 /* return if "show RUNG" and ringcounter>1 */
2527 if ((m->mdmreg[REG_RUNG] & BIT_RUNG) &&
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528 (m->mdmreg[REG_RINGCNT] > 1))
Joe Perches475be4d2012-02-19 19:52:38 -08002529 return;
2530 /* print CID, _before_ _every_ ring */
2531 if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) {
2532 isdn_tty_at_cout("\r\nCALLER NUMBER: ", info);
2533 isdn_tty_at_cout(dev->num[info->drv_index], info);
2534 if (m->mdmreg[REG_CDN] & BIT_CDN) {
2535 isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
2536 isdn_tty_at_cout(info->emu.cpn, info);
2537 }
2538 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539 }
2540 isdn_tty_at_cout("\r\n", info);
2541 isdn_tty_at_cout(msg[code], info);
2542 switch (code) {
Joe Perches475be4d2012-02-19 19:52:38 -08002543 case RESULT_CONNECT:
2544 switch (m->mdmreg[REG_L2PROT]) {
2545 case ISDN_PROTO_L2_MODEM:
2546 isdn_tty_at_cout(" ", info);
2547 isdn_tty_at_cout(m->connmsg, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002549 }
2550 break;
2551 case RESULT_RING:
2552 /* Append CPN, if enabled */
2553 if ((m->mdmreg[REG_CPN] & BIT_CPN)) {
2554 sprintf(s, "/%s", m->cpn);
2555 isdn_tty_at_cout(s, info);
2556 }
2557 /* Print CID only once, _after_ 1st RING */
2558 if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
2559 (m->mdmreg[REG_RINGCNT] == 1)) {
2560 isdn_tty_at_cout("\r\n", info);
2561 isdn_tty_at_cout("CALLER NUMBER: ", info);
2562 isdn_tty_at_cout(dev->num[info->drv_index], info);
2563 if (m->mdmreg[REG_CDN] & BIT_CDN) {
2564 isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
2565 isdn_tty_at_cout(info->emu.cpn, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 }
Joe Perches475be4d2012-02-19 19:52:38 -08002567 }
2568 break;
2569 case RESULT_NO_CARRIER:
2570 case RESULT_NO_DIALTONE:
2571 case RESULT_BUSY:
2572 case RESULT_NO_ANSWER:
2573 m->mdmreg[REG_RINGCNT] = 0;
2574 /* Append Cause-Message if enabled */
2575 if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) {
2576 sprintf(s, "/%s", info->last_cause);
2577 isdn_tty_at_cout(s, info);
2578 }
2579 break;
2580 case RESULT_CONNECT64000:
2581 /* Append Protocol to CONNECT message */
2582 switch (m->mdmreg[REG_L2PROT]) {
2583 case ISDN_PROTO_L2_X75I:
2584 case ISDN_PROTO_L2_X75UI:
2585 case ISDN_PROTO_L2_X75BUI:
2586 isdn_tty_at_cout("/X.75", info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002588 case ISDN_PROTO_L2_HDLC:
2589 isdn_tty_at_cout("/HDLC", info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002590 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002591 case ISDN_PROTO_L2_V11096:
2592 isdn_tty_at_cout("/V110/9600", info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002594 case ISDN_PROTO_L2_V11019:
2595 isdn_tty_at_cout("/V110/19200", info);
2596 break;
2597 case ISDN_PROTO_L2_V11038:
2598 isdn_tty_at_cout("/V110/38400", info);
2599 break;
2600 }
2601 if (m->mdmreg[REG_T70] & BIT_T70) {
2602 isdn_tty_at_cout("/T.70", info);
2603 if (m->mdmreg[REG_T70] & BIT_T70_EXT)
2604 isdn_tty_at_cout("+", info);
2605 }
2606 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002607 }
2608 isdn_tty_at_cout("\r\n", info);
2609 }
2610 }
2611 if (code == RESULT_NO_CARRIER) {
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002612 if ((info->flags & ASYNC_CLOSING) || (!info->tty))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 return;
Jiri Slaby6776a2f2012-04-02 13:53:50 +02002614
2615 if (info->flags & ASYNC_CHECK_CD)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616 tty_hangup(info->tty);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617 }
2618}
2619
2620
2621/*
2622 * Display a modem-register-value.
2623 */
2624static void
Joe Perches475be4d2012-02-19 19:52:38 -08002625isdn_tty_show_profile(int ridx, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002626{
2627 char v[6];
2628
2629 sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
2630 isdn_tty_at_cout(v, info);
2631}
2632
2633/*
2634 * Get MSN-string from char-pointer, set pointer to end of number
2635 */
2636static void
2637isdn_tty_get_msnstr(char *n, char **p)
2638{
2639 int limit = ISDN_MSNLEN - 1;
2640
2641 while (((*p[0] >= '0' && *p[0] <= '9') ||
2642 /* Why a comma ??? */
2643 (*p[0] == ',') || (*p[0] == ':')) &&
Joe Perches475be4d2012-02-19 19:52:38 -08002644 (limit--))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645 *n++ = *p[0]++;
2646 *n = '\0';
2647}
2648
2649/*
2650 * Get phone-number from modem-commandbuffer
2651 */
2652static void
Joe Perches475be4d2012-02-19 19:52:38 -08002653isdn_tty_getdial(char *p, char *q, int cnt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654{
2655 int first = 1;
2656 int limit = ISDN_MSNLEN - 1; /* MUST match the size of interface var to avoid
Joe Perches475be4d2012-02-19 19:52:38 -08002657 buffer overflow */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658
Joe Perches475be4d2012-02-19 19:52:38 -08002659 while (strchr(" 0123456789,#.*WPTSR-", *p) && *p && --cnt > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660 if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) ||
Karsten Keil924ad152007-06-01 00:46:55 -07002661 ((*p == 'R') && first) ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662 (*p == '*') || (*p == '#')) {
2663 *q++ = *p;
2664 limit--;
2665 }
Joe Perches475be4d2012-02-19 19:52:38 -08002666 if (!limit)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002667 break;
2668 p++;
2669 first = 0;
2670 }
2671 *q = 0;
2672}
2673
2674#define PARSE_ERROR { isdn_tty_modem_result(RESULT_ERROR, info); return; }
2675#define PARSE_ERROR1 { isdn_tty_modem_result(RESULT_ERROR, info); return 1; }
2676
2677static void
Joe Perches475be4d2012-02-19 19:52:38 -08002678isdn_tty_report(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679{
2680 atemu *m = &info->emu;
2681 char s[80];
2682
2683 isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
2684 sprintf(s, " Remote Number: %s\r\n", info->last_num);
2685 isdn_tty_at_cout(s, info);
2686 sprintf(s, " Direction: %s\r\n", info->last_dir ? "outgoing" : "incoming");
2687 isdn_tty_at_cout(s, info);
2688 isdn_tty_at_cout(" Layer-2 Protocol: ", info);
2689 switch (info->last_l2) {
Joe Perches475be4d2012-02-19 19:52:38 -08002690 case ISDN_PROTO_L2_X75I:
2691 isdn_tty_at_cout("X.75i", info);
2692 break;
2693 case ISDN_PROTO_L2_X75UI:
2694 isdn_tty_at_cout("X.75ui", info);
2695 break;
2696 case ISDN_PROTO_L2_X75BUI:
2697 isdn_tty_at_cout("X.75bui", info);
2698 break;
2699 case ISDN_PROTO_L2_HDLC:
2700 isdn_tty_at_cout("HDLC", info);
2701 break;
2702 case ISDN_PROTO_L2_V11096:
2703 isdn_tty_at_cout("V.110 9600 Baud", info);
2704 break;
2705 case ISDN_PROTO_L2_V11019:
2706 isdn_tty_at_cout("V.110 19200 Baud", info);
2707 break;
2708 case ISDN_PROTO_L2_V11038:
2709 isdn_tty_at_cout("V.110 38400 Baud", info);
2710 break;
2711 case ISDN_PROTO_L2_TRANS:
2712 isdn_tty_at_cout("transparent", info);
2713 break;
2714 case ISDN_PROTO_L2_MODEM:
2715 isdn_tty_at_cout("modem", info);
2716 break;
2717 case ISDN_PROTO_L2_FAX:
2718 isdn_tty_at_cout("fax", info);
2719 break;
2720 default:
2721 isdn_tty_at_cout("unknown", info);
2722 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723 }
2724 if (m->mdmreg[REG_T70] & BIT_T70) {
2725 isdn_tty_at_cout("/T.70", info);
2726 if (m->mdmreg[REG_T70] & BIT_T70_EXT)
2727 isdn_tty_at_cout("+", info);
2728 }
2729 isdn_tty_at_cout("\r\n", info);
2730 isdn_tty_at_cout(" Service: ", info);
2731 switch (info->last_si) {
Joe Perches475be4d2012-02-19 19:52:38 -08002732 case 1:
2733 isdn_tty_at_cout("audio\r\n", info);
2734 break;
2735 case 5:
2736 isdn_tty_at_cout("btx\r\n", info);
2737 break;
2738 case 7:
2739 isdn_tty_at_cout("data\r\n", info);
2740 break;
2741 default:
2742 sprintf(s, "%d\r\n", info->last_si);
2743 isdn_tty_at_cout(s, info);
2744 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 }
2746 sprintf(s, " Hangup location: %s\r\n", info->last_lhup ? "local" : "remote");
2747 isdn_tty_at_cout(s, info);
2748 sprintf(s, " Last cause: %s\r\n", info->last_cause);
2749 isdn_tty_at_cout(s, info);
2750}
2751
2752/*
2753 * Parse AT&.. commands.
2754 */
2755static int
Joe Perches475be4d2012-02-19 19:52:38 -08002756isdn_tty_cmd_ATand(char **p, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002757{
2758 atemu *m = &info->emu;
2759 int i;
2760 char rb[100];
2761
2762#define MAXRB (sizeof(rb) - 1)
2763
2764 switch (*p[0]) {
Joe Perches475be4d2012-02-19 19:52:38 -08002765 case 'B':
2766 /* &B - Set Buffersize */
2767 p[0]++;
2768 i = isdn_getnum(p);
2769 if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
2770 PARSE_ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771#ifdef CONFIG_ISDN_AUDIO
Joe Perches475be4d2012-02-19 19:52:38 -08002772 if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF))
2773 PARSE_ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774#endif
Joe Perches475be4d2012-02-19 19:52:38 -08002775 m->mdmreg[REG_PSIZE] = i / 16;
2776 info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
2777 switch (m->mdmreg[REG_L2PROT]) {
2778 case ISDN_PROTO_L2_V11096:
2779 case ISDN_PROTO_L2_V11019:
2780 case ISDN_PROTO_L2_V11038:
2781 info->xmit_size /= 10;
2782 }
2783 break;
2784 case 'C':
2785 /* &C - DCD Status */
2786 p[0]++;
2787 switch (isdn_getnum(p)) {
2788 case 0:
2789 m->mdmreg[REG_DCD] &= ~BIT_DCD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002791 case 1:
2792 m->mdmreg[REG_DCD] |= BIT_DCD;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002794 default:
2795 PARSE_ERROR1
2796 }
2797 break;
2798 case 'D':
2799 /* &D - Set DTR-Low-behavior */
2800 p[0]++;
2801 switch (isdn_getnum(p)) {
2802 case 0:
2803 m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP;
2804 m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002806 case 2:
2807 m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
2808 m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002810 case 3:
2811 m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
2812 m->mdmreg[REG_DTRR] |= BIT_DTRR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002814 default:
2815 PARSE_ERROR1
2816 }
2817 break;
2818 case 'E':
2819 /* &E -Set EAZ/MSN */
2820 p[0]++;
2821 isdn_tty_get_msnstr(m->msn, p);
2822 break;
2823 case 'F':
2824 /* &F -Set Factory-Defaults */
2825 p[0]++;
2826 if (info->msr & UART_MSR_DCD)
2827 PARSE_ERROR1;
2828 isdn_tty_reset_profile(m);
2829 isdn_tty_modem_reset_regs(info, 1);
2830 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831#ifdef DUMMY_HAYES_AT
Joe Perches475be4d2012-02-19 19:52:38 -08002832 case 'K':
2833 /* only for be compilant with common scripts */
2834 /* &K Flowcontrol - no function */
2835 p[0]++;
2836 isdn_getnum(p);
2837 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838#endif
Joe Perches475be4d2012-02-19 19:52:38 -08002839 case 'L':
2840 /* &L -Set Numbers to listen on */
2841 p[0]++;
2842 i = 0;
2843 while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) &&
2844 (i < ISDN_LMSNLEN - 1))
2845 m->lmsn[i++] = *p[0]++;
2846 m->lmsn[i] = '\0';
2847 break;
2848 case 'R':
2849 /* &R - Set V.110 bitrate adaption */
2850 p[0]++;
2851 i = isdn_getnum(p);
2852 switch (i) {
2853 case 0:
2854 /* Switch off V.110, back to X.75 */
2855 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
2856 m->mdmreg[REG_SI2] = 0;
2857 info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002859 case 9600:
2860 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096;
2861 m->mdmreg[REG_SI2] = 197;
2862 info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002864 case 19200:
2865 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019;
2866 m->mdmreg[REG_SI2] = 199;
2867 info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002868 break;
Joe Perches475be4d2012-02-19 19:52:38 -08002869 case 38400:
2870 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038;
2871 m->mdmreg[REG_SI2] = 198; /* no existing standard for this */
2872 info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002873 break;
2874 default:
2875 PARSE_ERROR1;
Joe Perches475be4d2012-02-19 19:52:38 -08002876 }
2877 /* Switch off T.70 */
2878 m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
2879 /* Set Service 7 */
2880 m->mdmreg[REG_SI1] |= 4;
2881 break;
2882 case 'S':
2883 /* &S - Set Windowsize */
2884 p[0]++;
2885 i = isdn_getnum(p);
2886 if ((i > 0) && (i < 9))
2887 m->mdmreg[REG_WSIZE] = i;
2888 else
2889 PARSE_ERROR1;
2890 break;
2891 case 'V':
2892 /* &V - Show registers */
2893 p[0]++;
2894 isdn_tty_at_cout("\r\n", info);
2895 for (i = 0; i < ISDN_MODEM_NUMREG; i++) {
2896 sprintf(rb, "S%02d=%03d%s", i,
2897 m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
2898 isdn_tty_at_cout(rb, info);
2899 }
2900 sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
2901 strlen(m->msn) ? m->msn : "None");
2902 isdn_tty_at_cout(rb, info);
2903 if (strlen(m->lmsn)) {
2904 isdn_tty_at_cout("\r\nListen: ", info);
2905 isdn_tty_at_cout(m->lmsn, info);
2906 isdn_tty_at_cout("\r\n", info);
2907 }
2908 break;
2909 case 'W':
2910 /* &W - Write Profile */
2911 p[0]++;
2912 switch (*p[0]) {
2913 case '0':
2914 p[0]++;
2915 modem_write_profile(m);
2916 break;
2917 default:
2918 PARSE_ERROR1;
2919 }
2920 break;
2921 case 'X':
2922 /* &X - Switch to BTX-Mode and T.70 */
2923 p[0]++;
2924 switch (isdn_getnum(p)) {
2925 case 0:
2926 m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
2927 info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
2928 break;
2929 case 1:
2930 m->mdmreg[REG_T70] |= BIT_T70;
2931 m->mdmreg[REG_T70] &= ~BIT_T70_EXT;
2932 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
2933 info->xmit_size = 112;
2934 m->mdmreg[REG_SI1] = 4;
2935 m->mdmreg[REG_SI2] = 0;
2936 break;
2937 case 2:
2938 m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT);
2939 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
2940 info->xmit_size = 112;
2941 m->mdmreg[REG_SI1] = 4;
2942 m->mdmreg[REG_SI2] = 0;
2943 break;
2944 default:
2945 PARSE_ERROR1;
2946 }
2947 break;
2948 default:
2949 PARSE_ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002950 }
2951 return 0;
2952}
2953
2954static int
Joe Perches475be4d2012-02-19 19:52:38 -08002955isdn_tty_check_ats(int mreg, int mval, modem_info *info, atemu *m)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956{
2957 /* Some plausibility checks */
2958 switch (mreg) {
Joe Perches475be4d2012-02-19 19:52:38 -08002959 case REG_L2PROT:
2960 if (mval > ISDN_PROTO_L2_MAX)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 return 1;
Joe Perches475be4d2012-02-19 19:52:38 -08002962 break;
2963 case REG_PSIZE:
2964 if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
2965 return 1;
2966#ifdef CONFIG_ISDN_AUDIO
2967 if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX))
2968 return 1;
2969#endif
2970 info->xmit_size = mval * 16;
2971 switch (m->mdmreg[REG_L2PROT]) {
2972 case ISDN_PROTO_L2_V11096:
2973 case ISDN_PROTO_L2_V11019:
2974 case ISDN_PROTO_L2_V11038:
2975 info->xmit_size /= 10;
2976 }
2977 break;
2978 case REG_SI1I:
2979 case REG_PLAN:
2980 case REG_SCREEN:
2981 /* readonly registers */
2982 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983 }
2984 return 0;
2985}
2986
2987/*
2988 * Perform ATS command
2989 */
2990static int
Joe Perches475be4d2012-02-19 19:52:38 -08002991isdn_tty_cmd_ATS(char **p, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002992{
2993 atemu *m = &info->emu;
2994 int bitpos;
2995 int mreg;
2996 int mval;
2997 int bval;
2998
2999 mreg = isdn_getnum(p);
3000 if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG)
3001 PARSE_ERROR1;
3002 switch (*p[0]) {
Joe Perches475be4d2012-02-19 19:52:38 -08003003 case '=':
3004 p[0]++;
3005 mval = isdn_getnum(p);
3006 if (mval < 0 || mval > 255)
3007 PARSE_ERROR1;
3008 if (isdn_tty_check_ats(mreg, mval, info, m))
3009 PARSE_ERROR1;
3010 m->mdmreg[mreg] = mval;
3011 break;
3012 case '.':
3013 /* Set/Clear a single bit */
3014 p[0]++;
3015 bitpos = isdn_getnum(p);
3016 if ((bitpos < 0) || (bitpos > 7))
3017 PARSE_ERROR1;
3018 switch (*p[0]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019 case '=':
3020 p[0]++;
Joe Perches475be4d2012-02-19 19:52:38 -08003021 bval = isdn_getnum(p);
3022 if (bval < 0 || bval > 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003023 PARSE_ERROR1;
Joe Perches475be4d2012-02-19 19:52:38 -08003024 if (bval)
3025 mval = m->mdmreg[mreg] | (1 << bitpos);
3026 else
3027 mval = m->mdmreg[mreg] & ~(1 << bitpos);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003028 if (isdn_tty_check_ats(mreg, mval, info, m))
3029 PARSE_ERROR1;
3030 m->mdmreg[mreg] = mval;
3031 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003032 case '?':
3033 p[0]++;
Joe Perches475be4d2012-02-19 19:52:38 -08003034 isdn_tty_at_cout("\r\n", info);
3035 isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0",
3036 info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003037 break;
3038 default:
3039 PARSE_ERROR1;
Joe Perches475be4d2012-02-19 19:52:38 -08003040 }
3041 break;
3042 case '?':
3043 p[0]++;
3044 isdn_tty_show_profile(mreg, info);
3045 break;
3046 default:
3047 PARSE_ERROR1;
3048 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049 }
3050 return 0;
3051}
3052
3053/*
3054 * Perform ATA command
3055 */
3056static void
Joe Perches475be4d2012-02-19 19:52:38 -08003057isdn_tty_cmd_ATA(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003058{
3059 atemu *m = &info->emu;
3060 isdn_ctrl cmd;
3061 int l2;
3062
3063 if (info->msr & UART_MSR_RI) {
3064 /* Accept incoming call */
3065 info->last_dir = 0;
3066 strcpy(info->last_num, dev->num[info->drv_index]);
3067 m->mdmreg[REG_RINGCNT] = 0;
3068 info->msr &= ~UART_MSR_RI;
3069 l2 = m->mdmreg[REG_L2PROT];
3070#ifdef CONFIG_ISDN_AUDIO
3071 /* If more than one bit set in reg18, autoselect Layer2 */
3072 if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
3073 if (m->mdmreg[REG_SI1I] == 1) {
3074 if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX))
3075 l2 = ISDN_PROTO_L2_TRANS;
3076 } else
3077 l2 = ISDN_PROTO_L2_X75I;
3078 }
3079#endif
3080 cmd.driver = info->isdn_driver;
3081 cmd.command = ISDN_CMD_SETL2;
3082 cmd.arg = info->isdn_channel + (l2 << 8);
3083 info->last_l2 = l2;
3084 isdn_command(&cmd);
3085 cmd.driver = info->isdn_driver;
3086 cmd.command = ISDN_CMD_SETL3;
3087 cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
3088#ifdef CONFIG_ISDN_TTY_FAX
3089 if (l2 == ISDN_PROTO_L2_FAX) {
3090 cmd.parm.fax = info->fax;
3091 info->fax->direction = ISDN_TTY_FAX_CONN_IN;
3092 }
3093#endif
3094 isdn_command(&cmd);
3095 cmd.driver = info->isdn_driver;
3096 cmd.arg = info->isdn_channel;
3097 cmd.command = ISDN_CMD_ACCEPTD;
3098 info->dialing = 16;
3099 info->emu.carrierwait = 0;
3100 isdn_command(&cmd);
3101 isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
3102 } else
3103 isdn_tty_modem_result(RESULT_NO_ANSWER, info);
3104}
3105
3106#ifdef CONFIG_ISDN_AUDIO
3107/*
3108 * Parse AT+F.. commands
3109 */
3110static int
Joe Perches475be4d2012-02-19 19:52:38 -08003111isdn_tty_cmd_PLUSF(char **p, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003112{
3113 atemu *m = &info->emu;
3114 char rs[20];
3115
3116 if (!strncmp(p[0], "CLASS", 5)) {
3117 p[0] += 5;
3118 switch (*p[0]) {
Joe Perches475be4d2012-02-19 19:52:38 -08003119 case '?':
3120 p[0]++;
3121 sprintf(rs, "\r\n%d",
3122 (m->mdmreg[REG_SI1] & 1) ? 8 : 0);
3123#ifdef CONFIG_ISDN_TTY_FAX
3124 if (TTY_IS_FCLASS2(info))
3125 sprintf(rs, "\r\n2");
3126 else if (TTY_IS_FCLASS1(info))
3127 sprintf(rs, "\r\n1");
3128#endif
3129 isdn_tty_at_cout(rs, info);
3130 break;
3131 case '=':
3132 p[0]++;
3133 switch (*p[0]) {
3134 case '0':
3135 p[0]++;
3136 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
3137 m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
3138 m->mdmreg[REG_SI1] = 4;
3139 info->xmit_size =
3140 m->mdmreg[REG_PSIZE] * 16;
3141 break;
3142#ifdef CONFIG_ISDN_TTY_FAX
3143 case '1':
3144 p[0]++;
3145 if (!(dev->global_features &
3146 ISDN_FEATURE_L3_FCLASS1))
3147 PARSE_ERROR1;
3148 m->mdmreg[REG_SI1] = 1;
3149 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
3150 m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1;
3151 info->xmit_size =
3152 m->mdmreg[REG_PSIZE] * 16;
3153 break;
3154 case '2':
3155 p[0]++;
3156 if (!(dev->global_features &
3157 ISDN_FEATURE_L3_FCLASS2))
3158 PARSE_ERROR1;
3159 m->mdmreg[REG_SI1] = 1;
3160 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
3161 m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2;
3162 info->xmit_size =
3163 m->mdmreg[REG_PSIZE] * 16;
3164 break;
3165#endif
3166 case '8':
3167 p[0]++;
3168 /* L2 will change on dialout with si=1 */
3169 m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
3170 m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
3171 m->mdmreg[REG_SI1] = 5;
3172 info->xmit_size = VBUF;
3173 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003174 case '?':
3175 p[0]++;
Joe Perches475be4d2012-02-19 19:52:38 -08003176 strcpy(rs, "\r\n0,");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177#ifdef CONFIG_ISDN_TTY_FAX
Joe Perches475be4d2012-02-19 19:52:38 -08003178 if (dev->global_features &
3179 ISDN_FEATURE_L3_FCLASS1)
3180 strcat(rs, "1,");
3181 if (dev->global_features &
3182 ISDN_FEATURE_L3_FCLASS2)
3183 strcat(rs, "2,");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003184#endif
Joe Perches475be4d2012-02-19 19:52:38 -08003185 strcat(rs, "8");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003186 isdn_tty_at_cout(rs, info);
3187 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003188 default:
3189 PARSE_ERROR1;
Joe Perches475be4d2012-02-19 19:52:38 -08003190 }
3191 break;
3192 default:
3193 PARSE_ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003194 }
3195 return 0;
3196 }
3197#ifdef CONFIG_ISDN_TTY_FAX
3198 return (isdn_tty_cmd_PLUSF_FAX(p, info));
3199#else
3200 PARSE_ERROR1;
3201#endif
3202}
3203
3204/*
3205 * Parse AT+V.. commands
3206 */
3207static int
Joe Perches475be4d2012-02-19 19:52:38 -08003208isdn_tty_cmd_PLUSV(char **p, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209{
3210 atemu *m = &info->emu;
3211 isdn_ctrl cmd;
3212 static char *vcmd[] =
Joe Perches475be4d2012-02-19 19:52:38 -08003213 {"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL};
Linus Torvalds1da177e2005-04-16 15:20:36 -07003214 int i;
3215 int par1;
3216 int par2;
3217 char rs[20];
3218
3219 i = 0;
3220 while (vcmd[i]) {
3221 if (!strncmp(vcmd[i], p[0], 2)) {
3222 p[0] += 2;
3223 break;
3224 }
3225 i++;
3226 }
3227 switch (i) {
Joe Perches475be4d2012-02-19 19:52:38 -08003228 case 0:
3229 /* AT+VNH - Auto hangup feature */
3230 switch (*p[0]) {
3231 case '?':
3232 p[0]++;
3233 isdn_tty_at_cout("\r\n1", info);
3234 break;
3235 case '=':
3236 p[0]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003237 switch (*p[0]) {
Joe Perches475be4d2012-02-19 19:52:38 -08003238 case '1':
3239 p[0]++;
3240 break;
3241 case '?':
3242 p[0]++;
3243 isdn_tty_at_cout("\r\n1", info);
3244 break;
3245 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003246 PARSE_ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003247 }
3248 break;
3249 default:
3250 PARSE_ERROR1;
Joe Perches475be4d2012-02-19 19:52:38 -08003251 }
3252 break;
3253 case 1:
3254 /* AT+VIP - Reset all voice parameters */
3255 isdn_tty_modem_reset_vpar(m);
3256 break;
3257 case 2:
3258 /* AT+VLS - Select device, accept incoming call */
3259 switch (*p[0]) {
3260 case '?':
3261 p[0]++;
3262 sprintf(rs, "\r\n%d", m->vpar[0]);
3263 isdn_tty_at_cout(rs, info);
3264 break;
3265 case '=':
3266 p[0]++;
3267 switch (*p[0]) {
3268 case '0':
3269 p[0]++;
3270 m->vpar[0] = 0;
3271 break;
3272 case '2':
3273 p[0]++;
3274 m->vpar[0] = 2;
3275 break;
3276 case '?':
3277 p[0]++;
3278 isdn_tty_at_cout("\r\n0,2", info);
3279 break;
3280 default:
3281 PARSE_ERROR1;
3282 }
3283 break;
3284 default:
3285 PARSE_ERROR1;
3286 }
3287 break;
3288 case 3:
3289 /* AT+VRX - Start recording */
3290 if (!m->vpar[0])
3291 PARSE_ERROR1;
3292 if (info->online != 1) {
3293 isdn_tty_modem_result(RESULT_NO_ANSWER, info);
3294 return 1;
3295 }
3296 info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
3297 if (!info->dtmf_state) {
3298 printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
3299 PARSE_ERROR1;
3300 }
3301 info->silence_state = isdn_audio_silence_init(info->silence_state);
3302 if (!info->silence_state) {
3303 printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
3304 PARSE_ERROR1;
3305 }
3306 if (m->vpar[3] < 5) {
3307 info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
3308 if (!info->adpcmr) {
3309 printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
3310 PARSE_ERROR1;
3311 }
3312 }
3313#ifdef ISDN_DEBUG_AT
3314 printk(KERN_DEBUG "AT: +VRX\n");
3315#endif
3316 info->vonline |= 1;
3317 isdn_tty_modem_result(RESULT_CONNECT, info);
3318 return 0;
3319 break;
3320 case 4:
3321 /* AT+VSD - Silence detection */
3322 switch (*p[0]) {
3323 case '?':
3324 p[0]++;
3325 sprintf(rs, "\r\n<%d>,<%d>",
3326 m->vpar[1],
3327 m->vpar[2]);
3328 isdn_tty_at_cout(rs, info);
3329 break;
3330 case '=':
3331 p[0]++;
3332 if ((*p[0] >= '0') && (*p[0] <= '9')) {
3333 par1 = isdn_getnum(p);
3334 if ((par1 < 0) || (par1 > 31))
3335 PARSE_ERROR1;
3336 if (*p[0] != ',')
3337 PARSE_ERROR1;
3338 p[0]++;
3339 par2 = isdn_getnum(p);
3340 if ((par2 < 0) || (par2 > 255))
3341 PARSE_ERROR1;
3342 m->vpar[1] = par1;
3343 m->vpar[2] = par2;
3344 break;
3345 } else
3346 if (*p[0] == '?') {
3347 p[0]++;
3348 isdn_tty_at_cout("\r\n<0-31>,<0-255>",
3349 info);
3350 break;
3351 } else
3352 PARSE_ERROR1;
3353 break;
3354 default:
3355 PARSE_ERROR1;
3356 }
3357 break;
3358 case 5:
3359 /* AT+VSM - Select compression */
3360 switch (*p[0]) {
3361 case '?':
3362 p[0]++;
3363 sprintf(rs, "\r\n<%d>,<%d><8000>",
3364 m->vpar[3],
3365 m->vpar[1]);
3366 isdn_tty_at_cout(rs, info);
3367 break;
3368 case '=':
3369 p[0]++;
3370 switch (*p[0]) {
3371 case '2':
3372 case '3':
3373 case '4':
3374 case '5':
3375 case '6':
3376 par1 = isdn_getnum(p);
3377 if ((par1 < 2) || (par1 > 6))
3378 PARSE_ERROR1;
3379 m->vpar[3] = par1;
3380 break;
3381 case '?':
3382 p[0]++;
3383 isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
3384 info);
3385 isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
3386 info);
3387 isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
3388 info);
3389 isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n",
3390 info);
3391 isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n",
3392 info);
3393 break;
3394 default:
3395 PARSE_ERROR1;
3396 }
3397 break;
3398 default:
3399 PARSE_ERROR1;
3400 }
3401 break;
3402 case 6:
3403 /* AT+VTX - Start sending */
3404 if (!m->vpar[0])
3405 PARSE_ERROR1;
3406 if (info->online != 1) {
3407 isdn_tty_modem_result(RESULT_NO_ANSWER, info);
3408 return 1;
3409 }
3410 info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
3411 if (!info->dtmf_state) {
3412 printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
3413 PARSE_ERROR1;
3414 }
3415 if (m->vpar[3] < 5) {
3416 info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
3417 if (!info->adpcms) {
3418 printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
3419 PARSE_ERROR1;
3420 }
3421 }
3422#ifdef ISDN_DEBUG_AT
3423 printk(KERN_DEBUG "AT: +VTX\n");
3424#endif
3425 m->lastDLE = 0;
3426 info->vonline |= 2;
3427 isdn_tty_modem_result(RESULT_CONNECT, info);
3428 return 0;
3429 break;
3430 case 7:
3431 /* AT+VDD - DTMF detection */
3432 switch (*p[0]) {
3433 case '?':
3434 p[0]++;
3435 sprintf(rs, "\r\n<%d>,<%d>",
3436 m->vpar[4],
3437 m->vpar[5]);
3438 isdn_tty_at_cout(rs, info);
3439 break;
3440 case '=':
3441 p[0]++;
3442 if ((*p[0] >= '0') && (*p[0] <= '9')) {
3443 if (info->online != 1)
3444 PARSE_ERROR1;
3445 par1 = isdn_getnum(p);
3446 if ((par1 < 0) || (par1 > 15))
3447 PARSE_ERROR1;
3448 if (*p[0] != ',')
3449 PARSE_ERROR1;
3450 p[0]++;
3451 par2 = isdn_getnum(p);
3452 if ((par2 < 0) || (par2 > 255))
3453 PARSE_ERROR1;
3454 m->vpar[4] = par1;
3455 m->vpar[5] = par2;
3456 cmd.driver = info->isdn_driver;
3457 cmd.command = ISDN_CMD_AUDIO;
3458 cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8);
3459 cmd.parm.num[0] = par1;
3460 cmd.parm.num[1] = par2;
3461 isdn_command(&cmd);
3462 break;
3463 } else
3464 if (*p[0] == '?') {
3465 p[0]++;
3466 isdn_tty_at_cout("\r\n<0-15>,<0-255>",
3467 info);
3468 break;
3469 } else
3470 PARSE_ERROR1;
3471 break;
3472 default:
3473 PARSE_ERROR1;
3474 }
3475 break;
3476 default:
3477 PARSE_ERROR1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003478 }
3479 return 0;
3480}
3481#endif /* CONFIG_ISDN_AUDIO */
3482
3483/*
3484 * Parse and perform an AT-command-line.
3485 */
3486static void
Joe Perches475be4d2012-02-19 19:52:38 -08003487isdn_tty_parse_at(modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003488{
3489 atemu *m = &info->emu;
3490 char *p;
Dan Carpenterf417f5e2010-09-04 08:33:03 +00003491 char ds[ISDN_MSNLEN];
Linus Torvalds1da177e2005-04-16 15:20:36 -07003492
3493#ifdef ISDN_DEBUG_AT
3494 printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
3495#endif
3496 for (p = &m->mdmcmd[2]; *p;) {
3497 switch (*p) {
Joe Perches475be4d2012-02-19 19:52:38 -08003498 case ' ':
3499 p++;
3500 break;
3501 case 'A':
3502 /* A - Accept incoming call */
3503 p++;
3504 isdn_tty_cmd_ATA(info);
3505 return;
3506 break;
3507 case 'D':
3508 /* D - Dial */
3509 if (info->msr & UART_MSR_DCD)
3510 PARSE_ERROR;
3511 if (info->msr & UART_MSR_RI) {
3512 isdn_tty_modem_result(RESULT_NO_CARRIER, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003513 return;
Joe Perches475be4d2012-02-19 19:52:38 -08003514 }
3515 isdn_tty_getdial(++p, ds, sizeof ds);
3516 p += strlen(p);
3517 if (!strlen(m->msn))
3518 isdn_tty_modem_result(RESULT_NO_MSN_EAZ, info);
3519 else if (strlen(ds))
3520 isdn_tty_dial(ds, info, m);
3521 else
3522 PARSE_ERROR;
3523 return;
3524 case 'E':
3525 /* E - Turn Echo on/off */
3526 p++;
3527 switch (isdn_getnum(&p)) {
3528 case 0:
3529 m->mdmreg[REG_ECHO] &= ~BIT_ECHO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003530 break;
Joe Perches475be4d2012-02-19 19:52:38 -08003531 case 1:
3532 m->mdmreg[REG_ECHO] |= BIT_ECHO;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533 break;
3534 default:
3535 PARSE_ERROR;
Joe Perches475be4d2012-02-19 19:52:38 -08003536 }
3537 break;
3538 case 'H':
3539 /* H - On/Off-hook */
3540 p++;
3541 switch (*p) {
3542 case '0':
3543 p++;
3544 isdn_tty_on_hook(info);
3545 break;
3546 case '1':
3547 p++;
3548 isdn_tty_off_hook();
3549 break;
3550 default:
3551 isdn_tty_on_hook(info);
3552 break;
3553 }
3554 break;
3555 case 'I':
3556 /* I - Information */
3557 p++;
3558 isdn_tty_at_cout("\r\nLinux ISDN", info);
3559 switch (*p) {
3560 case '0':
3561 case '1':
3562 p++;
3563 break;
3564 case '2':
3565 p++;
3566 isdn_tty_report(info);
3567 break;
3568 case '3':
3569 p++;
3570 snprintf(ds, sizeof(ds), "\r\n%d", info->emu.charge);
3571 isdn_tty_at_cout(ds, info);
3572 break;
3573 default:;
3574 }
3575 break;
3576#ifdef DUMMY_HAYES_AT
3577 case 'L':
3578 case 'M':
3579 /* only for be compilant with common scripts */
3580 /* no function */
3581 p++;
3582 isdn_getnum(&p);
3583 break;
3584#endif
3585 case 'O':
3586 /* O - Go online */
3587 p++;
3588 if (info->msr & UART_MSR_DCD)
3589 /* if B-Channel is up */
3590 isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT : RESULT_CONNECT64000, info);
3591 else
3592 isdn_tty_modem_result(RESULT_NO_CARRIER, info);
3593 return;
3594 case 'Q':
3595 /* Q - Turn Emulator messages on/off */
3596 p++;
3597 switch (isdn_getnum(&p)) {
3598 case 0:
3599 m->mdmreg[REG_RESP] |= BIT_RESP;
3600 break;
3601 case 1:
3602 m->mdmreg[REG_RESP] &= ~BIT_RESP;
3603 break;
3604 default:
3605 PARSE_ERROR;
3606 }
3607 break;
3608 case 'S':
3609 /* S - Set/Get Register */
3610 p++;
3611 if (isdn_tty_cmd_ATS(&p, info))
3612 return;
3613 break;
3614 case 'V':
3615 /* V - Numeric or ASCII Emulator-messages */
3616 p++;
3617 switch (isdn_getnum(&p)) {
3618 case 0:
3619 m->mdmreg[REG_RESP] |= BIT_RESPNUM;
3620 break;
3621 case 1:
3622 m->mdmreg[REG_RESP] &= ~BIT_RESPNUM;
3623 break;
3624 default:
3625 PARSE_ERROR;
3626 }
3627 break;
3628 case 'Z':
3629 /* Z - Load Registers from Profile */
3630 p++;
3631 if (info->msr & UART_MSR_DCD) {
3632 info->online = 0;
3633 isdn_tty_on_hook(info);
3634 }
3635 isdn_tty_modem_reset_regs(info, 1);
3636 break;
3637 case '+':
3638 p++;
3639 switch (*p) {
3640#ifdef CONFIG_ISDN_AUDIO
3641 case 'F':
3642 p++;
3643 if (isdn_tty_cmd_PLUSF(&p, info))
3644 return;
3645 break;
3646 case 'V':
3647 if ((!(m->mdmreg[REG_SI1] & 1)) ||
3648 (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
3649 PARSE_ERROR;
3650 p++;
3651 if (isdn_tty_cmd_PLUSV(&p, info))
3652 return;
3653 break;
3654#endif /* CONFIG_ISDN_AUDIO */
3655 case 'S': /* SUSPEND */
3656 p++;
3657 isdn_tty_get_msnstr(ds, &p);
3658 isdn_tty_suspend(ds, info, m);
3659 break;
3660 case 'R': /* RESUME */
3661 p++;
3662 isdn_tty_get_msnstr(ds, &p);
3663 isdn_tty_resume(ds, info, m);
3664 break;
3665 case 'M': /* MESSAGE */
3666 p++;
3667 isdn_tty_send_msg(info, m, p);
3668 break;
3669 default:
3670 PARSE_ERROR;
3671 }
3672 break;
3673 case '&':
3674 p++;
3675 if (isdn_tty_cmd_ATand(&p, info))
3676 return;
3677 break;
3678 default:
3679 PARSE_ERROR;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003680 }
3681 }
3682#ifdef CONFIG_ISDN_AUDIO
3683 if (!info->vonline)
3684#endif
3685 isdn_tty_modem_result(RESULT_OK, info);
3686}
3687
3688/* Need own toupper() because standard-toupper is not available
3689 * within modules.
3690 */
Joe Perches475be4d2012-02-19 19:52:38 -08003691#define my_toupper(c) (((c >= 'a') && (c <= 'z')) ? (c & 0xdf) : c)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003692
3693/*
3694 * Perform line-editing of AT-commands
3695 *
3696 * Parameters:
3697 * p inputbuffer
3698 * count length of buffer
3699 * channel index to line (minor-device)
3700 */
3701static int
Joe Perches475be4d2012-02-19 19:52:38 -08003702isdn_tty_edit_at(const char *p, int count, modem_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703{
3704 atemu *m = &info->emu;
3705 int total = 0;
3706 u_char c;
3707 char eb[2];
3708 int cnt;
3709
3710 for (cnt = count; cnt > 0; p++, cnt--) {
3711 c = *p;
3712 total++;
3713 if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) {
3714 /* Separator (CR or LF) */
3715 m->mdmcmd[m->mdmcmdl] = 0;
3716 if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
3717 eb[0] = c;
3718 eb[1] = 0;
3719 isdn_tty_at_cout(eb, info);
3720 }
3721 if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2))))
3722 isdn_tty_parse_at(info);
3723 m->mdmcmdl = 0;
3724 continue;
3725 }
3726 if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) {
3727 /* Backspace-Function */
3728 if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
3729 if (m->mdmcmdl)
3730 m->mdmcmdl--;
3731 if (m->mdmreg[REG_ECHO] & BIT_ECHO)
3732 isdn_tty_at_cout("\b", info);
3733 }
3734 continue;
3735 }
3736 if (cmdchar(c)) {
3737 if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
3738 eb[0] = c;
3739 eb[1] = 0;
3740 isdn_tty_at_cout(eb, info);
3741 }
3742 if (m->mdmcmdl < 255) {
3743 c = my_toupper(c);
3744 switch (m->mdmcmdl) {
Joe Perches475be4d2012-02-19 19:52:38 -08003745 case 1:
3746 if (c == 'T') {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747 m->mdmcmd[m->mdmcmdl] = c;
3748 m->mdmcmd[++m->mdmcmdl] = 0;
Joe Perches475be4d2012-02-19 19:52:38 -08003749 break;
3750 } else
3751 m->mdmcmdl = 0;
3752 /* Fall through, check for 'A' */
3753 case 0:
3754 if (c == 'A') {
3755 m->mdmcmd[m->mdmcmdl] = c;
3756 m->mdmcmd[++m->mdmcmdl] = 0;
3757 }
3758 break;
3759 default:
3760 m->mdmcmd[m->mdmcmdl] = c;
3761 m->mdmcmd[++m->mdmcmdl] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003762 }
3763 }
3764 }
3765 }
3766 return total;
3767}
3768
3769/*
3770 * Switch all modem-channels who are online and got a valid
3771 * escape-sequence 1.5 seconds ago, to command-mode.
3772 * This function is called every second via timer-interrupt from within
3773 * timer-dispatcher isdn_timer_function()
3774 */
3775void
3776isdn_tty_modem_escape(void)
3777{
3778 int ton = 0;
3779 int i;
3780 int midx;
3781
3782 for (i = 0; i < ISDN_MAX_CHANNELS; i++)
3783 if (USG_MODEM(dev->usage[i]))
3784 if ((midx = dev->m_idx[i]) >= 0) {
3785 modem_info *info = &dev->mdm.info[midx];
3786 if (info->online) {
3787 ton = 1;
3788 if ((info->emu.pluscount == 3) &&
3789 time_after(jiffies , info->emu.lastplus + PLUSWAIT2)) {
3790 info->emu.pluscount = 0;
3791 info->online = 0;
3792 isdn_tty_modem_result(RESULT_OK, info);
3793 }
3794 }
3795 }
3796 isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
3797}
3798
3799/*
3800 * Put a RING-message to all modem-channels who have the RI-bit set.
3801 * This function is called every second via timer-interrupt from within
3802 * timer-dispatcher isdn_timer_function()
3803 */
3804void
3805isdn_tty_modem_ring(void)
3806{
3807 int ton = 0;
3808 int i;
3809
3810 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
3811 modem_info *info = &dev->mdm.info[i];
3812 if (info->msr & UART_MSR_RI) {
3813 ton = 1;
3814 isdn_tty_modem_result(RESULT_RING, info);
3815 }
3816 }
3817 isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
3818}
3819
3820/*
3821 * For all online tty's, try sending data to
3822 * the lower levels.
3823 */
3824void
3825isdn_tty_modem_xmit(void)
3826{
3827 int ton = 1;
3828 int i;
3829
3830 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
3831 modem_info *info = &dev->mdm.info[i];
3832 if (info->online) {
3833 ton = 1;
3834 isdn_tty_senddown(info);
3835 isdn_tty_tint(info);
3836 }
3837 }
3838 isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
3839}
3840
3841/*
3842 * Check all channels if we have a 'no carrier' timeout.
3843 * Timeout value is set by Register S7.
3844 */
3845void
3846isdn_tty_carrier_timeout(void)
3847{
3848 int ton = 0;
3849 int i;
3850
3851 for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
3852 modem_info *info = &dev->mdm.info[i];
3853 if (info->dialing) {
3854 if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
3855 info->dialing = 0;
3856 isdn_tty_modem_result(RESULT_NO_CARRIER, info);
3857 isdn_tty_modem_hup(info, 1);
3858 }
3859 else
3860 ton = 1;
3861 }
3862 }
3863 isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
3864}