713 lines
18 KiB
Diff
713 lines
18 KiB
Diff
Upstream-Status: Inappropriate [not used]
|
|
|
|
From 516d67c679101d1503dbd4c0613bcd6ff1b604e4 Mon Sep 17 00:00:00 2001
|
|
From: Andrzej Zaborowski <balrog@zabor.org>
|
|
Date: Wed, 19 Sep 2007 14:03:28 +0200
|
|
Subject: [PATCH] Introduce ports.
|
|
|
|
---
|
|
include/gsmd/atcmd.h | 2 +-
|
|
include/gsmd/gsmd.h | 7 +-
|
|
include/gsmd/uart.h | 28 ++++++
|
|
include/gsmd/vendorplugin.h | 4 +-
|
|
src/gsmd/Makefile.am | 2 +-
|
|
src/gsmd/atcmd.c | 177 +++++++++++++++++---------------------
|
|
src/gsmd/gsmd.c | 64 ++------------
|
|
src/gsmd/uart.c | 202 +++++++++++++++++++++++++++++++++++++++++++
|
|
8 files changed, 328 insertions(+), 158 deletions(-)
|
|
create mode 100644 include/gsmd/uart.h
|
|
create mode 100644 src/gsmd/uart.c
|
|
|
|
diff --git a/include/gsmd/atcmd.h b/include/gsmd/atcmd.h
|
|
index 0d6c62a..a1af6a0 100644
|
|
--- a/include/gsmd/atcmd.h
|
|
+++ b/include/gsmd/atcmd.h
|
|
@@ -9,7 +9,7 @@ typedef int atcmd_cb_t(struct gsmd_atcmd *cmd, void *ctx, char *resp);
|
|
|
|
extern struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen, atcmd_cb_t *cb, void *ctx, u_int16_t id);
|
|
extern int atcmd_submit(struct gsmd *g, struct gsmd_atcmd *cmd);
|
|
-extern int atcmd_init(struct gsmd *g, int sockfd);
|
|
+extern int atcmd_init(struct gsmd *g, struct gsmd_port *port);
|
|
extern void atcmd_drain(int fd);
|
|
|
|
#endif /* __GSMD__ */
|
|
diff --git a/include/gsmd/gsmd.h b/include/gsmd/gsmd.h
|
|
index ed334f1..4afdf66 100644
|
|
--- a/include/gsmd/gsmd.h
|
|
+++ b/include/gsmd/gsmd.h
|
|
@@ -10,6 +10,7 @@
|
|
#include <gsmd/machineplugin.h>
|
|
#include <gsmd/vendorplugin.h>
|
|
#include <gsmd/select.h>
|
|
+#include <gsmd/uart.h>
|
|
#include <gsmd/state.h>
|
|
|
|
void *gsmd_tallocs;
|
|
@@ -52,6 +53,7 @@ enum llparse_state {
|
|
#define MLPARSE_BUF_SIZE 65535
|
|
|
|
struct llparser {
|
|
+ struct gsmd_port *port;
|
|
enum llparse_state state;
|
|
unsigned int len;
|
|
unsigned int flags;
|
|
@@ -70,7 +72,7 @@ struct gsmd;
|
|
struct gsmd {
|
|
unsigned int flags;
|
|
int interpreter_ready;
|
|
- struct gsmd_fd gfd_uart;
|
|
+ struct gsmd_uart uart;
|
|
struct gsmd_fd gfd_sock;
|
|
struct llparser llp;
|
|
struct llist_head users;
|
|
@@ -81,9 +83,10 @@ struct gsmd {
|
|
struct gsmd_device_state dev_state;
|
|
|
|
struct llist_head operators; /* cached list of operator names */
|
|
- unsigned char *mlbuf; /* ml_parse buffer */
|
|
+ char *mlbuf; /* ml_parse buffer */
|
|
unsigned int mlbuf_len;
|
|
int mlunsolicited;
|
|
+ int clear_to_send;
|
|
};
|
|
|
|
struct gsmd_user {
|
|
diff --git a/include/gsmd/uart.h b/include/gsmd/uart.h
|
|
new file mode 100644
|
|
index 0000000..a006fa7
|
|
--- /dev/null
|
|
+++ b/include/gsmd/uart.h
|
|
@@ -0,0 +1,28 @@
|
|
+#ifndef __GSMD_UART_H
|
|
+#define __GSMD_UART_H
|
|
+
|
|
+#ifdef __GSMD__
|
|
+
|
|
+struct gsmd_port {
|
|
+ int (*write)(struct gsmd_port *port, const char data[], int len);
|
|
+ int (*set_break)(struct gsmd_port *port, int state);
|
|
+ /* more parameters here */
|
|
+ int (*newdata_cb)(void *opaque, const char data[], int len);
|
|
+ void *newdata_opaque;
|
|
+};
|
|
+
|
|
+struct gsmd_uart {
|
|
+ struct gsmd_port port;
|
|
+ struct gsmd_fd gfd;
|
|
+ char txfifo[2048];
|
|
+ int tx_start;
|
|
+ int tx_len;
|
|
+};
|
|
+
|
|
+extern int set_baudrate(int fd, int baudrate, int hwflow);
|
|
+extern void uart_drain(int fd);
|
|
+extern int uart_init(struct gsmd_uart *uart, int sockfd);
|
|
+
|
|
+#endif /* __GSMD__ */
|
|
+
|
|
+#endif
|
|
diff --git a/include/gsmd/vendorplugin.h b/include/gsmd/vendorplugin.h
|
|
index 1911fef..1c82790 100644
|
|
--- a/include/gsmd/vendorplugin.h
|
|
+++ b/include/gsmd/vendorplugin.h
|
|
@@ -11,8 +11,8 @@ struct gsmd_unsolicit;
|
|
|
|
struct gsmd_vendor_plugin {
|
|
struct llist_head list;
|
|
- unsigned char *name;
|
|
- unsigned char *ext_chars;
|
|
+ char *name;
|
|
+ char *ext_chars;
|
|
unsigned int num_unsolicit;
|
|
const struct gsmd_unsolicit *unsolicit;
|
|
int (*detect)(struct gsmd *g);
|
|
diff --git a/src/gsmd/Makefile.am b/src/gsmd/Makefile.am
|
|
index 9ac45ee..110b757 100644
|
|
--- a/src/gsmd/Makefile.am
|
|
+++ b/src/gsmd/Makefile.am
|
|
@@ -13,7 +13,7 @@ sbin_PROGRAMS = gsmd
|
|
gsmd_CFLAGS = -D PLUGINDIR=\"$(plugindir)\"
|
|
gsmd_SOURCES = gsmd.c atcmd.c select.c machine.c vendor.c unsolicited.c log.c \
|
|
usock.c talloc.c timer.c operator_cache.c ext_response.c \
|
|
- sms_cb.c sms_pdu.c
|
|
+ sms_cb.c sms_pdu.c uart.c
|
|
gsmd_LDADD = -ldl
|
|
gsmd_LDFLAGS = -Wl,--export-dynamic
|
|
|
|
diff --git a/src/gsmd/atcmd.c b/src/gsmd/atcmd.c
|
|
index 2ef6a10..27dfa41 100644
|
|
--- a/src/gsmd/atcmd.c
|
|
+++ b/src/gsmd/atcmd.c
|
|
@@ -159,7 +159,8 @@ static int llparse_byte(struct llparser *llp, char byte)
|
|
return ret;
|
|
}
|
|
|
|
-static int llparse_string(struct llparser *llp, char *buf, unsigned int len)
|
|
+static int llparse_string(struct llparser *llp, const char *buf,
|
|
+ unsigned int len)
|
|
{
|
|
while (len--) {
|
|
int rc = llparse_byte(llp, *(buf++));
|
|
@@ -187,6 +188,55 @@ static int llparse_init(struct llparser *llp)
|
|
return 0;
|
|
}
|
|
|
|
+/* See if we can now send more commands to the port */
|
|
+static void atcmd_wake_queue(struct gsmd *g)
|
|
+{
|
|
+ int len, rc;
|
|
+ char *cr;
|
|
+
|
|
+ /* write pending commands to UART */
|
|
+ while (g->interpreter_ready && g->clear_to_send) {
|
|
+ struct gsmd_atcmd *pos, *pos2;
|
|
+ llist_for_each_entry_safe(pos, pos2, &g->pending_atcmds, list) {
|
|
+ cr = strchr(pos->cur, '\n');
|
|
+ if (cr)
|
|
+ len = cr - pos->cur;
|
|
+ else
|
|
+ len = pos->buflen;
|
|
+ rc = g->llp.port->write(g->llp.port, pos->cur, len);
|
|
+ if (rc == 0) {
|
|
+ gsmd_log(GSMD_ERROR,
|
|
+ "write returns 0, aborting\n");
|
|
+ break;
|
|
+ }
|
|
+ if (cr && rc == len)
|
|
+ rc ++; /* Skip the \n */
|
|
+ pos->buflen -= rc;
|
|
+ pos->cur += rc;
|
|
+ g->llp.port->write(g->llp.port, "\r", 1);
|
|
+
|
|
+ if (!pos->buflen) {
|
|
+ /* 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);
|
|
+
|
|
+ /* we only send one cmd at the moment */
|
|
+ g->clear_to_send = 0;
|
|
+ break;
|
|
+ } else {
|
|
+ /* The write was short or the atcmd has more
|
|
+ * lines to send after a "> ". */
|
|
+ if (rc < len)
|
|
+ break;
|
|
+ g->clear_to_send = 0;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/* mid-level parser */
|
|
|
|
static int parse_final_result(const char *res)
|
|
@@ -216,6 +266,7 @@ static int ml_parse(const char *buf, int len, void *ctx)
|
|
g->interpreter_ready = 1;
|
|
gsmd_initsettings(g);
|
|
gmsd_alive_start(g);
|
|
+ atcmd_wake_queue(g);
|
|
return 0;
|
|
}
|
|
|
|
@@ -316,6 +367,7 @@ static int ml_parse(const char *buf, int len, void *ctx)
|
|
} else {
|
|
DEBUGP("Calling cmd->cb()\n");
|
|
cmd->resp = g->mlbuf;
|
|
+ g->mlbuf[g->mlbuf_len] = 0;
|
|
rc = cmd->cb(cmd, cmd->ctx, cmd->resp);
|
|
DEBUGP("Clearing mlbuf\n");
|
|
}
|
|
@@ -370,12 +422,15 @@ static int ml_parse(const char *buf, int len, void *ctx)
|
|
if (g->mlbuf_len)
|
|
g->mlbuf[g->mlbuf_len ++] = '\n';
|
|
DEBUGP("Appending buf to mlbuf\n");
|
|
- if (len > MLPARSE_BUF_SIZE - g->mlbuf_len)
|
|
+ if (len > MLPARSE_BUF_SIZE - g->mlbuf_len) {
|
|
len = MLPARSE_BUF_SIZE - g->mlbuf_len;
|
|
+ gsmd_log(GSMD_NOTICE, "g->mlbuf overrun\n");
|
|
+ }
|
|
memcpy(g->mlbuf + g->mlbuf_len, buf, len);
|
|
g->mlbuf_len += len;
|
|
|
|
if (g->mlunsolicited) {
|
|
+ g->mlbuf[g->mlbuf_len] = 0;
|
|
rc = unsolicited_parse(g, g->mlbuf, g->mlbuf_len,
|
|
strchr(g->mlbuf, ':') + 1);
|
|
if (rc == -EAGAIN) {
|
|
@@ -422,8 +477,11 @@ final_cb:
|
|
|
|
/* if we're finished with current commands, but still have pending
|
|
* commands: we want to WRITE again */
|
|
- if (llist_empty(&g->busy_atcmds) && !llist_empty(&g->pending_atcmds))
|
|
- g->gfd_uart.when |= GSMD_FD_WRITE;
|
|
+ if (llist_empty(&g->busy_atcmds)) {
|
|
+ g->clear_to_send = 1;
|
|
+ if (!llist_empty(&g->pending_atcmds))
|
|
+ atcmd_wake_queue(g);
|
|
+ }
|
|
|
|
return rc;
|
|
}
|
|
@@ -433,85 +491,23 @@ static int atcmd_prompt(void *data)
|
|
{
|
|
struct gsmd *g = data;
|
|
|
|
- g->gfd_uart.when |= GSMD_FD_WRITE;
|
|
+ g->clear_to_send = 1;
|
|
+ atcmd_wake_queue(g);
|
|
}
|
|
|
|
/* callback to be called if [virtual] UART has some data for us */
|
|
-static int atcmd_select_cb(int fd, unsigned int what, void *data)
|
|
+static int atcmd_newdata_cb(void *opaque, const char data[], int len)
|
|
{
|
|
- int len, rc;
|
|
- static char rxbuf[1024];
|
|
- struct gsmd *g = data;
|
|
- char *cr;
|
|
-
|
|
- if (what & GSMD_FD_READ) {
|
|
- memset(rxbuf, 0, sizeof(rxbuf));
|
|
- while ((len = read(fd, rxbuf, sizeof(rxbuf)))) {
|
|
- if (len < 0) {
|
|
- if (errno == EAGAIN)
|
|
- return 0;
|
|
- gsmd_log(GSMD_NOTICE, "ERROR reading from fd %u: %d (%s)\n", fd, len,
|
|
- strerror(errno));
|
|
- return len;
|
|
- }
|
|
- rc = llparse_string(&g->llp, rxbuf, len);
|
|
- if (rc < 0) {
|
|
- gsmd_log(GSMD_ERROR, "ERROR during llparse_string: %d\n", rc);
|
|
- return rc;
|
|
- }
|
|
- }
|
|
- }
|
|
-
|
|
- /* write pending commands to UART */
|
|
- if ((what & GSMD_FD_WRITE) && g->interpreter_ready) {
|
|
- struct gsmd_atcmd *pos, *pos2;
|
|
- llist_for_each_entry_safe(pos, pos2, &g->pending_atcmds, list) {
|
|
- cr = strchr(pos->cur, '\n');
|
|
- if (cr)
|
|
- len = cr - pos->cur;
|
|
- else
|
|
- len = pos->buflen - 1; /* assuming zero-terminated strings */
|
|
- rc = write(fd, pos->cur, len);
|
|
- if (rc == 0) {
|
|
- gsmd_log(GSMD_ERROR, "write returns 0, aborting\n");
|
|
- break;
|
|
- } else if (rc < 0) {
|
|
- gsmd_log(GSMD_ERROR, "error during write to fd %d: %d\n",
|
|
- fd, rc);
|
|
- return rc;
|
|
- }
|
|
- if (!cr || rc == len)
|
|
- rc ++; /* Skip the \n or \0 */
|
|
- pos->buflen -= rc;
|
|
- pos->cur += rc;
|
|
- write(fd, "\r", 1);
|
|
-
|
|
- if (!pos->buflen) {
|
|
- /* 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);
|
|
-
|
|
- /* we only send one cmd at the moment */
|
|
- break;
|
|
- } else {
|
|
- /* The write was short or the atcmd has more
|
|
- * lines to send after a "> ". */
|
|
- if (rc < len)
|
|
- return 0;
|
|
- break;
|
|
- }
|
|
- }
|
|
+ struct gsmd *g = opaque;
|
|
+ int rc;
|
|
|
|
- /* Either pending_atcmds is empty or a command has to wait */
|
|
- g->gfd_uart.when &= ~GSMD_FD_WRITE;
|
|
- }
|
|
+ rc = llparse_string(&g->llp, data, len);
|
|
+ if (rc < 0)
|
|
+ gsmd_log(GSMD_ERROR, "ERROR during llparse_string: %d\n", rc);
|
|
|
|
- return 0;
|
|
+ return rc;
|
|
}
|
|
|
|
-
|
|
struct gsmd_atcmd *atcmd_fill(const char *cmd, int rlen,
|
|
atcmd_cb_t cb, void *ctx, u_int16_t id)
|
|
{
|
|
@@ -544,36 +540,18 @@ int atcmd_submit(struct gsmd *g, struct gsmd_atcmd *cmd)
|
|
{
|
|
DEBUGP("submitting command `%s'\n", cmd->buf);
|
|
|
|
- if (llist_empty(&g->pending_atcmds))
|
|
- g->gfd_uart.when |= GSMD_FD_WRITE;
|
|
+ llist_empty(&g->pending_atcmds);
|
|
llist_add_tail(&cmd->list, &g->pending_atcmds);
|
|
+ atcmd_wake_queue(g);
|
|
|
|
return 0;
|
|
}
|
|
|
|
-void atcmd_drain(int fd)
|
|
-{
|
|
- int rc;
|
|
- struct termios t;
|
|
- rc = tcflush(fd, TCIOFLUSH);
|
|
- rc = tcgetattr(fd, &t);
|
|
- DEBUGP("c_iflag = 0x%08x, c_oflag = 0x%08x, c_cflag = 0x%08x, c_lflag = 0x%08x\n",
|
|
- t.c_iflag, t.c_oflag, t.c_cflag, t.c_lflag);
|
|
- t.c_iflag = t.c_oflag = 0;
|
|
- cfmakeraw(&t);
|
|
- rc = tcsetattr(fd, TCSANOW, &t);
|
|
-}
|
|
-
|
|
/* init atcmd parser */
|
|
-int atcmd_init(struct gsmd *g, int sockfd)
|
|
+int atcmd_init(struct gsmd *g, struct gsmd_port *port)
|
|
{
|
|
__atcmd_ctx = talloc_named_const(gsmd_tallocs, 1, "atcmds");
|
|
|
|
- g->gfd_uart.fd = sockfd;
|
|
- g->gfd_uart.when = GSMD_FD_READ;
|
|
- g->gfd_uart.data = g;
|
|
- g->gfd_uart.cb = &atcmd_select_cb;
|
|
-
|
|
INIT_LLIST_HEAD(&g->pending_atcmds);
|
|
INIT_LLIST_HEAD(&g->busy_atcmds);
|
|
|
|
@@ -581,7 +559,9 @@ int atcmd_init(struct gsmd *g, int sockfd)
|
|
|
|
g->mlbuf_len = 0;
|
|
g->mlunsolicited = 0;
|
|
+ g->clear_to_send = 1;
|
|
|
|
+ g->llp.port = port;
|
|
g->llp.cur = g->llp.buf;
|
|
g->llp.len = sizeof(g->llp.buf);
|
|
g->llp.cb = &ml_parse;
|
|
@@ -589,5 +569,8 @@ int atcmd_init(struct gsmd *g, int sockfd)
|
|
g->llp.ctx = g;
|
|
g->llp.flags = LGSM_ATCMD_F_EXTENDED;
|
|
|
|
- return gsmd_register_fd(&g->gfd_uart);
|
|
+ port->newdata_opaque = g;
|
|
+ port->newdata_cb = atcmd_newdata_cb;
|
|
+
|
|
+ return 0;
|
|
}
|
|
diff --git a/src/gsmd/gsmd.c b/src/gsmd/gsmd.c
|
|
index 51b4f2c..846bd17 100644
|
|
--- a/src/gsmd/gsmd.c
|
|
+++ b/src/gsmd/gsmd.c
|
|
@@ -26,7 +26,6 @@
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
-#include <termios.h>
|
|
#include <signal.h>
|
|
|
|
#define _GNU_SOURCE
|
|
@@ -247,56 +246,6 @@ int gsmd_initsettings(struct gsmd *gsmd)
|
|
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 },
|
|
- { 230400, B230400 },
|
|
- { 460800, B460800 },
|
|
- { 921600, B921600 },
|
|
-};
|
|
-
|
|
-static int set_baudrate(int fd, int baudrate, int hwflow)
|
|
-{
|
|
- 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;
|
|
-
|
|
- if (hwflow)
|
|
- ti.c_cflag |= CRTSCTS;
|
|
- else
|
|
- ti.c_cflag &= ~CRTSCTS;
|
|
-
|
|
- return tcsetattr(fd, 0, &ti);
|
|
-}
|
|
-
|
|
static int gsmd_initialize(struct gsmd *g)
|
|
{
|
|
INIT_LLIST_HEAD(&g->users);
|
|
@@ -478,14 +427,19 @@ int main(int argc, char **argv)
|
|
if (wait >= 0)
|
|
g.interpreter_ready = !wait;
|
|
|
|
- if (atcmd_init(&g, fd) < 0) {
|
|
+ if (uart_init(&g.uart, fd) < 0) {
|
|
fprintf(stderr, "can't initialize UART device\n");
|
|
exit(1);
|
|
}
|
|
|
|
- write(fd, "\r", 1);
|
|
- sleep(1);
|
|
- atcmd_drain(fd);
|
|
+ if (atcmd_init(&g, &g.uart.port) < 0) {
|
|
+ fprintf(stderr, "can't initialize AT parser\n");
|
|
+ exit(1);
|
|
+ }
|
|
+ write(fd, "\r", 1);
|
|
+ sleep(1);
|
|
+
|
|
+ uart_drain(fd);
|
|
|
|
if (usock_init(&g) < 0) {
|
|
fprintf(stderr, "can't open unix socket\n");
|
|
diff --git a/src/gsmd/uart.c b/src/gsmd/uart.c
|
|
new file mode 100644
|
|
index 0000000..22a4a5c
|
|
--- /dev/null
|
|
+++ b/src/gsmd/uart.c
|
|
@@ -0,0 +1,202 @@
|
|
+/* Wrapper for the physical UART in a struct gsmd_port abstraction.
|
|
+ *
|
|
+ * Copyright (C) 2007 OpenMoko, Inc.
|
|
+ * Written by Andrzej Zaborowski <andrew@openedhand.com>
|
|
+ *
|
|
+ * 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
|
|
+ */
|
|
+
|
|
+#include <string.h>
|
|
+#include <fcntl.h>
|
|
+#include <termios.h>
|
|
+#include <unistd.h>
|
|
+#include <errno.h>
|
|
+
|
|
+#include "gsmd.h"
|
|
+
|
|
+#include <gsmd/gsmd.h>
|
|
+
|
|
+void uart_drain(int fd)
|
|
+{
|
|
+ int rc;
|
|
+ struct termios t;
|
|
+ rc = tcflush(fd, TCIOFLUSH);
|
|
+ rc = tcgetattr(fd, &t);
|
|
+ DEBUGP(
|
|
+ "c_iflag = 0x%08x, c_oflag = 0x%08x, "
|
|
+ "c_cflag = 0x%08x, c_lflag = 0x%08x\n",
|
|
+ t.c_iflag, t.c_oflag, t.c_cflag, t.c_lflag);
|
|
+ t.c_iflag = t.c_oflag = 0;
|
|
+ cfmakeraw(&t);
|
|
+ rc = tcsetattr(fd, TCSANOW, &t);
|
|
+}
|
|
+
|
|
+struct bdrt {
|
|
+ int bps;
|
|
+ u_int32_t b;
|
|
+};
|
|
+
|
|
+static struct bdrt bdrts[] = {
|
|
+ { 0, B0 },
|
|
+ { 9600, B9600 },
|
|
+ { 19200, B19200 },
|
|
+ { 38400, B38400 },
|
|
+ { 57600, B57600 },
|
|
+ { 115200, B115200 },
|
|
+ { 230400, B230400 },
|
|
+ { 460800, B460800 },
|
|
+ { 921600, B921600 },
|
|
+};
|
|
+
|
|
+int set_baudrate(int fd, int baudrate, int hwflow)
|
|
+{
|
|
+ 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;
|
|
+
|
|
+ if (hwflow)
|
|
+ ti.c_cflag |= CRTSCTS;
|
|
+ else
|
|
+ ti.c_cflag &= ~CRTSCTS;
|
|
+
|
|
+ return tcsetattr(fd, 0, &ti);
|
|
+}
|
|
+
|
|
+static int uart_select_cb(int fd, unsigned int what, void *data)
|
|
+{
|
|
+ struct gsmd_uart *uart = (struct gsmd_uart *) data;
|
|
+ static char rxbuf[2048];
|
|
+ int rc, len;
|
|
+
|
|
+ if ((what & GSMD_FD_READ) && uart->port.newdata_cb) {
|
|
+ while ((len = read(fd, rxbuf, sizeof(rxbuf)))) {
|
|
+ if (len < 0) {
|
|
+ if (errno == EAGAIN || errno == EINTR)
|
|
+ return 0;
|
|
+ gsmd_log(GSMD_NOTICE, "ERROR reading from "
|
|
+ "fd %u: %d (%s)\n", fd, errno,
|
|
+ strerror(errno));
|
|
+ return -errno;
|
|
+ }
|
|
+
|
|
+ rc = uart->port.newdata_cb(
|
|
+ uart->port.newdata_opaque,
|
|
+ rxbuf,
|
|
+ len);
|
|
+ if (rc < 0)
|
|
+ return rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* Write pending data to UART. */
|
|
+ if ((what & GSMD_FD_WRITE) && uart->tx_len) {
|
|
+ while (uart->tx_start + uart->tx_len >= sizeof(uart->txfifo)) {
|
|
+ len = sizeof(uart->txfifo) - uart->tx_start;
|
|
+ rc = write(fd, &uart->txfifo[uart->tx_start], len);
|
|
+ if (rc < 0 && errno != EINTR) {
|
|
+ if (errno == EAGAIN)
|
|
+ return 0;
|
|
+ gsmd_log(GSMD_NOTICE, "ERROR writing "
|
|
+ "fd %u: %d (%s)\n", fd, errno,
|
|
+ strerror(errno));
|
|
+ return -errno;
|
|
+ }
|
|
+
|
|
+ if (rc > 0) {
|
|
+ uart->tx_start += rc;
|
|
+ uart->tx_len -= rc;
|
|
+ }
|
|
+ }
|
|
+ uart->tx_start &= sizeof(uart->txfifo) - 1;
|
|
+
|
|
+ while (uart->tx_len) {
|
|
+ rc = write(fd, &uart->txfifo[uart->tx_start],
|
|
+ uart->tx_len);
|
|
+ if (rc < 0 && errno != EINTR) {
|
|
+ if (errno == EAGAIN)
|
|
+ return 0;
|
|
+ gsmd_log(GSMD_NOTICE, "ERROR writing "
|
|
+ "fd %u: %d (%s)\n", fd, errno,
|
|
+ strerror(errno));
|
|
+ return -errno;
|
|
+ }
|
|
+
|
|
+ if (rc > 0) {
|
|
+ uart->tx_start += rc;
|
|
+ uart->tx_len -= rc;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ /* If we reached here, there's no more data for the moment. */
|
|
+ uart->gfd.when &= ~GSMD_FD_WRITE;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int uart_write(struct gsmd_port *port, const char data[], int len)
|
|
+{
|
|
+ struct gsmd_uart *uart = (struct gsmd_uart *) port;
|
|
+ int start = (uart->tx_start + uart->tx_len) &
|
|
+ (sizeof(uart->txfifo) - 1);
|
|
+ int space = sizeof(uart->txfifo) - start;
|
|
+
|
|
+ if (uart->tx_len + len > sizeof(uart->txfifo))
|
|
+ len = sizeof(uart->txfifo) - uart->tx_len;
|
|
+
|
|
+ if (len)
|
|
+ uart->gfd.when |= GSMD_FD_WRITE;
|
|
+
|
|
+ if (len > space) {
|
|
+ memcpy(uart->txfifo + start, data, space);
|
|
+ memcpy(uart->txfifo, data + space, len - space);
|
|
+ } else
|
|
+ memcpy(uart->txfifo + start, data, len);
|
|
+
|
|
+ uart->tx_len += len;
|
|
+ return len;
|
|
+}
|
|
+
|
|
+int uart_init(struct gsmd_uart *uart, int sockfd)
|
|
+{
|
|
+ uart->gfd.fd = sockfd;
|
|
+ uart->gfd.when = GSMD_FD_READ;
|
|
+ uart->gfd.data = uart;
|
|
+ uart->gfd.cb = &uart_select_cb;
|
|
+
|
|
+ uart->port.write = uart_write;
|
|
+
|
|
+ return gsmd_register_fd(&uart->gfd);
|
|
+}
|
|
--
|
|
1.5.2.1
|
|
|