[keyboard] Add a manual layout switch button to the keyboard
[qtopia.git] / devices / ficgta01 / src / plugins / phonevendors / ficgta01 / vendor_ficgta01.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2000-2008 TROLLTECH ASA. All rights reserved.
4 **
5 ** This file is part of the Opensource Edition of the Qtopia Toolkit.
6 **
7 ** This software is licensed under the terms of the GNU General Public
8 ** License (GPL) version 2.
9 **
10 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
11 **
12 ** Contact info@trolltech.com if any conditions of this licensing are
13 ** not clear to you.
14 **
15 **
16 **
17 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 **
20 ****************************************************************************/
21
22 #include "vendor_ficgta01_p.h"
23 #include <qmodemindicators.h>
24 #include <qatutils.h>
25 #include <qatresultparser.h>
26 #include <QProcess>
27 #include <QTimer>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <QFile>
31 #include <QTextStream>
32 #include <QSettings>
33
34 #include <qmodemcallvolume.h>
35 #include <qmodemsiminfo.h>
36
37 static bool supportsStk = false;
38
39 #define DEFAULT_CHARSET QLatin1String("UCS2")
40
41 Ficgta01CallProvider::Ficgta01CallProvider( QModemService *service )
42     : QModemCallProvider( service )
43 {
44     service->primaryAtChat()->registerNotificationType
45         ( "%CPI:", this, SLOT(cpiNotification(QString)) );
46     service->primaryAtChat()->registerNotificationType
47         ( "%CNAP:", this, SLOT(cnapNotification(QString)) );
48
49     setUseMissedTimer(false);
50     setUseDetectTimer(false);
51     setTreatAcceptCommandFailedAsMissed(false);
52 }
53
54 Ficgta01CallProvider::~Ficgta01CallProvider()
55 {
56 }
57
58 QModemCallProvider::AtdBehavior Ficgta01CallProvider::atdBehavior() const
59 {
60     // When ATD reports OK or NO CARRIER, it indicates that it is
61     // back in command mode. We just want to ignore these in QModemCall
62     // as we will have %CPI that is going to give us the right state.
63     return AtdOkIgnore;
64 }
65
66 void Ficgta01CallProvider::abortDial( uint id, QPhoneCall::Scope scope )
67 {
68     // Use the ATH command to abort outgoing calls, instead of AT+CHLD=1.
69     //atchat()->chat( "ATH" );
70
71     // Use default behaviour of CR followed by AT+CHLD - seems to work better.
72     QModemCallProvider::abortDial( id, scope );
73 }
74
75 void Ficgta01CallProvider::cpiNotification( const QString& msg )
76 {
77     // Call progress notification for the FICGTA01 device.
78     // %CPI: <cId>,<msgType>,<ibt>,<tch>,[<dir>],[<mode>][,<number>,<type>[,<alpha>]] 
79     // where <cId> is the call identifier, and <msgType> is one of:
80     // 0 = SETUP, 1 = DISCONNECT, 2 = ALERT, 3 = PROCEED,
81     // 4 = SYNCHRONIZATION, 5 = PROGRESS, 6 = CONNECTED,
82     // 7 = RELEASE, 8 = REJECT
83     // dir: 0 = mobile originated, 1 = mobile terminated, 2 = network initiaited mobile
84     // originated call, 3 = redialing mobile originated
85     uint posn = 5;
86     uint identifier = QAtUtils::parseNumber( msg, posn );
87
88     uint status = QAtUtils::parseNumber( msg, posn );
89     QAtUtils::skipField( msg, posn );
90     QAtUtils::skipField( msg, posn );
91     uint direction = QAtUtils::parseNumber( msg, posn );
92     QModemCall *call = callForIdentifier( identifier );
93
94     if ( status == 6 && call &&
95          ( call->state() == QPhoneCall::Dialing ||
96            call->state() == QPhoneCall::Alerting ) ) {
97
98         // This is an indication that a "Dialing" connection
99         // is now in the "Connected" state.
100         call->setConnected();
101
102     } else if ( ( status == 1 || status == 7 ) && call &&
103                 ( call->state() == QPhoneCall::Dialing ||
104                   call->state() == QPhoneCall::Alerting ) ) {
105
106         // We never managed to connect.
107         hangupRemote( call );
108
109     } else if ( status == 2 && call &&
110                 call->state() == QPhoneCall::Dialing ) {
111
112         // Call is moving from Dialing to Alerting.
113         call->setState( QPhoneCall::Alerting );
114
115     } else if ( ( status == 1 || status == 7 ) && call &&
116                 ( call->state() == QPhoneCall::Connected ||
117                   call->state() == QPhoneCall::Hold ) ) {
118
119         // This is an indication that the connection has been lost.
120         hangupRemote( call );
121
122     } else if ( ( status == 1 || status == 7 ) && call &&
123                 call->state() == QPhoneCall::Incoming ) {
124
125         // This is an indication that an incoming call was missed.
126         call->setState( QPhoneCall::Missed );
127
128     } else if ( ( status == 2 || status == 4 || status == 0) && !call && direction == 1) {
129
130         // This is a newly waiting call.  Treat it the same as "RING".
131         uint mode = QAtUtils::parseNumber( msg, posn );
132         QString callType;
133         if ( mode == 1 || mode == 6 || mode == 7 )
134             callType = "Data";  // No tr
135         else if ( mode == 2 || mode == 8 )
136             callType = "Fax";   // No tr
137         else
138             callType = "Voice"; // No tr
139         QString number = QAtUtils::nextString( msg, posn );
140         uint type = QAtUtils::parseNumber( msg, posn );
141         ringing( QAtUtils::decodeNumber( number, type ), callType, identifier );
142
143         // We got everything we need, indicate the call to the outside world
144         announceCall();
145     }
146 }
147
148 void Ficgta01CallProvider::cnapNotification( const QString& msg )
149 {
150     // Calling name presentation from the network.
151     uint posn = 6;
152     QAtUtils::skipField( msg, posn );       // pres_mode
153     QAtUtils::skipField( msg, posn );       // dcs
154     QAtUtils::skipField( msg, posn );       // name_length
155     QString name = QAtUtils::nextString( msg, posn );
156     QModemCall *call = incomingCall();
157     if ( call )
158         call->emitNotification( QPhoneCall::CallingName, name );
159 }
160
161 /*
162  * Reimplementation because we don't want the standard notifications
163  * as we have a CPI which should give us everything that we need.
164  * We don't need:
165  *    +CRING (CPI gives us the calltype)
166  *    +CLIP  (CPI gives us the number)
167  *    RING
168  */
169 void Ficgta01CallProvider::resetModem()
170 {
171     // disable all of these and do not call the base class
172     atchat()->chat( "AT+CRC=0" );
173     service()->retryChat( "AT+CLIP=0" );
174     service()->retryChat( "AT+COLP=0" );
175
176     // enable callwaiting support
177     service()->retryChat( "AT+CCWA=1" );
178 }
179
180 // We need to set GSM as codec for that to work on the TI Calypso see
181 // https://docs.openmoko.org/trac/ticket/2038 for more information
182 QString Ficgta01CallProvider::dialServiceCommand(const QDialOptions& options) const
183 {
184     QTextCodec* codec = QAtUtils::codec(DEFAULT_CHARSET);
185     QString cmd = QString::fromLatin1("AT+CUSD=1,%1,15");
186     return cmd.arg(QAtUtils::quote(options.number(), codec));
187 }
188
189 // Use the enable the echo suppression for TI Calypso before dialing
190 QString Ficgta01CallProvider::dialVoiceCommand(const QDialOptions& options) const
191 {
192         Ficgta01ModemService::echoCancellation(atchat());
193         return QModemCallProvider::dialVoiceCommand(options);
194 }
195
196 // Use the enable the echo suppression for TI Calypso before accepting calls
197 QString Ficgta01CallProvider::acceptCallCommand( bool otherActiveCalls ) const
198 {
199         Ficgta01ModemService::echoCancellation(atchat());
200         return QModemCallProvider::acceptCallCommand(otherActiveCalls);
201 }
202
203 Ficgta01PhoneBook::Ficgta01PhoneBook( QModemService *service )
204     : QModemPhoneBook( service )
205     , m_phoneBookWasReady(false)
206 {
207     qLog(AtChat)<<"Ficgta01PhoneBook::Ficgta01PhoneBook";
208     connect(this, SIGNAL(queryFailed(const QString&)),
209             SLOT(slotQueryFailed(const QString&)));
210 }
211
212 Ficgta01PhoneBook::~Ficgta01PhoneBook()
213 {
214 }
215
216 bool Ficgta01PhoneBook::hasModemPhoneBookCache() const
217 {
218     return true;
219 }
220
221 bool Ficgta01PhoneBook::hasEmptyPhoneBookIndex() const
222 {
223     return true;
224 }
225
226 void Ficgta01PhoneBook::sendPhoneBooksReady()
227 {
228     m_phoneBookWasReady = true;
229     phoneBooksReady();
230 }
231
232 void Ficgta01PhoneBook::slotQueryFailed(const QString& book)
233 {
234     // We didn't say we are ready, ignore this
235     if (!m_phoneBookWasReady)
236         return;
237
238     qLog(Modem) << "Phonebook query failed, ask to retry. " << book;
239     phoneBooksReady();
240 }
241
242 Ficgta01PinManager::Ficgta01PinManager( QModemService *service )
243     : QModemPinManager( service )
244 {
245 }
246
247 Ficgta01PinManager::~Ficgta01PinManager()
248 {
249 }
250
251 bool Ficgta01PinManager::emptyPinIsReady() const
252 {
253     return true;
254 }
255
256
257 // Known bands, by mask.
258 typedef struct
259 {
260     const char *name;
261     int         value;
262
263 } BandInfo;
264 static BandInfo const bandInfo[] = {
265     {"GSM 900",             1},
266     {"DCS 1800",            2},
267     {"PCS 1900",            4},
268     {"E-GSM",               8},
269 /*    {"GSM 850",             16}, */
270     {"Tripleband 900/1800/1900", 15},
271 };
272 #define numBands    ((int)(sizeof(bandInfo) / sizeof(BandInfo)))
273
274 Ficgta01BandSelection::Ficgta01BandSelection( QModemService *service )
275     : QBandSelection( service->service(), service, Server )
276 {
277     this->service = service;
278 }
279
280 Ficgta01BandSelection::~Ficgta01BandSelection()
281 {
282 }
283
284 void Ficgta01BandSelection::requestBand()
285 {
286     service->primaryAtChat()->chat
287         ( "AT%BAND?", this, SLOT(bandQuery(bool,QAtResult)) );
288 }
289
290 void Ficgta01BandSelection::requestBands()
291 {
292     QStringList list;
293     for ( int index = 0; index < numBands; ++index ) {
294         list += QString( bandInfo[index].name );
295     }
296     emit bands( list );
297
298 //     service->primaryAtChat()->chat
299 //         ( "AT%BAND=?", this, SLOT(bandList(bool,QAtResult)) );
300 }
301
302 void Ficgta01BandSelection::setBand( QBandSelection::BandMode mode, const QString& value )
303 {
304     if ( mode == Automatic ) {
305         service->primaryAtChat()->chat
306             ( "AT%BAND=0", this, SLOT(bandSet(bool,QAtResult)) );
307     } else {
308         int bandValue = 0;
309         QStringList names = value.split(", ");
310         foreach ( QString name, names ) {
311             bool seen = false;
312             for ( int index = 0; index < numBands; ++index ) {
313                 if ( name == bandInfo[index].name ) {
314                     bandValue |= bandInfo[index].value;
315                     seen = true;
316                     break;
317                 }
318             }
319             if ( !seen ) {
320                 // The band name is not valid.
321                 emit setBandResult( QTelephony::OperationNotSupported );
322                 return;
323             }
324         }
325         if ( !bandValue ) {
326             // No band names supplied.
327             emit setBandResult( QTelephony::OperationNotSupported );
328             return;
329         }
330         service->primaryAtChat()->chat
331             ( "AT%BAND=1," + QString::number( bandValue ),
332               this, SLOT(bandSet(bool,QAtResult)) );
333     }
334 }
335
336 // Convert a band value into a name.  Returns an empty list if unknown.
337 static QStringList bandValueToName( int bandValue )
338 {
339     QStringList bands;
340     for ( int index = 0; index < numBands; ++index ) {
341         if ( ( bandValue & bandInfo[index].value ) == bandInfo[index].value ) {
342             bandValue &= ~bandInfo[index].value;
343             bands += QString( bandInfo[index].name );
344         }
345     }
346     return bands;
347 }
348
349 void Ficgta01BandSelection::bandQuery( bool, const QAtResult& result )
350 {
351
352     QAtResultParser parser( result );
353     int bandValue;
354         qLog(AtChat)<<"bandQuery";
355     if ( parser.next( "%BAND:" ) ) {
356         bandValue = (int)parser.readNumeric();
357     } else {
358         // Command did not work, so assume "Auto".
359         bandValue = 4;
360     }
361     for ( int index = 0; index < numBands; ++index ) {
362         if ( bandValue == bandInfo[index].value ) {
363             emit band( Manual, bandInfo[index].name );
364             return;
365         }
366     }
367     emit band( Automatic, QString() );
368
369
370
371 //     QAtResultParser parser( result );
372 //     int bandValue;
373 //     qLog(AtChat)<<"bandQuery";
374 //     if ( parser.next( "%BAND:" ) ) {
375 //         if ( parser.readNumeric() != 0 ) {
376 //             bandValue = (int)parser.readNumeric();
377 //             QStringList bands = bandValueToName( bandValue );
378 //             if ( bands.size() > 0 ) {
379 //                 emit band( Manual, bands.join(", ") );
380 //                 return;
381 //             }
382
383 //         }
384 //     }
385
386 //     emit band( Automatic, QString() );
387
388 }
389
390 void Ficgta01BandSelection::bandList( bool, const QAtResult& result )
391 {
392     QAtResultParser parser( result );
393     QStringList bandNames;
394     if ( parser.next( "%BAND:" ) ) {
395
396         parser.readList();  // Skip list of supported modes.
397         QList<QAtResultParser::Node> list = parser.readList();
398         foreach ( QAtResultParser::Node node, list ) {
399
400             if ( node.isNumber() ) {
401                 bandNames += bandValueToName( (int)node.asNumber() );
402                 qLog(AtChat)<<  (int)node.asNumber();
403             } else if ( node.isRange() ) {
404                 int first = (int)node.asFirst();
405                 int last = (int)node.asLast();
406                 qLog(AtChat)<<"isRange"<<first<<last;
407                 while ( first <= last ) {
408                     qLog(AtChat)<< bandValueToName( first ) << first;
409                     bandNames += bandValueToName( first ).join(" | ");
410                     ++first;
411                 }
412             }
413         }
414     }
415      emit bands( bandNames );
416 }
417
418 void Ficgta01BandSelection::bandSet( bool, const QAtResult& result )
419 {
420     emit setBandResult( (QTelephony::Result)result.resultCode() );
421 }
422
423 DummyCellBroadcast::DummyCellBroadcast(QModemService* service)
424     : QCellBroadcast(service->service(), service, QCommInterface::Server)
425 {
426 }
427
428 void DummyCellBroadcast::setChannels(const QList<int>&)
429 {
430 }
431
432 Ficgta01ModemService::Ficgta01ModemService
433         ( const QString& service, QSerialIODeviceMultiplexer *mux,
434           QObject *parent )
435     : QModemService( service, mux, parent )
436     , m_vibratorService( 0 )
437     , m_phoneBook( 0 )
438     , m_phoneBookIsReady( false )
439     , m_smsIsReady( false )
440 {
441     connect( this, SIGNAL(resetModem()), this, SLOT(reset()) );
442
443     // Register a wakeup command to ping the modem if we haven't
444     // sent anything during the last 5 seconds.  This command may
445     // not get a response, but the modem should then become responsive
446     // to the next command that is sent afterwards.
447     primaryAtChat()->registerWakeupCommand( QChar(0x1a), 5000 );
448
449     // Attempt to reset the modem
450     chat("AT%CWUP=1");
451     chat("AT+CFUN=0");
452
453
454     // Turn on dynamic signal quality notifications.
455     // Register for "%CSQ" notifications to get signal quality updates.
456      primaryAtChat()->registerNotificationType
457          ( "%CSQ:", this, SLOT(csq(QString)) );
458      chat("AT%CSQ=1");
459      QTimer::singleShot( 2500, this, SLOT(firstCsqQuery()) );
460
461     // Modem dead detection for https://docs.openmoko.org/trac/ticket/1192
462     primaryAtChat()->registerNotificationType
463         ( "+CME ERROR: 512", this, SLOT(modemDied()));
464
465
466     // Enable %CPRI for ciphering indications.
467 //    chat( "AT%CPRI=1" );
468
469     // Make the modem send unsolicited reports at any time
470     // the "user is not typing".  i.e. don't intersperse unsolicited
471     // notifications and command echo as it will confuse QAtChat.
472     chat( "AT%CUNS=1" );
473
474     // Enable the reporting of timezone and local time information.
475     primaryAtChat()->registerNotificationType
476          ( "%CTZV:", this, SLOT(ctzv(QString)), true );
477     chat( "AT%CTZV=1" );
478
479
480 // Turn on call progress indications, with phone number information.
481     chat( "AT%CPI=2" );
482
483 // Enable the work-around for the bouncy rubber calypso problem
484     csqTimer = new QTimer( this );
485     csqTimer->setSingleShot( true );
486     connect ( csqTimer, SIGNAL(timeout()), this, SLOT(csqTimeOut()) );
487
488     // Setup the default text codec to UCS2 for none English langs
489     setDefaultCharset(DEFAULT_CHARSET);
490     chat( "AT+CSCS=\""+ DEFAULT_CHARSET + "\"" );
491
492     // Turn on status notification messages for finding out when
493     // the SIM/phonebook/SMS is ready to use.
494     primaryAtChat()->registerNotificationType
495         ( "%CSTAT:", this, SLOT(cstatNotification(QString)) );
496     primaryAtChat()->chat( "AT%CSTAT=1" );
497 }
498
499 Ficgta01ModemService::~Ficgta01ModemService()
500 {
501     delete csqTimer;
502 }
503
504 void Ficgta01ModemService::initialize()
505 {
506     if ( !supports<QPinManager>() )
507         addInterface( new Ficgta01PinManager( this ) );
508
509     if ( !supports<QPhoneBook>() ) {
510         m_phoneBook = new Ficgta01PhoneBook( this );
511         addInterface( m_phoneBook );
512     }
513
514
515     if ( !supports<QBandSelection>() )
516         addInterface( new Ficgta01BandSelection( this ) );
517
518     if ( !supports<QSimInfo>() ) {
519         QModemSimInfo* simInfo = new QModemSimInfo( this );
520         simInfo->setSimNotInsertedReason( QModemSimInfo::Reason_SimFailure );
521         addInterface( simInfo );
522     }
523
524     if ( !callProvider() )
525         setCallProvider( new Ficgta01CallProvider( this ) );
526
527    if ( !supports<QVibrateAccessory>() ) {
528         m_vibratorService = new Ficgta01VibrateAccessory( this );
529         addInterface( m_vibratorService );
530     }
531
532     if ( !supports<QCallVolume>() )
533         addInterface( new Ficgta01CallVolume(this));
534
535     if ( !supports<QPreferredNetworkOperators>() )
536         addInterface( new Ficgta01PreferredNetworkOperators(this));
537
538     // CBMs create an issue on suspend/resume disable for now #1530
539     if ( !supports<QCellBroadcast>() )
540         addInterface( new DummyCellBroadcast(this) );
541
542
543    QModemService::initialize();
544 }
545
546 void Ficgta01ModemService::csq( const QString& msg )
547 {
548     // Automatic signal quality update, in the range 0-31.
549     if ( msg.contains( QChar(',') ) ) {
550         uint posn = 6;
551         uint rssi = QAtUtils::parseNumber( msg, posn );
552
553         // An rssi of "99" indicates invalid (i.e. loss of signal).
554         // We wish to be careful about reporting that, because the
555         // silly calypso will report that each and every time it
556         // changes cell sites, followed almost immediately by a new
557         // (valid) signal report.  So we defer reporting the invalid
558         // signal, and wait to see if we get an new one first.
559         qLog(AtChat)<< "percentCSQ event, rssi: " << (int)(rssi);
560         if ( rssi == 99 ) {
561             if ( !csqTimer->isActive() )
562                 csqTimer->start( 8000 );  // 8 second one-shot timer
563         } else {
564             if ( csqTimer->isActive() ) {
565                 csqTimer->stop();
566                 indicators()->setSignalQuality( (int)rssi, 31 );
567             }
568         }
569     }
570 }
571
572 void Ficgta01ModemService::csqTimeOut()
573 {
574     // Timeout on a signal quality notification, it must have been real
575     qLog(AtChat)<< "percentCSQ timer expired; reporting loss";
576     indicators()->setSignalQuality( -1, 31 );
577 }
578
579
580 void Ficgta01ModemService::firstCsqQuery()
581 {
582     // Perform an initial AT%CSQ? which should cause the modem to
583     // respond with a %CSQ notification.  This is needed to shut
584     // off AT+CSQ polling in qmodemindicators.cpp when the modem is
585     // quiet and not sending periodic %CSQ notifications at startup.
586     chat( "AT%CSQ?" );
587 }
588
589 void Ficgta01ModemService::ctzv( const QString& msg )
590 {
591     // Timezone information from the network.  Format is "yy/mm/dd,hh:mm:ss+/-tz".
592     // There is no dst indicator according to the spec, but we parse an extra
593     // argument just in case future modem firmware versions fix this oversight.
594     // If there is no extra argument, the default value of zero will be used.
595     uint posn = 7;
596     QString time = QAtUtils::nextString( msg, posn );
597     int dst = ((int)QAtUtils::parseNumber( msg, posn )) * 60;
598     int zoneIndex = time.length();
599     while ( zoneIndex > 0 && time[zoneIndex - 1] != QChar('-') &&
600             time[zoneIndex - 1] != QChar('+') )
601         --zoneIndex;
602     int zoneOffset;
603     if ( zoneIndex > 0 && time[zoneIndex - 1] == QChar('-') ) {
604         zoneOffset = time.mid(zoneIndex - 1).toInt() * 15;
605     } else if ( zoneIndex > 0 && time[zoneIndex - 1] == QChar('+') ) {
606         zoneOffset = time.mid(zoneIndex).toInt() * 15;
607     } else {
608         // Unknown timezone information.
609         return;
610     }
611     QString timeString;
612     if (zoneIndex > 0)
613         timeString = time.mid(0, zoneIndex - 1);
614     else
615         timeString = time;
616     QDateTime t = QDateTime::fromString(timeString, "yy/MM/dd,HH:mm:ss");
617     if (!t.isValid())
618         t = QDateTime::fromString(timeString, "yyyy/MM/dd,HH:mm:ss"); // Just in case.
619     QDateTime utc = QDateTime(t.date(), t.time(), Qt::UTC);
620     utc = utc.addSecs(-zoneOffset * 60);
621     indicators()->setNetworkTime( utc.toTime_t(), zoneOffset, dst );
622 }
623
624 void Ficgta01ModemService::configureDone( bool ok )
625 {
626     supportsStk = ok;
627 }
628
629  void Ficgta01ModemService::reset()
630  {
631       qLog(AtChat)<<" Ficgta01ModemService::reset()";
632
633 //     // Turn on "%CNAP" notifications, which supply the caller's
634 //     // name on an call.  Only supported on some networks.
635      chat( "AT%CNAP=1" );
636
637      //begin really ugky hack
638      QSettings cfg("Trolltech", "PhoneProfile");
639      cfg.beginGroup("Profiles");
640
641     if( !cfg.value("PlaneMode",false).toBool()) {
642          chat("AT%NRG=0"); //force auto operations
643      }
644
645     // Set a default CBM state, disable all, setChannels on QCellBroadcast
646     // would enable them again. This should mean no (empty) CBM is accepted.
647     chat("AT+CSCB=0");
648
649 //      chat("AT%COPS=0");
650
651 }
652
653 void Ficgta01ModemService::sendSuspendDone()
654 {
655     suspendDone();
656 }
657
658 void Ficgta01ModemService::suspend()
659 {
660     qLog(AtChat)<<" Ficgta01ModemService::suspend()";
661     // Turn off cell id information on +CREG and +CGREG as it will
662     // cause unnecessary wakeups when moving between cells.
663     chat( "AT+CREG=0" );
664     chat( "AT+CGREG=0" );
665
666     // Don't need these, just clutter (nothing uses these
667     // proprietary commands yet -- MJW)
668     //chat( "AT%CREG=0" );
669     //chat( "AT%CGREG=0" );
670
671     // Turn off cell broadcast location messages.
672
673     // Make sure the (useless) CIEV notifications are disabled too.
674     chat( "AT+CMER=0,0,0,0,0" );
675
676     chat( "AT%CSQ=0", this, SLOT(sendSuspendDone()) );
677 }
678
679 void Ficgta01ModemService::wake()
680 {
681     qLog(AtChat)<<" Ficgta01ModemService::wake()";
682
683 //reset modem
684
685 //  chat( "AT%CWUP=1" );
686     chat("\r");
687     chat("ATE0\r");
688     // Turn cell id information back on.
689      chat( "AT+CREG=2" );
690      chat( "AT+CGREG=2" );
691
692      //   chat( "AT%CREG=2" );
693      //   chat( "AT%CGREG=2" );
694     // Turn cell broadcast location messages back on again.
695
696     // Re-enable signal quality notifications when the system wakes up again.
697    // Turn on dynamic signal quality notifications.
698     chat( "AT%CSQ=1" );
699
700     wakeDone();
701 }
702
703 // Modem state notification. It will tell us once the SIM is ready
704 // to give us access to the phonebook and SMS. To delay certain access
705 // we will also send simready from here...
706 void Ficgta01ModemService::cstatNotification( const QString& msg )
707 {
708     // %CSTAT: PHB, 0
709     // %CSTAT: <entity>,<status>
710     // PHB (phone book)
711     // SMS
712     // RDY (Ready when both PHB and SMS have reported they are ready)
713     QString entity = msg.mid(8, 3);
714     uint status = msg.mid(13).toInt();
715
716     // We are already ready, ignore
717     if (m_phoneBookIsReady && m_smsIsReady)
718         return;
719
720     if (entity == "PHB")
721         m_phoneBookIsReady = status;
722     else if (entity == "SMS")
723         m_smsIsReady = status;
724     else if (entity == "RDY") {
725         m_smsIsReady = status;
726         m_phoneBookIsReady = status;
727     }
728
729     if  (m_smsIsReady && m_phoneBookIsReady) {
730         post("simready"); 
731
732         if (m_phoneBook)
733             m_phoneBook->sendPhoneBooksReady();
734     }
735 }
736
737
738 // Modem dead detection for https://docs.openmoko.org/trac/ticket/1192
739 // The modem will not work with us anymore. We would need to make the AT
740 // command queue to freeze, fail all commands, we would need to reset the
741 // modem, force reinit (send the commands from all the c'tors again), make sure
742 // every application logic gets restarted. This will be a major effort. So the
743 // 2nd best thing is to detect the error and inform the user that he should
744 // restart his device. Currently I have no idea how often the error occurs
745 // and assume that it is not often at all.
746 //
747 // We do something unusual and open a QWidget from this plugin. This is okay
748 // as we will have a QtopiaApplication for the QCOP communication anyway. So
749 // there will be a GUI connection.
750 void Ficgta01ModemService::modemDied()
751 {
752     static bool claimedDead = false;
753     if (claimedDead)
754         return;
755
756     claimedDead = true;
757
758     if (m_vibratorService)
759         m_vibratorService->setVibrateNow(true);
760     QMessageBox::information(0, tr("Modem Died"),
761                              tr("The firmware of the modem appears to have crashed. "
762                                 "No further interaction with the modem will be possible. "
763                                 "Please restart the device."), QMessageBox::Ok);
764     if (m_vibratorService)
765         m_vibratorService->setVibrateNow(false);
766 }
767
768 void Ficgta01ModemService::echoCancellation(QAtChat * atChat)
769 {
770     Ficgta01ModemHiddenFeatures* hs = new Ficgta01ModemHiddenFeatures(atChat);
771     atChat->chat( "AT@ST=\"-26\"" ); // audio side tone: set to minimum
772     atChat->chat( "AT%N028B" );      // Long Echo Cancellation: active, -6db
773     atChat->chat( "AT%N0125" );      // Noise reduction: active, -6db
774 }
775
776  Ficgta01VibrateAccessory::Ficgta01VibrateAccessory
777         ( QModemService *service )
778     : QVibrateAccessoryProvider( service->service(), service )
779 {
780     setSupportsVibrateOnRing( true );
781     setSupportsVibrateNow( false );
782 }
783
784 Ficgta01VibrateAccessory::~Ficgta01VibrateAccessory()
785 {
786 }
787
788 void Ficgta01VibrateAccessory::setVibrateOnRing( const bool value )
789 {
790     qLog(AtChat) << __FUNCTION__ << value;
791     setVibrateNow(value);
792 }
793
794 void Ficgta01VibrateAccessory::setVibrateNow( const bool value )
795 {
796     qLog(AtChat) << __FUNCTION__ << value;
797     QString cmd;
798
799     if ( value ) { //turn on
800         QFile trigger( "/sys/class/leds/neo1973:vibrator/trigger");
801         trigger.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
802         QTextStream out(&trigger);
803         out<<"timer";
804         trigger.close();
805
806         QFile delayOn( "/sys/class/leds/neo1973:vibrator/delay_on");
807         delayOn.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
808         QTextStream out1(&delayOn);
809         out1<<"500";
810         delayOn.close();
811
812         QFile delayOff("/sys/class/leds/neo1973:vibrator/delay_off");
813         delayOff.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
814         QTextStream out2(&delayOff);
815         out2<<"1000";
816         delayOff.close();
817
818     } else { //turn off
819         QFile trigger( "/sys/class/leds/neo1973:vibrator/trigger");
820         trigger.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate);
821         QTextStream out(&trigger);
822         out<<"none";
823         trigger.close();
824     }
825
826     QVibrateAccessoryProvider::setVibrateNow( value );
827 }
828
829
830 Ficgta01CallVolume::Ficgta01CallVolume(Ficgta01ModemService *service)
831     : QModemCallVolume(service)
832 {
833    this->service = service;
834
835     QtopiaIpcAdaptor *adaptor
836             = new QtopiaIpcAdaptor( "QPE/Ficgta01Modem", this );
837
838     // Set default, then query real value
839     setSpeakerVolumeRange(0, 5);
840
841     service->primaryAtChat()->chat("AT+CLVL=?", this, SLOT(volumeLevelRangeQueryDone(bool,QAtResult)) );
842     service->primaryAtChat()->chat("AT+CLVL?", this, SLOT(volumeLevelQueryDone(bool,QAtResult)) );
843
844     QtopiaIpcAdaptor::connect
845             ( adaptor, MESSAGE(setSpeakerVolumeRange(int, int)),
846               this, SLOT(setSpeakerVolumeRange(int,int)) );
847
848     QtopiaIpcAdaptor::connect
849             ( adaptor, MESSAGE(setMicVolumeRange(int, int)),
850               this, SLOT(setMicVolumeRange(int,int)) );
851
852     QtopiaIpcAdaptor::connect
853             ( adaptor, MESSAGE(setOutputVolume(int)),
854               this, SLOT(setSpeakerVolume(int)) );
855     QtopiaIpcAdaptor::connect
856             ( adaptor, MESSAGE(setMicVolume(int)),
857               this, SLOT(setMicrophoneVolume(int)) );
858
859 }
860
861 Ficgta01CallVolume::~Ficgta01CallVolume()
862 {
863
864 }
865
866 bool Ficgta01CallVolume::hasDelayedInit() const
867 {
868     return true;
869 }
870
871 void Ficgta01CallVolume::setSpeakerVolume( int volume )
872 {
873     int volumeLevel;
874     int boundedVolume = qBound(value("MinimumSpeakerVolume").toInt(), volume,
875                                value("MaximumSpeakerVolume").toInt());
876
877     setValue( "SpeakerVolume", boundedVolume );
878     volumeLevel = virtual2real(boundedVolume);
879     if (currentVolumeLevel == volumeLevel)
880         return;
881     currentVolumeLevel = volumeLevel;
882     service->primaryAtChat()->chat("AT+CLVL="+QString::number(currentVolumeLevel));
883     emit speakerVolumeChanged(boundedVolume);
884 }
885
886 void Ficgta01CallVolume::setMicrophoneVolume( int volume )
887 {
888     int boundedVolume = qBound(value("MinimumMicrophoneVolume").toInt(), volume,
889                                value("MaximumMicrophoneVolume").toInt());
890
891     setValue( "MicrophoneVolume", boundedVolume );
892     emit microphoneVolumeChanged(boundedVolume);
893 }
894
895
896 void Ficgta01CallVolume::setSpeakerVolumeRange(int min,int max)
897 {
898     setValue( "MinimumSpeakerVolume", min );
899     setValue( "MaximumSpeakerVolume", max );
900 }
901
902 void Ficgta01CallVolume::setMicVolumeRange(int min,int max)
903 {
904     setValue( "MinimumMicrophoneVolume", min );
905     setValue( "MaximumMicrophoneVolume", max );
906 }
907
908 void Ficgta01CallVolume::volumeLevelRangeQueryDone(bool ok,const QAtResult & result)
909 {
910     if (!ok)
911         return;
912     QAtResultParser parser( result );
913     if ( parser.next( "+CLVL:" ) ) {
914         QList<QAtResultParser::Node> nodes = parser.readList();
915         if (!nodes.isEmpty()) {
916             if (nodes.at(0).isRange()) {
917                 minVolumeLevel = nodes.at(0).asFirst();
918                 maxVolumeLevel = nodes.at(0).asLast();
919             }
920         }
921     }
922 }
923
924 void Ficgta01CallVolume::volumeLevelQueryDone(bool ok,const QAtResult & result) 
925 {
926     int volumeLevel;
927     if (!ok)
928         return;
929     QAtResultParser parser( result );
930     if ( parser.next( "+CLVL:" )) {
931         volumeLevel = (int) parser.readNumeric();
932         currentVolumeLevel = volumeLevel;
933         setSpeakerVolume(real2virtual(currentVolumeLevel));
934     }
935 }
936
937 int Ficgta01CallVolume::virtual2real(int volume)
938 {
939     int ans;
940     int min = value("MinimumSpeakerVolume").toInt();
941     int max = value("MaximumSpeakerVolume").toInt();
942     if (minVolumeLevel >= maxVolumeLevel)
943         return 0;
944     if (min >= max)
945         return 0;
946     ans = volume * (maxVolumeLevel - minVolumeLevel);
947     ans = ans / (max - min);
948     return ans;
949 }
950
951 int Ficgta01CallVolume::real2virtual(int volumeLevel)
952 {
953     int ans;
954     int min = value("MinimumSpeakerVolume").toInt();
955     int max = value("MaximumSpeakerVolume").toInt();
956     if (minVolumeLevel >= maxVolumeLevel)
957         return 0;
958     if (min >= max)
959         return 0;
960     ans = volumeLevel * (max - min);
961     ans = ans / (maxVolumeLevel - minVolumeLevel);
962     return ans;
963 }
964
965 Ficgta01PreferredNetworkOperators::Ficgta01PreferredNetworkOperators( QModemService *service )
966     : QModemPreferredNetworkOperators( service )
967 {
968     // We have to delete an entry before we can write operator details into it.
969     setDeleteBeforeUpdate( true );
970
971     // Quote operator numbers when modifying preferred operator entries.
972     setQuoteOperatorNumber( true );
973 }
974
975 Ficgta01PreferredNetworkOperators::~Ficgta01PreferredNetworkOperators()
976 {
977 }
978
979 Ficgta01ModemHiddenFeatures::Ficgta01ModemHiddenFeatures(QAtChat* atChat) :
980     atPrefix( "AT%N" )
981 {
982     m_atChat = atChat;
983 }
984
985 Ficgta01ModemHiddenFeatures::~Ficgta01ModemHiddenFeatures()
986 {
987 }
988
989 void Ficgta01ModemHiddenFeatures::sendHiddenFeatureCommand(int code)
990 {
991     m_atChat->chat(atPrefix + QString::number(code, 16).toUpper().rightJustified(4, '0'));
992 }
993
994 void Ficgta01ModemHiddenFeatures::enableAEC(int type, bool longAEC = true)
995 {
996     if (type <= 0 && type >= -18 && (-type) % 6 == 0) {
997         int code = 0x0083 + (((-type) / 6) * 0x8);
998         
999         if (longAEC)
1000             code += 0x0200;
1001             
1002          sendHiddenFeatureCommand(code);
1003     }
1004 }
1005
1006 void Ficgta01ModemHiddenFeatures::enableNoiseReduction(int type)
1007 {
1008     if (type <= 0 && type >= -18 && (-type) % 6 == 0) {
1009         int code = 0x0105 + (((-type) / 6) * 0x20);
1010             
1011         sendHiddenFeatureCommand(code);
1012     }
1013 }
1014
1015 void Ficgta01ModemHiddenFeatures::enableNoiseReductionAEC()
1016 {
1017     sendHiddenFeatureCommand(0x0187);
1018 }
1019
1020 void Ficgta01ModemHiddenFeatures::disableNoiseReductionAEC()
1021 {
1022     sendHiddenFeatureCommand(0x0001);
1023 }
1024