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:
commit
1584a74a4a
|
@ -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
|
||||
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
#ifndef _GSMD_H
|
||||
#define _GSMD_H
|
||||
|
||||
#endif /* _GSMD_H */
|
|
@ -0,0 +1,83 @@
|
|||
#ifndef _GSMD_USOCK_H
|
||||
#define _GSMD_USOCK_H
|
||||
|
||||
#include <gsmd/event.h>
|
||||
|
||||
#define GSMD_UNIX_SOCKET "\0gsmd"
|
||||
#define GSMD_UNIX_SOCKET_TYPE SOCK_SEQPACKET
|
||||
|
||||
#define GSMD_PROTO_VERSION 1
|
||||
|
||||
enum gsmd_prot_cmd {
|
||||
GSMD_PCMD_NONE,
|
||||
GSMD_PCMD_EVT_SUBSCRIPTIONS, /* alter event subscriptions */
|
||||
GSMD_PCMD_PASSTHROUGH, /* transparent atcmd passthrough */
|
||||
};
|
||||
|
||||
enum gsmd_pcmd_result {
|
||||
GSMD_PCMD_OK = 0,
|
||||
GSMD_PCMD_ERR_UNSPEC = 0xff,
|
||||
};
|
||||
|
||||
struct gsmd_prot_hdr {
|
||||
u_int16_t cmd;
|
||||
u_int8_t result;
|
||||
u_int8_t version;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
enum gsmd_msg_type {
|
||||
GSMD_MSG_NONE = 0,
|
||||
GSMD_MSG_EVENT = 1,
|
||||
GSMD_MSG_PASSTHROUGH = 2,
|
||||
};
|
||||
|
||||
enum gsmd_passthrough_type {
|
||||
GSMD_PASSTHROUGH_NONE = 0,
|
||||
GSMD_PASSTHROUGH_REQ = 1,
|
||||
GSMD_PASSTHROUGH_RESP = 2,
|
||||
};
|
||||
|
||||
/* Length from 3GPP TS 04.08, Clause 10.5.4.7 */
|
||||
|
||||
#define GSMD_ADDR_MAXLEN 13
|
||||
struct gsmd_addr {
|
||||
u_int8_t type;
|
||||
char number[GSMD_ADDR_MAXLEN+1];
|
||||
};
|
||||
|
||||
struct gsmd_evt_auxdata {
|
||||
union {
|
||||
struct {
|
||||
enum gsmd_call_type type;
|
||||
} call;
|
||||
struct {
|
||||
struct gsmd_addr addr;
|
||||
} clip;
|
||||
struct {
|
||||
struct gsmd_addr addr;
|
||||
} colp;
|
||||
struct {
|
||||
/* TBD */
|
||||
struct gsmd_addr addr;
|
||||
} sms;
|
||||
struct {
|
||||
enum gsmd_pin_type type;
|
||||
} pin;
|
||||
struct {
|
||||
enum gsmd_netreg_state state;
|
||||
u_int16_t lac;
|
||||
u_int16_t ci;
|
||||
} netreg;
|
||||
} u;
|
||||
};
|
||||
|
||||
struct gsmd_msg_hdr {
|
||||
u_int8_t version;
|
||||
u_int8_t msg_type;
|
||||
u_int8_t msg_subtype;
|
||||
u_int8_t len;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,500 @@
|
|||
ccu -l /dev/ttU yUSB0 -s 15200
|
||||
cu: Unsupported baud rate 15200
|
||||
[1m[3m%[23m[1m[m
[m[23m[24m[J[3mlaforge@rama%pts/11 (20:27)[23m phone/src/libgsmd/atcmd > [K[46C [1m2/1[m[50Dcu -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.
|
||||
[1m[3m%[23m[1m[m
[m[23m[24m[J[3mlaforge@rama%pts/11 (22:31)[23m phone/src/libgsmd/atcmd > [K[46C [1m3/0[m[50D
|
Binary file not shown.
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
|
|
@ -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,
|
||||
};
|
|
@ -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
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
struct lgsm_handle {
|
||||
int fd;
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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"
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue