📄 virtmodem.c
字号:
/* $Id: virtmodem.c,v 1.41 2003/10/23 17:45:41 bozo Exp $ G N O K I I A Linux/Unix toolset and driver for Nokia mobile phones. This file is part of gnokii. Gnokii 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. Gnokii 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 gnokii; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Copyright (C) 1999, 2000 Hugh Blemings & Pavel Jan韐 ml. This file provides a virtual modem interface to the GSM phone by calling code in gsm-api.c, at-emulator.c and datapump.c. The code here provides the overall framework and coordinates switching between command mode (AT-emulator) and "online" mode where the data pump code translates data from/to the GSM handset and the modem data/fax stream.*/#include "config.h"/* * BEWARE! UGLY HACK! * We will define _XOPEN_SOURCE but it will disable the u_char, u_short, * u_int and u_long types on OpenBSD. It seems only the OpenBSD is affected, * so this is the workaround - bozo */#ifdef __OpenBSD__# include <sys/types.h># define HAVE_MSGHDR_MSG_CONTROL 1#endif/* same for FreeBSD */#ifdef __FreeBSD__# include <sys/types.h># undef __BSD_VISIBLE# define __BSD_VISIBLE 1#endif/* This is the correct way to include stdlib with _XOPEN_SOURCE = 500 defined. * Needed for clean unlockpt() declaration. * msghdr structure in Solaris depends on _XOPEN_SOURCE = 500 too. - bozo */#define _XOPEN_SOURCE 500#include <stdio.h>#include <errno.h>#include <fcntl.h>#include <signal.h>#include <termios.h>#include <grp.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#ifdef HAVE_SYS_TIME_H# include <sys/time.h>#endif#include <unistd.h>#include <sys/socket.h>#include <sys/wait.h>#include <sys/uio.h>#include <sys/param.h>#include "compat.h"#include "misc.h"#include "gnokii.h"#include "data/at-emulator.h"#include "data/datapump.h"#include "device.h"/* Defines */#ifndef AF_LOCAL # ifdef AF_UNIX# define AF_LOCAL AF_UNIX# else# error AF_LOCAL not defined# endif#endif/* Prototypes */static int VM_PtySetup(char *bindir);static gn_error VM_GSMInitialise(struct gn_statemachine *sm);/* Global variables */extern bool GTerminateThread;int ConnectCount;bool CommandMode;/* Local variables */static int PtyRDFD; /* File descriptor for reading and writing to/from */static int PtyWRFD; /* pty interface - only different in debug mode. */static bool UseSTDIO; /* Use STDIO for debugging purposes instead of pty */struct vm_queue queue;/* If initialised in debug mode, stdin/out is used instead of ptys for interface. */bool gn_vm_initialise(const char *iname, char *bindir, bool debug_mode, bool GSMInit){ static struct gn_statemachine State; sm = &State; queue.n = 0; queue.head = 0; queue.tail = 0; CommandMode = true; if (debug_mode == true) { UseSTDIO = true; } else { UseSTDIO = false; } if (GSMInit) { dprintf("Initialising GSM\n"); if (!gn_cfg_phone_load(iname, sm)) return false; if ((VM_GSMInitialise(sm) != GN_ERR_NONE)) { fprintf (stderr, _("gn_vm_initialise - VM_GSMInitialise failed!\n")); return (false); } } GSMInit = false; if (VM_PtySetup(bindir) < 0) { fprintf (stderr, _("gn_vm_initialise - VM_PtySetup failed!\n")); return (false); } if (gn_atem_initialise(PtyRDFD, PtyWRFD, sm) != true) { fprintf (stderr, _("gn_vm_initialise - gn_atem_initialise failed!\n")); return (false); } if (dp_Initialise(PtyRDFD, PtyWRFD) != true) { fprintf (stderr, _("gn_vm_Initialise - dp_Initialise failed!\n")); return (false); } return (true);}void gn_vm_loop(void){ fd_set rfds; struct timeval tv; int res; int nfd, devfd; int i, n; char buf[256], *d; devfd = device_getfd(sm); nfd = (PtyRDFD > devfd) ? PtyRDFD + 1 : devfd + 1; while (!GTerminateThread) { if (CommandMode && gn_atem_initialised && queue.n > 0) { d = queue.buf + queue.head; queue.head = (queue.head + 1) % sizeof(queue.buf); queue.n--; gn_atem_incoming_data_handle(d, 1); continue; } FD_ZERO(&rfds); FD_SET(devfd, &rfds); if ( queue.n < sizeof(queue.buf) ) { FD_SET(PtyRDFD, &rfds); } tv.tv_sec = 0; tv.tv_usec = 500000; res = select(nfd, &rfds, NULL, NULL, &tv); switch (res) { case 0: /* Timeout */ continue; case -1: perror("gn_vm_loop - select"); exit (-1); default: break; } if (FD_ISSET(PtyRDFD, &rfds)) { n = sizeof(queue.buf) - queue.n < sizeof(buf) ? sizeof(queue.buf) - queue.n : sizeof(buf); if ( (n = read(PtyRDFD, buf, n)) <= 0 ) gn_vm_terminate(); for (i = 0; i < n; i++) { queue.buf[queue.tail++] = buf[i]; queue.tail %= sizeof(queue.buf); queue.n++; } } if (FD_ISSET(devfd, &rfds)) gn_sm_loop(1, sm); }}/* Application should call gn_vm_terminate to shut down the virtual modem thread */void gn_vm_terminate(void){ /* Request termination of thread */ GTerminateThread = true; if (!UseSTDIO) { close (PtyRDFD); close (PtyWRFD); } /* Shutdown device */ gn_sm_functions(GN_OP_Terminate, NULL, sm);}/* The following two functions are based on the skeleton from * W. Richard Stevens' "UNIX Network Programming", Volume 1, Second Edition. */static int gread(int fd, void *ptr, size_t nbytes, int *recvfd){ struct msghdr msg; struct iovec iov[1]; int n;#ifdef HAVE_MSGHDR_MSG_CONTROL union { struct cmsghdr cm; char control[CMSG_SPACE(sizeof(int))]; } control_un; struct cmsghdr *cmptr; msg.msg_control = control_un.control; msg.msg_controllen = sizeof(control_un.control);#else int newfd; msg.msg_accrights = (caddr_t) &newfd; msg.msg_accrightslen = sizeof(int);#endif /* HAVE_MSGHDR_MSG_CONTROL */ msg.msg_name = NULL; msg.msg_namelen = 0; iov[0].iov_base = ptr; iov[0].iov_len = nbytes; msg.msg_iov = iov; msg.msg_iovlen = 1; if ((n = recvmsg(fd, &msg, 0)) <= 0) return n;#ifdef HAVE_MSGHDR_MSG_CONTROL if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL && cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { if (cmptr->cmsg_level != SOL_SOCKET) perror("control level != SOL_SOCKET"); if (cmptr->cmsg_type != SCM_RIGHTS) perror("control type != SCM_RIGHTS"); *recvfd = *((int *)CMSG_DATA(cmptr)); } else { *recvfd = -1; }#else if (msg.msg_accrightslen == sizeof(int)) *recvfd = newfd; else *recvfd = -1;#endif /* HAVE_MSGHDR_MSG_CONTROL */ return (n);}static int gopen(const char *command){ int fd, sockfd[2], status; pid_t childpid; char c, argsockfd[10]; if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0) return -1; switch (childpid = fork()) { case -1: /* Critical error */ return -1; case 0: /* Child */ close(sockfd[0]); snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]); execl(command, "mgnokiidev", argsockfd, NULL); perror("execl: "); default: /* Parent */ break; } close(sockfd[1]); waitpid(childpid, &status, 0); if (WIFEXITED(status) == 0) perror("wait: "); if ((status = WEXITSTATUS(status)) == 0) { gread(sockfd[0], &c, 1, &fd); } else { errno = status; fd = -1; } close(sockfd[0]);#ifdef USE_UNIX98PTYS /* * I don't know why but it's required to operate correctly. * bozo -- tested on: Linux 2.4.17 */ if (fd >= 0 && unlockpt(fd)) { close(fd); fd = -1; }#endif return(fd);}/* Open pseudo tty interface and (in due course create a symlink to be /dev/gnokii etc. ) */static int VM_PtySetup(char *bindir){ char mgnokiidev[200]; if (UseSTDIO) { PtyRDFD = STDIN_FILENO; PtyWRFD = STDOUT_FILENO; return (0); } if (bindir) { strncpy(mgnokiidev, bindir, 200); strcat(mgnokiidev, "/"); } strncat(mgnokiidev, "mgnokiidev", 200 - strlen(bindir)); if (access(mgnokiidev, X_OK) != 0) { fprintf(stderr, _("Cannot access %s, check the bindir in your gnokiirc!\n"), mgnokiidev); exit(1); } PtyRDFD = gopen(mgnokiidev); if (PtyRDFD < 0) { fprintf (stderr, _("Couldn't open pty!\n")); return(-1); } PtyWRFD = PtyRDFD; return (0);}/* Initialise GSM interface, returning gn_error as appropriate */static gn_error VM_GSMInitialise(struct gn_statemachine *sm){ gn_error error; /* Initialise the code for the GSM interface. */ error = gn_gsm_initialise(sm); if (error != GN_ERR_NONE) fprintf(stderr, _("GSM/FBUS init failed!\n")); return (error);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -