summaryrefslogtreecommitdiff
path: root/src/gsmd
diff options
context:
space:
mode:
authorlaforge <laforge@99fdad57-331a-0410-800a-d7fa5415bdb3>2006-09-02 10:32:06 +0000
committerlaforge <laforge@99fdad57-331a-0410-800a-d7fa5415bdb3>2006-09-02 10:32:06 +0000
commit1584a74a4acdfb17aab0d6a2b13b18a7799aa37d (patch)
tree3d20dd370920df32bedb5b7205fb9ee7d35bef8b /src/gsmd
first compiling (unfinished, not-working) version of userspace gsm infrastructure
git-svn-id: http://svn.openmoko.org/trunk/src/target/gsm@40 99fdad57-331a-0410-800a-d7fa5415bdb3
Diffstat (limited to 'src/gsmd')
-rw-r--r--src/gsmd/.gpsd.h.swpbin0 -> 12288 bytes
-rw-r--r--src/gsmd/Makefile14
-rw-r--r--src/gsmd/atcmd.c332
-rw-r--r--src/gsmd/atcmd.h12
-rw-r--r--src/gsmd/gsmd.c171
-rw-r--r--src/gsmd/gsmd.h68
-rw-r--r--src/gsmd/gsmd_event.h53
-rw-r--r--src/gsmd/select.c96
-rw-r--r--src/gsmd/select.h22
-rw-r--r--src/gsmd/unsolicited.c272
-rw-r--r--src/gsmd/unsolicited.h8
-rw-r--r--src/gsmd/usock.c166
-rw-r--r--src/gsmd/usock.h15
-rw-r--r--src/gsmd/vendor_ti.c53
-rw-r--r--src/gsmd/vendorplugin.h29
15 files changed, 1311 insertions, 0 deletions
diff --git a/src/gsmd/.gpsd.h.swp b/src/gsmd/.gpsd.h.swp
new file mode 100644
index 0000000..75dfa6b
--- /dev/null
+++ b/src/gsmd/.gpsd.h.swp
Binary files differ
diff --git a/src/gsmd/Makefile b/src/gsmd/Makefile
new file mode 100644
index 0000000..ab0a1bf
--- /dev/null
+++ b/src/gsmd/Makefile
@@ -0,0 +1,14 @@
+
+CFLAGS:=-I../../include -Wall -g
+gsmd_OBJS:=gsmd.o atcmd.o select.o vendor_ti.o usock.o unsolicited.o
+
+all: gsmd
+
+gsmd: $(gsmd_OBJS)
+ $(CC) -o $@ $^
+
+%.o: %.c
+ $(CC) $(CFLAGS) -o $@ -c $^
+
+clean:
+ rm -f $(gsmd_OBJS) gsmd
diff --git a/src/gsmd/atcmd.c b/src/gsmd/atcmd.c
new file mode 100644
index 0000000..f482eb2
--- /dev/null
+++ b/src/gsmd/atcmd.c
@@ -0,0 +1,332 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/types.h>
+
+#include <common/linux_list.h>
+#include "gsmd.h"
+#include "atcmd.h"
+#include "unsolicited.h"
+
+/* libgsmd / gsmd AT command interpreter / parser / constructor
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ *
+ * Written for First International Computer, Inc., Taiwan
+ */
+
+enum final_result_codes {
+ GSMD_RESULT_OK = 0,
+ GSMD_RESULT_ERR = 1,
+ NUM_FINAL_RESULTS,
+};
+
+static const char *final_results[] = {
+ "OK",
+ "ERROR",
+ "+CME ERROR:"
+};
+
+/* we basically implement a parse that can deal with
+ * - receiving and queueing commands from higher level of libgmsd
+ * - optionally combining them into one larger command (; appending)
+ * - sending those commands to the TA, receiving and parsing responses
+ * - calling back application on completion, or waiting synchronously
+ * for a response
+ * - dealing with intermediate and unsolicited resultcodes by calling
+ * back into the application / higher levels
+ */
+
+static inline int llparse_append(struct llparser *llp, char byte)
+{
+ if (llp->cur < llp->buf + llp->len) {
+ *(llp->cur++) = byte;
+ return 0;
+ } else
+ return -EFBIG;
+}
+
+static int llparse_byte(struct llparser *llp, char byte)
+{
+ int ret = 0;
+
+ switch (llp->state) {
+ case LLPARSE_STATE_IDLE:
+ if (llp->flags & LGSM_ATCMD_F_EXTENDED) {
+ if (byte == '\r')
+ llp->state = LLPARSE_STATE_IDLE_CR;
+ else {
+#ifdef STRICT
+ llp->state = LLPARSE_STATE_ERROR;
+#else
+ llp->state = LLPARSE_STATE_RESULT;
+ ret = llparse_append(llp, byte);
+#endif
+ }
+ } else {
+ llp->state = LLPARSE_STATE_RESULT;
+ ret = llparse_append(llp, byte);
+ }
+ break;
+ case LLPARSE_STATE_IDLE_CR:
+ if (byte == '\n')
+ llp->state = LLPARSE_STATE_IDLE_LF;
+ else
+ llp->state = LLPARSE_STATE_ERROR;
+ break;
+ case LLPARSE_STATE_IDLE_LF:
+ /* can we really go directly into result_cr ? */
+ if (byte == '\r')
+ llp->state = LLPARSE_STATE_RESULT_CR;
+ else {
+ llp->state = LLPARSE_STATE_RESULT;
+ ret = llparse_append(llp, byte);
+ }
+ break;
+ case LLPARSE_STATE_RESULT:
+ if (byte == '\r')
+ llp->state = LLPARSE_STATE_RESULT_CR;
+ else
+ ret = llparse_append(llp, byte);
+ break;
+ case LLPARSE_STATE_RESULT_CR:
+ if (byte == '\n')
+ llp->state = LLPARSE_STATE_IDLE;
+ break;
+ case LLPARSE_STATE_ERROR:
+ break;
+ }
+
+ return ret;
+}
+
+static int llparse_string(struct llparser *llp, char *buf, unsigned int len)
+{
+ while (len--) {
+ int rc = llparse_byte(llp, *(buf++));
+ if (rc < 0)
+ return rc;
+
+ /* if _after_ parsing the current byte we have finished,
+ * let the caller know that there is something to handle */
+ if (llp->state == LLPARSE_STATE_RESULT_CR) {
+ llp->cb(llp->buf, llp->cur - llp->buf, llp->ctx);
+ /* re-set cursor to start of buffer */
+ llp->cur = llp->buf;
+ }
+ }
+
+ return 0;
+}
+
+/* mid-level parser */
+
+static int parse_final_result(const char *res)
+{
+ int i;
+ for (i = 0; i < NUM_FINAL_RESULTS; i++) {
+ if (!strcmp(res, final_results[i]))
+ return i;
+ }
+
+ return -1;
+}
+
+static int ml_parse(const char *buf, int len, void *ctx)
+{
+ struct gsmd *g = ctx;
+ struct gsmd_atcmd *cmd;
+ int final = 0;
+
+ /* responses come in order, so first response has to be for first
+ * command we sent, i.e. first entry in list */
+ cmd = llist_entry(g->busy_atcmds.next, struct gsmd_atcmd, list);
+
+ if (buf[0] == '+') {
+ /* an extended response */
+ const char *colon = strchr(buf, ':');
+ if (!colon) {
+ fprintf(stderr, "no colon in extd response `%s'\n",
+ buf);
+ return -EINVAL;
+ }
+ if (cmd->buf[2] != '+') {
+ fprintf(stderr, "extd reply to non-extd command?\n");
+ return -EINVAL;
+ }
+
+ if (!strncmp(buf+1, "CME ERROR", 9)) {
+ unsigned long err_nr;
+ err_nr = strtoul(colon+1, NULL, 10);
+ DEBUGP("error number %lu\n", err_nr);
+ cmd->ret = err_nr;
+ final = 1;
+ goto final_cb;
+ }
+
+ if (strncmp(buf, &cmd->buf[2], colon-buf)) {
+ DEBUGP("extd reply `%s' to cmd `%s', must be "
+ "unsolicited\n", buf, &cmd->buf[2]);
+ colon++;
+ if (colon > buf+len)
+ colon = NULL;
+ return unsolicited_parse(g, buf, len, colon);
+ }
+
+ /* if we survive till here, it's a valid extd response
+ * to an extended command */
+
+ /* FIXME: solve multi-line responses ! */
+ if (cmd->buflen < len)
+ len = cmd->buflen;
+
+ memcpy(cmd->buf, buf, len);
+ } else {
+
+ /* this is the only non-extended unsolicited return code */
+ if (!strcmp(buf, "RING"))
+ return unsolicited_parse(g, buf, len, NULL);
+
+ if (!strcmp(buf, "ERROR") ||
+ ((g->flags & GSMD_FLAG_V0) && cmd->buf[0] == '4')){
+ DEBUGP("unspecified error\n");
+ cmd->ret = 4;
+ final = 1;
+ goto final_cb;
+ }
+
+ if (!strncmp(buf, "OK", 2)
+ || ((g->flags & GSMD_FLAG_V0) && cmd->buf[0] == '0')) {
+ cmd->ret = 0;
+ final = 1;
+ goto final_cb;
+ }
+
+ /* FIXME: handling of those special commands in response to
+ * ATD / ATA */
+ if (!strncmp(buf, "NO CARRIER", 10)) {
+ }
+
+ if (!strncmp(buf, "BUSY", 4)) {
+ }
+ }
+
+final_cb:
+ if (final) {
+ /* remove from list of currently executing cmds */
+ llist_del(&cmd->list);
+
+ if (cmd->cb) {
+ fprintf(stderr, "command without cb!!!\n");
+ return -EINVAL;
+ }
+ return cmd->cb(cmd, cmd->ctx);
+
+ }
+
+ return 0;
+}
+
+/* callback to be called if [virtual] UART has some data for us */
+static int atcmd_select_cb(int fd, unsigned int what, void *data)
+{
+ int len;
+ static char rxbuf[1024];
+ struct gsmd *g = data;
+
+ if (what & GSMD_FD_READ) {
+ while ((len = read(fd, rxbuf, sizeof(rxbuf)))) {
+ int rc;
+
+ if (len < 0) {
+ DEBUGP("ERROR reading from fd %u: %d (%s)\n", fd, len,
+ strerror(errno));
+ return len;
+ }
+ rc = llparse_string(&g->llp, rxbuf, len);
+ if (rc < 0) {
+ DEBUGP("ERROR during llparse_string: %d\n", rc);
+ return rc;
+ }
+ }
+ }
+
+ /* write pending commands to UART */
+ if (what & GSMD_FD_WRITE) {
+ struct gsmd_atcmd *pos, *pos2;
+ llist_for_each_entry_safe(pos, pos2, &g->pending_atcmds, list) {
+ int rc = write(fd, pos->buf, strlen(pos->buf));
+ if (rc == 0) {
+ DEBUGP("write returns 0, aborting\n");
+ break;
+ } else if (rc < 0) {
+ DEBUGP("error during write to fd %d: %d\n",
+ fd, rc);
+ return rc;
+ }
+ if (rc < len) {
+ fprintf(stderr, "short write!!! FIXME!\n");
+ exit(3);
+ }
+ /* success: remove from global list of to-be-sent atcmds */
+ llist_del(&pos->list);
+ /* append to global list of executing atcmds */
+ llist_add_tail(&pos->list, &g->busy_atcmds);
+ }
+ }
+
+ return 0;
+}
+
+
+struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen,
+ atcmd_cb_t cb, void *ctx)
+{
+ int buflen = strlen(cmd);
+ struct gsmd_atcmd *atcmd;
+
+ if (rlen > buflen)
+ buflen = rlen;
+
+ atcmd = malloc(sizeof(*atcmd)+ buflen);
+ if (!atcmd)
+ return NULL;
+
+ atcmd->ctx = ctx;
+ atcmd->flags = 0;
+ atcmd->ret = -255;
+ atcmd->buflen = buflen;
+ atcmd->buf[buflen-1] = '\0';
+ atcmd->cb = cb;
+ strncpy(atcmd->buf, cmd, buflen-1);
+
+ return atcmd;
+}
+
+/* submit an atcmd in the global queue of pending atcmds */
+int atcmd_submit(struct gsmd *g, struct gsmd_atcmd *cmd)
+{
+ llist_add_tail(&cmd->list, &g->pending_atcmds);
+ g->gfd_uart.when |= GSMD_FD_WRITE;
+
+ return 0;
+}
+
+/* init atcmd parser */
+int atcmd_init(struct gsmd *g, int sockfd)
+{
+ g->gfd_uart.fd = sockfd;
+ g->gfd_uart.when = GSMD_FD_READ;
+ g->gfd_uart.data = g;
+ g->gfd_uart.cb = &atcmd_select_cb;
+
+ g->llp.cur = g->llp.buf;
+ g->llp.cb = &ml_parse;
+ g->llp.ctx = g;
+ g->llp.flags = LGSM_ATCMD_F_EXTENDED;
+
+ return gsmd_register_fd(&g->gfd_uart);
+}
+
diff --git a/src/gsmd/atcmd.h b/src/gsmd/atcmd.h
new file mode 100644
index 0000000..f6b9d1d
--- /dev/null
+++ b/src/gsmd/atcmd.h
@@ -0,0 +1,12 @@
+#ifndef __GSMD_ATCMD_H
+#define __GSMD_ATCMD_H
+
+#include "gsmd.h"
+
+typedef int atcmd_cb_t(struct gsmd_atcmd *cmd, void *ctx);
+
+struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen, atcmd_cb_t *cb, void *ctx);
+int atcmd_submit(struct gsmd *g, struct gsmd_atcmd *cmd);
+int atcmd_init(struct gsmd *g, int sockfd);
+
+#endif
diff --git a/src/gsmd/gsmd.c b/src/gsmd/gsmd.c
new file mode 100644
index 0000000..ea44a73
--- /dev/null
+++ b/src/gsmd/gsmd.c
@@ -0,0 +1,171 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <gsmd/gsmd.h>
+
+#include "gsmd.h"
+#include "atcmd.h"
+#include "select.h"
+#include "usock.h"
+#include "vendorplugin.h"
+
+static int gsmd_test_atcb(struct gsmd_atcmd *cmd, void *ctx)
+{
+ printf("returned: `%s'\n", cmd->buf);
+ free(cmd);
+ return 0;
+}
+
+static int gsmd_test(struct gsmd *gsmd)
+{
+ struct gsmd_atcmd *cmd;
+ cmd = atcmd_fill("AT+CLCK=?", 255, &gsmd_test_atcb, NULL);
+ return atcmd_submit(gsmd, cmd);
+}
+
+struct bdrt {
+ int bps;
+ u_int32_t b;
+};
+
+static struct bdrt bdrts[] = {
+ { 0, B0 },
+ { 9600, B9600 },
+ { 19200, B19200 },
+ { 38400, B38400 },
+ { 57600, B57600 },
+ { 115200, B115200 },
+};
+
+static int set_baudrate(int fd, int baudrate)
+{
+ int i;
+ u_int32_t bd = 0;
+ struct termios ti;
+
+ for (i = 0; i < ARRAY_SIZE(bdrts); i++) {
+ if (bdrts[i].bps == baudrate)
+ bd = bdrts[i].b;
+ }
+ if (bd == 0)
+ return -EINVAL;
+
+ i = tcgetattr(fd, &ti);
+ if (i < 0)
+ return i;
+
+ i = cfsetispeed(&ti, B0);
+ if (i < 0)
+ return i;
+
+ i = cfsetospeed(&ti, bd);
+ if (i < 0)
+ return i;
+
+ return tcsetattr(fd, 0, &ti);
+}
+
+
+static struct gsmd g;
+
+static struct option opts[] = {
+ { "version", 0, NULL, 'V' },
+ { "daemon", 0, NULL, 'd' },
+ { "help", 0, NULL, 'h' },
+ { "device", 1, NULL, 'p' },
+ { "speed", 1, NULL, 's' },
+};
+
+int main(int argc, char **argv)
+{
+ int fd, argch;
+
+ int daemonize = 0;
+ int bps = 115200;
+ char *device = "/dev/ttyUSB0";
+
+ /*FIXME: parse commandline, set daemonize, device, ... */
+ while ((argch = getopt_long(argc, argv, "Vdhps:", opts, NULL)) != -1) {
+ switch (argch) {
+ case 'V':
+ /* FIXME */
+ break;
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'h':
+ /* FIXME */
+ break;
+ case 'p':
+ device = optarg;
+ break;
+ case 's':
+ bps = atoi(optarg);
+ break;
+ }
+ }
+
+ /* use direct access to device node ([virtual] tty device) */
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "can't open device `%s': %s\n",
+ device, strerror(errno));
+ exit(1);
+ }
+
+ if (set_baudrate(fd, bps) < 0) {
+ fprintf(stderr, "can't set baudrate\n");
+ exit(1);
+ }
+
+ if (atcmd_init(&g, fd) < 0) {
+ fprintf(stderr, "can't initialize UART device\n");
+ exit(1);
+ }
+
+ if (usock_init(&g) < 0) {
+ fprintf(stderr, "can't open unix socket\n");
+ exit(1);
+ }
+
+ if (daemonize) {
+ if (fork()) {
+ exit(0);
+ }
+ fclose(stdout);
+ fclose(stderr);
+ fclose(stdin);
+ setsid();
+ }
+
+ gsmd_test(&g);
+
+ while (1) {
+ int ret = gsmd_select_main();
+ if (ret == 0)
+ continue;
+
+ if (ret < 0) {
+ if (errno == -EINTR)
+ continue;
+ else {
+ DEBUGP("select returned error (%s)\n",
+ strerror(errno));
+ break;
+ }
+ }
+ }
+
+ exit(0);
+}
diff --git a/src/gsmd/gsmd.h b/src/gsmd/gsmd.h
new file mode 100644
index 0000000..ff05a27
--- /dev/null
+++ b/src/gsmd/gsmd.h
@@ -0,0 +1,68 @@
+#ifndef __GSMD_H
+#define __GSMD_H
+
+#include <sys/types.h>
+
+#include <common/linux_list.h>
+
+#include "select.h"
+
+/* Refer to 3GPP TS 07.07 v 7.8.0, Chapter 4.1 */
+#define LGSM_ATCMD_F_EXTENDED 0x01 /* as opposed to basic */
+#define LGSM_ATCMD_F_PARAM 0x02 /* as opposed to action */
+
+struct gsmd_atcmd {
+ struct llist_head list;
+ void *ctx;
+ int (*cb)(struct gsmd_atcmd *, void *);
+ u_int8_t flags;
+ int32_t ret;
+ u_int32_t buflen;
+ char buf[];
+};
+
+enum llparse_state {
+ LLPARSE_STATE_IDLE, /* idle, not parsing a response */
+ LLPARSE_STATE_IDLE_CR, /* CR before response (V1) */
+ LLPARSE_STATE_IDLE_LF, /* LF before response (V1) */
+ LLPARSE_STATE_RESULT, /* within result payload */
+ LLPARSE_STATE_RESULT_CR, /* CR after result */
+ LLPARSE_STATE_ERROR, /* something went wrong */
+ /* ... idle again */
+};
+
+/* we can't take any _single_ response bigger than this: */
+#define LLPARSE_BUF_SIZE 256
+
+struct llparser {
+ enum llparse_state state;
+ unsigned int len;
+ unsigned int flags;
+ void *ctx;
+ int (*cb)(const char *buf, int len, void *ctx);
+ char *cur;
+ char buf[LLPARSE_BUF_SIZE];
+};
+
+#define GSMD_FLAG_V0 0x0001 /* V0 responses to be expected from TA */
+
+struct gsmd {
+ unsigned int flags;
+ struct gsmd_fd gfd_uart;
+ struct gsmd_fd gfd_sock;
+ struct llparser llp;
+ struct llist_head users;
+ struct llist_head pending_atcmds; /* our busy gsmd_atcmds */
+ struct llist_head busy_atcmds; /* our busy gsmd_atcmds */
+};
+
+struct gsmd_user {
+ struct llist_head list; /* our entry in the global list */
+ struct llist_head finished_ucmds; /* our busy gsmd_ucmds */
+ struct gsmd *gsmd;
+ struct gsmd_fd gfd; /* the socket */
+ u_int32_t subscriptions; /* bitmaks of subscribed event groups */
+};
+
+#define DEBUGP(x, args ...) printf("%s:%s(%d):" x, __FILE__, __FUNCTION__, __LINE__, ## args)
+#endif
diff --git a/src/gsmd/gsmd_event.h b/src/gsmd/gsmd_event.h
new file mode 100644
index 0000000..b25ee9d
--- /dev/null
+++ b/src/gsmd/gsmd_event.h
@@ -0,0 +1,53 @@
+#ifndef __GSMD_EVENT_H
+#define __GSMD_EVENT_H
+
+/* event handling */
+
+enum gsmd_event_type {
+ GSMD_EVTTYPE_NONE,
+ GSMD_EVTTYPE_VOICECALL,
+ GSMD_EVTTYPE_DATACALL,
+ GSMD_EVTTYPE_SMS,
+ GSMD_EVTTYPE_GPRS,
+ GSMD_EVTTYPE_CIPHER_IND,
+};
+
+enum gsmd_event_call {
+ GSMD_EVT_CALL_NONE,
+ GSMD_EVT_CALL_HANGUP, /* any call: hanged up */
+ GSMD_EVT_CALL_RING, /* incoming call: we're ringing */
+ GSMD_EVT_CALL_BUSY, /* outgoing call: busy */
+ GSMD_EVT_CALL_RINGING, /* outgoing call: other end ringing */
+ GSMD_EVT_CALL_ESTABLISHED, /* any call: now established */
+};
+
+enum gsmd_event_voice {
+ /* all of event_call */
+};
+
+enum gsmd_event_data {
+ /* all of event_call */
+};
+
+enum gsmd_event_sms {
+ GSMD_EVT_SMS_NONE,
+ GSMD_EVT_SMS_RCVD, /* incoming SMS received */
+ GSMD_EVT_SMS_OVERFLOW, /* sms memory full, can't receive */
+};
+
+enum gsmd_event_gprs {
+};
+
+enum gsmd_event_cipher {
+ GSMD_EVT_CI_NONE,
+ GSMD_EVT_CI_ENABLED, /* cipher enabled */
+ GSMD_EVT_CI_DISABLED, /* cipher disabled */
+};
+
+enum gsmd_event_network {
+ GSMD_EVT_NW_NONE,
+ GSMD_EVT_NW_SIGNAL, /* signal strength */
+ GSMD_EVT_NW_REG, /* network registration */
+};
+
+#endif
diff --git a/src/gsmd/select.c b/src/gsmd/select.c
new file mode 100644
index 0000000..62dc4c2
--- /dev/null
+++ b/src/gsmd/select.c
@@ -0,0 +1,96 @@
+/* select() FD handling for GSM Daemon.
+ * (C) 2000-2006 by Harald Welte <laforge@gnumonks.org>
+ *
+ * Based on code originally written by myself for ulogd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation
+ *
+ * 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
+ */
+
+#include <fcntl.h>
+#include <common/linux_list.h>
+#include "select.h"
+
+static int maxfd = 0;
+static LLIST_HEAD(gsmd_fds);
+
+int gsmd_register_fd(struct gsmd_fd *fd)
+{
+ int flags;
+
+ /* make FD nonblocking */
+ flags = fcntl(fd->fd, F_GETFL);
+ if (flags < 0)
+ return -1;
+ flags |= O_NONBLOCK;
+ flags = fcntl(fd->fd, F_SETFL, flags);
+ if (flags < 0)
+ return -1;
+
+ /* Register FD */
+ if (fd->fd > maxfd)
+ maxfd = fd->fd;
+
+ llist_add_tail(&fd->list, &gsmd_fds);
+
+ return 0;
+}
+
+void gsmd_unregister_fd(struct gsmd_fd *fd)
+{
+ llist_del(&fd->list);
+}
+
+int gsmd_select_main()
+{
+ struct gsmd_fd *ufd;
+ fd_set readset, writeset, exceptset;
+ int i;
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_ZERO(&exceptset);
+
+ /* prepare read and write fdsets */
+ llist_for_each_entry(ufd, &gsmd_fds, list) {
+ if (ufd->when & GSMD_FD_READ)
+ FD_SET(ufd->fd, &readset);
+
+ if (ufd->when & GSMD_FD_WRITE)
+ FD_SET(ufd->fd, &writeset);
+
+ if (ufd->when & GSMD_FD_EXCEPT)
+ FD_SET(ufd->fd, &exceptset);
+ }
+
+ i = select(maxfd+1, &readset, &writeset, &exceptset, NULL);
+ if (i > 0) {
+ /* call registered callback functions */
+ llist_for_each_entry(ufd, &gsmd_fds, list) {
+ int flags = 0;
+
+ if (FD_ISSET(ufd->fd, &readset))
+ flags |= GSMD_FD_READ;
+
+ if (FD_ISSET(ufd->fd, &writeset))
+ flags |= GSMD_FD_WRITE;
+
+ if (FD_ISSET(ufd->fd, &exceptset))
+ flags |= GSMD_FD_EXCEPT;
+
+ if (flags)
+ ufd->cb(ufd->fd, flags, ufd->data);
+ }
+ }
+ return i;
+}
diff --git a/src/gsmd/select.h b/src/gsmd/select.h
new file mode 100644
index 0000000..c734eb6
--- /dev/null
+++ b/src/gsmd/select.h
@@ -0,0 +1,22 @@
+#ifndef __GSMD_SELECT_H
+#define __GSMD_SELECT_H
+
+#include <common/linux_list.h>
+
+#define GSMD_FD_READ 0x0001
+#define GSMD_FD_WRITE 0x0002
+#define GSMD_FD_EXCEPT 0x0004
+
+struct gsmd_fd {
+ struct llist_head list;
+ int fd; /* file descriptor */
+ unsigned int when;
+ int (*cb)(int fd, unsigned int what, void *data);
+ void *data; /* void * to pass to callback */
+};
+
+int gsmd_register_fd(struct gsmd_fd *ufd);
+void gsmd_unregister_fd(struct gsmd_fd *ufd);
+int gsmd_select_main(void);
+
+#endif
diff --git a/src/gsmd/unsolicited.c b/src/gsmd/unsolicited.c
new file mode 100644
index 0000000..fdcdac7
--- /dev/null
+++ b/src/gsmd/unsolicited.c
@@ -0,0 +1,272 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <gsmd/usock.h>
+#include <gsmd/event.h>
+
+#include "gsmd.h"
+#include "usock.h"
+
+static struct gsmd_ucmd *build_event(u_int8_t type, u_int8_t subtype, u_int8_t len)
+{
+ struct gsmd_ucmd *ucmd = malloc(sizeof(*ucmd)+len);
+
+ if (!ucmd)
+ return NULL;
+
+ ucmd->hdr.version = GSMD_PROTO_VERSION;
+ ucmd->hdr.msg_type = type;
+ ucmd->hdr.msg_subtype = subtype;
+ ucmd->hdr.len = len;
+
+ return ucmd;
+}
+
+static struct gsmd_ucmd *ucmd_copy(const struct gsmd_ucmd *orig)
+{
+ int size = sizeof(*orig) + orig->hdr.len;
+ struct gsmd_ucmd *copy = malloc(size);
+
+ if (copy)
+ memcpy(copy, orig, size);
+
+ return copy;
+}
+
+static int usock_evt_send(struct gsmd *gsmd, struct gsmd_ucmd *ucmd, u_int32_t evt)
+{
+ struct gsmd_user *gu;
+ int num_sent = 0;
+
+ llist_for_each_entry(gu, &gsmd->users, list) {
+ if (gu->subscriptions & (1 << evt)) {
+ struct gsmd_ucmd *cpy = ucmd_copy(ucmd);
+ llist_add_tail(&ucmd->list, &gu->finished_ucmds);
+ num_sent++;
+ ucmd = cpy;
+ if (!ucmd) {
+ fprintf(stderr, "can't allocate memory for copy of ucmd\n");
+ return num_sent;
+ }
+ }
+ }
+
+ return num_sent;
+}
+
+static int ring_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ /* FIXME: generate ring event */
+ struct gsmd_ucmd *ucmd = build_event(GSMD_MSG_EVENT, GSMD_EVT_IN_CALL,
+ sizeof(struct gsmd_evt_auxdata));
+ struct gsmd_evt_auxdata *aux;
+ if (!ucmd)
+ return -ENOMEM;
+
+ aux = (struct gsmd_evt_auxdata *)ucmd->buf;
+
+ aux->u.call.type = GSMD_CALL_UNSPEC;
+
+ return usock_evt_send(gsmd, ucmd, GSMD_EVT_IN_CALL);
+}
+
+static int cring_parse(const char *buf, int len, const char *param, struct gsmd *gsmd)
+{
+ struct gsmd_ucmd *ucmd = build_event(GSMD_MSG_EVENT, GSMD_EVT_IN_CALL,
+ sizeof(struct gsmd_evt_auxdata));
+ struct gsmd_evt_auxdata *aux;
+ if (!ucmd)
+ return -ENOMEM;
+
+ aux = (struct gsmd_evt_auxdata *) ucmd->buf;
+
+ if (!strcmp(param, "VOICE")) {
+ /* incoming voice call */
+ aux->u.call.type = GSMD_CALL_VOICE;
+ } else if (!strcmp(param, "SYNC")) {
+ aux->u.call.type = GSMD_CALL_DATA_SYNC;
+ } else if (!strcmp(param, "REL ASYNC")) {
+ aux->u.call.type = GSMD_CALL_DATA_REL_ASYNC;
+ } else if (!strcmp(param, "REL SYNC")) {
+ aux->u.call.type = GSMD_CALL_DATA_REL_SYNC;
+ } else if (!strcmp(param, "FAX")) {
+ aux->u.call.type = GSMD_CALL_FAX;
+ } else if (!strncmp(param, "GPRS ", 5)) {
+ /* FIXME: change event type to GPRS */
+ free(ucmd);
+ return 0;
+ }
+ /* FIXME: parse all the ALT* profiles, Chapter 6.11 */
+
+ return usock_evt_send(gsmd, ucmd, GSMD_EVT_IN_CALL);
+}
+
+/* Chapter 7.2, network registration */
+static int creg_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ const char *comma = strchr(param, ',');
+ struct gsmd_ucmd *ucmd = build_event(GSMD_MSG_EVENT, GSMD_EVT_NETREG,
+ sizeof(struct gsmd_evt_auxdata));
+ struct gsmd_evt_auxdata *aux;
+ if (!ucmd)
+ return -ENOMEM;
+ aux = (struct gsmd_evt_auxdata *) ucmd->buf;
+
+ aux->u.netreg.state = atoi(param);
+ if (comma) {
+ /* FIXME: we also have location area code and cell id to parse (hex) */
+ }
+
+ return usock_evt_send(gsmd, ucmd, GSMD_EVT_NETREG);
+}
+
+/* Chapter 7.11, call waiting */
+static int ccwa_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ const char *number;
+ u_int8_t type, class;
+
+ /* FIXME: parse */
+ return 0;
+}
+
+/* Chapter 7.14, unstructured supplementary service data */
+static int cusd_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ /* FIXME: parse */
+ return 0;
+}
+
+/* Chapter 7.15, advise of charge */
+static int cccm_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ /* FIXME: parse */
+ return 0;
+}
+
+/* Chapter 10.1.13, GPRS event reporting */
+static int cgev_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ /* FIXME: parse */
+ return 0;
+}
+
+/* Chapter 10.1.14, GPRS network registration status */
+static int cgreg_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ /* FIXME: parse */
+ return 0;
+}
+
+/* Chapter 7.6, calling line identification presentation */
+static int clip_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ struct gsmd_ucmd *ucmd = build_event(GSMD_MSG_EVENT, GSMD_EVT_IN_CLIP,
+ sizeof(struct gsmd_evt_auxdata));
+ struct gsmd_evt_auxdata *aux;
+ const char *comma = strchr(param, ',');
+
+ if (!ucmd)
+ return -ENOMEM;
+
+ aux = (struct gsmd_evt_auxdata *) ucmd->buf;
+
+ if (!comma)
+ return -EINVAL;
+
+ if (comma - param > GSMD_ADDR_MAXLEN)
+ return -EINVAL;
+
+ memcpy(aux->u.clip.addr.number, param, comma-param);
+ /* FIXME: parse of subaddr, etc. */
+
+ return usock_evt_send(gsmd, ucmd, GSMD_EVT_IN_CLIP);
+}
+
+/* Chapter 7.9, calling line identification presentation */
+static int colp_parse(const char *buf, int len, const char *param,
+ struct gsmd *gsmd)
+{
+ struct gsmd_ucmd *ucmd = build_event(GSMD_MSG_EVENT, GSMD_EVT_OUT_COLP,
+ sizeof(struct gsmd_evt_auxdata));
+ struct gsmd_evt_auxdata *aux;
+ const char *comma = strchr(param, ',');
+
+ if (!ucmd)
+ return -ENOMEM;
+
+ aux = (struct gsmd_evt_auxdata *) ucmd->buf;
+
+ if (!comma)
+ return -EINVAL;
+
+ if (comma - param > GSMD_ADDR_MAXLEN)
+ return -EINVAL;
+
+ memcpy(aux->u.colp.addr.number, param, comma-param);
+ /* FIXME: parse of subaddr, etc. */
+
+ return usock_evt_send(gsmd, ucmd, GSMD_EVT_OUT_COLP);
+}
+
+struct gsmd_unsolicit {
+ const char *prefix;
+ int (*parse)(const char *unsol, int len, const char *param, struct gsmd *gsmd);
+};
+
+static const struct gsmd_unsolicit gsm0707_unsolicit[] = {
+ { "RING", &ring_parse },
+ { "+CRING", &cring_parse },
+ { "+CREG", &creg_parse },
+ { "+CCWA", &ccwa_parse },
+ { "+CUSD", &cusd_parse },
+ { "+CCCM", &cccm_parse },
+ { "+CGEV", &cgev_parse },
+ { "+CGREG", &cgreg_parse },
+ { "+CLIP", &clip_parse },
+ { "+COLP", &colp_parse },
+ /*
+ { "+CKEV", &ckev_parse },
+ { "+CDEV", &cdev_parse },
+ { "+CIEV", &ciev_parse },
+ { "+CLAV", &clav_parse },
+ { "+CCWV", &ccwv_parse },
+ { "+CLAV", &clav_parse },
+ { "+CSSU", &cssu_parse },
+ */
+};
+
+/* called by midlevel parser if a response seems unsolicited */
+int unsolicited_parse(struct gsmd *g, const char *buf, int len, const char *param)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(gsm0707_unsolicit); i++) {
+ const char *colon;
+ if (strncmp(buf, gsm0707_unsolicit[i].prefix,
+ strlen(gsm0707_unsolicit[i].prefix)))
+ continue;
+
+ colon = strchr(buf, ':') + 1;
+ if (colon > buf+len)
+ colon = NULL;
+
+ return gsm0707_unsolicit[i].parse(buf, len, colon, g);
+ }
+
+ /* FIXME: call vendor-specific unsolicited code parser */
+ return -EINVAL;
+}
+
diff --git a/src/gsmd/unsolicited.h b/src/gsmd/unsolicited.h
new file mode 100644
index 0000000..4cc5bb5
--- /dev/null
+++ b/src/gsmd/unsolicited.h
@@ -0,0 +1,8 @@
+#ifndef __GSMD_UNSOLICITED_H
+#define __GSMD_UNSOLICITED_H
+
+#include "gsmd.h"
+
+int unsolicited_parse(struct gsmd *g, const char *buf, int len, const char *param);
+
+#endif
diff --git a/src/gsmd/usock.c b/src/gsmd/usock.c
new file mode 100644
index 0000000..b00c031
--- /dev/null
+++ b/src/gsmd/usock.c
@@ -0,0 +1,166 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <gsmd/gsmd.h>
+#include <gsmd/usock.h>
+
+#include "gsmd.h"
+#include "select.h"
+#include "atcmd.h"
+#include "usock.h"
+
+/* callback for completed passthrough gsmd_atcmd's */
+static int usock_cmd_cb(struct gsmd_atcmd *cmd, void *ctx)
+{
+ struct gsmd_user *gu = ctx;
+ struct gsmd_ucmd *ucmd = malloc(sizeof(*ucmd)+cmd->buflen);
+
+ if (!ucmd)
+ return -ENOMEM;
+
+ ucmd->hdr.version = GSMD_PROTO_VERSION;
+ ucmd->hdr.msg_type = GSMD_MSG_PASSTHROUGH;
+ ucmd->hdr.msg_subtype = GSMD_PASSTHROUGH_RESP;
+ ucmd->hdr.len = cmd->buflen;
+ memcpy(ucmd->buf, cmd->buf, ucmd->hdr.len);
+
+ /* add to per-user list of finished cmds */
+ llist_add_tail(&ucmd->list, &gu->finished_ucmds);
+
+ /* mark socket of user as we-want-to-write */
+ gu->gfd.when |= GSMD_FD_WRITE;
+
+ return 0;
+}
+
+static int usock_rcv_pcmd(struct gsmd_user *gu, char *buf, int len)
+{
+ struct gsmd_msg_hdr *gph = (struct gsmd_msg_hdr *)buf;
+
+ if (gph->version != GSMD_PROTO_VERSION)
+ return -EINVAL;
+
+ switch (gph->msg_type) {
+ case GSMD_MSG_PASSTHROUGH:
+ {
+ struct gsmd_atcmd *cmd;
+ cmd = atcmd_fill((char *)gph+sizeof(*gph),
+ 255, &usock_cmd_cb, gu);
+ if (!cmd)
+ return -ENOMEM;
+ return atcmd_submit(gu->gsmd, cmd);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* callback for read/write on client (libgsmd) socket */
+static int gsmd_usock_user_cb(int fd, unsigned int what, void *data)
+{
+ struct gsmd_user *gu = data;
+
+ /* FIXME: check some kind of backlog and limit it */
+
+ if (what & GSMD_FD_READ) {
+ char buf[1024];
+ int rcvlen;
+ /* read data from socket, determine what he wants */
+ rcvlen = read(fd, buf, sizeof(buf));
+ return usock_rcv_pcmd(gu, buf, rcvlen);
+ }
+
+ if (what & GSMD_FD_WRITE) {
+ /* write data from pending replies to socket */
+ struct gsmd_ucmd *ucmd, *uctmp;
+ llist_for_each_entry_safe(ucmd, uctmp, &gu->finished_ucmds,
+ list) {
+ int rc;
+
+ rc = write(fd, &ucmd->hdr, sizeof(ucmd->hdr) + ucmd->hdr.len);
+ if (rc < 0) {
+ DEBUGP("write return %d\n", rc);
+ return rc;
+ }
+ if (rc == 0) {
+ DEBUGP("write returns zero!!\n");
+ break;
+ }
+ if (rc != sizeof(ucmd->hdr) + ucmd->hdr.len) {
+ DEBUGP("short write\n");
+ break;
+ }
+
+ llist_del(&ucmd->list);
+ free(ucmd);
+ }
+ }
+
+ return 0;
+}
+
+/* callback for read on master-listen-socket */
+static int gsmd_usock_cb(int fd, unsigned int what, void *data)
+{
+ struct gsmd *g = data;
+ struct gsmd_user *newuser;
+
+ /* FIXME: implement this */
+ if (what & GSMD_FD_READ) {
+ /* new incoming connection */
+ newuser = malloc(sizeof(*newuser));
+ if (!newuser)
+ return -ENOMEM;
+
+ newuser->gfd.fd = accept(fd, NULL, 0);
+ if (newuser->gfd.fd < 0) {
+ DEBUGP("error accepting incoming conn: `%s'\n",
+ strerror(errno));
+ free(newuser);
+ }
+ newuser->gfd.when = GSMD_FD_READ;
+ newuser->gfd.data = newuser;
+ newuser->gfd.cb = &gsmd_usock_user_cb;
+ newuser->gsmd = g;
+
+ llist_add(&newuser->list, &g->users);
+ }
+
+ return 0;
+}
+
+/* handling of socket with incoming client connections */
+int usock_init(struct gsmd *g)
+{
+ struct sockaddr_un sun;
+ int fd, rc;
+
+ fd = socket(PF_UNIX, GSMD_UNIX_SOCKET_TYPE, 0);
+ if (fd < 0)
+ return fd;
+
+ sun.sun_family = AF_UNIX;
+ memcpy(sun.sun_path, GSMD_UNIX_SOCKET, sizeof(GSMD_UNIX_SOCKET));
+
+ rc = bind(fd, (struct sockaddr *)&sun, sizeof(sun));
+ if (rc < 0) {
+ close(fd);
+ return rc;
+ }
+
+ g->gfd_sock.fd = fd;
+ g->gfd_sock.when = GSMD_FD_READ | GSMD_FD_EXCEPT;
+ g->gfd_sock.data = g;
+ g->gfd_sock.cb = &gsmd_usock_cb;
+
+ return gsmd_register_fd(&g->gfd_sock);
+}
diff --git a/src/gsmd/usock.h b/src/gsmd/usock.h
new file mode 100644
index 0000000..aa7c43f
--- /dev/null
+++ b/src/gsmd/usock.h
@@ -0,0 +1,15 @@
+#ifndef __GSMD_USOCK_H
+#define __GSMD_USOCK_H
+
+#include <gsmd/usock.h>
+
+int usock_init(struct gsmd *g);
+
+struct gsmd_ucmd {
+ struct llist_head list;
+ struct gsmd_msg_hdr hdr;
+ char buf[];
+} __attribute__ ((packed));
+
+#endif
+
diff --git a/src/gsmd/vendor_ti.c b/src/gsmd/vendor_ti.c
new file mode 100644
index 0000000..ac67b92
--- /dev/null
+++ b/src/gsmd/vendor_ti.c
@@ -0,0 +1,53 @@
+/* TI [Calypso] compatible backend */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "vendorplugin.h"
+
+static int
+ti_getopt(struct gsmd *gh, int optname, void *optval, int *optlen)
+{
+ switch (optname) {
+ case GSMD_OPT_CIPHER_IND:
+ /* FIXME: send AT%CPRI=? */
+ break;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int
+ti_setopt(struct gsmd *gh, int optname, const void *optval, int optlen)
+{
+ switch (optname) {
+ case GSMD_OPT_CIPHER_IND:
+ /* FIXME: send AT%CPRI= */
+ break;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ti_parseunsolicit(struct gsmd *gh)
+{
+ /* FIXME: parse all the below and generate the respective events */
+
+ /* %CPROAM: CPHS Home Country Roaming Indicator */
+ /* %CPVWI: CPHS Voice Message Waiting */
+ /* %CGREG: reports extended information about GPRS registration state */
+ /* %CTZV: reports network time and date information */
+ /* %CNIV: reports network name information */
+ /* %CPKY: Press Key */
+ /* %CMGRS: Message Retransmission Service */
+ /* %CGEV: reports GPRS network events */
+ return -EINVAL;
+}
+
+struct gsmd_vendorspecific ti_vendorspec = {
+ .getopt = &ti_getopt,
+ .setopt = &ti_setopt,
+ .parse_unsolicit = &ti_parseunsolicit,
+};
diff --git a/src/gsmd/vendorplugin.h b/src/gsmd/vendorplugin.h
new file mode 100644
index 0000000..b8be9ba
--- /dev/null
+++ b/src/gsmd/vendorplugin.h
@@ -0,0 +1,29 @@
+#ifndef __GSMD_VENDORPLUG_H
+#define __GSMD_VENDORPLUG_H
+
+#include "gsmd.h"
+
+/* gsmd vendor-specific plugin */
+
+enum gsmd_options {
+ GSMD_OPT_NONE,
+ GSMD_OPT_CIPHER_IND,
+};
+
+/* CIPHER_IND */
+enum gsmd_cipher_ind {
+ GSMD_CIPHER_IND_OFF,
+ GSMD_CIPHER_IND_ON,
+ GSMD_CIPHER_IND_SIM_FORBID,
+};
+
+struct gsmd_vendorspecific {
+ /* callback function to parse unknown unsolicited responses */
+ int (*parse_unsolicit)(void);
+ int (*getopt)(struct gsmd *gh, int optname, void *optval, int *optlen);
+ int (*setopt)(struct gsmd *gh, int optname, const void *optval, int optlen);
+};
+
+/* ciphering indications */
+
+#endif
personal git repositories of Harald Welte. Your mileage may vary