summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.txt54
-rw-r--r--include/common/gsmd_proto.h9
-rw-r--r--include/common/linux_list.h364
-rw-r--r--include/gsmd/event.h52
-rw-r--r--include/gsmd/gsmd.h4
-rw-r--r--include/gsmd/usock.h83
-rw-r--r--include/libgsmd/event.h14
-rw-r--r--include/libgsmd/handset.h15
-rw-r--r--include/libgsmd/libgsmd.h50
-rw-r--r--include/libgsmd/misc.h72
-rw-r--r--include/libgsmd/phonebook.h51
-rw-r--r--include/libgsmd/voicecall.h18
-rw-r--r--logs/screenlog.0500
-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
-rw-r--r--src/libgsmd/lgsm_internals.h4
-rw-r--r--src/libgsmd/libgsmd.c69
-rw-r--r--src/libgsmd/libgsmd_input.c13
-rw-r--r--src/libgsmd/libgsmd_voicecall.c24
32 files changed, 2707 insertions, 0 deletions
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..5ea8894
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,54 @@
+
+
+1. GSMD api towards libgsmd
+ - provides api for other processes to interact with GSM subsystem
+
+2. GSMD api towards vendor-specific plugins
+ - implement vendor-specific AT commands
+
+3. libgsmd api towards applications
+ - wraps GSMD-libgsmd api into C functions that can be used by applications
+
+
+
+code flow in gsmd:
+
+- select loop detects data has arrived on user socket
+ - gsmd_usock_user_cb()
+ - if this is atcmd passthrough,
+ - alloc + fill in gsmd_atcmd
+ - atcmd_submit()
+ - append it to pending_atcmds
+ - mark interest of writing to UART
+ - if this is not passthrough
+ - do whatever handling, including enqueueing of atcmds
+- select loop detects UART is marked writable
+ - atcmd_select_cb()
+ - iterate over list of pending_atcmds()
+ - write a particular atcmd to UART
+ - move atcmd to busy_atcmds
+- select loop detects UART is marked readable
+ - atcmd_select_cb()
+ - read up to 1024 bytes into buffer
+ - llparse_string()
+ - llparse_byte()
+ - llparse_append()
+ - llp->cb == ml_parse()
+ - if this is not unsolicited
+ - call cmd->cb()
+ - alloc + fill ucmd reply
+ - add to finished_ucmds
+ - mark user sock writable
+ - unlink + free atcmd
+ - if this is unsolicited
+ - unsolicited_parse()
+ - usock_evt_send()
+ - alloc + fill ucmd reply
+ - add to finished_ucmds
+ - mark user sock writable
+- select loop detects user sock writeability
+ - gsmd_usock_user_cb()
+ - iterate over finished_ucmds
+ - write ucmd
+ - unlink + free ucmd
+
diff --git a/include/common/gsmd_proto.h b/include/common/gsmd_proto.h
new file mode 100644
index 0000000..cf0f99d
--- /dev/null
+++ b/include/common/gsmd_proto.h
@@ -0,0 +1,9 @@
+#ifndef _GSMD_PROTO_H
+#define _GSMD_PROTO_H
+
+/* this header file describes the protocol between gsmd and libgsmd
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de> */
+
+
+
+#endif /* _GSMD_PROTO_H */
diff --git a/include/common/linux_list.h b/include/common/linux_list.h
new file mode 100644
index 0000000..05546ad
--- /dev/null
+++ b/include/common/linux_list.h
@@ -0,0 +1,364 @@
+#ifndef _LINUX_LLIST_H
+#define _LINUX_LLIST_H
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
+#include <stddef.h>
+
+#ifndef inline
+#define inline __inline__
+#endif
+
+static inline void prefetch(const void *x) {;}
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized llist entries.
+ */
+#define LLIST_POISON1 ((void *) 0x00100100)
+#define LLIST_POISON2 ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked llist implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct llist_head {
+ struct llist_head *next, *prev;
+};
+
+#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LLIST_HEAD(name) \
+ struct llist_head name = LLIST_HEAD_INIT(name)
+
+#define INIT_LLIST_HEAD(ptr) do { \
+ (ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_add(struct llist_head *new,
+ struct llist_head *prev,
+ struct llist_head *next)
+{
+ next->prev = new;
+ new->next = next;
+ new->prev = prev;
+ prev->next = new;
+}
+
+/**
+ * llist_add - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void llist_add(struct llist_head *new, struct llist_head *head)
+{
+ __llist_add(new, head, head->next);
+}
+
+/**
+ * llist_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void llist_add_tail(struct llist_head *new, struct llist_head *head)
+{
+ __llist_add(new, head->prev, head);
+}
+
+/*
+ * Delete a llist entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
+{
+ next->prev = prev;
+ prev->next = next;
+}
+
+/**
+ * llist_del - deletes entry from llist.
+ * @entry: the element to delete from the llist.
+ * Note: llist_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void llist_del(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ entry->next = LLIST_POISON1;
+ entry->prev = LLIST_POISON2;
+}
+
+/**
+ * llist_del_init - deletes entry from llist and reinitialize it.
+ * @entry: the element to delete from the llist.
+ */
+static inline void llist_del_init(struct llist_head *entry)
+{
+ __llist_del(entry->prev, entry->next);
+ INIT_LLIST_HEAD(entry);
+}
+
+/**
+ * llist_move - delete from one llist and add as another's head
+ * @llist: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void llist_move(struct llist_head *llist, struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add(llist, head);
+}
+
+/**
+ * llist_move_tail - delete from one llist and add as another's tail
+ * @llist: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void llist_move_tail(struct llist_head *llist,
+ struct llist_head *head)
+{
+ __llist_del(llist->prev, llist->next);
+ llist_add_tail(llist, head);
+}
+
+/**
+ * llist_empty - tests whether a llist is empty
+ * @head: the llist to test.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+ return head->next == head;
+}
+
+static inline void __llist_splice(struct llist_head *llist,
+ struct llist_head *head)
+{
+ struct llist_head *first = llist->next;
+ struct llist_head *last = llist->prev;
+ struct llist_head *at = head->next;
+
+ first->prev = head;
+ head->next = first;
+
+ last->next = at;
+ at->prev = last;
+}
+
+/**
+ * llist_splice - join two llists
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ */
+static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
+{
+ if (!llist_empty(llist))
+ __llist_splice(llist, head);
+}
+
+/**
+ * llist_splice_init - join two llists and reinitialise the emptied llist.
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ *
+ * The llist at @llist is reinitialised
+ */
+static inline void llist_splice_init(struct llist_head *llist,
+ struct llist_head *head)
+{
+ if (!llist_empty(llist)) {
+ __llist_splice(llist, head);
+ INIT_LLIST_HEAD(llist);
+ }
+}
+
+/**
+ * llist_entry - get the struct for this entry
+ * @ptr: the &struct llist_head pointer.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_entry(ptr, type, member) \
+ container_of(ptr, type, member)
+
+/**
+ * llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, prefetch(pos->next))
+
+/**
+ * __llist_for_each - iterate over a llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ *
+ * This variant differs from llist_for_each() in that it's the
+ * simplest possible llist iteration code, no prefetching is done.
+ * Use this for code that knows the llist to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __llist_for_each(pos, head) \
+ for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * llist_for_each_prev - iterate over a llist backwards
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_prev(pos, head) \
+ for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+ pos = pos->prev, prefetch(pos->prev))
+
+/**
+ * llist_for_each_safe - iterate over a llist safe against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, n = pos->next)
+
+/**
+ * llist_for_each_entry - iterate over llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_reverse - iterate backwards over llist of given type.
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_reverse(pos, head, member) \
+ for (pos = llist_entry((head)->prev, typeof(*pos), member), \
+ prefetch(pos->member.prev); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.prev, typeof(*pos), member), \
+ prefetch(pos->member.prev))
+
+/**
+ * llist_for_each_entry_continue - iterate over llist of given type
+ * continuing after existing point
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_continue(pos, head, member) \
+ for (pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
+ * @pos: the type * to use as a loop counter.
+ * @n: another type * to use as temporary storage
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ n = llist_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (head); \
+ pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * llist_for_each_rcu - iterate over an rcu-protected llist
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+
+#define __llist_for_each_rcu(pos, head) \
+ for (pos = (head)->next; pos != (head); \
+ pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+
+/**
+ * llist_for_each_safe_rcu - iterate over an rcu-protected llist safe
+ * against removal of llist entry
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @n: another &struct llist_head to use as temporary storage
+ * @head: the head for your llist.
+ */
+#define llist_for_each_safe_rcu(pos, n, head) \
+ for (pos = (head)->next, n = pos->next; pos != (head); \
+ pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * llist_for_each_entry_rcu - iterate over rcu llist of given type
+ * @pos: the type * to use as a loop counter.
+ * @head: the head for your llist.
+ * @member: the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_rcu(pos, head, member) \
+ for (pos = llist_entry((head)->next, typeof(*pos), member), \
+ prefetch(pos->member.next); \
+ &pos->member != (head); \
+ pos = llist_entry(pos->member.next, typeof(*pos), member), \
+ ({ smp_read_barrier_depends(); 0;}), \
+ prefetch(pos->member.next))
+
+
+/**
+ * llist_for_each_continue_rcu - iterate over an rcu-protected llist
+ * continuing after existing point.
+ * @pos: the &struct llist_head to use as a loop counter.
+ * @head: the head for your llist.
+ */
+#define llist_for_each_continue_rcu(pos, head) \
+ for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+ (pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+
+#endif
diff --git a/include/gsmd/event.h b/include/gsmd/event.h
new file mode 100644
index 0000000..9403164
--- /dev/null
+++ b/include/gsmd/event.h
@@ -0,0 +1,52 @@
+#ifndef _GSMD_EVENT_H
+#define _GSMD_EVENT_H
+
+enum gsmd_events {
+ GSMD_EVT_NONE = 0,
+ GSMD_EVT_IN_CALL = 1, /* Incoming call */
+ GSMD_EVT_IN_SMS = 2, /* Incoming SMS */
+ GSMD_EVT_IN_GPRS = 3, /* Network initiated GPRS */
+ GSMD_EVT_IN_CLIP = 4, /* Incoming CLIP */
+ GSMD_EVT_NETREG = 5, /* Network (un)registration event */
+ GSMD_EVT_SIGNAL = 6, /* Signal quality event */
+ GSMD_EVT_PIN = 7, /* Modem is waiting for some PIN/PUK */
+ GSMD_EVT_OUT_STATUS = 8, /* Outgoing call status */
+ GSMD_EVT_OUT_COLP = 9, /* Outgoing COLP */
+ GSMD_EVT_CALL_WAIT = 10, /* Call Waiting */
+};
+
+/* Chapter 8.3 */
+enum gsmd_pin_type { /* waiting for ... */
+ GSMD_PIN_NONE = 0, /* not for any PIN */
+ GSMD_PIN_SIM_PIN = 1, /* SIM PIN */
+ GSMD_PIN_SIM_PUK = 2, /* SIM PUK */
+ GSMD_PIN_PH_SIM_PIN = 3, /* phone-to-SIM passowrd */
+ GSMD_PIN_PH_FSIM_PIN = 4, /* phone-to-very-first SIM password */
+ GSMD_PIN_PH_FSIM_PUK = 5, /* phone-to-very-first SIM PUK password */
+ GSMD_PIN_SIM_PIN2 = 6, /* SIM PIN2 */
+ GSMD_PIN_SIM_PUK2 = 7, /* SIM PUK2 */
+ GSMD_PIN_PH_NET_PIN = 8, /* netwokr personalisation password */
+ GSMD_PIN_PH_NET_PUK = 9, /* network personalisation PUK */
+ GSMD_PIN_PH_NETSUB_PIN = 10, /* network subset personalisation PIN */
+ GSMD_PIN_PH_NETSUB_PUK = 11, /* network subset personalisation PUK */
+ GSMD_PIN_PH_SP_PIN = 12, /* service provider personalisation PIN */
+ GSMD_PIN_PH_SP_PUK = 13, /* service provider personalisation PUK */
+ GSMD_PIN_PH_CORP_PIN = 14, /* corporate personalisation PIN */
+ GSMD_PIN_PH_CORP_PUK = 15, /* corporate personalisation PUK */
+};
+
+enum gsmd_call_type {
+ GSMD_CALL_NONE = 0,
+ GSMD_CALL_UNSPEC = 1,
+ GSMD_CALL_VOICE = 2,
+ GSMD_CALL_FAX = 4,
+ GSMD_CALL_DATA_SYNC = 5,
+ GSMD_CALL_DATA_REL_ASYNC= 6,
+ GSMD_CALL_DATA_REL_SYNC = 7,
+};
+
+enum gsmd_netreg_state {
+ GSMD_NETREG_NONE = 0,
+};
+
+#endif
diff --git a/include/gsmd/gsmd.h b/include/gsmd/gsmd.h
new file mode 100644
index 0000000..f5b61d5
--- /dev/null
+++ b/include/gsmd/gsmd.h
@@ -0,0 +1,4 @@
+#ifndef _GSMD_H
+#define _GSMD_H
+
+#endif /* _GSMD_H */
diff --git a/include/gsmd/usock.h b/include/gsmd/usock.h
new file mode 100644
index 0000000..43ed792
--- /dev/null
+++ b/include/gsmd/usock.h
@@ -0,0 +1,83 @@
+#ifndef _GSMD_USOCK_H
+#define _GSMD_USOCK_H
+
+#include <gsmd/event.h>
+
+#define GSMD_UNIX_SOCKET "\0gsmd"
+#define GSMD_UNIX_SOCKET_TYPE SOCK_SEQPACKET
+
+#define GSMD_PROTO_VERSION 1
+
+enum gsmd_prot_cmd {
+ GSMD_PCMD_NONE,
+ GSMD_PCMD_EVT_SUBSCRIPTIONS, /* alter event subscriptions */
+ GSMD_PCMD_PASSTHROUGH, /* transparent atcmd passthrough */
+};
+
+enum gsmd_pcmd_result {
+ GSMD_PCMD_OK = 0,
+ GSMD_PCMD_ERR_UNSPEC = 0xff,
+};
+
+struct gsmd_prot_hdr {
+ u_int16_t cmd;
+ u_int8_t result;
+ u_int8_t version;
+} __attribute__((packed));
+
+
+enum gsmd_msg_type {
+ GSMD_MSG_NONE = 0,
+ GSMD_MSG_EVENT = 1,
+ GSMD_MSG_PASSTHROUGH = 2,
+};
+
+enum gsmd_passthrough_type {
+ GSMD_PASSTHROUGH_NONE = 0,
+ GSMD_PASSTHROUGH_REQ = 1,
+ GSMD_PASSTHROUGH_RESP = 2,
+};
+
+/* Length from 3GPP TS 04.08, Clause 10.5.4.7 */
+
+#define GSMD_ADDR_MAXLEN 13
+struct gsmd_addr {
+ u_int8_t type;
+ char number[GSMD_ADDR_MAXLEN+1];
+};
+
+struct gsmd_evt_auxdata {
+ union {
+ struct {
+ enum gsmd_call_type type;
+ } call;
+ struct {
+ struct gsmd_addr addr;
+ } clip;
+ struct {
+ struct gsmd_addr addr;
+ } colp;
+ struct {
+ /* TBD */
+ struct gsmd_addr addr;
+ } sms;
+ struct {
+ enum gsmd_pin_type type;
+ } pin;
+ struct {
+ enum gsmd_netreg_state state;
+ u_int16_t lac;
+ u_int16_t ci;
+ } netreg;
+ } u;
+};
+
+struct gsmd_msg_hdr {
+ u_int8_t version;
+ u_int8_t msg_type;
+ u_int8_t msg_subtype;
+ u_int8_t len;
+} __attribute__((packed));
+
+
+#endif
diff --git a/include/libgsmd/event.h b/include/libgsmd/event.h
new file mode 100644
index 0000000..d7faa9b
--- /dev/null
+++ b/include/libgsmd/event.h
@@ -0,0 +1,14 @@
+#ifndef _LIBGSMD_EVENT_H
+#define _LIBGSMD_EVENT_H
+
+#include <gsmd/event.h>
+
+/* Prototype of libgsmd callback handler function */
+typedef int evt_cb_func(struct lgsm_handle *lh, enum gsmd_events evt,
+ void *user);
+
+/* Register an event callback handler with libgsmd */
+extern int lgsm_register_evt_cb(struct lgsm_handle *lh,
+ evt_cb_func *cb, void *user);
+
+#endif
diff --git a/include/libgsmd/handset.h b/include/libgsmd/handset.h
new file mode 100644
index 0000000..c6b5ef8
--- /dev/null
+++ b/include/libgsmd/handset.h
@@ -0,0 +1,15 @@
+#ifndef _LIBGSMD_HANDSET_H
+#define _LIBGSMD_HANDSET_H
+
+#include <libgsmd/libgsmd.h>
+/* Set speaker level (Chapter 8.23) */
+extern int lgsm_set_spkr_level(struct lgsm_handle *lh,
+ u_int32_t level);
+
+/* Mute call during voice call */
+extern int lgsm_mute_set(struct lgsm_handle *lh, u_int8_t on);
+
+/* Get information on whether voice call is muted or not */
+extern int lgsm_mute_get(struct lgsm_handle *lh, u_int8_t *on);
+
+#endif
diff --git a/include/libgsmd/libgsmd.h b/include/libgsmd/libgsmd.h
new file mode 100644
index 0000000..579f13d
--- /dev/null
+++ b/include/libgsmd/libgsmd.h
@@ -0,0 +1,50 @@
+#ifndef _LIBGSMD_H
+#define _LIBGSMD_H
+
+/* libgsmd.h - Library API for gsmd, the GSM Daemon
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ * Development funded by First International Computers, Inc.
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+
+/* Generic Information
+ *
+ * Return value:
+ * < 0 Error, see libgsmd/errno.h and errno.h
+ * = 0 Success
+ * > 0 Success, number of information elements returned
+ *
+ * Allocation:
+ * All data structures are caller-allocated. The only exception
+ * is struct lgsm_handle which is allocatedi in lgsm_init() and
+ * free'd in lgsm_exit()
+ *
+ * References:
+ * Recefences to "Chapter X" are referring to 3GPP TS 07.07 version 7.8.0
+ */
+
+/* Opaque data structure, content only known to libgsm implementation */
+struct lgsm_handle;
+
+/* initialize usage of libgsmd, obtain handle for othe API calls */
+extern struct lgsm_handle *lgsm_init(void);
+
+/* Terminate usage of libgsmd */
+extern int lgsm_exit(struct lgsm_handle *lh);
+
+/* Refer to GSM 04.08 [8] subclause 10.5.4.7 */
+enum lgsm_addr_type {
+ LGSM_ATYPE_ISDN_UNKN = 161,
+ LGSM_ATYPE_ISDN_INTL = ,
+ LGSM_ATYPE_ISDN_NATIONAL = ,
+};
+
+#define LGSM_ADDR_MAXLEN 31
+struct lgsm_addr {
+ char addr[LGSM_ADDR_MAXLEN+1];
+ enum lgsm_addr_type tyoe;
+};
+
+#endif
diff --git a/include/libgsmd/misc.h b/include/libgsmd/misc.h
new file mode 100644
index 0000000..e191ce6
--- /dev/null
+++ b/include/libgsmd/misc.h
@@ -0,0 +1,72 @@
+#ifndef _LIBGSMD_H
+#define _LIBGSMD_H
+
+/* libgsmd.h - Library API for gsmd, the GSM Daemon
+ * (C) 2006 by Harald Welte <hwelte@hmw-consulting.de>
+ * Development funded by First International Computers, Inc.
+ */
+
+#include <libgsmd/libgsmd.h>
+
+enum lgsm_netreg_state {
+ LGSM_NETREG_ST_NOTREG = 0,
+ LGSM_NETREG_ST_REG_HOME = 1,
+ LGSM_NETREG_ST_NOTREG_SEARCH = 2,
+ LGSM_NETREG_ST_DENIED = 3,
+ LGSM_NETREG_ST_UNKNOWN = 4,
+ LGSM_NETREG_ST_REG_ROAMING = 5,
+};
+
+/* Get the current network registration status */
+extern int lgsm_get_netreg_state(struct lgsm_handle *lh,
+ enum lgsm_netreg_state *state);
+
+enum lgsm_info_type {
+ LGSM_INFO_TYPE_NONE = 0,
+ LGSM_INFO_TYPE_MANUF = 1,
+ LGSM_INFO_TYPE_MODEL = 2,
+ LGSM_INFO_TYPE_REVISION = 3,
+ LGSM_INFO_TYPE_SERIAL = 4,
+ LGSM_INFO_TYPE_IMSI = 5,
+};
+
+/* Get some information about the handset */
+extern int lgsm_get_info(struct lgsm_handle *lh,
+ enum lgsm_info_type type,
+ char *ret_string, u_int16_t len);
+
+/* Authenticate to SIM Card using specified null-terminated pin */
+extern int lgsm_pin_auth(struct lgsm_handle *lh, const char *pin);
+
+
+/* General Commands */
+
+/* Get Signal Strehngth (Chapter 8.5) */
+extern int lgsm_get_signal_quality(struct lgsm_handle *h,
+ unsigned int *rssi);
+
+/* Set voice mail number */
+extern int lgsm_voicemail_set(struct lgsm_handle *lh,
+ struct lgsm_addr *addr);
+
+/* Get currently configured voice mail number */
+extern int lgsm_voicemail_get(struct lgsm_handle *lh,
+ struct lgsm_addr *addr);
+
+/* Operator Selection, Network Registration */
+/* TBD */
+
+
+/* CLIP, CLIR, COLP, Call Forwarding, Call Waiting, Call Deflecting */
+/* TBD */
+
+
+/* SMS related functions */
+/* TBD */
+
+
+/* GPRS related functions */
+/* TBD */
+
+
+#endif
diff --git a/include/libgsmd/phonebook.h b/include/libgsmd/phonebook.h
new file mode 100644
index 0000000..92c3a7f
--- /dev/null
+++ b/include/libgsmd/phonebook.h
@@ -0,0 +1,51 @@
+#ifndef _LIBGSMD_PBOOK_H
+#define _LIBGSMD_PBOOK_H
+
+#include <libgsmd/libgsmd.h>
+
+/* Phonebook */
+
+/* Chapter 8.11 */
+enum lgsm_pbook_type {
+ LGSM_PB_ME_DIALLED = 1,
+ LGSM_PB_SIM_EMERGENCY = 2,
+ LGSM_PB_SIM_FIXDIAL = 3,
+ LGSM_PB_SIM_DIALLED = 4,
+ LGSM_PB_ME_MISSED = 5,
+ LGSM_PB_ME_PHONEBOOK = 6,
+ LGSM_PB_COMB_PHONEBOOK = 7,
+ LGSM_PB_SIM_OWN_NUMBERS = 8,
+ LGSM_PB_ME_RECEIVED = 9,
+ LGSM_PB_SIM_PHONEBOOK = 10,
+ LGSM_PB_TA_PHONEBOOK = 11,
+};
+
+/* Get a bitmask of supported phonebook types */
+extern int lgsm_pb_get_types(struct lgsm_handle *lh, u_int32 *typemask);
+
+/* Get a range of supported indexes in given phonebook type, Chapter 8.12 */
+extern int lgsm_pb_get_range(struct lgsm_handle *lh,
+ enum lgsm_pbook_type type,
+ u_int32_t *from, u_int32_t *to,
+ u_int32_t *nlength, *u_int32_t tlength);
+
+#define LGSM_PB_TEXT_MAXLEN 31
+
+struct lgsm_pb_entry {
+ struct lgsm_pb_entry *next;
+ enum lgsm_pbook_type type;
+ u_int32_t index;
+ char text[LGSM_PB_TEXT_MAXLEN+1];
+};
+
+/* Get a specific phonebook entry and store it to 'pb'
+ * pb' is caller-allocated */
+extern int lgsm_pb_get_entry(struct lgsm_handle *lh,
+ struct lgsm_pb_entry *pb);
+
+/* Store a specific phonebook entry 'pb' into phone */
+extern int lgsm_pb_set_entry(struct lgsm_handle *lh,
+ struct lgsm_pb_entry *pb);
+
+
+#endif
diff --git a/include/libgsmd/voicecall.h b/include/libgsmd/voicecall.h
new file mode 100644
index 0000000..4fffe4e
--- /dev/null
+++ b/include/libgsmd/voicecall.h
@@ -0,0 +1,18 @@
+#ifndef _LIBGSMD_VCALL_H
+#define _LIBGSMD_VCALL_H
+
+#include <libgsmd/libgsmd.h>
+
+/* Voice Calls */
+
+/* Initiate an outgoing voice call */
+extern int lgsm_voice_out_init(struct lgsm_handle *lh,
+ const struct lgsm_addr *number);
+
+/* Accept incoming voice call */
+extern int lgsm_voice_in_accept(struct lgsm_handle *lh);
+
+/* Terminate outgoing (or incoming) voice call */
+extern int lgsm_voice_hangup(struct lgsm_handle *lh);
+
+#endif
diff --git a/logs/screenlog.0 b/logs/screenlog.0
new file mode 100644
index 0000000..f7ae84b
--- /dev/null
+++ b/logs/screenlog.0
@@ -0,0 +1,500 @@
+ccu -l /dev/ttU yUSB0 -s 15200
+cu: Unsupported baud rate 15200
+% laforge@rama%pts/11 (20:27) phone/src/libgsmd/atcmd >  2/1cu -l /dev/ttyUSB0 -s 1520015200
+Connected.
+
+OK
+ATV1
+OK
+AT+COPS=?
+ERROR
+AT+COPS=?
+ERROR
+AT+COPS?
++COPS: 0
+
+OK
+AT+COPS=?
+ERROR
+
+OK
+
+OK
+AT+CRC=1
+OK
+AT+CRC=?
++CRC: (0,1)
+
+OK
+AT+CGMI
+<manufacturer>
+
+OK
+AT+GMM
+<model>
+
+OK
+AT+CGSI
+EXT: I
+
+ERROR
+AT+CGSS
+EXT: I
+
+ERROR
+AT+CGSS=?
+EXT: I
+
+ERROR
+AT+CGSS?
+EXT: I
+
+ERROR
+AT+CGSN
+<serial number>
+
+OK
+AT+CSCS?
++CSCS: "IRA"
+
+OK
+AT+CSCS=?
++CSCS: "GSM","IRA","PCCP437","PCDN","8859-1","HEX","UCS2"
+
+OK
+AT+CIMI?
+ERROR
+AT+CIMI
+262074992516579
+
+OK
+AT+M
+EXT: I
+
+ERROR
+AT+CMUX?
+OK
+AT-Command Interpreter ready
+
+OK
+AT+CMUX=?
++CMUX: (1),(0),(1-5),(10-100),(1-255),(0-100),(2-255),(1-255),(1-7)
+
+OK
+AT-Command Interpreter ready
+AT+CSTA
+ERROR
+AT+CSTA?
++CSTA: 129
+
+OK
+AT+CSTA=?
++CSTA: (129,145)
+
+OK
+AT+CPIN?
++CPIN: SIM PIN
+
+OK
+ATP
+ERROR
+AT+CPIN=9450
+ERROR
+AT+CMOD?
++CMOD: 0
+
+OK
+AT+CMOD=?
++CMOD: (0-3)
+
+OK
+
+OK
+AT+CHUP
+OK
+AT+CBST=?
++CBST: (0-7,12,14,65,66,68,70,71,75),(0),(0-3)
+
+OK
+AT
+ERROR
+AT+CRLP=?
++CRLP: (0-61),(0-61),(39-255),(1-255)
+
+OK
+
+OK
+AT+CR?
++CR: 0
+
+OK
+AT+CR=?
++CR: (0,1)
+
+OK
+AT+CR=1
+OK
+
+OK
+
+OK
+
+OK
+AT+CEER=1
++CEER: 0,1,1,255,no error
+
+OK
+AT+CEER?
+ERROR
+AT+CEER=?
+OK
+
+OK
+AT+CRC=1
+OK
+AT+CRC=?
++CRC: (0,1)
+
+OK
+AT+CSNS=?
+ERROR
+AT+CSNS?
++CSNS: 0
+
+OK
+AT+CSNS=?
++CSNS: (0-7)
+
+OK
+
+OK
+AT+CHVU?
+EXT: I
+
+ERROR
+AT+CVUHU?
+EXT: I
+
+ERROR
+AT+CVHU?
+EXT: I
+
+ERROR
+AT+CVHU=?
+EXT: I
+
+ERROR
+
+OK
+AT+CV120?
+EXT: I
+
+ERROR
+AT+CV120=?
+EXT: I
+
+ERROR
+
+OK
+AT+CNUM?
+ERROR
+AT+CNUM=?
+OK
+AT+CNUM?
+ERROR
+AT+CNUM=?
+OK
+AT+CREG?
++CREG: 0,2
+
+OK
+AT+CREG=?
++CREG: (0-2)
+
+OK
+AT
+ERROR
+ATC
+EXT: I
+
+ERROR
+AT+CREG=2
+OK
+
+OK
+AT+COPS=?
+ERROR
+AT+COPS?
++COPS: 0
+
+OK
+AT+COPS=?
+ERROR
+
+OK
+AT+CLKS=?
+EXT: I
+
+ERROR
+AT+CLCK=?
++CLCK: ("SC","AO","OI","OX","AI","IR","AB","AG","AC","FD","PS","PN","PU","PP","PC","PF","AL")
+
+OK
+
+OK
+AT+CLIP=?
++CLIP: (0,1)
+
+OK
+ATP
+ERROR
+AT+CLIP?
++CLIP: 0,2
+
+OK
+AT+CLIR=?
++CLIR: (0,1,2)
+
+OK
+AT+CLIR?
++CLIR: 0,2
+
+OK
+AT+COPLP+
+EXT: I
+
+ERROR
+AT+COLP?
++COLP: 0,2
+
+OK
+AT+COLP=?
++COLP: (0,1)
+
+OK
+AT+CCFG
+EXT: I
+
+ERROR
+a
+ERROR
+AT+CCFC?
+ERROR
+AT+CCFC=?
++CCFC: (0-5)
+
+OK
+AT+CCWA
+OK
+AT+CCWA?
++CCWA: 0
+
+OK
+AT+CCWA=?
++CCWA: (0,1)
+
+OK
+
+OK
+AT+CCWA=1
+OK
+
+OK
+AT+CTFR=?
+OK
+AT+CTFR?
+ERROR
+
+OK
+AT+CUSD=?
++CUSD: (0,1,2)
+
+OK
+AT+CUSD?
++CUSD: 0
+
+OK
+AT+CUSD=1
+OK
+AT+CCSN?
+EXT: I
+
+ERROR
+AT+CCSN=?
+EXT: I
+
+ERROR
+
+OK
+AT+CCSI?
+EXT: I
+
+ERROR
+AT
+ERROR
+AT+CLCC?
+ERROR
+AT+CLCC=?
+OK
+
+OK
+AT+CPOL?
+ERROR
+AT+CPOL=?
+ERROR
+AT+
+ERROR
+AT+COPN?
+ERROR
+AT+COPN=?
+OK
+
+OK
+AT+CPAS?
+ERROR
+AT+CPAS=?
++CPAS: (0-5)
+
+OK
+AT+CPAS
++CPAS: 0
+
+OK
+
+OK
+AT+CMEC?
+EXT: I
+
+ERROR
+AT+CMEC=?
+EXT: I
+
+ERROR
+AT+CFUN?
++CFUN: 1
+
+OK
+AT+CFUN=?
++CFUN: (0,1,4),(0)
+
+OK
+
+OK
+AT+PIN
+EXT: I
+
+ERROR
+AT+CPIN?
++CPIN: SIM PIN
+
+OK
+AT+CPIN?
++CPIN: SIM PIN
+
+OK
+AT+CPIN=9450
+ERROR
+
++CREG: 2
+
+OK
+AT+CPIN?
++CPIN: READY
+
+OK
+AT+CBC?
+ERROR
+AT+CBC=?
++CBC: (0-3),(0-100)
+
+OK
+AT+CBC
++CBC: 0,0
+
+OK
+AT+CBC
++CBC: 0,0
+
+OK
+AT+CBC
++CBC: 0,0
+
+OK
+AT+CSQ?
+ERROR
+AT+COPS?
++COPS: 0
+
+OK
+AT+COPS=? AT-Command Interpreter ready
+
+OK
+
+OK
+AT+CPIN
+ERROR
+AT+CPIN?
++CPIN: SIM PIN
+
+OK
+AT+COPS?
++COPS: 0
+
+OK
+AT+COPS?
++COPS: 0
+
+OK
+AT+CPAS?
+ERROR
+AT+CAP
+EXT: I
+
+ERROR
+AT+CPAS
++CPAS: 0
+
+OK
+AT+COPS?
++COPS: 0
+
+OK
+AT+COPS=? AT-Command Interpreter ready
+AT+CPIN?
++CPIN: READY
+
+OK
+AT+CMEC
+EXT: I
+
+ERROR
+AT+CMEC?
+EXT: I
+
+ERROR
+AT+CDIS?
+EXT: I
+
+ERROR
+AT+CIND?
++CIND: 0,0
+
+OK
+AT+CIND=/?
+OK
+AT+CIND=?
++CIND: ("signal", (0-5)), ("smsfull", (0-1))
+
+OK
+AT+CMER?
++CMER: 0,0,0,0,0
+
+OK
+AT+CMER=?
++CMER: (0-2), (0), (0), (0-2), (0,1)
+
+OK
+cu: Got hangup signal
+
+Disconnected.
+% laforge@rama%pts/11 (22:31) phone/src/libgsmd/atcmd >  3/0 \ No newline at end of file
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
diff --git a/src/libgsmd/lgsm_internals.h b/src/libgsmd/lgsm_internals.h
new file mode 100644
index 0000000..66fd5fe
--- /dev/null
+++ b/src/libgsmd/lgsm_internals.h
@@ -0,0 +1,4 @@
+
+struct lgsm_handle {
+ int fd;
+};
diff --git a/src/libgsmd/libgsmd.c b/src/libgsmd/libgsmd.c
new file mode 100644
index 0000000..e514617
--- /dev/null
+++ b/src/libgsmd/libgsmd.c
@@ -0,0 +1,69 @@
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <libgsmd/libgsmd.h>
+
+#include "lgsm_internals.h"
+
+static int lgsm_open_backend(struct lgsm_handle *lh, const char *device)
+{
+ int rc;
+
+ if (!strcmp(device, "gsmd")) {
+ struct sockaddr_un sun;
+
+ /* use unix domain socket to gsm daemon */
+ lh->fd = socket(PF_UNIX, GSMD_UNIX_SOCKET_TYPE, 0);
+ if (lh->fd < 0)
+ return lh->fd;
+
+ sun.sun_family = AF_UNIX;
+ memcpy(sun.sun_path, GSMD_UNIX_SOCKET, sizeof(GSMD_UNIX_SOCKET));
+
+ rc = connect(lh->fd, (struct sockaddr *)&sun, sizeof(sun));
+ if (rc < 0) {
+ close(lh->fd);
+ lh->fd = -1;
+ return rc;
+ }
+ } else {
+ /* use direct access to device node ([virtual] tty device) */
+ lh->fd = open(device, O_RDWR);
+ if (lh->fd < 0)
+ return lh->fd;
+ }
+
+ return 0;
+}
+
+struct lgsm_handle *lgsm_init(const char *device)
+{
+ struct lgsm_handle *lh = malloc(sizeof(*lh));
+
+ memset(lh, 0, sizeof(*lh));
+ lh->fd = -1;
+
+ if (lgsm_open_backend(lh, device) < 0) {
+ free(lh);
+ return NULL;
+ }
+
+ /* send some initial commands, such as ATV1 (verbose response)
+ * and +CRC=1 (which we currently require!) */
+
+ return lh;
+}
+
+int lgsm_exit(struct lgsm_handle *lh)
+{
+ free(lh);
+
+ return 0;
+}
diff --git a/src/libgsmd/libgsmd_input.c b/src/libgsmd/libgsmd_input.c
new file mode 100644
index 0000000..134cfea
--- /dev/null
+++ b/src/libgsmd/libgsmd_input.c
@@ -0,0 +1,13 @@
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+
+#include <libgsmd/libgsmd.h>
+
+#include "lgsm_internals.h"
+
+
diff --git a/src/libgsmd/libgsmd_voicecall.c b/src/libgsmd/libgsmd_voicecall.c
new file mode 100644
index 0000000..f59bdff
--- /dev/null
+++ b/src/libgsmd/libgsmd_voicecall.c
@@ -0,0 +1,24 @@
+
+
+#include <libgsmd/voicecall.h>
+
+#include "libgsmd/internals.h"
+
+
+int lgsm_voice_out_init(struct lgsm_handle *lh,
+ const struct lgsm_addr *number)
+{
+ /* send ATD command */
+ return -EINVAL;
+}
+
+int lgsm_voice_in_accept(struct lgsm_handle *lh)
+{
+ return -EINVAL;
+}
+
+int lgsm_voice_hangup(struct lgsm_handle *lh)
+{
+ /* Send ATH0 */
+ return -EINVAL;
+}
personal git repositories of Harald Welte. Your mileage may vary