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
This commit is contained in:
laforge 2006-09-02 10:32:06 +00:00
commit 1584a74a4a
32 changed files with 2707 additions and 0 deletions

54
README.txt Normal file
View File

@ -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

View File

@ -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 */

364
include/common/linux_list.h Normal file
View File

@ -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

52
include/gsmd/event.h Normal file
View File

@ -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

4
include/gsmd/gsmd.h Normal file
View File

@ -0,0 +1,4 @@
#ifndef _GSMD_H
#define _GSMD_H
#endif /* _GSMD_H */

83
include/gsmd/usock.h Normal file
View File

@ -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

14
include/libgsmd/event.h Normal file
View File

@ -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

15
include/libgsmd/handset.h Normal file
View File

@ -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

50
include/libgsmd/libgsmd.h Normal file
View File

@ -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

72
include/libgsmd/misc.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

500
logs/screenlog.0 Normal file
View File

@ -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

BIN
src/gsmd/.gpsd.h.swp Normal file

Binary file not shown.

14
src/gsmd/Makefile Normal file
View File

@ -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

332
src/gsmd/atcmd.c Normal file
View File

@ -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);
}

12
src/gsmd/atcmd.h Normal file
View File

@ -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

171
src/gsmd/gsmd.c Normal file
View File

@ -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);
}

68
src/gsmd/gsmd.h Normal file
View File

@ -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

53
src/gsmd/gsmd_event.h Normal file
View File

@ -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

96
src/gsmd/select.c Normal file
View File

@ -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;
}

22
src/gsmd/select.h Normal file
View File

@ -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

272
src/gsmd/unsolicited.c Normal file
View File

@ -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;
}

8
src/gsmd/unsolicited.h Normal file
View File

@ -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

166
src/gsmd/usock.c Normal file
View File

@ -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);
}

15
src/gsmd/usock.h Normal file
View File

@ -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

53
src/gsmd/vendor_ti.c Normal file
View File

@ -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,
};

29
src/gsmd/vendorplugin.h Normal file
View File

@ -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

View File

@ -0,0 +1,4 @@
struct lgsm_handle {
int fd;
};

69
src/libgsmd/libgsmd.c Normal file
View File

@ -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;
}

View File

@ -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"

View File

@ -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;
}