More 32->64 bit codec conversions.
[asterisk/asterisk.git] / channels / chan_skinny.c
index 69fa953..79eb2fb 100644 (file)
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <netdb.h>
 #include <arpa/inet.h>
@@ -50,17 +45,16 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #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/lock.h"
 #include "asterisk/sched.h"
 #include "asterisk/io.h"
-#include "asterisk/rtp.h"
+#include "asterisk/rtp_engine.h"
+#include "asterisk/netsock.h"
 #include "asterisk/acl.h"
 #include "asterisk/callerid.h"
 #include "asterisk/cli.h"
+#include "asterisk/manager.h"
 #include "asterisk/say.h"
 #include "asterisk/cdr.h"
 #include "asterisk/astdb.h"
@@ -70,10 +64,76 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/utils.h"
 #include "asterisk/dsp.h"
 #include "asterisk/stringfields.h"
-#include "asterisk/astobj.h"
 #include "asterisk/abstract_jb.h"
 #include "asterisk/threadstorage.h"
 #include "asterisk/devicestate.h"
+#include "asterisk/event.h"
+#include "asterisk/indications.h"
+#include "asterisk/linkedlists.h"
+
+/*** DOCUMENTATION
+       <manager name="SKINNYdevices" language="en_US">
+               <synopsis>
+                       List SKINNY devices (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+               </syntax>
+               <description>
+                       <para>Lists Skinny devices in text format with details on current status.
+                       Devicelist will follow as separate events, followed by a final event called
+                       DevicelistComplete.</para>
+               </description>
+       </manager>
+       <manager name="SKINNYshowdevice" language="en_US">
+               <synopsis>
+                       Show SKINNY device (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+                       <parameter name="Device" required="true">
+                               <para>The device name you want to check.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Show one SKINNY device with details on current status.</para>
+               </description>
+       </manager>
+       <manager name="SKINNYlines" language="en_US">
+               <synopsis>
+                       List SKINNY lines (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+               </syntax>
+               <description>
+                       <para>Lists Skinny lines in text format with details on current status.
+                       Linelist will follow as separate events, followed by a final event called
+                       LinelistComplete.</para>
+               </description>
+       </manager>
+       <manager name="SKINNYshowline" language="en_US">
+               <synopsis>
+                       Show SKINNY line (text format).
+               </synopsis>
+               <syntax>
+                       <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
+                       <parameter name="Line" required="true">
+                               <para>The line name you want to check.</para>
+                       </parameter>
+               </syntax>
+               <description>
+                       <para>Show one SKINNY line with details on current status.</para>
+               </description>
+       </manager>
+ ***/
+
+#ifdef SKINNY_DEVMODE
+#define SKINNY_DEVONLY(code)   \
+       code
+#else
+#define SKINNY_DEVONLY(code)
+#endif
 
 /*************************************
  * Skinny/Asterisk Protocol Settings *
@@ -81,7 +141,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
 static const char config[] = "skinny.conf";
 
-static int default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW;
+static format_t default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW;
 static struct ast_codec_pref default_prefs;
 
 enum skinny_codecs {
@@ -94,13 +154,23 @@ enum skinny_codecs {
        SKINNY_CODEC_H263 = 101
 };
 
-#define DEFAULT_SKINNY_PORT    2000
-#define DEFAULT_SKINNY_BACKLOG 2
-#define SKINNY_MAX_PACKET      1000
+#define DEFAULT_SKINNY_PORT 2000
+#define DEFAULT_SKINNY_BACKLOG 2
+#define SKINNY_MAX_PACKET 1000
+
+static struct {
+       unsigned int tos;
+       unsigned int tos_audio;
+       unsigned int tos_video;
+       unsigned int cos;
+       unsigned int cos_audio;
+       unsigned int cos_video;
+} qos = { 0, 0, 0, 0, 0, 0 };
 
 static int keep_alive = 120;
-static char vmexten[AST_MAX_EXTENSION];                /* Voicemail pilot number */
-static char regcontext[AST_MAX_CONTEXT];       /* Context for auto-extension */
+static char global_vmexten[AST_MAX_EXTENSION];      /* Voicemail pilot number */
+static char used_context[AST_MAX_EXTENSION]; /* placeholder to check if context are already used in regcontext */
+static char regcontext[AST_MAX_CONTEXT];     /* Context for auto-extension */
 static char date_format[6] = "D-M-Y";
 static char version_id[16] = "P002F202";
 
@@ -154,6 +224,11 @@ static struct ast_jb_conf default_jbconf =
 };
 static struct ast_jb_conf global_jbconf;
 
+#ifdef SKINNY_DEVMODE
+AST_THREADSTORAGE(message2str_threadbuf);
+#define MESSAGE2STR_BUFSIZE   35
+#endif
+
 AST_THREADSTORAGE(device2str_threadbuf);
 #define DEVICE2STR_BUFSIZE   15
 
@@ -177,7 +252,7 @@ struct register_message {
        uint32_t maxStreams;
 };
 
-#define IP_PORT_MESSAGE        0x0002
+#define IP_PORT_MESSAGE 0x0002
 
 #define KEYPAD_BUTTON_MESSAGE 0x0003
 struct keypad_button_message {
@@ -189,7 +264,7 @@ struct keypad_button_message {
 
 #define ENBLOC_CALL_MESSAGE 0x0004
 struct enbloc_call_message {
-       char calledParty[24];
+       char calledParty[24];
 };
 
 #define STIMULUS_MESSAGE 0x0005
@@ -201,14 +276,14 @@ struct stimulus_message {
 
 #define OFFHOOK_MESSAGE 0x0006
 struct offhook_message {
-       uint32_t unknown1;
-       uint32_t unknown2;
+       uint32_t instance;
+       uint32_t reference;
 };
 
 #define ONHOOK_MESSAGE 0x0007
 struct onhook_message {
-       uint32_t unknown1;
-       uint32_t unknown2;
+       uint32_t instance;
+       uint32_t reference;
 };
 
 #define CAPABILITIES_RES_MESSAGE 0x0010
@@ -370,6 +445,18 @@ struct call_info_message {
        uint32_t space[3];
 };
 
+#define FORWARD_STAT_MESSAGE 0x0090
+struct forward_stat_message {
+       uint32_t activeforward;
+       uint32_t lineNumber;
+       uint32_t fwdall;
+       char fwdallnum[24];
+       uint32_t fwdbusy;
+       char fwdbusynum[24];
+       uint32_t fwdnoanswer;
+       char fwdnoanswernum[24];
+};
+
 #define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
 struct speed_dial_stat_res_message {
        uint32_t speedDialNumber;
@@ -387,9 +474,9 @@ struct line_stat_res_message {
 
 #define DEFINETIMEDATE_MESSAGE 0x0094
 struct definetimedate_message {
-       uint32_t year;  /* since 1900 */
+       uint32_t year; /* since 1900 */
        uint32_t month;
-       uint32_t dayofweek;     /* monday = 1 */
+       uint32_t dayofweek; /* monday = 1 */
        uint32_t day;
        uint32_t hour;
        uint32_t minute;
@@ -410,44 +497,46 @@ struct button_definition_template {
        /*int custom;*/
 };
 
-#define STIMULUS_REDIAL                        0x01
-#define STIMULUS_SPEEDDIAL             0x02
-#define STIMULUS_HOLD                  0x03
-#define STIMULUS_TRANSFER              0x04
-#define STIMULUS_FORWARDALL            0x05
-#define STIMULUS_FORWARDBUSY           0x06
-#define STIMULUS_FORWARDNOANSWER       0x07
-#define STIMULUS_DISPLAY               0x08
-#define STIMULUS_LINE                  0x09
-#define STIMULUS_VOICEMAIL             0x0F
-#define STIMULUS_AUTOANSWER            0x11
-#define STIMULUS_CONFERENCE            0x7D
-#define STIMULUS_CALLPARK              0x7E
-#define STIMULUS_CALLPICKUP            0x7F
-#define STIMULUS_NONE                  0xFF
+#define STIMULUS_REDIAL 0x01
+#define STIMULUS_SPEEDDIAL 0x02
+#define STIMULUS_HOLD 0x03
+#define STIMULUS_TRANSFER 0x04
+#define STIMULUS_FORWARDALL 0x05
+#define STIMULUS_FORWARDBUSY 0x06
+#define STIMULUS_FORWARDNOANSWER 0x07
+#define STIMULUS_DISPLAY 0x08
+#define STIMULUS_LINE 0x09
+#define STIMULUS_VOICEMAIL 0x0F
+#define STIMULUS_AUTOANSWER 0x11
+#define STIMULUS_DND 0x3F
+#define STIMULUS_CONFERENCE 0x7D
+#define STIMULUS_CALLPARK 0x7E
+#define STIMULUS_CALLPICKUP 0x7F
+#define STIMULUS_NONE 0xFF
 
 /* Button types */
-#define BT_REDIAL                      STIMULUS_REDIAL
-#define BT_SPEEDDIAL                   STIMULUS_SPEEDDIAL
-#define BT_HOLD                                STIMULUS_HOLD
-#define BT_TRANSFER                    STIMULUS_TRANSFER
-#define BT_FORWARDALL                  STIMULUS_FORWARDALL
-#define BT_FORWARDBUSY                 STIMULUS_FORWARDBUSY
-#define BT_FORWARDNOANSWER             STIMULUS_FORWARDNOANSWER
-#define BT_DISPLAY                     STIMULUS_DISPLAY
-#define BT_LINE                                STIMULUS_LINE
-#define BT_VOICEMAIL                   STIMULUS_VOICEMAIL
-#define BT_AUTOANSWER                  STIMULUS_AUTOANSWER
-#define BT_CONFERENCE                  STIMULUS_CONFERENCE
-#define BT_CALLPARK                    STIMULUS_CALLPARK
-#define BT_CALLPICKUP                  STIMULUS_CALLPICKUP
-#define BT_NONE                                0x00
+#define BT_REDIAL STIMULUS_REDIAL
+#define BT_SPEEDDIAL STIMULUS_SPEEDDIAL
+#define BT_HOLD STIMULUS_HOLD
+#define BT_TRANSFER STIMULUS_TRANSFER
+#define BT_FORWARDALL STIMULUS_FORWARDALL
+#define BT_FORWARDBUSY STIMULUS_FORWARDBUSY
+#define BT_FORWARDNOANSWER STIMULUS_FORWARDNOANSWER
+#define BT_DISPLAY STIMULUS_DISPLAY
+#define BT_LINE STIMULUS_LINE
+#define BT_VOICEMAIL STIMULUS_VOICEMAIL
+#define BT_AUTOANSWER STIMULUS_AUTOANSWER
+#define BT_DND STIMULUS_DND
+#define BT_CONFERENCE STIMULUS_CONFERENCE
+#define BT_CALLPARK STIMULUS_CALLPARK
+#define BT_CALLPICKUP STIMULUS_CALLPICKUP
+#define BT_NONE 0x00
 
 /* Custom button types - add our own between 0xB0 and 0xCF.
    This may need to be revised in the future,
    if stimuluses are ever added in this range. */
-#define BT_CUST_LINESPEEDDIAL          0xB0    /* line or speeddial with/without hint */
-#define BT_CUST_LINE                   0xB1    /* line or speeddial with hint only */
+#define BT_CUST_LINESPEEDDIAL 0xB0 /* line or speeddial with/without hint */
+#define BT_CUST_LINE 0xB1          /* line or speeddial with hint only */
 
 struct button_template_res_message {
        uint32_t buttonOffset;
@@ -519,57 +608,61 @@ struct soft_key_template_definition {
        uint32_t softKeyEvent;
 };
 
-#define KEYDEF_ONHOOK                  0
-#define KEYDEF_CONNECTED               1
-#define KEYDEF_ONHOLD                  2
-#define KEYDEF_RINGIN                  3
-#define KEYDEF_OFFHOOK                 4
-#define KEYDEF_CONNWITHTRANS           5
-#define KEYDEF_DADFD                   6 /* Digits After Dialing First Digit */
-#define KEYDEF_CONNWITHCONF            7
-#define KEYDEF_RINGOUT                 8
-#define KEYDEF_OFFHOOKWITHFEAT         9
-#define KEYDEF_UNKNOWN                 10
-
-#define SOFTKEY_NONE                   0x00
-#define SOFTKEY_REDIAL                 0x01
-#define SOFTKEY_NEWCALL                        0x02
-#define SOFTKEY_HOLD                   0x03
-#define SOFTKEY_TRNSFER                        0x04
-#define SOFTKEY_CFWDALL                        0x05
-#define SOFTKEY_CFWDBUSY               0x06
-#define SOFTKEY_CFWDNOANSWER           0x07
-#define SOFTKEY_BKSPC                  0x08
-#define SOFTKEY_ENDCALL                        0x09
-#define SOFTKEY_RESUME                 0x0A
-#define SOFTKEY_ANSWER                 0x0B
-#define SOFTKEY_INFO                   0x0C
-#define SOFTKEY_CONFRN                 0x0D
-#define SOFTKEY_PARK                   0x0E
-#define SOFTKEY_JOIN                   0x0F
-#define SOFTKEY_MEETME                 0x10
-#define SOFTKEY_PICKUP                 0x11
-#define SOFTKEY_GPICKUP                        0x12
-
-struct soft_key_template_definition soft_key_template_default[] = {
-       { "\200\001",           SOFTKEY_REDIAL },
-       { "\200\002",           SOFTKEY_NEWCALL },
-       { "\200\003",           SOFTKEY_HOLD },
-       { "\200\004",           SOFTKEY_TRNSFER },
-       { "\200\005",           SOFTKEY_CFWDALL },
-       { "\200\006",           SOFTKEY_CFWDBUSY },
-       { "\200\007",           SOFTKEY_CFWDNOANSWER },
-       { "\200\010",           SOFTKEY_BKSPC },
-       { "\200\011",           SOFTKEY_ENDCALL },
-       { "\200\012",           SOFTKEY_RESUME },
-       { "\200\013",           SOFTKEY_ANSWER },
-       { "\200\014",           SOFTKEY_INFO },
-       { "\200\015",           SOFTKEY_CONFRN },
-       { "\200\016",           SOFTKEY_PARK },
-       { "\200\017",           SOFTKEY_JOIN },
-       { "\200\020",           SOFTKEY_MEETME },
-       { "\200\021",           SOFTKEY_PICKUP },
-       { "\200\022",           SOFTKEY_GPICKUP },
+#define KEYDEF_ONHOOK 0
+#define KEYDEF_CONNECTED 1
+#define KEYDEF_ONHOLD 2
+#define KEYDEF_RINGIN 3
+#define KEYDEF_OFFHOOK 4
+#define KEYDEF_CONNWITHTRANS 5
+#define KEYDEF_DADFD 6 /* Digits After Dialing First Digit */
+#define KEYDEF_CONNWITHCONF 7
+#define KEYDEF_RINGOUT 8
+#define KEYDEF_OFFHOOKWITHFEAT 9
+#define KEYDEF_UNKNOWN 10
+
+#define SOFTKEY_NONE 0x00
+#define SOFTKEY_REDIAL 0x01
+#define SOFTKEY_NEWCALL 0x02
+#define SOFTKEY_HOLD 0x03
+#define SOFTKEY_TRNSFER 0x04
+#define SOFTKEY_CFWDALL 0x05
+#define SOFTKEY_CFWDBUSY 0x06
+#define SOFTKEY_CFWDNOANSWER 0x07
+#define SOFTKEY_BKSPC 0x08
+#define SOFTKEY_ENDCALL 0x09
+#define SOFTKEY_RESUME 0x0A
+#define SOFTKEY_ANSWER 0x0B
+#define SOFTKEY_INFO 0x0C
+#define SOFTKEY_CONFRN 0x0D
+#define SOFTKEY_PARK 0x0E
+#define SOFTKEY_JOIN 0x0F
+#define SOFTKEY_MEETME 0x10
+#define SOFTKEY_PICKUP 0x11
+#define SOFTKEY_GPICKUP 0x12
+#define SOFTKEY_DND 0x13
+#define SOFTKEY_IDIVERT 0x14
+
+static struct soft_key_template_definition soft_key_template_default[] = {
+       { "\200\001", SOFTKEY_REDIAL },
+       { "\200\002", SOFTKEY_NEWCALL },
+       { "\200\003", SOFTKEY_HOLD },
+       { "\200\004", SOFTKEY_TRNSFER },
+       { "\200\005", SOFTKEY_CFWDALL },
+       { "\200\006", SOFTKEY_CFWDBUSY },
+       { "\200\007", SOFTKEY_CFWDNOANSWER },
+       { "\200\010", SOFTKEY_BKSPC },
+       { "\200\011", SOFTKEY_ENDCALL },
+       { "\200\012", SOFTKEY_RESUME },
+       { "\200\013", SOFTKEY_ANSWER },
+       { "\200\014", SOFTKEY_INFO },
+       { "\200\015", SOFTKEY_CONFRN },
+       { "\200\016", SOFTKEY_PARK },
+       { "\200\017", SOFTKEY_JOIN },
+       { "\200\020", SOFTKEY_MEETME },
+       { "\200\021", SOFTKEY_PICKUP },
+       { "\200\022", SOFTKEY_GPICKUP },
+       { "\200\077", SOFTKEY_DND },
+       { "\200\120", SOFTKEY_IDIVERT },
 };
 
 /* Localized message "codes" (in octal)
@@ -717,8 +810,9 @@ static const uint8_t soft_key_default_onhook[] = {
        SOFTKEY_NEWCALL,
        SOFTKEY_CFWDALL,
        SOFTKEY_CFWDBUSY,
-       SOFTKEY_GPICKUP,
-       SOFTKEY_CONFRN,
+       SOFTKEY_DND,
+       /*SOFTKEY_GPICKUP,
+       SOFTKEY_CONFRN,*/
 };
 
 static const uint8_t soft_key_default_connected[] = {
@@ -748,7 +842,7 @@ static const uint8_t soft_key_default_offhook[] = {
        SOFTKEY_ENDCALL,
        SOFTKEY_CFWDALL,
        SOFTKEY_CFWDBUSY,
-       SOFTKEY_GPICKUP,
+       /*SOFTKEY_GPICKUP,*/
 };
 
 static const uint8_t soft_key_default_connwithtrans[] = {
@@ -777,6 +871,7 @@ static const uint8_t soft_key_default_ringout[] = {
 static const uint8_t soft_key_default_offhookwithfeat[] = {
        SOFTKEY_REDIAL,
        SOFTKEY_ENDCALL,
+       SOFTKEY_TRNSFER,
 };
 
 static const uint8_t soft_key_default_unknown[] = {
@@ -841,12 +936,13 @@ struct display_prompt_status_message {
        char promptMessage[32];
        uint32_t lineInstance;
        uint32_t callReference;
+       uint32_t space[3];
 };
 
 #define CLEAR_PROMPT_MESSAGE  0x0113
 struct clear_prompt_message {
-       uint32_t lineInstance;
-       uint32_t callReference;
+       uint32_t lineInstance;
+       uint32_t callReference;
 };
 
 #define DISPLAY_NOTIFY_MESSAGE 0x0114
@@ -910,6 +1006,7 @@ union skinny_data {
        struct dialed_number_message dialednumber;
        struct soft_key_event_message softkeyeventmessage;
        struct enbloc_call_message enbloccallmessage;
+       struct forward_stat_message forwardstat;
 };
 
 /* packet composition */
@@ -923,78 +1020,64 @@ struct skinny_req {
 /* XXX This is the combined size of the variables above.  (len, res, e)
    If more are added, this MUST change.
    (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */
-int skinny_header_size = 12;
+static int skinny_header_size = 12;
 
 /*****************************
  * Asterisk specific globals *
  *****************************/
 
 static int skinnydebug = 0;
+static int skinnyreload = 0;
 
 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
 static struct sockaddr_in bindaddr;
 static char ourhost[256];
 static int ourport;
 static struct in_addr __ourip;
-struct ast_hostent ahp;
-struct hostent *hp;
+static struct ast_hostent ahp;
+static struct hostent *hp;
 static int skinnysock = -1;
 static pthread_t accept_t;
-static char context[AST_MAX_CONTEXT] = "default";
-static char language[MAX_LANGUAGE] = "";
-static char mohinterpret[MAX_MUSICCLASS] = "default";
-static char mohsuggest[MAX_MUSICCLASS] = "";
-static char cid_num[AST_MAX_EXTENSION] = "";
-static char cid_name[AST_MAX_EXTENSION] = "";
-static char linelabel[AST_MAX_EXTENSION] ="";
-static int nat = 0;
-static ast_group_t cur_callergroup = 0;
-static ast_group_t cur_pickupgroup = 0;
-static int immediate = 0;
-static int callwaiting = 0;
-static int callreturn = 0;
-static int threewaycalling = 0;
-static int mwiblink = 0;
-/* This is for flashhook transfers */
-static int transfer = 0;
-static int cancallforward = 0;
-/* static int busycount = 3;*/
-static char accountcode[AST_MAX_ACCOUNT_CODE] = "";
-static char mailbox[AST_MAX_EXTENSION];
-static char regexten[AST_MAX_EXTENSION];
-static int amaflags = 0;
 static int callnums = 1;
-static int canreinvite = 0;
-
-#define SKINNY_DEVICE_UNKNOWN          -1
-#define SKINNY_DEVICE_NONE             0
-#define SKINNY_DEVICE_30SPPLUS         1
-#define SKINNY_DEVICE_12SPPLUS         2
-#define SKINNY_DEVICE_12SP             3
-#define SKINNY_DEVICE_12               4
-#define SKINNY_DEVICE_30VIP            5
-#define SKINNY_DEVICE_7910             6
-#define SKINNY_DEVICE_7960             7
-#define SKINNY_DEVICE_7940             8
-#define SKINNY_DEVICE_7935             9
-#define SKINNY_DEVICE_ATA186           12      /* Cisco ATA-186 */
-#define SKINNY_DEVICE_7941             115
-#define SKINNY_DEVICE_7971             119
-#define SKINNY_DEVICE_7985             302
-#define SKINNY_DEVICE_7911             307
-#define SKINNY_DEVICE_7961GE           308
-#define SKINNY_DEVICE_7941GE           309
-#define SKINNY_DEVICE_7921             365
-#define SKINNY_DEVICE_7905             20000
-#define SKINNY_DEVICE_7920             30002
-#define SKINNY_DEVICE_7970             30006
-#define SKINNY_DEVICE_7912             30007
-#define SKINNY_DEVICE_7902             30008
-#define SKINNY_DEVICE_CIPC             30016   /* Cisco IP Communicator */
-#define SKINNY_DEVICE_7961             30018
-#define SKINNY_DEVICE_7936             30019
-#define SKINNY_DEVICE_SCCPGATEWAY_AN   30027   /* ??? */
-#define SKINNY_DEVICE_SCCPGATEWAY_BRI  30028   /* ??? */
+
+#define SKINNY_DEVICE_UNKNOWN -1
+#define SKINNY_DEVICE_NONE 0
+#define SKINNY_DEVICE_30SPPLUS 1
+#define SKINNY_DEVICE_12SPPLUS 2
+#define SKINNY_DEVICE_12SP 3
+#define SKINNY_DEVICE_12 4
+#define SKINNY_DEVICE_30VIP 5
+#define SKINNY_DEVICE_7910 6
+#define SKINNY_DEVICE_7960 7
+#define SKINNY_DEVICE_7940 8
+#define SKINNY_DEVICE_7935 9
+#define SKINNY_DEVICE_ATA186 12 /* Cisco ATA-186 */
+#define SKINNY_DEVICE_7941 115
+#define SKINNY_DEVICE_7971 119
+#define SKINNY_DEVICE_7914 124 /* Expansion module */
+#define SKINNY_DEVICE_7985 302
+#define SKINNY_DEVICE_7911 307
+#define SKINNY_DEVICE_7961GE 308
+#define SKINNY_DEVICE_7941GE 309
+#define SKINNY_DEVICE_7931 348
+#define SKINNY_DEVICE_7921 365
+#define SKINNY_DEVICE_7906 369
+#define SKINNY_DEVICE_7962 404 /* Not found */
+#define SKINNY_DEVICE_7937 431
+#define SKINNY_DEVICE_7942 434
+#define SKINNY_DEVICE_7945 435
+#define SKINNY_DEVICE_7965 436
+#define SKINNY_DEVICE_7975 437
+#define SKINNY_DEVICE_7905 20000
+#define SKINNY_DEVICE_7920 30002
+#define SKINNY_DEVICE_7970 30006
+#define SKINNY_DEVICE_7912 30007
+#define SKINNY_DEVICE_7902 30008
+#define SKINNY_DEVICE_CIPC 30016 /* Cisco IP Communicator */
+#define SKINNY_DEVICE_7961 30018
+#define SKINNY_DEVICE_7936 30019
+#define SKINNY_DEVICE_SCCPGATEWAY_AN 30027 /* Analog gateway */
+#define SKINNY_DEVICE_SCCPGATEWAY_BRI 30028 /* BRI gateway */
 
 #define SKINNY_SPEAKERON 1
 #define SKINNY_SPEAKEROFF 2
@@ -1017,13 +1100,13 @@ static int canreinvite = 0;
 #define SKINNY_CALLREMOTEMULTILINE 13
 #define SKINNY_INVALID 14
 
-#define SKINNY_SILENCE                 0x00
-#define SKINNY_DIALTONE        0x21
-#define SKINNY_BUSYTONE        0x23
-#define SKINNY_ALERT           0x24
-#define SKINNY_REORDER                 0x25
-#define SKINNY_CALLWAITTONE    0x2D
-#define SKINNY_NOTONE          0x7F
+#define SKINNY_SILENCE 0x00
+#define SKINNY_DIALTONE 0x21
+#define SKINNY_BUSYTONE 0x23
+#define SKINNY_ALERT 0x24
+#define SKINNY_REORDER 0x25
+#define SKINNY_CALLWAITTONE 0x2D
+#define SKINNY_NOTONE 0x7F
 
 #define SKINNY_LAMP_OFF 1
 #define SKINNY_LAMP_ON 2
@@ -1036,20 +1119,21 @@ static int canreinvite = 0;
 #define SKINNY_RING_OUTSIDE 3
 #define SKINNY_RING_FEATURE 4
 
-#define TYPE_TRUNK 1
-#define TYPE_LINE 2
+#define SKINNY_CFWD_ALL       (1 << 0)
+#define SKINNY_CFWD_BUSY      (1 << 1)
+#define SKINNY_CFWD_NOANSWER  (1 << 2)
 
 /* Skinny rtp stream modes. Do we really need this? */
-#define SKINNY_CX_SENDONLY     0
-#define SKINNY_CX_RECVONLY     1
-#define SKINNY_CX_SENDRECV     2
-#define SKINNY_CX_CONF         3
-#define SKINNY_CX_CONFERENCE   3
-#define SKINNY_CX_MUTE         4
-#define SKINNY_CX_INACTIVE     4
+#define SKINNY_CX_SENDONLY 0
+#define SKINNY_CX_RECVONLY 1
+#define SKINNY_CX_SENDRECV 2
+#define SKINNY_CX_CONF 3
+#define SKINNY_CX_CONFERENCE 3
+#define SKINNY_CX_MUTE 4
+#define SKINNY_CX_INACTIVE 4
 
 #if 0
-static char *skinny_cxmodes[] = {
+static const char * const skinny_cxmodes[] = {
        "sendonly",
        "recvonly",
        "sendrecv",
@@ -1067,14 +1151,6 @@ static struct io_context *io;
 AST_MUTEX_DEFINE_STATIC(monlock);
 /* Protect the network socket */
 AST_MUTEX_DEFINE_STATIC(netlock);
-/* Protect the session list */
-AST_MUTEX_DEFINE_STATIC(sessionlock);
-/* Protect the device list */
-AST_MUTEX_DEFINE_STATIC(devicelock);
-#if 0
-/* Protect the paging device list */
-AST_MUTEX_DEFINE_STATIC(pagingdevicelock);
-#endif
 
 /* This is the thread for the monitor which checks for input on the channels
    which are not currently in use. */
@@ -1092,74 +1168,115 @@ static int matchdigittimeout = 3000;
 struct skinny_subchannel {
        ast_mutex_t lock;
        struct ast_channel *owner;
-       struct ast_rtp *rtp;
-       struct ast_rtp *vrtp;
+       struct ast_rtp_instance *rtp;
+       struct ast_rtp_instance *vrtp;
        unsigned int callid;
-       /* time_t lastouttime; */                       /* Unused */
+       /* time_t lastouttime; */ /* Unused */
        int progress;
        int ringing;
        int onhold;
-       /* int lastout; */                              /* Unused */
+       /* int lastout; */ /* Unused */
        int cxmode;
        int nat;
        int outgoing;
        int alreadygone;
+       int blindxfer;
+       int xferor;
+
 
-       struct skinny_subchannel *next;
+       AST_LIST_ENTRY(skinny_subchannel) list;
+       struct skinny_subchannel *related;
        struct skinny_line *parent;
 };
 
+#define SKINNY_LINE_OPTIONS                            \
+       char name[80];                                  \
+       char label[24];                                 \
+       char accountcode[AST_MAX_ACCOUNT_CODE];         \
+       char exten[AST_MAX_EXTENSION];                  \
+       char context[AST_MAX_CONTEXT];                  \
+       char language[MAX_LANGUAGE];                    \
+       char cid_num[AST_MAX_EXTENSION];                \
+       char cid_name[AST_MAX_EXTENSION];               \
+       char lastcallerid[AST_MAX_EXTENSION];           \
+       int cfwdtype;                                   \
+       char call_forward_all[AST_MAX_EXTENSION];       \
+       char call_forward_busy[AST_MAX_EXTENSION];      \
+       char call_forward_noanswer[AST_MAX_EXTENSION];  \
+       char mailbox[AST_MAX_EXTENSION];                \
+       char vmexten[AST_MAX_EXTENSION];                \
+       char regexten[AST_MAX_EXTENSION];               \
+       char regcontext[AST_MAX_CONTEXT];               \
+       char parkinglot[AST_MAX_CONTEXT];               \
+       char mohinterpret[MAX_MUSICCLASS];              \
+       char mohsuggest[MAX_MUSICCLASS];                \
+       char lastnumberdialed[AST_MAX_EXTENSION];       \
+       int curtone;                                    \
+       ast_group_t callgroup;                          \
+       ast_group_t pickupgroup;                        \
+       int callwaiting;                                \
+       int transfer;                                   \
+       int threewaycalling;                            \
+       int mwiblink;                                   \
+       int cancallforward;                             \
+       int getforward;                                 \
+       int callreturn;                                 \
+       int dnd;                                        \
+       int hascallerid;                                \
+       int hidecallerid;                               \
+       int amaflags;                                   \
+       int type;                                       \
+       int instance;                                   \
+       int group;                                      \
+       int needdestroy;                                \
+       format_t confcapability;                                \
+       struct ast_codec_pref confprefs;                \
+       format_t capability;                                    \
+       struct ast_codec_pref prefs;                    \
+       int nonCodecCapability;                         \
+       int onhooktime;                                 \
+       int msgstate;                                   \
+       int immediate;                                  \
+       int hookstate;                                  \
+       int nat;                                        \
+       int directmedia;                                \
+       int prune;
+
 struct skinny_line {
+       SKINNY_LINE_OPTIONS
        ast_mutex_t lock;
-       char name[80];
-       char label[24];                                 /* Label that shows next to the line buttons */
-       char accountcode[AST_MAX_ACCOUNT_CODE];
-       char exten[AST_MAX_EXTENSION];                  /* Extension where to start */
-       char context[AST_MAX_CONTEXT];
-       char language[MAX_LANGUAGE];
-       char cid_num[AST_MAX_EXTENSION];                /* Caller*ID */
-       char cid_name[AST_MAX_EXTENSION];               /* Caller*ID */
-       char lastcallerid[AST_MAX_EXTENSION];           /* Last Caller*ID */
-       char call_forward[AST_MAX_EXTENSION];
-       char mailbox[AST_MAX_EXTENSION];
-       char vmexten[AST_MAX_EXTENSION];
-       char regexten[AST_MAX_EXTENSION];               /* Extension for auto-extensions */
-       char regcontext[AST_MAX_CONTEXT];               /* Context for auto-extensions */
-       char mohinterpret[MAX_MUSICCLASS];
-       char mohsuggest[MAX_MUSICCLASS];
-       char lastnumberdialed[AST_MAX_EXTENSION];       /* Last number that was dialed - used for redial */
-       int curtone;                                    /* Current tone being played */
-       ast_group_t callgroup;
-       ast_group_t pickupgroup;
-       int callwaiting;
-       int transfer;
-       int threewaycalling;
-       int mwiblink;
-       int cancallforward;
-       int callreturn;
-       int dnd; /* How does this affect callwait?  Do we just deny a skinny_request if we're dnd? */
-       int hascallerid;
-       int hidecallerid;
-       int amaflags;
-       int type;
-       int instance;
-       int group;
-       int needdestroy;
-       int capability;
-       int nonCodecCapability;
-       int onhooktime;
-       int msgstate;                                   /* voicemail message state */
-       int immediate;
-       int hookstate;
-       int nat;
-       int canreinvite;
+       struct ast_event_sub *mwi_event_sub; /* Event based MWI */
+       struct skinny_subchannel *activesub;
+       AST_LIST_HEAD(, skinny_subchannel) sub;
+       AST_LIST_ENTRY(skinny_line) list;
+       AST_LIST_ENTRY(skinny_line) all;
+       struct skinny_device *device;
+       struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */
+       int newmsgs;
+};
 
-       struct ast_codec_pref prefs;
-       struct skinny_subchannel *sub;
-       struct skinny_line *next;
-       struct skinny_device *parent;
-       struct ast_variable *chanvars;          /*!< Channel variables to set for inbound call */
+static struct skinny_line_options{
+       SKINNY_LINE_OPTIONS
+} default_line_struct = {
+       .callwaiting = 1,
+       .transfer = 1,
+       .mwiblink = 0,
+       .dnd = 0,
+       .hidecallerid = 0,
+       .amaflags = 0,
+       .instance = 0,
+       .directmedia = 0,
+       .nat = 0,
+       .confcapability = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
+       .capability = 0,
+       .getforward = 0,
+       .needdestroy = 0,
+       .prune = 0,
+       .hookstate = SKINNY_ONHOOK,
 };
+static struct skinny_line_options *default_line = &default_line_struct;
+
+static AST_LIST_HEAD_STATIC(lines, skinny_line);
 
 struct skinny_speeddial {
        ast_mutex_t lock;
@@ -1171,48 +1288,79 @@ struct skinny_speeddial {
        int laststate;
        int isHint;
 
-       struct skinny_speeddial *next;
+       AST_LIST_ENTRY(skinny_speeddial) list;
        struct skinny_device *parent;
 };
 
 struct skinny_addon {
        ast_mutex_t lock;
        char type[10];
-
-       struct skinny_addon *next;
+       AST_LIST_ENTRY(skinny_addon) list;
        struct skinny_device *parent;
 };
 
-static struct skinny_device {
-       /* A device containing one or more lines */
-       char name[80];
-       char id[16];
-       char version_id[16];
-       char exten[AST_MAX_EXTENSION];          /* Cruddy variable name, pick a better one */
-       int type;
-       int registered;
-       int lastlineinstance;
-       int lastcallreference;
-       int capability;
+#define SKINNY_DEVICE_OPTIONS                                  \
+       char name[80];                                          \
+       char id[16];                                            \
+       char version_id[16];                                    \
+       char exten[AST_MAX_EXTENSION];                          \
+       char vmexten[AST_MAX_EXTENSION];                        \
+       int type;                                               \
+       int registered;                                         \
+       int lastlineinstance;                                   \
+       int lastcallreference;                                  \
+       format_t confcapability;                                        \
+       struct ast_codec_pref confprefs;                        \
+       format_t capability;                                            \
+       int earlyrtp;                                           \
+       int transfer;                                           \
+       int callwaiting;                                        \
+       int mwiblink;                                           \
+       int dnd;                                                \
+       int prune;
+
+struct skinny_device {
+       SKINNY_DEVICE_OPTIONS
+       struct type *first;
+       struct type *last;
+       ast_mutex_t lock;
        struct sockaddr_in addr;
        struct in_addr ourip;
-       struct skinny_line *lines;
-       struct skinny_speeddial *speeddials;
-       struct skinny_addon *addons;
-       struct ast_codec_pref prefs;
        struct ast_ha *ha;
        struct skinnysession *session;
-       struct skinny_device *next;
-} *devices = NULL;
-
-struct skinny_paging_device {
-       char name[80];
-       char id[16];
-       struct skinny_device ** devices;
-       struct skinny_paging_device *next;
+       struct skinny_line *activeline;
+       AST_LIST_HEAD(, skinny_line) lines;
+       AST_LIST_HEAD(, skinny_speeddial) speeddials;
+       AST_LIST_HEAD(, skinny_addon) addons;
+       AST_LIST_ENTRY(skinny_device) list;
+};
+
+static struct skinny_device_options {
+       SKINNY_DEVICE_OPTIONS
+} default_device_struct = {
+       .transfer = 1,
+       .earlyrtp = 1,
+       .callwaiting = 1,
+       .mwiblink = 0,
+       .dnd = 0,
+       .confcapability = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
+       .capability = 0,
+       .prune = 0,
+};
+static struct skinny_device_options *default_device = &default_device_struct;
+       
+static AST_LIST_HEAD_STATIC(devices, skinny_device);
+
+/*static struct ast_jb_conf default_jbconf =
+{
+       .flags = 0,
+       .max_size = -1,
+       .resync_threshold = -1,
+       .impl = ""
 };
+static struct ast_jb_conf global_jbconf;*/
 
-static struct skinnysession {
+struct skinnysession {
        pthread_t t;
        ast_mutex_t lock;
        struct sockaddr_in sin;
@@ -1220,10 +1368,12 @@ static struct skinnysession {
        char inbuf[SKINNY_MAX_PACKET];
        char outbuf[SKINNY_MAX_PACKET];
        struct skinny_device *device;
-       struct skinnysession *next;
-} *sessions = NULL;
+       AST_LIST_ENTRY(skinnysession) list;
+};
+
+static struct ast_channel *skinny_request(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
+static AST_LIST_HEAD_STATIC(sessions, skinnysession);
 
-static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
 static int skinny_devicestate(void *data);
 static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
 static int skinny_hangup(struct ast_channel *ast);
@@ -1235,11 +1385,13 @@ static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan
 static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
 static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s);
+static void mwi_event_cb(const struct ast_event *event, void *userdata);
+static int skinny_reload(void);
 
 static const struct ast_channel_tech skinny_tech = {
        .type = "Skinny",
        .description = tdesc,
-       .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
+       .capabilities = AST_FORMAT_AUDIO_MASK,
        .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
        .requester = skinny_request,
        .devicestate = skinny_devicestate,
@@ -1252,15 +1404,16 @@ static const struct ast_channel_tech skinny_tech = {
        .fixup = skinny_fixup,
        .send_digit_begin = skinny_senddigit_begin,
        .send_digit_end = skinny_senddigit_end,
-       .bridge = ast_rtp_bridge,  
+       .bridge = ast_rtp_instance_bridge, 
 };
 
 static int skinny_extensionstate_cb(char *context, char* exten, int state, void *data);
+static int skinny_transfer(struct skinny_subchannel *sub);
 
 static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn)
 {
        struct skinny_device *d = s->device;
-       struct skinny_addon *a = d->addons;
+       struct skinny_addon *a;
        int i;
 
        switch (d->type) {
@@ -1310,12 +1463,16 @@ static void *get_button_template(struct skinnysession *s, struct button_definiti
                case SKINNY_DEVICE_7960:
                case SKINNY_DEVICE_7961:
                case SKINNY_DEVICE_7961GE:
+               case SKINNY_DEVICE_7962:
+               case SKINNY_DEVICE_7965:
                        for (i = 0; i < 6; i++)
                                (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
                        break;
                case SKINNY_DEVICE_7940:
                case SKINNY_DEVICE_7941:
                case SKINNY_DEVICE_7941GE:
+               case SKINNY_DEVICE_7942:
+               case SKINNY_DEVICE_7945:
                        for (i = 0; i < 2; i++)
                                (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
                        break;
@@ -1329,6 +1486,7 @@ static void *get_button_template(struct skinnysession *s, struct button_definiti
                        break;
                case SKINNY_DEVICE_7970:
                case SKINNY_DEVICE_7971:
+               case SKINNY_DEVICE_7975:
                case SKINNY_DEVICE_CIPC:
                        for (i = 0; i < 8; i++)
                                (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
@@ -1344,14 +1502,29 @@ static void *get_button_template(struct skinnysession *s, struct button_definiti
                        (btn++)->buttonDefinition = BT_HOLD;
                        break;
                case SKINNY_DEVICE_7920:
-               case SKINNY_DEVICE_7921:
                        /* XXX I don't know if this is right. */
                        for (i = 0; i < 4; i++)
                                (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
                        break;
+               case SKINNY_DEVICE_7921:
+                       for (i = 0; i < 6; i++)
+                               (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
+                       break;
                case SKINNY_DEVICE_7902:
                        ast_log(LOG_WARNING, "Unsupported device type '%d (7902)' found.\n", d->type);
                        break;
+               case SKINNY_DEVICE_7906:
+                       ast_log(LOG_WARNING, "Unsupported device type '%d (7906)' found.\n", d->type);
+                       break;
+               case SKINNY_DEVICE_7931:
+                       ast_log(LOG_WARNING, "Unsupported device type '%d (7931)' found.\n", d->type);
+                       break;
+               case SKINNY_DEVICE_7937:
+                       ast_log(LOG_WARNING, "Unsupported device type '%d (7937)' found.\n", d->type);
+                       break;
+               case SKINNY_DEVICE_7914:
+                       ast_log(LOG_WARNING, "Unsupported device type '%d (7914)' found.  Expansion module registered by itself?\n", d->type);
+                       break;
                case SKINNY_DEVICE_SCCPGATEWAY_AN:
                case SKINNY_DEVICE_SCCPGATEWAY_BRI:
                        ast_log(LOG_WARNING, "Unsupported device type '%d (SCCP gateway)' found.\n", d->type);
@@ -1361,7 +1534,8 @@ static void *get_button_template(struct skinnysession *s, struct button_definiti
                        break;
        }
 
-       for (a = d->addons; a; a = a->next) {
+       AST_LIST_LOCK(&d->addons);
+       AST_LIST_TRAVERSE(&d->addons, a, list) {
                if (!strcasecmp(a->type, "7914")) {
                        for (i = 0; i < 14; i++)
                                (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
@@ -1369,6 +1543,7 @@ static void *get_button_template(struct skinnysession *s, struct button_definiti
                        ast_log(LOG_WARNING, "Unknown addon type '%s' found.  Skipping.\n", a->type);
                }
        }
+       AST_LIST_UNLOCK(&d->addons);
 
        return btn;
 }
@@ -1396,7 +1571,7 @@ static struct skinny_line *find_line_by_instance(struct skinny_device *d, int in
        if (!instance)
                instance = 1;
 
-       for (l = d->lines; l; l = l->next) {
+       AST_LIST_TRAVERSE(&d->lines, l, list){
                if (l->instance == instance)
                        break;
        }
@@ -1426,32 +1601,32 @@ static struct skinny_line *find_line_by_name(const char *dest)
        if (!ast_strlen_zero(device))
                checkdevice = 1;
 
-       ast_mutex_lock(&devicelock);
-       for (d = devices; d; d = d->next) {
+       AST_LIST_LOCK(&devices);
+       AST_LIST_TRAVERSE(&devices, d, list){
                if (checkdevice && tmpl)
                        break;
                else if (!checkdevice) {
                        /* This is a match, since we're checking for line on every device. */
                } else if (!strcasecmp(d->name, device)) {
                        if (skinnydebug)
-                               ast_verbose(VERBOSE_PREFIX_2 "Found device: %s\n", d->name);
+                               ast_verb(2, "Found device: %s\n", d->name);
                } else
                        continue;
 
                /* Found the device (or we don't care which device) */
-               for (l = d->lines; l; l = l->next) {
+               AST_LIST_TRAVERSE(&d->lines, l, list){
                        /* Search for the right line */
                        if (!strcasecmp(l->name, line)) {
                                if (tmpl) {
-                                       ast_verbose(VERBOSE_PREFIX_2 "Ambiguous line name: %s\n", line);
-                                       ast_mutex_unlock(&devicelock);
+                                       ast_verb(2, "Ambiguous line name: %s\n", line);
+                                       AST_LIST_UNLOCK(&devices);
                                        return NULL;
                                } else
                                        tmpl = l;
                        }
                }
        }
-       ast_mutex_unlock(&devicelock);
+       AST_LIST_UNLOCK(&devices);
        return tmpl;
 }
 
@@ -1487,9 +1662,9 @@ static struct skinny_subchannel *find_subchannel_by_instance_reference(struct sk
           sub-channel on the list.
            This MIGHT need more love to be right */
        if (!reference)
-               sub = l->sub;
+               sub = AST_LIST_FIRST(&l->sub);
        else {
-               for (sub = l->sub; sub; sub = sub->next) {
+               AST_LIST_TRAVERSE(&l->sub, sub, list) {
                        if (sub->callid == reference)
                                break;
                }
@@ -1506,8 +1681,8 @@ static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_devi
        struct skinny_line *l;
        struct skinny_subchannel *sub = NULL;
 
-       for (l = d->lines; l; l = l->next) {
-               for (sub = l->sub; sub; sub = sub->next) {
+       AST_LIST_TRAVERSE(&d->lines, l, list){
+               AST_LIST_TRAVERSE(&l->sub, sub, list){
                        if (sub->callid == reference)
                                break;
                }
@@ -1529,7 +1704,7 @@ static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device
 {
        struct skinny_speeddial *sd;
 
-       for (sd = d->speeddials; sd; sd = sd->next) {
+       AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                if (sd->isHint == isHint && sd->instance == instance)
                        break;
        }
@@ -1540,7 +1715,7 @@ static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device
        return sd;
 }
 
-static int codec_skinny2ast(enum skinny_codecs skinnycodec)
+static format_t codec_skinny2ast(enum skinny_codecs skinnycodec)
 {
        switch (skinnycodec) {
        case SKINNY_CODEC_ALAW:
@@ -1562,7 +1737,7 @@ static int codec_skinny2ast(enum skinny_codecs skinnycodec)
        }
 }
 
-static int codec_ast2skinny(int astcodec)
+static int codec_ast2skinny(format_t astcodec)
 {
        switch (astcodec) {
        case AST_FORMAT_ALAW:
@@ -1584,6 +1759,41 @@ static int codec_ast2skinny(int astcodec)
        }
 }
 
+static int set_callforwards(struct skinny_line *l, const char *cfwd, int cfwdtype)
+{
+       if (!l)
+               return 0;
+
+       if (!ast_strlen_zero(cfwd)) {
+               if (cfwdtype & SKINNY_CFWD_ALL) {
+                       l->cfwdtype |= SKINNY_CFWD_ALL;
+                       ast_copy_string(l->call_forward_all, cfwd, sizeof(l->call_forward_all));
+               }
+               if (cfwdtype & SKINNY_CFWD_BUSY) {
+                       l->cfwdtype |= SKINNY_CFWD_BUSY;
+                       ast_copy_string(l->call_forward_busy, cfwd, sizeof(l->call_forward_busy));
+               }
+               if (cfwdtype & SKINNY_CFWD_NOANSWER) {
+                       l->cfwdtype |= SKINNY_CFWD_NOANSWER;
+                       ast_copy_string(l->call_forward_noanswer, cfwd, sizeof(l->call_forward_noanswer));
+               }
+       } else {
+               if (cfwdtype & SKINNY_CFWD_ALL) {
+                       l->cfwdtype &= ~SKINNY_CFWD_ALL;
+                       memset(l->call_forward_all, 0, sizeof(l->call_forward_all));
+               }
+               if (cfwdtype & SKINNY_CFWD_BUSY) {
+                       l->cfwdtype &= ~SKINNY_CFWD_BUSY;
+                       memset(l->call_forward_busy, 0, sizeof(l->call_forward_busy));
+               }
+               if (cfwdtype & SKINNY_CFWD_NOANSWER) {
+                       l->cfwdtype &= ~SKINNY_CFWD_NOANSWER;
+                       memset(l->call_forward_noanswer, 0, sizeof(l->call_forward_noanswer));
+               }
+       }
+       return l->cfwdtype;
+}
+
 static void cleanup_stale_contexts(char *new, char *old)
 {
        char *oldcontext, *newcontext, *stalecontext, *stringp, newlist[AST_MAX_CONTEXT];
@@ -1619,7 +1829,7 @@ static void register_exten(struct skinny_line *l)
        stringp = multi;
        while ((ext = strsep(&stringp, "&"))) {
                if ((context = strchr(ext, '@'))) {
-                       *context++ = '\0';      /* split ext@context */
+                       *context++ = '\0'; /* split ext@context */
                        if (!ast_context_find(context)) {
                                ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
                                continue;
@@ -1644,7 +1854,7 @@ static void unregister_exten(struct skinny_line *l)
        stringp = multi;
        while ((ext = strsep(&stringp, "&"))) {
                if ((context = strchr(ext, '@'))) {
-                       *context++ = '\0';      /* split ext@context */
+                       *context++ = '\0'; /* split ext@context */
                        if (!ast_context_find(context)) {
                                ast_log(LOG_WARNING, "Context %s must exist in regcontext= in skinny.conf!\n", context);
                                continue;
@@ -1663,9 +1873,10 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
        struct skinny_speeddial *sd;
        struct sockaddr_in sin;
        socklen_t slen;
+       int instance;
 
-       ast_mutex_lock(&devicelock);
-       for (d = devices; d; d = d->next) {
+       AST_LIST_LOCK(&devices);
+       AST_LIST_TRAVERSE(&devices, d, list){
                if (!strcasecmp(req->data.reg.name, d->id)
                                && ast_apply_ha(d->ha, &(s->sin))) {
                        s->device = d;
@@ -1683,17 +1894,42 @@ static int skinny_register(struct skinny_req *req, struct skinnysession *s)
                        }
                        d->ourip = sin.sin_addr;
 
-                       for (sd = d->speeddials; sd; sd = sd->next) {
+                       AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                                sd->stateid = ast_extension_state_add(sd->context, sd->exten, skinny_extensionstate_cb, sd);
                        }
-                       for (l = d->lines; l; l = l->next) {
-                               register_exten(l);
-                               ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+                       instance = 0;
+                       AST_LIST_TRAVERSE(&d->lines, l, list) {
+                               instance++;
+                       }
+                       AST_LIST_TRAVERSE(&d->lines, l, list) {
+                               /* FIXME: All sorts of issues will occur if this line is already connected to a device */
+                               if (l->device) {
+                                       manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Rejected\r\nCause: LINE_ALREADY_CONNECTED\r\n", l->name, l->device->name); 
+                                       ast_verb(1, "Line %s already connected to %s. Not connecting to %s.\n", l->name, l->device->name, d->name);
+                               } else {
+                                       l->device = d;
+                                       l->capability = l->confcapability & d->capability;
+                                       l->prefs = l->confprefs;
+                                       if (!l->prefs.order[0]) {
+                                               l->prefs = d->confprefs;
+                                       }
+                                       /* l->capability = d->capability;
+                                       l->prefs = d->prefs; */
+                                       l->instance = instance;
+                                       l->newmsgs = ast_app_has_voicemail(l->mailbox, NULL);
+                                       set_callforwards(l, NULL, 0);
+                                       manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Registered\r\n", l->name, d->name);
+                                       register_exten(l);
+                                       /* initialize MWI on line and device */
+                                       mwi_event_cb(0, l);
+                                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
+                               }
+                               --instance;
                        }
                        break;
                }
        }
-       ast_mutex_unlock(&devicelock);
+       AST_LIST_UNLOCK(&devices);
        if (!d) {
                return 0;
        }
@@ -1712,44 +1948,183 @@ static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
                d->session = NULL;
                d->registered = 0;
 
-               for (sd = d->speeddials; sd; sd = sd->next) {
+               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                        if (sd->stateid > -1)
                                ast_extension_state_del(sd->stateid, NULL);
                }
-               for (l = d->lines; l; l = l->next) {
-                       unregister_exten(l);
-                       ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+               AST_LIST_TRAVERSE(&d->lines, l, list) {
+                       if (l->device == d) {
+                               l->device = NULL;
+                               l->capability = 0;
+                               ast_parse_allow_disallow(&l->prefs, &l->capability, "all", 0);                  
+                               l->instance = 0;
+                               manager_event(EVENT_FLAG_SYSTEM, "PeerStatus", "ChannelType: Skinny\r\nPeer: Skinny/%s@%s\r\nPeerStatus: Unregistered\r\n", l->name, d->name);
+                               unregister_exten(l);
+                               ast_devstate_changed(AST_DEVICE_UNAVAILABLE, "Skinny/%s@%s", l->name, d->name);
+                       }
                }
        }
 
        return -1; /* main loop will destroy the session */
 }
 
-static int transmit_response(struct skinnysession *s, struct skinny_req *req)
+#ifdef SKINNY_DEVMODE
+static char *message2str(int type)
+{
+       char *tmp;
+
+       switch (type) {
+       case KEEP_ALIVE_MESSAGE:
+               return "KEEP_ALIVE_MESSAGE";
+       case REGISTER_MESSAGE:
+               return "REGISTER_MESSAGE";
+       case IP_PORT_MESSAGE:
+               return "IP_PORT_MESSAGE";
+       case KEYPAD_BUTTON_MESSAGE:
+               return "KEYPAD_BUTTON_MESSAGE";
+       case ENBLOC_CALL_MESSAGE:
+               return "ENBLOC_CALL_MESSAGE";
+       case STIMULUS_MESSAGE:
+               return "STIMULUS_MESSAGE";
+       case OFFHOOK_MESSAGE:
+               return "OFFHOOK_MESSAGE";
+       case ONHOOK_MESSAGE:
+               return "ONHOOK_MESSAGE";
+       case CAPABILITIES_RES_MESSAGE:
+               return "CAPABILITIES_RES_MESSAGE";
+       case SPEED_DIAL_STAT_REQ_MESSAGE:
+               return "SPEED_DIAL_STAT_REQ_MESSAGE";
+       case LINE_STATE_REQ_MESSAGE:
+               return "LINE_STATE_REQ_MESSAGE";
+       case TIME_DATE_REQ_MESSAGE:
+               return "TIME_DATE_REQ_MESSAGE";
+       case BUTTON_TEMPLATE_REQ_MESSAGE:
+               return "BUTTON_TEMPLATE_REQ_MESSAGE";
+       case VERSION_REQ_MESSAGE:
+               return "VERSION_REQ_MESSAGE";
+       case SERVER_REQUEST_MESSAGE:
+               return "SERVER_REQUEST_MESSAGE";
+       case ALARM_MESSAGE:
+               return "ALARM_MESSAGE";
+       case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
+               return "OPEN_RECEIVE_CHANNEL_ACK_MESSAGE";
+       case SOFT_KEY_SET_REQ_MESSAGE:
+               return "SOFT_KEY_SET_REQ_MESSAGE";
+       case SOFT_KEY_EVENT_MESSAGE:
+               return "SOFT_KEY_EVENT_MESSAGE";
+       case UNREGISTER_MESSAGE:
+               return "UNREGISTER_MESSAGE";
+       case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
+               return "SOFT_KEY_TEMPLATE_REQ_MESSAGE";
+       case HEADSET_STATUS_MESSAGE:
+               return "HEADSET_STATUS_MESSAGE";
+       case REGISTER_AVAILABLE_LINES_MESSAGE:
+               return "REGISTER_AVAILABLE_LINES_MESSAGE";
+       case REGISTER_ACK_MESSAGE:
+               return "REGISTER_ACK_MESSAGE";
+       case START_TONE_MESSAGE:
+               return "START_TONE_MESSAGE";
+       case STOP_TONE_MESSAGE:
+               return "STOP_TONE_MESSAGE";
+       case SET_RINGER_MESSAGE:
+               return "SET_RINGER_MESSAGE";
+       case SET_LAMP_MESSAGE:
+               return "SET_LAMP_MESSAGE";
+       case SET_SPEAKER_MESSAGE:
+               return "SET_SPEAKER_MESSAGE";
+       case SET_MICROPHONE_MESSAGE:
+               return "SET_MICROPHONE_MESSAGE";
+       case START_MEDIA_TRANSMISSION_MESSAGE:
+               return "START_MEDIA_TRANSMISSION_MESSAGE";
+       case STOP_MEDIA_TRANSMISSION_MESSAGE:
+               return "STOP_MEDIA_TRANSMISSION_MESSAGE";
+       case CALL_INFO_MESSAGE:
+               return "CALL_INFO_MESSAGE";
+       case FORWARD_STAT_MESSAGE:
+               return "FORWARD_STAT_MESSAGE";
+       case SPEED_DIAL_STAT_RES_MESSAGE:
+               return "SPEED_DIAL_STAT_RES_MESSAGE";
+       case LINE_STAT_RES_MESSAGE:
+               return "LINE_STAT_RES_MESSAGE";
+       case DEFINETIMEDATE_MESSAGE:
+               return "DEFINETIMEDATE_MESSAGE";
+       case BUTTON_TEMPLATE_RES_MESSAGE:
+               return "BUTTON_TEMPLATE_RES_MESSAGE";
+       case VERSION_RES_MESSAGE:
+               return "VERSION_RES_MESSAGE";
+       case DISPLAYTEXT_MESSAGE:
+               return "DISPLAYTEXT_MESSAGE";
+       case CLEAR_NOTIFY_MESSAGE:
+               return "CLEAR_NOTIFY_MESSAGE";
+       case CLEAR_DISPLAY_MESSAGE:
+               return "CLEAR_DISPLAY_MESSAGE";
+       case CAPABILITIES_REQ_MESSAGE:
+               return "CAPABILITIES_REQ_MESSAGE";
+       case REGISTER_REJ_MESSAGE:
+               return "REGISTER_REJ_MESSAGE";
+       case SERVER_RES_MESSAGE:
+               return "SERVER_RES_MESSAGE";
+       case RESET_MESSAGE:
+               return "RESET_MESSAGE";
+       case KEEP_ALIVE_ACK_MESSAGE:
+               return "KEEP_ALIVE_ACK_MESSAGE";
+       case OPEN_RECEIVE_CHANNEL_MESSAGE:
+               return "OPEN_RECEIVE_CHANNEL_MESSAGE";
+       case CLOSE_RECEIVE_CHANNEL_MESSAGE:
+               return "CLOSE_RECEIVE_CHANNEL_MESSAGE";
+       case SOFT_KEY_TEMPLATE_RES_MESSAGE:
+               return "SOFT_KEY_TEMPLATE_RES_MESSAGE";
+       case SOFT_KEY_SET_RES_MESSAGE:
+               return "SOFT_KEY_SET_RES_MESSAGE";
+       case SELECT_SOFT_KEYS_MESSAGE:
+               return "SELECT_SOFT_KEYS_MESSAGE";
+       case CALL_STATE_MESSAGE:
+               return "CALL_STATE_MESSAGE";
+       case DISPLAY_PROMPT_STATUS_MESSAGE:
+               return "DISPLAY_PROMPT_STATUS_MESSAGE";
+       case CLEAR_PROMPT_MESSAGE:
+               return "CLEAR_PROMPT_MESSAGE";
+       case DISPLAY_NOTIFY_MESSAGE:
+               return "DISPLAY_NOTIFY_MESSAGE";
+       case ACTIVATE_CALL_PLANE_MESSAGE:
+               return "ACTIVATE_CALL_PLANE_MESSAGE";
+       case DIALED_NUMBER_MESSAGE:
+               return "DIALED_NUMBER_MESSAGE";
+       default:
+               if (!(tmp = ast_threadstorage_get(&message2str_threadbuf, MESSAGE2STR_BUFSIZE)))
+                       return "Unknown";
+               snprintf(tmp, MESSAGE2STR_BUFSIZE, "UNKNOWN_MESSAGE-%d", type);
+               return tmp;
+       }
+}
+#endif
+
+static int transmit_response(struct skinny_device *d, struct skinny_req *req)
 {
+       struct skinnysession *s = d->session;
        int res = 0;
 
        if (!s) {
-               ast_log(LOG_WARNING, "Asked to transmit to a non-existant session!\n");
+               ast_log(LOG_WARNING, "Asked to transmit to a non-existent session!\n");
                return -1;
        }
 
        ast_mutex_lock(&s->lock);
 
-       if (skinnydebug)
-               ast_log(LOG_VERBOSE, "writing packet type %04X (%d bytes) to socket %d\n", letohl(req->e), letohl(req->len)+8, s->fd);
+       SKINNY_DEVONLY(if (skinnydebug>1) ast_verb(4, "Transmitting %s to %s\n", message2str(req->e), d->name);)
 
        if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
                ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
+               ast_mutex_unlock(&s->lock);
                return -1;
        }
 
-       memset(s->outbuf,0,sizeof(s->outbuf));
+       memset(s->outbuf, 0, sizeof(s->outbuf));
        memcpy(s->outbuf, req, skinny_header_size);
        memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
 
        res = write(s->fd, s->outbuf, letohl(req->len)+8);
-
+       
        if (res != letohl(req->len)+8) {
                ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
                if (res == -1) {
@@ -1760,11 +2135,12 @@ static int transmit_response(struct skinnysession *s, struct skinny_req *req)
                
        }
        
+       ast_free(req);
        ast_mutex_unlock(&s->lock);
        return 1;
 }
 
-static void transmit_speaker_mode(struct skinnysession *s, int mode)
+static void transmit_speaker_mode(struct skinny_device *d, int mode)
 {
        struct skinny_req *req;
 
@@ -1772,10 +2148,10 @@ static void transmit_speaker_mode(struct skinnysession *s, int mode)
                return;
 
        req->data.setspeaker.mode = htolel(mode);
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 /*
-static void transmit_microphone_mode(struct skinnysession *s, int mode)
+static void transmit_microphone_mode(struct skinny_device *d, int mode)
 {
        struct skinny_req *req;
 
@@ -1783,19 +2159,23 @@ static void transmit_microphone_mode(struct skinnysession *s, int mode)
                return;
 
        req->data.setmicrophone.mode = htolel(mode);
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 */
 
-static void transmit_callinfo(struct skinnysession *s, const char *fromname, const char *fromnum, const char *toname, const char *tonum, int instance, int callid, int calltype)
+static void transmit_callinfo(struct skinny_device *d, const char *fromname, const char *fromnum, const char *toname, const char *tonum, int instance, int callid, int calltype)
 {
        struct skinny_req *req;
 
+       /* We should not be able to get here without a device */
+       if (!d)
+               return;
+
        if (!(req = req_alloc(sizeof(struct call_info_message), CALL_INFO_MESSAGE)))
                return;
 
        if (skinnydebug)
-                       ast_verbose("Setting Callinfo to %s(%s) from %s(%s) on %s(%d)\n", fromname, fromnum, toname, tonum, s->device->name, instance);
+                       ast_verb(1, "Setting Callinfo to %s(%s) from %s(%s) on %s(%d)\n", fromname, fromnum, toname, tonum, d->name, instance);
 
        if (fromname) {
                ast_copy_string(req->data.callinfo.callingPartyName, fromname, sizeof(req->data.callinfo.callingPartyName));
@@ -1812,10 +2192,10 @@ static void transmit_callinfo(struct skinnysession *s, const char *fromname, con
        req->data.callinfo.instance = htolel(instance);
        req->data.callinfo.reference = htolel(callid);
        req->data.callinfo.type = htolel(calltype);
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_connect(struct skinnysession *s, struct skinny_subchannel *sub)
+static void transmit_connect(struct skinny_device *d, struct skinny_subchannel *sub)
 {
        struct skinny_req *req;
        struct skinny_line *l = sub->parent;
@@ -1832,10 +2212,10 @@ static void transmit_connect(struct skinnysession *s, struct skinny_subchannel *
        req->data.openreceivechannel.capability = htolel(codec_ast2skinny(fmt.bits));
        req->data.openreceivechannel.echo = htolel(0);
        req->data.openreceivechannel.bitrate = htolel(0);
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_tone(struct skinnysession *s, int tone, int instance, int reference)
+static void transmit_tone(struct skinny_device *d, int tone, int instance, int reference)
 {
        struct skinny_req *req;
 
@@ -1857,13 +2237,15 @@ static void transmit_tone(struct skinnysession *s, int tone, int instance, int r
                req->data.stoptone.reference = htolel(reference);
        }
 
-       if (tone > 0) {
-               req->data.starttone.tone = htolel(tone);
-       }
-       transmit_response(s, req);
+       //Bad, tone is already set so this is redundant and a change to the if above
+       //may lead to issues where we try to set a tone to a stop_tone_message
+       //if (tone > 0) {
+       //      req->data.starttone.tone = htolel(tone);
+       //}
+       transmit_response(d, req);
 }
 
-static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int callid, int softkey)
+static void transmit_selectsoftkeys(struct skinny_device *d, int instance, int callid, int softkey)
 {
        struct skinny_req *req;
 
@@ -1874,10 +2256,10 @@ static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int c
        req->data.selectsoftkey.reference = htolel(callid);
        req->data.selectsoftkey.softKeySetIndex = htolel(softkey);
        req->data.selectsoftkey.validKeyMask = htolel(0xFFFFFFFF);
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_lamp_indication(struct skinnysession *s, int stimulus, int instance, int indication)
+static void transmit_lamp_indication(struct skinny_device *d, int stimulus, int instance, int indication)
 {
        struct skinny_req *req;
 
@@ -1887,15 +2269,15 @@ static void transmit_lamp_indication(struct skinnysession *s, int stimulus, int
        req->data.setlamp.stimulus = htolel(stimulus);
        req->data.setlamp.stimulusInstance = htolel(instance);
        req->data.setlamp.deviceStimulus = htolel(indication);
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_ringer_mode(struct skinnysession *s, int mode)
+static void transmit_ringer_mode(struct skinny_device *d, int mode)
 {
        struct skinny_req *req;
 
        if (skinnydebug)
-               ast_verbose("Setting ringer mode to '%d'.\n", mode);
+               ast_verb(1, "Setting ringer mode to '%d'.\n", mode);
 
        if (!(req = req_alloc(sizeof(struct set_ringer_message), SET_RINGER_MESSAGE)))
                return;
@@ -1912,10 +2294,10 @@ static void transmit_ringer_mode(struct skinnysession *s, int mode)
        /* XXX the value here doesn't seem to change anything.  Must be higher than 0.
           Perhaps a packet capture can shed some light on this. */
        req->data.setringer.unknown2 = htolel(1);
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_displaymessage(struct skinnysession *s, const char *text, int instance, int reference)
+static void transmit_displaymessage(struct skinny_device *d, const char *text, int instance, int reference)
 {
        struct skinny_req *req;
 
@@ -1923,24 +2305,29 @@ static void transmit_displaymessage(struct skinnysession *s, const char *text, i
                if (!(req = req_alloc(0, CLEAR_DISPLAY_MESSAGE)))
                        return;
 
-               req->data.clearpromptstatus.lineInstance = instance;
-               req->data.clearpromptstatus.callReference = reference;
+               //what do we want hear CLEAR_DISPLAY_MESSAGE or CLEAR_PROMPT_STATUS???
+               //if we are clearing the display, it appears there is no instance and refernece info (size 0)
+               //req->data.clearpromptstatus.lineInstance = instance;
+               //req->data.clearpromptstatus.callReference = reference;
+
+               /* send datetime message. We have to do it here because it will clear the display on the phone if we do it elsewhere */
+               handle_time_date_req_message(NULL, d->session);
 
                if (skinnydebug)
-                       ast_verbose("Clearing Display\n");
+                       ast_verb(1, "Clearing Display\n");
        } else {
                if (!(req = req_alloc(sizeof(struct displaytext_message), DISPLAYTEXT_MESSAGE)))
                        return;
 
                ast_copy_string(req->data.displaytext.text, text, sizeof(req->data.displaytext.text));
                if (skinnydebug)
-                       ast_verbose("Displaying message '%s'\n", req->data.displaytext.text);
+                       ast_verb(1, "Displaying message '%s'\n", req->data.displaytext.text);
        }
 
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_displaynotify(struct skinnysession *s, const char *text, int t)
+static void transmit_displaynotify(struct skinny_device *d, const char *text, int t)
 {
        struct skinny_req *req;
 
@@ -1951,12 +2338,12 @@ static void transmit_displaynotify(struct skinnysession *s, const char *text, in
        req->data.displaynotify.displayTimeout = htolel(t);
 
        if (skinnydebug)
-               ast_verbose("Displaying notify '%s'\n", text);
+               ast_verb(1, "Displaying notify '%s'\n", text);
 
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_displaypromptstatus(struct skinnysession *s, const char *text, int t, int instance, int callid)
+static void transmit_displaypromptstatus(struct skinny_device *d, const char *text, int t, int instance, int callid)
 {
        struct skinny_req *req;
 
@@ -1968,7 +2355,7 @@ static void transmit_displaypromptstatus(struct skinnysession *s, const char *te
                req->data.clearpromptstatus.callReference = htolel(callid);
 
                if (skinnydebug)
-                       ast_verbose("Clearing Prompt\n");
+                       ast_verb(1, "Clearing Prompt\n");
        } else {
                if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE)))
                        return;
@@ -1979,13 +2366,13 @@ static void transmit_displaypromptstatus(struct skinnysession *s, const char *te
                req->data.displaypromptstatus.callReference = htolel(callid);
 
                if (skinnydebug)
-                       ast_verbose("Displaying Prompt Status '%s'\n", text);
+                       ast_verb(1, "Displaying Prompt Status '%s'\n", text);
        }
 
-       transmit_response(s, req);
+       transmit_response(d, req);
 }
 
-static void transmit_dialednumber(struct skinnysession *s, const char *text, int instance, int callid)
+static void transmit_dialednumber(struct skinny_device *d, const char *text, int instance, int callid)
 {
        struct skinny_req *req;
 
@@ -1996,10 +2383,58 @@ static void transmit_dialednumber(struct skinnysession *s, const char *text, int
        req->data.dialednumber.lineInstance = htolel(instance);
        req->data.dialednumber.callReference = htolel(callid);
 
-       transmit_response(s, req);
+       transmit_response(d, req);
+}
+
+static void transmit_closereceivechannel(struct skinny_device *d, struct skinny_subchannel *sub)
+{
+       struct skinny_req *req;
+
+       if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
+               return;
+
+       req->data.closereceivechannel.conferenceId = htolel(0);
+       req->data.closereceivechannel.partyId = htolel(sub->callid);
+       transmit_response(d, req);
+}
+
+static void transmit_stopmediatransmission(struct skinny_device *d, struct skinny_subchannel *sub)
+{
+       struct skinny_req *req;
+
+       if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
+               return;
+
+       req->data.stopmedia.conferenceId = htolel(0);
+       req->data.stopmedia.passThruPartyId = htolel(sub->callid);
+       transmit_response(d, req);
+}
+
+static void transmit_activatecallplane(struct skinny_device *d, struct skinny_line *l)
+{
+       struct skinny_req *req;
+
+       if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
+               return;
+
+       req->data.activatecallplane.lineInstance = htolel(l->instance);
+       transmit_response(d, req);
+}
+
+static void transmit_callstateonly(struct skinny_device *d, struct skinny_subchannel *sub, int state)
+{
+       struct skinny_req *req;
+
+       if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
+               return;
+
+       req->data.callstate.callState = htolel(state);
+       req->data.callstate.lineInstance = htolel(sub->parent->instance);
+       req->data.callstate.callReference = htolel(sub->callid);
+       transmit_response(d, req);
 }
 
-static void transmit_callstate(struct skinnysession *s, int instance, int state, unsigned callid)
+static void transmit_callstate(struct skinny_device *d, int instance, int state, unsigned callid)
 {
        struct skinny_req *req;
 
@@ -2009,18 +2444,18 @@ static void transmit_callstate(struct skinnysession *s, int instance, int state,
 
                req->data.closereceivechannel.conferenceId = htolel(callid);
                req->data.closereceivechannel.partyId = htolel(callid);
-               transmit_response(s, req);
+               transmit_response(d, req);
 
                if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
                        return;
 
                req->data.stopmedia.conferenceId = htolel(callid);
                req->data.stopmedia.passThruPartyId = htolel(callid);
-               transmit_response(s, req);
+               transmit_response(d, req);
 
-               transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
+               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
 
-               transmit_displaypromptstatus(s, NULL, 0, instance, callid);
+               transmit_displaypromptstatus(d, NULL, 0, instance, callid);
        }
 
        if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
@@ -2029,10 +2464,10 @@ static void transmit_callstate(struct skinnysession *s, int instance, int state,
        req->data.callstate.callState = htolel(state);
        req->data.callstate.lineInstance = htolel(instance);
        req->data.callstate.callReference = htolel(callid);
-       transmit_response(s, req);
+       transmit_response(d, req);
 
        if (state == SKINNY_ONHOOK) {
-               transmit_selectsoftkeys(s, 0, 0, KEYDEF_ONHOOK);
+               transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
        }
 
        if (state == SKINNY_OFFHOOK || state == SKINNY_ONHOOK) {
@@ -2040,15 +2475,59 @@ static void transmit_callstate(struct skinnysession *s, int instance, int state,
                        return;
 
                req->data.activatecallplane.lineInstance = htolel(instance);
-               transmit_response(s, req);
+               transmit_response(d, req);
+       }
+}
+
+
+static void transmit_cfwdstate(struct skinny_device *d, struct skinny_line *l)
+{
+       struct skinny_req *req;
+       int anyon = 0;
+
+       if (!(req = req_alloc(sizeof(struct forward_stat_message), FORWARD_STAT_MESSAGE)))
+               return;
+
+       if (l->cfwdtype & SKINNY_CFWD_ALL) {
+               if (!ast_strlen_zero(l->call_forward_all)) {
+                       ast_copy_string(req->data.forwardstat.fwdallnum, l->call_forward_all, sizeof(req->data.forwardstat.fwdallnum));
+                       req->data.forwardstat.fwdall = htolel(1);
+                       anyon++;
+               } else {
+                       req->data.forwardstat.fwdall = htolel(0);
+               }
+       }
+       if (l->cfwdtype & SKINNY_CFWD_BUSY) {
+               if (!ast_strlen_zero(l->call_forward_busy)) {
+                       ast_copy_string(req->data.forwardstat.fwdbusynum, l->call_forward_busy, sizeof(req->data.forwardstat.fwdbusynum));
+                       req->data.forwardstat.fwdbusy = htolel(1);
+                       anyon++;
+               } else {
+                       req->data.forwardstat.fwdbusy = htolel(0);
+               }
+       }
+       if (l->cfwdtype & SKINNY_CFWD_NOANSWER) {
+               if (!ast_strlen_zero(l->call_forward_noanswer)) {
+                       ast_copy_string(req->data.forwardstat.fwdnoanswernum, l->call_forward_noanswer, sizeof(req->data.forwardstat.fwdnoanswernum));
+                       req->data.forwardstat.fwdnoanswer = htolel(1);
+                       anyon++;
+               } else {
+                       req->data.forwardstat.fwdnoanswer = htolel(0);
+               }
        }
+       req->data.forwardstat.lineNumber = htolel(l->instance);
+       if (anyon)
+               req->data.forwardstat.activeforward = htolel(7);
+       else
+               req->data.forwardstat.activeforward = htolel(0);
+
+       transmit_response(d, req);
 }
 
 static int skinny_extensionstate_cb(char *context, char *exten, int state, void *data)
 {
        struct skinny_speeddial *sd = data;
        struct skinny_device *d = sd->parent;
-       struct skinnysession *s = d->session;
        char hint[AST_MAX_EXTENSION];
        int callstate = SKINNY_CALLREMOTEMULTILINE;
        int lamp = SKINNY_LAMP_OFF;
@@ -2090,93 +2569,133 @@ static int skinny_extensionstate_cb(char *context, char *exten, int state, void
                }
        }
 
-       transmit_lamp_indication(s, STIMULUS_LINE, sd->instance, lamp);
-       transmit_callstate(s, sd->instance, callstate, 0);
+       transmit_lamp_indication(d, STIMULUS_LINE, sd->instance, lamp);
+       transmit_callstate(d, sd->instance, callstate, 0);
        sd->laststate = state;
 
        return 0;
 }
 
-static int has_voicemail(struct skinny_line *l)
+static void update_connectedline(struct skinny_subchannel *sub, const void *data, size_t datalen)
 {
-       return ast_app_has_voicemail(l->mailbox, NULL);
+       struct ast_channel *c = sub->owner;
+       struct skinny_line *l = sub->parent;
+       struct skinny_device *d = l->device;
+
+       if (ast_strlen_zero(c->cid.cid_num) || ast_strlen_zero(c->connected.id.number))
+               return;
+
+       if (sub->owner->_state == AST_STATE_UP) {
+               transmit_callstate(d, l->instance, SKINNY_CONNECTED, sub->callid);
+               transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
+               if (sub->outgoing)
+                       transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
+               else
+                       transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2);
+       } else {
+               if (sub->outgoing) {
+                       transmit_callstate(d, l->instance, SKINNY_RINGIN, sub->callid);
+                       transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
+                       transmit_callinfo(d, c->connected.id.name, c->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
+               } else {
+                       if (!sub->ringing) {
+                               transmit_callstate(d, l->instance, SKINNY_RINGOUT, sub->callid);
+                               transmit_displaypromptstatus(d, "Ring-Out", 0, l->instance, sub->callid);
+                               sub->ringing = 1;
+                       } else {
+                               transmit_callstate(d, l->instance, SKINNY_PROGRESS, sub->callid);
+                               transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
+                               sub->progress = 1;
+                       }
+
+                       transmit_callinfo(d, l->cid_name, l->cid_num, c->connected.id.name, c->connected.id.number, l->instance, sub->callid, 2);
+               }
+       }
 }
 
-static void do_housekeeping(struct skinnysession *s)
+static void mwi_event_cb(const struct ast_event *event, void *userdata)
 {
-       int new;
-       int old;
-       int device_lamp = 0;
-       struct skinny_device *d = s->device;
-       struct skinny_line *l;
+       struct skinny_line *l = userdata;
+       struct skinny_device *d = l->device;
+       if (d) {
+               struct skinnysession *s = d->session;
+               struct skinny_line *l2;
+               int new_msgs = 0;
+               int dev_msgs = 0;
+
+               if (s) {
+                       if (event) {
+                               l->newmsgs = ast_event_get_ie_uint(event, AST_EVENT_IE_NEWMSGS);
+                       }
 
-       /* Update time on device */
-       handle_time_date_req_message(NULL, s);
+                       if (l->newmsgs) {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
+                       } else {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
+                       }
 
-       /* Set MWI on individual lines */
-       for (l = d->lines; l; l = l->next) {
-               if (has_voicemail(l)) {
-                       if (skinnydebug)
-                               ast_verbose("Checking for voicemail Skinny %s@%s\n", l->name, d->name);
-                       ast_app_inboxcount(l->mailbox, &new, &old);
-                       if (skinnydebug)
-                               ast_verbose("Skinny %s@%s has voicemail!\n", l->name, d->name);
-                       transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
-                       device_lamp++;
-               } else {
-                       transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
+                       /* find out wether the device lamp should be on or off */
+                       AST_LIST_TRAVERSE(&d->lines, l2, list) {
+                               if (l2->newmsgs) {
+                                       dev_msgs++;
+                               }
+                       }
+
+                       if (dev_msgs) {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, d->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
+                       } else {
+                               transmit_lamp_indication(d, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
+                       }
+                       ast_verb(3, "Skinny mwi_event_cb found %d new messages\n", new_msgs);
                }
        }
-       /* If at least one line has VM, turn the device level lamp on */
-       if (device_lamp)
-               transmit_lamp_indication(s, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_ON);
-       else
-               transmit_lamp_indication(s, STIMULUS_VOICEMAIL, 0, SKINNY_LAMP_OFF);
 }
 
 /* I do not believe skinny can deal with video.
    Anyone know differently? */
 /* Yes, it can.  Currently 7985 and Cisco VT Advantage do video. */
-static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
+static enum ast_rtp_glue_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
 {
        struct skinny_subchannel *sub = NULL;
 
        if (!(sub = c->tech_pvt) || !(sub->vrtp))
-               return AST_RTP_GET_FAILED;
+               return AST_RTP_GLUE_RESULT_FORBID;
 
-       *rtp = sub->vrtp;
+       ao2_ref(sub->vrtp, +1);
+       *instance = sub->vrtp;
 
-       return AST_RTP_TRY_NATIVE;
+       return AST_RTP_GLUE_RESULT_REMOTE;
 }
 
-static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
+static enum ast_rtp_glue_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp_instance **instance)
 {
        struct skinny_subchannel *sub = NULL;
        struct skinny_line *l;
-       enum ast_rtp_get_result res = AST_RTP_TRY_NATIVE;
+       enum ast_rtp_glue_result res = AST_RTP_GLUE_RESULT_REMOTE;
 
        if (skinnydebug)
-               ast_verbose("skinny_get_rtp_peer() Channel = %s\n", c->name);
+               ast_verb(1, "skinny_get_rtp_peer() Channel = %s\n", c->name);
 
 
        if (!(sub = c->tech_pvt))
-               return AST_RTP_GET_FAILED;
+               return AST_RTP_GLUE_RESULT_FORBID;
 
        ast_mutex_lock(&sub->lock);
 
        if (!(sub->rtp)){
                ast_mutex_unlock(&sub->lock);
-               return AST_RTP_GET_FAILED;
+               return AST_RTP_GLUE_RESULT_FORBID;
        }
-       
-       *rtp = sub->rtp;
+
+       ao2_ref(sub->rtp, +1);
+       *instance = sub->rtp;
 
        l = sub->parent;
 
-       if (!l->canreinvite || l->nat){
-               res = AST_RTP_TRY_PARTIAL;
+       if (!l->directmedia || l->nat){
+               res = AST_RTP_GLUE_RESULT_LOCAL;
                if (skinnydebug)
-                       ast_verbose("skinny_get_rtp_peer() Using AST_RTP_TRY_PARTIAL \n");
+                       ast_verb(1, "skinny_get_rtp_peer() Using AST_RTP_GLUE_RESULT_LOCAL \n");
        }
 
        ast_mutex_unlock(&sub->lock);
@@ -2185,15 +2704,15 @@ static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct
 
 }
 
-static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, struct ast_rtp *trtp, int codecs, int nat_active)
+static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *trtp, format_t codecs, int nat_active)
 {
        struct skinny_subchannel *sub;
        struct skinny_line *l;
        struct skinny_device *d;
        struct skinnysession *s;
        struct ast_format_list fmt;
-       struct sockaddr_in us;
-       struct sockaddr_in them;
+       struct sockaddr_in us = { 0, };
+       struct sockaddr_in them = { 0, };
        struct skinny_req *req;
        
        sub = c->tech_pvt;
@@ -2206,11 +2725,11 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
        }
 
        l = sub->parent;
-       d = l->parent;
+       d = l->device;
        s = d->session;
 
        if (rtp){
-               ast_rtp_get_peer(rtp, &them);
+               ast_rtp_instance_get_remote_address(rtp, &them);
 
                /* Shutdown any early-media or previous media on re-invite */
                if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
@@ -2218,10 +2737,10 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
 
                req->data.stopmedia.conferenceId = htolel(sub->callid);
                req->data.stopmedia.passThruPartyId = htolel(sub->callid);
-               transmit_response(s, req);
+               transmit_response(d, req);
 
                if (skinnydebug)
-                       ast_verbose("Peerip = %s:%d\n", ast_inet_ntoa(them.sin_addr), ntohs(them.sin_port));
+                       ast_verb(1, "Peerip = %s:%d\n", ast_inet_ntoa(them.sin_addr), ntohs(them.sin_port));
 
                if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
                        return -1;
@@ -2229,12 +2748,12 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
                fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
 
                if (skinnydebug)
-                       ast_verbose("Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
+                       ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
 
                req->data.startmedia.conferenceId = htolel(sub->callid);
                req->data.startmedia.passThruPartyId = htolel(sub->callid);
-               if (!(l->canreinvite) || (l->nat)){
-                       ast_rtp_get_us(rtp, &us);
+               if (!(l->directmedia) || (l->nat)){
+                       ast_rtp_instance_get_local_address(rtp, &us);
                        req->data.startmedia.remoteIp = htolel(d->ourip.s_addr);
                        req->data.startmedia.remotePort = htolel(ntohs(us.sin_port));
                } else {
@@ -2247,7 +2766,7 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
                req->data.startmedia.qualifier.vad = htolel(0);
                req->data.startmedia.qualifier.packets = htolel(0);
                req->data.startmedia.qualifier.bitRate = htolel(0);
-               transmit_response(s, req);
+               transmit_response(d, req);
 
                return 0;
        }
@@ -2255,31 +2774,74 @@ static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struc
        return 0;
 }
 
-static struct ast_rtp_protocol skinny_rtp = {
+static struct ast_rtp_glue skinny_rtp_glue = {
        .type = "Skinny",
        .get_rtp_info = skinny_get_rtp_peer,
        .get_vrtp_info = skinny_get_vrtp_peer,
-       .set_rtp_peer = skinny_set_rtp_peer,
+       .update_peer = skinny_set_rtp_peer,
 };
 
-static int skinny_do_debug(int fd, int argc, char *argv[])
+static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       if (argc != 3)
-               return RESULT_SHOWUSAGE;
-
-       skinnydebug = 1;
-       ast_cli(fd, "Skinny Debugging Enabled\n");
-       return RESULT_SUCCESS;
+       switch (cmd) {
+       case CLI_INIT:
+#ifdef SKINNY_DEVMODE
+               e->command = "skinny set debug {off|on|packet}";
+               e->usage =
+                       "Usage: skinny set debug {off|on|packet}\n"
+                       "       Enables/Disables dumping of Skinny packets for debugging purposes\n";
+#else
+               e->command = "skinny set debug {off|on}";
+               e->usage =
+                       "Usage: skinny set debug {off|on}\n"
+                       "       Enables/Disables dumping of Skinny packets for debugging purposes\n";
+#endif
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+       
+       if (a->argc != e->args)
+               return CLI_SHOWUSAGE;
+
+       if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
+               skinnydebug = 1;
+               ast_cli(a->fd, "Skinny Debugging Enabled\n");
+               return CLI_SUCCESS;
+       } else if (!strncasecmp(a->argv[e->args - 1], "off", 3)) {
+               skinnydebug = 0;
+               ast_cli(a->fd, "Skinny Debugging Disabled\n");
+               return CLI_SUCCESS;
+#ifdef SKINNY_DEVMODE
+       } else if (!strncasecmp(a->argv[e->args - 1], "packet", 6)) {
+               skinnydebug = 2;
+               ast_cli(a->fd, "Skinny Debugging Enabled including Packets\n");
+               return CLI_SUCCESS;
+#endif
+       } else {
+               return CLI_SHOWUSAGE;
+       }
 }
 
-static int skinny_no_debug(int fd, int argc, char *argv[])
+static char *handle_skinny_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       if (argc != 4)
-               return RESULT_SHOWUSAGE;
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny reload";
+               e->usage =
+                       "Usage: skinny reload\n"
+                       "       Reloads the chan_skinny configuration\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+       
+       if (a->argc != e->args)
+               return CLI_SHOWUSAGE;
+
+       skinny_reload();
+       return CLI_SUCCESS;
 
-       skinnydebug = 0;
-       ast_cli(fd, "Skinny Debugging Disabled\n");
-       return RESULT_SUCCESS;
 }
 
 static char *complete_skinny_devices(const char *word, int state)
@@ -2288,7 +2850,7 @@ static char *complete_skinny_devices(const char *word, int state)
        char *result = NULL;
        int wordlen = strlen(word), which = 0;
 
-       for (d = devices; d && !result; d = d->next) {
+       AST_LIST_TRAVERSE(&devices, d, list) {
                if (!strncasecmp(word, d->id, wordlen) && ++which > state)
                        result = ast_strdup(d->id);
        }
@@ -2306,7 +2868,7 @@ static char *complete_skinny_reset(const char *line, const char *word, int pos,
        return (pos == 2 ? ast_strdup(complete_skinny_devices(word, state)) : NULL);
 }
 
-static char *complete_skinny_show_lines(const char *line, const char *word, int pos, int state)
+static char *complete_skinny_show_line(const char *line, const char *word, int pos, int state)
 {
        struct skinny_device *d;
        struct skinny_line *l;
@@ -2316,8 +2878,8 @@ static char *complete_skinny_show_lines(const char *line, const char *word, int
        if (pos != 3)
                return NULL;
        
-       for (d = devices; d && !result; d = d->next) {
-               for (l = d->lines; l && !result; l = l->next) {
+       AST_LIST_TRAVERSE(&devices, d, list) {
+               AST_LIST_TRAVERSE(&d->lines, l, list) {
                        if (!strncasecmp(word, l->name, wordlen) && ++which > state)
                                result = ast_strdup(l->name);
                }
@@ -2326,26 +2888,36 @@ static char *complete_skinny_show_lines(const char *line, const char *word, int
        return result;
 }
 
-static int skinny_reset_device(int fd, int argc, char *argv[])
+static char *handle_skinny_reset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
        struct skinny_device *d;
        struct skinny_req *req;
 
-       if (argc < 3 || argc > 4)
-               return RESULT_SHOWUSAGE;
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny reset";
+               e->usage =
+                       "Usage: skinny reset <DeviceId|DeviceName|all> [restart]\n"
+                       "       Causes a Skinny device to reset itself, optionally with a full restart\n";
+               return NULL;
+       case CLI_GENERATE:
+               return complete_skinny_reset(a->line, a->word, a->pos, a->n);
+       }
 
-       ast_mutex_lock(&devicelock);
+       if (a->argc < 3 || a->argc > 4)
+               return CLI_SHOWUSAGE;
 
-       for (d = devices; d; d = d->next) {
+       AST_LIST_LOCK(&devices);
+       AST_LIST_TRAVERSE(&devices, d, list) {
                int fullrestart = 0;
-               if (!strcasecmp(argv[2], d->id) || !strcasecmp(argv[2], d->name) || !strcasecmp(argv[2], "all")) {
+               if (!strcasecmp(a->argv[2], d->id) || !strcasecmp(a->argv[2], d->name) || !strcasecmp(a->argv[2], "all")) {
                        if (!(d->session))
                                continue;
 
                        if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
                                continue;
 
-                       if (argc == 4 && !strcasecmp(argv[3], "restart"))
+                       if (a->argc == 4 && !strcasecmp(a->argv[3], "restart"))
                                fullrestart = 1;
 
                        if (fullrestart)
@@ -2354,11 +2926,11 @@ static int skinny_reset_device(int fd, int argc, char *argv[])
                                req->data.reset.resetType = 1;
 
                        ast_verb(3, "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id);
-                       transmit_response(d->session, req);
+                       transmit_response(d, req);
                }
        }
-       ast_mutex_unlock(&devicelock);
-       return RESULT_SUCCESS;
+       AST_LIST_UNLOCK(&devices);
+       return CLI_SUCCESS;
 }
 
 static char *device2str(int type)
@@ -2392,6 +2964,8 @@ static char *device2str(int type)
                return "7941";
        case SKINNY_DEVICE_7971:
                return "7971";
+       case SKINNY_DEVICE_7914:
+               return "7914";
        case SKINNY_DEVICE_7985:
                return "7985";
        case SKINNY_DEVICE_7911:
@@ -2400,8 +2974,24 @@ static char *device2str(int type)
                return "7961GE";
        case SKINNY_DEVICE_7941GE:
                return "7941GE";
+       case SKINNY_DEVICE_7931:
+               return "7931";
        case SKINNY_DEVICE_7921:
                return "7921";
+       case SKINNY_DEVICE_7906:
+               return "7906";
+       case SKINNY_DEVICE_7962:
+               return "7962";
+       case SKINNY_DEVICE_7937:
+               return "7937";
+       case SKINNY_DEVICE_7942:
+               return "7942";
+       case SKINNY_DEVICE_7945:
+               return "7945";
+       case SKINNY_DEVICE_7965:
+               return "7965";
+       case SKINNY_DEVICE_7975:
+               return "7975";
        case SKINNY_DEVICE_7905:
                return "7905";
        case SKINNY_DEVICE_7920:
@@ -2450,517 +3040,634 @@ static void print_codec_to_cli(int fd, struct ast_codec_pref *pref)
                ast_cli(fd, "none");
 }
 
-static int skinny_show_devices(int fd, int argc, char *argv[])
+static char *_skinny_show_devices(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
 {
        struct skinny_device *d;
        struct skinny_line *l;
+       const char *id;
+       char idtext[256] = "";
+       int total_devices = 0;
 
-       if (argc != 3)
-               return RESULT_SHOWUSAGE;
+       if (s) {        /* Manager - get ActionID */
+               id = astman_get_header(m, "ActionID");
+               if (!ast_strlen_zero(id))
+                       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+       }
 
-       ast_mutex_lock(&devicelock);
+       switch (argc) {
+       case 3:
+               break;
+       default:
+               return CLI_SHOWUSAGE;
+       }
 
-       ast_cli(fd, "Name                 DeviceId         IP              Type            R NL\n");
-       ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
+       if (!s) {
+               ast_cli(fd, "Name                 DeviceId         IP              Type            R NL\n");
+               ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
+       }
 
-       for (d = devices; d; d = d->next) {
+       AST_LIST_LOCK(&devices);
+       AST_LIST_TRAVERSE(&devices, d, list) {
                int numlines = 0;
-
-               for (l = d->lines; l; l = l->next)
+               total_devices++;
+               AST_LIST_TRAVERSE(&d->lines, l, list) {
                        numlines++;
-               
-               ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
-                       d->name,
-                       d->id,
-                       d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
-                       device2str(d->type),
-                       d->registered?'Y':'N',
-                       numlines);
+               }
+               if (!s) {
+                       ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
+                               d->name,
+                               d->id,
+                               d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
+                               device2str(d->type),
+                               d->registered?'Y':'N',
+                               numlines);
+               } else {
+                       astman_append(s,
+                               "Event: DeviceEntry\r\n%s"
+                               "Channeltype: SKINNY\r\n"
+                               "ObjectName: %s\r\n"
+                               "ChannelObjectType: device\r\n"
+                               "DeviceId: %s\r\n"
+                               "IPaddress: %s\r\n"
+                               "Type: %s\r\n"
+                               "Devicestatus: %s\r\n"
+                               "NumberOfLines: %d\r\n",
+                               idtext,
+                               d->name,
+                               d->id,
+                               d->session?ast_inet_ntoa(d->session->sin.sin_addr):"-none-",
+                               device2str(d->type),
+                               d->registered?"registered":"unregistered",
+                               numlines);
+               }
        }
+       AST_LIST_UNLOCK(&devices);
 
-       ast_mutex_unlock(&devicelock);
+       if (total)
+               *total = total_devices;
+       
+       return CLI_SUCCESS;
+}
 
-       return RESULT_SUCCESS;
+/*! \brief  Show SKINNY devices in the manager API */
+/*    Inspired from chan_sip */
+static int manager_skinny_show_devices(struct mansession *s, const struct message *m)
+{
+       const char *id = astman_get_header(m, "ActionID");
+       const char *a[] = {"skinny", "show", "devices"};
+       char idtext[256] = "";
+       int total = 0;
+
+       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");
+       /* List the devices in separate manager events */
+       _skinny_show_devices(-1, &total, s, m, 3, a);
+       /* Send final confirmation */
+       astman_append(s,
+       "Event: DevicelistComplete\r\n"
+       "EventList: Complete\r\n"
+       "ListItems: %d\r\n"
+       "%s"
+       "\r\n", total, idtext);
+       return 0;
 }
 
-/*! \brief Show device information */
-static int skinny_show_device(int fd, int argc, char *argv[])
+static char *handle_skinny_show_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny show devices";
+               e->usage =
+                       "Usage: skinny show devices\n"
+                       "       Lists all devices known to the Skinny subsystem.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       return _skinny_show_devices(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+static char *_skinny_show_device(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
 {
        struct skinny_device *d;
        struct skinny_line *l;
        struct skinny_speeddial *sd;
-       struct skinny_addon *a;
+       struct skinny_addon *sa;
+       char codec_buf[512];
 
-       if (argc < 4)
-               return RESULT_SHOWUSAGE;
+       if (argc < 4) {
+               return CLI_SHOWUSAGE;
+       }
 
-       ast_mutex_lock(&devicelock);
-       for (d = devices; d; d = d->next) {
+       AST_LIST_LOCK(&devices);
+       AST_LIST_TRAVERSE(&devices, d, list) {
                if (!strcasecmp(argv[3], d->id) || !strcasecmp(argv[3], d->name)) {
                        int numlines = 0, numaddons = 0, numspeeddials = 0;
 
-                       for (l = d->lines; l; l = l->next)
+                       AST_LIST_TRAVERSE(&d->lines, l, list){
                                numlines++;
+                       }
 
-                       ast_cli(fd, "Name:        %s\n", d->name);
-                       ast_cli(fd, "Id:          %s\n", d->id);
-                       ast_cli(fd, "version:     %s\n", S_OR(d->version_id, "Unknown"));
-                       ast_cli(fd, "Ip address:  %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
-                       ast_cli(fd, "Port:        %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
-                       ast_cli(fd, "Device Type: %s\n", device2str(d->type));
-                       ast_cli(fd, "Registered:  %s\n", (d->registered ? "Yes" : "No"));
-                       ast_cli(fd, "Lines:       %d\n", numlines);
-                       for (l = d->lines; l; l = l->next)
-                               ast_cli(fd, "  %s (%s)\n", l->name, l->label);
-                       for (a = d->addons; a; a = a->next)
+                       AST_LIST_TRAVERSE(&d->addons, sa, list) {
                                numaddons++;
-                       ast_cli(fd, "Addons:      %d\n", numaddons);
-                       for (a = d->addons; a; a = a->next)
-                               ast_cli(fd, "  %s\n", a->type);
-                       for (sd = d->speeddials; sd; sd = sd->next)
+                       }
+
+                       AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                                numspeeddials++;
-                       ast_cli(fd, "Speeddials:  %d\n", numspeeddials);
-                       for (sd = d->speeddials; sd; sd = sd->next)
-                               ast_cli(fd, "  %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
+                       }
+
+                       if (type == 0) { /* CLI */
+                               ast_cli(fd, "Name:        %s\n", d->name);
+                               ast_cli(fd, "Id:          %s\n", d->id);
+                               ast_cli(fd, "version:     %s\n", S_OR(d->version_id, "Unknown"));
+                               ast_cli(fd, "Ip address:  %s\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
+                               ast_cli(fd, "Port:        %d\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
+                               ast_cli(fd, "Device Type: %s\n", device2str(d->type));
+                               ast_cli(fd, "Conf Codecs:");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->confcapability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Neg Codecs: ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, d->capability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Registered:  %s\n", (d->registered ? "Yes" : "No"));
+                               ast_cli(fd, "Lines:       %d\n", numlines);
+                               AST_LIST_TRAVERSE(&d->lines, l, list) {
+                                       ast_cli(fd, "  %s (%s)\n", l->name, l->label);
+                               }
+                               AST_LIST_TRAVERSE(&d->addons, sa, list) {
+                                       numaddons++;
+                               }       
+                               ast_cli(fd, "Addons:      %d\n", numaddons);
+                               AST_LIST_TRAVERSE(&d->addons, sa, list) {
+                                       ast_cli(fd, "  %s\n", sa->type);
+                               }
+                               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
+                                       numspeeddials++;
+                               }
+                               ast_cli(fd, "Speeddials:  %d\n", numspeeddials);
+                               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
+                                       ast_cli(fd, "  %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint);
+                               }
+                       } else { /* manager */
+                               astman_append(s, "Channeltype: SKINNY\r\n");
+                               astman_append(s, "ObjectName: %s\r\n", d->name);
+                               astman_append(s, "ChannelObjectType: device\r\n");
+                               astman_append(s, "Id: %s\r\n", d->id);
+                               astman_append(s, "version: %s\r\n", S_OR(d->version_id, "Unknown"));
+                               astman_append(s, "Ipaddress: %s\r\n", (d->session ? ast_inet_ntoa(d->session->sin.sin_addr) : "Unknown"));
+                               astman_append(s, "Port: %d\r\n", (d->session ? ntohs(d->session->sin.sin_port) : 0));
+                               astman_append(s, "DeviceType: %s\r\n", device2str(d->type));
+                               astman_append(s, "Codecs: ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->confcapability);
+                               astman_append(s, "%s\r\n", codec_buf);
+                               astman_append(s, "CodecOrder: ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, d->capability);
+                               astman_append(s, "%s\r\n", codec_buf);
+                               astman_append(s, "Devicestatus: %s\r\n", (d->registered?"registered":"unregistered"));
+                               astman_append(s, "NumberOfLines: %d\r\n", numlines);
+                               AST_LIST_TRAVERSE(&d->lines, l, list) {
+                                       astman_append(s, "Line: %s (%s)\r\n", l->name, l->label);
+                               }
+                               astman_append(s, "NumberOfAddons: %d\r\n", numaddons);
+                               AST_LIST_TRAVERSE(&d->addons, sa, list) {
+                                       astman_append(s, "Addon: %s\r\n", sa->type);
+                               }
+                               astman_append(s, "NumberOfSpeeddials: %d\r\n", numspeeddials);
+                               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
+                                       astman_append(s, "Speeddial: %s (%s) ishint: %d\r\n", sd->exten, sd->label, sd->isHint);
+                               }
+                       }
                }
        }
-       ast_mutex_unlock(&devicelock);
-       return RESULT_SUCCESS;
+       AST_LIST_UNLOCK(&devices);
+       return CLI_SUCCESS;
 }
 
-static int skinny_show_lines(int fd, int argc, char *argv[])
+static int manager_skinny_show_device(struct mansession *s, const struct message *m)
+{
+       const char *a[4];
+       const char *device;
+
+       device = astman_get_header(m, "Device");
+       if (ast_strlen_zero(device)) {
+               astman_send_error(s, m, "Device: <name> missing.");
+               return 0;
+       }
+       a[0] = "skinny";
+       a[1] = "show";
+       a[2] = "device";
+       a[3] = device;
+
+       _skinny_show_device(1, -1, s, m, 4, a);
+       astman_append(s, "\r\n\r\n" );
+       return 0;
+}
+
+/*! \brief Show device information */
+static char *handle_skinny_show_device(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny show device";
+               e->usage =
+                       "Usage: skinny show device <DeviceId|DeviceName>\n"
+                       "       Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return complete_skinny_show_device(a->line, a->word, a->pos, a->n);
+       }
+
+       return _skinny_show_device(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+static char *_skinny_show_lines(int fd, int *total, struct mansession *s, const struct message *m, int argc, const char *argv[])
 {
-       struct skinny_device *d;
        struct skinny_line *l;
+       struct skinny_subchannel *sub;
+       int total_lines = 0;
+       int verbose = 0;
+       const char *id;
+       char idtext[256] = "";
 
-       if (argc != 3)
-               return RESULT_SHOWUSAGE;
-       
-       ast_mutex_lock(&devicelock);
-       
-       ast_cli(fd, "Device Name          Instance Name                 Label               \n");
-       ast_cli(fd, "-------------------- -------- -------------------- --------------------\n");
-       for (d = devices; d; d = d->next) {
-               for (l = d->lines; l; l = l->next) {
-                       ast_cli(fd, "%-20s %8d %-20s %-20s\n",
-                               d->name,
+       if (s) {        /* Manager - get ActionID */
+               id = astman_get_header(m, "ActionID");
+               if (!ast_strlen_zero(id))
+                       snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+       }
+
+       switch (argc) {
+       case 4:
+               verbose = 1;
+               break;
+       case 3:
+               verbose = 0;
+               break;
+       default:
+               return CLI_SHOWUSAGE;
+       }
+
+       if (!s) {
+               ast_cli(fd, "Name                 Device Name          Instance Label               \n");
+               ast_cli(fd, "-------------------- -------------------- -------- --------------------\n");
+       }
+       AST_LIST_LOCK(&lines);
+       AST_LIST_TRAVERSE(&lines, l, all) {
+               total_lines++;
+               if (!s) {
+                       ast_cli(fd, "%-20s %-20s %8d %-20s\n",
+                               l->name,
+                               (l->device ? l->device->name : "Not connected"),
                                l->instance,
+                               l->label);
+                       if (verbose) {
+                               AST_LIST_TRAVERSE(&l->sub, sub, list) {
+                                       ast_cli(fd, "  %s> %s to %s\n",
+                                               (sub == l->activesub?"Active  ":"Inactive"),
+                                               sub->owner->name,
+                                               (ast_bridged_channel(sub->owner)?ast_bridged_channel(sub->owner)->name:"")
+                                       );
+                               }
+                       }
+               } else {
+                       astman_append(s,
+                               "Event: LineEntry\r\n%s"
+                               "Channeltype: SKINNY\r\n"
+                               "ObjectName: %s\r\n"
+                               "ChannelObjectType: line\r\n"
+                               "Device: %s\r\n"
+                               "Instance: %d\r\n"
+                               "Label: %s\r\n",
+                               idtext,
                                l->name,
+                               (l->device?l->device->name:"None"),
+                               l->instance,
                                l->label);
                }
+               AST_LIST_UNLOCK(&lines);
        }
-       
-       ast_mutex_unlock(&devicelock);
-       return RESULT_SUCCESS;
+
+       if (total) {
+               *total = total_lines;
+       }
+
+       return CLI_SUCCESS;
 }
 
-/*! \brief List line information. */
-static int skinny_show_line(int fd, int argc, char *argv[])
+/*! \brief  Show Skinny lines in the manager API */
+/*    Inspired from chan_sip */
+static int manager_skinny_show_lines(struct mansession *s, const struct message *m)
+{
+       const char *id = astman_get_header(m, "ActionID");
+       const char *a[] = {"skinny", "show", "lines"};
+       char idtext[256] = "";
+       int total = 0;
+
+       if (!ast_strlen_zero(id))
+               snprintf(idtext, sizeof(idtext), "ActionID: %s\r\n", id);
+
+       astman_send_listack(s, m, "Line status list will follow", "start");
+       /* List the lines in separate manager events */
+       _skinny_show_lines(-1, &total, s, m, 3, a);
+       /* Send final confirmation */
+       astman_append(s,
+       "Event: LinelistComplete\r\n"
+       "EventList: Complete\r\n"
+       "ListItems: %d\r\n"
+       "%s"
+       "\r\n", total, idtext);
+       return 0;
+}
+
+static char *handle_skinny_show_lines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       int verbose = 0;
+
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny show lines [verbose]";
+               e->usage =
+                       "Usage: skinny show lines\n"
+                       "       Lists all lines known to the Skinny subsystem.\n"
+                       "       If 'verbose' is specified, the output includes\n"
+                       "       information about subs for each line.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }
+
+       if (a->argc == e->args) {
+               if (!strcasecmp(a->argv[e->args-1], "verbose")) {
+                       verbose = 1;
+               } else {
+                       return CLI_SHOWUSAGE;
+               }
+       } else if (a->argc != e->args - 1) {
+               return CLI_SHOWUSAGE;
+       }
+
+       return _skinny_show_lines(a->fd, NULL, NULL, NULL, a->argc, (const char **) a->argv);
+}
+
+static char *_skinny_show_line(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[])
 {
        struct skinny_device *d;
        struct skinny_line *l;
-
+       struct ast_codec_pref *pref;
+       int x = 0, codec = 0;
        char codec_buf[512];
        char group_buf[256];
+       char cbuf[256];
 
-       if (argc < 4)
-               return RESULT_SHOWUSAGE;
-       
-       ast_mutex_lock(&devicelock);
+       switch (argc) {
+       case 4:
+               break;
+       case 6:
+               break;
+       default:
+               return CLI_SHOWUSAGE;
+       }
+
+       AST_LIST_LOCK(&devices);
 
        /* Show all lines matching the one supplied */
-       for (d = devices; d; d = d->next) {
-               if (argc == 6 && (strcasecmp(argv[5], d->id) && strcasecmp(argv[5], d->name)))
+       AST_LIST_TRAVERSE(&devices, d, list) {
+               if (argc == 6 && (strcasecmp(argv[5], d->id) && strcasecmp(argv[5], d->name))) {
                        continue;
-               for (l = d->lines; l; l = l->next) {
-                       if (strcasecmp(argv[3], l->name))
+               }
+               AST_LIST_TRAVERSE(&d->lines, l, list) {
+                       if (strcasecmp(argv[3], l->name)) {
                                continue;
-                       ast_cli(fd, "Line:             %s\n", l->name);
-                       ast_cli(fd, "On Device:        %s\n", d->name);
-                       ast_cli(fd, "Line Label:       %s\n", l->label);
-                       ast_cli(fd, "Extension:        %s\n", S_OR(l->exten, "<not set>"));
-                       ast_cli(fd, "Context:          %s\n", l->context);
-                       ast_cli(fd, "CallGroup:        %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
-                       ast_cli(fd, "PickupGroup:      %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
-                       ast_cli(fd, "Language:         %s\n", S_OR(l->language, "<not set>"));
-                       ast_cli(fd, "Accountcode:      %s\n", S_OR(l->accountcode, "<not set>"));
-                       ast_cli(fd, "AmaFlag:          %s\n", ast_cdr_flags2str(l->amaflags));
-                       ast_cli(fd, "CallerId Number:  %s\n", S_OR(l->cid_num, "<not set>"));
-                       ast_cli(fd, "CallerId Name:    %s\n", S_OR(l->cid_name, "<not set>"));
-                       ast_cli(fd, "Hide CallerId:    %s\n", (l->hidecallerid ? "Yes" : "No"));
-                       ast_cli(fd, "CallForward:      %s\n", S_OR(l->call_forward, "<not set>"));
-                       ast_cli(fd, "VoicemailBox:     %s\n", S_OR(l->mailbox, "<not set>"));
-                       ast_cli(fd, "VoicemailNumber:  %s\n", S_OR(l->vmexten, "<not set>"));
-                       ast_cli(fd, "MWIblink:         %d\n", l->mwiblink);
-                       ast_cli(fd, "Regextension:     %s\n", S_OR(l->regexten, "<not set>"));
-                       ast_cli(fd, "Regcontext:       %s\n", S_OR(l->regcontext, "<not set>"));
-                       ast_cli(fd, "MoHInterpret:     %s\n", S_OR(l->mohinterpret, "<not set>"));
-                       ast_cli(fd, "MoHSuggest:       %s\n", S_OR(l->mohsuggest, "<not set>"));
-                       ast_cli(fd, "Last dialed nr:   %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
-                       ast_cli(fd, "Last CallerID:    %s\n", S_OR(l->lastcallerid, "<not set>"));
-                       ast_cli(fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
-                       ast_cli(fd, "Callwaiting:      %s\n", (l->callwaiting ? "Yes" : "No"));
-                       ast_cli(fd, "3Way Calling:     %s\n", (l->threewaycalling ? "Yes" : "No"));
-                       ast_cli(fd, "Can forward:      %s\n", (l->cancallforward ? "Yes" : "No"));
-                       ast_cli(fd, "Do Not Disturb:   %s\n", (l->dnd ? "Yes" : "No"));
-                       ast_cli(fd, "NAT:              %s\n", (l->nat ? "Yes" : "No"));
-                       ast_cli(fd, "immediate:        %s\n", (l->immediate ? "Yes" : "No"));
-                       ast_cli(fd, "Group:            %d\n", l->group);
-                       ast_cli(fd, "Codecs:           ");
-                       ast_getformatname_multiple(codec_buf, sizeof(codec_buf) -1, l->capability);
-                       ast_cli(fd, "%s\n", codec_buf);
-                       ast_cli(fd, "Codec Order:      (");
-                       print_codec_to_cli(fd, &l->prefs);
-                       ast_cli(fd, ")\n");
-                       ast_cli(fd, "\n");
+                       }
+                       if (type == 0) { /* CLI */
+                               ast_cli(fd, "Line:             %s\n", l->name);
+                               ast_cli(fd, "On Device:        %s\n", d->name);
+                               ast_cli(fd, "Line Label:       %s\n", l->label);
+                               ast_cli(fd, "Extension:        %s\n", S_OR(l->exten, "<not set>"));
+                               ast_cli(fd, "Context:          %s\n", l->context);
+                               ast_cli(fd, "CallGroup:        %s\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
+                               ast_cli(fd, "PickupGroup:      %s\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
+                               ast_cli(fd, "Language:         %s\n", S_OR(l->language, "<not set>"));
+                               ast_cli(fd, "Accountcode:      %s\n", S_OR(l->accountcode, "<not set>"));
+                               ast_cli(fd, "AmaFlag:          %s\n", ast_cdr_flags2str(l->amaflags));
+                               ast_cli(fd, "CallerId Number:  %s\n", S_OR(l->cid_num, "<not set>"));
+                               ast_cli(fd, "CallerId Name:    %s\n", S_OR(l->cid_name, "<not set>"));
+                               ast_cli(fd, "Hide CallerId:    %s\n", (l->hidecallerid ? "Yes" : "No"));
+                               ast_cli(fd, "CFwdAll:          %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
+                               ast_cli(fd, "CFwdBusy:         %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
+                               ast_cli(fd, "CFwdNoAnswer:     %s\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
+                               ast_cli(fd, "VoicemailBox:     %s\n", S_OR(l->mailbox, "<not set>"));
+                               ast_cli(fd, "VoicemailNumber:  %s\n", S_OR(l->vmexten, "<not set>"));
+                               ast_cli(fd, "MWIblink:         %d\n", l->mwiblink);
+                               ast_cli(fd, "Regextension:     %s\n", S_OR(l->regexten, "<not set>"));
+                               ast_cli(fd, "Regcontext:       %s\n", S_OR(l->regcontext, "<not set>"));
+                               ast_cli(fd, "MoHInterpret:     %s\n", S_OR(l->mohinterpret, "<not set>"));
+                               ast_cli(fd, "MoHSuggest:       %s\n", S_OR(l->mohsuggest, "<not set>"));
+                               ast_cli(fd, "Last dialed nr:   %s\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
+                               ast_cli(fd, "Last CallerID:    %s\n", S_OR(l->lastcallerid, "<not set>"));
+                               ast_cli(fd, "Transfer enabled: %s\n", (l->transfer ? "Yes" : "No"));
+                               ast_cli(fd, "Callwaiting:      %s\n", (l->callwaiting ? "Yes" : "No"));
+                               ast_cli(fd, "3Way Calling:     %s\n", (l->threewaycalling ? "Yes" : "No"));
+                               ast_cli(fd, "Can forward:      %s\n", (l->cancallforward ? "Yes" : "No"));
+                               ast_cli(fd, "Do Not Disturb:   %s\n", (l->dnd ? "Yes" : "No"));
+                               ast_cli(fd, "NAT:              %s\n", (l->nat ? "Yes" : "No"));
+                               ast_cli(fd, "immediate:        %s\n", (l->immediate ? "Yes" : "No"));
+                               ast_cli(fd, "Group:            %d\n", l->group);
+                               ast_cli(fd, "Parkinglot:       %s\n", S_OR(l->parkinglot, "<not set>"));
+                               ast_cli(fd, "Conf Codecs:      ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Neg Codecs:       ");
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->capability);
+                               ast_cli(fd, "%s\n", codec_buf);
+                               ast_cli(fd, "Codec Order:      (");
+                               print_codec_to_cli(fd, &l->prefs);
+                               ast_cli(fd, ")\n");
+                               ast_cli(fd, "\n");
+                       } else { /* manager */
+                               astman_append(s, "Channeltype: SKINNY\r\n");
+                               astman_append(s, "ObjectName: %s\r\n", l->name);
+                               astman_append(s, "ChannelObjectType: line\r\n");
+                               astman_append(s, "Device: %s\r\n", d->name);
+                               astman_append(s, "LineLabel: %s\r\n", l->label);
+                               astman_append(s, "Extension: %s\r\n", S_OR(l->exten, "<not set>"));
+                               astman_append(s, "Context: %s\r\n", l->context);
+                               astman_append(s, "CallGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->callgroup));
+                               astman_append(s, "PickupGroup: %s\r\n", ast_print_group(group_buf, sizeof(group_buf), l->pickupgroup));
+                               astman_append(s, "Language: %s\r\n", S_OR(l->language, "<not set>"));
+                               astman_append(s, "Accountcode: %s\r\n", S_OR(l->accountcode, "<not set>"));
+                               astman_append(s, "AMAflags: %s\r\n", ast_cdr_flags2str(l->amaflags));
+                               astman_append(s, "Callerid: %s\r\n", ast_callerid_merge(cbuf, sizeof(cbuf), l->cid_name, l->cid_num, ""));
+                               astman_append(s, "HideCallerId: %s\r\n", (l->hidecallerid ? "Yes" : "No"));
+                               astman_append(s, "CFwdAll: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_ALL), l->call_forward_all, "<not set>"));
+                               astman_append(s, "CFwdBusy: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_BUSY), l->call_forward_busy, "<not set>"));
+                               astman_append(s, "CFwdNoAnswer: %s\r\n", S_COR((l->cfwdtype & SKINNY_CFWD_NOANSWER), l->call_forward_noanswer, "<not set>"));
+                               astman_append(s, "VoicemailBox: %s\r\n", S_OR(l->mailbox, "<not set>"));
+                               astman_append(s, "VoicemailNumber: %s\r\n", S_OR(l->vmexten, "<not set>"));
+                               astman_append(s, "MWIblink: %d\r\n", l->mwiblink);
+                               astman_append(s, "RegExtension: %s\r\n", S_OR(l->regexten, "<not set>"));
+                               astman_append(s, "Regcontext: %s\r\n", S_OR(l->regcontext, "<not set>"));
+                               astman_append(s, "MoHInterpret: %s\r\n", S_OR(l->mohinterpret, "<not set>"));
+                               astman_append(s, "MoHSuggest: %s\r\n", S_OR(l->mohsuggest, "<not set>"));
+                               astman_append(s, "LastDialedNr: %s\r\n", S_OR(l->lastnumberdialed, "<no calls made yet>"));
+                               astman_append(s, "LastCallerID: %s\r\n", S_OR(l->lastcallerid, "<not set>"));
+                               astman_append(s, "Transfer: %s\r\n", (l->transfer ? "Yes" : "No"));
+                               astman_append(s, "Callwaiting: %s\r\n", (l->callwaiting ? "Yes" : "No"));
+                               astman_append(s, "3WayCalling: %s\r\n", (l->threewaycalling ? "Yes" : "No"));
+                               astman_append(s, "CanForward: %s\r\n", (l->cancallforward ? "Yes" : "No"));
+                               astman_append(s, "DoNotDisturb: %s\r\n", (l->dnd ? "Yes" : "No"));
+                               astman_append(s, "NAT: %s\r\n", (l->nat ? "Yes" : "No"));
+                               astman_append(s, "immediate: %s\r\n", (l->immediate ? "Yes" : "No"));
+                               astman_append(s, "Group: %d\r\n", l->group);
+                               astman_append(s, "Parkinglot: %s\r\n", S_OR(l->parkinglot, "<not set>"));
+                               ast_getformatname_multiple(codec_buf, sizeof(codec_buf) - 1, l->confcapability);
+                               astman_append(s, "Codecs: %s\r\n", codec_buf);
+                               astman_append(s, "CodecOrder: ");
+                               pref = &l->prefs;
+                               for(x = 0; x < 32 ; x++) {
+                                       codec = ast_codec_pref_index(pref, x);
+                                       if (!codec)
+                                               break;
+                                       astman_append(s, "%s", ast_getformatname(codec));
+                                       if (x < 31 && ast_codec_pref_index(pref, x+1))
+                                               astman_append(s, ",");
+                               }
+                               astman_append(s, "\r\n");
+                       }
                }
        }
        
-       ast_mutex_unlock(&devicelock);
-       return RESULT_SUCCESS;
+       AST_LIST_UNLOCK(&devices);
+       return CLI_SUCCESS;
 }
 
-/*! \brief List global settings for the Skinny subsystem. */
-static int skinny_show_settings(int fd, int argc, char *argv[])
+static int manager_skinny_show_line(struct mansession *s, const struct message *m)
 {
-       if (argc != 3)
-               return RESULT_SHOWUSAGE;
-
-       ast_cli(fd, "\nGlobal Settings:\n");
-       ast_cli(fd, "  Skinny Port:            %d\n", ntohs(bindaddr.sin_port));
-       ast_cli(fd, "  Bindaddress:            %s\n", ast_inet_ntoa(bindaddr.sin_addr));
-       ast_cli(fd, "  KeepAlive:              %d\n", keep_alive);
-       ast_cli(fd, "  Date Format:            %s\n", date_format);
-       ast_cli(fd, "  Voice Mail Extension:   %s\n", S_OR(vmexten, "(not set)"));
-       ast_cli(fd, "  Reg. context:           %s\n", S_OR(regcontext, "(not set)"));
-       ast_cli(fd, "  Jitterbuffer enabled:   %s\n", (ast_test_flag(&global_jbconf, AST_JB_ENABLED) ? "Yes" : "No"));
-       ast_cli(fd, "  Jitterbuffer forced:    %s\n", (ast_test_flag(&global_jbconf, AST_JB_FORCED) ? "Yes" : "No"));
-       ast_cli(fd, "  Jitterbuffer max size:  %ld\n", global_jbconf.max_size);
-       ast_cli(fd, "  Jitterbuffer resync:    %ld\n", global_jbconf.resync_threshold);
-       ast_cli(fd, "  Jitterbuffer impl:      %s\n", global_jbconf.impl);
-       ast_cli(fd, "  Jitterbuffer log:       %s\n", (ast_test_flag(&global_jbconf, AST_JB_LOG) ? "Yes" : "No"));
-
-       return RESULT_SUCCESS;
-}
-
-static const char show_devices_usage[] =
-"Usage: skinny show devices\n"
-"       Lists all devices known to the Skinny subsystem.\n";
-
-static const char show_device_usage[] =
-"Usage: skinny show device <DeviceId|DeviceName>\n"
-"       Lists all deviceinformation of a specific device known to the Skinny subsystem.\n";
-
-static const char show_lines_usage[] =
-"Usage: skinny show lines\n"
-"       Lists all lines known to the Skinny subsystem.\n";
-
-static const char show_line_usage[] =
-"Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
-"       List all lineinformation of a specific line known to the Skinny subsystem.\n";
-
-static const char show_settings_usage[] =
-"Usage: skinny show settings\n"
-"       Lists all global configuration settings of the Skinny subsystem.\n";
-
-static const char debug_usage[] =
-"Usage: skinny set debug\n"
-"       Enables dumping of Skinny packets for debugging purposes\n";
-
-static const char no_debug_usage[] =
-"Usage: skinny set debug off\n"
-"       Disables dumping of Skinny packets for debugging purposes\n";
-
-static const char reset_usage[] =
-"Usage: skinny reset <DeviceId|DeviceName|all> [restart]\n"
-"       Causes a Skinny device to reset itself, optionally with a full restart\n";
-
-static struct ast_cli_entry cli_skinny[] = {
-       { { "skinny", "show", "devices", NULL },
-       skinny_show_devices, "List defined Skinny devices",
-       show_devices_usage },
-
-       { { "skinny", "show", "device", NULL },
-       skinny_show_device, "List Skinny device information",
-       show_device_usage, complete_skinny_show_device },
+       const char *a[4];
+       const char *line;
 
-       { { "skinny", "show", "lines", NULL },
-       skinny_show_lines, "List defined Skinny lines per device",
-       show_lines_usage },
-
-       { { "skinny", "show", "line", NULL },
-       skinny_show_line, "List Skinny line information",
-       show_line_usage, complete_skinny_show_lines },
-
-       { { "skinny", "show", "settings", NULL },
-       skinny_show_settings, "List global Skinny settings",
-       show_settings_usage },
-
-       { { "skinny", "set", "debug", NULL },
-       skinny_do_debug, "Enable Skinny debugging",
-       debug_usage },
-
-       { { "skinny", "set", "debug", "off", NULL },
-       skinny_no_debug, "Disable Skinny debugging",
-       no_debug_usage },
-
-       { { "skinny", "reset", NULL },
-       skinny_reset_device, "Reset Skinny device(s)",
-       reset_usage, complete_skinny_reset },
-};
+       line = astman_get_header(m, "Line");
+       if (ast_strlen_zero(line)) {
+               astman_send_error(s, m, "Line: <name> missing.");
+               return 0;
+       }
+       a[0] = "skinny";
+       a[1] = "show";
+       a[2] = "line";
+       a[3] = line;
 
-#if 0
-static struct skinny_paging_device *build_paging_device(const char *cat, struct ast_variable *v)
-{
-       return NULL;
+       _skinny_show_line(1, -1, s, m, 4, a);
+       astman_append(s, "\r\n\r\n" );
+       return 0;
 }
-#endif
 
-static struct skinny_device *build_device(const char *cat, struct ast_variable *v)
+/*! \brief List line information. */
+static char *handle_skinny_show_line(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-       struct skinny_device *d;
-       struct skinny_line *l;
-       struct skinny_speeddial *sd;
-       struct skinny_addon *a;
-       char device_vmexten[AST_MAX_EXTENSION];
-       struct ast_variable *chanvars = NULL;
-       int lineInstance = 1;
-       int speeddialInstance = 1;
-       int y = 0;
-
-       if (!(d = ast_calloc(1, sizeof(*d)))) {
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny show line";
+               e->usage =
+                       "Usage: skinny show line <Line> [ on <DeviceID|DeviceName> ]\n"
+                       "       List all lineinformation of a specific line known to the Skinny subsystem.\n";
                return NULL;
-       } else {
-               ast_copy_string(d->name, cat, sizeof(d->name));
-               d->lastlineinstance = 1;
-               d->capability = default_capability;
-               d->prefs = default_prefs;
-               if (!ast_strlen_zero(vmexten))
-                       ast_copy_string(device_vmexten, vmexten, sizeof(device_vmexten));
-               while(v) {
-                       if (!strcasecmp(v->name, "host")) {
-                               if (ast_get_ip(&d->addr, v->value)) {
-                                       ast_free(d);
-                                       return NULL;
-                               }
-                       } else if (!strcasecmp(v->name, "port")) {
-                               d->addr.sin_port = htons(atoi(v->value));
-                       } else if (!strcasecmp(v->name, "device")) {
-                               ast_copy_string(d->id, v->value, sizeof(d->id));
-                       } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
-                               d->ha = ast_append_ha(v->name, v->value, d->ha, NULL);
-                       } else if (!strcasecmp(v->name, "vmexten")) {
-                               ast_copy_string(device_vmexten, v->value, sizeof(device_vmexten));
-                       } else if (!strcasecmp(v->name, "context")) {
-                               ast_copy_string(context, v->value, sizeof(context));
-                       } else if (!strcasecmp(v->name, "regexten")) {
-                               ast_copy_string(regexten, v->value, sizeof(regexten));
-                       } else if (!strcasecmp(v->name, "allow")) {
-                               ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 1);
-                       } else if (!strcasecmp(v->name, "disallow")) {
-                               ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 0);
-                       } else if (!strcasecmp(v->name, "version")) {
-                               ast_copy_string(d->version_id, v->value, sizeof(d->version_id));
-                       } else if (!strcasecmp(v->name, "canreinvite")) {
-                               canreinvite = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "nat")) {
-                               nat = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "callerid")) {
-                               if (!strcasecmp(v->value, "asreceived")) {
-                                       cid_num[0] = '\0';
-                                       cid_name[0] = '\0';
-                               } else {
-                                       ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
-                               }
-                       } else if (!strcasecmp(v->name, "language")) {
-                               ast_copy_string(language, v->value, sizeof(language));
-                       } else if (!strcasecmp(v->name, "accountcode")) {
-                               ast_copy_string(accountcode, v->value, sizeof(accountcode));
-                       } else if (!strcasecmp(v->name, "amaflags")) {
-                               y = ast_cdr_amaflags2int(v->value);
-                               if (y < 0) {
-                                       ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
-                               } else {
-                                       amaflags = y;
-                               }
-                       } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
-                               ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
-                       } else if (!strcasecmp(v->name, "mohsuggest")) {
-                               ast_copy_string(mohsuggest, v->value, sizeof(mohsuggest));
-                       } else if (!strcasecmp(v->name, "callgroup")) {
-                               cur_callergroup = ast_get_group(v->value);
-                       } else if (!strcasecmp(v->name, "pickupgroup")) {
-                               cur_pickupgroup = ast_get_group(v->value);
-                       } else if (!strcasecmp(v->name, "immediate")) {
-                               immediate = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "cancallforward")) {
-                               cancallforward = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "mailbox")) {
-                               ast_copy_string(mailbox, v->value, sizeof(mailbox));
-                       } else if (!strcasecmp(v->name, "callreturn")) {
-                               callreturn = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "callwaiting")) {
-                               callwaiting = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "transfer")) {
-                               transfer = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "threewaycalling")) {
-                               threewaycalling = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "mwiblink")) {
-                               mwiblink = ast_true(v->value);
-                       } else if (!strcasecmp(v->name, "linelabel")) {
-                               ast_copy_string(linelabel, v->value, sizeof(linelabel));
-                       } else if (!strcasecmp(v->name, "setvar")) {
-                               chanvars = add_var(v->value, chanvars);
-                       } else if (!strcasecmp(v->name, "speeddial")) {
-                               if (!(sd = ast_calloc(1, sizeof(*sd)))) {
-                                       return NULL;
-                               } else {
-                                       char *stringp, *exten, *context, *label;
-                                       stringp = v->value;
-                                       exten = strsep(&stringp, ",");
-                                       if ((context = strchr(exten, '@'))) {
-                                               *context++ = '\0';
-                                       }
-                                       label = stringp;
-                                       ast_mutex_init(&sd->lock);
-                                       ast_copy_string(sd->exten, exten, sizeof(sd->exten));
-                                       if (!ast_strlen_zero(context)) {
-                                               sd->isHint = 1;
-                                               sd->instance = lineInstance++;
-                                               ast_copy_string(sd->context, context, sizeof(sd->context));
-                                       } else {
-                                               sd->isHint = 0;
-                                               sd->instance = speeddialInstance++;
-                                               sd->context[0] = '\0';
-                                       }
-                                       ast_copy_string(sd->label, S_OR(label, exten), sizeof(sd->label));
-
-                                       sd->parent = d;
-
-                                       sd->next = d->speeddials;
-                                       d->speeddials = sd;
-                               }
-                       } else if (!strcasecmp(v->name, "addon")) {
-                               if (!(a = ast_calloc(1, sizeof(*a)))) {
-                                       return NULL;
-                               } else {
-                                       ast_mutex_init(&a->lock);
-                                       ast_copy_string(a->type, v->value, sizeof(a->type));
+       case CLI_GENERATE:
+               return complete_skinny_show_line(a->line, a->word, a->pos, a->n);
+       }
 
-                                       a->next = d->addons;
-                                       d->addons = a;
-                               }
-                       } else if (!strcasecmp(v->name, "trunk") || !strcasecmp(v->name, "line")) {
-                               if (!(l = ast_calloc(1, sizeof(*l)))) {
-                                       return NULL;
-                               } else {
-                                       ast_mutex_init(&l->lock);
-                                       ast_copy_string(l->name, v->value, sizeof(l->name));
-
-                                       /* XXX Should we check for uniqueness?? XXX */
-                                       ast_copy_string(l->context, context, sizeof(l->context));
-                                       ast_copy_string(l->cid_num, cid_num, sizeof(l->cid_num));
-                                       ast_copy_string(l->cid_name, cid_name, sizeof(l->cid_name));
-                                       ast_copy_string(l->label, linelabel, sizeof(l->label));
-                                       ast_copy_string(l->language, language, sizeof(l->language));
-                                       ast_copy_string(l->mohinterpret, mohinterpret, sizeof(l->mohinterpret));
-                                       ast_copy_string(l->mohsuggest, mohsuggest, sizeof(l->mohsuggest));
-                                       ast_copy_string(l->regexten, regexten, sizeof(l->regexten));
-                                       ast_copy_string(l->mailbox, mailbox, sizeof(l->mailbox));
-                                       if (!ast_strlen_zero(mailbox))
-                                               ast_verb(3, "Setting mailbox '%s' on %s@%s\n", mailbox, d->name, l->name);
-                                       if (!ast_strlen_zero(device_vmexten))
-                                               ast_copy_string(l->vmexten, device_vmexten, sizeof(vmexten));
-                                       l->chanvars = chanvars;
-                                       l->msgstate = -1;
-                                       l->capability = d->capability;
-                                       l->prefs = d->prefs;
-                                       l->parent = d;
-                                       if (!strcasecmp(v->name, "trunk")) {
-                                               l->type = TYPE_TRUNK;
-                                       } else {
-                                               l->type = TYPE_LINE;
-                                       }
-                                       l->immediate = immediate;
-                                       l->callgroup = cur_callergroup;
-                                       l->pickupgroup = cur_pickupgroup;
-                                       l->callreturn = callreturn;
-                                       l->cancallforward = cancallforward;
-                                       l->callwaiting = callwaiting;
-                                       l->transfer = transfer;
-                                       l->threewaycalling = threewaycalling;
-                                       l->mwiblink = mwiblink;
-                                       l->onhooktime = time(NULL);
-                                       l->instance = lineInstance++;
-                                       /* ASSUME we're onhook at this point */
-                                       l->hookstate = SKINNY_ONHOOK;
-                                       l->nat = nat;
-                                       l->canreinvite = canreinvite;
-
-                                       l->next = d->lines;
-                                       d->lines = l;
-                               }
-                       } else {
-                               ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
-                       }
-                       v = v->next;
-               }
+       return _skinny_show_line(0, a->fd, NULL, NULL, a->argc, (const char **) a->argv);
+}
 
-               if (!d->lines) {
-                       ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
-                       return NULL;
-               }
-               if (/*d->addr.sin_addr.s_addr && */!ntohs(d->addr.sin_port)) {
-                       d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
-               }
-#if 0
-               /* I don't think we need this anymore at all, since d->ourip is set in skinny_register now */
-               if (d->addr.sin_addr.s_addr) {
-                       /* XXX See note above, in 'host' option. */
-                       if (ast_ouraddrfor(&d->addr.sin_addr, &d->ourip)) {
-                               d->ourip = __ourip;
-                       }
-               } else {
-                       d->ourip = __ourip;
-               }
-#endif
-       }
-       return d;
+/*! \brief List global settings for the Skinny subsystem. */
+static char *handle_skinny_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+       switch (cmd) {
+       case CLI_INIT:
+               e->command = "skinny show settings";
+               e->usage =
+                       "Usage: skinny show settings\n"
+                       "       Lists all global configuration settings of the Skinny subsystem.\n";
+               return NULL;
+       case CLI_GENERATE:
+               return NULL;
+       }       
+
+       if (a->argc != 3)
+               return CLI_SHOWUSAGE;
+
+       ast_cli(a->fd, "\nGlobal Settings:\n");
+       ast_cli(a->fd, "  Skinny Port:            %d\n", ntohs(bindaddr.sin_port));
+       ast_cli(a->fd, "  Bindaddress:            %s\n", ast_inet_ntoa(bindaddr.sin_addr));
+       ast_cli(a->fd, "  KeepAlive:              %d\n", keep_alive);
+       ast_cli(a->fd, "  Date Format:            %s\n", date_format);
+       ast_cli(a->fd, "  Voice Mail Extension:   %s\n", S_OR(global_vmexten, "(not set)"));
+       ast_cli(a->fd, "  Reg. context:           %s\n", S_OR(regcontext, "(not set)"));
+       ast_cli(a->fd, "  Jitterbuffer enabled:   %s\n", (ast_test_flag(&global_jbconf, AST_JB_ENABLED) ? "Yes" : "No"));
+       ast_cli(a->fd, "  Jitterbuffer forced:    %s\n", (ast_test_flag(&global_jbconf, AST_JB_FORCED) ? "Yes" : "No"));
+       ast_cli(a->fd, "  Jitterbuffer max size:  %ld\n", global_jbconf.max_size);
+       ast_cli(a->fd, "  Jitterbuffer resync:    %ld\n", global_jbconf.resync_threshold);
+       ast_cli(a->fd, "  Jitterbuffer impl:      %s\n", global_jbconf.impl);
+       ast_cli(a->fd, "  Jitterbuffer log:       %s\n", (ast_test_flag(&global_jbconf, AST_JB_LOG) ? "Yes" : "No"));
+
+       return CLI_SUCCESS;
 }
 
+static struct ast_cli_entry cli_skinny[] = {
+       AST_CLI_DEFINE(handle_skinny_show_devices, "List defined Skinny devices"),
+       AST_CLI_DEFINE(handle_skinny_show_device, "List Skinny device information"),
+       AST_CLI_DEFINE(handle_skinny_show_lines, "List defined Skinny lines per device"),
+       AST_CLI_DEFINE(handle_skinny_show_line, "List Skinny line information"),
+       AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"),
+       AST_CLI_DEFINE(handle_skinny_set_debug, "Enable/Disable Skinny debugging"),
+       AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"),
+       AST_CLI_DEFINE(handle_skinny_reload, "Reload Skinny config"),
+};
+
 static void start_rtp(struct skinny_subchannel *sub)
 {
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
+       struct skinny_device *d = l->device;
        int hasvideo = 0;
 
        ast_mutex_lock(&sub->lock);
        /* Allocate the RTP */
-       sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+       sub->rtp = ast_rtp_instance_new("asterisk", sched, &bindaddr, NULL);
        if (hasvideo)
-               sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
-       
+               sub->vrtp = ast_rtp_instance_new("asterisk", sched, &bindaddr, NULL);
+
+       if (sub->rtp) {
+               ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
+       }
+       if (sub->vrtp) {
+               ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_RTCP, 1);
+       }
+
        if (sub->rtp && sub->owner) {
-               ast_channel_set_fd(sub->owner, 0, ast_rtp_fd(sub->rtp));
-               ast_channel_set_fd(sub->owner, 1, ast_rtcp_fd(sub->rtp));
+               ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0));
+               ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1));
        }
        if (hasvideo && sub->vrtp && sub->owner) {
-               ast_channel_set_fd(sub->owner, 2, ast_rtp_fd(sub->vrtp));
-               ast_channel_set_fd(sub->owner, 3, ast_rtcp_fd(sub->vrtp));
+               ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0));
+               ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1));
        }
        if (sub->rtp) {
-               ast_rtp_setnat(sub->rtp, l->nat);
+               ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "Skinny RTP");
+               ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_NAT, l->nat);
        }
        if (sub->vrtp) {
-               ast_rtp_setnat(sub->vrtp, l->nat);
+               ast_rtp_instance_set_qos(sub->vrtp, qos.tos_video, qos.cos_video, "Skinny VRTP");
+               ast_rtp_instance_set_prop(sub->vrtp, AST_RTP_PROPERTY_NAT, l->nat);
        }
        /* Set Frame packetization */
        if (sub->rtp)
-               ast_rtp_codec_setpref(sub->rtp, &l->prefs);
+               ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(sub->rtp), sub->rtp, &l->prefs);
 
        /* Create the RTP connection */
-       transmit_connect(d->session, sub);
-       ast_mutex_unlock(&sub->lock);
+       transmit_connect(d, sub);
+       ast_mutex_unlock(&sub->lock);
 }
 
 static void *skinny_newcall(void *data)
@@ -2968,8 +3675,7 @@ static void *skinny_newcall(void *data)
        struct ast_channel *c = data;
        struct skinny_subchannel *sub = c->tech_pvt;
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
-       struct skinnysession *s = d->session;
+       struct skinny_device *d = l->device;
        int res = 0;
 
        ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed));
@@ -2977,11 +3683,16 @@ static void *skinny_newcall(void *data)
                l->hidecallerid ? "" : l->cid_num,
                l->hidecallerid ? "" : l->cid_name,
                c->cid.cid_ani ? NULL : l->cid_num);
+       c->connected.id.number = ast_strdup(c->exten);
+       c->connected.id.name = NULL;
        ast_setstate(c, AST_STATE_RING);
+       if (!sub->rtp) {
+               start_rtp(sub);
+       }
        res = ast_pbx_run(c);
        if (res) {
                ast_log(LOG_WARNING, "PBX exited non-zero\n");
-               transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+               transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
        }
        return NULL;
 }
@@ -2991,12 +3702,10 @@ static void *skinny_ss(void *data)
        struct ast_channel *c = data;
        struct skinny_subchannel *sub = c->tech_pvt;
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
-       struct skinnysession *s = d->session;
+       struct skinny_device *d = l->device;
        int len = 0;
        int timeout = firstdigittimeout;
        int res = 0;
-       int getforward=0;
        int loop_pause = 100;
 
        ast_verb(3, "Starting simple switch on '%s@%s'\n", l->name, d->name);
@@ -3019,26 +3728,30 @@ static void *skinny_ss(void *data)
                len = strlen(d->exten);
 
                if (!ast_ignore_pattern(c->context, d->exten)) {
-                       transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+                       transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                }
                if (ast_exists_extension(c, c->context, d->exten, 1, l->cid_num)) {
                        if (!res || !ast_matchmore_extension(c, c->context, d->exten, 1, l->cid_num)) {
-                               if (getforward) {
+                               if (l->getforward) {
                                        /* Record this as the forwarding extension */
-                                       ast_copy_string(l->call_forward, d->exten, sizeof(l->call_forward));
-                                       ast_verb(3, "Setting call forward to '%s' on channel %s\n",
-                                                       l->call_forward, c->name);
-                                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                                       if (res) {
-                                               break;
-                                       }
+                                       set_callforwards(l, d->exten, l->getforward);
+                                       ast_verb(3, "Setting call forward (%d) to '%s' on channel %s\n",
+                                                       l->cfwdtype, d->exten, c->name);
+                                       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                                       transmit_lamp_indication(d, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
+                                       transmit_displaynotify(d, "CFwd enabled", 10);
+                                       transmit_cfwdstate(d, l);
                                        ast_safe_sleep(c, 500);
                                        ast_indicate(c, -1);
-                                       ast_safe_sleep(c, 1000);
+                                       ast_safe_sleep(c, 1000);
                                        memset(d->exten, 0, sizeof(d->exten));
-                                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
                                        len = 0;
-                                       getforward = 0;
+                                       l->getforward = 0;
+                                       if (sub->owner && sub->owner->_state != AST_STATE_UP) {
+                                               ast_indicate(c, -1);
+                                               ast_hangup(c);
+                                       }
+                                       return NULL;
                                } else {
                                        ast_copy_string(c->exten, d->exten, sizeof(c->exten));
                                        ast_copy_string(l->lastnumberdialed, d->exten, sizeof(l->lastnumberdialed));
@@ -3054,7 +3767,9 @@ static void *skinny_ss(void *data)
                } else if (res == 0) {
                        ast_debug(1, "Not enough digits (%s) (and no ambiguous match)...\n", d->exten);
                        memset(d->exten, 0, sizeof(d->exten));
-                       transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
+                       if (l->hookstate == SKINNY_OFFHOOK) {
+                               transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
+                       }
                        if (sub->owner && sub->owner->_state != AST_STATE_UP) {
                                ast_indicate(c, -1);
                                ast_hangup(c);
@@ -3064,9 +3779,11 @@ static void *skinny_ss(void *data)
                           ((d->exten[0] != '*') || (!ast_strlen_zero(d->exten) > 2))) {
                        ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", d->exten, c->cid.cid_num ? c->cid.cid_num : "<Unknown Caller>", c->context);
                        memset(d->exten, 0, sizeof(d->exten));
-                       transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
-                       /* hang out for 3 seconds to let congestion play */
-                       ast_safe_sleep(c, 3000);
+                       if (l->hookstate == SKINNY_OFFHOOK) {
+                               transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
+                               /* hang out for 3 seconds to let congestion play */
+                               ast_safe_sleep(c, 3000);
+                       }
                        break;
                }
                if (!timeout) {
@@ -3090,8 +3807,7 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
        int tone = 0;
        struct skinny_subchannel *sub = ast->tech_pvt;
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
-       struct skinnysession *s = d->session;
+       struct skinny_device *d = l->device;
 
        if (!d->registered) {
                ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
@@ -3111,24 +3827,30 @@ static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
                return -1;
        }
 
+       if (AST_LIST_NEXT(sub,list) && !l->callwaiting) {
+               ast_queue_control(ast, AST_CONTROL_BUSY);
+               return -1;
+       }
+       
        switch (l->hookstate) {
        case SKINNY_OFFHOOK:
                tone = SKINNY_CALLWAITTONE;
                break;
        case SKINNY_ONHOOK:
                tone = SKINNY_ALERT;
+               l->activesub = sub;
                break;
        default:
                ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
                break;
        }
 
-       transmit_callstate(s, l->instance, SKINNY_RINGIN, sub->callid);
-       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGIN);
-       transmit_displaypromptstatus(s, "Ring-In", 0, l->instance, sub->callid);
-       transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
-       transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
-       transmit_ringer_mode(s, SKINNY_RING_INSIDE);
+       transmit_callstateonly(d, sub, SKINNY_RINGIN);
+       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGIN);
+       transmit_displaypromptstatus(d, "Ring-In", 0, l->instance, sub->callid);
+       transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
+       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
+       transmit_ringer_mode(d, SKINNY_RING_INSIDE);
 
        ast_setstate(ast, AST_STATE_RINGING);
        ast_queue_control(ast, AST_CONTROL_RINGING);
@@ -3147,24 +3869,65 @@ static int skinny_hangup(struct ast_channel *ast)
                ast_debug(1, "Asked to hangup channel not connected\n");
                return 0;
        }
+
        l = sub->parent;
-       d = l->parent;
+       d = l->device;
        s = d->session;
+
        if (skinnydebug)
-               ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, d->name);
+               ast_verb(3,"Hanging up %s/%d\n",d->name,sub->callid);
+
+       AST_LIST_REMOVE(&l->sub, sub, list);
 
        if (d->registered) {
-               if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) {
+               /* Ignoring l->type, doesn't seem relevant and previous code 
+                  assigned rather than tested, ie always true */
+               if (!AST_LIST_EMPTY(&l->sub)) {
+                       if (sub->related) {
+                               sub->related->related = NULL;
+
+                       }
+                       if (sub == l->activesub) {      /* we are killing the active sub, but there are other subs on the line*/
+                               ast_verb(4,"Killing active sub %d\n", sub->callid);
+                               if (sub->related) {
+                                       l->activesub = sub->related;
+                               } else {
+                                       if (AST_LIST_NEXT(sub, list)) {
+                                               l->activesub = AST_LIST_NEXT(sub, list);
+                                       } else {
+                                               l->activesub = AST_LIST_FIRST(&l->sub);
+                                       }
+                               }
+                               //transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
+                               transmit_activatecallplane(d, l);
+                               transmit_closereceivechannel(d, sub);
+                               transmit_stopmediatransmission(d, sub);
+                               transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
+                               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
+                       } else {    /* we are killing a background sub on the line with other subs*/
+                               ast_verb(4,"Killing inactive sub %d\n", sub->callid);
+                               if (AST_LIST_NEXT(sub, list)) {
+                                       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
+                               } else {
+                                       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+                               }
+                       }
+               } else {                                                /* no more subs on line so make idle */
+                       ast_verb(4,"Killing only sub %d\n", sub->callid);
                        l->hookstate = SKINNY_ONHOOK;
-                       transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
-                       transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
-                       transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
-               } else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) {
-                       transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
-                       transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
-                       transmit_ringer_mode(s, SKINNY_RING_OFF);
-                       transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
-                       do_housekeeping(s);
+                       transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
+                       l->activesub = NULL;
+                       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
+                       if (sub->parent == d->activeline) {
+                               transmit_activatecallplane(d, l);
+                               transmit_closereceivechannel(d, sub);
+                               transmit_stopmediatransmission(d, sub);
+                               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
+                               transmit_ringer_mode(d, SKINNY_RING_OFF);
+                               transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
+                               /* we should check to see if we can start the ringer if another line is ringing */
+                       }
                }
        }
        ast_mutex_lock(&sub->lock);
@@ -3173,10 +3936,12 @@ static int skinny_hangup(struct ast_channel *ast)
        sub->alreadygone = 0;
        sub->outgoing = 0;
        if (sub->rtp) {
-               ast_rtp_destroy(sub->rtp);
+               ast_rtp_instance_destroy(sub->rtp);
                sub->rtp = NULL;
        }
        ast_mutex_unlock(&sub->lock);
+       ast_free(sub);
+       ast_module_unref(ast_module_info->self);
        return 0;
 }
 
@@ -3185,30 +3950,37 @@ static int skinny_answer(struct ast_channel *ast)
        int res = 0;
        struct skinny_subchannel *sub = ast->tech_pvt;
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
-       struct skinnysession *s = d->session;
-       char exten[AST_MAX_EXTENSION] = "";
+       struct skinny_device *d = l->device;
 
-       ast_copy_string(exten, S_OR(ast->macroexten, ast->exten), sizeof(exten));
+       if (sub->blindxfer) {
+               if (skinnydebug)
+                       ast_debug(1, "skinny_answer(%s) on %s@%s-%d with BlindXFER, transferring\n",
+                               ast->name, l->name, d->name, sub->callid);
+               ast_setstate(ast, AST_STATE_UP);
+               skinny_transfer(sub);
+               return 0;
+       }
 
        sub->cxmode = SKINNY_CX_SENDRECV;
        if (!sub->rtp) {
                start_rtp(sub);
        }
        if (skinnydebug)
-               ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid);
+               ast_verb(1, "skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid);
        if (ast->_state != AST_STATE_UP) {
                ast_setstate(ast, AST_STATE_UP);
        }
 
-       transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+       transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
        /* order matters here...
           for some reason, transmit_callinfo must be before transmit_callstate,
           or you won't get keypad messages in some situations. */
-       transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2);
-       transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
-       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
-       transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
+       transmit_callinfo(d, ast->connected.id.name, ast->connected.id.number, l->lastnumberdialed, l->lastnumberdialed, l->instance, sub->callid, 2);
+       transmit_callstateonly(d, sub, SKINNY_CONNECTED);
+       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
+       transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
+       transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
+       l->activesub = sub;
        return res;
 }
 
@@ -3225,21 +3997,21 @@ static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
 
        switch(ast->fdno) {
        case 0:
-               f = ast_rtp_read(sub->rtp);     /* RTP Audio */
+               f = ast_rtp_instance_read(sub->rtp, 0); /* RTP Audio */
                break;
        case 1:
-               f = ast_rtcp_read(sub->rtp);    /* RTCP Control Channel */
+               f = ast_rtp_instance_read(sub->rtp, 1); /* RTCP Control Channel */
                break;
        case 2:
-               f = ast_rtp_read(sub->vrtp);    /* RTP Video */
+               f = ast_rtp_instance_read(sub->vrtp, 0); /* RTP Video */
                break;
        case 3:
-               f = ast_rtcp_read(sub->vrtp);   /* RTCP Control Channel for video */
+               f = ast_rtp_instance_read(sub->vrtp, 1); /* RTCP Control Channel for video */
                break;
 #if 0
        case 5:
                /* Not yet supported */
-               f = ast_udptl_read(sub->udptl); /* UDPTL for T.38 */
+               f = ast_udptl_read(sub->udptl); /* UDPTL for T.38 */
                break;
 #endif
        default:
@@ -3249,9 +4021,9 @@ static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
        if (ast) {
                /* We already hold the channel lock */
                if (f->frametype == AST_FRAME_VOICE) {
-                       if (f->subclass != ast->nativeformats) {
-                               ast_debug(1, "Oooh, format changed to %d\n", f->subclass);
-                               ast->nativeformats = f->subclass;
+                       if (f->subclass.codec != ast->nativeformats) {
+                               ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(f->subclass.codec));
+                               ast->nativeformats = f->subclass.codec;
                                ast_set_read_format(ast, ast->readformat);
                                ast_set_write_format(ast, ast->writeformat);
                        }
@@ -3282,16 +4054,20 @@ static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
                        return 0;
                }
        } else {
-               if (!(frame->subclass & ast->nativeformats)) {
-                       ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
-                               frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
+               if (!(frame->subclass.codec & ast->nativeformats)) {
+                       char buf[256];
+                       ast_log(LOG_WARNING, "Asked to transmit frame type %s, while native formats is %s (read/write = %s/%s)\n",
+                               ast_getformatname(frame->subclass.codec),
+                               ast_getformatname_multiple(buf, sizeof(buf), ast->nativeformats),
+                               ast_getformatname(ast->readformat),
+                               ast_getformatname(ast->writeformat));
                        return -1;
                }
        }
        if (sub) {
                ast_mutex_lock(&sub->lock);
                if (sub->rtp) {
-                       res = ast_rtp_write(sub->rtp, frame);
+                       res = ast_rtp_instance_write(sub->rtp, frame);
                }
                ast_mutex_unlock(&sub->lock);
        }
@@ -3320,15 +4096,44 @@ static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned in
 #if 0
        struct skinny_subchannel *sub = ast->tech_pvt;
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
+       struct skinny_device *d = l->device;
        int tmp;
        /* not right */
        sprintf(tmp, "%d", digit);
-       transmit_tone(d->session, digit, l->instance, sub->callid);
+       transmit_tone(d, digit, l->instance, sub->callid);
 #endif
        return -1; /* Stop inband indications */
 }
 
+static int get_devicestate(struct skinny_line *l)
+{
+       struct skinny_subchannel *sub;
+       int res = AST_DEVICE_UNKNOWN;
+
+       if (!l)
+               res = AST_DEVICE_INVALID;
+       else if (!l->device)
+               res = AST_DEVICE_UNAVAILABLE;
+       else if (l->dnd)
+               res = AST_DEVICE_BUSY;
+       else {
+               if (l->hookstate == SKINNY_ONHOOK) {
+                       res = AST_DEVICE_NOT_INUSE;
+               } else {
+                       res = AST_DEVICE_INUSE;
+               }
+
+               AST_LIST_TRAVERSE(&l->sub, sub, list) {
+                       if (sub->onhold) {
+                               res = AST_DEVICE_ONHOLD;
+                               break;
+                       }
+               }
+       }
+
+       return res;
+}
+
 static char *control2str(int ind) {
        char *tmp;
 
@@ -3367,6 +4172,12 @@ static char *control2str(int ind) {
                return "Hold";
        case AST_CONTROL_UNHOLD:
                return "Unhold";
+       case AST_CONTROL_SRCUPDATE:
+               return "Media Source Update";
+       case AST_CONTROL_CONNECTED_LINE:
+               return "Connected Line";
+       case AST_CONTROL_REDIRECTING:
+               return "Redirecting";
        case -1:
                return "Stop tone";
        default:
@@ -3377,68 +4188,153 @@ static char *control2str(int ind) {
        }
 }
 
+static int skinny_transfer(struct skinny_subchannel *sub)
+{
+       struct skinny_subchannel *xferor; /* the sub doing the transferring */
+       struct skinny_subchannel *xferee; /* the sub being transferred */
+       struct ast_tone_zone_sound *ts = NULL;
+               
+       if (ast_bridged_channel(sub->owner) || ast_bridged_channel(sub->related->owner)) {
+               if (sub->xferor) {
+                       xferor = sub;
+                       xferee = sub->related;
+               } else {
+                       xferor = sub;
+                       xferee = sub->related;
+               }
+               
+               if (skinnydebug) {
+                       ast_debug(1, "Transferee channels (local/remote): %s and %s\n",
+                               xferee->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
+                       ast_debug(1, "Transferor channels (local/remote): %s and %s\n",
+                               xferor->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
+               }
+               if (ast_bridged_channel(xferor->owner)) {
+                       if (ast_bridged_channel(xferee->owner)) {
+                               ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
+                       }
+                       if (xferor->owner->_state == AST_STATE_RING) {
+                               /* play ringing inband */
+                               if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
+                                       ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                                       ts = ast_tone_zone_sound_unref(ts);
+                               }
+                       }
+                       if (skinnydebug)
+                               ast_debug(1, "Transfer Masquerading %s to %s\n",
+                                       xferee->owner->name, ast_bridged_channel(xferor->owner)?ast_bridged_channel(xferor->owner)->name:"");
+                       if (ast_channel_masquerade(xferee->owner, ast_bridged_channel(xferor->owner))) {
+                               ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+                                       ast_bridged_channel(xferor->owner)->name, xferee->owner->name);
+                               return -1;
+                       }
+               } else if (ast_bridged_channel(xferee->owner)) {
+                       ast_queue_control(xferee->owner, AST_CONTROL_UNHOLD);
+                       if (xferor->owner->_state == AST_STATE_RING) {
+                               /* play ringing inband */
+                               if ((ts = ast_get_indication_tone(xferor->owner->zone, "ring"))) {
+                                       ast_playtones_start(xferor->owner, 0, ts->data, 1);
+                                       ts = ast_tone_zone_sound_unref(ts);
+                               }
+                       }
+                       if (skinnydebug)
+                               ast_debug(1, "Transfer Masquerading %s to %s\n",
+                                       xferor->owner->name, ast_bridged_channel(xferee->owner)?ast_bridged_channel(xferee->owner)->name:"");
+                       if (ast_channel_masquerade(xferor->owner, ast_bridged_channel(xferee->owner))) {
+                               ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+                                       ast_bridged_channel(xferee->owner)->name, xferor->owner->name);
+                               return -1;
+                       }
+                       return 0;
+               } else {
+                       if (option_debug)
+                               ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n",
+                                       xferor->owner->name, xferee->owner->name);
+               }
+       }
+       return 0;
+}
 
 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
 {
        struct skinny_subchannel *sub = ast->tech_pvt;
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
+       struct skinny_device *d = l->device;
        struct skinnysession *s = d->session;
-       char exten[AST_MAX_EXTENSION] = "";
 
        if (!s) {
                ast_log(LOG_NOTICE, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind), ast->name);
                return -1;
        }
 
-       ast_copy_string(exten, S_OR(ast->macroexten, ast->exten), sizeof(exten));
-
        if (skinnydebug)
                ast_verb(3, "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
        switch(ind) {
        case AST_CONTROL_RINGING:
+               if (sub->blindxfer) {
+                       if (skinnydebug)
+                               ast_debug(1, "Channel %s set up for Blind Xfer, so Xfer rather than ring device\n", ast->name);
+                       skinny_transfer(sub);
+                       break;
+               }
                if (ast->_state != AST_STATE_UP) {
                        if (!sub->progress) {
-                               transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
-                               transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
-                               transmit_dialednumber(s, exten, l->instance, sub->callid);
-                               transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid);
-                               transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
+                               if (!d->earlyrtp) {
+                                       transmit_tone(d, SKINNY_ALERT, l->instance, sub->callid);
+                               }
+                               transmit_callstateonly(d, sub, SKINNY_RINGOUT);
+                               transmit_dialednumber(d, l->lastnumberdialed, l->instance, sub->callid);
+                               transmit_displaypromptstatus(d, "Ring Out", 0, l->instance, sub->callid);
+                               transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */
                                sub->ringing = 1;
-                               break;
+                               if (!d->earlyrtp) {
+                                       break;
+                               }
                        }
                }
-               return -1;
+               return -1; /* Tell asterisk to provide inband signalling */
        case AST_CONTROL_BUSY:
                if (ast->_state != AST_STATE_UP) {
-                       transmit_tone(s, SKINNY_BUSYTONE, l->instance, sub->callid);
-                       transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
+                       if (!d->earlyrtp) {
+                               transmit_tone(d, SKINNY_BUSYTONE, l->instance, sub->callid);
+                       }
+                       transmit_callstateonly(d, sub, SKINNY_BUSY);
                        sub->alreadygone = 1;
                        ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
-                       break;
+                       if (!d->earlyrtp) {
+                               break;
+                       }
                }
-               return -1;
+               return -1; /* Tell asterisk to provide inband signalling */
        case AST_CONTROL_CONGESTION:
                if (ast->_state != AST_STATE_UP) {
-                       transmit_tone(s, SKINNY_REORDER, l->instance, sub->callid);
-                       transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
+                       if (!d->earlyrtp) {
+                               transmit_tone(d, SKINNY_REORDER, l->instance, sub->callid);
+                       }
+                       transmit_callstateonly(d, sub, SKINNY_CONGESTION);
                        sub->alreadygone = 1;
                        ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
-                       break;
+                       if (!d->earlyrtp) {
+                               break;
+                       }
                }
-               return -1;
+               return -1; /* Tell asterisk to provide inband signalling */
        case AST_CONTROL_PROGRESS:
                if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
-                       transmit_tone(s, SKINNY_ALERT, l->instance, sub->callid);
-                       transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
-                       transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid);
-                       transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, exten, exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
+                       if (!d->earlyrtp) {
+                               transmit_tone(d, SKINNY_ALERT, l->instance, sub->callid);
+                       }
+                       transmit_callstateonly(d, sub, SKINNY_PROGRESS);
+                       transmit_displaypromptstatus(d, "Call Progress", 0, l->instance, sub->callid);
+                       transmit_callinfo(d, ast->cid.cid_name, ast->cid.cid_num, S_OR(ast->connected.id.name, l->lastnumberdialed), S_OR(ast->connected.id.number, l->lastnumberdialed), l->instance, sub->callid, 2); /* 2 = outgoing from phone */
                        sub->progress = 1;
-                       break;
+                       if (!d->earlyrtp) {
+                               break;
+                       }
                }
-               return -1;
-       case -1:
-               transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+               return -1; /* Tell asterisk to provide inband signalling */
+       case -1:  /* STOP_TONE */
+               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                break;
        case AST_CONTROL_HOLD:
                ast_moh_start(ast, data, l->mohinterpret);
@@ -3448,22 +4344,33 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
                break;
        case AST_CONTROL_PROCEEDING:
                break;
+       case AST_CONTROL_SRCUPDATE:
+               ast_rtp_instance_new_source(sub->rtp);
+               break;
+       case AST_CONTROL_CONNECTED_LINE:
+               update_connectedline(sub, data, datalen);
+               break;
        default:
                ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
-               return -1;
+               return -1; /* Tell asterisk to provide inband signalling */
        }
        return 0;
 }
 
-static struct ast_channel *skinny_new(struct skinny_line *l, int state)
+static struct ast_channel *skinny_new(struct skinny_line *l, int state, const char *linkedid)
 {
        struct ast_channel *tmp;
        struct skinny_subchannel *sub;
-       struct skinny_device *d = l->parent;
+       struct skinny_device *d = l->device;
        struct ast_variable *v = NULL;
        int fmt;
 
-       tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
+       if (!l->device) {
+               ast_log(LOG_WARNING, "Device for line %s is not registered.\n", l->name);
+               return NULL;
+       }
+
+       tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, l->accountcode, l->exten, l->context, linkedid, l->amaflags, "Skinny/%s@%s-%d", l->name, d->name, callnums);
        if (!tmp) {
                ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
                return NULL;
@@ -3483,20 +4390,28 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
                        sub->nat = l->nat;
                        sub->parent = l;
                        sub->onhold = 0;
+                       sub->blindxfer = 0;
+                       sub->xferor = 0;
+                       sub->related = NULL;
 
-                       sub->next = l->sub;
-                       l->sub = sub;
+                       AST_LIST_INSERT_HEAD(&l->sub, sub, list);
+                       //l->activesub = sub;
                }
                tmp->tech = &skinny_tech;
                tmp->tech_pvt = sub;
                tmp->nativeformats = l->capability;
                if (!tmp->nativeformats)
+                       // Should throw an error
                        tmp->nativeformats = default_capability;
                fmt = ast_best_codec(tmp->nativeformats);
-               if (skinnydebug)
-                       ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
+               if (skinnydebug) {
+                       char buf[256];
+                       ast_verb(1, "skinny_new: tmp->nativeformats=%s fmt=%s\n",
+                               ast_getformatname_multiple(buf, sizeof(buf), tmp->nativeformats),
+                               ast_getformatname(fmt));
+               }
                if (sub->rtp) {
-                       ast_channel_set_fd(tmp, 0, ast_rtp_fd(sub->rtp));
+                       ast_channel_set_fd(tmp, 0, ast_rtp_instance_fd(sub->rtp, 0));
                }
                if (state == AST_STATE_RING) {
                        tmp->rings = 1;
@@ -3509,13 +4424,24 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
                        ast_string_field_set(tmp, language, l->language);
                if (!ast_strlen_zero(l->accountcode))
                        ast_string_field_set(tmp, accountcode, l->accountcode);
+               if (!ast_strlen_zero(l->parkinglot))
+                       ast_string_field_set(tmp, parkinglot, l->parkinglot);
                if (l->amaflags)
                        tmp->amaflags = l->amaflags;
 
                ast_module_ref(ast_module_info->self);
                tmp->callgroup = l->callgroup;
                tmp->pickupgroup = l->pickupgroup;
-               ast_string_field_set(tmp, call_forward, l->call_forward);
+
+               /* XXX Need to figure out how to handle CFwdNoAnswer */
+               if (l->cfwdtype & SKINNY_CFWD_ALL) {
+                       ast_string_field_set(tmp, call_forward, l->call_forward_all);
+               } else if (l->cfwdtype & SKINNY_CFWD_BUSY) {
+                       if (get_devicestate(l) != AST_DEVICE_NOT_INUSE) {
+                               ast_string_field_set(tmp, call_forward, l->call_forward_busy);
+                       }
+               }
+
                ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
                ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
 
@@ -3547,9 +4473,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, int state)
 static int skinny_hold(struct skinny_subchannel *sub)
 {
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
-       struct skinnysession *s = d->session;
-       struct skinny_req *req;
+       struct skinny_device *d = l->device;
 
        /* Don't try to hold a channel that doesn't exist */
        if (!sub || !sub->owner)
@@ -3557,33 +4481,18 @@ static int skinny_hold(struct skinny_subchannel *sub)
 
        /* Channel needs to be put on hold */
        if (skinnydebug)
-               ast_verbose("Putting on Hold(%d)\n", l->instance);
+               ast_verb(1, "Putting on Hold(%d)\n", l->instance);
 
        ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
                S_OR(l->mohsuggest, NULL),
                !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
 
-       if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
-               return 0;
-
-       req->data.activatecallplane.lineInstance = htolel(l->instance);
-       transmit_response(s, req);
+       transmit_activatecallplane(d, l);
+       transmit_closereceivechannel(d, sub);
+       transmit_stopmediatransmission(d, sub);
 
-       if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
-               return 0;
-
-       req->data.closereceivechannel.conferenceId = htolel(sub->callid);
-       req->data.closereceivechannel.partyId = htolel(sub->callid);
-       transmit_response(s, req);
-
-       if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
-               return 0;
-
-       req->data.stopmedia.conferenceId = htolel(sub->callid);
-       req->data.stopmedia.passThruPartyId = htolel(sub->callid);
-       transmit_response(s, req);
-
-       transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
+       transmit_callstateonly(d, sub, SKINNY_HOLD);
+       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_WINK);
        sub->onhold = 1;
        return 1;
 }
@@ -3591,9 +4500,7 @@ static int skinny_hold(struct skinny_subchannel *sub)
 static int skinny_unhold(struct skinny_subchannel *sub)
 {
        struct skinny_line *l = sub->parent;
-       struct skinny_device *d = l->parent;
-       struct skinnysession *s = d->session;
-       struct skinny_req *req;
+       struct skinny_device *d = l->device;
 
        /* Don't try to unhold a channel that doesn't exist */
        if (!sub || !sub->owner)
@@ -3601,34 +4508,113 @@ static int skinny_unhold(struct skinny_subchannel *sub)
 
        /* Channel is on hold, so we will unhold */
        if (skinnydebug)
-               ast_verbose("Taking off Hold(%d)\n", l->instance);
+               ast_verb(1, "Taking off Hold(%d)\n", l->instance);
 
        ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
 
-       if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
-               return 0;
-
-       req->data.activatecallplane.lineInstance = htolel(l->instance);
-       transmit_response(s, req);
+       transmit_activatecallplane(d, l);
 
-       transmit_connect(s, sub);
-       transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+       transmit_connect(d, sub);
+       transmit_callstateonly(d, sub, SKINNY_CONNECTED);
+       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+       l->hookstate = SKINNY_OFFHOOK;
        sub->onhold = 0;
        return 1;
 }
 
+static int handle_hold_button(struct skinny_subchannel *sub)
+{
+       if (!sub)
+               return -1;
+       if (sub->related) {
+               skinny_hold(sub);
+               skinny_unhold(sub->related);
+               sub->parent->activesub = sub->related;
+       } else {
+               if (sub->onhold) {
+                       skinny_unhold(sub);
+                       transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_CONNECTED);
+               } else {
+                       skinny_hold(sub);
+                       transmit_selectsoftkeys(sub->parent->device, sub->parent->instance, sub->callid, KEYDEF_ONHOLD);
+               }
+       }
+       return 1;
+}
+
+static int handle_transfer_button(struct skinny_subchannel *sub)
+{
+       struct skinny_line *l = sub->parent;
+       struct skinny_device *d = l->device;
+       struct skinny_subchannel *newsub;
+       struct ast_channel *c;
+       pthread_t t;
+
+       if (!sub) {
+               ast_verbose("Transfer: No subchannel to transfer\n");
+               return -1;
+       }
+       if (!sub->related) {
+               /* Another sub has not been created so this must be first XFER press */
+               if (!sub->onhold) {
+                       skinny_hold(sub);
+               }
+               c = skinny_new(l, AST_STATE_DOWN, NULL);
+               if (c) {
+                       newsub = c->tech_pvt;
+                       /* point the sub and newsub at each other so we know they are related */
+                       newsub->related = sub;
+                       sub->related = newsub;
+                       newsub->xferor = 1;
+                       l->activesub = newsub;
+                       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, newsub->callid);
+                       if (skinnydebug)
+                               ast_debug(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                       transmit_displaymessage(d, NULL, l->instance, newsub->callid); /* clear display */
+                       transmit_tone(d, SKINNY_DIALTONE, l->instance, newsub->callid);
+                       transmit_selectsoftkeys(d, l->instance, newsub->callid, KEYDEF_OFFHOOKWITHFEAT);
+                       /* start the switch thread */
+                       if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
+                               ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
+                               ast_hangup(c);
+                       }
+               } else {
+                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+               }
+       } else {
+               /* We already have a related sub so we can either complete XFER or go into BLINDXFER (or cancel BLINDXFER */
+               if (sub->blindxfer) {
+                       /* toggle blindxfer off */
+                       sub->blindxfer = 0;
+                       sub->related->blindxfer = 0;
+                       /* we really need some indications */
+               } else {
+                       /* We were doing attended transfer */
+                       if (sub->owner->_state == AST_STATE_DOWN || sub->related->owner->_state == AST_STATE_DOWN) {
+                               /* one of the subs so we cant transfer yet, toggle blindxfer on */
+                               sub->blindxfer = 1;
+                               sub->related->blindxfer = 1;
+                       } else {
+                               /* big assumption we have two channels, lets transfer */
+                               skinny_transfer(sub);
+                       }
+               }
+       }
+       return 0;
+}
+
 static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
 {
        if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
                return -1;
 
-       transmit_response(s, req);
-       do_housekeeping(s);
+       transmit_response(s->device, req);
        return 1;
 }
 
 static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
 {
+       struct skinny_device *d = NULL;
        char name[16];
        int res;
 
@@ -3641,11 +4627,34 @@ static int handle_register_message(struct skinny_req *req, struct skinnysession
                        return -1;
 
                snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
-               transmit_response(s, req);
+
+               /* transmit_respons in line as we don't have a valid d */
+               ast_mutex_lock(&s->lock);
+
+               if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
+                       ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
+                       ast_mutex_unlock(&s->lock);
+                       return -1;
+               }
+
+               memset(s->outbuf, 0, sizeof(s->outbuf));
+               memcpy(s->outbuf, req, skinny_header_size);
+               memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
+
+               res = write(s->fd, s->outbuf, letohl(req->len)+8);
+
+               if (res != letohl(req->len)+8) {
+                       ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
+               }
+       
+               ast_mutex_unlock(&s->lock);
+
                return 0;
        }
        ast_verb(3, "Device '%s' successfully registered\n", name);
 
+       d = s->device;
+       
        if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
                return -1;
 
@@ -3656,18 +4665,56 @@ static int handle_register_message(struct skinny_req *req, struct skinnysession
        req->data.regack.res2[0] = '0';
        req->data.regack.res2[1] = '\0';
        req->data.regack.secondaryKeepAlive = htolel(keep_alive);
-       transmit_response(s, req);
+       transmit_response(d, req);
        if (skinnydebug)
-               ast_verbose("Requesting capabilities\n");
+               ast_verb(1, "Requesting capabilities\n");
 
        if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
                return -1;
 
-       transmit_response(s, req);
+       transmit_response(d, req);
 
        return res;
 }
 
+static int handle_callforward_button(struct skinny_subchannel *sub, int cfwdtype)
+{
+       struct skinny_line *l = sub->parent;
+       struct skinny_device *d = l->device;
+       struct ast_channel *c = sub->owner;
+       pthread_t t;
+
+       if (l->hookstate == SKINNY_ONHOOK) {
+               l->hookstate = SKINNY_OFFHOOK;
+               transmit_speaker_mode(d, SKINNY_SPEAKERON);
+               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
+       }
+       if (skinnydebug)
+               ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+
+       if (l->cfwdtype & cfwdtype) {
+               set_callforwards(l, NULL, cfwdtype);
+               ast_safe_sleep(c, 500);
+               transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
+               transmit_callstate(d, l->instance, SKINNY_ONHOOK, sub->callid);
+               transmit_displaynotify(d, "CFwd disabled", 10);
+               if (sub->owner && sub->owner->_state != AST_STATE_UP) {
+                       ast_indicate(c, -1);
+                       ast_hangup(c);
+               }
+               transmit_cfwdstate(d, l);
+       } else {
+               l->getforward = cfwdtype;
+               transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+               transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
+               if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
+                       ast_log(LOG_WARNING, "Unable to create switch thread: %s\n", strerror(errno));
+                       ast_hangup(c);
+               }
+       }
+       return 0;
+}
 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
 {
        /* no response necessary */
@@ -3707,14 +4754,15 @@ static int handle_keypad_button_message(struct skinny_req *req, struct skinnyses
                ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
        }
 
-       f.subclass = dgt;
+       f.subclass.integer = dgt;
 
        f.src = "skinny";
 
        if (lineInstance && callReference)
                sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
        else
-               sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+               sub = d->activeline->activesub;
+               //sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
 
        if (!sub)
                return 0;
@@ -3729,17 +4777,17 @@ static int handle_keypad_button_message(struct skinny_req *req, struct skinnyses
                f.frametype = AST_FRAME_DTMF_END;
                ast_queue_frame(sub->owner, &f);
                /* XXX This seriously needs to be fixed */
-               if (sub->next && sub->next->owner) {
+               if (AST_LIST_NEXT(sub, list) && AST_LIST_NEXT(sub, list)->owner) {
                        if (sub->owner->_state == 0) {
                                f.frametype = AST_FRAME_DTMF_BEGIN;
-                               ast_queue_frame(sub->next->owner, &f);
+                               ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
                        }
                        f.frametype = AST_FRAME_DTMF_END;
-                       ast_queue_frame(sub->next->owner, &f);
+                       ast_queue_frame(AST_LIST_NEXT(sub, list)->owner, &f);
                }
        } else {
                if (skinnydebug)
-                       ast_verbose("No owner: %s\n", l->name);
+                       ast_verb(1, "No owner: %s\n", l->name);
        }
        return 1;
 }
@@ -3761,7 +4809,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
        instance = letohl(req->data.stimulus.stimulusInstance);
        callreference = letohl(req->data.stimulus.callreference); 
        if (skinnydebug)
-               ast_verbose("callreference in handle_stimulus_message is '%d'\n", callreference);
+               ast_verb(1, "callreference in handle_stimulus_message is '%d'\n", callreference);
 
        /*  Note that this call should be using the passed in instance and callreference */
        sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
@@ -3771,6 +4819,7 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                if (!l) {
                        return 0;
                }
+               sub = l->activesub;
        } else {
                l = sub->parent;
        }
@@ -3778,34 +4827,35 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
        switch(event) {
        case STIMULUS_REDIAL:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Redial(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: Redial(%d/%d)\n", instance, callreference);
 
                if (ast_strlen_zero(l->lastnumberdialed)) {
                        ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
                        l->hookstate = SKINNY_ONHOOK;
-                       transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
-                       transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance);
+                       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
+                       transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
                        break;
                }
 
-               c = skinny_new(l, AST_STATE_DOWN);
+               c = skinny_new(l, AST_STATE_DOWN, NULL);
                if (!c) {
                        ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
                } else {
                        sub = c->tech_pvt;
                        l = sub->parent;
+                       l->activesub = sub;
                        if (l->hookstate == SKINNY_ONHOOK) {
                                l->hookstate = SKINNY_OFFHOOK;
-                               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+                               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
                        }
                        if (skinnydebug)
-                               ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
-                       transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+                               ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
 
                        if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
-                               transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+                               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                        }
                        ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
                        if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
@@ -3819,13 +4869,13 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                struct skinny_speeddial *sd;
 
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: SpeedDial(%d/%d)\n", instance, callreference);
                if (!(sd = find_speeddial_by_instance(d, instance, 0))) {
                        return 0;
                }
 
                if (!sub || !sub->owner)
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                else
                        c = sub->owner;
 
@@ -3834,19 +4884,20 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                } else {
                        sub = c->tech_pvt;
                        l = sub->parent;
+                       l->activesub = sub;
                        if (l->hookstate == SKINNY_ONHOOK) {
                                l->hookstate = SKINNY_OFFHOOK;
-                               transmit_speaker_mode(s, SKINNY_SPEAKERON);
-                               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+                               transmit_speaker_mode(d, SKINNY_SPEAKERON);
+                               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
                        }
                        if (skinnydebug)
-                               ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
-                       transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+                               ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
 
                        if (!ast_ignore_pattern(c->context, sd->exten)) {
-                               transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+                               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                        }
                        if (ast_exists_extension(c, c->context, sd->exten, 1, l->cid_num)) {
                                ast_copy_string(c->exten, sd->exten, sizeof(c->exten));
@@ -3863,33 +4914,28 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                break;
        case STIMULUS_HOLD:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Hold(%d/%d)\n", instance, callreference);
-
-               if (!sub)
-                       break;
-
-               if (sub->onhold) {
-                       skinny_unhold(sub);
-               } else {
-                       skinny_hold(sub);
-               }
+                       ast_verb(1, "Received Stimulus: Hold(%d/%d)\n", instance, callreference);
+               handle_hold_button(sub);
                break;
        case STIMULUS_TRANSFER:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
-               /* XXX figure out how to transfer */
+                       ast_verb(1, "Received Stimulus: Transfer(%d/%d)\n", instance, callreference);
+               if (l->transfer)
+                       handle_transfer_button(sub);
+               else
+                       transmit_displaynotify(d, "Transfer disabled", 10);
                break;
        case STIMULUS_CONFERENCE:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Conference(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: Conference(%d/%d)\n", instance, callreference);
                /* XXX determine the best way to pull off a conference.  Meetme? */
                break;
        case STIMULUS_VOICEMAIL:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: Voicemail(%d/%d)\n", instance, callreference);
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -3898,25 +4944,26 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                } else {
                        sub = c->tech_pvt;
                        l = sub->parent;
+                       l->activesub = sub;
 
                        if (ast_strlen_zero(l->vmexten))  /* Exit the call if no VM pilot */
                                break;
 
                        if (l->hookstate == SKINNY_ONHOOK){
                                l->hookstate = SKINNY_OFFHOOK;
-                               transmit_speaker_mode(s, SKINNY_SPEAKERON);
-                               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+                               transmit_speaker_mode(d, SKINNY_SPEAKERON);
+                               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
                        }
 
                        if (skinnydebug)
-                               ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                               ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
 
-                       transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+                       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
 
-                       if (!ast_ignore_pattern(c->context, vmexten)) {
-                               transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+                       if (!ast_ignore_pattern(c->context, l->vmexten)) {
+                               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                        }
 
                        if (ast_exists_extension(c, c->context, l->vmexten, 1, l->cid_num)) {
@@ -3931,45 +4978,104 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                }
                break;
        case STIMULUS_CALLPARK:
+               {
+               int extout;
+               char message[32];
+
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
-               /* XXX Park the call */
+                       ast_verb(1, "Received Stimulus: Park Call(%d/%d)\n", instance, callreference);
+
+               if ((sub && sub->owner) && (sub->owner->_state ==  AST_STATE_UP)){
+                       c = sub->owner;
+                       if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
+                               snprintf(message, sizeof(message), "Call Parked at: %d", extout);
+                               transmit_displaynotify(d, message, 10);
+                       } else {
+                               transmit_displaynotify(d, "Call Park failed", 10);
+                       }
+               } else {
+                       transmit_displaynotify(d, "Call Park not available", 10);
+               }
+               }
                break;
-       case STIMULUS_FORWARDALL:
+       case STIMULUS_DND:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
-               /* Why is DND under FORWARDALL? */
-               /* Because it's the same thing. */
+                       ast_verb(1, "Received Stimulus: DND (%d/%d)\n", instance, callreference);
 
                /* Do not disturb */
                if (l->dnd != 0){
                        ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
                        l->dnd = 0;
-                       transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
-                       transmit_displaynotify(s, "DnD disabled", 10);
+                       transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
+                       transmit_displaynotify(d, "DnD disabled", 10);
                } else {
                        ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
                        l->dnd = 1;
-                       transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_OFF);
-                       transmit_displaynotify(s, "DnD enabled", 10);
+                       transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
+                       transmit_displaynotify(d, "DnD enabled", 10);
+               }
+               break;
+       case STIMULUS_FORWARDALL:
+               if (skinnydebug)
+                       ast_verb(1, "Received Stimulus: Forward All(%d/%d)\n", instance, callreference);
+
+               if (!sub || !sub->owner) {
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
+               } else {
+                       c = sub->owner;
+               }
+
+               if (!c) {
+                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+               } else {
+                       sub = c->tech_pvt;
+                       handle_callforward_button(sub, SKINNY_CFWD_ALL);
                }
                break;
        case STIMULUS_FORWARDBUSY:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: Forward Busy (%d/%d)\n", instance, callreference);
+
+               if (!sub || !sub->owner) {
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
+               } else {
+                       c = sub->owner;
+               }
+
+               if (!c) {
+                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+               } else {
+                       sub = c->tech_pvt;
+                       handle_callforward_button(sub, SKINNY_CFWD_BUSY);
+               }
                break;
        case STIMULUS_FORWARDNOANSWER:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: Forward No Answer (%d/%d)\n", instance, callreference);
+
+#if 0 /* Not sure how to handle this yet */
+               if (!sub || !sub->owner) {
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
+               } else {
+                       c = sub->owner;
+               }
+
+               if (!c) {
+                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+               } else {
+                       sub = c->tech_pvt;
+                       handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
+               }
+#endif
                break;
        case STIMULUS_DISPLAY:
                /* Not sure what this is */
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Display(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: Display(%d/%d)\n", instance, callreference);
                break;
        case STIMULUS_LINE:
                if (skinnydebug)
-                       ast_verbose("Received Stimulus: Line(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Stimulus: Line(%d/%d)\n", instance, callreference);
 
                l = find_line_by_instance(d, instance);
 
@@ -3977,38 +5083,39 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                        return 0;
                }
 
+               d->activeline = l;
+
                /* turn the speaker on */
-               transmit_speaker_mode(s, SKINNY_SPEAKERON);
-               transmit_ringer_mode(s, SKINNY_RING_OFF);
-               transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+               transmit_speaker_mode(d, SKINNY_SPEAKERON);
+               transmit_ringer_mode(d, SKINNY_RING_OFF);
+               transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
 
                l->hookstate = SKINNY_OFFHOOK;
 
-               ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
-
                if (sub && sub->outgoing) {
                        /* We're answering a ringing call */
                        ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
-                       transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
-                       transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
-                       transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
-                       transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
-                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
+                       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
+                       transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
+                       transmit_callstateonly(d, sub, SKINNY_CONNECTED);
+                       transmit_displaypromptstatus(d, "Connected", 0, l->instance, sub->callid);
+                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
                        start_rtp(sub);
                        ast_setstate(sub->owner, AST_STATE_UP);
                } else {
                        if (sub && sub->owner) {
                                ast_debug(1, "Current subchannel [%s] already has owner\n", sub->owner->name);
                        } else {
-                               c = skinny_new(l, AST_STATE_DOWN);
+                               c = skinny_new(l, AST_STATE_DOWN, NULL);
                                if (c) {
                                        sub = c->tech_pvt;
-                                       transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+                                       l->activesub = sub;
+                                       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
                                        if (skinnydebug)
-                                               ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
-                                       transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-                                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
+                                               ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                                       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                                       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
 
                                        /* start the switch thread */
                                        if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
@@ -4023,10 +5130,10 @@ static int handle_stimulus_message(struct skinny_req *req, struct skinnysession
                break;
        default:
                if (skinnydebug)
-                       ast_verbose("RECEIVED UNKNOWN STIMULUS:  %d(%d/%d)\n", event, instance, callreference);
+                       ast_verb(1, "RECEIVED UNKNOWN STIMULUS:  %d(%d/%d)\n", event, instance, callreference);
                break;
        }
-       ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_UNKNOWN, "Skinny/%s@%s", l->name, d->name);
 
        return 1;
 }
@@ -4037,57 +5144,76 @@ static int handle_offhook_message(struct skinny_req *req, struct skinnysession *
        struct skinny_line *l;
        struct skinny_subchannel *sub;
        struct ast_channel *c;
+       struct skinny_line *tmp;
        pthread_t t;
-       int unknown1;
-       int unknown2;
-
-       unknown1 = letohl(req->data.offhook.unknown1);
-       unknown2 = letohl(req->data.offhook.unknown2);
+       int instance;
+       int reference;
 
-       sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+       /* if any line on a device is offhook, than the device must be offhook, 
+          unless we have shared lines CCM seems that it would never get here, 
+          but asterisk does, so we may need to do more work.  Ugly, we should 
+          probably move hookstate from line to device, afterall, it's actually
+           a device that changes hookstates */
 
-       if (!sub) {
-               l = find_line_by_instance(d, d->lastlineinstance);
-               if (!l) {
+       AST_LIST_TRAVERSE(&d->lines, tmp, list) {
+               if (tmp->hookstate == SKINNY_OFFHOOK) {
+                       ast_verbose(VERBOSE_PREFIX_3 "Got offhook message when device (%s@%s) already offhook\n", tmp->name, d->name);
                        return 0;
                }
+       }
+
+       instance = letohl(req->data.offhook.instance);
+       reference = letohl(req->data.offhook.reference);
+
+       if (instance) {
+               sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+               if (!sub) {
+                       l = find_line_by_instance(d, d->lastlineinstance);
+                       if (!l) {
+                               return 0;
+                       }
+               } else {
+                       l = sub->parent;
+               }
        } else {
-               l = sub->parent;
+               l = d->activeline;
+               sub = l->activesub;
        }
 
-       transmit_ringer_mode(s, SKINNY_RING_OFF);
+       transmit_ringer_mode(d, SKINNY_RING_OFF);
        l->hookstate = SKINNY_OFFHOOK;
 
-       ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
 
        if (sub && sub->onhold) {
                return 1;
        }
 
-       transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+       transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
 
        if (sub && sub->outgoing) {
                /* We're answering a ringing call */
                ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
-               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
-               transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
-               transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
-               transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
+               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
+               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
+               transmit_callstateonly(d, sub, SKINNY_CONNECTED);
+               transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
                start_rtp(sub);
                ast_setstate(sub->owner, AST_STATE_UP);
        } else {
                if (sub && sub->owner) {
                        ast_debug(1, "Current sub [%s] already has owner\n", sub->owner->name);
                } else {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                        if (c) {
                                sub = c->tech_pvt;
-                               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+                               l->activesub = sub;
+                               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
                                if (skinnydebug)
-                                       ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
-                               transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-                               transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                               transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
+                                       ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                               transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                               transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                               transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
 
                                /* start the switch thread */
                                if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
@@ -4107,53 +5233,64 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
        struct skinny_device *d = s->device;
        struct skinny_line *l;
        struct skinny_subchannel *sub;
-       int unknown1;
-       int unknown2;
-
-       unknown1 = letohl(req->data.onhook.unknown1);
-       unknown2 = letohl(req->data.onhook.unknown2);
+       int instance;
+       int reference;
+       int onlysub = 0;
 
-       sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
+       instance = letohl(req->data.onhook.instance);
+       reference = letohl(req->data.onhook.reference);
 
-       if (!sub) {
-               return 0;
+       if (instance && reference) {
+               sub = find_subchannel_by_instance_reference(d, instance, reference);
+               if (!sub) {
+                       return 0;
+               }
+               l = sub->parent;
+       } else {
+               l = d->activeline;
+               sub = l->activesub;
+               if (!sub) {
+                       return 0;
+               }
        }
-       l = sub->parent;
 
        if (l->hookstate == SKINNY_ONHOOK) {
                /* Something else already put us back on hook */
                return 0;
        }
-       l->hookstate = SKINNY_ONHOOK;
 
-       ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
 
        if (sub->onhold) {
                return 0;
        }
 
+       if (!AST_LIST_NEXT(sub, list)) {
+               onlysub = 1;
+       } else {
+               AST_LIST_REMOVE(&l->sub, sub, list);
+       }
+
        sub->cxmode = SKINNY_CX_RECVONLY;
-       transmit_callstate(s, l->instance, l->hookstate, sub->callid);
-       if (skinnydebug)
-               ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name);
-       if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) {
+       if (onlysub || sub->xferor){  /* is this the only call to this device? */
+               l->hookstate = SKINNY_ONHOOK;
+               if (skinnydebug)
+                       ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, reference);
+       }
+
+       transmit_callstate(d, l->instance, l->hookstate, sub->callid);
+       if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
                /* We're allowed to transfer, we have two active calls and
                   we made at least one of the calls.  Let's try and transfer */
-
-#if 0
-               if ((res = attempt_transfer(p)) < 0) {
-                       if (sub->next && sub->next->owner) {
-                               sub->next->alreadygone = 1;
-                               ast_queue_hangup(sub->next->owner,1);
-                       }
-               } else if (res) {
-                       ast_log(LOG_WARNING, "Transfer attempt failed\n");
-                       return 0;
-               }
-#endif
+               handle_transfer_button(sub);
        } else {
                /* Hangup the current call */
                /* If there is another active call, skinny_hangup will ring the phone with the other call */
+               if (sub->xferor && sub->related){
+                       sub->related->related = NULL;
+                       sub->related->blindxfer = 0;
+               }
+
                if (sub->owner) {
                        sub->alreadygone = 1;
                        ast_queue_hangup(sub->owner);
@@ -4162,9 +5299,6 @@ static int handle_onhook_message(struct skinny_req *req, struct skinnysession *s
                                l->name, d->name, sub->callid);
                }
        }
-       if ((l->hookstate == SKINNY_ONHOOK) && (sub->next && !sub->next->rtp)) {
-               do_housekeeping(s);
-       }
        return 1;
 }
 
@@ -4173,8 +5307,9 @@ static int handle_capabilities_res_message(struct skinny_req *req, struct skinny
        struct skinny_device *d = s->device;
        struct skinny_line *l;
        uint32_t count = 0;
-       int codecs = 0;
+       format_t codecs = 0;
        int i;
+       char buf[256];
 
        count = letohl(req->data.caps.count);
        if (count > SKINNY_MAX_CAPABILITIES) {
@@ -4183,20 +5318,20 @@ static int handle_capabilities_res_message(struct skinny_req *req, struct skinny
        }
 
        for (i = 0; i < count; i++) {
-               int acodec = 0;
+               format_t acodec = 0;
                int scodec = 0;
                scodec = letohl(req->data.caps.caps[i].codec);
                acodec = codec_skinny2ast(scodec);
                if (skinnydebug)
-                       ast_verbose("Adding codec capability '%d (%d)'\n", acodec, scodec);
+                       ast_verb(1, "Adding codec capability '%" PRId64 " (%d)'\n", acodec, scodec);
                codecs |= acodec;
        }
 
-       d->capability &= codecs;
-       ast_verbose("Device capability set to '%d'\n", d->capability);
-       for (l = d->lines; l; l = l->next) {
+       d->capability = d->confcapability & codecs;
+       ast_verb(0, "Device capability set to '%s'\n", ast_getformatname_multiple(buf, sizeof(buf), d->capability));
+       AST_LIST_TRAVERSE(&d->lines, l, list) {
                ast_mutex_lock(&l->lock);
-               l->capability = d->capability;
+               l->capability = l->confcapability & d->capability;
                ast_mutex_unlock(&l->lock);
        }
 
@@ -4221,10 +5356,10 @@ static int handle_speed_dial_stat_req_message(struct skinny_req *req, struct ski
                return -1;
 
        req->data.speeddialreq.speedDialNumber = htolel(instance);
-       snprintf(req->data.speeddial.speedDialDirNumber, sizeof(req->data.speeddial.speedDialDirNumber), sd->exten);
-       snprintf(req->data.speeddial.speedDialDisplayName, sizeof(req->data.speeddial.speedDialDisplayName), sd->label);
+       ast_copy_string(req->data.speeddial.speedDialDirNumber, sd->exten, sizeof(req->data.speeddial.speedDialDirNumber));
+       ast_copy_string(req->data.speeddial.speedDialDisplayName, sd->label, sizeof(req->data.speeddial.speedDialDisplayName));
 
-       transmit_response(s, req);
+       transmit_response(d, req);
        return 1;
 }
 
@@ -4237,7 +5372,7 @@ static int handle_line_state_req_message(struct skinny_req *req, struct skinnyse
 
        instance = letohl(req->data.line.lineNumber);
 
-       ast_mutex_lock(&devicelock);
+       AST_LIST_LOCK(&devices);
 
        l = find_line_by_instance(d, instance);
 
@@ -4249,7 +5384,7 @@ static int handle_line_state_req_message(struct skinny_req *req, struct skinnyse
                return 0;
        }
 
-       ast_mutex_unlock(&devicelock);
+       AST_LIST_UNLOCK(&devices);
 
        if (!(req = req_alloc(sizeof(struct line_stat_res_message), LINE_STAT_RES_MESSAGE)))
                return -1;
@@ -4262,19 +5397,19 @@ static int handle_line_state_req_message(struct skinny_req *req, struct skinnyse
                memcpy(req->data.linestat.lineDirNumber, l->name, sizeof(req->data.linestat.lineDirNumber));
                memcpy(req->data.linestat.lineDisplayName, l->label, sizeof(req->data.linestat.lineDisplayName));
        }
-       transmit_response(s,req);
+       transmit_response(d, req);
        return 1;
 }
 
 static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s)
 {
-       struct timeval tv = ast_tvnow();
+       struct timeval now = ast_tvnow();
        struct ast_tm cmtime;
 
        if (!(req = req_alloc(sizeof(struct definetimedate_message), DEFINETIMEDATE_MESSAGE)))
                return -1;
 
-       ast_localtime(&tv, &cmtime, NULL);
+       ast_localtime(&now, &cmtime, NULL);
        req->data.definetimedate.year = htolel(cmtime.tm_year+1900);
        req->data.definetimedate.month = htolel(cmtime.tm_mon+1);
        req->data.definetimedate.dayofweek = htolel(cmtime.tm_wday);
@@ -4283,8 +5418,8 @@ static int handle_time_date_req_message(struct skinny_req *req, struct skinnyses
        req->data.definetimedate.minute = htolel(cmtime.tm_min);
        req->data.definetimedate.seconds = htolel(cmtime.tm_sec);
        req->data.definetimedate.milliseconds = htolel(cmtime.tm_usec / 1000);
-       req->data.definetimedate.timestamp = htolel(tv.tv_sec);
-       transmit_response(s, req);
+       req->data.definetimedate.timestamp = htolel(now.tv_sec);
+       transmit_response(s->device, req);
        return 1;
 }
 
@@ -4315,9 +5450,9 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                                req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
                                req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
 
-                               for (l = d->lines; l; l = l->next) {
+                               AST_LIST_TRAVERSE(&d->lines, l, list) {
                                        if (l->instance == lineInstance) {
-                                               ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+                                               ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
                                                req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
                                                req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
                                                lineInstance++;
@@ -4328,9 +5463,9 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                                }
 
                                if (!btnSet) {
-                                       for (sd = d->speeddials; sd; sd = sd->next) {
+                                       AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                                                if (sd->isHint && sd->instance == lineInstance) {
-                                                       ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+                                                       ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
                                                        req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
                                                        req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
                                                        lineInstance++;
@@ -4346,9 +5481,9 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                                req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
                                req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
 
-                               for (l = d->lines; l; l = l->next) {
+                               AST_LIST_TRAVERSE(&d->lines, l, list) {
                                        if (l->instance == lineInstance) {
-                                               ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+                                               ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
                                                req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
                                                req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
                                                lineInstance++;
@@ -4359,9 +5494,9 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                                }
 
                                if (!btnSet) {
-                                       for (sd = d->speeddials; sd; sd = sd->next) {
+                                       AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                                                if (sd->isHint && sd->instance == lineInstance) {
-                                                       ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+                                                       ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
                                                        req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
                                                        req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
                                                        lineInstance++;
@@ -4369,7 +5504,7 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                                                        btnSet = 1;
                                                        break;
                                                } else if (!sd->isHint && sd->instance == speeddialInstance) {
-                                                       ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
+                                                       ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
                                                        req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
                                                        req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance);
                                                        speeddialInstance++;
@@ -4384,9 +5519,9 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                                req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE);
                                req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
 
-                               for (l = d->lines; l; l = l->next) {
+                               AST_LIST_TRAVERSE(&d->lines, l, list) {
                                        if (l->instance == lineInstance) {
-                                               ast_verbose("Adding button: %d, %d\n", BT_LINE, lineInstance);
+                                               ast_verb(0, "Adding button: %d, %d\n", BT_LINE, lineInstance);
                                                req->data.buttontemplate.definition[i].buttonDefinition = BT_LINE;
                                                req->data.buttontemplate.definition[i].instanceNumber = htolel(lineInstance);
                                                lineInstance++;
@@ -4400,9 +5535,9 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                                req->data.buttontemplate.definition[i].buttonDefinition = BT_NONE;
                                req->data.buttontemplate.definition[i].instanceNumber = 0;
 
-                               for (sd = d->speeddials; sd; sd = sd->next) {
+                               AST_LIST_TRAVERSE(&d->speeddials, sd, list) {
                                        if (!sd->isHint && sd->instance == speeddialInstance) {
-                                               ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
+                                               ast_verb(0, "Adding button: %d, %d\n", BT_SPEEDDIAL, speeddialInstance);
                                                req->data.buttontemplate.definition[i].buttonDefinition = BT_SPEEDDIAL;
                                                req->data.buttontemplate.definition[i].instanceNumber = htolel(speeddialInstance - 1);
                                                speeddialInstance++;
@@ -4415,7 +5550,7 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
                        case BT_NONE:
                                break;
                        default:
-                               ast_verbose("Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
+                               ast_verb(0, "Adding button: %d, %d\n", btn[i].buttonDefinition, 0);
                                req->data.buttontemplate.definition[i].buttonDefinition = htolel(btn[i].buttonDefinition);
                                req->data.buttontemplate.definition[i].instanceNumber = htolel(0);
                                buttonCount++;
@@ -4429,10 +5564,10 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski
        req->data.buttontemplate.totalButtonCount = htolel(buttonCount);
 
        if (skinnydebug)
-               ast_verbose("Sending %d template to %s\n",
+               ast_verb(1, "Sending %d template to %s\n",
                                        d->type,
                                        d->name);
-       transmit_response(s, req);
+       transmit_response(d, req);
        return 1;
 }
 
@@ -4442,8 +5577,8 @@ static int handle_version_req_message(struct skinny_req *req, struct skinnysessi
        if (!(req = req_alloc(sizeof(struct version_res_message), VERSION_RES_MESSAGE)))
                return -1;
 
-       snprintf(req->data.version.version, sizeof(req->data.version.version), d->version_id);
-       transmit_response(s, req);
+       ast_copy_string(req->data.version.version, d->version_id, sizeof(req->data.version.version));
+       transmit_response(d, req);
        return 1;
 }
 
@@ -4457,7 +5592,7 @@ static int handle_server_request_message(struct skinny_req *req, struct skinnyse
                        sizeof(req->data.serverres.server[0].serverName));
        req->data.serverres.serverListenPort[0] = htolel(ourport);
        req->data.serverres.serverIpAddr[0] = htolel(d->ourip.s_addr);
-       transmit_response(s, req);
+       transmit_response(d, req);
        return 1;
 }
 
@@ -4465,7 +5600,7 @@ static int handle_alarm_message(struct skinny_req *req, struct skinnysession *s)
 {
        /* no response necessary */
        if (skinnydebug)
-               ast_verbose("Received Alarm Message: %s\n", req->data.alarm.displayMessage);
+               ast_verb(1, "Received Alarm Message: %s\n", req->data.alarm.displayMessage);
 
        return 1;
 }
@@ -4476,8 +5611,8 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc
        struct skinny_line *l;
        struct skinny_subchannel *sub;
        struct ast_format_list fmt;
-       struct sockaddr_in sin;
-       struct sockaddr_in us;
+       struct sockaddr_in sin = { 0, };
+       struct sockaddr_in us = { 0, };
        uint32_t addr;
        int port;
        int status;
@@ -4504,15 +5639,15 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc
        l = sub->parent;
 
        if (sub->rtp) {
-               ast_rtp_set_peer(sub->rtp, &sin);
-               ast_rtp_get_us(sub->rtp, &us);
+               ast_rtp_instance_set_remote_address(sub->rtp, &sin);
+               ast_rtp_instance_get_local_address(sub->rtp, &us);
        } else {
                ast_log(LOG_ERROR, "No RTP structure, this is very bad\n");
                return 0;
        }
 
        if (skinnydebug)
-               ast_verbose("ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
+               ast_verb(1, "ipaddr = %s:%d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
 
        if (!(req = req_alloc(sizeof(struct start_media_transmission_message), START_MEDIA_TRANSMISSION_MESSAGE)))
                return -1;
@@ -4520,7 +5655,7 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc
        fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
 
        if (skinnydebug)
-               ast_verbose("Setting payloadType to '%d' (%d ms)\n", fmt.bits, fmt.cur_ms);
+               ast_verb(1, "Setting payloadType to '%s' (%d ms)\n", ast_getformatname(fmt.bits), fmt.cur_ms);
 
        req->data.startmedia.conferenceId = htolel(sub->callid);
        req->data.startmedia.passThruPartyId = htolel(sub->callid);
@@ -4532,7 +5667,7 @@ static int handle_open_receive_channel_ack_message(struct skinny_req *req, struc
        req->data.startmedia.qualifier.vad = htolel(0);
        req->data.startmedia.qualifier.packets = htolel(0);
        req->data.startmedia.qualifier.bitRate = htolel(0);
-       transmit_response(s, req);
+       transmit_response(d, req);
 
        return 1;
 }
@@ -4546,7 +5681,7 @@ static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysessi
        pthread_t t;
 
        if (skinnydebug)
-               ast_verbose("Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
+               ast_verb(1, "Received Enbloc Call: %s\n", req->data.enbloccallmessage.calledParty);
 
        sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
 
@@ -4559,7 +5694,7 @@ static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysessi
                l = sub->parent;
        }
 
-       c = skinny_new(l, AST_STATE_DOWN);
+       c = skinny_new(l, AST_STATE_DOWN, NULL);
 
        if(!c) {
                ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
@@ -4567,14 +5702,15 @@ static int handle_enbloc_call_message(struct skinny_req *req, struct skinnysessi
                l->hookstate = SKINNY_OFFHOOK;
 
                sub = c->tech_pvt;
-               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+               l->activesub = sub;
+               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
                if (skinnydebug)
-                       ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
-               transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-               transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
+                       ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+               transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+               transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
 
                if (!ast_ignore_pattern(c->context, req->data.enbloccallmessage.calledParty)) {
-                       transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+                       transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                }
                ast_copy_string(c->exten, req->data.enbloccallmessage.calledParty, sizeof(c->exten));
                if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
@@ -4593,6 +5729,7 @@ static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinny
        int x;
        int y;
        const struct soft_key_definitions *softkeymode = soft_key_default_definitions;
+       struct skinny_device *d = s->device;
 
        if (!(req = req_alloc(sizeof(struct soft_key_set_res_message), SOFT_KEY_SET_RES_MESSAGE)))
                return -1;
@@ -4614,8 +5751,8 @@ static int handle_soft_key_set_req_message(struct skinny_req *req, struct skinny
                }
                softkeymode++;
        }
-       transmit_response(s,req);
-       transmit_selectsoftkeys(s, 0, 0, KEYDEF_ONHOOK);
+       transmit_response(d, req);
+       transmit_selectsoftkeys(d, 0, 0, KEYDEF_ONHOOK);
        return 1;
 }
 
@@ -4647,31 +5784,31 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
 
        if (!l) {
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
+                       ast_verb(1, "Received Softkey Event: %d(%d/%d)\n", event, instance, callreference);
                return 0;
        }
 
-       ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
+       ast_devstate_changed(AST_DEVICE_INUSE, "Skinny/%s@%s", l->name, d->name);
 
        switch(event) {
        case SOFTKEY_NONE:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: None(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: None(%d/%d)\n", instance, callreference);
                break;
        case SOFTKEY_REDIAL:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Redial(%d/%d)\n", instance, callreference);
 
                if (ast_strlen_zero(l->lastnumberdialed)) {
                        ast_log(LOG_WARNING, "Attempted redial, but no previously dialed number found.\n");
                        l->hookstate = SKINNY_ONHOOK;
-                       transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
-                       transmit_callstate(s, l->instance, SKINNY_ONHOOK, instance);
+                       transmit_speaker_mode(d, SKINNY_SPEAKEROFF);
+                       transmit_callstate(d, l->instance, SKINNY_ONHOOK, instance);
                        break;
                }
 
                if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
                } else {
                        c = sub->owner;
                }
@@ -4680,19 +5817,20 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                        ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
                } else {
                        sub = c->tech_pvt;
+                       l->activesub = sub;
                        if (l->hookstate == SKINNY_ONHOOK) {
                                l->hookstate = SKINNY_OFFHOOK;
-                               transmit_speaker_mode(s, SKINNY_SPEAKERON);
-                               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+                               transmit_speaker_mode(d, SKINNY_SPEAKERON);
+                               transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
                        }
                        if (skinnydebug)
-                               ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
-                       transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGOUT);
+                               ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_RINGOUT);
 
                        if (!ast_ignore_pattern(c->context, l->lastnumberdialed)) {
-                               transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
+                               transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
                        }
                        ast_copy_string(c->exten, l->lastnumberdialed, sizeof(c->exten));
                        if (ast_pthread_create(&t, NULL, skinny_newcall, c)) {
@@ -4703,16 +5841,14 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                break;
        case SOFTKEY_NEWCALL:  /* Actually the DIAL softkey */
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: New Call(%d/%d)\n", instance, callreference);
 
-               if (!sub || !sub->owner) {
-                       c = skinny_new(l, AST_STATE_DOWN);
-               } else {
-                       c = sub->owner;
-               }
-
-               /* transmit_ringer_mode(s,SKINNY_RING_OFF);
-               transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); */
+               /* New Call ALWAYS gets a new sub-channel */
+               c = skinny_new(l, AST_STATE_DOWN, NULL);
+               sub = c->tech_pvt;
+       
+               /* transmit_ringer_mode(d, SKINNY_RING_OFF);
+               transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON); */
 
                /* l->hookstate = SKINNY_OFFHOOK; */
 
@@ -4720,17 +5856,20 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                        ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
                } else {
                        sub = c->tech_pvt;
+                       l->activesub = sub;
                        if (l->hookstate == SKINNY_ONHOOK) {
                                l->hookstate = SKINNY_OFFHOOK;
-                               transmit_speaker_mode(s, SKINNY_SPEAKERON);
-                               transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
+                               transmit_speaker_mode(d, SKINNY_SPEAKERON);
                        }
+                       ast_verb(1, "Call-id: %d\n", sub->callid);
+
+                       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
 
                        if (skinnydebug)
-                               ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
-                       transmit_displaymessage(s, NULL, l->instance, sub->callid); /* clear display */
-                       transmit_tone(s, SKINNY_DIALTONE, l->instance, sub->callid);
-                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_OFFHOOK);
+                               ast_verb(1, "Attempting to Clear display on Skinny %s@%s\n", l->name, d->name);
+                       transmit_displaymessage(d, NULL, l->instance, sub->callid); /* clear display */
+                       transmit_tone(d, SKINNY_DIALTONE, l->instance, sub->callid);
+                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_OFFHOOK);
 
                        /* start the switch thread */
                        if (ast_pthread_create(&t, NULL, skinny_ss, c)) {
@@ -4741,157 +5880,237 @@ static int handle_soft_key_event_message(struct skinny_req *req, struct skinnyse
                break;
        case SOFTKEY_HOLD:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
-
-               if (sub) {
-                       if (sub->onhold) {
-                               skinny_unhold(sub);
-                       } else {
-                               skinny_hold(sub);
-                       }
-               }
-                               
+                       ast_verb(1, "Received Softkey Event: Hold(%d/%d)\n", instance, callreference);
+               handle_hold_button(sub);        
                break;
        case SOFTKEY_TRNSFER:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
-               /* XXX figure out how to transfer */
+                       ast_verb(1, "Received Softkey Event: Transfer(%d/%d)\n", instance, callreference);
+               if (l->transfer)
+                       handle_transfer_button(sub);
+               else
+                       transmit_displaynotify(d, "Transfer disabled", 10);
+
                break;
-       case SOFTKEY_CFWDALL:
+       case SOFTKEY_DND:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: DND(%d/%d)\n", instance, callreference);
 
                /* Do not disturb */
                if (l->dnd != 0){
                        ast_verb(3, "Disabling DND on %s@%s\n", l->name, d->name);
                        l->dnd = 0;
-                       transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_ON);
-                       transmit_displaynotify(s, "DnD disabled", 10);
+                       transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_ON);
+                       transmit_displaynotify(d, "DnD disabled", 10);
                } else {
                        ast_verb(3, "Enabling DND on %s@%s\n", l->name, d->name);
                        l->dnd = 1;
-                       transmit_lamp_indication(s, STIMULUS_FORWARDALL, 1, SKINNY_LAMP_OFF);
-                       transmit_displaynotify(s, "DnD enabled", 10);
+                       transmit_lamp_indication(d, STIMULUS_DND, 1, SKINNY_LAMP_OFF);
+                       transmit_displaynotify(d, "DnD enabled", 10);
+               }
+               break;
+       case SOFTKEY_CFWDALL:
+               if (skinnydebug)
+                       ast_verb(1, "Received Softkey Event: Forward All(%d/%d)\n", instance, callreference);
+
+               if (!sub || !sub->owner) {
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
+               } else {
+                       c = sub->owner;
+               }
+
+               if (!c) {
+                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+               } else {
+                       sub = c->tech_pvt;
+                       l->activesub = sub;
+                       handle_callforward_button(sub, SKINNY_CFWD_ALL);
                }
                break;
        case SOFTKEY_CFWDBUSY:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Forward Busy (%d/%d)\n", instance, callreference);
+
+               if (!sub || !sub->owner) {
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
+               } else {
+                       c = sub->owner;
+               }
+
+               if (!c) {
+                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+               } else {
+                       sub = c->tech_pvt;
+                       l->activesub = sub;
+                       handle_callforward_button(sub, SKINNY_CFWD_BUSY);
+               }
                break;
        case SOFTKEY_CFWDNOANSWER:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Forward No Answer (%d/%d)\n", instance, callreference);
+
+#if 0 /* Not sure how to handle this yet */
+               if (!sub || !sub->owner) {
+                       c = skinny_new(l, AST_STATE_DOWN, NULL);
+               } else {
+                       c = sub->owner;
+               }
+
+               if (!c) {
+                       ast_log(LOG_WARNING, "Unable to create channel for %s@%s\n", l->name, d->name);
+               } else {
+                       sub = c->tech_pvt;
+                       l->activesub = sub;
+                       handle_callforward_button(sub, SKINNY_CFWD_NOANSWER);
+               }
+#endif
                break;
        case SOFTKEY_BKSPC:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Backspace(%d/%d)\n", instance, callreference);
                break;
        case SOFTKEY_ENDCALL:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: End Call(%d/%d)\n", instance, callreference);
 
                if (l->hookstate == SKINNY_ONHOOK) {
                        /* Something else already put us back on hook */
                        break;
                }
                if (sub) {
+                       int onlysub = 0;
+
+                       if (!AST_LIST_NEXT(sub, list)) {
+                               onlysub = 1;
+                       } else {
+                               AST_LIST_REMOVE(&l->sub, sub, list);
+                       }
+
                        sub->cxmode = SKINNY_CX_RECVONLY;
-                       l->hookstate = SKINNY_ONHOOK;
-                       transmit_callstate(s, l->instance, l->hookstate, sub->callid);
+                       if (onlysub || sub->xferor){    /*Are there other calls to this device */
+                               l->hookstate = SKINNY_ONHOOK;
+                               if (skinnydebug)
+                                       ast_debug(1, "Skinny %s@%s-%d went on hook\n", l->name, d->name, callreference);
+                       }
+
+                       transmit_callstate(d, l->instance, l->hookstate, sub->callid);
+                       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
                        if (skinnydebug)
-                               ast_verbose("Skinny %s@%s went on hook\n", l->name, d->name);
-                       if (l->transfer && (sub->owner && sub->next && sub->next->owner) && ((!sub->outgoing) || (sub->next && !sub->next->outgoing))) {
+                               ast_verb(1, "Skinny %s@%s went on hook\n", l->name, d->name);
+                       if (l->transfer && sub->xferor && sub->owner->_state >= AST_STATE_RING) {
                                /* We're allowed to transfer, we have two active calls and
                                   we made at least one of the calls.  Let's try and transfer */
-
-#if 0
-                               if ((res = attempt_transfer(p)) < 0) {
-                                       if (sub->next && sub->next->owner) {
-                                               sub->next->alreadygone = 1;
-                                               ast_queue_hangup(sub->next->owner, 1);
-                                       }
-                               } else if (res) {
-                                       ast_log(LOG_WARNING, "Transfer attempt failed\n");
-                                       break;
-                               }
-#endif
+                               handle_transfer_button(sub);
                        } else {
                                /* Hangup the current call */
                                /* If there is another active call, skinny_hangup will ring the phone with the other call */
+                               if (sub->xferor && sub->related){
+                                       sub->related->related = NULL;
+                                       sub->related->blindxfer = 0;
+                               }
+
                                if (sub->owner) {
                                        sub->alreadygone = 1;
                                        ast_queue_hangup(sub->owner);
                                } else {
                                        ast_log(LOG_WARNING, "Skinny(%s@%s-%d) channel already destroyed\n",
                                                l->name, d->name, sub->callid);
-                               }
+                               }
                        }
-                       if ((l->hookstate == SKINNY_ONHOOK) && (sub->next && !sub->next->rtp)) {
-                               do_housekeeping(s);
+                       if ((l->hookstate == SKINNY_ONHOOK) && (AST_LIST_NEXT(sub, list) && !AST_LIST_NEXT(sub, list)->rtp)) {
+                               ast_devstate_changed(AST_DEVICE_NOT_INUSE, "Skinny/%s@%s", l->name, d->name);
                        }
                }
                break;
        case SOFTKEY_RESUME:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Resume(%d/%d)\n", instance, callreference);
+
+               if (sub) {
+                       if (sub->onhold) {
+                               skinny_unhold(sub);
+                               transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
+                       } else {
+                               skinny_hold(sub);
+                               transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_ONHOLD);
+                       }
+               }
+
                break;
        case SOFTKEY_ANSWER:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Answer(%d/%d)\n", instance, callreference);
 
-               transmit_ringer_mode(s,SKINNY_RING_OFF);
-               transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
-
-               l->hookstate = SKINNY_OFFHOOK;
+               transmit_ringer_mode(d, SKINNY_RING_OFF);
+               transmit_lamp_indication(d, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
+               if (l->hookstate == SKINNY_ONHOOK) {
+                       transmit_speaker_mode(d, SKINNY_SPEAKERON);
+                       l->hookstate = SKINNY_OFFHOOK;
+               }
 
                if (sub && sub->outgoing) {
                        /* We're answering a ringing call */
                        ast_queue_control(sub->owner, AST_CONTROL_ANSWER);
-                       transmit_callstate(s, l->instance, SKINNY_OFFHOOK, sub->callid);
-                       transmit_tone(s, SKINNY_SILENCE, l->instance, sub->callid);
-                       transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
-                       transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_CONNECTED);
+                       transmit_callstate(d, l->instance, SKINNY_OFFHOOK, sub->callid);
+                       transmit_tone(d, SKINNY_SILENCE, l->instance, sub->callid);
+                       transmit_callstateonly(d, sub, SKINNY_CONNECTED);
+                       transmit_selectsoftkeys(d, l->instance, sub->callid, KEYDEF_CONNECTED);
                        start_rtp(sub);
                        ast_setstate(sub->owner, AST_STATE_UP);
                }
                break;
        case SOFTKEY_INFO:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Info(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Info(%d/%d)\n", instance, callreference);
                break;
        case SOFTKEY_CONFRN:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Conference(%d/%d)\n", instance, callreference);
                /* XXX determine the best way to pull off a conference.  Meetme? */
                break;
        case SOFTKEY_PARK:
+               {
+               int extout;
+               char message[32];
+
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
-               /* XXX Park the call */
+                       ast_verb(1, "Received Softkey Event: Park Call(%d/%d)\n", instance, callreference);
+
+               if ((sub && sub->owner) && (sub->owner->_state ==  AST_STATE_UP)){
+                       c = sub->owner;
+                       if (!ast_masq_park_call(ast_bridged_channel(c), c, 0, &extout)) {
+                               snprintf(message, sizeof(message), "Call Parked at: %d", extout);
+                               transmit_displaynotify(d, message, 10);
+                       } else {
+                               transmit_displaynotify(d, "Call Park failed", 10);
+                       }
+               } else {
+                       transmit_displaynotify(d, "Call Park not available", 10);
+               }
+               }
                break;
        case SOFTKEY_JOIN:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Join(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Join(%d/%d)\n", instance, callreference);
                break;
        case SOFTKEY_MEETME:
                /* XXX How is this different from CONFRN? */
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Meetme(%d/%d)\n", instance, callreference);
                break;
        case SOFTKEY_PICKUP:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Pickup(%d/%d)\n", instance, callreference);
                break;
        case SOFTKEY_GPICKUP:
                if (skinnydebug)
-                       ast_verbose("Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
+                       ast_verb(1, "Received Softkey Event: Group Pickup(%d/%d)\n", instance, callreference);
                break;
        default:
                if (skinnydebug)
-                       ast_verbose("Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
+                       ast_verb(1, "Received unknown Softkey Event: %d(%d/%d)\n", event, instance, callreference);
                break;
        }
-       ast_device_state_changed("Skinny/%s@%s", l->name, d->name);
 
        return 1;
 }
@@ -4907,12 +6126,12 @@ static int handle_soft_key_template_req_message(struct skinny_req *req, struct s
                return -1;
 
        req->data.softkeytemplate.softKeyOffset = htolel(0);
-       req->data.softkeytemplate.softKeyCount  = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
+       req->data.softkeytemplate.softKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
        req->data.softkeytemplate.totalSoftKeyCount = htolel(sizeof(soft_key_template_default) / sizeof(struct soft_key_template_definition));
        memcpy(req->data.softkeytemplate.softKeyTemplateDefinition,
                soft_key_template_default,
                sizeof(soft_key_template_default));
-       transmit_response(s,req);
+       transmit_response(s->device, req);
        return 1;
 }
 
@@ -4938,13 +6157,17 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                return 0;
        }
 
-       switch(letohl(req->e))  {
+       SKINNY_DEVONLY(if (skinnydebug > 1) {
+               ast_verb(4, "Received %s from %s\n", message2str(req->e), s->device->name);
+       })
+
+       switch(letohl(req->e)) {
        case KEEP_ALIVE_MESSAGE:
                res = handle_keep_alive_message(req, s);
                break;
        case REGISTER_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Device %s is attempting to register\n", req->data.reg.name);
+                       ast_verb(1, "Device %s is attempting to register\n", req->data.reg.name);
 
                res = handle_register_message(req, s);
                break;
@@ -4959,17 +6182,21 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                int callReference;
 
                if (skinnydebug)
-                       ast_verbose("Collected digit: [%d]\n", letohl(req->data.keypad.button));
+                       ast_verb(1, "Collected digit: [%d]\n", letohl(req->data.keypad.button));
 
                lineInstance = letohl(req->data.keypad.lineInstance);
                callReference = letohl(req->data.keypad.callReference);
 
-               sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
+               if (lineInstance) {
+                       sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
+               } else {
+                       sub = d->activeline->activesub;
+               }
 
-               if (sub && (sub->owner && sub->owner->_state <  AST_STATE_UP)) {
+               if (sub && ((sub->owner && sub->owner->_state <  AST_STATE_UP) || sub->onhold)) {
                        char dgt;
                        int digit = letohl(req->data.keypad.button);
-       
+
                        if (digit == 14) {
                                dgt = '*';
                        } else if (digit == 15) {
@@ -4978,12 +6205,12 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                                dgt = '0' + digit;
                        } else {
                                /* digit=10-13 (A,B,C,D ?), or
-                               * digit is bad value
-                               *
-                               * probably should not end up here, but set
-                               * value for backward compatibility, and log
-                               * a warning.
-                               */
+                               * digit is bad value
+                               *
+                               * probably should not end up here, but set
+                               * value for backward compatibility, and log
+                               * a warning.
+                               */
                                dgt = '0' + digit;
                                ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
                        }
@@ -4992,7 +6219,7 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                        d->exten[strlen(d->exten)+1] = '\0';
                } else
                        res = handle_keypad_button_message(req, s);
-           }
+               }
                break;
        case ENBLOC_CALL_MESSAGE:
                res = handle_enbloc_call_message(req, s);
@@ -5008,42 +6235,42 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                break;
        case CAPABILITIES_RES_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received CapabilitiesRes\n");
+                       ast_verb(1, "Received CapabilitiesRes\n");
 
                res = handle_capabilities_res_message(req, s);
                break;
        case SPEED_DIAL_STAT_REQ_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received SpeedDialStatRequest\n");
+                       ast_verb(1, "Received SpeedDialStatRequest\n");
 
                res = handle_speed_dial_stat_req_message(req, s);
                break;
        case LINE_STATE_REQ_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received LineStatRequest\n");
+                       ast_verb(1, "Received LineStatRequest\n");
                res = handle_line_state_req_message(req, s);
                break;
        case TIME_DATE_REQ_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received Time/Date Request\n");
+                       ast_verb(1, "Received Time/Date Request\n");
 
                res = handle_time_date_req_message(req, s);
                break;
        case BUTTON_TEMPLATE_REQ_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Buttontemplate requested\n");
+                       ast_verb(1, "Buttontemplate requested\n");
 
                res = handle_button_template_req_message(req, s);
                break;
        case VERSION_REQ_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Version Request\n");
+                       ast_verb(1, "Version Request\n");
 
                res = handle_version_req_message(req, s);
                break;
        case SERVER_REQUEST_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received Server Request\n");
+                       ast_verb(1, "Received Server Request\n");
 
                res = handle_server_request_message(req, s);
                break;
@@ -5052,13 +6279,13 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                break;
        case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received Open Receive Channel Ack\n");
+                       ast_verb(1, "Received Open Receive Channel Ack\n");
 
                res = handle_open_receive_channel_ack_message(req, s);
                break;
        case SOFT_KEY_SET_REQ_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received SoftKeySetReq\n");
+                       ast_verb(1, "Received SoftKeySetReq\n");
 
                res = handle_soft_key_set_req_message(req, s);
                break;
@@ -5067,13 +6294,13 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                break;
        case UNREGISTER_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received Unregister Request\n");
+                       ast_verb(1, "Received Unregister Request\n");
 
                res = handle_unregister_message(req, s);
                break;
        case SOFT_KEY_TEMPLATE_REQ_MESSAGE:
                if (skinnydebug)
-                       ast_verbose("Received SoftKey Template Request\n");
+                       ast_verb(1, "Received SoftKey Template Request\n");
 
                res = handle_soft_key_template_req_message(req, s);
                break;
@@ -5085,7 +6312,7 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
                break;
        default:
                if (skinnydebug)
-                       ast_verbose("RECEIVED UNKNOWN MESSAGE TYPE:  %x\n", letohl(req->e));
+                       ast_verb(1, "RECEIVED UNKNOWN MESSAGE TYPE:  %x\n", letohl(req->e));
                break;
        }
        if (res >= 0 && req)
@@ -5095,43 +6322,36 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s)
 
 static void destroy_session(struct skinnysession *s)
 {
-       struct skinnysession *cur, *prev = NULL;
-       ast_mutex_lock(&sessionlock);
-       cur = sessions;
-       while(cur) {
+       struct skinnysession *cur;
+       AST_LIST_LOCK(&sessions);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&sessions, cur, list) {
                if (cur == s) {
-                       break;
-               }
-               prev = cur;
-               cur = cur->next;
-       }
-       if (cur) {
-               if (prev) {
-                       prev->next = cur->next;
+                       AST_LIST_REMOVE_CURRENT(list);
+                       if (s->fd > -1) 
+                               close(s->fd);
+                       
+                       ast_mutex_destroy(&s->lock);
+                       
+                       ast_free(s);
                } else {
-                       sessions = cur->next;
-               }
-               if (s->fd > -1) {
-                       close(s->fd);
+                       ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
                }
-               ast_mutex_destroy(&s->lock);
-               ast_free(s);
-       } else {
-               ast_log(LOG_WARNING, "Trying to delete nonexistent session %p?\n", s);
        }
-       ast_mutex_unlock(&sessionlock);
+       AST_LIST_TRAVERSE_SAFE_END
+       AST_LIST_UNLOCK(&sessions);
 }
 
 static int get_input(struct skinnysession *s)
 {
        int res;
        int dlen = 0;
+       int *bufaddr;
        struct pollfd fds[1];
 
-       fds[0].fd = s->fd;
+       fds[0].fd = s->fd;
        fds[0].events = POLLIN;
        fds[0].revents = 0;
-       res = poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
+       res = ast_poll(fds, 1, (keep_alive * 1100)); /* If nothing has happen, client is dead */
                                                 /* we add 10% to the keep_alive to deal */
                                                 /* with network delays, etc */
        if (res < 0) {
@@ -5139,24 +6359,24 @@ static int get_input(struct skinnysession *s)
                        ast_log(LOG_WARNING, "Select returned error: %s\n", strerror(errno));
                        return res;
                }
-       } else if (res == 0) {
+       } else if (res == 0) {
                if (skinnydebug)
-                       ast_verbose("Skinny Client was lost, unregistering\n");
+                       ast_verb(1, "Skinny Client was lost, unregistering\n");
                skinny_unregister(NULL, s);
                return -1;
        }
                     
        if (fds[0].revents) {
                ast_mutex_lock(&s->lock);
-               memset(s->inbuf,0,sizeof(s->inbuf));
+               memset(s->inbuf, 0, sizeof(s->inbuf));
                res = read(s->fd, s->inbuf, 4);
                if (res < 0) {
                        ast_log(LOG_WARNING, "read() returned error: %s\n", strerror(errno));
 
                        if (skinnydebug)
-                               ast_verbose("Skinny Client was lost, unregistering\n");
-             
-                       skinny_unregister(NULL,s);
+                               ast_verb(1, "Skinny Client was lost, unregistering\n");
+
+                       skinny_unregister(NULL, s);
                        ast_mutex_unlock(&s->lock);
                        return res;
                } else if (res != 4) {
@@ -5165,23 +6385,24 @@ static int&