// // anyRemote // a wi-fi or bluetooth remote for your PC. // // Copyright (C) 2006-2016 Mikhail Fedotov // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // // // AT+CKPD commands handling // #include #include #include #include #include #include #include "parse.h" #include "utils.h" #include "btio.h" #include "conf.h" #include "dispatcher.h" #include "peer.h" #include "pr_rfcomm.h" #include "pr_serial.h" #define PAUSE_TIME 1 extern char tmp[MAXMAXLEN]; static int useQuestionInCLCC = 0; char callerId[MAXLEN]; // // max should be >= 100 // int writeAtCommand(int fd, const char* command, char* answer, int max, int timeout, char* expect) { int count=0; int readcount; char tmp2[100]; int timeoutcounter=0; int found=0; if (fd < 0) { return 0; } logger(L_DBG,command); DEBUG2("writeAtCommand command >%s<", command); if (expect != NULL) { DEBUG2("writeAtCommand skip till >%s<", expect); } // send command if (command && command[0]) { write(fd,command,strlen(command)); tcdrain(fd); } if (max == 0) { return 0; } answer[0]=0; do { // try to read some bytes. usleep(100000); //write(1,".",1); timeoutcounter++; // read data readcount=read(fd,tmp2,sizeof(tmp2)-1); if (readcount<0) { readcount=0; } tmp2[readcount]=0; // add read bytes to the output buffer if (readcount) { //DEBUG2("writeAtCommand got reply >%s<", tmp2); strcat(answer,tmp2); count+=readcount; // if we have more time to read, check if we got already the expected string if ((timeoutcounter%s<\n", bytes); // initial timeout was 5 writeAtCommand(fd, bytes, answer, size, 40, expect); } int sendCMER(int fd, int onOff) { char answer[100]; char cmd[64]; char * cmer = getAT_CMER(onOff); if (cmer == NULL) { return EXIT_NOK; } strcpy(cmd,cmer); free(cmer); strcat(cmd,"\r"); //sendSyncCommand(cmd, answer, sizeof(answer), NULL); writeAtCommand (fd, cmd, answer, sizeof(answer), 10, NULL); if(!IS_OK(answer)) { DEBUG2("ERROR: AT_CMER SET >%s< -> %s\n", cmd, answer); return EXIT_NOK; } return EXIT_OK; } // Send AT+CKPD command static int sendCKPD(int fd, char *key) { char answer[1024]; char ckpd[MAXCKPDLEN]; if (key == NULL || key[0] == '\0') { logger(L_DBG,"Empty symbol in sendCKPD()"); return EXIT_NOK; } DEBUG2("sendCKPD >%s<", key); sprintf(ckpd,"%s\"%s\"\r", DEF_AT_CKPD, key); int model = getModel(); if (model == MODEL_SE || model == MODEL_SIEMENS) { // SE-K700, Siemens-S55 // If we send AT+CKPD=X // SE phone will replay // OK // +CKEV: X,1 // +CKEV: X,0 // And we need to filter out that CKEVs char expect[16]; strcpy(expect, DEF_CKEV); strcat(expect, " "); if (model == MODEL_SIEMENS) { strcat(expect, "\""); } strcat(expect, key); if (model == MODEL_SIEMENS) { strcat(expect, "\""); } strcat(expect, ",0"); // Wait button up event sendSyncCommand(fd, ckpd, answer, sizeof(answer), expect); //DEBUG2("Expect >%s<", expect); //DEBUG2("Got all >%s<", answer); // Filter out button up char *ptr = strstr(answer, expect); if(ptr == NULL) { logger(L_DBG,"cant find button up event"); } else { int z; int l = strlen(expect); for (z=0; z%s<", answer); } else { sendSyncCommand(fd, ckpd, answer, sizeof(answer), NULL); } parseCommand(PEER_ANY, answer); return EXIT_OK; } // // Should be used in AT mode only // int sendSeq(int fd, const char *seq) { if (getUseScreen() == 0 || seq == NULL) { return EXIT_OK; } DEBUG2("sendSeq() >%s<", seq); char* copy = strdup(seq); char *bufPtr = NULL; char *ckpd = strtok_r(copy," ",&bufPtr); while (ckpd != NULL) { //DEBUG2("NEXT sendSeq()->sendCKPD()>%s<", ckpd); if (strcmp(ckpd, PAUSE_STR)==0) { sleep(PAUSE_TIME); // Just wait a bit } else { sendCKPD(fd, ckpd); } ckpd = strtok_r(NULL," ",&bufPtr); } free(copy); return EXIT_OK; } static void parse_CLCC(char *clcc, char* dest) { // There are could be more than one call at time ... but here we will get then one-by-one // +CLCC: 1,,4,0,0,"",129, char *start ,*ptr; int commaCount = 0; ptr = clcc; while(ptr && commaCount < 5) { if (*ptr == ',') { commaCount++; } ptr++; } if (ptr == NULL) { // Call was finished strcpy(dest, "FINISHED"); return; } if (ptr && *ptr == '"') { ptr++; } start = ptr; while(ptr && *ptr != '"' && *ptr != ',') { ptr++; } if (ptr) { *ptr = '\0'; } if(start) { DEBUG2("parse_CLCC >%s<", start); } else { DEBUG2("parse_CLCC >NULL callerID<"); strcpy(dest, "NO CALLER ID"); return; } strcpy(dest, start); return; } int getClip(int fd, char* CallerID) { char clcc[MTEXTLEN]; char answer[1024]; char *ptr; int tryAnyway = 0; answer[0] = '\0'; logger(L_DBG, "getClip()"); // Some phones uses AT+CLCC?, some just AT+CLCC (Sagem, Siemens-S55, SE-K750) if (useQuestionInCLCC == 0) { //logger(L_DBG, "getClip() send AT+CLCC"); strcpy(clcc,DEF_AT_CLCC); strcat(clcc,"\r"); sendSyncCommand(fd, clcc, answer, sizeof(answer), NULL); if ((ptr = strstr(answer,DEF_CLCC)) != NULL) { parse_CLCC(ptr,CallerID); return EXIT_EXACT; } else if (strstr(answer,"OK") != NULL) { return EXIT_OK; } else if (strstr(answer,"ERR") != NULL) { logger(L_WARN, "Got ERROR on AT+CLCC. Will try AT+CLCC?"); useQuestionInCLCC = 1; // Ask phone again; but will never ask AT+CLCC more } else { tryAnyway = 1; // Ask phone again; to be sure } } if (useQuestionInCLCC == 1 || tryAnyway == 1) { tryAnyway = 0; //logger(L_DBG, "getClip() send AT+CLCC?"); strcpy(clcc,DEF_AT_CLCC); strcat(clcc,"?"); strcat(clcc,"\r"); sendSyncCommand(fd, clcc, answer, sizeof(answer), NULL); if ((ptr = strstr(answer,DEF_CLCC)) != NULL) { parse_CLCC(ptr,CallerID); return EXIT_EXACT; } else if (strstr(answer,"OK") != NULL) { return EXIT_OK; } else if (strstr(answer,"ERR") != NULL) { logger(L_ERR, "Got ERROR on AT+CLCC?"); return EXIT_NOK; } } DEBUG2("sendClip() unappropriate answer: >%s<", answer); return EXIT_NOK; } // CLIENT_RFCOMM / CLIENT_AT int setupAtConnection(ConnectInfo* peer, int fd) { char answer[1024]; char cmd[32]; answer[0] = '\0'; sendSyncCommand(fd, "ATZ\r", answer, sizeof(answer), NULL); //sendSyncCommand("AT\r", answer, sizeof(answer), NULL); // Do not care - it can fail after reconnect //if(!IS_OK(answer)) { // ERROR2("AT -> %s\n", answer) // return -1; //} sendSyncCommand(fd, "ATE0\r", answer, sizeof(answer), NULL); if(!IS_OK(answer)) { ERROR2("ATE0 -> %s\n", answer); } char* ptr = getCharset(); if (ptr) { sprintf(cmd,"%s\"%s\"\r", DEF_AT_CSCS, ptr); sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL); if(!IS_OK(answer)) { ERROR2("Can't set charset to %s\n", ptr); } free(ptr); } // Determine model sprintf(cmd,"%s\r", DEF_AT_CGMI); sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL); //Set model in conf. data setModel(answer); if (getModel() == MODEL_MOTOROLA) { // Motorola RIZR Z3 needs to set MODE=2 to allow AT+CLIP command // do not care about responce sendSyncCommand(fd, "AT+MODE=2\r", answer, sizeof(answer), NULL); } sprintf(cmd,"%s\r", DEF_AT_CLIP); sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL); if(!IS_OK(answer)) { ERROR2("Can't set CLIP ON\n"); } int ret = sendCMER(fd, CMER_ON); if(ret != EXIT_OK) { ERROR2("fails in set event reporting on"); return -1; } // Siemens S55 needs additional AT+CMEC=2 to make AT+CKPD works // not sure about other Siemens phones if (getModel() == MODEL_SIEMENS) { sprintf(cmd,"%s\r", DEF_AT_CMEC); sendSyncCommand(fd, cmd, answer, sizeof(answer), NULL); if(!IS_OK(answer)) { ERROR2("ON AT+CMEC\n"); } } // Will set global callerId [MAXLEN]; ret = getClip(fd, callerId); if(ret == EXIT_NOK) { // Got ERROR on AT+CLCC; probably phone does not supports this command printf("ERROR: fails in getClip\n"); if (peer->mode == CLIENT_RFCOMM) { _RfcommConnection* cn = (_RfcommConnection*) peer->connectionData; if (cn) { cn->useCallId = BOOL_NO; } } else if (peer->mode == CLIENT_AT) { _SerialConnection* cn = (_SerialConnection*) peer->connectionData; if (cn) { cn->useCallId = BOOL_NO; } } } return 1; } // // rfcomm peer reader // in case of incoming call try to get caller ID // int atRead(ConnectInfo* peer, char* buffer, int max) { if (! (peer->mode == CLIENT_RFCOMM || peer->mode == CLIENT_AT)) { ERROR2("[DS]: atRead() peer type mismatch"); return 0; } if (!peer->connectionData) return EXIT_NOK; _RfcommConnection* cnr = NULL; _SerialConnection* cns = NULL; int fd = -1; boolean_t useCallID = BOOL_NO; boolean_t hasCall = BOOL_NO; if (peer->mode == CLIENT_RFCOMM) { cnr = (_RfcommConnection*) peer->connectionData; fd = cnr->fileDescriptor; useCallID = cnr->useCallId; hasCall = cnr->hasActiveCall; } else if (peer->mode == CLIENT_AT) { cns = (_SerialConnection*) peer->connectionData; fd = cns->fileDescriptor; useCallID = cns->useCallId; hasCall = cns->hasActiveCall; } if (fd < 0) { ERROR2("[DS]: atRead() not connected"); return 0; } int nbytes = read(fd, buffer, max); if (nbytes < 0) { // Read error ERROR2("[DS]: atRead() error %d",errno); errnoDebug("[DS]: atRead() read ",errno); // testing debug } else if (nbytes == 0) { DEBUG2("[DS]: atRead() EOF"); } else { buffer[nbytes] = '\0'; if (strstr(buffer, DEF_RING) != NULL && !hasCall) { // Incoming call logger(L_INF,"[DS]: Incoming call"); // This event sent periodically until used answered a call if (cnr) { cnr->hasActiveCall = BOOL_YES; } else if (cns) { cns->hasActiveCall = BOOL_YES; } if (useCallID) { char callerId[MAXLEN]; int ret = getClip(fd, callerId); if (ret == EXIT_EXACT) { // Active call exists, got ID strcat(buffer, "Msg:InCall(,"); strcat(buffer, callerId); strcat(buffer, ")\r"); nbytes = strlen(buffer); } } else { strcat(buffer, "Msg:InCall(,)\r"); nbytes = strlen(buffer); } } } //DEBUG2("[DS]: atRead() got >%s< %d", buffer, nbytes); return nbytes; }