📄 gsm0710.c
字号:
/*** GSM 07.10 Implementation with User Space Serial Ports** Copyright (C) 2003 Tuukka Karvonen <tkarvone@iki.fi>** Version 1.0 October 2003** 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 2* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.** Modified November 2004 by David Jander <david@protonic.nl>* - Hacked to use Pseudo-TTY's instead of the (obsolete?) USSP driver.* - Fixed some bugs which prevented it from working with Sony-Ericsson modems* - Seriously broke hardware handshaking.* - Changed commandline interface to use getopts:** Modified January 2006 by Tuukka Karvonen <tkarvone@iki.fi> and * Antti Haapakoski <antti.haapakoski@iki.fi>* - Applied patches received from Ivan S. Dubrov* - Disabled possible CRLF -> LFLF conversions in serial port initialization* - Added minicom like serial port option setting if baud rate is configured.* This was needed to get the options right on some platforms and to * wake up some modems.* - Added possibility to pass PIN code for modem in initialization* (Sometimes WebBox modems seem to hang if PIN is given on a virtual channel)* - Removed old code that was commented out* - Added support for Unix98 scheme pseudo terminals (/dev/ptmx)* and creation of symlinks for slave devices* - Corrected logging of slave port names* - at_command looks for AT/ERROR responses with findInBuf function instead* of strstr function so that incoming carbage won't confuse it** Modified March 2006 by Tuukka Karvonen <tkarvone@iki.fi>* - Added -r option which makes the mux driver to restart itself in case* the modem stops responding. This should make the driver more fault* tolerant. * - Some code restructuring that was required by the automatic restarting* - buffer.c to use syslog instead of PDEBUG* - fixed open_pty function to grant right for Unix98 scheme pseudo* terminals even though symlinks are not in use** New Usage:* gsmMuxd [options] <pty1> <pty2> ...** To see the options, type:* ./gsmMuxd -h*/#ifdef HAVE_CONFIG_H#include <config.h>#endif#ifndef _GNU_SOURCE// To get ptsname grandpt and unlockpt definitions from stdlib.h#define _GNU_SOURCE#endif#include <features.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <stdio.h>#include <time.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <termios.h>#include <sys/time.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <string.h>#include <paths.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/wait.h>//syslog#include <syslog.h>#include "buffer.h"#include "gsm0710.h"#define DEFAULT_NUMBER_OF_PORTS 3#define WRITE_RETRIES 5/********************************************************** modified by Huangsw 2008-02-29 : change from 32 to 4**********************************************************/#define MAX_CHANNELS 4//vitorio, only to use if necessary (don't ask in what i was thinking when i wrote this)#define TRUE 1#define FALSE 0#define UNKNOW_MODEM 0#define MC35 1#define GENERIC 2// Defines how often the modem is polled when automatic restarting is enabled// The value is in seconds#define POLLING_INTERVAL 5#define MAX_PINGS 4static volatile int terminate = 0;static int terminateCount = 0;static char* devSymlinkPrefix = 0;static int *ussp_fd;static int serial_fd;static Channel_Status *cstatus;static int max_frame_size = 31; // The limit of Sony-Ericsson GM47static int wait_for_daemon_status = 0;static GSM0710_Buffer *in_buf; // input bufferstatic int _debug = 0;static pid_t the_pid;int _priority;int _modem_type;static char *serportdev;static int pin_code = 0;static char *ptydev[MAX_CHANNELS];static int numOfPorts;static int maxfd;static int baudrate = 0;static int *remaining;static int faultTolerant = 0;static int restart = 0;/* The following arrays must have equal length and the values must * correspond. */static int baudrates[] = { 0, 9600, 19200, 38400, 57600, 115200, 230400, 460800};static speed_t baud_bits[] = { 0, B9600, B19200, B38400, B57600, B115200, B230400, B460800 };#if 0/* Opens USSP port for use.** PARAMS:* port - port number* RETURNS* file descriptor or -1 on error*/int ussp_open(int port){ int fd; char name[] = "ser0\0"; name[3] = (char) (0x30 + port); PDEBUG("Open serial port %s ", name); fd = open(name, O_RDWR | O_NONBLOCK); PDEBUG("done.\n"); return fd;}#endif/** * Returns success, when an ussp is opened. */int ussp_connected(int port){#if 0 struct ussp_operation op; op.op = USSP_OPEN_RESULT; if (cstatus[port + 1].opened) op.arg = 0; else op.arg = -1; op.len = 0; write(ussp_fd[port], &op, sizeof(op)); PDEBUG("USSP port %d opened.\n", port); return 0;#else return 0;#endif}/** Writes a frame to a logical channel. C/R bit is set to 1.* Doesn't support FCS counting for UI frames.** PARAMS:* channel - channel number (0 = control)* input - the data to be written* count - the length of the data* type - the type of the frame (with possible P/F-bit)** RETURNS:* number of characters written*/int write_frame(int channel, const char *input, int count, unsigned char type){ // flag, EA=1 C channel, frame type, length 1-2 unsigned char prefix[5] = { F_FLAG, EA | CR, 0, 0, 0 }; unsigned char postfix[2] = { 0xFF, F_FLAG }; int prefix_length = 4, c; if(_debug) syslog(LOG_DEBUG,"send frame to ch: %d \n", channel); // EA=1, Command, let's add address prefix[1] = prefix[1] | ((63 & (unsigned char) channel) << 2); // let's set control field prefix[2] = type; // let's not use too big frames count = min(max_frame_size, count); // length if (count > 127) { prefix_length = 5; prefix[3] = ((127 & count) << 1); prefix[4] = (32640 & count) >> 7; } else { prefix[3] = 1 | (count << 1); } // CRC checksum postfix[0] = make_fcs(prefix + 1, prefix_length - 1); c = write(serial_fd, prefix, prefix_length); if (c != prefix_length) { if(_debug) syslog(LOG_DEBUG,"Couldn't write the whole prefix to the serial port for the virtual port %d. Wrote only %d bytes.", channel, c); return 0; } if (count > 0) { c = write(serial_fd, input, count); if (count != c) { if(_debug) syslog(LOG_DEBUG,"Couldn't write all data to the serial port from the virtual port %d. Wrote only %d bytes.\n", channel, c); return 0; } } c = write(serial_fd, postfix, 2); if (c != 2) { if(_debug) syslog(LOG_DEBUG,"Couldn't write the whole postfix to the serial port for the virtual port %d. Wrote only %d bytes.", channel, c); return 0; } return count;}/* Handles received data from ussp device.** This function is derived from a similar function in RFCOMM Implementation* with USSPs made by Marcel Holtmann.** PARAMS:* buf - buffer, which contains received data* len - the length of the buffer* port - the number of ussp device (logical channel), where data was* received* RETURNS:* the number of remaining bytes in partial packet*/int ussp_recv_data(unsigned char *buf, int len, int port){#if 0 int n, written; unsigned char pkt_buf[4096]; struct ussp_operation *op = (struct ussp_operation *) pkt_buf, *top; struct termios *tiosp; int i; // size unsigned char msc[5] = { CR | C_MSC, 0x5, 0, 0, 1 }; PDEBUG( "(DEBUG) %s chamada\n", __FUNCTION__); memcpy(pkt_buf, buf, len); n = len; op = (struct ussp_operation *) pkt_buf; for (top = op; /* check for partial packet - first, make sure top->len is actually in pkt_buf */ ((char *) top + sizeof(struct ussp_operation) <= ((char *) op) + n) && ((char *) top + sizeof(struct ussp_operation) + top->len <= ((char *) op) + n); top = (struct ussp_operation *) (((char *) top) + top->len + sizeof(struct ussp_operation))) { switch (top->op) { case USSP_OPEN: ussp_connected(port); break; case USSP_CLOSE: PDEBUG("Close ussp port %d\n", port); break; case USSP_WRITE: written = 0; i = 0; // try to write 5 times while ((written += write_frame(port + 1, top->data + written, top->len - written, UIH)) != top->len && i < WRITE_RETRIES) { i++; } if (i == WRITE_RETRIES) { PDEBUG("Couldn't write data to channel %d. Wrote only %d bytes, when should have written %ld.\n", (port + 1), written, top->len); } break; case USSP_SET_TERMIOS: tiosp = (struct termios *) (top + 1); if ((tiosp->c_cflag & CBAUD) == B0 && (cstatus[(port + 1)].v24_signals & S_RTC) > 0) { // drop DTR PDEBUG("Drop DTR.\n"); msc[2] = 3 | ((63 & (port + 1)) << 2); msc[3] = cstatus[(port + 1)].v24_signals & ~S_RTC; cstatus[(port + 1)].v24_signals = msc[3]; write_frame(0, msc, 4, UIH); } else if ((tiosp->c_cflag & CBAUD) != B0 && (cstatus[(port + 1)].v24_signals & S_RTC) == 0) { // DTR up PDEBUG("DTR up.\n"); msc[2] |= ((63 & (port + 1)) << 2); msc[3] = cstatus[(port + 1)].v24_signals | S_RTC; cstatus[(port + 1)].v24_signals = msc[3]; write_frame(0, msc, 4, UIH); }#ifdef DEBUG PDEBUG("Set termios for ussp port %d\n", port); PDEBUG("\tinput mode flags: 0x%04x\n", tiosp->c_iflag); PDEBUG("\toutput mode flags: 0x%04x\n", tiosp->c_oflag); PDEBUG("\tcontrol mode flags: 0x%04x\n", tiosp->c_cflag); PDEBUG("\tlocal mode flags: 0x%04x\n", tiosp->c_lflag); PDEBUG("\tline discipline: 0x%02x\n", tiosp->c_line); PDEBUG("\tcontrol characters: "); for (i = 0; i < NCCS; i++) PDEBUG("0x%02x ", tiosp->c_cc[i]); PDEBUG("\n"); PDEBUG("\tinput speed: 0x%02x (%i)\n", tiosp->c_ispeed, tiosp->c_ispeed); PDEBUG("\toutput speed: 0x%02x (%i)\n", tiosp->c_ospeed, tiosp->c_ospeed);#endif break; case USSP_MSC: PDEBUG("Modem signal change\n"); msc[2] = 3 | ((63 & (port + 1)) << 2); msc[3] = S_DV; if ((top->arg & USSP_DTR) == USSP_DTR) { msc[3] |= S_RTC; PDEBUG("RTC\n"); } if ((top->arg & USSP_RTS) == USSP_RTS) { msc[3] |= S_RTR; PDEBUG("RTR\n"); } else { msc[3] |= S_FC; PDEBUG("FC\n"); } cstatus[(port + 1)].v24_signals = msc[3]; // save the signals write_frame(0, msc, 4, UIH); break; default: PDEBUG("Unknown code: %d\n", top->op); break; } } /* remaining bytes in partial packet */ return ((char *) op + n) - (char *) top;#else int written = 0; int i = 0; int last = 0; // try to write 5 times while (written != len && i < WRITE_RETRIES) { last = write_frame(port + 1, buf + written, len - written, UIH); written += last; if (last == 0) { i++; } } if (i == WRITE_RETRIES) { if(_debug) syslog(LOG_DEBUG,"Couldn't write data to channel %d. Wrote only %d bytes, when should have written %ld.\n", (port + 1), written, (long)len); } return 0;#endif}int ussp_send_data(unsigned char *buf, int n, int port){#if 0 struct ussp_operation *op; op = malloc(sizeof(struct ussp_operation) + n); op->op = USSP_READ; op->arg = 0; op->len = n; memcpy(op->data, buf, n); write(ussp_fd[port], op, sizeof(struct ussp_operation) + n); free(op);#else if(_debug) syslog(LOG_DEBUG,"send data to port virtual port %d\n", port); write(ussp_fd[port], buf, n);#endif return n;}// Returns 1 if found, 0 otherwise. needle must be null-terminated.// strstr might not work because WebBox sends garbage before the first OKint findInBuf(char* buf, int len, char* needle) { int i; int needleMatchedPos=0; if (needle[0] == '\0') { return 1; } for (i=0;i<len;i++) { if (needle[needleMatchedPos] == buf[i]) { needleMatchedPos++; if (needle[needleMatchedPos] == '\0') { // Entire needle was found return 1; } } else { needleMatchedPos=0; } } return 0;}/* Sends an AT-command to a given serial port and waits* for reply.** PARAMS:* fd - file descriptor* cmd - command* to - how many microseconds to wait for response (this is done 100 times)* RETURNS:* 1 on success (OK-response), 0 otherwise*/int at_command(int fd, char *cmd, int to){ fd_set rfds; struct timeval timeout; unsigned char buf[1024]; int sel, len, i; int returnCode = 0; int wrote = 0; if(_debug) syslog(LOG_DEBUG, "is in %s\n", __FUNCTION__); wrote = write(fd, cmd, strlen(cmd)); if(_debug) syslog(LOG_DEBUG, " wrote %d \n", wrote); tcdrain(fd); sleep(1); //memset(buf, 0, sizeof(buf)); //len = read(fd, buf, sizeof(buf)); for (i = 0; i < 100; i++) { FD_ZERO(&rfds); FD_SET(fd, &rfds); timeout.tv_sec = 0; timeout.tv_usec = to; if ((sel = select(fd + 1, &rfds, NULL, NULL, &timeout)) > 0) //if ((sel = select(fd + 1, &rfds, NULL, NULL, NULL)) > 0) { if (FD_ISSET(fd, &rfds)) { memset(buf, 0, sizeof(buf)); len = read(fd, buf, sizeof(buf)); if(_debug) syslog(LOG_DEBUG, " read %d bytes == %s\n", len, buf); //if (strstr(buf, "\r\nOK\r\n") != NULL) if (findInBuf(buf, len, "OK")) { returnCode = 1; break; } if (findInBuf(buf, len, "ERROR"))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -