Codename Pineapple

Home page | Mailing list | Docs

Last updated: Sat Feb 3 05:00:41 2007

Asterisk developer's documentation :: Codename Pineapple


chan_skinny.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * chan_skinny was developed by Jeremy McNamara & Florian Overkamp
00007  * chan_skinny was heavily modified/fixed by North Antara
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Implementation of the Skinny protocol
00023  *
00024  * \author Jeremy McNamara & Florian Overkamp & North Antara
00025  * \ingroup channel_drivers
00026  */
00027 
00028 
00029 #include "asterisk.h"
00030 
00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 51806 $")
00032 
00033 #include <stdio.h>
00034 #include <stdlib.h>
00035 #include <string.h>
00036 #include <unistd.h>
00037 #include <sys/socket.h>
00038 #include <netinet/in.h>
00039 #include <netinet/tcp.h>
00040 #include <sys/ioctl.h>
00041 #include <net/if.h>
00042 #include <errno.h>
00043 #include <fcntl.h>
00044 #include <netdb.h>
00045 #include <arpa/inet.h>
00046 #include <sys/signal.h>
00047 #include <signal.h>
00048 #include <ctype.h>
00049 
00050 #include "asterisk/lock.h"
00051 #include "asterisk/channel.h"
00052 #include "asterisk/config.h"
00053 #include "asterisk/logger.h"
00054 #include "asterisk/module.h"
00055 #include "asterisk/pbx.h"
00056 #include "asterisk/options.h"
00057 #include "asterisk/lock.h"
00058 #include "asterisk/sched.h"
00059 #include "asterisk/io.h"
00060 #include "asterisk/rtp.h"
00061 #include "asterisk/acl.h"
00062 #include "asterisk/callerid.h"
00063 #include "asterisk/cli.h"
00064 #include "asterisk/say.h"
00065 #include "asterisk/cdr.h"
00066 #include "asterisk/astdb.h"
00067 #include "asterisk/features.h"
00068 #include "asterisk/app.h"
00069 #include "asterisk/musiconhold.h"
00070 #include "asterisk/utils.h"
00071 #include "asterisk/dsp.h"
00072 #include "asterisk/stringfields.h"
00073 #include "asterisk/astobj.h"
00074 #include "asterisk/abstract_jb.h"
00075 #include "asterisk/threadstorage.h"
00076 
00077 /*************************************
00078  * Skinny/Asterisk Protocol Settings *
00079  *************************************/
00080 static const char tdesc[] = "Skinny Client Control Protocol (Skinny)";
00081 static const char config[] = "skinny.conf";
00082 
00083 static int default_capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW;
00084 static struct ast_codec_pref default_prefs;
00085 
00086 enum skinny_codecs {
00087    SKINNY_CODEC_ALAW = 2,
00088    SKINNY_CODEC_ULAW = 4,
00089    SKINNY_CODEC_G723_1 = 9,
00090    SKINNY_CODEC_G729A = 12,
00091    SKINNY_CODEC_G726_32 = 82, /* XXX Which packing order does this translate to? */
00092    SKINNY_CODEC_H261 = 100,
00093    SKINNY_CODEC_H263 = 101
00094 };
00095 
00096 #define DEFAULT_SKINNY_PORT   2000
00097 #define DEFAULT_SKINNY_BACKLOG   2
00098 #define SKINNY_MAX_PACKET  1000
00099 
00100 static int keep_alive = 120;
00101 static char date_format[6] = "D-M-Y";
00102 static char version_id[16] = "P002F202";
00103 
00104 #if __BYTE_ORDER == __LITTLE_ENDIAN
00105 #define letohl(x) (x)
00106 #define letohs(x) (x)
00107 #define htolel(x) (x)
00108 #define htoles(x) (x)
00109 #else
00110 #if defined(SOLARIS) || defined(__Darwin__) || defined(__NetBSD__)
00111 #define __bswap_16(x) \
00112    ((((x) & 0xff00) >> 8) | \
00113     (((x) & 0x00ff) << 8))
00114 #define __bswap_32(x) \
00115    ((((x) & 0xff000000) >> 24) | \
00116     (((x) & 0x00ff0000) >>  8) | \
00117     (((x) & 0x0000ff00) <<  8) | \
00118     (((x) & 0x000000ff) << 24))
00119 #else
00120 #include <bits/byteswap.h>
00121 #endif
00122 #define letohl(x) __bswap_32(x)
00123 #define letohs(x) __bswap_16(x)
00124 #define htolel(x) __bswap_32(x)
00125 #define htoles(x) __bswap_16(x)
00126 #endif
00127 
00128 /*! Global jitterbuffer configuration - by default, jb is disabled */
00129 static struct ast_jb_conf default_jbconf =
00130 {
00131    .flags = 0,
00132    .max_size = -1,
00133    .resync_threshold = -1,
00134    .impl = ""
00135 };
00136 static struct ast_jb_conf global_jbconf;
00137 
00138 AST_THREADSTORAGE(device2str_threadbuf);
00139 #define DEVICE2STR_BUFSIZE   15
00140 
00141 AST_THREADSTORAGE(control2str_threadbuf);
00142 #define CONTROL2STR_BUFSIZE   100
00143 
00144 /*********************
00145  * Protocol Messages *
00146  *********************/
00147 /* message types */
00148 #define KEEP_ALIVE_MESSAGE 0x0000
00149 /* no additional struct */
00150 
00151 #define REGISTER_MESSAGE 0x0001
00152 struct register_message {
00153    char name[16];
00154    uint32_t userId;
00155    uint32_t instance;
00156    uint32_t ip;
00157    uint32_t type;
00158    uint32_t maxStreams;
00159 };
00160 
00161 #define IP_PORT_MESSAGE 0x0002
00162 
00163 #define KEYPAD_BUTTON_MESSAGE 0x0003
00164 struct keypad_button_message {
00165    uint32_t button;
00166    uint32_t lineInstance;
00167    uint32_t callReference;
00168 };
00169 
00170 #define STIMULUS_MESSAGE 0x0005
00171 struct stimulus_message {
00172    uint32_t stimulus;
00173    uint32_t stimulusInstance;
00174    uint32_t unknown1;
00175 };
00176 
00177 #define OFFHOOK_MESSAGE 0x0006
00178 struct offhook_message {
00179    uint32_t unknown1;
00180    uint32_t unknown2;
00181 };
00182 
00183 #define ONHOOK_MESSAGE 0x0007
00184 struct onhook_message {
00185    uint32_t unknown1;
00186    uint32_t unknown2;
00187 };
00188 
00189 #define CAPABILITIES_RES_MESSAGE 0x0010
00190 struct station_capabilities {
00191    uint32_t codec;
00192    uint32_t frames;
00193    union {
00194       char res[8];
00195       uint32_t rate;
00196    } payloads;
00197 };
00198 
00199 struct capabilities_res_message {
00200    uint32_t count;
00201    struct station_capabilities caps[18];
00202 };
00203 
00204 #define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
00205 struct speed_dial_stat_req_message {
00206    uint32_t speedDialNumber;
00207 };
00208 
00209 #define LINE_STATE_REQ_MESSAGE 0x000B
00210 struct line_state_req_message {
00211    uint32_t lineNumber;
00212 };
00213 
00214 #define TIME_DATE_REQ_MESSAGE 0x000D
00215 #define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
00216 #define VERSION_REQ_MESSAGE 0x000F
00217 #define SERVER_REQUEST_MESSAGE 0x0012
00218 
00219 #define ALARM_MESSAGE 0x0020
00220 struct alarm_message {
00221    uint32_t alarmSeverity;
00222    char displayMessage[80];
00223    uint32_t alarmParam1;
00224    uint32_t alarmParam2;
00225 };
00226 
00227 #define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022
00228 struct open_receive_channel_ack_message {
00229    uint32_t status;
00230    uint32_t ipAddr;
00231    uint32_t port;
00232    uint32_t passThruId;
00233 };
00234 
00235 #define SOFT_KEY_SET_REQ_MESSAGE 0x0025
00236 
00237 #define SOFT_KEY_EVENT_MESSAGE 0x0026
00238 struct soft_key_event_message {
00239    uint32_t softKeyEvent;
00240    uint32_t instance;
00241    uint32_t reference;
00242 };
00243 
00244 #define UNREGISTER_MESSAGE 0x0027
00245 #define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
00246 #define HEADSET_STATUS_MESSAGE 0x002B
00247 #define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D
00248 
00249 #define REGISTER_ACK_MESSAGE 0x0081
00250 struct register_ack_message {
00251    uint32_t keepAlive;
00252    char dateTemplate[6];
00253    char res[2];
00254    uint32_t secondaryKeepAlive;
00255    char res2[4];
00256 };
00257 
00258 #define START_TONE_MESSAGE 0x0082
00259 struct start_tone_message {
00260    uint32_t tone;
00261 };
00262 
00263 #define STOP_TONE_MESSAGE 0x0083
00264 
00265 #define SET_RINGER_MESSAGE 0x0085
00266 struct set_ringer_message {
00267    uint32_t ringerMode;
00268    uint32_t unknown1; /* See notes in transmit_ringer_mode */
00269    uint32_t unknown2;
00270 };
00271 
00272 #define SET_LAMP_MESSAGE 0x0086
00273 struct set_lamp_message {
00274    uint32_t stimulus;
00275    uint32_t stimulusInstance;
00276    uint32_t deviceStimulus;
00277 };
00278 
00279 #define SET_SPEAKER_MESSAGE 0x0088
00280 struct set_speaker_message {
00281    uint32_t mode;
00282 };
00283 
00284 /* XXX When do we need to use this? */
00285 #define SET_MICROPHONE_MESSAGE 0x0089
00286 struct set_microphone_message {
00287    uint32_t mode;
00288 };
00289 
00290 #define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
00291 struct media_qualifier {
00292    uint32_t precedence;
00293    uint32_t vad;
00294    uint32_t packets;
00295    uint32_t bitRate;
00296 };
00297 
00298 struct start_media_transmission_message {
00299    uint32_t conferenceId;
00300    uint32_t passThruPartyId;
00301    uint32_t remoteIp;
00302    uint32_t remotePort;
00303    uint32_t packetSize;
00304    uint32_t payloadType;
00305    struct media_qualifier qualifier;
00306 };
00307 
00308 #define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
00309 struct stop_media_transmission_message {
00310    uint32_t conferenceId;
00311    uint32_t passThruPartyId;
00312 };
00313 
00314 #define CALL_INFO_MESSAGE 0x008F
00315 struct call_info_message {
00316    char callingPartyName[40];
00317    char callingParty[24];
00318    char calledPartyName[40];
00319    char calledParty[24];
00320    uint32_t instance;
00321    uint32_t reference;
00322    uint32_t type;
00323    char originalCalledPartyName[40];
00324    char originalCalledParty[24];
00325 };
00326 
00327 #define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
00328 struct speed_dial_stat_res_message {
00329    uint32_t speedDialNumber;
00330    char speedDialDirNumber[24];
00331    char speedDialDisplayName[40];
00332 };
00333 
00334 #define LINE_STAT_RES_MESSAGE 0x0092
00335 struct line_stat_res_message {
00336    uint32_t lineNumber;
00337    char lineDirNumber[24];
00338    char lineDisplayName[42];
00339    uint32_t space;
00340 };
00341 
00342 #define DEFINETIMEDATE_MESSAGE 0x0094
00343 struct definetimedate_message {
00344    uint32_t year; /* since 1900 */
00345    uint32_t month;
00346    uint32_t dayofweek;  /* monday = 1 */
00347    uint32_t day;
00348    uint32_t hour;
00349    uint32_t minute;
00350    uint32_t seconds;
00351    uint32_t milliseconds;
00352    uint32_t timestamp;
00353 };
00354 
00355 #define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
00356 struct button_definition {
00357    uint8_t instanceNumber;
00358    uint8_t buttonDefinition;
00359 };
00360 
00361 struct button_definition_template {
00362    uint8_t buttonDefinition;
00363    /* for now, anything between 0xB0 and 0xCF is custom */
00364    /*int custom;*/
00365 };
00366 
00367 #define STIMULUS_REDIAL       0x01
00368 #define STIMULUS_SPEEDDIAL    0x02
00369 #define STIMULUS_HOLD         0x03
00370 #define STIMULUS_TRANSFER     0x04
00371 #define STIMULUS_FORWARDALL      0x05
00372 #define STIMULUS_FORWARDBUSY     0x06
00373 #define STIMULUS_FORWARDNOANSWER 0x07
00374 #define STIMULUS_DISPLAY      0x08
00375 #define STIMULUS_LINE         0x09
00376 #define STIMULUS_VOICEMAIL    0x0F
00377 #define STIMULUS_AUTOANSWER      0x11
00378 #define STIMULUS_CONFERENCE      0x7D
00379 #define STIMULUS_CALLPARK     0x7E
00380 #define STIMULUS_CALLPICKUP      0x7F
00381 #define STIMULUS_NONE         0xFF
00382 
00383 /* Button types */
00384 #define BT_REDIAL       STIMULUS_REDIAL
00385 #define BT_SPEEDDIAL       STIMULUS_SPEEDDIAL
00386 #define BT_HOLD            STIMULUS_HOLD
00387 #define BT_TRANSFER        STIMULUS_TRANSFER
00388 #define BT_FORWARDALL         STIMULUS_FORWARDALL
00389 #define BT_FORWARDBUSY        STIMULUS_FORWARDBUSY
00390 #define BT_FORWARDNOANSWER    STIMULUS_FORWARDNOANSWER
00391 #define BT_DISPLAY         STIMULUS_DISPLAY
00392 #define BT_LINE            STIMULUS_LINE
00393 #define BT_VOICEMAIL       STIMULUS_VOICEMAIL
00394 #define BT_AUTOANSWER         STIMULUS_AUTOANSWER
00395 #define BT_CONFERENCE         STIMULUS_CONFERENCE
00396 #define BT_CALLPARK        STIMULUS_CALLPARK
00397 #define BT_CALLPICKUP         STIMULUS_CALLPICKUP
00398 #define BT_NONE            0x00
00399 
00400 /* Custom button types - add our own between 0xB0 and 0xCF.
00401    This may need to be revised in the future,
00402    if stimuluses are ever added in this range. */
00403 #define BT_CUST_LINESPEEDDIAL    0xB0  /* line or speeddial */
00404 #define BT_CUST_HINT       0xB1  /* pipe dream */
00405 
00406 struct button_template_res_message {
00407    uint32_t buttonOffset;
00408    uint32_t buttonCount;
00409    uint32_t totalButtonCount;
00410    struct button_definition definition[42];
00411 };
00412 
00413 #define VERSION_RES_MESSAGE 0x0098
00414 struct version_res_message {
00415    char version[16];
00416 };
00417 
00418 #define DISPLAYTEXT_MESSAGE 0x0099
00419 struct displaytext_message {
00420    char text[40];
00421 };
00422 
00423 #define CLEAR_NOTIFY_MESSAGE  0x0115
00424 #define CLEAR_PROMPT_MESSAGE  0x0113
00425 #define CLEAR_DISPLAY_MESSAGE 0x009A
00426 
00427 #define CAPABILITIES_REQ_MESSAGE 0x009B
00428 
00429 #define REGISTER_REJ_MESSAGE 0x009D
00430 struct register_rej_message {
00431    char errMsg[33];
00432 };
00433 
00434 #define SERVER_RES_MESSAGE 0x009E
00435 struct server_identifier {
00436    char serverName[48];
00437 };
00438 
00439 struct server_res_message {
00440    struct server_identifier server[5];
00441    uint32_t serverListenPort[5];
00442    uint32_t serverIpAddr[5];
00443 };
00444 
00445 #define RESET_MESSAGE 0x009F
00446 struct reset_message {
00447    uint32_t resetType;
00448 };
00449 
00450 #define KEEP_ALIVE_ACK_MESSAGE 0x0100
00451 
00452 #define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105
00453 struct open_receive_channel_message {
00454    uint32_t conferenceId;
00455    uint32_t partyId;
00456    uint32_t packets;
00457    uint32_t capability;
00458    uint32_t echo;
00459    uint32_t bitrate;
00460 };
00461 
00462 #define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106
00463 struct close_receive_channel_message {
00464    uint32_t conferenceId;
00465    uint32_t partyId;
00466 };
00467 
00468 #define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
00469 
00470 struct soft_key_template_definition {
00471    char softKeyLabel[16];
00472    uint32_t softKeyEvent;
00473 };
00474 
00475 #define KEYDEF_ONHOOK         0
00476 #define KEYDEF_CONNECTED      1
00477 #define KEYDEF_ONHOLD         2
00478 #define KEYDEF_RINGIN         3
00479 #define KEYDEF_OFFHOOK        4
00480 #define KEYDEF_CONNWITHTRANS     5
00481 #define KEYDEF_DADFD       6 /* Digits After Dialing First Digit */
00482 #define KEYDEF_CONNWITHCONF      7
00483 #define KEYDEF_RINGOUT        8
00484 #define KEYDEF_OFFHOOKWITHFEAT      9
00485 #define KEYDEF_UNKNOWN        10
00486 
00487 #define SOFTKEY_NONE       0x00
00488 #define SOFTKEY_REDIAL        0x01
00489 #define SOFTKEY_NEWCALL       0x02
00490 #define SOFTKEY_HOLD       0x03
00491 #define SOFTKEY_TRNSFER       0x04
00492 #define SOFTKEY_CFWDALL       0x05
00493 #define SOFTKEY_CFWDBUSY      0x06
00494 #define SOFTKEY_CFWDNOANSWER     0x07
00495 #define SOFTKEY_BKSPC         0x08
00496 #define SOFTKEY_ENDCALL       0x09
00497 #define SOFTKEY_RESUME        0x0A
00498 #define SOFTKEY_ANSWER        0x0B
00499 #define SOFTKEY_INFO       0x0C
00500 #define SOFTKEY_CONFRN        0x0D
00501 #define SOFTKEY_PARK       0x0E
00502 #define SOFTKEY_JOIN       0x0F
00503 #define SOFTKEY_MEETME        0x10
00504 #define SOFTKEY_PICKUP        0x11
00505 #define SOFTKEY_GPICKUP       0x12
00506 
00507 struct soft_key_template_definition soft_key_template_default[] = {
00508    { "Redial",    0x01 },
00509    { "NewCall",      0x02 },
00510    { "Hold",      0x03 },
00511    { "Trnsfer",      0x04 },
00512    { "CFwdAll",      0x05 },
00513    { "CFwdBusy",     0x06 },
00514    { "CFwdNoAnswer", 0x07 },
00515    { "<<",        0x08 },
00516    { "EndCall",      0x09 },
00517    { "Resume",    0x0A },
00518    { "Answer",    0x0B },
00519    { "Info",      0x0C },
00520    { "Confrn",    0x0D },
00521    { "Park",      0x0E },
00522    { "Join",      0x0F },
00523    { "MeetMe",    0x10 },
00524    { "PickUp",    0x11 },
00525    { "GPickUp",      0x12 },
00526 };
00527 
00528 struct soft_key_definitions {
00529    const uint8_t mode;
00530    const uint8_t *defaults;
00531    const int count;
00532 };
00533 
00534 static const uint8_t soft_key_default_onhook[] = {
00535    SOFTKEY_REDIAL,
00536    SOFTKEY_CFWDALL,
00537    SOFTKEY_CFWDBUSY,
00538    SOFTKEY_GPICKUP,
00539    SOFTKEY_CONFRN,
00540 };
00541 
00542 static const uint8_t soft_key_default_connected[] = {
00543    SOFTKEY_HOLD,
00544    SOFTKEY_ENDCALL,
00545    SOFTKEY_TRNSFER,
00546    SOFTKEY_PARK,
00547    SOFTKEY_CFWDALL,
00548    SOFTKEY_CFWDBUSY,
00549 };
00550 
00551 static const uint8_t soft_key_default_onhold[] = {
00552    SOFTKEY_RESUME,
00553    SOFTKEY_NEWCALL,
00554    SOFTKEY_ENDCALL,
00555    SOFTKEY_TRNSFER,
00556 };
00557 
00558 static const uint8_t soft_key_default_ringin[] = {
00559    SOFTKEY_ANSWER,
00560    SOFTKEY_ENDCALL,
00561    SOFTKEY_TRNSFER,
00562 };
00563 
00564 static const uint8_t soft_key_default_offhook[] = {
00565    SOFTKEY_REDIAL,
00566    SOFTKEY_ENDCALL,
00567    SOFTKEY_CFWDALL,
00568    SOFTKEY_CFWDBUSY,
00569    SOFTKEY_GPICKUP,
00570 };
00571 
00572 static const uint8_t soft_key_default_connwithtrans[] = {
00573    SOFTKEY_HOLD,
00574    SOFTKEY_ENDCALL,
00575    SOFTKEY_TRNSFER,
00576    SOFTKEY_PARK,
00577    SOFTKEY_CFWDALL,
00578    SOFTKEY_CFWDBUSY,
00579 };
00580 
00581 static const uint8_t soft_key_default_dadfd[] = {
00582    SOFTKEY_BKSPC,
00583    SOFTKEY_ENDCALL,
00584 };
00585 
00586 static const uint8_t soft_key_default_connwithconf[] = {
00587    SOFTKEY_NONE,
00588 };
00589 
00590 static const uint8_t soft_key_default_ringout[] = {
00591    SOFTKEY_ENDCALL,
00592    SOFTKEY_TRNSFER,
00593    SOFTKEY_CFWDALL,
00594    SOFTKEY_CFWDBUSY,
00595 };
00596 
00597 static const uint8_t soft_key_default_offhookwithfeat[] = {
00598    SOFTKEY_REDIAL,
00599    SOFTKEY_ENDCALL,
00600 };
00601 
00602 static const uint8_t soft_key_default_unknown[] = {
00603    SOFTKEY_NONE,
00604 };
00605 
00606 static const struct soft_key_definitions soft_key_default_definitions[] = {
00607    {KEYDEF_ONHOOK, soft_key_default_onhook, sizeof(soft_key_default_onhook) / sizeof(uint8_t)},
00608    {KEYDEF_CONNECTED, soft_key_default_connected, sizeof(soft_key_default_connected) / sizeof(uint8_t)},
00609    {KEYDEF_ONHOLD, soft_key_default_onhold, sizeof(soft_key_default_onhold) / sizeof(uint8_t)},
00610    {KEYDEF_RINGIN, soft_key_default_ringin, sizeof(soft_key_default_ringin) / sizeof(uint8_t)},
00611    {KEYDEF_OFFHOOK, soft_key_default_offhook, sizeof(soft_key_default_offhook) / sizeof(uint8_t)},
00612    {KEYDEF_CONNWITHTRANS, soft_key_default_connwithtrans, sizeof(soft_key_default_connwithtrans) / sizeof(uint8_t)},
00613    {KEYDEF_DADFD, soft_key_default_dadfd, sizeof(soft_key_default_dadfd) / sizeof(uint8_t)},
00614    {KEYDEF_CONNWITHCONF, soft_key_default_connwithconf, sizeof(soft_key_default_connwithconf) / sizeof(uint8_t)},
00615    {KEYDEF_RINGOUT, soft_key_default_ringout, sizeof(soft_key_default_ringout) / sizeof(uint8_t)},
00616    {KEYDEF_OFFHOOKWITHFEAT, soft_key_default_offhookwithfeat, sizeof(soft_key_default_offhookwithfeat) / sizeof(uint8_t)},
00617    {KEYDEF_UNKNOWN, soft_key_default_unknown, sizeof(soft_key_default_unknown) / sizeof(uint8_t)}
00618 };
00619 
00620 struct soft_key_template_res_message {
00621    uint32_t softKeyOffset;
00622    uint32_t softKeyCount;
00623    uint32_t totalSoftKeyCount;
00624    struct soft_key_template_definition softKeyTemplateDefinition[32];
00625 };
00626 
00627 #define SOFT_KEY_SET_RES_MESSAGE 0x0109
00628 
00629 struct soft_key_set_definition {
00630    uint8_t softKeyTemplateIndex[16];
00631    uint16_t softKeyInfoIndex[16];
00632 };
00633 
00634 struct soft_key_set_res_message {
00635    uint32_t softKeySetOffset;
00636    uint32_t softKeySetCount;
00637    uint32_t totalSoftKeySetCount;
00638    struct soft_key_set_definition softKeySetDefinition[16];
00639    uint32_t res;
00640 };
00641 
00642 #define SELECT_SOFT_KEYS_MESSAGE 0x0110
00643 struct select_soft_keys_message {
00644    uint32_t instance;
00645    uint32_t reference;
00646    uint32_t softKeySetIndex;
00647    uint32_t validKeyMask;
00648 };
00649 
00650 #define CALL_STATE_MESSAGE 0x0111
00651 struct call_state_message {
00652    uint32_t callState;
00653    uint32_t lineInstance;
00654    uint32_t callReference;
00655 };
00656 
00657 #define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112
00658 struct display_prompt_status_message {
00659    uint32_t messageTimeout;
00660    char promptMessage[32];
00661    uint32_t lineInstance;
00662    uint32_t callReference;
00663 };
00664 
00665 #define DISPLAY_NOTIFY_MESSAGE 0x0114
00666 struct display_notify_message {
00667    uint32_t displayTimeout;
00668    char displayMessage[100];
00669 };
00670 
00671 #define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
00672 struct activate_call_plane_message {
00673    uint32_t lineInstance;
00674 };
00675 
00676 #define DIALED_NUMBER_MESSAGE 0x011D
00677 struct dialed_number_message {
00678    char dialedNumber[24];
00679    uint32_t lineInstance;
00680    uint32_t callReference;
00681 };
00682 
00683 union skinny_data {
00684    struct alarm_message alarm;
00685    struct speed_dial_stat_req_message speeddialreq;
00686    struct register_message reg;
00687    struct register_ack_message regack;
00688    struct register_rej_message regrej;
00689    struct capabilities_res_message caps;
00690    struct version_res_message version;
00691    struct button_template_res_message buttontemplate;
00692    struct displaytext_message displaytext;
00693    struct display_prompt_status_message displaypromptstatus;
00694    struct definetimedate_message definetimedate;
00695    struct start_tone_message starttone;
00696    struct speed_dial_stat_res_message speeddial;
00697    struct line_state_req_message line;
00698    struct line_stat_res_message linestat;
00699    struct soft_key_set_res_message softkeysets;
00700    struct soft_key_template_res_message softkeytemplate;
00701    struct server_res_message serverres;
00702    struct reset_message reset;
00703    struct set_lamp_message setlamp;
00704    struct set_ringer_message setringer;
00705    struct call_state_message callstate;
00706    struct keypad_button_message keypad;
00707    struct select_soft_keys_message selectsoftkey;
00708    struct activate_call_plane_message activatecallplane;
00709    struct stimulus_message stimulus;
00710    struct offhook_message offhook;
00711    struct onhook_message onhook;
00712    struct set_speaker_message setspeaker;
00713    struct set_microphone_message setmicrophone;
00714    struct call_info_message callinfo;
00715    struct start_media_transmission_message startmedia;
00716    struct stop_media_transmission_message stopmedia;
00717    struct open_receive_channel_message openreceivechannel;
00718    struct open_receive_channel_ack_message openreceivechannelack;
00719    struct close_receive_channel_message closereceivechannel;
00720    struct display_notify_message displaynotify;
00721    struct dialed_number_message dialednumber;
00722    struct soft_key_event_message softkeyeventmessage;
00723 };
00724 
00725 /* packet composition */
00726 struct skinny_req {
00727    int len;
00728    int res;
00729    int e;
00730    union skinny_data data;
00731 };
00732 
00733 /* XXX This is the combined size of the variables above.  (len, res, e)
00734    If more are added, this MUST change.
00735    (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */
00736 int skinny_header_size = 12;
00737 
00738 /*****************************
00739  * Asterisk specific globals *
00740  *****************************/
00741 
00742 static int skinnydebug = 0;
00743 
00744 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
00745 static struct sockaddr_in bindaddr;
00746 static char ourhost[256];
00747 static int ourport;
00748 static struct in_addr __ourip;
00749 struct ast_hostent ahp;
00750 struct hostent *hp;
00751 static int skinnysock = -1;
00752 static pthread_t accept_t;
00753 static char context[AST_MAX_CONTEXT] = "default";
00754 static char language[MAX_LANGUAGE] = "";
00755 static char mohinterpret[MAX_MUSICCLASS] = "default";
00756 static char mohsuggest[MAX_MUSICCLASS] = "";
00757 static char cid_num[AST_MAX_EXTENSION] = "";
00758 static char cid_name[AST_MAX_EXTENSION] = "";
00759 static char linelabel[AST_MAX_EXTENSION] ="";
00760 static int nat = 0;
00761 static ast_group_t cur_callergroup = 0;
00762 static ast_group_t cur_pickupgroup = 0;
00763 static int immediate = 0;
00764 static int callwaiting = 0;
00765 static int callreturn = 0;
00766 static int threewaycalling = 0;
00767 static int mwiblink = 0;
00768 /* This is for flashhook transfers */
00769 static int transfer = 0;
00770 static int cancallforward = 0;
00771 /* static int busycount = 3;*/
00772 static char accountcode[AST_MAX_ACCOUNT_CODE] = "";
00773 static char mailbox[AST_MAX_EXTENSION];
00774 static int amaflags = 0;
00775 static int callnums = 1;
00776 
00777 #define SKINNY_DEVICE_UNKNOWN    -1
00778 #define SKINNY_DEVICE_NONE    0
00779 #define SKINNY_DEVICE_30SPPLUS      1
00780 #define SKINNY_DEVICE_12SPPLUS      2
00781 #define SKINNY_DEVICE_12SP    3
00782 #define SKINNY_DEVICE_12      4
00783 #define SKINNY_DEVICE_30VIP      5
00784 #define SKINNY_DEVICE_7910    6
00785 #define SKINNY_DEVICE_7960    7
00786 #define SKINNY_DEVICE_7940    8
00787 #define SKINNY_DEVICE_7935    9
00788 #define SKINNY_DEVICE_ATA186     12 /* Cisco ATA-186 */
00789 #define SKINNY_DEVICE_7941    115
00790 #define SKINNY_DEVICE_7971    119
00791 #define SKINNY_DEVICE_7985    302
00792 #define SKINNY_DEVICE_7911    307
00793 #define SKINNY_DEVICE_7961GE     308
00794 #define SKINNY_DEVICE_7941GE     309
00795 #define SKINNY_DEVICE_7905    20000
00796 #define SKINNY_DEVICE_7920    30002
00797 #define SKINNY_DEVICE_7970    30006
00798 #define SKINNY_DEVICE_7912    30007
00799 #define SKINNY_DEVICE_7902    30008
00800 #define SKINNY_DEVICE_CIPC    30016 /* Cisco IP Communicator */
00801 #define SKINNY_DEVICE_7961    30018
00802 #define SKINNY_DEVICE_7936    30019
00803 #define SKINNY_DEVICE_SCCPGATEWAY_AN   30027 /* ??? */
00804 #define SKINNY_DEVICE_SCCPGATEWAY_BRI  30028 /* ??? */
00805 
00806 #define SKINNY_SPEAKERON 1
00807 #define SKINNY_SPEAKEROFF 2
00808 
00809 #define SKINNY_MICON 1
00810 #define SKINNY_MICOFF 2
00811 
00812 #define SKINNY_OFFHOOK 1
00813 #define SKINNY_ONHOOK 2
00814 #define SKINNY_RINGOUT 3
00815 #define SKINNY_RINGIN 4
00816 #define SKINNY_CONNECTED 5
00817 #define SKINNY_BUSY 6
00818 #define SKINNY_CONGESTION 7
00819 #define SKINNY_HOLD 8
00820 #define SKINNY_CALLWAIT 9
00821 #define SKINNY_TRANSFER 10
00822 #define SKINNY_PARK 11
00823 #define SKINNY_PROGRESS 12
00824 #define SKINNY_INVALID 14
00825 
00826 #define SKINNY_SILENCE     0x00
00827 #define SKINNY_DIALTONE    0x21
00828 #define SKINNY_BUSYTONE    0x23
00829 #define SKINNY_ALERT    0x24
00830 #define SKINNY_REORDER     0x25
00831 #define SKINNY_CALLWAITTONE   0x2D
00832 #define SKINNY_NOTONE      0x7F
00833 
00834 #define SKINNY_LAMP_OFF 1
00835 #define SKINNY_LAMP_ON 2
00836 #define SKINNY_LAMP_WINK 3
00837 #define SKINNY_LAMP_FLASH 4
00838 #define SKINNY_LAMP_BLINK 5
00839 
00840 #define SKINNY_RING_OFF 1
00841 #define SKINNY_RING_INSIDE 2
00842 #define SKINNY_RING_OUTSIDE 3
00843 #define SKINNY_RING_FEATURE 4
00844 
00845 #define TYPE_TRUNK 1
00846 #define TYPE_LINE 2
00847 
00848 /* Skinny rtp stream modes. Do we really need this? */
00849 #define SKINNY_CX_SENDONLY 0
00850 #define SKINNY_CX_RECVONLY 1
00851 #define SKINNY_CX_SENDRECV 2
00852 #define SKINNY_CX_CONF     3
00853 #define SKINNY_CX_CONFERENCE  3
00854 #define SKINNY_CX_MUTE     4
00855 #define SKINNY_CX_INACTIVE 4
00856 
00857 #if 0
00858 static char *skinny_cxmodes[] = {
00859    "sendonly",
00860    "recvonly",
00861    "sendrecv",
00862    "confrnce",
00863    "inactive"
00864 };
00865 #endif
00866 
00867 /* driver scheduler */
00868 static struct sched_context *sched;
00869 static struct io_context *io;
00870 
00871 /* Protect the monitoring thread, so only one process can kill or start it, and not
00872    when it's doing something critical. */
00873 AST_MUTEX_DEFINE_STATIC(monlock);
00874 /* Protect the network socket */
00875 AST_MUTEX_DEFINE_STATIC(netlock);
00876 /* Protect the session list */
00877 AST_MUTEX_DEFINE_STATIC(sessionlock);
00878 /* Protect the device list */
00879 AST_MUTEX_DEFINE_STATIC(devicelock);
00880 #if 0
00881 /* Protect the paging device list */
00882 AST_MUTEX_DEFINE_STATIC(pagingdevicelock);
00883 #endif
00884 
00885 /* This is the thread for the monitor which checks for input on the channels
00886    which are not currently in use. */
00887 static pthread_t monitor_thread = AST_PTHREADT_NULL;
00888 
00889 /* Wait up to 16 seconds for first digit */
00890 static int firstdigittimeout = 16000;
00891 
00892 /* How long to wait for following digits */
00893 static int gendigittimeout = 8000;
00894 
00895 /* How long to wait for an extra digit, if there is an ambiguous match */
00896 static int matchdigittimeout = 3000;
00897 
00898 struct skinny_subchannel {
00899    ast_mutex_t lock;
00900    struct ast_channel *owner;
00901    struct ast_rtp *rtp;
00902    struct ast_rtp *vrtp;
00903    unsigned int callid;
00904    /* time_t lastouttime; */        /* Unused */
00905    int progress;
00906    int ringing;
00907    int onhold;
00908    /* int lastout; */            /* Unused */
00909    int cxmode;
00910    int nat;
00911    int outgoing;
00912    int alreadygone;
00913 
00914    struct skinny_subchannel *next;
00915    struct skinny_line *parent;
00916 };
00917 
00918 struct skinny_line {
00919    ast_mutex_t lock;
00920    char name[80];
00921    char label[42];               /* Label that shows next to the line buttons */
00922    char accountcode[AST_MAX_ACCOUNT_CODE];
00923    char exten[AST_MAX_EXTENSION];         /* Extension where to start */
00924    char context[AST_MAX_CONTEXT];
00925    char language[MAX_LANGUAGE];
00926    char cid_num[AST_MAX_EXTENSION];    /* Caller*ID */
00927    char cid_name[AST_MAX_EXTENSION];      /* Caller*ID */
00928    char lastcallerid[AST_MAX_EXTENSION];     /* Last Caller*ID */
00929    char call_forward[AST_MAX_EXTENSION];
00930    char mailbox[AST_MAX_EXTENSION];
00931    char mohinterpret[MAX_MUSICCLASS];
00932    char mohsuggest[MAX_MUSICCLASS];
00933    char lastnumberdialed[AST_MAX_EXTENSION]; /* Last number that was dialed - used for redial */
00934    int curtone;               /* Current tone being played */
00935    ast_group_t callgroup;
00936    ast_group_t pickupgroup;
00937    int callwaiting;
00938    int transfer;
00939    int threewaycalling;
00940    int mwiblink;
00941    int cancallforward;
00942    int callreturn;
00943    int dnd; /* How does this affect callwait?  Do we just deny a skinny_request if we're dnd? */
00944    int hascallerid;
00945    int hidecallerid;
00946    int amaflags;
00947    int type;
00948    int instance;
00949    int group;
00950    int needdestroy;
00951    int capability;
00952    int nonCodecCapability;
00953    int onhooktime;
00954    int msgstate;              /* voicemail message state */
00955    int immediate;
00956    int hookstate;
00957    int nat;
00958 
00959    struct ast_codec_pref prefs;
00960    struct skinny_subchannel *sub;
00961    struct skinny_line *next;
00962    struct skinny_device *parent;
00963 };
00964 
00965 struct skinny_speeddial {
00966    ast_mutex_t lock;
00967    char label[42];
00968    char exten[AST_MAX_EXTENSION];
00969    int instance;
00970 
00971    struct skinny_speeddial *next;
00972    struct skinny_device *parent;
00973 };
00974 
00975 struct skinny_addon {
00976    ast_mutex_t lock;
00977    char type[10];
00978 
00979    struct skinny_addon *next;
00980    struct skinny_device *parent;
00981 };
00982 
00983 static struct skinny_device {
00984    /* A device containing one or more lines */
00985    char name[80];
00986    char id[16];
00987    char version_id[16];
00988    int type;
00989    int registered;
00990    int lastlineinstance;
00991    int lastcallreference;
00992    int capability;
00993    struct sockaddr_in addr;
00994    struct in_addr ourip;
00995    struct skinny_line *lines;
00996    struct skinny_speeddial *speeddials;
00997    struct skinny_addon *addons;
00998    struct ast_codec_pref prefs;
00999    struct ast_ha *ha;
01000    struct skinnysession *session;
01001    struct skinny_device *next;
01002 } *devices = NULL;
01003 
01004 struct skinny_paging_device {
01005    char name[80];
01006    char id[16];
01007    struct skinny_device ** devices;
01008    struct skinny_paging_device *next;
01009 };
01010 
01011 static struct skinnysession {
01012    pthread_t t;
01013    ast_mutex_t lock;
01014    struct sockaddr_in sin;
01015    int fd;
01016    char inbuf[SKINNY_MAX_PACKET];
01017    char outbuf[SKINNY_MAX_PACKET];
01018    struct skinny_device *device;
01019    struct skinnysession *next;
01020 } *sessions = NULL;
01021 
01022 static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause);
01023 static int skinny_call(struct ast_channel *ast, char *dest, int timeout);
01024 static int skinny_hangup(struct ast_channel *ast);
01025 static int skinny_answer(struct ast_channel *ast);
01026 static struct ast_frame *skinny_read(struct ast_channel *ast);
01027 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame);
01028 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen);
01029 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
01030 static int skinny_senddigit_begin(struct ast_channel *ast, char digit);
01031 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration);
01032 
01033 static const struct ast_channel_tech skinny_tech = {
01034    .type = "Skinny",
01035    .description = tdesc,
01036    .capabilities = ((AST_FORMAT_MAX_AUDIO << 1) - 1),
01037    .properties = AST_CHAN_TP_WANTSJITTER | AST_CHAN_TP_CREATESJITTER,
01038    .requester = skinny_request,
01039    .call = skinny_call,
01040    .hangup = skinny_hangup,
01041    .answer = skinny_answer,
01042    .read = skinny_read,
01043    .write = skinny_write,
01044    .indicate = skinny_indicate,
01045    .fixup = skinny_fixup,
01046    .send_digit_begin = skinny_senddigit_begin,
01047    .send_digit_end = skinny_senddigit_end,
01048 /* .bridge = ast_rtp_bridge, */
01049 };
01050 
01051 static void *get_button_template(struct skinnysession *s, struct button_definition_template *btn)
01052 {
01053    struct skinny_device *d = s->device;
01054    struct skinny_addon *a = d->addons;
01055    int i;
01056 
01057    switch (d->type) {
01058       case SKINNY_DEVICE_30SPPLUS:
01059       case SKINNY_DEVICE_30VIP:
01060          /* 13 rows, 2 columns */
01061          for (i = 0; i < 4; i++)
01062             (btn++)->buttonDefinition = BT_LINE;
01063          (btn++)->buttonDefinition = BT_REDIAL;
01064          (btn++)->buttonDefinition = BT_VOICEMAIL;
01065          (btn++)->buttonDefinition = BT_CALLPARK;
01066          (btn++)->buttonDefinition = BT_FORWARDALL;
01067          (btn++)->buttonDefinition = BT_CONFERENCE;
01068          for (i = 0; i < 4; i++)
01069             (btn++)->buttonDefinition = BT_NONE;
01070          for (i = 0; i < 13; i++)
01071             (btn++)->buttonDefinition = BT_SPEEDDIAL;
01072          
01073          break;
01074       case SKINNY_DEVICE_12SPPLUS:
01075       case SKINNY_DEVICE_12SP:
01076       case SKINNY_DEVICE_12:
01077          /* 6 rows, 2 columns */
01078          for (i = 0; i < 2; i++)
01079             (btn++)->buttonDefinition = BT_LINE;
01080          (btn++)->buttonDefinition = BT_REDIAL;
01081          for (i = 0; i < 3; i++)
01082             (btn++)->buttonDefinition = BT_SPEEDDIAL;
01083          (btn++)->buttonDefinition = BT_HOLD;
01084          (btn++)->buttonDefinition = BT_TRANSFER;
01085          (btn++)->buttonDefinition = BT_FORWARDALL;
01086          (btn++)->buttonDefinition = BT_CALLPARK;
01087          (btn++)->buttonDefinition = BT_VOICEMAIL;
01088          (btn++)->buttonDefinition = BT_CONFERENCE;
01089          break;
01090       case SKINNY_DEVICE_7910:
01091          (btn++)->buttonDefinition = BT_LINE;
01092          (btn++)->buttonDefinition = BT_HOLD;
01093          (btn++)->buttonDefinition = BT_TRANSFER;
01094          (btn++)->buttonDefinition = BT_DISPLAY;
01095          (btn++)->buttonDefinition = BT_VOICEMAIL;
01096          (btn++)->buttonDefinition = BT_CONFERENCE;
01097          (btn++)->buttonDefinition = BT_FORWARDALL;
01098          for (i = 0; i < 2; i++)
01099             (btn++)->buttonDefinition = BT_SPEEDDIAL;
01100          (btn++)->buttonDefinition = BT_REDIAL;
01101          break;
01102       case SKINNY_DEVICE_7960:
01103       case SKINNY_DEVICE_7961:
01104       case SKINNY_DEVICE_7961GE:
01105          for (i = 0; i < 6; i++)
01106             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01107          break;
01108       case SKINNY_DEVICE_7940:
01109       case SKINNY_DEVICE_7941:
01110       case SKINNY_DEVICE_7941GE:
01111          for (i = 0; i < 2; i++)
01112             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01113          break;
01114       case SKINNY_DEVICE_7935:
01115       case SKINNY_DEVICE_7936:
01116          for (i = 0; i < 2; i++)
01117             (btn++)->buttonDefinition = BT_LINE;
01118          break;
01119       case SKINNY_DEVICE_ATA186:
01120          (btn++)->buttonDefinition = BT_LINE;
01121          break;
01122       case SKINNY_DEVICE_7970:
01123       case SKINNY_DEVICE_7971:
01124       case SKINNY_DEVICE_CIPC:
01125          for (i = 0; i < 8; i++)
01126             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01127          break;
01128       case SKINNY_DEVICE_7985:
01129          /* XXX I have no idea what the buttons look like on these. */
01130          ast_log(LOG_WARNING, "Unsupported device type '%d (7985)' found.\n", d->type);
01131          break;
01132       case SKINNY_DEVICE_7912:
01133       case SKINNY_DEVICE_7911:
01134       case SKINNY_DEVICE_7905:
01135          (btn++)->buttonDefinition = BT_LINE;
01136          (btn++)->buttonDefinition = BT_HOLD;
01137          break;
01138       case SKINNY_DEVICE_7920:
01139          /* XXX I don't know if this is right. */
01140          for (i = 0; i < 4; i++)
01141             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01142          break;
01143       case SKINNY_DEVICE_7902:
01144          ast_log(LOG_WARNING, "Unsupported device type '%d (7902)' found.\n", d->type);
01145          break;
01146       case SKINNY_DEVICE_SCCPGATEWAY_AN:
01147       case SKINNY_DEVICE_SCCPGATEWAY_BRI:
01148          ast_log(LOG_WARNING, "Unsupported device type '%d (SCCP gateway)' found.\n", d->type);
01149          break;
01150       default:
01151          ast_log(LOG_WARNING, "Unknown device type '%d' found.\n", d->type);
01152          break;
01153    }
01154 
01155    for (a = d->addons; a; a = a->next) {
01156       if (!strcasecmp(a->type, "7914")) {
01157          for (i = 0; i < 14; i++)
01158             (btn++)->buttonDefinition = BT_CUST_LINESPEEDDIAL;
01159       } else {
01160          ast_log(LOG_WARNING, "Unknown addon type '%s' found.  Skipping.\n", a->type);
01161       }
01162    }
01163 
01164    return btn;
01165 }
01166 
01167 static struct skinny_req *req_alloc(size_t size, int response_message)
01168 {
01169    struct skinny_req *req;
01170 
01171    if (!(req = ast_calloc(1, skinny_header_size + size + 4)))
01172       return NULL;
01173 
01174    req->len = htolel(size+4);
01175    req->e = htolel(response_message);
01176 
01177    return req;
01178 }
01179 
01180 static struct skinny_line *find_line_by_instance(struct skinny_device *d, int instance)
01181 {
01182    struct skinny_line *l;
01183 
01184    for (l = d->lines; l; l = l->next) {
01185       if (l->instance == instance)
01186          break;
01187    }
01188 
01189    if (!l) {
01190       ast_log(LOG_WARNING, "Could not find line with instance '%d' on device '%s'\n", instance, d->name);
01191    }
01192    return l;
01193 }
01194 
01195 static struct skinny_line *find_line_by_name(const char *dest)
01196 {
01197    struct skinny_line *l;
01198    struct skinny_device *d;
01199    char line[256];
01200    char *at;
01201    char *device;
01202 
01203    ast_copy_string(line, dest, sizeof(line));
01204    at = strchr(line, '@');
01205    if (!at) {
01206       ast_log(LOG_NOTICE, "Device '%s' has no @ (at) sign!\n", dest);
01207       return NULL;
01208    }
01209    *at++ = '\0';
01210    device = at;
01211    ast_mutex_lock(&devicelock);
01212    for (d = devices; d; d = d->next) {
01213       if (!strcasecmp(d->name, device)) {
01214          if (skinnydebug)
01215             ast_verbose("Found device: %s\n", d->name);
01216          /* Found the device */
01217          for (l = d->lines; l; l = l->next) {
01218             /* Search for the right line */
01219             if (!strcasecmp(l->name, line)) {
01220                ast_mutex_unlock(&devicelock);
01221                return l;
01222             }
01223          }
01224       }
01225    }
01226    /* Device not found */
01227    ast_mutex_unlock(&devicelock);
01228    return NULL;
01229 }
01230 
01231 /* It's quicker/easier to find the subchannel when we know the instance number too */
01232 static struct skinny_subchannel *find_subchannel_by_instance_reference(struct skinny_device *d, int instance, int reference)
01233 {
01234    struct skinny_line *l = find_line_by_instance(d, instance);
01235    struct skinny_subchannel *sub;
01236 
01237    if (!l) {
01238       return NULL;
01239    }
01240 
01241    for (sub = l->sub; sub; sub = sub->next) {
01242       if (sub->callid == reference)
01243          break;
01244    }
01245 
01246    if (!sub) {
01247       ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s'\n", reference, d->name);
01248    }
01249    return sub;
01250 }
01251 
01252 /* Find the subchannel when we only have the callid - this shouldn't happen often */
01253 static struct skinny_subchannel *find_subchannel_by_reference(struct skinny_device *d, int reference)
01254 {
01255    struct skinny_line *l;
01256    struct skinny_subchannel *sub = NULL;
01257 
01258    for (l = d->lines; l; l = l->next) {
01259       for (sub = l->sub; sub; sub = sub->next) {
01260          if (sub->callid == reference)
01261             break;
01262       }
01263       if (sub)
01264          break;
01265    }
01266 
01267    if (!l) {
01268       ast_log(LOG_WARNING, "Could not find any lines that contained a subchannel with reference '%d' on device '%s'\n", reference, d->name);
01269    } else {
01270       if (!sub) {
01271          ast_log(LOG_WARNING, "Could not find subchannel with reference '%d' on '%s@%s'\n", reference, l->name, d->name);
01272       }
01273    }
01274    return sub;
01275 }
01276 
01277 static struct skinny_speeddial *find_speeddial_by_instance(struct skinny_device *d, int instance)
01278 {
01279    struct skinny_speeddial *sd;
01280 
01281    for (sd = d->speeddials; sd; sd = sd->next) {
01282       if (sd->instance == instance)
01283          break;
01284    }
01285 
01286    if (!sd) {
01287       ast_log(LOG_WARNING, "Could not find speeddial with instance '%d' on device '%s'\n", instance, d->name);
01288    }
01289    return sd;
01290 }
01291 
01292 static int codec_skinny2ast(enum skinny_codecs skinnycodec)
01293 {
01294    switch (skinnycodec) {
01295    case SKINNY_CODEC_ALAW:
01296       return AST_FORMAT_ALAW;
01297    case SKINNY_CODEC_ULAW:
01298       return AST_FORMAT_ULAW;
01299    case SKINNY_CODEC_G723_1:
01300       return AST_FORMAT_G723_1;
01301    case SKINNY_CODEC_G729A:
01302       return AST_FORMAT_G729A;
01303    case SKINNY_CODEC_G726_32:
01304       return AST_FORMAT_G726_AAL2; /* XXX Is this right? */
01305    case SKINNY_CODEC_H261:
01306       return AST_FORMAT_H261;
01307    case SKINNY_CODEC_H263:
01308       return AST_FORMAT_H263;
01309    default:
01310       return 0;
01311    }
01312 }
01313 
01314 static int codec_ast2skinny(int astcodec)
01315 {
01316    switch (astcodec) {
01317    case AST_FORMAT_ALAW:
01318       return SKINNY_CODEC_ALAW;
01319    case AST_FORMAT_ULAW:
01320       return SKINNY_CODEC_ULAW;
01321    case AST_FORMAT_G723_1:
01322       return SKINNY_CODEC_G723_1;
01323    case AST_FORMAT_G729A:
01324       return SKINNY_CODEC_G729A;
01325    case AST_FORMAT_G726_AAL2: /* XXX Is this right? */
01326       return SKINNY_CODEC_G726_32;
01327    case AST_FORMAT_H261:
01328       return SKINNY_CODEC_H261;
01329    case AST_FORMAT_H263:
01330       return SKINNY_CODEC_H263;
01331    default:
01332       return 0;
01333    }
01334 }
01335 
01336 
01337 static int skinny_register(struct skinny_req *req, struct skinnysession *s)
01338 {
01339    struct skinny_device *d;
01340    struct sockaddr_in sin;
01341    socklen_t slen;
01342 
01343    ast_mutex_lock(&devicelock);
01344    for (d = devices; d; d = d->next) {
01345       if (!strcasecmp(req->data.reg.name, d->id)
01346             && ast_apply_ha(d->ha, &(s->sin))) {
01347          s->device = d;
01348          d->type = letohl(req->data.reg.type);
01349          if (ast_strlen_zero(d->version_id)) {
01350             ast_copy_string(d->version_id, version_id, sizeof(d->version_id));
01351          }
01352          d->registered = 1;
01353          d->session = s;
01354 
01355          slen = sizeof(sin);
01356          if (getsockname(s->fd, (struct sockaddr *)&sin, &slen)) {
01357             ast_log(LOG_WARNING, "Cannot get socket name\n");
01358             sin.sin_addr = __ourip;
01359          }
01360          d->ourip = sin.sin_addr;
01361          break;
01362       }
01363    }
01364    ast_mutex_unlock(&devicelock);
01365    if (!d) {
01366       return 0;
01367    }
01368    return 1;
01369 }
01370 
01371 static int skinny_unregister(struct skinny_req *req, struct skinnysession *s)
01372 {
01373    struct skinny_device *d;
01374 
01375    d = s->device;
01376 
01377    if (d) {
01378       d->session = NULL;
01379       d->registered = 0;
01380    }
01381 
01382    return -1; /* main loop will destroy the session */
01383 }
01384 
01385 static int transmit_response(struct skinnysession *s, struct skinny_req *req)
01386 {
01387    int res = 0;
01388    ast_mutex_lock(&s->lock);
01389 
01390    if (skinnydebug)
01391       ast_log(LOG_VERBOSE, "writing packet type %04X (%d bytes) to socket %d\n", letohl(req->e), letohl(req->len)+8, s->fd);
01392 
01393    if (letohl(req->len > SKINNY_MAX_PACKET) || letohl(req->len < 0)) {
01394       ast_log(LOG_WARNING, "transmit_response: the length of the request is out of bounds\n");
01395       return -1;
01396    }
01397 
01398    memset(s->outbuf,0,sizeof(s->outbuf));
01399    memcpy(s->outbuf, req, skinny_header_size);
01400    memcpy(s->outbuf+skinny_header_size, &req->data, letohl(req->len));
01401 
01402    res = write(s->fd, s->outbuf, letohl(req->len)+8);
01403 
01404    if (res != letohl(req->len)+8) {
01405       ast_log(LOG_WARNING, "Transmit: write only sent %d out of %d bytes: %s\n", res, letohl(req->len)+8, strerror(errno));
01406       if (res == -1) {
01407          if (skinnydebug)
01408             ast_log(LOG_WARNING, "Transmit: Skinny Client was lost, unregistering\n");
01409          skinny_unregister(NULL, s);
01410       }
01411       
01412    }
01413    
01414    ast_mutex_unlock(&s->lock);
01415    return 1;
01416 }
01417 
01418 static void transmit_speaker_mode(struct skinnysession *s, int mode)
01419 {
01420    struct skinny_req *req;
01421 
01422    if (!(req = req_alloc(sizeof(struct set_speaker_message), SET_SPEAKER_MESSAGE)))
01423       return;
01424 
01425    req->data.setspeaker.mode = htolel(mode);
01426    transmit_response(s, req);
01427 }
01428 /*
01429 static void transmit_microphone_mode(struct skinnysession *s, int mode)
01430 {
01431    struct skinny_req *req;
01432 
01433    if (!(req = req_alloc(sizeof(struct set_microphone_message), SET_MICROPHONE_MESSAGE)))
01434       return;
01435 
01436    req->data.setmicrophone.mode = htolel(mode);
01437    transmit_response(s, req);
01438 }
01439 */
01440 static void transmit_callstate(struct skinnysession *s, int instance, int state, unsigned callid)
01441 {
01442    struct skinny_req *req;
01443 
01444    if (!(req = req_alloc(sizeof(struct call_state_message), CALL_STATE_MESSAGE)))
01445       return;
01446 
01447    if (state == SKINNY_ONHOOK) {
01448       transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
01449    }
01450    req->data.callstate.callState = htolel(state);
01451    req->data.callstate.lineInstance = htolel(instance);
01452    req->data.callstate.callReference = htolel(callid);
01453    transmit_response(s, req);
01454    if (state == SKINNY_OFFHOOK) {
01455       if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
01456          return;
01457 
01458       req->data.activatecallplane.lineInstance = htolel(instance);
01459       transmit_response(s, req);
01460    } else if (state == SKINNY_ONHOOK) {
01461       if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
01462          return;
01463 
01464       req->data.activatecallplane.lineInstance = htolel(instance);
01465       transmit_response(s, req);
01466 
01467       if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
01468          return;
01469 
01470       req->data.closereceivechannel.conferenceId = 0;
01471       req->data.closereceivechannel.partyId = htolel(callid);
01472       transmit_response(s, req);
01473 
01474       if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
01475          return;
01476 
01477       req->data.stopmedia.conferenceId = 0;
01478       req->data.stopmedia.passThruPartyId = htolel(callid);
01479       transmit_response(s, req);
01480    }
01481 }
01482 
01483 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)
01484 {
01485    struct skinny_req *req;
01486 
01487    if (!(req = req_alloc(sizeof(struct call_info_message), CALL_INFO_MESSAGE)))
01488       return;
01489 
01490    if (fromname) {
01491       ast_copy_string(req->data.callinfo.callingPartyName, fromname, sizeof(req->data.callinfo.callingPartyName));
01492    }
01493    if (fromnum) {
01494       ast_copy_string(req->data.callinfo.callingParty, fromnum, sizeof(req->data.callinfo.callingParty));
01495    }
01496    if (toname) {
01497       ast_copy_string(req->data.callinfo.calledPartyName, toname, sizeof(req->data.callinfo.calledPartyName));
01498    }
01499    if (tonum) {
01500       ast_copy_string(req->data.callinfo.calledParty, tonum, sizeof(req->data.callinfo.calledParty));
01501    }
01502    req->data.callinfo.instance = htolel(instance);
01503    req->data.callinfo.reference = htolel(callid);
01504    req->data.callinfo.type = htolel(calltype);
01505    transmit_response(s, req);
01506 }
01507 
01508 static void transmit_connect(struct skinnysession *s, struct skinny_subchannel *sub)
01509 {
01510    struct skinny_req *req;
01511    struct skinny_line *l = sub->parent;
01512    struct ast_format_list fmt;
01513 
01514    if (!(req = req_alloc(sizeof(struct open_receive_channel_message), OPEN_RECEIVE_CHANNEL_MESSAGE)))
01515       return;
01516 
01517    fmt = ast_codec_pref_getsize(&l->prefs, ast_best_codec(l->capability));
01518 
01519    req->data.openreceivechannel.conferenceId = htolel(0);
01520    req->data.openreceivechannel.partyId = htolel(sub->callid);
01521    req->data.openreceivechannel.packets = htolel(fmt.cur_ms);
01522    req->data.openreceivechannel.capability = htolel(codec_ast2skinny(fmt.bits));
01523    req->data.openreceivechannel.echo = htolel(0);
01524    req->data.openreceivechannel.bitrate = htolel(0);
01525    transmit_response(s, req);
01526 }
01527 
01528 static void transmit_tone(struct skinnysession *s, int tone)
01529 {
01530    struct skinny_req *req;
01531 
01532    if (tone == SKINNY_NOTONE) {
01533       /* This is bad, mmm'kay? */
01534       return;
01535    }
01536 
01537    if (tone > 0) {
01538       if (!(req = req_alloc(sizeof(struct start_tone_message), START_TONE_MESSAGE)))
01539          return;
01540    } else {
01541       if (!(req = req_alloc(0, STOP_TONE_MESSAGE)))
01542          return;
01543    }
01544 
01545    if (tone > 0) {
01546       req->data.starttone.tone = htolel(tone);
01547    }
01548    transmit_response(s, req);
01549 }
01550 
01551 static void transmit_selectsoftkeys(struct skinnysession *s, int instance, int callid, int softkey)
01552 {
01553    struct skinny_req *req;
01554 
01555    if (!(req = req_alloc(sizeof(struct select_soft_keys_message), SELECT_SOFT_KEYS_MESSAGE)))
01556       return;
01557 
01558    req->data.selectsoftkey.instance = htolel(instance);
01559    req->data.selectsoftkey.reference = htolel(callid);
01560    req->data.selectsoftkey.softKeySetIndex = htolel(softkey);
01561    req->data.selectsoftkey.validKeyMask = htolel(0xFFFFFFFF);
01562    transmit_response(s, req);
01563 }
01564 
01565 static void transmit_lamp_indication(struct skinnysession *s, int stimulus, int instance, int indication)
01566 {
01567    struct skinny_req *req;
01568 
01569    if (!(req = req_alloc(sizeof(struct set_lamp_message), SET_LAMP_MESSAGE)))
01570       return;
01571 
01572    req->data.setlamp.stimulus = htolel(stimulus);
01573    req->data.setlamp.stimulusInstance = htolel(instance);
01574    req->data.setlamp.deviceStimulus = htolel(indication);
01575    transmit_response(s, req);
01576 }
01577 
01578 static void transmit_ringer_mode(struct skinnysession *s, int mode)
01579 {
01580    struct skinny_req *req;
01581 
01582    if (skinnydebug)
01583       ast_verbose("Setting ringer mode to '%d'.\n", mode);
01584 
01585    if (!(req = req_alloc(sizeof(struct set_ringer_message), SET_RINGER_MESSAGE)))
01586       return;
01587 
01588    req->data.setringer.ringerMode = htolel(mode);
01589    /* XXX okay, I don't quite know what this is, but here's what happens (on a 7960).
01590       Note: The phone will always show as ringing on the display.
01591 
01592       1: phone will audibly ring over and over
01593       2: phone will audibly ring only once
01594       any other value, will NOT cause the phone to audibly ring
01595    */
01596    req->data.setringer.unknown1 = htolel(1);
01597    /* XXX the value here doesn't seem to change anything.  Must be higher than 0.
01598       Perhaps a packet capture can shed some light on this. */
01599    req->data.setringer.unknown2 = htolel(1);
01600    transmit_response(s, req);
01601 }
01602 
01603 static void transmit_displaymessage(struct skinnysession *s, const char *text)
01604 {
01605    struct skinny_req *req;
01606 
01607    if (text == 0) {
01608       if (!(req = req_alloc(0, CLEAR_DISPLAY_MESSAGE)))
01609          return;
01610 
01611       if (skinnydebug)
01612          ast_verbose("Clearing Display\n");
01613    } else {
01614       if (!(req = req_alloc(sizeof(struct displaytext_message), DISPLAYTEXT_MESSAGE)))
01615          return;
01616 
01617       ast_copy_string(req->data.displaytext.text, text, sizeof(req->data.displaytext.text));
01618       if (skinnydebug)
01619          ast_verbose("Displaying message '%s'\n", req->data.displaytext.text);
01620    }
01621 
01622    transmit_response(s, req);
01623 }
01624 
01625 static void transmit_displaynotify(struct skinnysession *s, const char *text, int t)
01626 {
01627    struct skinny_req *req;
01628 
01629    if (!(req = req_alloc(sizeof(struct display_notify_message), DISPLAY_NOTIFY_MESSAGE)))
01630       return;
01631 
01632    ast_copy_string(req->data.displaynotify.displayMessage, text, sizeof(req->data.displaynotify.displayMessage));
01633    req->data.displaynotify.displayTimeout = htolel(t);
01634 
01635    if (skinnydebug)
01636       ast_verbose("Displaying notify '%s'\n", text);
01637 
01638    transmit_response(s, req);
01639 }
01640 
01641 static void transmit_displaypromptstatus(struct skinnysession *s, const char *text, int t, int instance, int callid)
01642 {
01643    struct skinny_req *req;
01644 
01645    if (!(req = req_alloc(sizeof(struct display_prompt_status_message), DISPLAY_PROMPT_STATUS_MESSAGE)))
01646       return;
01647 
01648    ast_copy_string(req->data.displaypromptstatus.promptMessage, text, sizeof(req->data.displaypromptstatus.promptMessage));
01649    req->data.displaypromptstatus.messageTimeout = htolel(t);
01650    req->data.displaypromptstatus.lineInstance = htolel(instance);
01651    req->data.displaypromptstatus.callReference = htolel(callid);
01652 
01653    if (skinnydebug)
01654       ast_verbose("Displaying Prompt Status '%s'\n", text);
01655 
01656    transmit_response(s, req);
01657 }
01658 
01659 static void transmit_dialednumber(struct skinnysession *s, const char *text, int instance, int callid)
01660 {
01661    struct skinny_req *req;
01662 
01663    if (!(req = req_alloc(sizeof(struct dialed_number_message), DIALED_NUMBER_MESSAGE)))
01664       return;
01665 
01666    ast_copy_string(req->data.dialednumber.dialedNumber, text, sizeof(req->data.dialednumber.dialedNumber));
01667    req->data.dialednumber.lineInstance = htolel(instance);
01668    req->data.dialednumber.callReference = htolel(callid);
01669 
01670    transmit_response(s, req);
01671 }
01672 
01673 /*
01674 static int has_voicemail(struct skinny_line *l)
01675 {
01676    return ast_app_has_voicemail(l->mailbox, NULL);
01677 }
01678 */
01679 
01680 static void do_housekeeping(struct skinnysession *s)
01681 {
01682 /*
01683    int new;
01684    int old;
01685    struct skinny_device *d = s->device;
01686    struct skinny_line *l;
01687 */
01688 
01689    transmit_displaymessage(s, NULL);
01690 
01691 /*
01692    for (l = d->lines; l; l = l->next) {
01693       if (has_voicemail(l)) {
01694          if (skinnydebug)
01695             ast_verbose("Checking for voicemail Skinny %s@%s\n", l->name, d->name);
01696          ast_app_inboxcount(l->mailbox, &new, &old);
01697          if (skinnydebug)
01698             ast_verbose("Skinny %s@%s has voicemail!\n", l->name, d->name);
01699          transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
01700       } else {
01701          transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
01702       }
01703    }
01704 */
01705 }
01706 
01707 /* I do not believe skinny can deal with video.
01708    Anyone know differently? */
01709 /* Yes, it can.  Currently 7985 and Cisco VT Advantage do video. */
01710 static enum ast_rtp_get_result skinny_get_vrtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
01711 {
01712    struct skinny_subchannel *sub = NULL;
01713 
01714    if (!(sub = c->tech_pvt) || !(sub->vrtp))
01715       return AST_RTP_GET_FAILED;
01716 
01717    *rtp = sub->vrtp;
01718 
01719    return AST_RTP_TRY_NATIVE;
01720 }
01721 
01722 static enum ast_rtp_get_result skinny_get_rtp_peer(struct ast_channel *c, struct ast_rtp **rtp)
01723 {
01724    struct skinny_subchannel *sub = NULL;
01725 
01726    if (!(sub = c->tech_pvt) || !(sub->rtp))
01727       return AST_RTP_GET_FAILED;
01728 
01729    *rtp = sub->rtp;
01730 
01731    return AST_RTP_TRY_NATIVE;
01732 }
01733 
01734 static int skinny_set_rtp_peer(struct ast_channel *c, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active)
01735 {
01736    struct skinny_subchannel *sub;
01737    sub = c->tech_pvt;
01738    if (sub) {
01739       /* transmit_modify_with_sdp(sub, rtp); @@FIXME@@ if needed */
01740       return 0;
01741    }
01742    return -1;
01743 }
01744 
01745 static struct ast_rtp_protocol skinny_rtp = {
01746    .type = "Skinny",
01747    .get_rtp_info = skinny_get_rtp_peer,
01748    .get_vrtp_info = skinny_get_vrtp_peer,
01749    .set_rtp_peer = skinny_set_rtp_peer,
01750 };
01751 
01752 static int skinny_do_debug(int fd, int argc, char *argv[])
01753 {
01754    if (argc != 3) {
01755       return RESULT_SHOWUSAGE;
01756    }
01757    skinnydebug = 1;
01758    ast_cli(fd, "Skinny Debugging Enabled\n");
01759    return RESULT_SUCCESS;
01760 }
01761 
01762 static int skinny_no_debug(int fd, int argc, char *argv[])
01763 {
01764    if (argc != 4) {
01765       return RESULT_SHOWUSAGE;
01766    }
01767    skinnydebug = 0;
01768    ast_cli(fd, "Skinny Debugging Disabled\n");
01769    return RESULT_SUCCESS;
01770 }
01771 
01772 static char *complete_skinny_reset(const char *line, const char *word, int pos, int state)
01773 {
01774    struct skinny_device *d;
01775 
01776    char *result = NULL;
01777    int wordlen = strlen(word);
01778    int which = 0;
01779 
01780    if (pos == 2) {
01781       for (d = devices; d && !result; d = d->next) {
01782          if (!strncasecmp(word, d->id, wordlen) && ++which > state)
01783             result = ast_strdup(d->id);
01784       }
01785    }
01786 
01787    return result;
01788 }
01789 
01790 static int skinny_reset_device(int fd, int argc, char *argv[])
01791 {
01792    struct skinny_device *d;
01793    struct skinny_req *req;
01794 
01795    if (argc < 3 || argc > 4) {
01796       return RESULT_SHOWUSAGE;
01797    }
01798    ast_mutex_lock(&devicelock);
01799 
01800    for (d = devices; d; d = d->next) {
01801       int fullrestart = 0;
01802       if (!strcasecmp(argv[2], d->id) || !strcasecmp(argv[2], "all")) {
01803          if (!(d->session))
01804             continue;
01805 
01806          if (!(req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE)))
01807             continue;
01808 
01809          if (argc == 4 && !strcasecmp(argv[3], "restart"))
01810             fullrestart = 1;
01811 
01812          if (fullrestart)
01813             req->data.reset.resetType = 2;
01814          else
01815             req->data.reset.resetType = 1;
01816 
01817          if (option_verbose > 2)
01818             ast_verbose(VERBOSE_PREFIX_3 "%s device %s.\n", (fullrestart) ? "Restarting" : "Resetting", d->id);
01819          transmit_response(d->session, req);
01820       }
01821    }
01822    ast_mutex_unlock(&devicelock);
01823    return RESULT_SUCCESS;
01824 }
01825 
01826 static char *device2str(int type)
01827 {
01828    char *tmp;
01829 
01830    switch (type) {
01831    case SKINNY_DEVICE_NONE:
01832       return "No Device";
01833    case SKINNY_DEVICE_30SPPLUS:
01834       return "30SP Plus";
01835    case SKINNY_DEVICE_12SPPLUS:
01836       return "12SP Plus";
01837    case SKINNY_DEVICE_12SP:
01838       return "12SP";
01839    case SKINNY_DEVICE_12:
01840       return "12";
01841    case SKINNY_DEVICE_30VIP:
01842       return "30VIP";
01843    case SKINNY_DEVICE_7910:
01844       return "7910";
01845    case SKINNY_DEVICE_7960:
01846       return "7960";
01847    case SKINNY_DEVICE_7940:
01848       return "7940";
01849    case SKINNY_DEVICE_7935:
01850       return "7935";
01851    case SKINNY_DEVICE_ATA186:
01852       return "ATA186";
01853    case SKINNY_DEVICE_7941:
01854       return "7941";
01855    case SKINNY_DEVICE_7971:
01856       return "7971";
01857    case SKINNY_DEVICE_7985:
01858       return "7985";
01859    case SKINNY_DEVICE_7911:
01860       return "7911";
01861    case SKINNY_DEVICE_7961GE:
01862       return "7961GE";
01863    case SKINNY_DEVICE_7941GE:
01864       return "7941GE";
01865    case SKINNY_DEVICE_7905:
01866       return "7905";
01867    case SKINNY_DEVICE_7920:
01868       return "7920";
01869    case SKINNY_DEVICE_7970:
01870       return "7970";
01871    case SKINNY_DEVICE_7912:
01872       return "7912";
01873    case SKINNY_DEVICE_7902:
01874       return "7902";
01875    case SKINNY_DEVICE_CIPC:
01876       return "IP Communicator";
01877    case SKINNY_DEVICE_7961:
01878       return "7961";
01879    case SKINNY_DEVICE_7936:
01880       return "7936";
01881    case SKINNY_DEVICE_SCCPGATEWAY_AN:
01882       return "SCCPGATEWAY_AN";
01883    case SKINNY_DEVICE_SCCPGATEWAY_BRI:
01884       return "SCCPGATEWAY_BRI";
01885    case SKINNY_DEVICE_UNKNOWN:
01886       return "Unknown";
01887    default:
01888       if (!(tmp = ast_threadstorage_get(&device2str_threadbuf, DEVICE2STR_BUFSIZE)))
01889          return "Unknown";
01890       snprintf(tmp, DEVICE2STR_BUFSIZE, "UNKNOWN-%d", type);
01891       return tmp;
01892    }
01893 }
01894 
01895 static int skinny_show_devices(int fd, int argc, char *argv[])
01896 {
01897    struct skinny_device *d;
01898    struct skinny_line *l;
01899    int numlines = 0;
01900 
01901    if (argc != 3) {
01902       return RESULT_SHOWUSAGE;
01903    }
01904    ast_mutex_lock(&devicelock);
01905 
01906    ast_cli(fd, "Name                 DeviceId         IP              Type            R NL\n");
01907    ast_cli(fd, "-------------------- ---------------- --------------- --------------- - --\n");
01908    for (d = devices; d; d = d->next) {
01909       numlines = 0;
01910       for (l = d->lines; l; l = l->next) {
01911          numlines++;
01912       }
01913 
01914       ast_cli(fd, "%-20s %-16s %-15s %-15s %c %2d\n",
01915             d->name,
01916             d->id,
01917             d->session?ast_inet_ntoa(d->session->sin.sin_addr):"",
01918             device2str(d->type),
01919             d->registered?'Y':'N',
01920             numlines);
01921    }
01922    ast_mutex_unlock(&devicelock);
01923    return RESULT_SUCCESS;
01924 }
01925 
01926 static int skinny_show_lines(int fd, int argc, char *argv[])
01927 {
01928    struct skinny_device *d;
01929    struct skinny_line *l;
01930 
01931    if (argc != 3) {
01932       return RESULT_SHOWUSAGE;
01933    }
01934    ast_mutex_lock(&devicelock);
01935 
01936    ast_cli(fd, "Device Name          Instance Name                 Label               \n");
01937    ast_cli(fd, "-------------------- -------- -------------------- --------------------\n");
01938    for (d = devices; d; d = d->next) {
01939       for (l = d->lines; l; l = l->next) {
01940          ast_cli(fd, "%-20s %8d %-20s %-20s\n",
01941             d->name,
01942             l->instance,
01943             l->name,
01944             l->label);
01945       }
01946    }
01947 
01948    ast_mutex_unlock(&devicelock);
01949    return RESULT_SUCCESS;
01950 }
01951 
01952 static const char show_devices_usage[] =
01953 "Usage: skinny show devices\n"
01954 "       Lists all devices known to the Skinny subsystem.\n";
01955 
01956 static const char show_lines_usage[] =
01957 "Usage: skinny show lines\n"
01958 "       Lists all lines known to the Skinny subsystem.\n";
01959 
01960 static const char debug_usage[] =
01961 "Usage: skinny set debug\n"
01962 "       Enables dumping of Skinny packets for debugging purposes\n";
01963 
01964 static const char no_debug_usage[] =
01965 "Usage: skinny set debug off\n"
01966 "       Disables dumping of Skinny packets for debugging purposes\n";
01967 
01968 static const char reset_usage[] =
01969 "Usage: skinny reset <DeviceId|all> [restart]\n"
01970 "       Causes a Skinny device to reset itself, optionally with a full restart\n";
01971 
01972 static struct ast_cli_entry cli_skinny[] = {
01973    { { "skinny", "show", "devices", NULL },
01974    skinny_show_devices, "List defined Skinny devices",
01975    show_devices_usage },
01976 
01977    { { "skinny", "show", "lines", NULL },
01978    skinny_show_lines, "List defined Skinny lines per device",
01979    show_lines_usage },
01980 
01981    { { "skinny", "set", "debug", NULL },
01982    skinny_do_debug, "Enable Skinny debugging",
01983    debug_usage },
01984 
01985    { { "skinny", "set", "debug", "off", NULL },
01986    skinny_no_debug, "Disable Skinny debugging",
01987    no_debug_usage },
01988 
01989    { { "skinny", "reset", NULL },
01990    skinny_reset_device, "Reset Skinny device(s)",
01991    reset_usage, complete_skinny_reset },
01992 };
01993 
01994 #if 0
01995 static struct skinny_paging_device *build_paging_device(const char *cat, struct ast_variable *v)
01996 {
01997    return NULL;
01998 }
01999 #endif
02000 
02001 static struct skinny_device *build_device(const char *cat, struct ast_variable *v)
02002 {
02003    struct skinny_device *d;
02004    struct skinny_line *l;
02005    struct skinny_speeddial *sd;
02006    struct skinny_addon *a;
02007    int lineInstance = 1;
02008    int speeddialInstance = 1;
02009    int y = 0;
02010 
02011    if (!(d = ast_calloc(1, sizeof(struct skinny_device)))) {
02012       return NULL;
02013    } else {
02014       ast_copy_string(d->name, cat, sizeof(d->name));
02015       d->lastlineinstance = 1;
02016       d->capability = default_capability;
02017       d->prefs = default_prefs;
02018       while(v) {
02019          if (!strcasecmp(v->name, "host")) {
02020             if (ast_get_ip(&d->addr, v->value)) {
02021                free(d);
02022                return NULL;
02023             }
02024          } else if (!strcasecmp(v->name, "port")) {
02025             d->addr.sin_port = htons(atoi(v->value));
02026          } else if (!strcasecmp(v->name, "device")) {
02027             ast_copy_string(d->id, v->value, sizeof(d->id));
02028          } else if (!strcasecmp(v->name, "permit") || !strcasecmp(v->name, "deny")) {
02029             d->ha = ast_append_ha(v->name, v->value, d->ha, NULL);
02030          } else if (!strcasecmp(v->name, "context")) {
02031             ast_copy_string(context, v->value, sizeof(context));
02032          } else if (!strcasecmp(v->name, "allow")) {
02033             ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 1);
02034          } else if (!strcasecmp(v->name, "disallow")) {
02035             ast_parse_allow_disallow(&d->prefs, &d->capability, v->value, 0);
02036          } else if (!strcasecmp(v->name, "version")) {
02037             ast_copy_string(d->version_id, v->value, sizeof(d->version_id));
02038          } else if (!strcasecmp(v->name, "nat")) {
02039             nat = ast_true(v->value);
02040          } else if (!strcasecmp(v->name, "callerid")) {
02041             if (!strcasecmp(v->value, "asreceived")) {
02042                cid_num[0] = '\0';
02043                cid_name[0] = '\0';
02044             } else {
02045                ast_callerid_split(v->value, cid_name, sizeof(cid_name), cid_num, sizeof(cid_num));
02046             }
02047          } else if (!strcasecmp(v->name, "language")) {
02048             ast_copy_string(language, v->value, sizeof(language));
02049          } else if (!strcasecmp(v->name, "accountcode")) {
02050             ast_copy_string(accountcode, v->value, sizeof(accountcode));
02051          } else if (!strcasecmp(v->name, "amaflags")) {
02052             y = ast_cdr_amaflags2int(v->value);
02053             if (y < 0) {
02054                ast_log(LOG_WARNING, "Invalid AMA flags: %s at line %d\n", v->value, v->lineno);
02055             } else {
02056                amaflags = y;
02057             }
02058          } else if (!strcasecmp(v->name, "mohinterpret") || !strcasecmp(v->name, "musiconhold")) {
02059             ast_copy_string(mohinterpret, v->value, sizeof(mohinterpret));
02060          } else if (!strcasecmp(v->name, "mohsuggest")) {
02061             ast_copy_string(mohsuggest, v->value, sizeof(mohsuggest));
02062          } else if (!strcasecmp(v->name, "callgroup")) {
02063             cur_callergroup = ast_get_group(v->value);
02064          } else if (!strcasecmp(v->name, "pickupgroup")) {
02065             cur_pickupgroup = ast_get_group(v->value);
02066          } else if (!strcasecmp(v->name, "immediate")) {
02067             immediate = ast_true(v->value);
02068          } else if (!strcasecmp(v->name, "cancallforward")) {
02069             cancallforward = ast_true(v->value);
02070          } else if (!strcasecmp(v->name, "mailbox")) {
02071             ast_copy_string(mailbox, v->value, sizeof(mailbox));
02072          } else if (!strcasecmp(v->name, "callreturn")) {
02073             callreturn = ast_true(v->value);
02074          } else if (!strcasecmp(v->name, "callwaiting")) {
02075             callwaiting = ast_true(v->value);
02076          } else if (!strcasecmp(v->name, "transfer")) {
02077             transfer = ast_true(v->value);
02078          } else if (!strcasecmp(v->name, "threewaycalling")) {
02079             threewaycalling = ast_true(v->value);
02080          } else if (!strcasecmp(v->name, "mwiblink")) {
02081             mwiblink = ast_true(v->value);
02082          } else if (!strcasecmp(v->name, "linelabel")) {
02083             ast_copy_string(linelabel, v->value, sizeof(linelabel));
02084          } else if (!strcasecmp(v->name, "speeddial")) {
02085             if (!(sd = ast_calloc(1, sizeof(struct skinny_speeddial)))) {
02086                return NULL;
02087             } else {
02088                char *stringp, *exten, *label;
02089                stringp = v->value;
02090                exten = strsep(&stringp, ",");
02091                label = strsep(&stringp, ",");
02092                ast_mutex_init(&sd->lock);
02093                ast_copy_string(sd->exten, exten, sizeof(sd->exten));
02094                if (label)
02095                   ast_copy_string(sd->label, label, sizeof(sd->label));
02096                else
02097                   ast_copy_string(sd->label, exten, sizeof(sd->label));
02098                sd->instance = speeddialInstance++;
02099 
02100                sd->next = d->speeddials;
02101                d->speeddials = sd;
02102             }
02103          } else if (!strcasecmp(v->name, "addon")) {
02104             if (!(a = ast_calloc(1, sizeof(struct skinny_addon)))) {
02105                return NULL;
02106             } else {
02107                ast_mutex_init(&a->lock);
02108                ast_copy_string(a->type, v->value, sizeof(a->type));
02109 
02110                a->next = d->addons;
02111                d->addons = a;
02112             }
02113          } else if (!strcasecmp(v->name, "trunk") || !strcasecmp(v->name, "line")) {
02114             if (!(l = ast_calloc(1, sizeof(struct skinny_line)))) {
02115                return NULL;
02116             } else {
02117                ast_mutex_init(&l->lock);
02118                ast_copy_string(l->name, v->value, sizeof(l->name));
02119 
02120                /* XXX Should we check for uniqueness?? XXX */
02121                ast_copy_string(l->context, context, sizeof(l->context));
02122                ast_copy_string(l->cid_num, cid_num, sizeof(l->cid_num));
02123                ast_copy_string(l->cid_name, cid_name, sizeof(l->cid_name));
02124                ast_copy_string(l->label, linelabel, sizeof(l->label));
02125                ast_copy_string(l->language, language, sizeof(l->language));
02126                ast_copy_string(l->mohinterpret, mohinterpret, sizeof(l->mohinterpret));
02127                ast_copy_string(l->mohsuggest, mohsuggest, sizeof(l->mohsuggest));
02128                ast_copy_string(l->mailbox, mailbox, sizeof(l->mailbox));
02129                ast_copy_string(l->mailbox, mailbox, sizeof(l->mailbox));
02130                if (!ast_strlen_zero(mailbox)) {
02131                   if (option_verbose > 2)
02132                      ast_verbose(VERBOSE_PREFIX_3 "Setting mailbox '%s' on %s@%s\n", mailbox, d->name, l->name);
02133                }
02134                l->msgstate = -1;
02135                l->capability = d->capability;
02136                l->prefs = d->prefs;
02137                l->parent = d;
02138                if (!strcasecmp(v->name, "trunk")) {
02139                   l->type = TYPE_TRUNK;
02140                } else {
02141                   l->type = TYPE_LINE;
02142                }
02143                l->immediate = immediate;
02144                l->callgroup = cur_callergroup;
02145                l->pickupgroup = cur_pickupgroup;
02146                l->callreturn = callreturn;
02147                l->cancallforward = cancallforward;
02148                l->callwaiting = callwaiting;
02149                l->transfer = transfer;
02150                l->threewaycalling = threewaycalling;
02151                l->mwiblink = mwiblink;
02152                l->onhooktime = time(NULL);
02153                l->instance = lineInstance++;
02154                /* ASSUME we're onhook at this point */
02155                l->hookstate = SKINNY_ONHOOK;
02156                l->nat = nat;
02157 
02158                l->next = d->lines;
02159                d->lines = l;
02160             }
02161          } else {
02162             ast_log(LOG_WARNING, "Don't know keyword '%s' at line %d\n", v->name, v->lineno);
02163          }
02164          v = v->next;
02165       }
02166 
02167       if (!d->lines) {
02168          ast_log(LOG_ERROR, "A Skinny device must have at least one line!\n");
02169          return NULL;
02170       }
02171       if (/*d->addr.sin_addr.s_addr && */!ntohs(d->addr.sin_port)) {
02172          d->addr.sin_port = htons(DEFAULT_SKINNY_PORT);
02173       }
02174 #if 0
02175       /* I don't think we need this anymore at all, since d->ourip is set in skinny_register now */
02176       if (d->addr.sin_addr.s_addr) {
02177          /* XXX See note above, in 'host' option. */
02178          if (ast_ouraddrfor(&d->addr.sin_addr, &d->ourip)) {
02179             d->ourip = __ourip;
02180          }
02181       } else {
02182          d->ourip = __ourip;
02183       }
02184 #endif
02185    }
02186    return d;
02187 }
02188 
02189 static void start_rtp(struct skinny_subchannel *sub)
02190 {
02191    struct skinny_line *l = sub->parent;
02192    struct skinny_device *d = l->parent;
02193    int hasvideo = 0;
02194 
02195    ast_mutex_lock(&sub->lock);
02196    /* Allocate the RTP */
02197    sub->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
02198    if (hasvideo)
02199       sub->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
02200    
02201    if (sub->rtp && sub->owner) {
02202       sub->owner->fds[0] = ast_rtp_fd(sub->rtp);
02203       sub->owner->fds[1] = ast_rtcp_fd(sub->rtp);
02204    }
02205    if (hasvideo && sub->vrtp && sub->owner) {
02206       sub->owner->fds[2] = ast_rtp_fd(sub->vrtp);
02207       sub->owner->fds[3] = ast_rtcp_fd(sub->vrtp);
02208    }
02209    if (sub->rtp) {
02210       ast_rtp_setnat(sub->rtp, l->nat);
02211    }
02212    if (sub->vrtp) {
02213       ast_rtp_setnat(sub->vrtp, l->nat);
02214    }
02215    /* Set Frame packetization */
02216    if (sub->rtp)
02217       ast_rtp_codec_setpref(sub->rtp, &l->prefs);
02218 
02219    /* Create the RTP connection */
02220    transmit_connect(d->session, sub);
02221    ast_mutex_unlock(&sub->lock);
02222 }
02223 
02224 static void *skinny_newcall(void *data)
02225 {
02226    struct ast_channel *c = data;
02227    struct skinny_subchannel *sub = c->tech_pvt;
02228    struct skinny_line *l = sub->parent;
02229    struct skinny_device *d = l->parent;
02230    struct skinnysession *s = d->session;
02231    int res = 0;
02232 
02233    ast_copy_string(l->lastnumberdialed, c->exten, sizeof(l->lastnumberdialed));
02234    ast_set_callerid(c,
02235       l->hidecallerid ? "" : l->cid_num,
02236       l->hidecallerid ? "" : l->cid_name,
02237       c->cid.cid_ani ? NULL : l->cid_num);
02238    ast_setstate(c, AST_STATE_RING);
02239    res = ast_pbx_run(c);
02240    if (res) {
02241       ast_log(LOG_WARNING, "PBX exited non-zero\n");
02242       transmit_tone(s, SKINNY_REORDER);
02243    }
02244    return NULL;
02245 }
02246 
02247 static void *skinny_ss(void *data)
02248 {
02249    struct ast_channel *c = data;
02250    struct skinny_subchannel *sub = c->tech_pvt;
02251    struct skinny_line *l = sub->parent;
02252    struct skinny_device *d = l->parent;
02253    struct skinnysession *s = d->session;
02254    char exten[AST_MAX_EXTENSION] = "";
02255    int len = 0;
02256    int timeout = firstdigittimeout;
02257    int res;
02258    int getforward=0;
02259 
02260    if (option_verbose > 2)
02261       ast_verbose( VERBOSE_PREFIX_3 "Starting simple switch on '%s@%s'\n", l->name, d->name);
02262 
02263    while (len < AST_MAX_EXTENSION-1) {
02264       res = ast_waitfordigit(c, timeout);
02265       timeout = 0;
02266       if (res < 0) {
02267          if (skinnydebug)
02268             ast_verbose("Skinny(%s@%s): waitfordigit returned < 0\n", l->name, d->name);
02269          ast_indicate(c, -1);
02270          ast_hangup(c);
02271          return NULL;
02272       } else if (res) {
02273          exten[len++]=res;
02274          exten[len] = '\0';
02275       }
02276       if (!ast_ignore_pattern(c->context, exten)) {
02277          transmit_tone(s, SKINNY_SILENCE);
02278       }
02279       if (ast_exists_extension(c, c->context, exten, 1, l->cid_num)) {
02280          if (!res || !ast_matchmore_extension(c, c->context, exten, 1, l->cid_num)) {
02281             if (getforward) {
02282                /* Record this as the forwarding extension */
02283                ast_copy_string(l->call_forward, exten, sizeof(l->call_forward));
02284                if (option_verbose > 2)
02285                   ast_verbose(VERBOSE_PREFIX_3 "Setting call forward to '%s' on channel %s\n",
02286                      l->call_forward, c->name);
02287                transmit_tone(s, SKINNY_DIALTONE);
02288                if (res) {
02289                   break;
02290                }
02291                ast_safe_sleep(c, 500);
02292                ast_indicate(c, -1);
02293                ast_safe_sleep(c, 1000);
02294                memset(exten, 0, sizeof(exten));
02295                transmit_tone(s, SKINNY_DIALTONE);
02296                len = 0;
02297                getforward = 0;
02298             } else {
02299                ast_copy_string(c->exten, exten, sizeof(c->exten));
02300                ast_copy_string(l->lastnumberdialed, exten, sizeof(l->lastnumberdialed));
02301                skinny_newcall(c);
02302                return NULL;
02303             }
02304          } else {
02305             /* It's a match, but they just typed a digit, and there is an ambiguous match,
02306                so just set the timeout to matchdigittimeout and wait some more */
02307             timeout = matchdigittimeout;
02308          }
02309       } else if (res == 0) {
02310          if (option_debug)
02311             ast_log(LOG_DEBUG, "Not enough digits (and no ambiguous match)...\n");
02312          transmit_tone(s, SKINNY_REORDER);
02313          ast_hangup(c);
02314          return NULL;
02315       } else if (!ast_canmatch_extension(c, c->context, exten, 1, c->cid.cid_num) &&
02316             ((exten[0] != '*') || (!ast_strlen_zero(exten) > 2))) {
02317          ast_log(LOG_WARNING, "Can't match [%s] from '%s' in context %s\n", exten, c->cid.cid_num ? c->cid.cid_num : "<Unknown Caller>", c->context);
02318          transmit_tone(s, SKINNY_REORDER);
02319          /* hang out for 3 seconds to let congestion play */
02320          ast_safe_sleep(c, 3000);
02321          break;
02322       }
02323       if (!timeout) {
02324          timeout = gendigittimeout;
02325       }
02326       if (len && !ast_ignore_pattern(c->context, exten)) {
02327          ast_indicate(c, -1);
02328       }
02329    }
02330    ast_hangup(c);
02331    return NULL;
02332 }
02333 
02334 
02335 
02336 static int skinny_call(struct ast_channel *ast, char *dest, int timeout)
02337 {
02338    int res = 0;
02339    int tone = 0;
02340    struct skinny_subchannel *sub = ast->tech_pvt;
02341    struct skinny_line *l = sub->parent;
02342    struct skinny_device *d = l->parent;
02343    struct skinnysession *s = d->session;
02344 
02345    if (!d->registered) {
02346       ast_log(LOG_ERROR, "Device not registered, cannot call %s\n", dest);
02347       return -1;
02348    }
02349 
02350    if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
02351       ast_log(LOG_WARNING, "skinny_call called on %s, neither down nor reserved\n", ast->name);
02352       return -1;
02353    }
02354 
02355    if (skinnydebug)
02356       ast_verbose(VERBOSE_PREFIX_3 "skinny_call(%s)\n", ast->name);
02357 
02358    if (l->dnd) {
02359       ast_queue_control(ast, AST_CONTROL_BUSY);
02360       return -1;
02361    }
02362 
02363    switch (l->hookstate) {
02364    case SKINNY_OFFHOOK:
02365       tone = SKINNY_CALLWAITTONE;
02366       break;
02367    case SKINNY_ONHOOK:
02368       tone = SKINNY_ALERT;
02369       break;
02370    default:
02371       ast_log(LOG_ERROR, "Don't know how to deal with hookstate %d\n", l->hookstate);
02372       break;
02373    }
02374 
02375    transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
02376    transmit_ringer_mode(s, SKINNY_RING_INSIDE);
02377 
02378    transmit_tone(s, tone);
02379    transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, l->cid_name, l->cid_num, l->instance, sub->callid, 1);
02380    transmit_callstate(s, l->instance, SKINNY_RINGIN, sub->callid);
02381    transmit_displaypromptstatus(s, "Ring-In", 0, l->instance, sub->callid);
02382    transmit_selectsoftkeys(s, l->instance, sub->callid, KEYDEF_RINGIN);
02383 
02384    ast_setstate(ast, AST_STATE_RINGING);
02385    ast_queue_control(ast, AST_CONTROL_RINGING);
02386    sub->outgoing = 1;
02387    return res;
02388 }
02389 
02390 static int skinny_hangup(struct ast_channel *ast)
02391 {
02392    struct skinny_subchannel *sub = ast->tech_pvt;
02393    struct skinny_line *l;
02394    struct skinny_device *d;
02395    struct skinnysession *s;
02396 
02397    if (!sub) {
02398       if (option_debug)
02399          ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
02400       return 0;
02401    }
02402    l = sub->parent;
02403    d = l->parent;
02404    s = d->session;
02405    if (skinnydebug)
02406       ast_verbose("skinny_hangup(%s) on %s@%s\n", ast->name, l->name, d->name);
02407 
02408    if (d->registered) {
02409       if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_OFFHOOK)) {
02410          l->hookstate = SKINNY_ONHOOK;
02411          transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
02412          transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
02413          transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
02414       } else if ((l->type = TYPE_LINE) && (l->hookstate == SKINNY_ONHOOK)) {
02415          transmit_callstate(s, l->instance, SKINNY_ONHOOK, sub->callid);
02416          transmit_speaker_mode(s, SKINNY_SPEAKEROFF);
02417          transmit_ringer_mode(s, SKINNY_RING_OFF);
02418          transmit_tone(s, SKINNY_SILENCE);
02419          transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_OFF);
02420          do_housekeeping(s);
02421       }
02422    }
02423    ast_mutex_lock(&sub->lock);
02424    sub->owner = NULL;
02425    ast->tech_pvt = NULL;
02426    sub->alreadygone = 0;
02427    sub->outgoing = 0;
02428    if (sub->rtp) {
02429       ast_rtp_destroy(sub->rtp);
02430       sub->rtp = NULL;
02431    }
02432    ast_mutex_unlock(&sub->lock);
02433    return 0;
02434 }
02435 
02436 static int skinny_answer(struct ast_channel *ast)
02437 {
02438    int res = 0;
02439    struct skinny_subchannel *sub = ast->tech_pvt;
02440    struct skinny_line *l = sub->parent;
02441    struct skinny_device *d = l->parent;
02442    struct skinnysession *s = d->session;
02443 
02444    sub->cxmode = SKINNY_CX_SENDRECV;
02445    if (!sub->rtp) {
02446       start_rtp(sub);
02447    }
02448    if (skinnydebug)
02449       ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast->name, l->name, d->name, sub->callid);
02450    if (ast->_state != AST_STATE_UP) {
02451       ast_setstate(ast, AST_STATE_UP);
02452    }
02453 
02454    transmit_tone(s, SKINNY_SILENCE);
02455    /* order matters here...
02456       for some reason, transmit_callinfo must be before transmit_callstate,
02457       or you won't get keypad messages in some situations. */
02458    transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2);
02459    transmit_callstate(s, l->instance, SKINNY_CONNECTED, sub->callid);
02460    transmit_displaypromptstatus(s, "Connected", 0, l->instance, sub->callid);
02461    return res;
02462 }
02463 
02464 /* Retrieve audio/etc from channel.  Assumes sub->lock is already held. */
02465 static struct ast_frame *skinny_rtp_read(struct skinny_subchannel *sub)
02466 {
02467    struct ast_channel *ast = sub->owner;
02468    struct ast_frame *f;
02469 
02470    if (!sub->rtp) {
02471       /* We have no RTP allocated for this channel */
02472       return &ast_null_frame;
02473    }
02474 
02475    switch(ast->fdno) {
02476    case 0:
02477       f = ast_rtp_read(sub->rtp);   /* RTP Audio */
02478       break;
02479    case 1:
02480       f = ast_rtcp_read(sub->rtp);  /* RTCP Control Channel */
02481       break;
02482    case 2:
02483       f = ast_rtp_read(sub->vrtp);  /* RTP Video */
02484       break;
02485    case 3:
02486       f = ast_rtcp_read(sub->vrtp); /* RTCP Control Channel for video */
02487       break;
02488 #if 0
02489    case 5:
02490       /* Not yet supported */
02491       f = ast_udptl_read(sub->udptl);  /* UDPTL for T.38 */
02492       break;
02493 #endif
02494    default:
02495       f = &ast_null_frame;
02496    }
02497 
02498    if (ast) {
02499       /* We already hold the channel lock */
02500       if (f->frametype == AST_FRAME_VOICE) {
02501          if (f->subclass != ast->nativeformats) {
02502             if (option_debug)
02503                ast_log(LOG_DEBUG, "Oooh, format changed to %d\n", f->subclass);
02504             ast->nativeformats = f->subclass;
02505             ast_set_read_format(ast, ast->readformat);
02506             ast_set_write_format(ast, ast->writeformat);
02507          }
02508       }
02509    }
02510    return f;
02511 }
02512 
02513 static struct ast_frame *skinny_read(struct ast_channel *ast)
02514 {
02515    struct ast_frame *fr;
02516    struct skinny_subchannel *sub = ast->tech_pvt;
02517    ast_mutex_lock(&sub->lock);
02518    fr = skinny_rtp_read(sub);
02519    ast_mutex_unlock(&sub->lock);
02520    return fr;
02521 }
02522 
02523 static int skinny_write(struct ast_channel *ast, struct ast_frame *frame)
02524 {
02525    struct skinny_subchannel *sub = ast->tech_pvt;
02526    int res = 0;
02527    if (frame->frametype != AST_FRAME_VOICE) {
02528       if (frame->frametype == AST_FRAME_IMAGE) {
02529          return 0;
02530       } else {
02531          ast_log(LOG_WARNING, "Can't send %d type frames with skinny_write\n", frame->frametype);
02532          return 0;
02533       }
02534    } else {
02535       if (!(frame->subclass & ast->nativeformats)) {
02536          ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
02537             frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
02538          return -1;
02539       }
02540    }
02541    if (sub) {
02542       ast_mutex_lock(&sub->lock);
02543       if (sub->rtp) {
02544          res = ast_rtp_write(sub->rtp, frame);
02545       }
02546       ast_mutex_unlock(&sub->lock);
02547    }
02548    return res;
02549 }
02550 
02551 static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
02552 {
02553    struct skinny_subchannel *sub = newchan->tech_pvt;
02554    ast_log(LOG_NOTICE, "skinny_fixup(%s, %s)\n", oldchan->name, newchan->name);
02555    if (sub->owner != oldchan) {
02556       ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
02557       return -1;
02558    }
02559    sub->owner = newchan;
02560    return 0;
02561 }
02562 
02563 static int skinny_senddigit_begin(struct ast_channel *ast, char digit)
02564 {
02565    return -1; /* Start inband indications */
02566 }
02567 
02568 static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration)
02569 {
02570 #if 0
02571    struct skinny_subchannel *sub = ast->tech_pvt;
02572    struct skinny_line *l = sub->parent;
02573    struct skinny_device *d = l->parent;
02574    int tmp;
02575    /* not right */
02576    sprintf(tmp, "%d", digit);
02577    transmit_tone(d->session, digit);
02578 #endif
02579    return -1; /* Stop inband indications */
02580 }
02581 
02582 static char *control2str(int ind) {
02583    char *tmp;
02584 
02585    switch (ind) {
02586    case AST_CONTROL_HANGUP:
02587       return "Other end has hungup";
02588    case AST_CONTROL_RING:
02589       return "Local ring";
02590    case AST_CONTROL_RINGING:
02591       return "Remote end is ringing";
02592    case AST_CONTROL_ANSWER:
02593       return "Remote end has answered";
02594    case AST_CONTROL_BUSY:
02595       return "Remote end is busy";
02596    case AST_CONTROL_TAKEOFFHOOK:
02597       return "Make it go off hook";
02598    case AST_CONTROL_OFFHOOK:
02599       return "Line is off hook";
02600    case AST_CONTROL_CONGESTION:
02601       return "Congestion (circuits busy)";
02602    case AST_CONTROL_FLASH:
02603       return "Flash hook";
02604    case AST_CONTROL_WINK:
02605       return "Wink";
02606    case AST_CONTROL_OPTION:
02607       return "Set a low-level option";
02608    case AST_CONTROL_RADIO_KEY:
02609       return "Key Radio";
02610    case AST_CONTROL_RADIO_UNKEY:
02611       return "Un-Key Radio";
02612    case AST_CONTROL_PROGRESS:
02613       return "Remote end is making Progress";
02614    case AST_CONTROL_PROCEEDING:
02615       return "Remote end is proceeding";
02616    case AST_CONTROL_HOLD:
02617       return "Hold";
02618    case AST_CONTROL_UNHOLD:
02619       return "Unhold";
02620    case -1:
02621       return "Stop tone";
02622    default:
02623       if (!(tmp = ast_threadstorage_get(&control2str_threadbuf, CONTROL2STR_BUFSIZE)))
02624                         return "Unknown";
02625       snprintf(tmp, CONTROL2STR_BUFSIZE, "UNKNOWN-%d", ind);
02626       return tmp;
02627    }
02628 }
02629 
02630 
02631 static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, size_t datalen)
02632 {
02633    struct skinny_subchannel *sub = ast->tech_pvt;
02634    struct skinny_line *l = sub->parent;
02635    struct skinny_device *d = l->parent;
02636    struct skinnysession *s = d->session;
02637 
02638    if (skinnydebug)
02639       ast_verbose(VERBOSE_PREFIX_3 "Asked to indicate '%s' condition on channel %s\n", control2str(ind), ast->name);
02640    switch(ind) {
02641    case AST_CONTROL_RINGING:
02642       if (ast->_state != AST_STATE_UP) {
02643          if (!sub->progress) {
02644             transmit_tone(s, SKINNY_ALERT);
02645             transmit_callstate(s, l->instance, SKINNY_RINGOUT, sub->callid);
02646             transmit_dialednumber(s, ast->exten, l->instance, sub->callid);
02647             transmit_displaypromptstatus(s, "Ring Out", 0, l->instance, sub->callid);
02648             transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
02649             sub->ringing = 1;
02650             break;
02651          }
02652       }
02653       return -1;
02654    case AST_CONTROL_BUSY:
02655       if (ast->_state != AST_STATE_UP) {
02656          transmit_tone(s, SKINNY_BUSYTONE);
02657          transmit_callstate(s, l->instance, SKINNY_BUSY, sub->callid);
02658          sub->alreadygone = 1;
02659          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
02660          break;
02661       }
02662       return -1;
02663    case AST_CONTROL_CONGESTION:
02664       if (ast->_state != AST_STATE_UP) {
02665          transmit_tone(s, SKINNY_REORDER);
02666          transmit_callstate(s, l->instance, SKINNY_CONGESTION, sub->callid);
02667          sub->alreadygone = 1;
02668          ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
02669          break;
02670       }
02671       return -1;
02672    case AST_CONTROL_PROGRESS:
02673       if ((ast->_state != AST_STATE_UP) && !sub->progress && !sub->outgoing) {
02674          transmit_tone(s, SKINNY_ALERT);
02675          transmit_callstate(s, l->instance, SKINNY_PROGRESS, sub->callid);
02676          transmit_displaypromptstatus(s, "Call Progress", 0, l->instance, sub->callid);
02677          transmit_callinfo(s, ast->cid.cid_name, ast->cid.cid_num, ast->exten, ast->exten, l->instance, sub->callid, 2); /* 2 = outgoing from phone */
02678          sub->progress = 1;
02679          break;
02680       }
02681       return -1;
02682    case -1:
02683       transmit_tone(s, SKINNY_SILENCE);
02684       break;
02685    case AST_CONTROL_HOLD:
02686       ast_moh_start(ast, data, l->mohinterpret);
02687       break;
02688    case AST_CONTROL_UNHOLD:
02689       ast_moh_stop(ast);
02690       break;
02691    case AST_CONTROL_PROCEEDING:
02692       break;
02693    default:
02694       ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", ind);
02695       return -1;
02696    }
02697    return 0;
02698 }
02699 
02700 static struct ast_channel *skinny_new(struct skinny_line *l, int state)
02701 {
02702    struct ast_channel *tmp;
02703    struct skinny_subchannel *sub;
02704    struct skinny_device *d = l->parent;
02705    int fmt;
02706 
02707    tmp = ast_channel_alloc(1, state, l->cid_num, l->cid_name, "Skinny/%s@%s-%d", l->name, d->name, callnums);
02708    if (!tmp) {
02709       ast_log(LOG_WARNING, "Unable to allocate channel structure\n");
02710       return NULL;
02711    } else {
02712       sub = ast_calloc(1, sizeof(struct skinny_subchannel));
02713       if (!sub) {
02714          ast_log(LOG_WARNING, "Unable to allocate Skinny subchannel\n");
02715          return NULL;
02716       } else {
02717          ast_mutex_init(&sub->lock);
02718 
02719          sub->owner = tmp;
02720          sub->callid = callnums++;
02721          d->lastlineinstance = l->instance;
02722          d->lastcallreference = sub->callid;
02723          sub->cxmode = SKINNY_CX_INACTIVE;
02724          sub->nat = l->nat;
02725          sub->parent = l;
02726          sub->onhold = 0;
02727 
02728          sub->next = l->sub;
02729          l->sub = sub;
02730       }
02731       tmp->tech = &skinny_tech;
02732       tmp->tech_pvt = sub;
02733       tmp->nativeformats = l->capability;
02734       if (!tmp->nativeformats)
02735          tmp->nativeformats = default_capability;
02736       fmt = ast_best_codec(tmp->nativeformats);
02737       if (skinnydebug)
02738          ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp->nativeformats, fmt);
02739       if (sub->rtp) {
02740          tmp->fds[0] = ast_rtp_fd(sub->rtp);
02741       }
02742       if (state == AST_STATE_RING) {
02743          tmp->rings = 1;
02744       }
02745       tmp->writeformat = fmt;
02746       tmp->rawwriteformat = fmt;
02747       tmp->readformat = fmt;
02748       tmp->rawreadformat = fmt;
02749       if (!ast_strlen_zero(l->language))
02750          ast_string_field_set(tmp, language, l->language);
02751       if (!ast_strlen_zero(l->accountcode))
02752          ast_string_field_set(tmp, accountcode, l->accountcode);
02753       if (l->amaflags)
02754          tmp->amaflags = l->amaflags;
02755 
02756       ast_module_ref(ast_module_info->self);
02757       tmp->callgroup = l->callgroup;
02758       tmp->pickupgroup = l->pickupgroup;
02759       ast_string_field_set(tmp, call_forward, l->call_forward);
02760       ast_copy_string(tmp->context, l->context, sizeof(tmp->context));
02761       ast_copy_string(tmp->exten, l->exten, sizeof(tmp->exten));
02762 
02763       /* Don't use ast_set_callerid() here because it will
02764        * generate a needless NewCallerID event */
02765       tmp->cid.cid_num = ast_strdup(l->cid_num);
02766       tmp->cid.cid_ani = ast_strdup(l->cid_num);
02767       tmp->cid.cid_name = ast_strdup(l->cid_name);
02768 
02769       tmp->priority = 1;
02770       tmp->adsicpe = AST_ADSI_UNAVAILABLE;
02771 
02772       if (sub->rtp)
02773          ast_jb_configure(tmp, &global_jbconf);
02774 
02775       if (state != AST_STATE_DOWN) {
02776          if (ast_pbx_start(tmp)) {
02777             ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
02778             ast_hangup(tmp);
02779             tmp = NULL;
02780          }
02781       }
02782    }
02783    return tmp;
02784 }
02785 
02786 static int skinny_hold(struct skinny_subchannel *sub)
02787 {
02788    struct skinny_line *l = sub->parent;
02789    struct skinny_device *d = l->parent;
02790    struct skinnysession *s = d->session;
02791    struct skinny_req *req;
02792 
02793    /* Channel needs to be put on hold */
02794    if (skinnydebug)
02795       ast_verbose("Putting on Hold(%d)\n", l->instance);
02796 
02797    ast_queue_control_data(sub->owner, AST_CONTROL_HOLD,
02798       S_OR(l->mohsuggest, NULL),
02799       !ast_strlen_zero(l->mohsuggest) ? strlen(l->mohsuggest) + 1 : 0);
02800 
02801    if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
02802       return 0;
02803 
02804    req->data.activatecallplane.lineInstance = htolel(l->instance);
02805    transmit_response(s, req);
02806 
02807    if (!(req = req_alloc(sizeof(struct close_receive_channel_message), CLOSE_RECEIVE_CHANNEL_MESSAGE)))
02808       return 0;
02809 
02810    req->data.closereceivechannel.conferenceId = htolel(0);
02811    req->data.closereceivechannel.partyId = htolel(sub->callid);
02812    transmit_response(s, req);
02813 
02814    if (!(req = req_alloc(sizeof(struct stop_media_transmission_message), STOP_MEDIA_TRANSMISSION_MESSAGE)))
02815       return 0;
02816 
02817    req->data.stopmedia.conferenceId = htolel(0);
02818    req->data.stopmedia.passThruPartyId = htolel(sub->callid);
02819    transmit_response(s, req);
02820 
02821    transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_BLINK);
02822    sub->onhold = 1;
02823    return 1;
02824 }
02825 
02826 static int skinny_unhold(struct skinny_subchannel *sub)
02827 {
02828    struct skinny_line *l = sub->parent;
02829    struct skinny_device *d = l->parent;
02830    struct skinnysession *s = d->session;
02831    struct skinny_req *req;
02832 
02833    /* Channel is on hold, so we will unhold */
02834    if (skinnydebug)
02835       ast_verbose("Taking off Hold(%d)\n", l->instance);
02836 
02837    ast_queue_control(sub->owner, AST_CONTROL_UNHOLD);
02838 
02839    if (!(req = req_alloc(sizeof(struct activate_call_plane_message), ACTIVATE_CALL_PLANE_MESSAGE)))
02840       return 0;
02841 
02842    req->data.activatecallplane.lineInstance = htolel(l->instance);
02843    transmit_response(s, req);
02844 
02845    transmit_connect(s, sub);
02846    transmit_lamp_indication(s, STIMULUS_LINE, l->instance, SKINNY_LAMP_ON);
02847    sub->onhold = 0;
02848    return 1;
02849 }
02850 
02851 static int handle_keep_alive_message(struct skinny_req *req, struct skinnysession *s)
02852 {
02853    if (!(req = req_alloc(0, KEEP_ALIVE_ACK_MESSAGE)))
02854       return -1;
02855 
02856    transmit_response(s, req);
02857    do_housekeeping(s);
02858    return 1;
02859 }
02860 
02861 static int handle_register_message(struct skinny_req *req, struct skinnysession *s)
02862 {
02863    char name[16];
02864    int res;
02865 
02866    memcpy(&name, req->data.reg.name, sizeof(name));
02867 
02868    res = skinny_register(req, s);
02869    if (!res) {
02870       ast_log(LOG_ERROR, "Rejecting Device %s: Device not found\n", name);
02871       if (!(req = req_alloc(sizeof(struct register_rej_message), REGISTER_REJ_MESSAGE)))
02872          return -1;
02873 
02874       snprintf(req->data.regrej.errMsg, sizeof(req->data.regrej.errMsg), "No Authority: %s", name);
02875       transmit_response(s, req);
02876       return 0;
02877    }
02878    if (option_verbose > 2)
02879       ast_verbose(VERBOSE_PREFIX_3 "Device '%s' successfully registered\n", name);
02880 
02881    if (!(req = req_alloc(sizeof(struct register_ack_message), REGISTER_ACK_MESSAGE)))
02882       return -1;
02883 
02884    req->data.regack.res[0] = '0';
02885    req->data.regack.res[1] = '\0';
02886    req->data.regack.keepAlive = htolel(keep_alive);
02887    ast_copy_string(req->data.regack.dateTemplate, date_format, sizeof(req->data.regack.dateTemplate));
02888    req->data.regack.res2[0] = '0';
02889    req->data.regack.res2[1] = '\0';
02890    req->data.regack.secondaryKeepAlive = htolel(keep_alive);
02891    transmit_response(s, req);
02892    if (skinnydebug)
02893       ast_verbose("Requesting capabilities\n");
02894 
02895    if (!(req = req_alloc(0, CAPABILITIES_REQ_MESSAGE)))
02896       return -1;
02897 
02898    transmit_response(s, req);
02899 
02900    return res;
02901 }
02902 
02903 static int handle_ip_port_message(struct skinny_req *req, struct skinnysession *s)
02904 {
02905    /* no response necessary */
02906    return 1;
02907 }
02908 
02909 static int handle_keypad_button_message(struct skinny_req *req, struct skinnysession *s)
02910 {
02911    struct skinny_subchannel *sub = NULL;
02912    struct skinny_line *l;
02913    struct skinny_device *d = s->device;
02914    struct ast_frame f = { 0, };
02915    char dgt;
02916    int digit;
02917    int lineInstance;
02918    int callReference;
02919 
02920    digit = letohl(req->data.keypad.button);
02921    lineInstance = letohl(req->data.keypad.lineInstance);
02922    callReference = letohl(req->data.keypad.callReference);
02923 
02924    if (digit == 14) {
02925       dgt = '*';
02926    } else if (digit == 15) {
02927       dgt = '#';
02928    } else if (digit >= 0 && digit <= 9) {
02929       dgt = '0' + digit;
02930    } else {
02931       /* digit=10-13 (A,B,C,D ?), or
02932        * digit is bad value
02933        *
02934        * probably should not end up here, but set
02935        * value for backward compatibility, and log
02936        * a warning.
02937        */
02938       dgt = '0' + digit;
02939       ast_log(LOG_WARNING, "Unsupported digit %d\n", digit);
02940    }
02941 
02942    f.subclass = dgt;
02943 
02944    f.src = "skinny";
02945 
02946    if (lineInstance && callReference)
02947       sub = find_subchannel_by_instance_reference(d, lineInstance, callReference);
02948    else
02949       sub = find_subchannel_by_instance_reference(d, d->lastlineinstance, d->lastcallreference);
02950 
02951    if (!sub)
02952       return 0;
02953 
02954    l = sub->parent;
02955    if (sub->owner) {
02956       if (sub->owner->_state == 0) {
02957          f.frametype = AST_FRAME_DTMF_BEGIN;
02958          ast_queue_frame(sub->owner, &f);
02959       }
02960       /* XXX MUST queue this frame to all lines in threeway call if threeway call is active */
02961       f.frametype = AST_FRAME_DTMF_END;
02962       ast_queue_frame(sub->owner, &f);
02963       /* XXX This seriously needs to be fixed */
02964       if (sub->next && sub->next->owner) {
02965          if (sub->owner->_state == 0) {
02966             f.frametype = AST_FRAME_DTMF_BEGIN;
02967             ast_queue_frame(sub->next->owner, &f);
02968          }
02969          f.frametype = AST_FRAME_DTMF_END;
02970          ast_queue_frame(sub->next->owner, &f);
02971       }
02972    } else {
02973       if (skinnydebug)
02974          ast_verbose("No owner: %s\n", l->name);
02975    }
02976    return 1;
02977 }
02978 
02979 static int handle_stimulus_message(struct skinny_req *req, struct skinnysession *s)
02980 {
02981    struct skinny_device *d = s->device;
02982    struct skinny_line *l;
02983    struct skinny_subchannel *sub;
02984    /*struct skinny_speeddial *sd;*/
02985    struct ast_channel *c;
02986    pthread_t t;
02987    int event;
02988    int instance;
02989    int unknown1;
02990    /*int res = 0;*/
02991 
02992    event = letohl(req->