From 1584a74a4acdfb17aab0d6a2b13b18a7799aa37d Mon Sep 17 00:00:00 2001 From: laforge Date: Sat, 2 Sep 2006 10:32:06 +0000 Subject: 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 --- README.txt | 54 +++++ include/common/gsmd_proto.h | 9 + include/common/linux_list.h | 364 +++++++++++++++++++++++++++++ include/gsmd/event.h | 52 +++++ include/gsmd/gsmd.h | 4 + include/gsmd/usock.h | 83 +++++++ include/libgsmd/event.h | 14 ++ include/libgsmd/handset.h | 15 ++ include/libgsmd/libgsmd.h | 50 ++++ include/libgsmd/misc.h | 72 ++++++ include/libgsmd/phonebook.h | 51 ++++ include/libgsmd/voicecall.h | 18 ++ logs/screenlog.0 | 500 ++++++++++++++++++++++++++++++++++++++++ src/gsmd/.gpsd.h.swp | Bin 0 -> 12288 bytes src/gsmd/Makefile | 14 ++ src/gsmd/atcmd.c | 332 ++++++++++++++++++++++++++ src/gsmd/atcmd.h | 12 + src/gsmd/gsmd.c | 171 ++++++++++++++ src/gsmd/gsmd.h | 68 ++++++ src/gsmd/gsmd_event.h | 53 +++++ src/gsmd/select.c | 96 ++++++++ src/gsmd/select.h | 22 ++ src/gsmd/unsolicited.c | 272 ++++++++++++++++++++++ src/gsmd/unsolicited.h | 8 + src/gsmd/usock.c | 166 +++++++++++++ src/gsmd/usock.h | 15 ++ src/gsmd/vendor_ti.c | 53 +++++ src/gsmd/vendorplugin.h | 29 +++ src/libgsmd/lgsm_internals.h | 4 + src/libgsmd/libgsmd.c | 69 ++++++ src/libgsmd/libgsmd_input.c | 13 ++ src/libgsmd/libgsmd_voicecall.c | 24 ++ 32 files changed, 2707 insertions(+) create mode 100644 README.txt create mode 100644 include/common/gsmd_proto.h create mode 100644 include/common/linux_list.h create mode 100644 include/gsmd/event.h create mode 100644 include/gsmd/gsmd.h create mode 100644 include/gsmd/usock.h create mode 100644 include/libgsmd/event.h create mode 100644 include/libgsmd/handset.h create mode 100644 include/libgsmd/libgsmd.h create mode 100644 include/libgsmd/misc.h create mode 100644 include/libgsmd/phonebook.h create mode 100644 include/libgsmd/voicecall.h create mode 100644 logs/screenlog.0 create mode 100644 src/gsmd/.gpsd.h.swp create mode 100644 src/gsmd/Makefile create mode 100644 src/gsmd/atcmd.c create mode 100644 src/gsmd/atcmd.h create mode 100644 src/gsmd/gsmd.c create mode 100644 src/gsmd/gsmd.h create mode 100644 src/gsmd/gsmd_event.h create mode 100644 src/gsmd/select.c create mode 100644 src/gsmd/select.h create mode 100644 src/gsmd/unsolicited.c create mode 100644 src/gsmd/unsolicited.h create mode 100644 src/gsmd/usock.c create mode 100644 src/gsmd/usock.h create mode 100644 src/gsmd/vendor_ti.c create mode 100644 src/gsmd/vendorplugin.h create mode 100644 src/libgsmd/lgsm_internals.h create mode 100644 src/libgsmd/libgsmd.c create mode 100644 src/libgsmd/libgsmd_input.c create mode 100644 src/libgsmd/libgsmd_voicecall.c 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 */ + + + +#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 + +#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 + +#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 + +/* 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 +/* 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 + * Development funded by First International Computers, Inc. + */ + +#include +#include + +/* 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 + * Development funded by First International Computers, Inc. + */ + +#include + +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 + +/* 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 + +/* 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 + + +OK +AT+GMM + + +OK +AT+CGSI +EXT: I + +ERROR +AT+CGSS +EXT: I + +ERROR +AT+CGSS=? +EXT: I + +ERROR +AT+CGSS? +EXT: I + +ERROR +AT+CGSN + + +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 Binary files /dev/null and b/src/gsmd/.gpsd.h.swp 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 +#include +#include +#include +#include + +#include + +#include +#include "gsmd.h" +#include "atcmd.h" +#include "unsolicited.h" + +/* libgsmd / gsmd AT command interpreter / parser / constructor + * (C) 2006 by Harald Welte + * + * 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 +#include +#include +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +#include +#include + +#include + +#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 + +#include + +#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 + * + * 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 +#include +#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 + +#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 +#include +#include +#include +#include +#include + +#include +#include + +#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 +#include +#include +#include +#include + +#include +#include + +#include +#include + +#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 + +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 +#include +#include +#include + +#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 +#include +#include +#include + +#include +#include +#include + +#include + +#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 +#include +#include +#include + +#include + +#include + +#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 + +#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; +} -- cgit v1.2.3