Asterisk per Freetz Webinterface starten / stoppen???

Asterisk is unable to handle the '+' symbol in the reported Caller ID.
Additional Information: Handling this problem requires adding '+' to the list of valid CallerID characters in callerid.c:ast_isphonenumber()
Schau mal ob in deiner "freetz-trunk/source/asterisk-1.6.2.2/main/callerid.c"-Datei,
das '+' Zeichen in der Funktion "ast_isphonenumber()" mit angegeben ist:
Code:
int ast_isphonenumber(const char *n)
{
	return ast_is_valid_string(n, "0123456789*#[B][COLOR="Red"]+[/COLOR][/B]");
}
 
Ja, leider.
Code:
return ast_is_valid_string(n, "0123456789*#+");
Dann ist es wohl ein anderes Problem.
Aber es tritt bei Handyanrufen auf und bei anderen Anrufen glücklicherweise meistens nicht.
---Edit---
Komischerweise tritt es bei der gleichen Handynummer innerhalb von 10 Minuten mal auf und mal nicht.
 
Zuletzt bearbeitet:
warum komme ich hier nicht weiter

Code:
if [ -r System.map -a -x /sbin/depmod ]; then /sbin/depmod -ae -F System.map -b /usr/src/freetz-trunk/source/ref-8mb_26-7270_04.80/kernel/kernel_8mb_26_build/modules/ -r 2.6.19.2; fi
make[1]: Leaving directory `/usr/src/freetz-trunk/source/ref-8mb_26-7270_04.80/kernel/kernel_8mb_26_build/kernel/linux-2.6.19.2'
touch source/ref-8mb_26-7270_04.80/kernel/.modules-ur8
rm -rf kernel/modules-8mb_26-7270_04.80/lib
mkdir -p kernel/modules-8mb_26-7270_04.80
tar -cf - -C source/ref-8mb_26-7270_04.80/kernel/kernel_8mb_26_build/modules \
                --exclude=lib/modules/2.6.19.2-ur8/build \
                --exclude=lib/modules/2.6.19.2-ur8/pcmcia \
                . | tar -xf - -C kernel/modules-8mb_26-7270_04.80
touch kernel/modules-8mb_26-7270_04.80/.modules-ur8
make: *** [ASTERISK_FREETZ_CHANGES_CHECK] Fehler 1
slightly@StinkyLinux:/usr/src/freetz-trunk$
 
Ich kann in deinem Log kein Fehler erkennen. Nur dass er wegen einem Fehler anhält.

MfG Oliver
 
@ichego1
Hast Du es mal hiermit probiert?

make asterisk-clean
make asterisk-dirclean
make asterisk-precompiled

Kommt da auch schon der Fehler?
Wenn das problemlos durchläuft, müßtest Du eigentlich im Ordner
/freetz-trunk/packages/asterisk-1.6.2.2/root
schon alles haben, was Du brauchst.
 
Code:
exten => s,n,record(/var/spool/asterisk/${MSGFILENAME}.wav,8,60)
Schickt es mir leere Anrufbeantworter Mails. Es gibt scheinbar Probleme mit record().
 
Zuletzt bearbeitet:
Ich kann zwar das Problem mit der Instabilität mit Asterisk 1.6.2.2 nicht nachvollziehen, aber du kannst ja den Patch für * 1.6.2.0 aus Post 4 nehmen.
Ansonsten führt noch dieser Thread auf einen irgendeinen Archiv für * 1.6.0.19.
 
Ich habe es mir mal genauer angeschaut.
record() arbeitet ganz normal, die wav-Datei wird abgespeichert, aber sobald record() fertig ist, wird die Datei wieder gelöscht. Das passiert auch, wenn ich die Datei direkt auf die NAS-Festplatte schreibe. Bei 1.6.0.1 und früher blieb die Datei stehen, bis ich sie selbst gelöscht hatte. Hast Du das auch mal probiert? record(...,k)

Danke für die Links, ich brauche aber einen Patch für chan_datacard.
1.6.0.1 ist ja schon in meinem freetz, aber ohne chan_datacard, wegen der Fehler (siehe oben).

Übrigens mit der gleichen Einstellung, wie bei 1.6.0.1 kann ich bei 1.6.2.2 keine 1&1-Anrufe mehr empfangen.
 
Zuletzt bearbeitet:
@ sf3978
Könntest du (wenn es dir nichts ausmacht) bitte noch einen Patch für * 1.6.2.5 und für das Rev.40 von chan_datacard schreiben? klick.

@ Mods (oder wer auch immer dafür zustandig ist): Kann man das mit den patches mal in den Trunk integrieren? Dann müsste keiner mehr patchen, sondern alles wäre gleich im Trunk.
 
[...] noch einen Patch für * 1.6.2.5 und für das Rev.40 von chan_datacard schreiben?
[...]
Der Patch wäre nicht das Problem. Wenn die Dateien asterisk.mk & Co., von der Version .4 zur Version .5 einfach anzupassen sind, dann sollte das machbar sein. Ich schau mir das am Wochenende mal an. Wenn nicht, dann könntest Du dich an er13 (Maintainer des Pakets asterisk für Freetz, siehe Beitrag #4 in diesem Thread) wenden.;)
 
Sorry, ich muss mal nachfragen:

Wie ist das Vorgehen, um Asterisk in den aktuellen Trunk zu installieren?
Trunk auschecken, den Patch von er13 anwenden, per menuconfig asterisk auswählen und installieren lassen?

Oder muss noch der Trunk 4103 benutzt werden?
Oder geht es mittlerweile ganz anders? ;)
 
[...]
Oder muss noch der Trunk 4103 benutzt werden?
[...]
Du kannst es auch mit dem aktuellen Trunk versuchen. Wenn es Probleme gibt, dann evtl. nur bei den Dateien "tools/external", "make/external.in" und "make/Config.in". Und diese Dateien kannst Du dann auch manuell anpassen.
 
Zuletzt bearbeitet:
Also ich hab den Patch gegen den aktuellen trunk eingespielt.
Damit habe ich unter Testing auch den Asterisk zur Auswahl.
Leider ist der so groß, dass damit das image zu groß für die 7170 ist.
Jetzt habe ich einige Libraries und Pakete mit external auf einen USB-Stick ausgelagert. Damit kann ich das image bauen und flashen.

Versuche ich jetzt aber den asterisk zu starten kommen einige Fehlermeldungen zum Beispiel, dass er seine *.pid-Datei nicht findet. Und kurze Zeit später rebootet die Box.

Die Dateiprobleme lassen sich ja beheben, aber das Neustarten? Kommt mir so vor, als wär die 7170 mit dem Asterisk überfordert.

Edit: Ok sieht eher so aus, als ob die Box einfach so neu startet. Also auch ohne meine Versuche den Asterisk zu laden.
 
Zuletzt bearbeitet:
habe mich mal damit beschäftigt:

1.)
vi make/asterisk/asterisk.mk
Code:
$(call PKG_INIT_BIN, 1.6.2.5)

2.)
rm make/asterisk/patches/160_rev39_chan_datacard_c.patch
vi make/asterisk/patches/160_rev40_chan_datacard_c.patch

Code:
--- channels/chan_datacard.c.orig       2010-02-13 21:41:40.000000000 +0100
+++ channels/chan_datacard.c            2010-02-13 21:41:40.000000000 +0100
@@ -0,0 +1,4625 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Copyright (C) 2009 Artem Makhutov
+ *
+ * Artem Makhutov <[email protected]>
+ *
+ * chan_datacard is based on chan_mobile by Digium
+ *
+ * Mark Spencer <[email protected]>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief UMTS Voice Datacard channel driver
+ *
+ * \author Artem Makhutov <[email protected]>
+ * \author Dave Bowerman <[email protected]>
+ *
+ * \ingroup channel_drivers
+ */
+
+#include <asterisk.h>
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 947 $")
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/termios.h>
+#include <signal.h>
+
+#include <asterisk/lock.h>
+#include <asterisk/channel.h>
+#include <asterisk/config.h>
+#include <asterisk/logger.h>
+#include <asterisk/module.h>
+#include <asterisk/pbx.h>
+#include <asterisk/options.h>
+#include <asterisk/utils.h>
+#include <asterisk/linkedlists.h>
+#include <asterisk/cli.h>
+#include <asterisk/devicestate.h>
+#include <asterisk/causes.h>
+#include <asterisk/dsp.h>
+#include <asterisk/app.h>
+#include <asterisk/manager.h>
+#include <asterisk/io.h>
+
+/*! Global jitterbuffer configuration - by default, jb is disabled */
+static struct ast_jb_conf default_jbconf = {
+	.flags = 0,
+	.max_size = -1,
+	.resync_threshold = -1,
+	.impl = ""
+};
+static struct ast_jb_conf global_jbconf;
+
+#include "char_conv.h"
+
+#define DC_CONFIG "datacard.conf"
+
+#define DEVICE_FRAME_SIZE 320
+#define DEVICE_FRAME_FORMAT AST_FORMAT_SLINEAR
+#define CHANNEL_FRAME_SIZE 320
+
+static int prefformat = DEVICE_FRAME_FORMAT;
+
+static int discovery_interval = 60;			/* The device discovery interval, default 60 seconds. */
+static pthread_t discovery_thread = AST_PTHREADT_NULL;	/* The discovery thread */
+
+AST_MUTEX_DEFINE_STATIC(unload_mutex);
+static int unloading_flag = 0;
+static inline int check_unloading();
+static inline void set_unloading();
+
+struct msg_queue_entry;
+struct dc_pvt {
+	struct ast_channel *owner;			/* Channel we belong to, possibly NULL */
+	struct ast_frame fr;				/* "null" frame */
+	ast_mutex_t lock;				/*!< pvt lock */
+	/*! queue for messages we are expecting */
+	AST_LIST_HEAD_NOLOCK(msg_queue, msg_queue_entry) msg_queue;
+	char id[31];					/* The id from datacard.conf */
+	int group;					/* group number for group dialling */
+	char context[AST_MAX_CONTEXT];			/* the context for incoming calls */
+	struct dc_pvt *pvt;				/*!< pvt pvt */
+	char io_buf[CHANNEL_FRAME_SIZE + AST_FRIENDLY_OFFSET];
+	struct ast_smoother *smoother;			/* our smoother, for making 320 byte frames */
+	char audio_tty_str[256];
+	char data_tty_str[256];
+	int audio_socket;				/* audio socket descriptor */
+	int data_socket;				/* rfcomm socket descriptor */
+	pthread_t monitor_thread;			/* monitor thread handle */
+	int timeout;					/*!< used to set the timeout for rfcomm data (may be used in the future) */
+	unsigned int has_sms:1;
+	unsigned int has_voice:1;
+	unsigned int do_alignment_detection:1;
+	unsigned int alignment_detection_triggered:1;
+	short alignment_samples[4];
+	int alignment_count;
+	struct ast_dsp *dsp;
+	int hangupcause;
+	int initialized:1;		/*!< whether a service level connection exists or not */
+	int rssi;
+	int linkmode;
+	int linksubmode;
+	int rxgain;
+	int txgain;
+	char provider_name[32];
+	char manufacturer[32];
+	char model[32];
+	char firmware[32];
+	char imei[17];
+	int sms_storage_position;
+	unsigned int auto_delete_sms:1;
+	unsigned int use_ucs2_encoding:1;
+
+	/* flags */
+	unsigned int outgoing:1;	/*!< outgoing call */
+	unsigned int incoming:1;	/*!< incoming call */
+	unsigned int outgoing_sms:1;	/*!< outgoing sms */
+	unsigned int incoming_sms:1;	/*!< outgoing sms */
+	unsigned int needchup:1;	/*!< we need to send a chup */
+	unsigned int needring:1;	/*!< we need to send a RING */
+	unsigned int answered:1;	/*!< we sent/recieved an answer */
+	unsigned int connected:1;	/*!< do we have an rfcomm connection to a device */
+	unsigned int volume_synchronized:1;	/*!< we have synchronized the volume */
+	unsigned int group_last_used:1; /*!< mark the last used device */
+
+	AST_LIST_ENTRY(dc_pvt) entry;
+};
+
+static AST_RWLIST_HEAD_STATIC(devices, dc_pvt);
+
+static void inline rfcomm_append_buf(char **buf, size_t count, size_t *in_count, char c);
+static int rfcomm_read_and_expect_char(int data_socket, char *result, char expected);
+static int rfcomm_read_and_append_char(int data_socket, char **buf, size_t count, size_t *in_count, char *result, char expected);
+static int rfcomm_read_until_crlf(int data_socket, char **buf, size_t count, size_t *in_count);
+static int rfcomm_read_until_ok(int data_socket, char **buf, size_t count, size_t *in_count);
+static int rfcomm_read_until_cusd_end(int data_socket, char **buf, size_t count, size_t *in_count);
+static int rfcomm_read_sms_prompt(int data_socket, char **buf, size_t count, size_t *in_count);
+static int rfcomm_read_result(int data_socket, char **buf, size_t count, size_t *in_count);
+static int rfcomm_read_command(int data_socket, char **buf, size_t count, size_t *in_count);
+static int rfcomm_read_cmgr(int data_socket, char **buf, size_t count, size_t *in_count);
+static int rfcomm_read_cusd(int data_socket, char **buf, size_t count, size_t *in_count);
+
+static int handle_response_ok(struct dc_pvt *pvt, char *buf);
+static int handle_response_error(struct dc_pvt *pvt, char *buf);
+static int handle_response_clip(struct dc_pvt *pvt, char *buf);
+static int handle_response_ring(struct dc_pvt *pvt, char *buf);
+static int handle_response_cmti(struct dc_pvt *pvt, char *buf);
+static int handle_response_cmgr(struct dc_pvt *pvt, char *buf);
+static int handle_response_cusd(struct dc_pvt *pvt, char *buf);
+static int handle_response_busy(struct dc_pvt *pvt);
+static int handle_response_no_dialtone(struct dc_pvt *pvt, char *buf);
+static int handle_response_no_carrier(struct dc_pvt *pvt, char *buf);
+static int handle_response_conn(struct dc_pvt *pvt, char *buf);
+static int handle_response_orig(struct dc_pvt *pvt, char *buf);
+static int handle_response_cssi(struct dc_pvt *pvt, char *buf);
+static int handle_response_cssu(struct dc_pvt *pvt, char *buf);
+static int handle_response_cpin(struct dc_pvt *pvt, char *buf);
+static int handle_response_smmemfull(struct dc_pvt *pvt, char *buf);
+static int handle_response_rssi(struct dc_pvt *pvt, char *buf);
+static int handle_response_cops(struct dc_pvt *pvt, char *buf);
+static int handle_response_mode(struct dc_pvt *pvt, char *buf);
+static int handle_response_cgmi(struct dc_pvt *pvt, char *buf);
+static int handle_response_cgmm(struct dc_pvt *pvt, char *buf);
+static int handle_response_cgmr(struct dc_pvt *pvt, char *buf);
+static int handle_response_cgsn(struct dc_pvt *pvt, char *buf);
+
+static int handle_sms_prompt(struct dc_pvt *pvt, char *buf);
+
+/* Manager stuff */
+static int dc_manager_show_devices(struct mansession *s, const struct message *m);
+static int dc_manager_send_cusd(struct mansession *s, const struct message *m);
+static char *dc_send_manager_event_new_cusd(struct dc_pvt *pvt, char *message);
+
+static char *manager_show_devices_desc =
+"Description: Lists Datacard devices in text format with details on current status.\n"
+"\n"
+"DatacardShowDevicesComplete.\n"
+"Variables:\n"
+"	ActionID: <id>	Action ID for this transaction. Will be returned.\n";
+
+static char *manager_send_cusd_desc =
+"Description: Send a cusd message to a datacard.\n"
+"\n"
+"Variables: (Names marked with * are required)\n"
+"	ActionID: <id>	Action ID for this transaction. Will be returned.\n"
+"	*Device: <id>	The datacard to which the cusd code will be send.\n"
+"	*CUSD: <code>	The cusd code that will be send to the device.\n";
+
+/* CLI stuff */
+static char *handle_cli_dc_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_dc_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *handle_cli_dc_cusd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+
+static struct ast_cli_entry dc_cli[] = {
+	AST_CLI_DEFINE(handle_cli_dc_show_devices, "Show Datacard devices"),
+	AST_CLI_DEFINE(handle_cli_dc_rfcomm,       "Send commands to the rfcomm port for debugging"),
+	AST_CLI_DEFINE(handle_cli_dc_cusd,         "Send CUSD commands to the datacard"),
+};
+
+/* App stuff */
+static char *app_dcstatus = "DatacardStatus";
+static char *dcstatus_synopsis = "DatacardStatus(Device,Variable)";
+static char *dcstatus_desc =
+"DatacardStatus(Device,Variable)\n"
+"  Device - Id of device from datacard.conf\n"
+"  Variable - Variable to store status in will be 1-3.\n"
+"             In order, Disconnected, Connected & Free, Connected & Busy.\n";
+
+static char *app_dcsendsms = "DatacardSendSMS";
+static char *dcsendsms_synopsis = "DatacardSendSMS(Device,Dest,Message)";
+static char *dcsendsms_desc =
+"DatacardSendSms(Device,Dest,Message)\n"
+"  Device - Id of device from datacard.conf\n"
+"  Dest - destination\n"
+"  Message - text of the message\n";
+
+static struct ast_channel *dc_new(int state, struct dc_pvt *pvt, char *cid_num);
+static struct ast_channel *dc_request(const char *type, int format, void *data, int *cause);
+static int dc_call(struct ast_channel *ast, char *dest, int timeout);
+static int dc_hangup(struct ast_channel *ast);
+static int dc_answer(struct ast_channel *ast);
+static int dc_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
+static struct ast_frame *dc_audio_read(struct ast_channel *ast);
+static int dc_audio_write(struct ast_channel *ast, struct ast_frame *frame);
+static int dc_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
+static int dc_devicestate(void *data);
+
+static void do_alignment_detection(struct dc_pvt *pvt, char *buf, int buflen);
+
+static int dc_queue_control(struct dc_pvt *pvt, enum ast_control_frame_type control);
+static int dc_queue_hangup(struct dc_pvt *pvt);
+static int dc_ast_hangup(struct dc_pvt *pvt);
+
+static int dc_data_connect(char *data_tty_str);
+static int dc_audio_connect(char *audio_tty_str);
+static int dc_get_device_status(int fd);
+static int disconnect_datacard(struct dc_pvt *pvt);
+static int opentty(char *iface);
+static int rfcomm_write(int data_socket, char *buf);
+static int rfcomm_write_full(int data_socket, char *buf, size_t count);
+static int rfcomm_wait(int data_socket, int *ms);
+static ssize_t rfcomm_read(int data_socket, char *buf, size_t count);
+
+static int audio_write(int s, char *buf, int len);
+
+static char *dc_parse_clip(struct dc_pvt *pvt, char *buf);
+static char *dc_parse_cops(struct dc_pvt *pvt, char *buf);
+static int dc_parse_cmti(struct dc_pvt *pvt, char *buf);
+static int dc_parse_cmgr(struct dc_pvt *pvt, char *buf, char **from_number, char **text);
+static char *dc_parse_cusd(struct dc_pvt *pvt, char *buf);
+static int dc_parse_cpin(struct dc_pvt *pvt, char *buf);
+static int dc_parse_rssi(struct dc_pvt *pvt, char *buf);
+static int dc_parse_linkmode(struct dc_pvt *pvt, char *buf);
+static int dc_parse_linksubmode(struct dc_pvt *pvt, char *buf);
+
+
+static int dc_send_cpin_test(struct dc_pvt *pvt);
+static int dc_send_clip(struct dc_pvt *pvt, int status);
+static int dc_send_ddsetex(struct dc_pvt *pvt);
+static int dc_send_cvoice_test(struct dc_pvt *pvt);
+static int dc_send_cssn(struct dc_pvt *pvt, int cssi, int cssu);
+
+static int dc_send_ate0(struct dc_pvt *pvt);
+static int dc_send_atz(struct dc_pvt *pvt);
+static int dc_send_dtmf(struct dc_pvt *pvt, char digit);
+static int dc_send_cmgf(struct dc_pvt *pvt, int mode);
+static int dc_send_cnmi(struct dc_pvt *pvt);
+static int dc_send_cmgr(struct dc_pvt *pvt, int index);
+static int dc_send_cmgd(struct dc_pvt *pvt, int index);
+static int dc_send_cmgs(struct dc_pvt *pvt, const char *number);
+static int dc_send_sms_text(struct dc_pvt *pvt, char *message);
+static int dc_send_chup(struct dc_pvt *pvt);
+static int dc_send_atd(struct dc_pvt *pvt, const char *number);
+static int dc_send_ata(struct dc_pvt *pvt);
+static int dc_send_cusd(struct dc_pvt *pvt, char *code);
+static int dc_send_clvl(struct dc_pvt *pvt, int level);
+static int dc_send_cops_init(struct dc_pvt *pvt,int mode, int format);
+static int dc_send_cops(struct dc_pvt *pvt);
+static int dc_send_creg_init(struct dc_pvt *pvt, int level);
+static int dc_send_creg(struct dc_pvt *pvt);
+static int dc_send_cgmi(struct dc_pvt *pvt);
+static int dc_send_cgmm(struct dc_pvt *pvt);
+static int dc_send_cgmr(struct dc_pvt *pvt);
+static int dc_send_cgsn(struct dc_pvt *pvt);
+static int dc_send_cscs(struct dc_pvt *pvt, const char *encoding);
+
+/*
+ * Hayes AT command helpers
+ */
+typedef enum {
+	/* errors */
+	AT_PARSE_ERROR = -2,
+	AT_READ_ERROR = -1,
+	AT_UNKNOWN = 0,
+	/* at responses */
+	AT_OK,
+	AT_ERROR,
+	AT_RING,
+	AT_CLIP,
+	AT_CMTI,
+	AT_CMGR,
+	AT_CMGD,
+	AT_SMS_PROMPT,
+	AT_CMS_ERROR,
+	/* at commands */
+	AT_A,
+	AT_Z,
+	AT_D,
+	AT_E,
+	AT_DDSETEX,
+	AT_CVOICE,
+	AT_CONN,
+	AT_CEND,
+	AT_CONF,
+	AT_ORIG,
+	AT_SMMEMFULL,
+	AT_RSSI,
+	AT_BOOT,
+	AT_CSSN,
+	AT_CSSI,
+	AT_CSSU,
+	AT_CHUP,
+	AT_CKPD,
+	AT_CMGS,
+	AT_VGM,
+	AT_VGS,
+	AT_VTS,
+	AT_DTMF,
+	AT_CMGF,
+	AT_CNMI,
+	AT_CUSD,
+	AT_BUSY,
+	AT_NO_DIALTONE,
+	AT_NO_CARRIER,
+	AT_CPIN,
+	AT_COPS_INIT,
+	AT_COPS,
+	AT_CREG_INIT,
+	AT_CREG,
+	AT_MODE,
+	AT_I,
+	AT_CGMI,
+	AT_CGMM,
+	AT_CGMR,
+	AT_CGSN,
+	AT_CLVL,
+	AT_CPMS,
+	AT_SIMST,
+	AT_SRVST,
+	AT_CSCS,
+} at_message_t;
+
+static int at_match_prefix(char *buf, char *prefix);
+static at_message_t at_read_full(int data_socket, char *buf, size_t count);
+static inline const char *at_msg2str(at_message_t msg);
+
+struct msg_queue_entry {
+	at_message_t expected;
+	at_message_t response_to;
+	void *data;
+
+	AST_LIST_ENTRY(msg_queue_entry) entry;
+};
+
+static int msg_queue_push(struct dc_pvt *pvt, at_message_t expect, at_message_t response_to);
+static int msg_queue_push_data(struct dc_pvt *pvt, at_message_t expect, at_message_t response_to, void *data);
+static struct msg_queue_entry *msg_queue_pop(struct dc_pvt *pvt);
+static void msg_queue_free_and_pop(struct dc_pvt *pvt);
+static void msg_queue_flush(struct dc_pvt *pvt);
+static struct msg_queue_entry *msg_queue_head(struct dc_pvt *pvt);
+
+/*
+ * channel stuff
+ */
+
+static const struct ast_channel_tech dc_tech = {
+	.type = "Datacard",
+	.description = "Datacard Channel Driver",
+	.capabilities = AST_FORMAT_SLINEAR,
+	.requester = dc_request,
+	.call = dc_call,
+	.hangup = dc_hangup,
+	.answer = dc_answer,
+	.send_digit_end = dc_digit_end,
+	.read = dc_audio_read,
+	.write = dc_audio_write,
+	.fixup = dc_fixup,
+	.devicestate = dc_devicestate
+};
+
+/* CLI Commands implementation */
+
+static char *handle_cli_dc_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct dc_pvt *pvt;
+	char group[6];
+	char rssi[6];
+	char linkmode[6];
+	char linksubmode[6];
+
+#define FORMAT1 "%-15.15s %-6.6s %-9.9s %-5.5s %-5.5s %-5.5s %-5.5s %-5.5s %-7.7s %-15.15s %-12.12s %-10.10s %-17.17s %-17.17s\n"
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "datacard show devices";
+		e->usage =
+			"Usage: datacard show devices\n"
+			"       Shows the state of Datacard devices.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 3)
+		return CLI_SHOWUSAGE;
+
+	ast_cli(a->fd, FORMAT1, "ID", "Group", "Connected", "State", "Voice", "SMS", "RSSI", "Mode", "Submode", "Provider Name", "Manufacturer", "Model", "Firmware", "IMEI");
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		ast_mutex_lock(&pvt->lock);
+		snprintf(group, sizeof(group), "%d", pvt->group);
+		snprintf(rssi, sizeof(rssi), "%d", pvt->rssi);
+		snprintf(linkmode, sizeof(linkmode), "%d", pvt->linkmode);
+		snprintf(linksubmode, sizeof(linksubmode), "%d", pvt->linksubmode);
+		ast_cli(a->fd, FORMAT1,
+				pvt->id,
+				group,
+				pvt->connected ? "Yes" : "No",
+				(!pvt->connected) ? "None" : (pvt->outgoing || pvt->incoming) ? "Busy" : (pvt->outgoing_sms || pvt->incoming_sms) ? "SMS" : "Free",
+				(pvt->has_voice) ? "Yes" : "No",
+				(pvt->has_sms) ? "Yes" : "No",
+				rssi,
+				linkmode,
+				linksubmode,
+				pvt->provider_name,
+				pvt->manufacturer,
+				pvt->model,
+				pvt->firmware,
+				pvt->imei
+		       );
+		ast_mutex_unlock(&pvt->lock);
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+#undef FORMAT1
+
+	return CLI_SUCCESS;
+}
+
+static int dc_manager_show_devices(struct mansession *s, const struct message *m)
+{
+	struct dc_pvt *pvt;
+	int count = 0;
+	const char *id = astman_get_header(m, "ActionID");
+	char idtext[256] = "";
+
+	if (!ast_strlen_zero(id))
+		snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+	astman_send_listack(s, m, "Device status list will follow", "start");
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		ast_mutex_lock(&pvt->lock);
+		astman_append(s,"Event: DatacardDeviceEntry\r\n%s", idtext);
+		astman_append(s,"Device: %s\r\n", pvt->id);
+		astman_append(s,"Group: %d\r\n", pvt->group);
+		astman_append(s,"Connected: %s\r\n", pvt->connected ? "Yes" : "No");
+		astman_append(s,"State: %s\r\n", (!pvt->connected) ? "None" : (pvt->outgoing || pvt->incoming) ? "Busy" : (pvt->outgoing_sms || pvt->incoming_sms) ? "SMS" : "Free");
+		astman_append(s,"Voice: %s\r\n", (pvt->has_voice) ? "Yes" : "No");
+		astman_append(s,"SMS: %s\r\n", (pvt->has_sms) ? "Yes" : "No");
+		astman_append(s,"RSSI: %d\r\n", pvt->rssi);
+		astman_append(s,"Mode: %d\r\n", pvt->linkmode);
+		astman_append(s,"Submode: %d\r\n", pvt->linksubmode);
+		astman_append(s,"ProviderName: %s\r\n", pvt->provider_name);
+		astman_append(s,"Manufacturer: %s\r\n", pvt->manufacturer);
+		astman_append(s,"Model: %s\r\n", pvt->model);
+		astman_append(s,"Firmware: %s\r\n", pvt->firmware);
+		astman_append(s,"IMEI: %s\r\n", pvt->imei);
+		astman_append(s,"\r\n");
+		count++;
+		ast_mutex_unlock(&pvt->lock);
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	astman_append(s,
+		"Event: DatacardShowDevicesComplete\r\n%s"
+		"EventList: Complete\r\n"
+		"ListItems: %d\r\n"
+		"\r\n",
+		idtext,
+		count);
+	return 0;
+}
+
+static char *handle_cli_dc_rfcomm(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	char buf[128];
+	struct dc_pvt *pvt = NULL;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "datacard rfcomm";
+		e->usage =
+			"Usage: datacard rfcomm <device ID> <command>\n"
+			"       Send <command> to the rfcomm port on the device\n"
+			"       with the specified <device ID>.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 4)
+		return CLI_SHOWUSAGE;
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		if (!strcmp(pvt->id, a->argv[2]))
+			break;
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	if (!pvt) {
+		ast_cli(a->fd, "Device %s not found.\n", a->argv[2]);
+		goto e_return;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	if (!pvt->connected) {
+		ast_cli(a->fd, "Device %s not connected.\n", a->argv[2]);
+		goto e_unlock_pvt;
+	}
+
+	snprintf(buf, sizeof(buf), "%s\r", a->argv[3]);
+	rfcomm_write(pvt->data_socket, buf);
+	msg_queue_push(pvt, AT_OK, AT_UNKNOWN);
+
+e_unlock_pvt:
+	ast_mutex_unlock(&pvt->lock);
+e_return:
+	return CLI_SUCCESS;
+}
+
+static char *handle_cli_dc_cusd(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	char buf[128];
+	struct dc_pvt *pvt = NULL;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "datacard cusd";
+		e->usage =
+			"Usage: datacard cusd <device ID> <command>\n"
+			"       Send cusd <command> to the datacard\n"
+			"       with the specified <device ID>.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 4)
+		return CLI_SHOWUSAGE;
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		if (!strcmp(pvt->id, a->argv[2]))
+			break;
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	if (!pvt) {
+		ast_cli(a->fd, "Device %s not found.\n", a->argv[2]);
+		goto e_return;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	if (!pvt->connected) {
+		ast_cli(a->fd, "Device %s not connected.\n", a->argv[2]);
+		goto e_unlock_pvt;
+	}
+
+	snprintf(buf, sizeof(buf), "%s", a->argv[3]);
+	dc_send_cusd(pvt, buf);
+	msg_queue_push(pvt, AT_OK, AT_CUSD);
+
+e_unlock_pvt:
+	ast_mutex_unlock(&pvt->lock);
+e_return:
+	return CLI_SUCCESS;
+}
+
+static int dc_manager_send_cusd(struct mansession *s, const struct message *m)
+{
+	char buf2[128];
+	char idtext[256] = "";
+	struct dc_pvt *pvt = NULL;
+	const char *id = astman_get_header(m, "ActionID");
+	const char *device = astman_get_header(m, "Device");
+	const char *cusd = astman_get_header(m, "CUSD");
+
+	if (ast_strlen_zero(device)) {
+		astman_send_error(s, m, "Device not specified");
+		return 0;
+	}
+
+		if (ast_strlen_zero(cusd)) {
+		astman_send_error(s, m, "CUSD not specified");
+		return 0;
+	}
+
+	if (!ast_strlen_zero(id))
+		snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		if (!strcmp(pvt->id, device))
+			break;
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	if (!pvt) {
+		char buf[256];
+		snprintf(buf, sizeof(buf), "Device %s not found.", device);
+		astman_send_error(s, m, buf);
+		goto e_return;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	if (!pvt->connected) {
+		char buf[256];
+		snprintf(buf, sizeof(buf), "Device %s not connected.", device);
+		astman_send_error(s, m, buf);
+		goto e_unlock_pvt;
+	}
+
+	snprintf(buf2, sizeof(buf2), "%s", cusd);
+	dc_send_cusd(pvt, buf2);
+	msg_queue_push(pvt, AT_OK, AT_CUSD);
+	astman_send_ack(s, m, "CUSD code send successful");
+
+e_unlock_pvt:
+	ast_mutex_unlock(&pvt->lock);
+e_return:
+	return 0;
+}
+
+/*
+
+	Dialplan applications implementation
+
+*/
+
+static int dc_status_exec(struct ast_channel *ast, void *data)
+{
+
+	struct dc_pvt *pvt;
+	char *parse;
+	int stat;
+	char status[2];
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(device);
+		AST_APP_ARG(variable);
+	);
+
+	if (ast_strlen_zero(data))
+		return -1;
+
+	parse = ast_strdupa(data);
+
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (ast_strlen_zero(args.device) || ast_strlen_zero(args.variable))
+		return -1;
+
+	stat = 1;
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		if (!strcmp(pvt->id, args.device))
+			break;
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	if (pvt) {
+		ast_mutex_lock(&pvt->lock);
+		if (pvt->connected)
+			stat = 2;
+		if (pvt->owner)
+			stat = 3;
+		ast_mutex_unlock(&pvt->lock);
+	}
+
+	snprintf(status, sizeof(status), "%d", stat);
+	pbx_builtin_setvar_helper(ast, args.variable, status);
+
+	return 0;
+
+}
+
+static int dc_sendsms_exec(struct ast_channel *ast, void *data)
+{
+
+	struct dc_pvt *pvt;
+	char *parse, *message;
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(device);
+		AST_APP_ARG(dest);
+		AST_APP_ARG(message);
+	);
+
+	if (ast_strlen_zero(data))
+		return -1;
+
+	parse = ast_strdupa(data);
+
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (ast_strlen_zero(args.device)) {
+		ast_log(LOG_ERROR,"NULL device for message -- SMS will not be sent.\n");
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.dest)) {
+		ast_log(LOG_ERROR,"NULL destination for message -- SMS will not be sent.\n");
+		return -1;
+	}
+
+	if (ast_strlen_zero(args.message)) {
+		ast_log(LOG_ERROR,"NULL Message to be sent -- SMS will not be sent.\n");
+		return -1;
+	}
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		if (!strcmp(pvt->id, args.device))
+			break;
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	if (!pvt) {
+		ast_log(LOG_ERROR,"Datacard %s wasn't found in the list -- SMS will not be sent.\n", args.device);
+		goto e_return;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	if (!pvt->connected) {
+		ast_log(LOG_ERROR,"Datacard %s wasn't connected -- SMS will not be sent.\n", args.device);
+		goto e_unlock_pvt;
+	}
+
+	if (!pvt->has_sms) {
+		ast_log(LOG_ERROR,"Datacard %s doesn't handle SMS -- SMS will not be sent.\n", args.device);
+		goto e_unlock_pvt;
+	}
+
+	message = ast_strdup(args.message);
+
+	if (dc_send_cmgs(pvt, args.dest)
+		|| msg_queue_push_data(pvt, AT_SMS_PROMPT, AT_CMGS, message)) {
+
+		ast_log(LOG_ERROR, "[%s] problem sending SMS message\n", pvt->id);
+		goto e_free_message;
+	}
+
+	ast_mutex_unlock(&pvt->lock);
+
+	return 0;
+
+e_free_message:
+	ast_free(message);
+e_unlock_pvt:
+	ast_mutex_unlock(&pvt->lock);
+e_return:
+	return -1;
+}
+
+/*
+
+	Channel Driver callbacks
+
+*/
+
+static struct ast_channel *dc_new(int state, struct dc_pvt *pvt, char *cid_num)
+{
+	struct ast_channel *chn;
+
+	pvt->answered = 0;
+	pvt->alignment_count = 0;
+	pvt->alignment_detection_triggered = 0;
+
+	ast_smoother_reset(pvt->smoother, DEVICE_FRAME_SIZE);
+	ast_dsp_digitreset(pvt->dsp);
+
+	chn = ast_channel_alloc(1, state, cid_num, pvt->id, 0, 0, pvt->context, 0, "Datacard/%s-%04lx", pvt->id, ast_random() & 0xffff);
+	if (!chn) {
+		goto e_return;
+	}
+
+	chn->tech = &dc_tech;
+	chn->nativeformats = prefformat;
+	chn->rawreadformat = prefformat;
+	chn->rawwriteformat = prefformat;
+	chn->writeformat = prefformat;
+	chn->readformat = prefformat;
+	chn->tech_pvt = pvt;
+
+	ast_jb_configure(chn, &global_jbconf);
+
+	if (state == AST_STATE_RING)
+		chn->rings = 1;
+
+	ast_string_field_set(chn, language, "en");
+	pvt->owner = chn;
+
+	if (pvt->audio_socket != -1) {
+		ast_channel_set_fd(chn, 0, pvt->audio_socket);
+	}
+
+	return chn;
+
+e_return:
+	return NULL;
+}
+
+static struct ast_channel *dc_request(const char *type, int format, void *data, int *cause)
+{
+
+	struct ast_channel *chn = NULL;
+	struct dc_pvt *pvt;
+	struct dc_pvt *device_list[256];
+	char *dest_dev = NULL;
+	char *dest_num = NULL;
+	int oldformat, group = -1;
+	int loop_count = 0;
+	int loop_count2 = 0;
+	int last_used_device = 0;
+	int i = 0;
+	for (i=0;i<256;i++) {
+		device_list[i] = NULL;
+	}
+
+	if (!data) {
+		ast_log(LOG_WARNING, "Channel requested with no data\n");
+		*cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+		return NULL;
+	}
+
+	oldformat = format;
+	format &= (AST_FORMAT_SLINEAR);
+	if (!format) {
+		ast_log(LOG_WARNING, "Asked to get a channel of unsupported format '%d'\n", oldformat);
+		*cause = AST_CAUSE_FACILITY_NOT_IMPLEMENTED;
+		return NULL;
+	}
+
+	dest_dev = ast_strdupa((char *)data);
+
+	dest_num = strchr(dest_dev, '/');
+	if (dest_num)
+		*dest_num++ = 0x00;
+
+	/* Find requested device and make sure it's connected. */
+	if (((dest_dev[0] == 'g') || (dest_dev[0] == 'G')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) {
+		group = atoi(&dest_dev[1]);
+		AST_RWLIST_RDLOCK(&devices);
+		AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+			if (group > -1 && pvt->group == group && pvt->connected && !pvt->owner) {
+				break;
+			}
+		}
+		AST_RWLIST_UNLOCK(&devices);
+	}
+	else if (((dest_dev[0] == 'r') || (dest_dev[0] == 'R')) && ((dest_dev[1] >= '0') && (dest_dev[1] <= '9'))) {
+		group = atoi(&dest_dev[1]);
+		AST_RWLIST_RDLOCK(&devices);
+		
+		// Generate a list of all availible devices
+		AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+			if (group > -1 && pvt->group == group) {
+				device_list[loop_count] = pvt;
+				loop_count++;
+			}
+		}
+
+		// Find last used device
+		for (i=0;i<loop_count;i++) {
+			if (device_list[i]->group_last_used == 1) {
+				last_used_device = i;
+				device_list[i]->group_last_used = 0;
+				break;
+			}
+		}
+
+		// Search for a availible device starting at the last used device 
+		loop_count2 = last_used_device;
+
+		for (i=0;i<loop_count;i++) {
+			loop_count2++;
+			if (loop_count2 == loop_count) {
+				loop_count2 = 0;
+			}
+
+			pvt=device_list[loop_count2];
+
+			if (pvt->connected && !pvt->owner) {
+				pvt->group_last_used = 1;
+				break;
+			}
+		}
+
+		AST_RWLIST_UNLOCK(&devices);
+	}
+	else {
+		AST_RWLIST_RDLOCK(&devices);
+		AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+			if (!strcmp(pvt->id, dest_dev)) {
+				break;
+			}
+		}
+		AST_RWLIST_UNLOCK(&devices);
+	}
+
+	if (!pvt || !pvt->connected || pvt->owner) {
+		ast_log(LOG_WARNING, "Request to call on device %s which is not connected / already in use.\n", dest_dev);
+		*cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+		return NULL;
+	}
+
+	if (!dest_num) {
+		ast_log(LOG_WARNING, "Can't determine destination number.\n");
+		*cause = AST_CAUSE_INCOMPATIBLE_DESTINATION;
+		return NULL;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	chn = dc_new(AST_STATE_DOWN, pvt, NULL);
+	ast_mutex_unlock(&pvt->lock);
+	if (!chn) {
+		ast_log(LOG_WARNING, "Unable to allocate channel structure.\n");
+		*cause = AST_CAUSE_REQUESTED_CHAN_UNAVAIL;
+		return NULL;
+	}
+
+	return chn;
+
+}
+
+static int dc_call(struct ast_channel *ast, char *dest, int timeout)
+{
+
+	struct dc_pvt *pvt;
+	char *dest_dev = NULL;
+	char *dest_num = NULL;
+
+	dest_dev = ast_strdupa((char *)dest);
+
+	pvt = ast->tech_pvt;
+
+	dest_num = strchr(dest_dev, '/');
+	if (!dest_num) {
+		ast_log(LOG_WARNING, "Cant determine destination number.\n");
+		return -1;
+	}
+	*dest_num++ = 0x00;
+
+	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
+		ast_log(LOG_WARNING, "dc_call called on %s, neither down nor reserved\n", ast->name);
+		return -1;
+	}
+
+	ast_debug(1, "Calling %s on %s\n", dest, ast->name);
+
+	ast_mutex_lock(&pvt->lock);
+
+	if (dc_send_atd(pvt, dest_num)) {
+		ast_mutex_unlock(&pvt->lock);
+		ast_log(LOG_ERROR, "error sending ATD command on %s\n", pvt->id);
+		return -1;
+	}
+	pvt->hangupcause = 0;
+	pvt->needchup = 1;
+	pvt->outgoing = 1;
+	msg_queue_push(pvt, AT_OK, AT_D);
+	
+	ast_mutex_unlock(&pvt->lock);
+
+	return 0;
+
+}
+
+static int dc_hangup(struct ast_channel *ast)
+{
+
+	struct dc_pvt *pvt;
+
+	if (!ast->tech_pvt) {
+		ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
+		return 0;
+	}
+	pvt = ast->tech_pvt;
+
+	ast_debug(1, "[%s] hanging up device\n", pvt->id);
+
+	ast_mutex_lock(&pvt->lock);
+
+	if (pvt->needchup) {
+		dc_send_chup(pvt);
+		msg_queue_push(pvt, AT_OK, AT_CHUP);
+		pvt->needchup = 0;
+	}
+
+	pvt->outgoing = 0;
+	pvt->incoming = 0;
+	pvt->needring = 0;
+	pvt->owner = NULL;
+	ast->tech_pvt = NULL;
+
+	ast_mutex_unlock(&pvt->lock);
+
+	ast_setstate(ast, AST_STATE_DOWN);
+
+	return 0;
+
+}
+
+static int dc_answer(struct ast_channel *ast)
+{
+
+	struct dc_pvt *pvt;
+
+	pvt = ast->tech_pvt;
+
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->incoming) {
+		dc_send_ata(pvt);
+		msg_queue_push(pvt, AT_OK, AT_A);
+		pvt->answered = 1;
+	}
+	ast_mutex_unlock(&pvt->lock);
+
+	return 0;
+
+}
+
+static int dc_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
+{
+	struct dc_pvt *pvt = ast->tech_pvt;
+
+	ast_mutex_lock(&pvt->lock);
+	if (dc_send_dtmf(pvt, digit)) {
+		ast_mutex_unlock(&pvt->lock);
+		ast_debug(1, "[%s] error sending digit %c\n", pvt->id, digit);
+		return -1;
+	}
+	//msg_queue_push(pvt, AT_OK, AT_VTS);
+	msg_queue_push(pvt, AT_OK, AT_DTMF);
+	ast_mutex_unlock(&pvt->lock);
+
+	ast_debug(1, "[%s] dialed %c\n", pvt->id, digit);
+
+	return 0;
+}
+
+static struct ast_frame *dc_audio_read(struct ast_channel *ast)
+{
+
+	struct dc_pvt *pvt = ast->tech_pvt;
+	struct ast_frame *fr = &ast_null_frame;
+	int r;
+
+	ast_debug(3, "*** dc_audio_read()\n");
+
+	while (ast_mutex_trylock(&pvt->lock)) {
+		CHANNEL_DEADLOCK_AVOIDANCE(ast);
+	}
+
+	if (!pvt->owner || pvt->audio_socket == -1) {
+		goto e_return;
+	}
+
+	memset(&pvt->fr, 0x00, sizeof(struct ast_frame));
+	pvt->fr.frametype = AST_FRAME_VOICE;
+	pvt->fr.subclass = DEVICE_FRAME_FORMAT;
+	pvt->fr.src = "Datacard";
+	pvt->fr.offset = AST_FRIENDLY_OFFSET;
+	pvt->fr.mallocd = 0;
+	pvt->fr.delivery.tv_sec = 0;
+	pvt->fr.delivery.tv_usec = 0;
+	pvt->fr.data.ptr = pvt->io_buf + AST_FRIENDLY_OFFSET;
+
+	if ((r = read(pvt->audio_socket, pvt->fr.data.ptr, DEVICE_FRAME_SIZE)) == -1) {
+		if (errno != EAGAIN && errno != EINTR) {
+			ast_debug(1, "[%s] read error %d, going to wait for new connection\n", pvt->id, errno);
+			//close(pvt->audio_socket);
+			//pvt->audio_socket = -1;
+			//ast_channel_set_fd(ast, 0, -1);
+		}
+		goto e_return;
+	}
+
+	pvt->fr.datalen = r;
+	pvt->fr.samples = r / 2;
+
+	do_alignment_detection(pvt, pvt->fr.data.ptr, r);
+
+	fr = ast_dsp_process(ast, pvt->dsp, &pvt->fr);
+
+	if (pvt->rxgain!=0) {
+		// Lets adjust the volume of the incoming audio
+		if (ast_frame_adjust_volume(fr, pvt->rxgain) != 0) {
+			ast_debug(1, "[%s] volume could not be adjusted!\n", pvt->id);
+		}
+	}
+
+	ast_mutex_unlock(&pvt->lock);
+
+	return fr;
+
+e_return:
+	ast_mutex_unlock(&pvt->lock);
+	return fr;
+}
+
+static int dc_audio_write(struct ast_channel *ast, struct ast_frame *frame)
+{
+
+	struct dc_pvt *pvt = ast->tech_pvt;
+	struct ast_frame *f;
+
+	ast_debug(3, "*** dc_write\n");
+
+	if (frame->frametype != AST_FRAME_VOICE) {
+		return 0;
+	}
+
+	while (ast_mutex_trylock(&pvt->lock)) {
+		CHANNEL_DEADLOCK_AVOIDANCE(ast);
+	}
+
+	ast_smoother_feed(pvt->smoother, frame);
+
+	while ((f = ast_smoother_read(pvt->smoother))) {
+		if (pvt->txgain!=0) {
+			// Lets adjust the volume of the incoming audio
+			if (ast_frame_adjust_volume(f, pvt->txgain) != 0) {
+				ast_debug(1, "[%s] volume could not be adjusted!\n", pvt->id);
+			}
+		}
+		audio_write(pvt->audio_socket, f->data.ptr, f->datalen);
+		ast_frfree(f);
+	}
+
+	ast_mutex_unlock(&pvt->lock);
+
+	return 0;
+
+}
+
+static int dc_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+
+	struct dc_pvt *pvt = newchan->tech_pvt;
+
+	if (!pvt) {
+		ast_debug(1, "fixup failed, no pvt on newchan\n");
+		return -1;
+	}
+
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->owner == oldchan)
+		pvt->owner = newchan;
+	ast_mutex_unlock(&pvt->lock);
+
+	return 0;
+
+}
+
+static int dc_devicestate(void *data)
+{
+
+	char *device;
+	int res = AST_DEVICE_INVALID;
+	struct dc_pvt *pvt;
+
+	device = ast_strdupa(S_OR(data, ""));
+
+	ast_debug(1, "Checking device state for device %s\n", device);
+
+	AST_RWLIST_RDLOCK(&devices);
+	AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+		if (!strcmp(pvt->id, device))
+			break;
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	if (!pvt)
+		return res;
+
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->connected) {
+		if (pvt->owner)
+			res = AST_DEVICE_INUSE;
+		else
+			res = AST_DEVICE_NOT_INUSE;
+	}
+	ast_mutex_unlock(&pvt->lock);
+
+	return res;
+
+}
+
+/*
+
+	Callback helpers
+
+*/
+
+/*
+
+	do_alignment_detection()
+
+	This routine attempts to detect where we get misaligned audio data from the datacard.
+
+	Sometimes datacards suffer a problem where occasionally they will byte shift the audio stream one byte to the right.
+	The result is static or white noise on the inbound (from the adapter) leg of the call.
+	This is characterised by a sudden jump in magnitude of the value of the 16 bit samples.
+
+	Here we look at the first 4 48 byte frames. We average the absolute values of each sample in the frame,
+	then average the sum of the averages of frames 1, 2, and 3.
+	Frame zero is usually zero.
+	If the end result > 100, and it usually is if we have the problem, set a flag and compensate by shifting the bytes
+	for each subsequent frame during the call.
+
+	If the result is <= 100 then clear the flag so we dont come back in here...
+
+	This seems to work OK....
+
+*/
+
+static void do_alignment_detection(struct dc_pvt *pvt, char *buf, int buflen)
+{
+
+	int i;
+	short a, *s;
+	char *p;
+
+	if (pvt->alignment_detection_triggered) {
+		for (i=buflen, p=buf+buflen-1; i>0; i--, p--)
+			*p = *(p-1);
+		*(p+1) = 0;
+		return;
+	}
+
+	if (pvt->alignment_count < 4) {
+		s = (short *)buf;
+		for (i=0, a=0; i<buflen/2; i++) {
+			a += *s++;
+			a /= i+1;
+		}
+		pvt->alignment_samples[pvt->alignment_count++] = a;
+		return;
+	}
+
+	ast_debug(2, "Alignment Detection result is [%-d %-d %-d %-d]\n", pvt->alignment_samples[0], pvt->alignment_samples[1], pvt->alignment_samples[2], pvt->alignment_samples[3]);
+
+	a = abs(pvt->alignment_samples[1]) + abs(pvt->alignment_samples[2]) + abs(pvt->alignment_samples[3]);
+	a /= 3;
+	if (a > 100) {
+		pvt->alignment_detection_triggered = 1;
+		ast_debug(1, "Alignment Detection Triggered.\n");
+	} else
+		pvt->do_alignment_detection = 0;
+
+}
+
+static int dc_queue_control(struct dc_pvt *pvt, enum ast_control_frame_type control)
+{
+	for (;;) {
+		if (pvt->owner) {
+			if (ast_channel_trylock(pvt->owner)) {
+				DEADLOCK_AVOIDANCE(&pvt->lock);
+			} else {
+				ast_queue_control(pvt->owner, control);
+				ast_channel_unlock(pvt->owner);
+				break;
+			}
+		} else
+			break;
+	}
+	return 0;
+}
+
+static int dc_queue_hangup(struct dc_pvt *pvt)
+{
+	for (;;) {
+		if (pvt->owner) {
+			if (ast_channel_trylock(pvt->owner)) {
+				DEADLOCK_AVOIDANCE(&pvt->lock);
+			} else {
+				if (pvt->hangupcause != 0) {
+					pvt->owner->hangupcause = pvt->hangupcause;
+				}
+				ast_queue_hangup(pvt->owner);
+				ast_channel_unlock(pvt->owner);
+				break;
+			}
+		} else
+			break;
+	}
+	return 0;
+}
+
+static int dc_ast_hangup(struct dc_pvt *pvt)
+{
+	int res = 0;
+	for (;;) {
+		if (pvt->owner) {
+			if (ast_channel_trylock(pvt->owner)) {
+				DEADLOCK_AVOIDANCE(&pvt->lock);
+			} else {
+				res = ast_hangup(pvt->owner);
+				/* no need to unlock, ast_hangup() frees the
+				 * channel */
+				break;
+			}
+		} else
+			break;
+	}
+	return res;
+}
+
+/*
+
+	rfcomm helpers
+
+*/
+
+static int opentty(char *iface)
+{
+	int fd;
+	struct termios term_attr;
+
+	fd = open(iface, O_RDWR | O_NOCTTY);
+
+	if (fd < 0) {
+		ast_log(LOG_WARNING, "Unable to open '%s'\n", iface);
+		return -1;
+	}
+
+	if (tcgetattr(fd, &term_attr) != 0) {
+		ast_log(LOG_WARNING, "tcgetattr() failed '%s'\n", iface);
+		return -1;
+	}
+
+	term_attr.c_cflag = B115200 | CS8 | CREAD;
+	term_attr.c_iflag = 0;
+	term_attr.c_oflag = 0;
+	term_attr.c_lflag = 0;
+	term_attr.c_cc[VMIN] = 1;
+	term_attr.c_cc[VTIME] = 0;
+
+	if (tcsetattr(fd, TCSAFLUSH, &term_attr) != 0) {
+		ast_log(LOG_WARNING, "tcsetattr() failed '%s'\n", iface);
+	}
+
+	return fd;
+}
+
+static int dc_data_connect(char *data_tty_str)
+{
+	return opentty(data_tty_str);
+}
+
+static int dc_audio_connect(char *audio_tty_str)
+{
+	return opentty(audio_tty_str);
+}
+
+/*!
+ * Get status of the datacard. It might happen that the device disappears (e.g.
+ * due to a USB unplug).
+ *
+ * \return 1 if device seems ok, 0 if it seems not available
+ */
+static int dc_get_device_status(int fd)
+{
+	struct termios t;
+	if (fd < 0)
+		return 0;
+	return !tcgetattr(fd, &t);
+}
+
+/*!
+ * \brief Write to an rfcomm socket.
+ * \param data_socket the socket to write to
+ * \param buf the null terminated buffer to write
+ *
+ * This function will write characters from buf.  The buffer must be null
+ * terminated.
+ *
+ * \retval -1 error
+ * \retval 0 success
+ */
+static int rfcomm_write(int data_socket, char *buf)
+{
+	return rfcomm_write_full(data_socket, buf, strlen(buf));
+}
+
+
+/*!
+ * \brief Write to an rfcomm socket.
+ * \param data_socket the socket to write to
+ * \param buf the buffer to write
+ * \param count the number of characters from the buffer to write
+ *
+ * This function will write count characters from buf.  It will always write
+ * count chars unless it encounters an error.
+ *
+ * \retval -1 error
+ * \retval 0 success
+ */
+static int rfcomm_write_full(int data_socket, char *buf, size_t count)
+{
+	char *p = buf;
+	ssize_t out_count;
+
+	ast_debug(1, "rfcomm_write() (%d) [%.*s]\n", data_socket, (int) count, buf);
+	while (count > 0) {
+		if ((out_count = write(data_socket, p, count)) == -1) {
+			if (errno==EBADF) ast_debug(1, "rfcomm_write() error: EBADF");
+			if (errno==EINVAL) ast_debug(1, "rfcomm_write() error: EINVAL");
+			if (errno==EFAULT) ast_debug(1, "rfcomm_write() error: EFAULT");
+			if (errno==EPIPE) ast_debug(1, "rfcomm_write() error: EPIPE");
+			if (errno==EAGAIN) ast_debug(1, "rfcomm_write() error: EAGAIN");
+			if (errno==EINTR) ast_debug(1, "rfcomm_write() error: EINTR");
+			if (errno==ENOSPC) ast_debug(1, "rfcomm_write() error: ENOSPC");
+			ast_debug(1, "rfcomm_write() error [%d]\n", errno);
+			return -1;
+		}
+		count -= out_count;
+		p += out_count;
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Wait for activity on an rfcomm socket.
+ * \param data_socket the socket to watch
+ * \param ms a pointer to an int containing a timeout in ms
+ * \return zero on timeout and the socket fd (non-zero) otherwise
+ * \retval 0 timeout
+ */
+static int rfcomm_wait(int data_socket, int *ms)
+{
+	int exception, outfd;
+	outfd = ast_waitfor_n_fd(&data_socket, 1, ms, &exception);
+	if (outfd < 0)
+		outfd = 0;
+
+	return outfd;
+}
+
+#ifdef RFCOMM_READ_DEBUG
+#define rfcomm_read_debug(c) __rfcomm_read_debug(c)
+static void __rfcomm_read_debug(char c)
+{
+	if (c == '\r')
+		ast_debug(2, "rfcomm_read: \\r\n");
+	else if (c == '\n')
+		ast_debug(2, "rfcomm_read: \\n\n");
+	else
+		ast_debug(2, "rfcomm_read: %c\n", c);
+}
+#else
+#define rfcomm_read_debug(c)
+#endif
+
+/*!
+ * \brief Append the given character to the given buffer and increase the
+ * in_count.
+ */
+static void inline rfcomm_append_buf(char **buf, size_t count, size_t *in_count, char c)
+{
+	if (*in_count < count) {
+		(*in_count)++;
+		*(*buf)++ = c;
+	}
+}
+
+/*!
+ * \brief Read a character from the given stream and check if it matches what
+ * we expected.
+ */
+static int rfcomm_read_and_expect_char(int data_socket, char *result, char expected)
+{
+	int res;
+	char c;
+
+	if (!result)
+		result = &c;
+
+	if ((res = read(data_socket, result, 1)) < 1) {
+		return res;
+	}
+	rfcomm_read_debug(*result);
+
+	if (*result != expected) {
+		return -2;
+	}
+
+	return 1;
+}
+
+/*!
+ * \brief Read a character from the given stream and append it to the given
+ * buffer if it matches the expected character.
+ */
+static int rfcomm_read_and_append_char(int data_socket, char **buf, size_t count, size_t *in_count, char *result, char expected)
+{
+	int res;
+	char c;
+
+	if (!result)
+		result = &c;
+
+	if ((res = rfcomm_read_and_expect_char(data_socket, result, expected)) < 1) {
+		return res;
+	}
+
+	rfcomm_append_buf(buf, count, in_count, *result);
+	return 1;
+}
+
+/*!
+ * \brief Read until '\r\n'.
+ * This function consumes the '\r\n' but does not add it to buf.
+ */
+static int rfcomm_read_until_crlf(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	while ((res = read(data_socket, &c, 1)) == 1) {
+		rfcomm_read_debug(c);
+
+		// Fix: The Huawei sticks do not terminate this command with a \r\n
+		// So we have to handle this command separately 
+		if (*in_count >= 7 && !strncmp(*buf - *in_count, "+CSSI: ", 7)) {
+			rfcomm_append_buf(buf, count, in_count, c);
+			return 1;
+		}
+
+		// Fix: The Huawei sticks do not terminate this command with a \r\n
+		// So we have to handle this command separately
+		if (*in_count >= 7 && !strncmp(*buf - *in_count, "+CSSU: ", 7)) {
+			rfcomm_append_buf(buf, count, in_count, c);
+			return 1;
+		}
+
+		if (c == '\r') {
+			if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\n')) == 1) {
+				break;
+			} else if (res == -2) {
+				rfcomm_append_buf(buf, count, in_count, '\r');
+			} else {
+				rfcomm_append_buf(buf, count, in_count, '\r');
+				break;
+			}
+		}
+
+		rfcomm_append_buf(buf, count, in_count, c);
+	}
+	return res;
+}
+
+/*!
+ * \brief Read the remainder of an AT SMS prompt.
+ * \note the entire parsed string is '\r\n> '
+ *
+ * By the time this function is executed, only a ' ' is left to read.
+ */
+static int rfcomm_read_sms_prompt(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	if ((res = rfcomm_read_and_append_char(data_socket, buf, count, in_count, NULL, ' ')) < 1)
+	       goto e_return;
+
+	return 1;
+
+e_return:
+	ast_log(LOG_ERROR, "error parsing SMS prompt on rfcomm socket\n");
+	return res;
+}
+
+/*!
+ * \brief Read until a \r\nOK\r\n message.
+ */
+static int rfcomm_read_until_ok(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	/* here, we read until finding a \r\n, then we read one character at a
+	 * time looking for the string '\r\nOK\r\n'.  If we only find a partial
+	 * match, we place that in the buffer and try again. */
+
+	for (;;) {
+		if ((res = rfcomm_read_until_crlf(data_socket, buf, count, in_count)) != 1) {
+			break;
+		}
+
+		rfcomm_append_buf(buf, count, in_count, '\r');
+		rfcomm_append_buf(buf, count, in_count, '\n');
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\r')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\n')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\r');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, 'O')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\r');
+			rfcomm_append_buf(buf, count, in_count, '\n');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, 'K')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\r');
+			rfcomm_append_buf(buf, count, in_count, '\n');
+			rfcomm_append_buf(buf, count, in_count, 'O');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\r')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\r');
+			rfcomm_append_buf(buf, count, in_count, '\n');
+			rfcomm_append_buf(buf, count, in_count, 'O');
+			rfcomm_append_buf(buf, count, in_count, 'K');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\n')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\r');
+			rfcomm_append_buf(buf, count, in_count, '\n');
+			rfcomm_append_buf(buf, count, in_count, 'O');
+			rfcomm_append_buf(buf, count, in_count, 'K');
+			rfcomm_append_buf(buf, count, in_count, '\r');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		/* we have successfully parsed a '\r\nOK\r\n' string */
+		return 1;
+	}
+
+	return res;
+}
+
+/*!
+ * \brief Read until a ",15\r\n message.
+ */
+static int rfcomm_read_until_cusd_end(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	/* here, we read one character at a time looking for the
+	 * string '",15\r\n'.  If we only find a partial
+	 * match, we place that in the buffer and try again. */
+
+	for (;;) {
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\"')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, ',')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\"');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '1')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\"');
+			rfcomm_append_buf(buf, count, in_count, ',');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '5')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\"');
+			rfcomm_append_buf(buf, count, in_count, ',');
+			rfcomm_append_buf(buf, count, in_count, '1');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\r')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\"');
+			rfcomm_append_buf(buf, count, in_count, ',');
+			rfcomm_append_buf(buf, count, in_count, '1');
+			rfcomm_append_buf(buf, count, in_count, '5');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\n')) != 1) {
+			if (res != -2) {
+				break;
+			}
+
+			rfcomm_append_buf(buf, count, in_count, '\"');
+			rfcomm_append_buf(buf, count, in_count, ',');
+			rfcomm_append_buf(buf, count, in_count, '1');
+			rfcomm_append_buf(buf, count, in_count, '5');
+			rfcomm_append_buf(buf, count, in_count, '\r');
+			rfcomm_append_buf(buf, count, in_count, c);
+			continue;
+		}
+
+		/* we have successfully parsed a '",15\r\n' string */
+		rfcomm_append_buf(buf, count, in_count, '\"');
+		rfcomm_append_buf(buf, count, in_count, ',');
+		rfcomm_append_buf(buf, count, in_count, '1');
+		rfcomm_append_buf(buf, count, in_count, '5');
+		rfcomm_append_buf(buf, count, in_count, '\r');
+		rfcomm_append_buf(buf, count, in_count, '\n');
+		return 1;
+	}
+
+	return res;
+}
+
+
+/*!
+ * \brief Read the remainder of a +CMGR message.
+ * \note the entire parsed string is '+CMGR: ...\r\n...\r\n...\r\n...\r\nOK\r\n'
+ */
+static int rfcomm_read_cmgr(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+
+	/* append the \r\n that was stripped by the calling function */
+	rfcomm_append_buf(buf, count, in_count, '\r');
+	rfcomm_append_buf(buf, count, in_count, '\n');
+
+	if ((res = rfcomm_read_until_ok(data_socket, buf, count, in_count)) != 1) {
+		ast_log(LOG_ERROR, "error reading +CMGR message on rfcomm socket\n");
+	}
+
+	return res;
+}
+
+/*!
+ * \brief Read the remainder of a +CUSD message.
+ * \note the entire parsed string is '+CUSD: 0,"...\r\n...\r\n...\r\n...",15\r\n'
+ */
+static int rfcomm_read_cusd(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+
+	/* append the \r\n that was stripped by the calling function */
+	rfcomm_append_buf(buf, count, in_count, '\r');
+	rfcomm_append_buf(buf, count, in_count, '\n');
+
+	if (*in_count >= 6 && !strncmp(*buf - 6, "\",15\r\n", 6)) {
+		return 1;
+	}
+
+	if ((res = rfcomm_read_until_cusd_end(data_socket, buf, count, in_count)) != 1) {
+		ast_log(LOG_ERROR, "error reading +CUSD message on rfcomm socket\n");
+	}
+
+	return res;
+}
+
+/*!
+ * \brief Read and AT result code.
+ * \note the entire parsed string is '\r\n<result code>\r\n'
+ */
+static int rfcomm_read_result(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\n')) < 1) {
+		goto e_return;
+	}
+
+	if ((res = rfcomm_read_and_append_char(data_socket, buf, count, in_count, &c, '>')) == 1) {
+		return rfcomm_read_sms_prompt(data_socket, buf, count, in_count);
+	} else if (res != -2) {
+		goto e_return;
+	}
+
+	rfcomm_append_buf(buf, count, in_count, c);
+	res = rfcomm_read_until_crlf(data_socket, buf, count, in_count);
+
+	if (res != 1)
+		return res;
+
+	/* check for CMGR, which contains an embedded \r\n pairs terminated by
+	 * an \r\nOK\r\n message */
+	if (*in_count >= 5 && !strncmp(*buf - *in_count, "+CMGR", 5)) {
+		return rfcomm_read_cmgr(data_socket, buf, count, in_count);
+	}
+
+	/* check for CUSD, which may contain embedded \r\n pairs terminated by
+	 * a '",15\r\n"' */
+	if (*in_count >= 5 && !strncmp(*buf - *in_count, "+CUSD", 5)) {
+		return rfcomm_read_cusd(data_socket, buf, count, in_count);
+	}
+
+	return 1;
+
+e_return:
+	ast_log(LOG_ERROR, "error parsing AT result on rfcomm socket.\n");
+	return res;
+}
+
+/*!
+ * \brief Read the remainder of an AT command.
+ * \note the entire parsed string is '<at command>\r'
+ */
+static int rfcomm_read_command(int data_socket, char **buf, size_t count, size_t *in_count)
+{
+	int res;
+	char c;
+
+	while ((res = read(data_socket, &c, 1)) == 1) {
+		rfcomm_read_debug(c);
+		/* stop when we get to '\r' */
+		if (c == '\r')
+			break;
+
+		rfcomm_append_buf(buf, count, in_count, c);
+	}
+	return res;
+}
+
+/*!
+ * \brief Read one Hayes AT message from an rfcomm socket.
+ * \param data_socket the rfcomm socket to read from
+ * \param buf the buffer to store the result in
+ * \param count the size of the buffer or the maximum number of characters to read
+ *
+ * Here we need to read complete Hayes AT messages.  The AT message formats we
+ * support are listed below.
+ *
+ * \verbatim
+ * \r\n<result code>\r\n
+ * <at command>\r
+ * \r\n> 
+ * \endverbatim
+ *
+ * These formats correspond to AT result codes, AT commands, and the AT SMS
+ * prompt respectively.  When messages are read the leading and trailing '\r'
+ * and '\n' characters are discarded.  If the given buffer is not large enough
+ * to hold the response, what does not fit in the buffer will be dropped.
+ *
+ * \note The rfcomm connection to the device is asynchronous, so there is no
+ * guarantee that responses will be returned in a single read() call. We handle
+ * this by blocking until we can read an entire response.
+ *
+ * \retval 0 end of file
+ * \retval -1 read error
+ * \retval -2 parse error
+ * \retval other the number of characters added to buf
+ */
+static ssize_t rfcomm_read(int data_socket, char *buf, size_t count)
+{
+	ssize_t res;
+	size_t in_count = 0;
+	char c;
+
+	if ((res = rfcomm_read_and_expect_char(data_socket, &c, '\r')) == 1) {
+		res = rfcomm_read_result(data_socket, &buf, count, &in_count);
+	} else if (res == -2) {
+		rfcomm_append_buf(&buf, count, &in_count, c);
+		res = rfcomm_read_command(data_socket, &buf, count, &in_count);
+	}
+
+	if (res < 1)
+		return res;
+	else
+		return in_count;
+}
+
+/*
+
+	audio and callbacks
+
+*/
+
+static int audio_write(int s, char *buf, int len)
+{
+
+	int r;
+
+	if (s == -1) {
+		ast_debug(3, "audio_write() not ready\n");
+		return 0;
+	}
+
+	ast_debug(3, "audio_write()\n");
+
+	r = write(s, buf, len);
+	if (r == -1) {
+		ast_debug(3, "audio write error %d\n", errno);
+		/*
+		if (errno==EBADF) ast_debug(1, "audio_write() error: EBADF\n");
+                if (errno==EINVAL) ast_debug(1, "audio_write() error: EINVAL\n");
+                if (errno==EFAULT) ast_debug(1, "audio_write() error: EFAULT\n");
+                if (errno==EPIPE) ast_debug(1, "audio_write() error: EPIPE\n");
+                if (errno==EAGAIN) ast_debug(1, "audio_write() error: EAGAIN\n");
+                if (errno==EINTR) ast_debug(1, "audio_write() error: EINTR\n");
+                if (errno==ENOSPC) ast_debug(1, "audio_write() error: ENOSPC\n");
+		*/
+                ast_debug(1, "audio_write() error [%d]\n", errno);
+		return 0;
+	}
+
+	return 1;
+
+}
+
+/*
+ * Hayes AT command helpers.
+ */
+
+/*!
+ * \brief Match the given buffer with the given prefix.
+ * \param buf the buffer to match
+ * \param prefix the prefix to match
+ */
+static int at_match_prefix(char *buf, char *prefix)
+{
+	return !strncmp(buf, prefix, strlen(prefix));
+}
+
+/*!
+ * \brief Read an AT message and clasify it.
+ * \param data_socket an rfcomm socket
+ * \param buf the buffer to store the result in
+ * \param count the size of the buffer or the maximum number of characters to read
+ * \return the type of message received, in addition buf will contain the
+ * message received and will be null terminated
+ * \see at_read()
+ */
+static at_message_t at_read_full(int data_socket, char *buf, size_t count)
+{
+	ssize_t s;
+	if ((s = rfcomm_read(data_socket, buf, count - 1)) < 1)
+		return s;
+	buf[s] = '\0';
+
+	if (!strcmp("OK", buf)) {
+		return AT_OK;
+	} else if (!strcmp("ERROR", buf)) {
+		return AT_ERROR;
+	} else if (!strcmp("COMMAND NOT SUPPORT", buf)) {
+		return AT_ERROR;
+	} else if (!strcmp("RING", buf)) {
+		return AT_RING;
+	} else if (!strcmp("AT+CKPD=200", buf)) {
+		return AT_CKPD;
+	} else if (!strcmp("> ", buf)) {
+		return AT_SMS_PROMPT;
+	} else if (at_match_prefix(buf, "+CMTI:")) {
+		return AT_CMTI;
+	} else if (at_match_prefix(buf, "+CLIP:")) {
+		return AT_CLIP;
+	} else if (at_match_prefix(buf, "+CMGR:")) {
+		return AT_CMGR;
+	} else if (at_match_prefix(buf, "+VGM:")) {
+		return AT_VGM;
+	} else if (at_match_prefix(buf, "+VGS:")) {
+		return AT_VGS;
+	} else if (at_match_prefix(buf, "+CMS ERROR:")) {
+		return AT_CMS_ERROR;
+	} else if (at_match_prefix(buf, "AT+VGM=")) {
+		return AT_VGM;
+	} else if (at_match_prefix(buf, "AT+VGS=")) {
+		return AT_VGS;
+	} else if (at_match_prefix(buf, "+CUSD:")) {
+		return AT_CUSD;
+	} else if (at_match_prefix(buf, "BUSY")) {
+		return AT_BUSY;
+	} else if (at_match_prefix(buf, "NO DIALTONE")) {
+		return AT_NO_DIALTONE;
+	} else if (at_match_prefix(buf, "NO CARRIER")) {
+		return AT_NO_CARRIER;
+	} else if (at_match_prefix(buf, "^CONN:")) {
+		return AT_CONN;
+	} else if (at_match_prefix(buf, "^CEND:")) {
+		return AT_CEND;
+	} else if (at_match_prefix(buf, "^CONF:")) {
+		return AT_CONF;
+	} else if (at_match_prefix(buf, "^ORIG:")) {
+		return AT_ORIG;
+	} else if (at_match_prefix(buf, "^SMMEMFULL:")) {
+		return AT_SMMEMFULL;
+	} else if (at_match_prefix(buf, "^RSSI:")) {
+		return AT_RSSI;
+	} else if (at_match_prefix(buf, "^BOOT:")) {
+		return AT_BOOT;
+	} else if (at_match_prefix(buf, "+CSSN:")) {
+		return AT_CSSN;
+	} else if (at_match_prefix(buf, "+CSSI:")) {
+		return AT_CSSI;
+	} else if (at_match_prefix(buf, "+CSSU:")) {
+		return AT_CSSU;
+	} else if (at_match_prefix(buf, "+CPIN:")) {
+		return AT_CPIN;
+	} else if (at_match_prefix(buf, "^DDSETEX:")) {
+		return AT_DDSETEX;
+	} else if (at_match_prefix(buf, "^CVOICE:")) {
+		return AT_CVOICE;
+	} else if (at_match_prefix(buf, "+COPS:")) {
+		return AT_COPS;
+	} else if (at_match_prefix(buf, "+CREG:")) {
+		return AT_CREG;
+	} else if (at_match_prefix(buf, "^MODE:")) {
+		return AT_MODE;
+	} else if (at_match_prefix(buf, "+CPMS:")) {
+		return AT_CPMS;
+	} else if (at_match_prefix(buf, "^SIMST:")) {
+		return AT_SIMST;
+	} else if (at_match_prefix(buf, "^SRVST:")) {
+		return AT_SRVST;
+	} else {
+		return AT_UNKNOWN;
+	}
+}
+
+/*!
+ * \brief Get the string representation of the given AT message.
+ * \param msg the message to process
+ * \return a string describing the given message
+ */
+static inline const char *at_msg2str(at_message_t msg)
+{
+	switch (msg) {
+	/* errors */
+	case AT_PARSE_ERROR:
+		return "PARSE ERROR";
+	case AT_READ_ERROR:
+		return "READ ERROR";
+	default:
+	case AT_UNKNOWN:
+		return "UNKNOWN";
+	/* at responses */
+	case AT_OK:
+		return "OK";
+	case AT_ERROR:
+		return "ERROR";
+	case AT_RING:
+		return "RING";
+	case AT_CLIP:
+		return "AT+CLIP";
+	case AT_CMTI:
+		return "AT+CMTI";
+	case AT_CMGR:
+		return "AT+CMGR";
+	case AT_CMGD:
+		return "AT+CMGD";
+	case AT_SMS_PROMPT:
+		return "SMS PROMPT";
+	case AT_CMS_ERROR:
+		return "+CMS ERROR";
+	case AT_BUSY:
+		return "BUSY";
+	case AT_NO_DIALTONE:
+		return "NO DIALTONE";
+	case AT_NO_CARRIER:
+		return "NO CARRIER";
+	/* at commands */
+	case AT_A:
+		return "ATA";
+	case AT_Z:
+		return "ATZ";
+	case AT_D:
+		return "ATD";
+	case AT_E:
+		return "ATE";
+	case AT_DDSETEX:
+		return "AT^DDSETEX";
+	case AT_CVOICE:
+		return "AT^CVOICE";
+	case AT_CONN:
+		return "^CONN:";
+	case AT_CEND:
+		return "^CEND:";
+	case AT_CONF:
+		return "^CONF:";
+	case AT_ORIG:
+		return "^ORIG:";
+	case AT_SMMEMFULL:
+		return "^SMMEMFULL:";
+	case AT_RSSI:
+		return "^RSSI:";
+	case AT_BOOT:
+		return "^BOOT:";
+	case AT_CSSN:
+		return "AT+CSSN";
+	case AT_CSSI:
+		return "+CSSI:";
+	case AT_CSSU:
+		return "+CSSU:";
+	case AT_CHUP:
+		return "AT+CHUP";
+	case AT_CKPD:
+		return "AT+CKPD";
+	case AT_CMGS:
+		return "AT+CMGS";
+	case AT_VGM:
+		return "AT+VGM";
+	case AT_VGS:
+		return "AT+VGS";
+	case AT_VTS:
+		return "AT+VTS";
+	case AT_DTMF:
+		return "AT^DTMF";
+	case AT_CMGF:
+		return "AT+CMGF";
+	case AT_CNMI:
+		return "AT+CNMI";
+	case AT_CUSD:
+		return "AT+CUSD";
+	case AT_CPIN:
+		return "AT+CPIN";
+	case AT_COPS_INIT:
+		return "AT+COPS";
+	case AT_COPS:
+		return "AT+COPS";
+	case AT_CREG_INIT:
+		return "AT+CREG";
+	case AT_CREG:
+		return "AT+CREG";
+	case AT_MODE:
+		return "AT^MODE";
+	case AT_I:
+		return "ATI";
+	case AT_CGMI:
+		return "AT+CGMI";
+	case AT_CGMM:
+		return "AT+CGMM";
+	case AT_CGMR:
+		return "AT+CGMR";
+	case AT_CGSN:
+		return "AT+CGSN";
+	case AT_CLVL:
+		return "AT+CLVL";
+	case AT_CPMS:
+		return "AT+CPMS";
+	case AT_CSCS:
+		return "AT+CSCS";
+	}
+}
+
+
+/*
+ * datacard helpers
+ */
+
+/*!
+ * \brief Parse a CLIP event.
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * @note buf will be modified when the CID string is parsed
+ * \return NULL on error (parse error) or a pointer to the caller id
+ * inforamtion in buf
+ * success
+ */
+static char *dc_parse_clip(struct dc_pvt *pvt, char *buf)
+{
+	int i, state;
+	char *clip;
+	size_t s;
+
+	/* parse clip info in the following format:
+	 * +CLIP: "123456789",128,...
+	 */
+	state = 0;
+	s = strlen(buf);
+	for (i = 0; i < s && state != 3; i++) {
+		switch (state) {
+		case 0: /* search for start of the number (") */
+			if (buf[i] == '"') {
+				state++;
+			}
+			break;
+		case 1: /* mark the number */
+			clip = &buf[i];
+			state++;
+			/* fall through */
+		case 2: /* search for the end of the number (") */
+			if (buf[i] == '"') {
+				buf[i] = '\0';
+				state++;
+			}
+			break;
+		}
+	}
+
+	if (state != 3) {
+		return NULL;
+	}
+
+	return clip;
+}
+
+/*!
+ * \brief Parse a COPS response.
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * @note buf will be modified when the COPS message is parsed
+ * \return NULL on error (parse error) or a pointer to the provider name
+ */
+static char *dc_parse_cops(struct dc_pvt *pvt, char *buf)
+{
+	int i, state;
+	char *provider;
+	size_t s;
+
+	/* parse COPS response in the following format:
+	 * +COPS: <mode>[,<format>,<oper>]
+	 */
+	provider = NULL;
+	state = 0;
+	s = strlen(buf);
+	for (i = 0; i < s && state != 3; i++) {
+		switch (state) {
+		case 0: /* search for start of the provider name (") */
+			if (buf[i] == '"') {
+				state++;
+			}
+			break;
+		case 1: /* mark the provider name */
+			provider = &buf[i];
+			state++;
+			/* fall through */
+		case 2: /* search for the end of the provider name (") */
+			if (buf[i] == '"') {
+				buf[i] = '\0';
+				state++;
+			}
+			break;
+		}
+	}
+
+	if (state != 3) {
+		return NULL;
+	}
+
+	return provider;
+}
+
+/*!
+ * \brief Parse a CMTI notification.
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * @note buf will be modified when the CMTI message is parsed
+ * \return -1 on error (parse error) or the index of the new sms message
+ */
+static int dc_parse_cmti(struct dc_pvt *pvt, char *buf)
+{
+	int index = -1;
+
+	/* parse cmti info in the following format:
+	 * +CMTI: <mem>,<index> 
+	 */
+	if (!sscanf(buf, "+CMTI: %*[^,],%d", &index)) {
+		ast_debug(2, "[%s] error parsing CMTI event '%s'\n", pvt->id, buf);
+		return -1;
+	}
+
+	return index;
+}
+
+/*!
+ * \brief Parse a CMGR message.
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \param from_number a pointer to a char pointer which will store the from
+ * number
+ * \param text a pointer to a char pointer which will store the message text
+ * @note buf will be modified when the CMGR message is parsed
+ * \retval -1 parse error
+ * \retval 0 success
+ */
+static int dc_parse_cmgr(struct dc_pvt *pvt, char *buf, char **from_number, char **text)
+{
+	int i, state;
+	size_t s;
+
+	/* parse cmgr info in the following format:
+	 * +CMGR: <msg status>,"+123456789",...\r\n
+	 * <message text>
+	 */
+	state = 0;
+	s = strlen(buf);
+	for (i = 0; i < s && s != 6; i++) {
+		switch (state) {
+		case 0: /* search for start of the number section (,) */
+			if (buf[i] == ',') {
+				state++;
+			}
+			break;
+		case 1: /* find the opening quote (") */
+			if (buf[i] == '"') {
+				state++;
+			}
+		case 2: /* mark the start of the number */
+			if (from_number) {
+				*from_number = &buf[i];
+				state++;
+			}
+			/* fall through */
+		case 3: /* search for the end of the number (") */
+			if (buf[i] == '"') {
+				buf[i] = '\0';
+				state++;
+			}
+			break;
+		case 4: /* search for the start of the message text (\n) */
+			if (buf[i] == '\n') {
+				state++;
+			}
+			break;
+		case 5: /* mark the start of the message text */
+			if (text) {
+				*text = &buf[i];
+				state++;
+			}
+			break;
+		}
+	}
+
+	if (state != 6) {
+		return -1;
+	}
+
+	return 0;
+}
+
+ /*!
+ * \brief Parse a CUSD answer.
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * @note buf will be modified when the CUSD string is parsed
+ * \return NULL on error (parse error) or a pointer to the cusd message
+ * inforamtion in buf
+ * success
+ */
+static char *dc_parse_cusd(struct dc_pvt *pvt, char *buf)
+{
+	int i, state, message_start, message_end;
+	char *cusd;
+	size_t s;
+
+	/* parse cusd message in the following format:
+	 * +CUSD: 0,"100,00 EURO, valid till 01.01.2010, you are using tariff "Mega Tariff". More informations *111#.",15
+	 */
+	state = 0;
+	message_start = 0;
+	message_end = 0;
+	s = strlen(buf);
+
+	/* Find the start of the message (") */
+	for (i = 0; i < s; i++) {
+		if (buf[i] == '"') {
+			message_start = i + 1;
+			break;
+		}
+	}
+
+	if (message_start == 0 || message_start >= s) {
+		return NULL;
+	}
+
+	/* Find the end of the message (") */
+	for (i = s; i > 0; i--) {
+		if (buf[i] == '"') {
+			message_end = i;
+			break;
+		}
+	}
+
+	if (message_end == 0) {
+		return NULL;
+	}
+
+	if (message_start >= message_end) {
+		return NULL;
+	}
+
+	cusd = &buf[message_start];
+	buf[message_end] = '\0';
+
+	return cusd;
+}
+
+// FIXME: Finish parsing
+/*!
+ * \brief Parse a CPIN notification.
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \return 2 if PUK required
+ * \return 1 if PIN required
+ * \return 0 if no PIN required
+ * \return -1 on error (parse error) or card lock
+ */
+static int dc_parse_cpin(struct dc_pvt *pvt, char *buf)
+{
+	if (strstr(buf, "READY")) return 0;
+	if (strstr(buf, "SIM PIN"))
+	{
+		ast_log(LOG_ERROR, "Datacard %s needs PIN code!\n", pvt->id);
+		return 1;
+	}
+	if (strstr(buf, "SIM PUK")) {
+		ast_log(LOG_ERROR, "Datacard %s needs PUK code!\n", pvt->id);
+		return 2;
+	}
+
+	ast_log(LOG_ERROR, "Error parsing +CPIN message on Datacard: %s %s\n", pvt->id, buf);
+	return -1;
+}
+
+/*!
+ * \brief Parse a ^RSSI notification.
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \return -1 on error (parse error) or the rssi value
+ */
+static int dc_parse_rssi(struct dc_pvt *pvt, char *buf)
+{
+	int rssi = -1;
+
+	/* parse RSSI info in the following format:
+	 * ^RSSI:<rssi>
+	 */
+	if (!sscanf(buf, "^RSSI:%d", &rssi)) {
+		ast_debug(2, "[%s] error parsing RSSI event '%s'\n", pvt->id, buf);
+		return -1;
+	}
+
+	return rssi;
+}
+
+/*!
+ * \brief Parse a ^MODE notification (link mode).
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \return -1 on error (parse error) or the the link mode value
+ */
+static int dc_parse_linkmode(struct dc_pvt *pvt, char *buf)
+{
+	int mode = -1;
+	int submode = -1;
+
+	/* parse RSSI info in the following format:
+	 * ^MODE:<mode>,<submode>
+	 */
+	if (!sscanf(buf, "^MODE:%d,%d", &mode, &submode)) {
+		ast_debug(2, "[%s] error parsing MODE event '%s'\n", pvt->id, buf);
+		return -1;
+	}
+
+	return mode;
+}
+
+/*!
+ * \brief Parse a ^MODE notification (link sub mode).
+ * \param pvt an dc_pvt struct
+ * \param buf the buffer to parse (null terminated)
+ * \return -1 on error (parse error) or the link sub mode value
+ */
+static int dc_parse_linksubmode(struct dc_pvt *pvt, char *buf)
+{
+	int mode = -1;
+	int submode = -1;
+
+	/* parse RSSI info in the following format:
+	 * ^MODE:<mode>,<submode>
+	 */
+	if (!sscanf(buf, "^MODE:%d,%d", &mode, &submode)) {
+		ast_debug(2, "[%s] error parsing MODE event '%s'\n", pvt->id, buf);
+		return -1;
+	}
+
+	return submode;
+}
+
+/*!
+ * \brief Send the ATZ command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_atz(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "ATZ\r");
+}
+
+/*!
+ * \brief Send the ATE0 command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_ate0(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "ATE0\r");
+}
+
+/*!
+ *  * \brief Manage Supplementary Service Notification.
+ *  * \param pvt an dc_pvt struct
+ *  * \param cssi the value to send (0 = disabled, 1 = enabled)
+ *  * \param cssu the value to send (0 = disabled, 1 = enabled)
+ *  */
+static int dc_send_cssn(struct dc_pvt *pvt, int cssi, int cssu)
+{
+	char cmd[32];
+	snprintf(cmd, sizeof(cmd), "AT+CSSN=%d,%d\r", cssi, cssu);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send AT+CPIN=? to ask the datacard if a pin code is required
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cpin_test(struct dc_pvt *pvt)
+{
+       char cmd[32];
+       snprintf(cmd, sizeof(cmd), "AT+CPIN?\r");
+       return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Enable or disable calling line identification.
+ * \param pvt an dc_pvt struct
+ * \param status enable or disable calling line identification (should be 1 or
+ * 0)
+ */
+static int dc_send_clip(struct dc_pvt *pvt, int status)
+{
+	char cmd[32];
+	snprintf(cmd, sizeof(cmd), "AT+CLIP=%d\r", status ? 1 : 0);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Enable transmitting of audio to the debug port (tty)
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_ddsetex(struct dc_pvt *pvt)
+{
+	char cmd[64];
+	snprintf(cmd, sizeof(cmd), "AT^DDSETEX=2\r");
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Check device for audio capabilities
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cvoice_test(struct dc_pvt *pvt)
+{
+	char cmd[64];
+	snprintf(cmd, sizeof(cmd), "AT^CVOICE?\r");
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Set storage location for incoming SMS
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cpms(struct dc_pvt *pvt)
+{
+	char cmd[64];
+	snprintf(cmd, sizeof(cmd), "AT+CPMS=\"SM\",\"SM\",\"SM\"\r");
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send a DTMF command.
+ * \param pvt an dc_pvt struct
+ * \param digit the dtmf digit to send
+ * \return the result of rfcomm_write() or -1 on an invalid digit being sent
+ */
+static int dc_send_dtmf(struct dc_pvt *pvt, char digit)
+{
+	char cmd[13];
+
+	switch(digit) {
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+	case '*':
+	case '#':
+		//snprintf(cmd, sizeof(cmd), "AT+VTS=%c\r", digit);
+		snprintf(cmd, sizeof(cmd), "AT^DTMF=1,%c\r", digit);
+		return rfcomm_write(pvt->data_socket, cmd);
+	default:
+		return -1;
+	}
+}
+
+/*!
+ * \brief Set the SMS mode.
+ * \param pvt an dc_pvt struct
+ * \param mode the sms mode (0 = PDU, 1 = Text)
+ */
+static int dc_send_cmgf(struct dc_pvt *pvt, int mode)
+{
+	char cmd[32];
+	snprintf(cmd, sizeof(cmd), "AT+CMGF=%d\r", mode);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Setup SMS new message indication.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cnmi(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+CNMI=2,1,0,0,0\r");
+}
+
+/*!
+ * \brief Read an SMS message.
+ * \param pvt an dc_pvt struct
+ * \param index the location of the requested message
+ */
+static int dc_send_cmgr(struct dc_pvt *pvt, int index)
+{
+	char cmd[32];
+	snprintf(cmd, sizeof(cmd), "AT+CMGR=%d\r", index);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Delete an SMS message.
+ * \param pvt an dc_pvt struct
+ * \param index the location of the requested message
+ */
+static int dc_send_cmgd(struct dc_pvt *pvt, int index)
+{
+	char cmd[32];
+	snprintf(cmd, sizeof(cmd), "AT+CMGD=%d\r", index);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Start sending an SMS message.
+ * \param pvt an dc_pvt struct
+ * \param number the destination of the message
+ */
+static int dc_send_cmgs(struct dc_pvt *pvt, const char *number)
+{
+	char cmd[64];
+	snprintf(cmd, sizeof(cmd), "AT+CMGS=\"%s\"\r", number);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send the text of an SMS message.
+ * \param pvt an dc_pvt struct
+ * \param message the text of the message
+ */
+static int dc_send_sms_text(struct dc_pvt *pvt, char *message)
+{
+	int res;
+	char *old_message = message;
+	char ucs2_message[4096];
+	char cmd[sizeof(ucs2_message) + 162];
+
+	if (pvt->use_ucs2_encoding) {
+		res = utf8_to_hexstr_ucs2(message,strlen(message),ucs2_message,sizeof(ucs2_message));
+		if (res>0) {
+			message = ucs2_message;
+		} else {
+			ast_log(LOG_ERROR, "[%s] error converting SMS to UCS-2): %s\n", pvt->id, message);
+		}
+	}
+
+	snprintf(cmd, sizeof(cmd), "%.160s\x1a", message);
+	message = old_message;
+
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send AT+CHUP.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_chup(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+CHUP\r");
+}
+
+/*!
+ * \brief Send ATD.
+ * \param pvt an dc_pvt struct
+ * \param number the number to send
+ */
+static int dc_send_atd(struct dc_pvt *pvt, const char *number)
+{
+	char cmd[64];
+	snprintf(cmd, sizeof(cmd), "ATD%s;\r", number);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send ATA.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_ata(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "ATA\r");
+}
+
+/*!
+ * \brief Send AT+CUSD.
+ * \param pvt an dc_pvt struct
+ * \param code the CUSD code to send
+ */
+static int dc_send_cusd(struct dc_pvt *pvt, char *code)
+{
+	int res;
+	char *old_code = code;
+	char ucs2_code[4096];
+	char cmd[sizeof(ucs2_code)+32];
+
+	if (pvt->use_ucs2_encoding) {
+		res = utf8_to_hexstr_ucs2(code,strlen(code),ucs2_code,sizeof(ucs2_code));
+		if (res>0) {
+			code = ucs2_code;
+		} else {
+			ast_log(LOG_ERROR, "[%s] error converting CUSD code to UCS-2): %s\n", pvt->id, code);
+		}
+	}
+
+	snprintf(cmd, sizeof(cmd), "AT+CUSD=1,\"%s\",15\r", code);
+	code = old_code;
+
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send AT+CLVL.
+ * \param pvt an dc_pvt struct
+ * \param volume level to send
+ */
+static int dc_send_clvl(struct dc_pvt *pvt, int level)
+{
+	char cmd[16];
+	snprintf(cmd, sizeof(cmd), "AT+CLVL=%d\r", level);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send AT+CSCS.
+ * \param pvt an dc_pvt struct
+ * \param volume level to send
+ */
+static int dc_send_cscs(struct dc_pvt *pvt, const char *encoding)
+{
+	char cmd[64];
+	snprintf(cmd, sizeof(cmd), "AT+CSCS=\"%s\"\r", encoding);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send the AT+COPS= command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cops_init(struct dc_pvt *pvt,int mode, int format)
+{
+	char cmd[16];
+	snprintf(cmd, sizeof(cmd), "AT+COPS=%d,%d\r", mode, format);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send the AT+COPS? command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cops(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+COPS?\r");
+}
+
+/*!
+ * \brief Send the AT+CREG=n command.
+ * \param pvt an dc_pvt struct
+ * \param level verbose level of CREG
+ */
+static int dc_send_creg_init(struct dc_pvt *pvt, int level)
+{
+	char cmd[16];
+	snprintf(cmd, sizeof(cmd), "AT+CREG=%d\r", level);
+	return rfcomm_write(pvt->data_socket, cmd);
+}
+
+/*!
+ * \brief Send the AT+CREG? command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_creg(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+CREG?\r");
+}
+
+/*!
+ * \brief Send the AT+CGMI command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cgmi(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+CGMI\r");
+}
+
+/*!
+ * \brief Send the AT+CGMM command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cgmm(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+CGMM\r");
+}
+
+/*!
+ * \brief Send the AT+CGMR command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cgmr(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+CGMR\r");
+}
+
+/*!
+ * \brief Send the AT+CGSN command.
+ * \param pvt an dc_pvt struct
+ */
+static int dc_send_cgsn(struct dc_pvt *pvt)
+{
+	return rfcomm_write(pvt->data_socket, "AT+CGSN\r");
+}
+
+
+/*
+ * message queue functions
+ */
+
+/*!
+ * \brief Add an item to the back of the queue.
+ * \param pvt a dc_pvt structure
+ * \param expect the msg we expect to recieve
+ * \param response_to the message that was sent to generate the expected
+ * response
+ */
+static int msg_queue_push(struct dc_pvt *pvt, at_message_t expect, at_message_t response_to)
+{
+	struct msg_queue_entry *msg;
+	if (!(msg = ast_calloc(1, sizeof(*msg)))) {
+		return -1;
+	}
+	msg->expected = expect;
+	msg->response_to = response_to;
+
+	AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
+	return 0;
+}
+
+/*!
+ * \brief Add an item to the back of the queue with data.
+ * \param pvt a dc_pvt structure
+ * \param expect the msg we expect to recieve
+ * \param response_to the message that was sent to generate the expected
+ * response
+ * \param data data associated with this message, it will be freed when the
+ * message is freed
+ */
+static int msg_queue_push_data(struct dc_pvt *pvt, at_message_t expect, at_message_t response_to, void *data)
+{
+	struct msg_queue_entry *msg;
+	if (!(msg = ast_calloc(1, sizeof(*msg)))) {
+		return -1;
+	}
+	msg->expected = expect;
+	msg->response_to = response_to;
+	msg->data = data;
+
+	AST_LIST_INSERT_TAIL(&pvt->msg_queue, msg, entry);
+	return 0;
+}
+
+/*!
+ * \brief Remove an item from the front of the queue.
+ * \param pvt a dc_pvt structure
+ * \return a pointer to the removed item
+ */
+static struct msg_queue_entry *msg_queue_pop(struct dc_pvt *pvt)
+{
+	return AST_LIST_REMOVE_HEAD(&pvt->msg_queue, entry);
+}
+
+/*!
+ * \brief Remove an item from the front of the queue, and free it.
+ * \param pvt a dc_pvt structure
+ */
+static void msg_queue_free_and_pop(struct dc_pvt *pvt)
+{
+	struct msg_queue_entry *msg;
+	if ((msg = msg_queue_pop(pvt))) {
+		if (msg->data)
+			ast_free(msg->data);
+		ast_free(msg);
+	}
+}
+
+/*!
+ * \brief Remove all itmes from the queue and free them.
+ * \param pvt a dc_pvt structure
+ */
+static void msg_queue_flush(struct dc_pvt *pvt)
+{
+	struct msg_queue_entry *msg;
+	while ((msg = msg_queue_head(pvt)))
+		msg_queue_free_and_pop(pvt);
+}
+
+/*!
+ * \brief Get the head of a queue.
+ * \param pvt a dc_pvt structure
+ * \return a pointer to the head of the given msg queue
+ */
+static struct msg_queue_entry *msg_queue_head(struct dc_pvt *pvt)
+{
+	return AST_LIST_FIRST(&pvt->msg_queue);
+}
+
+
+
+/*
+
+	Thread routines
+
+*/
+
+/*!
+ * \brief Handle OK AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_ok(struct dc_pvt *pvt, char *buf)
+{
+		struct msg_queue_entry *entry;
+		if ((entry = msg_queue_head(pvt)) && entry->expected == AT_OK) {
+			switch (entry->response_to) {
+		
+		/* initilization stuff */
+		case AT_Z:
+			if (dc_send_ate0(pvt) || msg_queue_push(pvt, AT_OK, AT_E)) {
+				ast_debug(1, "[%s] Error disableing echo.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_E:
+			if (dc_send_cgmi(pvt) || msg_queue_push(pvt, AT_OK, AT_CGMI)) {
+				ast_debug(1, "[%s] Error asking datacard for vendor info.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CGMI:
+			if (dc_send_cgmm(pvt) || msg_queue_push(pvt, AT_OK, AT_CGMM)) {
+				ast_debug(1, "[%s] Error asking datacard for manufacturer.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CGMM:
+			if (dc_send_cgmr(pvt) || msg_queue_push(pvt, AT_OK, AT_CGMR)) {
+				ast_debug(1, "[%s] Error asking datacard for model.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CGMR:
+			if (dc_send_cgsn(pvt) || msg_queue_push(pvt, AT_OK, AT_CGSN)) {
+				ast_debug(1, "[%s] Error asking datacard for firmware.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CGSN:
+			if (dc_send_cpin_test(pvt) || msg_queue_push(pvt, AT_OK, AT_CPIN)) {
+				ast_debug(1, "[%s] Error asking datacard for IMEI number.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CPIN:
+			if (dc_send_cops_init(pvt,0,0) || msg_queue_push(pvt, AT_OK, AT_COPS_INIT)) {
+				ast_debug(1, "[%s] Error setting operator select parameters.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_COPS_INIT:
+			ast_debug(1, "[%s] Operator select parameters set.\n", pvt->id);
+			if (dc_send_creg_init(pvt,2) || msg_queue_push(pvt, AT_OK, AT_CREG_INIT)) {
+				ast_debug(1, "[%s] Error enabeling registration info.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CREG_INIT:
+			ast_debug(1, "[%s] registration info enabled\n", pvt->id);
+			if (dc_send_creg(pvt) || msg_queue_push(pvt, AT_OK, AT_CREG)) {
+				ast_debug(1, "[%s] Error sending registration query.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CREG:
+			ast_debug(1, "[%s] registration query sent\n", pvt->id);
+			if (dc_send_cvoice_test(pvt) || msg_queue_push(pvt, AT_OK, AT_CVOICE)) {
+				ast_debug(1, "[%s] Error checking voice capabilities.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CVOICE:
+			pvt->has_voice = 1;
+			ast_debug(1, "[%s] Datacard has voice support.\n", pvt->id);
+			if (dc_send_clvl(pvt,1) || msg_queue_push(pvt, AT_OK, AT_CLVL)) {
+				ast_debug(1, "[%s] Error syncronizing audio level (part1/2)\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CLVL:
+			if (pvt->volume_synchronized == 0) {
+				pvt->volume_synchronized = 1;
+				if (dc_send_clvl(pvt,5) || msg_queue_push(pvt, AT_OK, AT_CLVL)) {
+					ast_debug(1, "[%s] Error syncronizing audio level (part2/2).\n", pvt->id);
+					goto e_return;
+				}
+			}
+			else {
+				if (dc_send_clip(pvt, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
+					ast_debug(1, "[%s] Error enabling calling line notification.\n", pvt->id);
+					goto e_return;
+				}
+			}
+			break;
+		case AT_CLIP:
+			ast_debug(1, "[%s] caling line indication enabled\n", pvt->id);
+			if (dc_send_cssn(pvt,1,1) || msg_queue_push(pvt, AT_OK, AT_CSSN)) {
+				ast_debug(1, "[%s] Error activating Supplementary Service Notification.\n", pvt->id);
+				goto e_return;
+			}
+
+			pvt->timeout = -1;
+			pvt->initialized = 1;
+			ast_verb(3, "Datacard %s initialized and ready.\n", pvt->id);
+
+			break;
+		case AT_CSSN:
+			ast_debug(1, "[%s] Supplementary Service Notification enabled successful\n", pvt->id);
+
+			/* set the SMS operating mode to text mode */
+			if (dc_send_cmgf(pvt, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
+				ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CMGF:
+			ast_debug(1, "[%s] sms text mode enabled\n", pvt->id);
+			/* set text encoding to UCS-2 */
+			if (dc_send_cscs(pvt,"UCS2") || msg_queue_push(pvt, AT_OK, AT_CSCS)) {
+				ast_debug(1, "[%s] error setting CSCS (text encoding)\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CSCS:
+			ast_debug(1, "[%s] UCS-2 text encoding enabled\n", pvt->id);
+			pvt->use_ucs2_encoding = 1;
+			/* set SMS storage location */
+			if (dc_send_cpms(pvt) || msg_queue_push(pvt, AT_OK, AT_CPMS)) {
+				ast_debug(1, "[%s] error setting CPMS\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CPMS:
+			/* turn on SMS new message indication */
+			if (dc_send_cnmi(pvt) || msg_queue_push(pvt, AT_OK, AT_CNMI)) {
+				ast_debug(1, "[%s] error setting CNMI\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CNMI:
+			ast_debug(1, "[%s] sms new message indication enabled\n", pvt->id);
+			ast_debug(1, "[%s] Datacard has sms support.\n", pvt->id);
+			pvt->has_sms = 1;
+			break;
+
+		/* end initilization stuff */
+
+		case AT_A:
+			ast_debug(1, "[%s] answer sent successfully\n", pvt->id);
+			pvt->needchup = 1;
+
+			if (dc_send_ddsetex(pvt) || msg_queue_push(pvt, AT_OK, AT_DDSETEX)) {
+				ast_debug(1, "[%s] error sending AT^DDSETEX\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_D:
+			ast_debug(1, "[%s] dial sent successfully\n", pvt->id);
+			dc_queue_control(pvt, AST_CONTROL_PROGRESS);
+
+			if (dc_send_ddsetex(pvt) || msg_queue_push(pvt, AT_OK, AT_DDSETEX)) {
+				ast_debug(1, "[%s] error sending AT^DDSETEX\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_DDSETEX:
+			ast_debug(1, "[%s] AT^DDSETEX sent successfully\n", pvt->id);
+			break;
+		case AT_CHUP:
+			ast_debug(1, "[%s] successful hangup\n", pvt->id);
+			break;
+		case AT_CMGS:
+			ast_debug(1, "[%s] successfully sent sms message\n", pvt->id);
+			pvt->outgoing_sms = 0;
+			break;
+		case AT_VTS:
+			ast_debug(1, "[%s] digit sent successfully\n", pvt->id);
+			break;
+		case AT_DTMF:
+			ast_debug(1, "[%s] digit sent successfully\n", pvt->id);
+			break;
+		case AT_CUSD:
+			ast_debug(1, "[%s] CUSD code sent successfully\n", pvt->id);
+			break;
+		case AT_COPS:
+			ast_debug(1, "[%s] provider query successfully\n", pvt->id);
+			break;
+		case AT_CMGD:
+			ast_debug(1, "[%s] sms message deleted successfully\n", pvt->id);
+			break;
+		case AT_UNKNOWN:
+		default:
+			ast_debug(1, "[%s] recieved OK for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
+			break;
+		}
+		msg_queue_free_and_pop(pvt);
+	} else if (entry) {
+		ast_debug(1, "[%s] recieved AT message 'OK' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+	} else {
+		ast_debug(1, "[%s] recieved unexpected AT message 'OK'\n", pvt->id);
+	}
+	return 0;
+
+e_return:
+	msg_queue_free_and_pop(pvt);
+	return -1;
+}
+
+/*!
+ * \brief Handle ERROR AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_error(struct dc_pvt *pvt, char *buf)
+{
+	struct msg_queue_entry *entry;
+	if ((entry = msg_queue_head(pvt))
+			&& (entry->expected == AT_OK
+			|| entry->expected == AT_ERROR
+			|| entry->expected == AT_CMS_ERROR
+			|| entry->expected == AT_CMGR
+			|| entry->expected == AT_SMS_PROMPT)) {
+		switch (entry->response_to) {
+
+		/* initilization stuff */
+		case AT_Z:
+			ast_debug(1, "[%s] ATZ failed\n", pvt->id);
+			goto e_return;
+		case AT_E:
+			ast_debug(1, "[%s] ATE0 failed\n", pvt->id);
+			goto e_return;
+		case AT_CGMI:
+			ast_debug(1, "[%s] getting manufacturer info failed.\n", pvt->id);
+			goto e_return;
+		case AT_CGMM:
+			ast_debug(1, "[%s] getting model info failed.\n", pvt->id);
+			goto e_return;
+		case AT_CGMR:
+			ast_debug(1, "[%s] getting firmware info failed.\n", pvt->id);
+			goto e_return;
+		case AT_CGSN:
+			ast_debug(1, "[%s] getting IMEI number failed.\n", pvt->id);
+			goto e_return;
+		case AT_CPIN:
+			ast_debug(1, "[%s] error checking PIN state\n", pvt->id);
+			goto e_return;
+			break;
+		case AT_COPS_INIT:
+			ast_debug(1, "[%s] Error setting operator select parameters.\n", pvt->id);
+			/* this is not a fatal error, let's continue with initilization */
+			if (dc_send_creg_init(pvt,2) || msg_queue_push(pvt, AT_OK, AT_CREG_INIT)) {
+				ast_debug(1, "[%s] Error enabeling registration info.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CREG_INIT:
+			ast_debug(1, "[%s] error enableling registration info\n", pvt->id);
+			/* this is not a fatal error, let's continue with initilization */
+			if (dc_send_creg(pvt) || msg_queue_push(pvt, AT_OK, AT_CREG)) {
+				ast_debug(1, "[%s] Error sending registration info query.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CREG:
+			ast_debug(1, "[%s] error getting registration info\n", pvt->id);
+			/* this is not a fatal error, let's continue with initilization */
+			if (dc_send_cvoice_test(pvt) || msg_queue_push(pvt, AT_OK, AT_CVOICE)) {
+				ast_debug(1, "[%s] Error checking voice capabilities.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CVOICE:
+			ast_debug(1, "[%s] Datacard has NO voice support.\n", pvt->id);
+			/* this is not a fatal error, let's continue with initilization */
+			pvt->has_voice = 0;
+			if (dc_send_clvl(pvt,1) || msg_queue_push(pvt, AT_OK, AT_CLVL)) {
+				ast_debug(1, "[%s] error syncronizing audio level (part1/2)\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CLVL:
+			ast_debug(1, "[%s] error syncronizing audio level\n", pvt->id);
+			/* this is not a fatal error, let's continue with initilization */
+			pvt->volume_synchronized = 0;
+			if (dc_send_clip(pvt, 1) || msg_queue_push(pvt, AT_OK, AT_CLIP)) {
+				ast_debug(1, "[%s] Error enabling calling line notification.\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CLIP:
+			ast_debug(1, "[%s] error enabling calling line indication\n", pvt->id);
+			goto e_return;
+		case AT_CSSN:
+			ast_debug(1, "[%s] error Supplementary Service Notification activation failed\n", pvt->id);
+
+			/* this is not a fatal error, let's continue with initilization */
+
+			/* set the SMS operating mode to text mode */
+			if (dc_send_cmgf(pvt, 1) || msg_queue_push(pvt, AT_OK, AT_CMGF)) {
+				ast_debug(1, "[%s] error setting CMGF\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CMGF:
+			ast_debug(1, "[%s] error enableing text-mode SMS (CMGF)\n", pvt->id);
+			ast_debug(1, "[%s] no SMS support\n", pvt->id);
+			break;
+		case AT_CSCS:
+			/* this is not a fatal error, let's continue with initilization */
+			ast_debug(1, "[%s] No UCS-2 encoding support.\n", pvt->id);
+			pvt->use_ucs2_encoding = 0;
+			/* set SMS storage location */
+			if (dc_send_cpms(pvt) || msg_queue_push(pvt, AT_OK, AT_CPMS)) {
+				ast_debug(1, "[%s] error setting CPMS\n", pvt->id);
+				goto e_return;
+			}
+			break;
+		case AT_CPMS:
+			ast_debug(1, "[%s] error setting sms storage location (CPMS)\n", pvt->id);
+			ast_debug(1, "[%s] no SMS support\n", pvt->id);
+			break;
+		case AT_CNMI:
+			ast_debug(1, "[%s] error setting sms notifications (CNMI)\n", pvt->id);
+			ast_debug(1, "[%s] no SMS support\n", pvt->id);
+			break;
+
+		/* end initilization stuff */
+
+		case AT_A:
+			ast_debug(1, "[%s] answer failed\n", pvt->id);
+			dc_queue_hangup(pvt);
+			break;
+		case AT_D:
+			ast_debug(1, "[%s] dial failed\n", pvt->id);
+			pvt->needchup = 0;
+			dc_queue_control(pvt, AST_CONTROL_CONGESTION);
+			break;
+		case AT_DDSETEX:
+			ast_debug(1, "[%s] AT^DDSETEX failed\n", pvt->id);
+			break;
+		case AT_CHUP:
+			ast_debug(1, "[%s] error sending hangup, disconnecting\n", pvt->id);
+			goto e_return;
+		case AT_CMGR:
+			ast_debug(1, "[%s] error reading sms message\n", pvt->id);
+			pvt->incoming_sms = 0;
+			break;
+		case AT_CMGD:
+			ast_debug(1, "[%s] error deleting sms message\n", pvt->id);
+			pvt->incoming_sms = 0;
+			break;
+		case AT_CMGS:
+			ast_debug(1, "[%s] error sending sms message\n", pvt->id);
+			pvt->outgoing_sms = 0;
+			break;
+		case AT_VTS:
+			ast_debug(1, "[%s] error sending digit\n", pvt->id);
+			break;
+		case AT_DTMF:
+			ast_debug(1, "[%s] error sending digit\n", pvt->id);
+			break;
+		case AT_COPS:
+			ast_debug(1, "[%s] could not get provider name.\n", pvt->id);
+			break;
+		case AT_UNKNOWN:
+		default:
+			ast_debug(1, "[%s] recieved ERROR for unhandled request: %s\n", pvt->id, at_msg2str(entry->response_to));
+			break;
+		}
+		msg_queue_free_and_pop(pvt);
+	} else if (entry) {
+		ast_debug(1, "[%s] recieved AT message 'ERROR' when expecting %s, ignoring\n", pvt->id, at_msg2str(entry->expected));
+	} else {
+		ast_debug(1, "[%s] recieved unexpected AT message 'ERROR'\n", pvt->id);
+	}
+
+	return 0;
+
+e_return:
+	msg_queue_free_and_pop(pvt);
+	return -1;
+}
+
+/*!
+ * \brief Handle ^CONF AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_conf(struct dc_pvt *pvt, char *buf)
+{
+	return 0;
+}
+
+/*!
+ * \brief Handle ^ORIG AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_orig(struct dc_pvt *pvt, char *buf)
+{
+	int call_index = 1;
+	int call_type = 0;
+
+	/* parse ORIG info in the following format:
+	 * ^ORIG:<call_index>,<call_type>
+	 */
+	if (!sscanf(buf, "^ORIG:%d,%d", &call_index, &call_type)) {
+		ast_debug(1, "[%s] error parsing ORIG event '%s'\n", pvt->id, buf);
+		return -1;
+	}
+
+	ast_debug(1, "[%s] recieved call_index: %d\n", pvt->id, call_index);
+	ast_debug(1, "[%s] recieved call_type: %d\n", pvt->id, call_type);
+	return 0;
+}
+
+/*!
+ * \brief Handle +CSSI AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cssi(struct dc_pvt *pvt, char *buf)
+{
+	if (pvt->outgoing) {
+		ast_debug(1, "[%s] remote alerting\n", pvt->id);
+		dc_queue_control(pvt, AST_CONTROL_RINGING);
+	}
+	return 0;
+}
+
+/*!
+ * \brief Handle +CSSU AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cssu(struct dc_pvt *pvt, char *buf)
+{
+        return 0;
+}
+
+/*!
+ * \brief Handle ^CEND AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cend(struct dc_pvt *pvt, char *buf)
+{
+	ast_debug(1, "[%s] line disconnected\n", pvt->id);
+	if (pvt->owner) {
+		ast_debug(1, "[%s] hanging up owner\n", pvt->id);
+		if (dc_queue_hangup(pvt)) {
+			ast_log(LOG_ERROR, "[%s] error queueing hangup, disconnectiong...\n", pvt->id);
+			return -1;
+		}
+	}
+	pvt->needchup = 0;
+	pvt->incoming = 0;
+	pvt->outgoing = 0;
+
+	return 0;
+}
+
+/*!
+ * \brief Handle ^CONN AT messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_conn(struct dc_pvt *pvt, char *buf)
+{
+	if (pvt->outgoing) {
+		ast_debug(1, "[%s] remote end answered\n", pvt->id);
+		dc_queue_control(pvt, AST_CONTROL_ANSWER);
+	} else if (pvt->incoming && pvt->answered) {
+		ast_setstate(pvt->owner, AST_STATE_UP);
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Handle AT+CLIP messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_clip(struct dc_pvt *pvt, char *buf)
+{
+	char *clip;
+	struct ast_channel *chan;
+
+	ast_debug(1, "[%s] executing handle_response_clip\n", pvt->id);
+
+	if (!(clip = dc_parse_clip(pvt, buf))) {
+		ast_debug(1, "[%s] error parsing CLIP: %s\n", pvt->id, buf);
+	}
+	
+	if (pvt->needring == 0)
+	{
+		pvt->incoming = 1;
+		
+		if (!(chan = dc_new(AST_STATE_RING, pvt, clip))) {
+			ast_log(LOG_ERROR, "[%s] unable to allocate channel for incoming call\n", pvt->id);
+			dc_send_chup(pvt);
+			msg_queue_push(pvt, AT_OK, AT_CHUP);
+			return -1;
+		}
+
+		/* from this point on, we need to send a chup in the event of a
+		 * hangup */
+		pvt->needchup = 1;
+		/* We dont need to send ring a 2nd time */
+		pvt->needring = 1;
+
+		if (ast_pbx_start(chan)) {
+			ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming call\n", pvt->id);
+			dc_ast_hangup(pvt);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Handle RING messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_ring(struct dc_pvt *pvt, char *buf)
+{
+	pvt->incoming = 1;
+	return 0;
+}
+
+/*!
+ * \brief Handle AT+CMTI messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cmti(struct dc_pvt *pvt, char *buf)
+{
+	int index = dc_parse_cmti(pvt, buf);
+	if (index > -1) {
+		ast_debug(1, "[%s] incoming sms message\n", pvt->id);
+
+		pvt->sms_storage_position = index;
+		if (dc_send_cmgr(pvt, index)
+				|| msg_queue_push(pvt, AT_CMGR, AT_CMGR)) {
+			ast_debug(1, "[%s] error sending CMGR to retrieve SMS message\n", pvt->id);
+			return -1;
+		}
+
+		pvt->incoming_sms = 1;
+		return 0;
+	} else {
+		ast_debug(1, "[%s] error parsing incoming sms message alert, disconnecting\n", pvt->id);
+		return -1;
+	}
+}
+
+/*!
+ * \brief Handle AT+CMGR messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cmgr(struct dc_pvt *pvt, char *buf)
+{
+	int res;
+	char sms_utf8_buf[4096];
+	char *from_number, *text;
+	struct ast_channel *chan;
+	struct msg_queue_entry *msg;
+
+	from_number = NULL;
+	text = NULL;
+
+	if ((msg = msg_queue_head(pvt)) && msg->expected == AT_CMGR) {
+		msg_queue_free_and_pop(pvt);
+
+		if (dc_parse_cmgr(pvt, buf, &from_number, &text)) {
+			ast_debug(1, "[%s] error parsing sms message, disconnecting\n", pvt->id);
+			return -1;
+		}
+
+		ast_debug(1, "[%s] successfully read sms message\n", pvt->id);
+		pvt->incoming_sms = 0;
+
+		/* XXX this channel probably does not need to be associated with this pvt */
+		if (!(chan = dc_new(AST_STATE_DOWN, pvt, NULL))) {
+			ast_debug(1, "[%s] error creating sms message channel, disconnecting\n", pvt->id);
+			return -1;
+		}
+
+		if (pvt->use_ucs2_encoding) {
+			res = hexstr_ucs2_to_utf8(text,strlen(text)-2,sms_utf8_buf,sizeof(sms_utf8_buf));
+			if (res>0) {
+				text = sms_utf8_buf;
+			} else {
+				ast_log(LOG_ERROR, "[%s] error parsing SMS (convert UCS-2 to UTF-8): %s\n", pvt->id, text);
+			}
+		}
+
+		strcpy(chan->exten, "sms");
+		pbx_builtin_setvar_helper(chan, "SMSSRC", from_number);
+		pbx_builtin_setvar_helper(chan, "SMSTXT", text);
+
+		if (ast_pbx_start(chan)) {
+			ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming sms\n", pvt->id);
+			dc_ast_hangup(pvt);
+		}
+
+	} else {
+		ast_debug(1, "[%s] got unexpected +CMGR message, ignoring\n", pvt->id);
+	}
+
+	if (pvt->auto_delete_sms)
+	{
+		if (dc_send_cmgd(pvt, pvt->sms_storage_position) || msg_queue_push(pvt, AT_OK, AT_CMGD)) {
+			ast_debug(1, "[%s] error sending CMGD to delete SMS message\n", pvt->id);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Send an SMS message from the queue.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_sms_prompt(struct dc_pvt *pvt, char *buf)
+{
+	struct msg_queue_entry *msg;
+	if (!(msg = msg_queue_head(pvt))) {
+		ast_debug(1, "[%s] error, got sms prompt with no pending sms messages\n", pvt->id);
+		return 0;
+	}
+
+	if (msg->expected != AT_SMS_PROMPT) {
+		ast_debug(1, "[%s] error, got sms prompt but no pending sms messages\n", pvt->id);
+		return 0;
+	}
+
+	if (dc_send_sms_text(pvt, msg->data)
+			|| msg_queue_push(pvt, AT_OK, AT_CMGS)) {
+		msg_queue_free_and_pop(pvt);
+		ast_debug(1, "[%s] error sending sms message\n", pvt->id);
+		return 0;
+	}
+
+	msg_queue_free_and_pop(pvt);
+	return 0;
+}
+
+/*!
+ * \brief Handle CUSD messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cusd(struct dc_pvt *pvt, char *buf)
+{
+	int res;
+	char *cusd;
+	struct ast_channel *chan;
+	char cusd_utf8_buf[4096];
+
+	if (!(cusd = dc_parse_cusd(pvt, buf))) {
+		ast_verb(1, "[%s] error parsing CUSD: %s\n", pvt->id, buf);
+		return 0;
+	}
+
+	ast_verb(1, "Got CUSD response from device %s: %s\n", pvt->id,cusd);
+
+	/* XXX this channel probably does not need to be associated with this pvt */
+	if (!(chan = dc_new(AST_STATE_DOWN, pvt, NULL))) {
+		ast_debug(1, "[%s] error creating cusd message channel, disconnecting\n", pvt->id);
+		return -1;
+	}
+
+	if (pvt->use_ucs2_encoding) {
+		res = hexstr_ucs2_to_utf8(cusd,strlen(cusd),cusd_utf8_buf,sizeof(cusd_utf8_buf));
+		if (res>0) {
+			cusd = cusd_utf8_buf;
+		} else {
+			ast_log(LOG_ERROR, "[%s] error parsing CUSD (convert UCS-2 to UTF-8): %s\n", pvt->id, cusd);
+		}
+	}
+
+	strcpy(chan->exten, "cusd");
+	pbx_builtin_setvar_helper(chan, "CUSDTXT", cusd);
+
+	dc_send_manager_event_new_cusd(pvt, cusd);
+
+	if (ast_pbx_start(chan)) {
+		ast_log(LOG_ERROR, "[%s] unable to start pbx on incoming cusd\n", pvt->id);
+		dc_ast_hangup(pvt);
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Handle BUSY messages.
+ * \param pvt a dc_pvt structure
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_busy(struct dc_pvt *pvt)
+{
+	pvt->hangupcause = AST_CAUSE_USER_BUSY;
+	pvt->needchup = 1;
+	dc_queue_control(pvt, AST_CONTROL_BUSY);
+	return 0;
+}
+ 
+/*!
+ * \brief Handle NO DIALTONE messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_no_dialtone(struct dc_pvt *pvt, char *buf)
+{
+	ast_verb(1, "[%s] datacard reports NO DIALTONE\n", pvt->id);
+	pvt->needchup = 1;
+	dc_queue_control(pvt, AST_CONTROL_CONGESTION);
+	return 0;
+}
+
+/*!
+ * \brief Handle NO CARRIER messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_no_carrier(struct dc_pvt *pvt, char *buf)
+{
+	ast_verb(1, "[%s] datacard reports NO CARRIER\n", pvt->id);
+	pvt->needchup = 1;
+	dc_queue_control(pvt, AST_CONTROL_CONGESTION);
+	return 0;
+}
+
+/*!
+ * \brief Handle +CPIN messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cpin(struct dc_pvt *pvt, char *buf)
+{
+	return dc_parse_cpin(pvt,buf);
+}
+
+/*!
+ * \brief Handle ^SMMEMFULL messages. This event notifies us, that the sms storage is full.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_smmemfull(struct dc_pvt *pvt, char *buf)
+{
+	ast_log(LOG_ERROR, "SMS storage is full on device: %s\n", pvt->id);
+	return 0;
+}
+
+/*!
+ * \brief Handle ^RSSI messages. Here we get the signal strength.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_rssi(struct dc_pvt *pvt, char *buf)
+{
+	pvt->rssi = dc_parse_rssi(pvt, buf);
+
+	if (pvt->rssi == -1) return -1;
+
+	return 0;
+}
+
+/*!
+ * \brief Handle ^MODE messages. Here we get the link mode (GSM, UMTS, EDGE...).
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_mode(struct dc_pvt *pvt, char *buf)
+{
+	pvt->linkmode = dc_parse_linkmode(pvt, buf);
+	pvt->linksubmode = dc_parse_linksubmode(pvt, buf);
+
+	if (pvt->linkmode == -1 || pvt->linksubmode == -1) return -1;
+
+	return 0;
+}
+
+/*!
+ * \brief Handle +COPS messages. Here we get the GSM provider name.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cops(struct dc_pvt *pvt, char *buf)
+{
+	char * provider_name;
+	provider_name = dc_parse_cops(pvt, buf);
+
+	if (provider_name!=NULL) {
+		ast_copy_string(pvt->provider_name, provider_name, sizeof(pvt->provider_name));
+		return 0;
+	}
+
+	ast_copy_string(pvt->provider_name, "NONE", sizeof(pvt->provider_name));
+	return -1;
+}
+
+/*!
+ * \brief Handle +CREG messages. Here we get the GSM registration status.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_creg(struct dc_pvt *pvt, char *buf)
+{
+	if (dc_send_cops(pvt) || msg_queue_push(pvt, AT_OK, AT_COPS)) {
+		ast_debug(1, "[%s] error sending query for provider name\n", pvt->id);
+	}
+
+	return 0;
+}
+
+/*!
+ * \brief Handle AT+CGMI messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cgmi(struct dc_pvt *pvt, char *buf)
+{
+	ast_copy_string(pvt->manufacturer, buf, sizeof(pvt->manufacturer));
+	return 0;
+}
+
+/*!
+ * \brief Handle AT+CGMM messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cgmm(struct dc_pvt *pvt, char *buf)
+{
+	ast_copy_string(pvt->model, buf, sizeof(pvt->model));
+	return 0;
+}
+
+/*!
+ * \brief Handle AT+CGMR messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cgmr(struct dc_pvt *pvt, char *buf)
+{
+	ast_copy_string(pvt->firmware, buf, sizeof(pvt->firmware));
+	return 0;
+}
+
+/*!
+ * \brief Handle AT+CGSN messages.
+ * \param pvt a dc_pvt structure
+ * \param buf a null terminated buffer containing an AT message
+ * \retval 0 success
+ * \retval -1 error
+ */
+static int handle_response_cgsn(struct dc_pvt *pvt, char *buf)
+{
+	ast_copy_string(pvt->imei, buf, sizeof(pvt->imei));
+	return 0;
+}
+
+static void *do_monitor_phone(void *data)
+{
+	struct dc_pvt *pvt = (struct dc_pvt *)data;
+	char buf[2048];
+	int t;
+	at_message_t at_msg;
+	struct msg_queue_entry *entry;
+
+	/* Note: At one point the initilization procedure was neatly contained
+	 * in the dc_init() function, but that initilization method did not
+	 * work with non standard devices.  As a result, the initilization
+	 * procedure is not spread throughout the event handling loop.
+	 */
+
+	/* start initilization with the ATE0 request (disable echo) */
+	ast_mutex_lock(&pvt->lock);
+	pvt->timeout = 10000;
+	if (dc_send_atz(pvt) || msg_queue_push(pvt, AT_OK, AT_Z)) {
+		ast_debug(1, "[%s] error sending ATZ\n", pvt->id);
+		goto e_cleanup;
+	}
+	ast_mutex_unlock(&pvt->lock);
+
+	while (!check_unloading()) {
+		ast_mutex_lock(&pvt->lock);
+		t = pvt->timeout;
+		ast_mutex_unlock(&pvt->lock);
+
+		if (dc_get_device_status(pvt->data_socket) != 1) {
+			ast_log(LOG_ERROR, "Lost data connection to Datacard %s.\n", pvt->id);
+			goto e_cleanup;
+		}
+
+		if (dc_get_device_status(pvt->audio_socket) != 1) {
+			ast_log(LOG_ERROR, "Lost audio connection to Datacard %s.\n", pvt->id);
+			goto e_cleanup;
+		}
+
+		if (!rfcomm_wait(pvt->data_socket, &t)) {
+			ast_debug(1, "[%s] timeout waiting for rfcomm data, disconnecting\n", pvt->id);
+			ast_mutex_lock(&pvt->lock);
+			if (!pvt->initialized) {
+				if ((entry = msg_queue_head(pvt))) {
+					switch (entry->response_to) {
+					default:
+						ast_debug(1, "[%s] timeout while waiting for %s in response to %s\n", pvt->id, at_msg2str(entry->expected), at_msg2str(entry->response_to));
+						break;
+					}
+				}
+			}
+			ast_mutex_unlock(&pvt->lock);
+			goto e_cleanup;
+		}
+
+		if ((at_msg = at_read_full(pvt->data_socket, buf, sizeof(buf))) < 0) {
+			/* XXX gnu specific strerror_r is assummed here, this
+			 * is not really safe.  See the strerror(3) man page
+			 * for more info. */
+			ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
+			break;
+		}
+
+		ast_debug(1, "[%s] %s\n", pvt->id, buf);
+
+		switch (at_msg) {
+		case AT_OK:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_ok(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CMS_ERROR:
+		case AT_ERROR:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_error(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_RING:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_ring(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CSSN:
+			break;
+		case AT_CSSI:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cssi(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CSSU:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cssu(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CONN:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_conn(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CEND:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cend(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CONF:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_conf(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_ORIG:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_orig(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_SMMEMFULL:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_smmemfull(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_RSSI:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_rssi(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_BOOT:
+			break;
+		case AT_CLIP:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_clip(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CMTI:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cmti(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CMGR:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cmgr(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_SMS_PROMPT:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_sms_prompt(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CUSD:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cusd(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_BUSY:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_busy(pvt)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_NO_DIALTONE:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_no_dialtone(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_NO_CARRIER:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_no_carrier(pvt, buf)) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CPIN:
+			ast_mutex_lock(&pvt->lock);
+			if (handle_response_cpin(pvt, buf) != 0) {
+				ast_mutex_unlock(&pvt->lock);
+				goto e_cleanup;
+			}
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_COPS:
+			// An error here is not fatal. Just keep going.
+			ast_mutex_lock(&pvt->lock);
+			handle_response_cops(pvt, buf);
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_CREG:
+			// An error here is not fatal. Just keep going.
+			ast_mutex_lock(&pvt->lock);
+			handle_response_creg(pvt, buf);
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_MODE:
+			// An error here is not fatal. Just keep going.
+			ast_mutex_lock(&pvt->lock);
+			handle_response_mode(pvt, buf);
+			ast_mutex_unlock(&pvt->lock);
+			break;
+		case AT_UNKNOWN:
+			if ((entry = msg_queue_head(pvt))) {
+				switch (entry->response_to) {
+				case AT_CGMI:
+					ast_debug(1, "[%s] Got AT_CGMI data (manufacturer info).\n", pvt->id);
+					ast_mutex_lock(&pvt->lock);
+					handle_response_cgmi(pvt, buf);
+					ast_mutex_unlock(&pvt->lock);
+					break;
+				case AT_CGMM:
+					ast_debug(1, "[%s] Got AT_CGMM data (model info).\n", pvt->id);
+					ast_mutex_lock(&pvt->lock);
+					handle_response_cgmm(pvt, buf);
+					ast_mutex_unlock(&pvt->lock);
+					break;
+				case AT_CGMR:
+					ast_debug(1, "[%s] Got AT+CGMR data (firmware info).\n", pvt->id);
+					ast_mutex_lock(&pvt->lock);
+					handle_response_cgmr(pvt, buf);
+					ast_mutex_unlock(&pvt->lock);
+					break;
+				case AT_CGSN:
+					ast_debug(1, "[%s] Got AT+CGSN data (IMEI number).\n", pvt->id);
+					ast_mutex_lock(&pvt->lock);
+					handle_response_cgsn(pvt, buf);
+					ast_mutex_unlock(&pvt->lock);
+					break;
+				default:
+					ast_debug(1, "[%s] ignoring unknown message: %s\n", pvt->id, buf);
+					break;
+				}
+			}
+			else {
+				ast_debug(1, "[%s] ignoring unknown message: %s\n", pvt->id, buf);
+			}
+			break;
+		case AT_PARSE_ERROR:
+			ast_debug(1, "[%s] error parsing message\n", pvt->id);
+			goto e_cleanup;
+		case AT_READ_ERROR:
+			ast_debug(1, "[%s] error reading from device: %s (%d)\n", pvt->id, strerror_r(errno, buf, sizeof(buf)), errno);
+			goto e_cleanup;
+		default:
+			break;
+		}
+	}
+
+e_cleanup:
+
+	if (!pvt->initialized)
+		ast_verb(3, "Error initializing Datacard %s.\n", pvt->id);
+
+	disconnect_datacard(pvt);
+
+	return NULL;
+}
+
+static int disconnect_datacard(struct dc_pvt *pvt)
+{
+	ast_mutex_lock(&pvt->lock);
+	if (pvt->owner) {
+		ast_debug(1, "[%s] Datacard disconnected, hanging up owner\n", pvt->id);
+		pvt->needchup = 0;
+		dc_queue_hangup(pvt);
+	}
+
+	close(pvt->data_socket);
+	close(pvt->audio_socket);
+	pvt->data_socket = -1;
+	pvt->audio_socket = -1;
+
+	msg_queue_flush(pvt);
+
+	pvt->connected = 0;
+	pvt->initialized = 0;
+
+	ast_mutex_unlock(&pvt->lock);
+
+	ast_verb(3, "Datacard %s has disconnected.\n", pvt->id);
+	manager_event(EVENT_FLAG_SYSTEM, "DatacardStatus", "Status: Disconnect\r\nDevice: %s\r\n", pvt->id);	
+
+	return 1;
+}
+
+static int start_monitor(struct dc_pvt *pvt)
+{
+
+	pvt->data_socket = pvt->data_socket;
+
+	if (ast_pthread_create_background(&pvt->monitor_thread, NULL, do_monitor_phone, pvt) < 0) {
+		pvt->monitor_thread = AST_PTHREADT_NULL;
+		return 0;
+	}
+
+	return 1;
+
+}
+
+static void *do_discovery(void *data)
+{
+	struct dc_pvt *pvt;
+
+	while (!check_unloading()) {
+		AST_RWLIST_RDLOCK(&devices);
+		AST_RWLIST_TRAVERSE(&devices, pvt, entry) {
+			ast_mutex_lock(&pvt->lock);
+			if (!pvt->connected) {
+				ast_verb(3, "Datacard %s trying to connect on %s...\n", pvt->id, pvt->data_tty_str);
+				if ((pvt->data_socket = dc_data_connect(pvt->data_tty_str)) > -1) {
+					if ((pvt->audio_socket = dc_audio_connect(pvt->audio_tty_str)) > -1) {
+						if (start_monitor(pvt)) {
+							pvt->connected = 1;
+							manager_event(EVENT_FLAG_SYSTEM, "DatacardStatus", "Status: Connect\r\nDevice: %s\r\n", pvt->id);
+							ast_verb(3, "Datacard %s has connected, initializing...\n", pvt->id);
+						}
+					}
+				}
+			}
+			ast_mutex_unlock(&pvt->lock);
+		}
+		AST_RWLIST_UNLOCK(&devices);
+
+		/* Go to sleep (only if we are not unloading) */
+
+		if (!check_unloading())
+			sleep(discovery_interval);
+	}
+
+	return NULL;
+}
+
+/*
+
+	Module
+
+*/
+
+/*!
+ * \brief Load a device from the configuration file.
+ * \param cfg the config to load the device from
+ * \param cat the device to load
+ * \return NULL on error, a pointer to the device that was loaded on success
+ */
+static struct dc_pvt *dc_load_device(struct ast_config *cfg, const char *cat)
+{
+	struct dc_pvt *pvt;
+	struct ast_variable *v;
+	const char *audio_tty_str, *data_tty_str;
+	ast_debug(1, "Reading configuration for device %s.\n", cat);
+
+	audio_tty_str = ast_variable_retrieve(cfg, cat, "audio");
+	data_tty_str = ast_variable_retrieve(cfg, cat, "data");
+	if (ast_strlen_zero(audio_tty_str) || ast_strlen_zero(data_tty_str)) {
+		ast_log(LOG_ERROR, "Skipping device %s. Missing required audio_tty or data_tty setting.\n", cat);
+		goto e_return;
+	}
+
+	/* create and initialize our pvt structure */
+	if (!(pvt = ast_calloc(1, sizeof(*pvt)))) {
+		ast_log(LOG_ERROR, "Skipping device %s. Error allocating memory.\n", cat);
+		goto e_return;
+	}
+
+	ast_mutex_init(&pvt->lock);
+	AST_LIST_HEAD_INIT_NOLOCK(&pvt->msg_queue);
+
+	/* set some defaults */
+
+	ast_copy_string(pvt->context, "default", sizeof(pvt->context));
+
+	/* populate the pvt structure */
+
+	ast_copy_string(pvt->id, cat, sizeof(pvt->id));
+	ast_copy_string(pvt->data_tty_str, data_tty_str, sizeof(pvt->data_tty_str));
+	ast_copy_string(pvt->audio_tty_str, audio_tty_str, sizeof(pvt->audio_tty_str));
+	pvt->timeout = -1;
+	pvt->data_socket = -1;
+	pvt->audio_socket = -1;
+	pvt->monitor_thread = AST_PTHREADT_NULL;
+	pvt->needring = 0;
+	pvt->incoming = 0;
+	pvt->has_sms = 0;
+	pvt->has_voice = 0;
+	pvt->rssi = 0;
+	pvt->linkmode = 0;
+	pvt->linksubmode = 0;
+	pvt->volume_synchronized = 0;
+	pvt->rxgain = 0;
+	pvt->txgain = 0;
+	pvt->sms_storage_position = 0;
+	pvt->use_ucs2_encoding = 1;
+
+	/* setup the smoother */
+	if (!(pvt->smoother = ast_smoother_new(DEVICE_FRAME_SIZE))) {
+		ast_log(LOG_ERROR, "Skipping device %s. Error setting up frame smoother.\n", cat);
+		goto e_free_pvt;
+	}
+
+	/* setup the dsp */
+	if (!(pvt->dsp = ast_dsp_new())) {
+		ast_log(LOG_ERROR, "Skipping device %s. Error setting up dsp for dtmf detection.\n", cat);
+		goto e_free_smoother;
+	}
+
+	ast_dsp_set_features(pvt->dsp, DSP_FEATURE_DIGIT_DETECT);
+	ast_dsp_set_digitmode(pvt->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
+
+	for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
+		if (!strcasecmp(v->name, "context")) {
+			ast_copy_string(pvt->context, v->value, sizeof(pvt->context));
+		} else if (!strcasecmp(v->name, "group")) {
+			/* group is set to 0 if invalid */
+			pvt->group = atoi(v->value);
+		} else if (!strcasecmp(v->name, "rxgain")) {
+			/* rxgain is set to 0 if invalid */
+			pvt->rxgain = atoi(v->value);
+		} else if (!strcasecmp(v->name, "txgain")) {
+			/* txgain is set to 0 if invalid */
+			pvt->txgain = atoi(v->value);
+		} else if (!strcasecmp(v->name, "autodeletesms")) {
+			/* auto_delete_sms is set to 0 if invalid */
+			pvt->auto_delete_sms = ast_true(v->value);
+		}
+	}
+
+	AST_RWLIST_WRLOCK(&devices);
+	AST_RWLIST_INSERT_HEAD(&devices, pvt, entry);
+	AST_RWLIST_UNLOCK(&devices);
+	ast_debug(1, "Loaded device %s.\n", pvt->id);
+	ast_log(LOG_NOTICE, "Loaded device %s. data_tty: %s \n", pvt->id, pvt->data_tty_str);
+	ast_log(LOG_NOTICE, "Loaded device %s. audio_tty: %s \n", pvt->id, pvt->audio_tty_str);
+
+	return pvt;
+
+e_free_smoother:
+	ast_smoother_free(pvt->smoother);
+e_free_pvt:
+	ast_free(pvt);
+e_return:
+	return NULL;
+}
+
+static int dc_load_config(void)
+{
+	struct ast_config *cfg;
+	const char *cat;
+	struct ast_variable *v;
+	struct ast_flags config_flags = { 0 };
+
+	cfg = ast_config_load(DC_CONFIG, config_flags);
+	if (!cfg)
+		return -1;
+
+	/* parse [general] section */
+	for (v = ast_variable_browse(cfg, "general"); v; v = v->next) {
+		/* handle jb conf */
+		if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
+			continue;
+
+		if (!strcasecmp(v->name, "interval")) {
+			if (!sscanf(v->value, "%d", &discovery_interval)) {
+				ast_log(LOG_NOTICE, "error parsing 'interval' in general section, using default value\n");
+			}
+		}
+	}
+
+	/* now load devices */
+	for (cat = ast_category_browse(cfg, NULL); cat; cat = ast_category_browse(cfg, cat)) {
+		if (strcasecmp(cat, "general")) {
+			dc_load_device(cfg, cat);
+		}
+	}
+
+	ast_config_destroy(cfg);
+
+	return 0;
+}
+
+/*!
+ * \brief Send a DatacardNewCUSD event to the manager
+ * This function splits the message in multiple lines, so multi-line
+ * CUSD messages can be send over the manager API.
+ * \param pvt a dc_pvt structure
+ * \param message a null terminated buffer containing the message
+ */
+static char *dc_send_manager_event_new_cusd(struct dc_pvt *pvt, char *message)
+{
+	int linecount = 0;
+	struct ast_str *buf;
+	char *pch;
+	char *ret;
+	char *saveptr;
+
+	buf = ast_str_create(256);
+
+	pch = strtok_r (message, "\r\n", &saveptr);
+	while (pch != NULL)
+	{
+		ast_str_append(&buf,0,"MessageLine%d: %s\r\n", linecount, pch);
+		pch = strtok_r (NULL, "\r\n", &saveptr);
+		linecount++;
+	}
+
+	manager_event(EVENT_FLAG_CALL, "DatacardNewCUSD",
+		"Device: %s\r\n"
+		"LineCount: %d\r\n"
+		"%s\r\n",
+		pvt->id,
+		linecount,
+		ast_str_buffer(buf)
+	);
+
+	ret = ast_strdup(ast_str_buffer(buf));
+	ast_free(buf);
+
+	return ret;
+}
+
+/*!
+ * \brief Check if the module is unloading.
+ * \retval 0 not unloading
+ * \retval 1 unloading
+ */
+static inline int check_unloading()
+{
+	int res;
+	ast_mutex_lock(&unload_mutex);
+	res = unloading_flag;
+	ast_mutex_unlock(&unload_mutex);
+
+	return res;
+}
+
+/*!
+ * \brief Set the unloading flag.
+ */
+static inline void set_unloading()
+{
+	ast_mutex_lock(&unload_mutex);
+	unloading_flag = 1;
+	ast_mutex_unlock(&unload_mutex);
+}
+
+static int unload_module(void)
+{
+	struct dc_pvt *pvt;
+
+	/* First, take us out of the channel loop */
+	ast_channel_unregister(&dc_tech);
+
+	/* Unregister the CLI & APP */
+	ast_cli_unregister_multiple(dc_cli, sizeof(dc_cli) / sizeof(dc_cli[0]));
+	ast_unregister_application(app_dcstatus);
+	ast_unregister_application(app_dcsendsms);
+
+	/* signal everyone we are unloading */
+	set_unloading();
+
+	/* Kill the discovery thread */
+	if (discovery_thread != AST_PTHREADT_NULL) {
+		pthread_kill(discovery_thread, SIGURG);
+		pthread_join(discovery_thread, NULL);
+	}
+
+	/* Destroy the device list */
+	AST_RWLIST_WRLOCK(&devices);
+	while ((pvt = AST_RWLIST_REMOVE_HEAD(&devices, entry))) {
+		if (pvt->monitor_thread != AST_PTHREADT_NULL) {
+			pthread_kill(pvt->monitor_thread, SIGURG);
+			pthread_join(pvt->monitor_thread, NULL);
+		}
+
+		close(pvt->audio_socket);
+		close(pvt->data_socket);
+
+		msg_queue_flush(pvt);
+
+		ast_smoother_free(pvt->smoother);
+		ast_dsp_free(pvt->dsp);
+		ast_free(pvt);
+	}
+	AST_RWLIST_UNLOCK(&devices);
+
+	return 0;
+}
+
+static int load_module(void)
+{
+	/* Copy the default jb config over global_jbconf */
+	memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
+
+	if (dc_load_config()) {
+		ast_log(LOG_ERROR, "Errors reading config file %s. Not loading module.\n", DC_CONFIG);
+		return AST_MODULE_LOAD_DECLINE;
+	}
+
+	/* Spin the discovery thread */
+	if (ast_pthread_create_background(&discovery_thread, NULL, do_discovery, NULL) < 0) {
+		ast_log(LOG_ERROR, "Unable to create discovery thread.\n");
+		goto e_cleanup;
+	}
+
+	/* register our channel type */
+	if (ast_channel_register(&dc_tech)) {
+		ast_log(LOG_ERROR, "Unable to register channel class %s\n", "Datacard");
+		goto e_cleanup;
+	}
+
+	ast_cli_register_multiple(dc_cli, sizeof(dc_cli) / sizeof(dc_cli[0]));
+	ast_register_application(app_dcstatus, dc_status_exec, dcstatus_synopsis, dcstatus_desc);
+	ast_register_application(app_dcsendsms, dc_sendsms_exec, dcsendsms_synopsis, dcsendsms_desc);
+
+	ast_manager_register2(
+		"DatacardShowDevices",
+		EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING,
+		dc_manager_show_devices,
+		"List Datacard devices",
+		manager_show_devices_desc);
+
+	ast_manager_register2(
+		"DatacardSendCUSD",
+		EVENT_FLAG_SYSTEM | EVENT_FLAG_CONFIG | EVENT_FLAG_REPORTING,
+		dc_manager_send_cusd,
+		"Send a cusd command to the datacard.",
+		manager_send_cusd_desc);
+
+	return AST_MODULE_LOAD_SUCCESS;
+
+e_cleanup:
+
+	return AST_MODULE_LOAD_FAILURE;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Datacard Channel Driver",
+		.load = load_module,
+		.unload = unload_module,
+);

Ich kanns leider nicht testen, da meine datacard wohl defekt ist :(
 
Bei mir kompiliert er mit der rev40 der datacard und asterisk-1.6.2.5 nicht durch. Die aktuelle "char_conv.c" und "char_conv.h" wurde benutzt. Mit der rev39 und asterisk-1.6.2.5 kompiliert er ohne Fehlermeldung. Das hier ist die Fehlermeldung:
Code:
..............
chan_datacard.c: In function 'load_module':
chan_datacard.c:4625: error: expected expression at end of input
chan_datacard.c:4625: error: too few arguments to function 'ast_manager_register2'
chan_datacard.c:4625: error: expected declaration or statement at end of input
chan_datacard.c:4605: error: label 'e_cleanup' used but not defined
make[2]: *** [chan_datacard.o] Fehler 1
make[2]: Leaving directory `/home/gm/myfreetz/freetz4364/freetz-trunk/source/asterisk-1.6.2.5/channels'
make[1]: *** [channels] Fehler 2
make[1]: *** Warte auf noch nicht beendete Prozesse...
..........
Der Unterschied zwischen rev40 und rev39 der datacard:
Code:
diff rev39_chan_datacard.c rev40_chan_datacard.c
70a71,79
> /*! Global jitterbuffer configuration - by default, jb is disabled */
> static struct ast_jb_conf default_jbconf = {
>      .flags = 0,
>      .max_size = -1,
>      .resync_threshold = -1,
>      .impl = ""
> };
> static struct ast_jb_conf global_jbconf;
>
804d812
<
826a835,836
>      ast_jb_configure(chn, &global_jbconf);
> 
4453a4464,4467
>              /* handle jb conf */
>              if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
>                      continue;
> 
4582a4597,4599
>      /* Copy the default jb config over global_jbconf */
>      memcpy(&global_jbconf, &default_jbconf, sizeof(struct ast_jb_conf));
>
 

Anhänge

  • 170_char_conv_h.patch.txt
    718 Bytes · Aufrufe: 2
  • 180_char_conv_c.patch.txt
    2.5 KB · Aufrufe: 2
Zuletzt bearbeitet:
oh sorry, dann habe ich was falsches gemacht
waren meine ersten experimente diesbezüglich :)

was ich aber noch sagen wollte - für record() gibt es neue optionen

Code:
	AST_APP_OPTION('a', OPTION_APPEND),
	AST_APP_OPTION('k', OPTION_KEEP),	
	AST_APP_OPTION('n', OPTION_NOANSWER),
	AST_APP_OPTION('q', OPTION_QUIET),
	AST_APP_OPTION('s', OPTION_SKIP),
	AST_APP_OPTION('t', OPTION_STAR_TERMINATE),
	AST_APP_OPTION('x', OPTION_IGNORE_TERMINATE),

mit "k" behält er die aufnahme nun endlich wieder
 
oh sorry, dann habe ich was falsches gemacht
[...]
Nein, Du hast nichts falsch gemacht. Es liegt nicht an deinem Patch.

EDIT:
Ich kann auch asterisk-1.6.2.4, mit der rev40-datacard auch nicht kompilieren.

EDIT 2:
Patch mit asterisk-1.6.2.5 und der rev39-datacard im Anhang. Kompilieren funktioniert.

EDIT 3:
Gute Nachricht. Ich habe den Patch überarbeitet, und siehe da, asterisk-1.6.2.5 kann doch mit rev40 der datacard kompiliert werden. :D Siehe Patch im Anhang.Anhang anzeigen 45729

EDIT 4:
Im Anhang ein Patch auf den trunk 4380, mit der rev43 der datacard.
 

Anhänge

  • asterisk-1.6.2.5__chan_datacard_rev40__20100227.patch.txt
    156.3 KB · Aufrufe: 2
  • asterisk-1.6.2.5__chan_datacard_rev39__20100227.patch.txt
    152.9 KB · Aufrufe: 2
  • asterisk-1.6.2.5__chan_datacard_rev43__20100228.patch.txt
    158.2 KB · Aufrufe: 10
Zuletzt bearbeitet:
Sorry, wenn ich jetzt nochmal so dazwischen haue:

Wie und wo kompiliert ihr den Asterisk jetzt genau?
Meiner ist wie gesagt jetzt auf der Box, aber die startet sich selbständig neu.
 
wir nehmen den patch und kompilieren via freetz.
Ich selber wähle die pakete als external aus und verschiebe die dateien so, wie es das script von spblinux und dynamic benötigt.

Wenn aber die Fritzbox selber restartet würde ich auf ein speicherproblem tippen.
Welche Box hast du und hast du einen swap? Hast du alle module genommen?
 
Ok, ich hab den Patch genommen, auf den freetz-trunk angewandt und dann über das freetz-menü den asterisk ausgewählt.

Box ist eine 7170, Pakete als extern hab ich openvpn und jede Menge Libs.
Mehr konnte ich als external nicht auswählen.

Welches Skript für spblinux?
 

Statistik des Forums

Themen
246,386
Beiträge
2,251,243
Mitglieder
374,051
Neuestes Mitglied
oli50
Holen Sie sich 3CX - völlig kostenlos!
Verbinden Sie Ihr Team und Ihre Kunden Telefonie Livechat Videokonferenzen

Gehostet oder selbst-verwaltet. Für bis zu 10 Nutzer dauerhaft kostenlos. Keine Kreditkartendetails erforderlich. Ohne Risiko testen.

3CX
Für diese E-Mail-Adresse besteht bereits ein 3CX-Konto. Sie werden zum Kundenportal weitergeleitet, wo Sie sich anmelden oder Ihr Passwort zurücksetzen können, falls Sie dieses vergessen haben.