summaryrefslogtreecommitdiff
path: root/noncore/settings/networksettings/ppp/modem.cpp
Unidiff
Diffstat (limited to 'noncore/settings/networksettings/ppp/modem.cpp') (more/less context) (show whitespace changes)
-rw-r--r--noncore/settings/networksettings/ppp/modem.cpp671
1 files changed, 671 insertions, 0 deletions
diff --git a/noncore/settings/networksettings/ppp/modem.cpp b/noncore/settings/networksettings/ppp/modem.cpp
new file mode 100644
index 0000000..e9e3f06
--- a/dev/null
+++ b/noncore/settings/networksettings/ppp/modem.cpp
@@ -0,0 +1,671 @@
1/*
2 * kPPP: A pppd Front End for the KDE project
3 *
4 * $Id$
5 *
6 * Copyright (C) 1997 Bernd Johannes Wuebben
7 * wuebben@math.cornell.edu
8 *
9 * This file was added by Harri Porten <porten@tu-harburg.de>
10 *
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Library General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with this program; if not, write to the Free
24 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 */
26
27#include <errno.h>
28#include <stdlib.h>
29#include <fcntl.h>
30#include <signal.h>
31#include <sys/ioctl.h>
32#include <setjmp.h>
33#include <qregexp.h>
34#include <assert.h>
35
36#include "modem.h"
37#include "pppdata.h"
38//#include "requester.h"
39//#include <klocale.h>
40#define i18n QObject::tr
41#define qError qDebug
42//#include <kdebug.h>
43//#include <config.h>
44
45#define MY_ASSERT(x) if (!(x)) { \
46 qFatal( "ASSERT: \"%s\" in %s (%d)\n",#x,__FILE__,__LINE__); \
47 exit(1); }
48
49
50static sigjmp_buf jmp_buffer;
51
52Modem *Modem::modem = 0;
53
54Modem::Modem() :
55 modemfd(-1),
56 sn(0L),
57 data_mode(false),
58 modem_is_locked(false)
59{
60 lockfile[0] = '\0';
61 device = "/dev/modem";
62 assert(modem==0);
63 modem = this;
64}
65
66
67Modem::~Modem() {
68 modem = 0;
69}
70
71
72speed_t Modem::modemspeed() {
73 // convert the string modem speed int the gpppdata object to a t_speed type
74 // to set the modem. The constants here should all be ifdef'd because
75 // other systems may not have them
76 int i = gpppdata.speed().toInt()/100;
77
78 switch(i) {
79 case 24:
80 return B2400;
81 break;
82 case 96:
83 return B9600;
84 break;
85 case 192:
86 return B19200;
87 break;
88 case 384:
89 return B38400;
90 break;
91#ifdef B57600
92 case 576:
93 return B57600;
94 break;
95#endif
96
97#ifdef B115200
98 case 1152:
99 return B115200;
100 break;
101#endif
102
103#ifdef B230400
104 case 2304:
105 return B230400;
106 break;
107#endif
108
109#ifdef B460800
110 case 4608:
111 return B460800;
112 break;
113#endif
114
115 default:
116 return B38400;
117 break;
118 }
119}
120
121bool Modem::opentty() {
122 // int flags;
123
124//begin if((modemfd = Requester::rq->openModem(gpppdata.modemDevice()))<0) {
125 close(modemfd);
126// device = "/dev/modem";//deviceByIndex(request.modem.deviceNum);
127 if ((modemfd = open(device, O_RDWR|O_NDELAY|O_NOCTTY)) == -1) {
128 qDebug("error opening modem device !");
129 errmsg = i18n("Unable to open modem.");
130 return false;
131 }
132//bend if((modemfd = Requester::rq->openModem(gpppdata.modemDevice()))<0) {
133//}
134
135#if 0
136 if(gpppdata.UseCDLine()) {
137 if(ioctl(modemfd, TIOCMGET, &flags) == -1) {
138 errmsg = i18n("Unable to detect state of CD line.");
139 ::close(modemfd);
140 modemfd = -1;
141 return false;
142 }
143 if ((flags&TIOCM_CD) == 0) {
144 errmsg = i18n("The modem is not ready.");
145 ::close(modemfd);
146 modemfd = -1;
147 return false;
148 }
149 }
150#endif
151
152 tcdrain (modemfd);
153 tcflush (modemfd, TCIOFLUSH);
154
155 if(tcgetattr(modemfd, &tty) < 0){
156 // this helps in some cases
157 tcsendbreak(modemfd, 0);
158 sleep(1);
159 if(tcgetattr(modemfd, &tty) < 0){
160 errmsg = i18n("The modem is busy.");
161 ::close(modemfd);
162 modemfd = -1;
163 return false;
164 }
165 }
166
167 memset(&initial_tty,'\0',sizeof(initial_tty));
168
169 initial_tty = tty;
170
171 tty.c_cc[VMIN] = 0; // nonblocking
172 tty.c_cc[VTIME] = 0;
173 tty.c_oflag = 0;
174 tty.c_lflag = 0;
175
176 tty.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
177 tty.c_cflag |= CS8 | CREAD;
178 tty.c_cflag |= CLOCAL; // ignore modem status lines
179 tty.c_iflag = IGNBRK | IGNPAR /* | ISTRIP */ ;
180 tty.c_lflag &= ~ICANON; // non-canonical mode
181 tty.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHOKE);
182
183
184 if(gpppdata.flowcontrol() != "None") {
185 if(gpppdata.flowcontrol() == "CRTSCTS") {
186 tty.c_cflag |= CRTSCTS;
187 }
188 else {
189 tty.c_iflag |= IXON | IXOFF;
190 tty.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
191 tty.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
192 }
193 }
194 else {
195 tty.c_cflag &= ~CRTSCTS;
196 tty.c_iflag &= ~(IXON | IXOFF);
197 }
198
199 cfsetospeed(&tty, modemspeed());
200 cfsetispeed(&tty, modemspeed());
201
202 tcdrain(modemfd);
203
204 if(tcsetattr(modemfd, TCSANOW, &tty) < 0){
205 errmsg = i18n("The modem is busy.");
206 ::close(modemfd);
207 modemfd=-1;
208 return false;
209 }
210
211 errmsg = i18n("Modem Ready.");
212 return true;
213}
214
215
216bool Modem::closetty() {
217 if(modemfd >=0 ) {
218 stop();
219 /* discard data not read or transmitted */
220 tcflush(modemfd, TCIOFLUSH);
221
222 if(tcsetattr(modemfd, TCSANOW, &initial_tty) < 0){
223 errmsg = i18n("Can't restore tty settings: tcsetattr()\n");
224 ::close(modemfd);
225 modemfd = -1;
226 return false;
227 }
228 ::close(modemfd);
229 modemfd = -1;
230 }
231
232 return true;
233}
234
235
236void Modem::readtty(int) {
237 char buffer[200];
238 unsigned char c;
239 int len;
240
241 // read data in chunks of up to 200 bytes
242 if((len = ::read(modemfd, buffer, 200)) > 0) {
243 // split buffer into single characters for further processing
244 for(int i = 0; i < len; i++) {
245 c = buffer[i] & 0x7F;
246 emit charWaiting(c);
247 }
248 }
249}
250
251
252void Modem::notify(const QObject *receiver, const char *member) {
253 connect(this, SIGNAL(charWaiting(unsigned char)), receiver, member);
254 startNotifier();
255}
256
257
258void Modem::stop() {
259 disconnect(SIGNAL(charWaiting(unsigned char)));
260 stopNotifier();
261}
262
263
264void Modem::startNotifier() {
265 if(modemfd >= 0) {
266 if(sn == 0) {
267 sn = new QSocketNotifier(modemfd, QSocketNotifier::Read, this);
268 connect(sn, SIGNAL(activated(int)), SLOT(readtty(int)));
269 qDebug("QSocketNotifier started!");
270 } else {
271 qDebug("QSocketNotifier re-enabled!");
272 sn->setEnabled(true);
273 }
274 }
275}
276
277
278void Modem::stopNotifier() {
279 if(sn != 0) {
280 sn->setEnabled(false);
281 disconnect(sn);
282 delete sn;
283 sn = 0;
284 qDebug( "QSocketNotifier stopped!" );
285 }
286}
287
288
289void Modem::flush() {
290 char c;
291 while(read(modemfd, &c, 1) == 1);
292}
293
294
295bool Modem::writeChar(unsigned char c) {
296 int s;
297 do {
298 s = write(modemfd, &c, 1);
299 if (s < 0) {
300 qError( "write() in Modem::writeChar failed" );
301 return false;
302 }
303 } while(s == 0);
304
305 return true;
306}
307
308
309bool Modem::writeLine(const char *buf) {
310 int len = strlen(buf);
311 char *b = new char[len+2];
312 memcpy(b, buf, len);
313 // different modems seem to need different line terminations
314 QString term = gpppdata.enter();
315 if(term == "LF")
316 b[len++]='\n';
317 else if(term == "CR")
318 b[len++]='\r';
319 else if(term == "CR/LF") {
320 b[len++]='\r';
321 b[len++]='\n';
322 }
323 int l = len;
324 while(l) {
325 int wr = write(modemfd, &b[len-l], l);
326 if(wr < 0) {
327 // TODO do something meaningful with the error code (or ignore it
328 qError( "write() in Modem::writeLine failed" );
329 delete[] b;
330 return false;
331 }
332 l -= wr;
333 }
334 delete[] b;
335 return true;
336}
337
338
339bool Modem::hangup() {
340 // this should really get the modem to hang up and go into command mode
341 // If anyone sees a fault in the following please let me know, since
342 // this is probably the most imporant snippet of code in the whole of
343 // kppp. If people complain about kppp being stuck, this piece of code
344 // is most likely the reason.
345 struct termios temptty;
346
347 if(modemfd >= 0) {
348
349 // is this Escape & HangupStr stuff really necessary ? (Harri)
350
351 if (data_mode) escape_to_command_mode();
352
353 // Then hangup command
354 writeLine(gpppdata.modemHangupStr().local8Bit());
355
356 usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 sec
357
358#ifndef DEBUG_WO_DIALING
359 if (sigsetjmp(jmp_buffer, 1) == 0) {
360 // set alarm in case tcsendbreak() hangs
361 signal(SIGALRM, alarm_handler);
362 alarm(2);
363
364 tcsendbreak(modemfd, 0);
365
366 alarm(0);
367 signal(SIGALRM, SIG_IGN);
368 } else {
369 // we reach this point if the alarm handler got called
370 closetty();
371 close(modemfd);
372 modemfd = -1;
373 errmsg = i18n("The modem does not respond.");
374 return false;
375 }
376
377#ifndef __svr4__ // drops DTR but doesn't set it afterwards again. not good for init.
378 tcgetattr(modemfd, &temptty);
379 cfsetospeed(&temptty, B0);
380 cfsetispeed(&temptty, B0);
381 tcsetattr(modemfd, TCSAFLUSH, &temptty);
382#else
383 int modemstat;
384 ioctl(modemfd, TIOCMGET, &modemstat);
385 modemstat &= ~TIOCM_DTR;
386 ioctl(modemfd, TIOCMSET, &modemstat);
387 ioctl(modemfd, TIOCMGET, &modemstat);
388 modemstat |= TIOCM_DTR;
389 ioctl(modemfd, TIOCMSET, &modemstat);
390#endif
391
392 usleep(gpppdata.modemInitDelay() * 10000); // 0.01 - 3.0 secs
393
394 cfsetospeed(&temptty, modemspeed());
395 cfsetispeed(&temptty, modemspeed());
396 tcsetattr(modemfd, TCSAFLUSH, &temptty);
397#endif
398 return true;
399 } else
400 return false;
401}
402
403
404void Modem::escape_to_command_mode() {
405 // Send Properly bracketed escape code to put the modem back into command state.
406 // A modem will accept AT commands only when it is in command state.
407 // When a modem sends the host the CONNECT string, that signals
408 // that the modem is now in the connect state (no long accepts AT commands.)
409 // Need to send properly timed escape sequence to put modem in command state.
410 // Escape codes and guard times are controlled by S2 and S12 values.
411 //
412 tcflush(modemfd, TCIOFLUSH);
413
414 // +3 because quiet time must be greater than guard time.
415 usleep((gpppdata.modemEscapeGuardTime()+3)*20000);
416 QCString tmp = gpppdata.modemEscapeStr().local8Bit();
417 write(modemfd, tmp.data(), tmp.length());
418 tcflush(modemfd, TCIOFLUSH);
419 usleep((gpppdata.modemEscapeGuardTime()+3)*20000);
420
421 data_mode = false;
422}
423
424
425const QString Modem::modemMessage() {
426 return errmsg;
427}
428
429
430QString Modem::parseModemSpeed(const QString &s) {
431 // this is a small (and bad) parser for modem speeds
432 int rx = -1;
433 int tx = -1;
434 int i;
435 QString result;
436
437 qDebug( "Modem reported result string: %s", s.latin1());
438
439 const int RXMAX = 7;
440 const int TXMAX = 2;
441 QRegExp rrx[RXMAX] = {
442 QRegExp("[0-9]+[:/ ]RX", false),
443 QRegExp("[0-9]+RX", false),
444 QRegExp("[/: -][0-9]+[/: ]", false),
445 QRegExp("[/: -][0-9]+$", false),
446 QRegExp("CARRIER [^0-9]*[0-9]+", false),
447 QRegExp("CONNECT [^0-9]*[0-9]+", false),
448 QRegExp("[0-9]+") // panic mode
449 };
450
451 QRegExp trx[TXMAX] = {
452 QRegExp("[0-9]+[:/ ]TX", false),
453 QRegExp("[0-9]+TX", false)
454 };
455
456 for(i = 0; i < RXMAX; i++) {
457 int len, idx, result;
458 if((idx = rrx[i].match(s,0,&len)) > -1) {
459// if((idx = rrx[i].search(s)) > -1) {
460 // len = rrx[i].matchedLength();
461
462 //
463 // rrx[i] has been matched, idx contains the start of the match
464 // and len contains how long the match is. Extract the match.
465 //
466 QString sub = s.mid(idx, len);
467
468 //
469 // Now extract the digits only from the match, which will
470 // then be converted to an int.
471 //
472 if ((idx = rrx[RXMAX-1].match( sub,0,&len )) > -1) {
473// if ((idx = rrx[RXMAX-1].search( sub )) > -1) {
474// len = rrx[RXMAX-1].matchedLength();
475 sub = sub.mid(idx, len);
476 result = sub.toInt();
477 if(result > 0) {
478 rx = result;
479 break;
480 }
481 }
482 }
483 }
484
485 for(i = 0; i < TXMAX; i++) {
486 int len, idx, result;
487 if((idx = trx[i].match(s,0,&len)) > -1) {
488// if((idx = trx[i].search(s)) > -1) {
489// len = trx[i].matchedLength();
490
491 //
492 // trx[i] has been matched, idx contains the start of the match
493 // and len contains how long the match is. Extract the match.
494 //
495 QString sub = s.mid(idx, len);
496
497 //
498 // Now extract the digits only from the match, which will then
499 // be converted to an int.
500 //
501 if((idx = rrx[RXMAX-1].match(sub,0,&len)) > -1) {
502// if((idx = rrx[RXMAX-1].search(sub)) > -1) {
503// len = rrx[RXMAX-1].matchedLength();
504 sub = sub.mid(idx, len);
505 result = sub.toInt();
506 if(result > 0) {
507 tx = result;
508 break;
509 }
510 }
511 }
512 }
513
514 if(rx == -1 && tx == -1)
515 result = i18n("Unknown speed");
516 else if(tx == -1)
517 result.setNum(rx);
518 else if(rx == -1) // should not happen
519 result.setNum(tx);
520 else
521 result.sprintf("%d/%d", rx, tx);
522
523 qDebug( "The parsed result is: %s", result.latin1());
524
525 return result;
526}
527
528
529// Lock modem device. Returns 0 on success 1 if the modem is locked and -1 if
530// a lock file can't be created ( permission problem )
531int Modem::lockdevice() {
532 int fd;
533 char newlock[80]=""; // safe
534
535 if(!gpppdata.modemLockFile()) {
536 qDebug("The user doesn't want a lockfile.");
537 return 0;
538 }
539
540 if (modem_is_locked)
541 return 1;
542
543 QString lockfile = LOCK_DIR"/LCK..";
544 lockfile += gpppdata.modemDevice().mid(5); // append everything after /dev/
545
546 if(access(QFile::encodeName(lockfile), F_OK) == 0) {
547// if ((fd = Requester::rq->
548if ((fd = openLockfile(QFile::encodeName(lockfile), O_RDONLY)) >= 0) {
549 // Mario: it's not necessary to read more than lets say 32 bytes. If
550 // file has more than 32 bytes, skip the rest
551 char oldlock[33]; // safe
552 int sz = read(fd, &oldlock, 32);
553 close (fd);
554 if (sz <= 0)
555 return 1;
556 oldlock[sz] = '\0';
557
558 qDebug( "Device is locked by: %s", oldlock);
559
560 int oldpid;
561 int match = sscanf(oldlock, "%d", &oldpid);
562
563 // found a pid in lockfile ?
564 if (match < 1 || oldpid <= 0)
565 return 1;
566
567 // check if process exists
568 if (kill((pid_t)oldpid, 0) == 0 || errno != ESRCH)
569 return 1;
570
571 qDebug( "lockfile is stale" );
572 }
573 }
574
575 fd = openLockfile(gpppdata.modemDevice(),O_WRONLY|O_TRUNC|O_CREAT);
576 if(fd >= 0) {
577 sprintf(newlock,"%010d\n", getpid());
578 qDebug("Locking Device: %s", newlock);
579
580 write(fd, newlock, strlen(newlock));
581 close(fd);
582 modem_is_locked=true;
583
584 return 0;
585 }
586
587 return -1;
588
589}
590
591
592// UnLock modem device
593void Modem::unlockdevice() {
594 if (modem_is_locked) {
595 qDebug( "UnLocking Modem Device" );
596 close(modemfd);
597 modemfd = -1;
598 unlink(lockfile);
599 lockfile[0] = '\0';
600 modem_is_locked=false;
601 }
602}
603
604int Modem::openLockfile( QString lockfile, int flags)
605{
606 int fd;
607 int mode;
608 flags = O_RDONLY;
609 if(flags == O_WRONLY|O_TRUNC|O_CREAT)
610 mode = 0644;
611 else
612 mode = 0;
613
614 lockfile = LOCK_DIR;
615 lockfile += "/LCK..";
616 lockfile += device.right( device.length() - device.findRev("/") -1 );
617 qDebug("lockfile >%s<",lockfile.latin1());
618 // TODO:
619 // struct stat st;
620 // if(stat(lockfile.data(), &st) == -1) {
621 // if(errno == EBADF)
622 // return -1;
623 // } else {
624 // // make sure that this is a regular file
625 // if(!S_ISREG(st.st_mode))
626 // return -1;
627 // }
628 if ((fd = open(lockfile, flags, mode)) == -1) {
629 qDebug("error opening lockfile!");
630 lockfile = QString::null;
631 fd = open(DEVNULL, O_RDONLY);
632 } else
633 fchown(fd, 0, 0);
634 return fd;
635}
636
637
638
639void alarm_handler(int) {
640 // fprintf(stderr, "alarm_handler(): Received SIGALRM\n");
641
642 // jump
643 siglongjmp(jmp_buffer, 1);
644}
645
646
647
648const char* pppdPath() {
649 // wasting a few bytes
650 static char buffer[sizeof(PPPDSEARCHPATH)+sizeof(PPPDNAME)];
651 static char *pppdPath = 0L;
652 char *p;
653
654 if(pppdPath == 0L) {
655 const char *c = PPPDSEARCHPATH;
656 while(*c != '\0') {
657 while(*c == ':')
658 c++;
659 p = buffer;
660 while(*c != '\0' && *c != ':')
661 *p++ = *c++;
662 *p = '\0';
663 strcat(p, "/");
664 strcat(p, PPPDNAME);
665 if(access(buffer, F_OK) == 0)
666 return (pppdPath = buffer);
667 }
668 }
669
670 return pppdPath;
671}