2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2005, Digium, Inc.
6 * chan_skinny was developed by Jeremy McNamara & Florian Overkamp
7 * chan_skinny was heavily modified/fixed by North Antara
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Implementation of the Skinny protocol
24 * \author Jeremy McNamara & Florian Overkamp & North Antara
25 * \ingroup channel_drivers
31 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <netinet/tcp.h>
40 #include <sys/ioctl.h>
45 #include <arpa/inet.h>
46 #include <sys/signal.h>
50 #include "asterisk/lock.h"
51 #include "asterisk/channel.h"
52 #include "asterisk/config.h"
53 #include "asterisk/logger.h"
54 #include "asterisk/module.h"
55 #include "asterisk/pbx.h"
56 #include "asterisk/options.h"
57 #include "asterisk/lock.h"
58 #include "asterisk/sched.h"
59 #include "asterisk/io.h"
60 #include "asterisk/rtp.h"
61 #include "asterisk/acl.h"
62 #include "asterisk/callerid.h"
63 #include "asterisk/cli.h"
64 #include "asterisk/say.h"
65 #include "asterisk/cdr.h"
66 #include "asterisk/astdb.h"
67 #include "asterisk/features.h"
68 #include "asterisk/app.h"
69 #include "asterisk/musiconhold.h"
70 #include "asterisk/utils.h"
71 #include "asterisk/dsp.h"
72 #include "asterisk/stringfields.h"
73 #include "asterisk/astobj.h"
74 #include "asterisk/abstract_jb.h"
75 #include "asterisk/threadstorage.h"
77 /*************************************
78 * Skinny/Asterisk Protocol Settings *
79 *************************************/
80 static const char tdesc
[] = "Skinny Client Control Protocol (Skinny)";
81 static const char config
[] = "skinny.conf";
83 static int default_capability
= AST_FORMAT_ULAW
| AST_FORMAT_ALAW
;
84 static struct ast_codec_pref default_prefs
;
87 SKINNY_CODEC_ALAW
= 2,
88 SKINNY_CODEC_ULAW
= 4,
89 SKINNY_CODEC_G723_1
= 9,
90 SKINNY_CODEC_G729A
= 12,
91 SKINNY_CODEC_G726_32
= 82, /* XXX Which packing order does this translate to? */
92 SKINNY_CODEC_H261
= 100,
93 SKINNY_CODEC_H263
= 101
96 #define DEFAULT_SKINNY_PORT 2000
97 #define DEFAULT_SKINNY_BACKLOG 2
98 #define SKINNY_MAX_PACKET 1000
100 static int keep_alive
= 120;
101 static char date_format
[6] = "D-M-Y";
102 static char version_id
[16] = "P002F202";
104 #if __BYTE_ORDER == __LITTLE_ENDIAN
105 #define letohl(x) (x)
106 #define letohs(x) (x)
107 #define htolel(x) (x)
108 #define htoles(x) (x)
110 #if defined(SOLARIS) || defined(__Darwin__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
111 #define __bswap_16(x) \
112 ((((x) & 0xff00) >> 8) | \
113 (((x) & 0x00ff) << 8))
114 #define __bswap_32(x) \
115 ((((x) & 0xff000000) >> 24) | \
116 (((x) & 0x00ff0000) >> 8) | \
117 (((x) & 0x0000ff00) << 8) | \
118 (((x) & 0x000000ff) << 24))
120 #include <bits/byteswap.h>
122 #define letohl(x) __bswap_32(x)
123 #define letohs(x) __bswap_16(x)
124 #define htolel(x) __bswap_32(x)
125 #define htoles(x) __bswap_16(x)
128 /*! Global jitterbuffer configuration - by default, jb is disabled */
129 static struct ast_jb_conf default_jbconf
=
133 .resync_threshold
= -1,
136 static struct ast_jb_conf global_jbconf
;
138 AST_THREADSTORAGE(device2str_threadbuf
, device2str_threadbuf_init
);
139 #define DEVICE2STR_BUFSIZE 15
141 AST_THREADSTORAGE(control2str_threadbuf
, control2str_threadbuf_init
);
142 #define CONTROL2STR_BUFSIZE 100
144 /*********************
145 * Protocol Messages *
146 *********************/
148 #define KEEP_ALIVE_MESSAGE 0x0000
149 /* no additional struct */
151 #define REGISTER_MESSAGE 0x0001
152 struct register_message
{
161 #define IP_PORT_MESSAGE 0x0002
163 #define KEYPAD_BUTTON_MESSAGE 0x0003
164 struct keypad_button_message
{
166 uint32_t lineInstance
;
167 uint32_t callReference
;
171 #define ENBLOC_CALL_MESSAGE 0x0004
172 struct enbloc_call_message
{
173 char calledParty
[24];
176 #define STIMULUS_MESSAGE 0x0005
177 struct stimulus_message
{
179 uint32_t stimulusInstance
;
180 uint32_t callreference
;
183 #define OFFHOOK_MESSAGE 0x0006
184 struct offhook_message
{
189 #define ONHOOK_MESSAGE 0x0007
190 struct onhook_message
{
195 #define CAPABILITIES_RES_MESSAGE 0x0010
196 struct station_capabilities
{
205 #define SKINNY_MAX_CAPABILITIES 18
207 struct capabilities_res_message
{
209 struct station_capabilities caps
[SKINNY_MAX_CAPABILITIES
];
212 #define SPEED_DIAL_STAT_REQ_MESSAGE 0x000A
213 struct speed_dial_stat_req_message
{
214 uint32_t speedDialNumber
;
217 #define LINE_STATE_REQ_MESSAGE 0x000B
218 struct line_state_req_message
{
222 #define TIME_DATE_REQ_MESSAGE 0x000D
223 #define BUTTON_TEMPLATE_REQ_MESSAGE 0x000E
224 #define VERSION_REQ_MESSAGE 0x000F
225 #define SERVER_REQUEST_MESSAGE 0x0012
227 #define ALARM_MESSAGE 0x0020
228 struct alarm_message
{
229 uint32_t alarmSeverity
;
230 char displayMessage
[80];
231 uint32_t alarmParam1
;
232 uint32_t alarmParam2
;
235 #define OPEN_RECEIVE_CHANNEL_ACK_MESSAGE 0x0022
236 struct open_receive_channel_ack_message
{
243 #define SOFT_KEY_SET_REQ_MESSAGE 0x0025
245 #define SOFT_KEY_EVENT_MESSAGE 0x0026
246 struct soft_key_event_message
{
247 uint32_t softKeyEvent
;
249 uint32_t callreference
;
252 #define UNREGISTER_MESSAGE 0x0027
253 #define SOFT_KEY_TEMPLATE_REQ_MESSAGE 0x0028
254 #define HEADSET_STATUS_MESSAGE 0x002B
255 #define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D
257 #define REGISTER_ACK_MESSAGE 0x0081
258 struct register_ack_message
{
260 char dateTemplate
[6];
262 uint32_t secondaryKeepAlive
;
266 #define START_TONE_MESSAGE 0x0082
267 struct start_tone_message
{
274 #define STOP_TONE_MESSAGE 0x0083
275 struct stop_tone_message
{
280 #define SET_RINGER_MESSAGE 0x0085
281 struct set_ringer_message
{
283 uint32_t unknown1
; /* See notes in transmit_ringer_mode */
288 #define SET_LAMP_MESSAGE 0x0086
289 struct set_lamp_message
{
291 uint32_t stimulusInstance
;
292 uint32_t deviceStimulus
;
295 #define SET_SPEAKER_MESSAGE 0x0088
296 struct set_speaker_message
{
300 /* XXX When do we need to use this? */
301 #define SET_MICROPHONE_MESSAGE 0x0089
302 struct set_microphone_message
{
306 #define START_MEDIA_TRANSMISSION_MESSAGE 0x008A
307 struct media_qualifier
{
314 struct start_media_transmission_message
{
315 uint32_t conferenceId
;
316 uint32_t passThruPartyId
;
320 uint32_t payloadType
;
321 struct media_qualifier qualifier
;
325 #define STOP_MEDIA_TRANSMISSION_MESSAGE 0x008B
326 struct stop_media_transmission_message
{
327 uint32_t conferenceId
;
328 uint32_t passThruPartyId
;
332 #define CALL_INFO_MESSAGE 0x008F
333 struct call_info_message
{
334 char callingPartyName
[40];
335 char callingParty
[24];
336 char calledPartyName
[40];
337 char calledParty
[24];
341 char originalCalledPartyName
[40];
342 char originalCalledParty
[24];
343 char lastRedirectingPartyName
[40];
344 char lastRedirectingParty
[24];
345 uint32_t originalCalledPartyRedirectReason
;
346 uint32_t lastRedirectingReason
;
347 char callingPartyVoiceMailbox
[24];
348 char calledPartyVoiceMailbox
[24];
349 char originalCalledPartyVoiceMailbox
[24];
350 char lastRedirectingVoiceMailbox
[24];
354 #define SPEED_DIAL_STAT_RES_MESSAGE 0x0091
355 struct speed_dial_stat_res_message
{
356 uint32_t speedDialNumber
;
357 char speedDialDirNumber
[24];
358 char speedDialDisplayName
[40];
361 #define LINE_STAT_RES_MESSAGE 0x0092
362 struct line_stat_res_message
{
364 char lineDirNumber
[24];
365 char lineDisplayName
[24];
369 #define DEFINETIMEDATE_MESSAGE 0x0094
370 struct definetimedate_message
{
371 uint32_t year
; /* since 1900 */
373 uint32_t dayofweek
; /* monday = 1 */
378 uint32_t milliseconds
;
382 #define BUTTON_TEMPLATE_RES_MESSAGE 0x0097
383 struct button_definition
{
384 uint8_t instanceNumber
;
385 uint8_t buttonDefinition
;
388 struct button_definition_template
{
389 uint8_t buttonDefinition
;
390 /* for now, anything between 0xB0 and 0xCF is custom */
394 #define STIMULUS_REDIAL 0x01
395 #define STIMULUS_SPEEDDIAL 0x02
396 #define STIMULUS_HOLD 0x03
397 #define STIMULUS_TRANSFER 0x04
398 #define STIMULUS_FORWARDALL 0x05
399 #define STIMULUS_FORWARDBUSY 0x06
400 #define STIMULUS_FORWARDNOANSWER 0x07
401 #define STIMULUS_DISPLAY 0x08
402 #define STIMULUS_LINE 0x09
403 #define STIMULUS_VOICEMAIL 0x0F
404 #define STIMULUS_AUTOANSWER 0x11
405 #define STIMULUS_CONFERENCE 0x7D
406 #define STIMULUS_CALLPARK 0x7E
407 #define STIMULUS_CALLPICKUP 0x7F
408 #define STIMULUS_NONE 0xFF
411 #define BT_REDIAL STIMULUS_REDIAL
412 #define BT_SPEEDDIAL STIMULUS_SPEEDDIAL
413 #define BT_HOLD STIMULUS_HOLD
414 #define BT_TRANSFER STIMULUS_TRANSFER
415 #define BT_FORWARDALL STIMULUS_FORWARDALL
416 #define BT_FORWARDBUSY STIMULUS_FORWARDBUSY
417 #define BT_FORWARDNOANSWER STIMULUS_FORWARDNOANSWER
418 #define BT_DISPLAY STIMULUS_DISPLAY
419 #define BT_LINE STIMULUS_LINE
420 #define BT_VOICEMAIL STIMULUS_VOICEMAIL
421 #define BT_AUTOANSWER STIMULUS_AUTOANSWER
422 #define BT_CONFERENCE STIMULUS_CONFERENCE
423 #define BT_CALLPARK STIMULUS_CALLPARK
424 #define BT_CALLPICKUP STIMULUS_CALLPICKUP
427 /* Custom button types - add our own between 0xB0 and 0xCF.
428 This may need to be revised in the future,
429 if stimuluses are ever added in this range. */
430 #define BT_CUST_LINESPEEDDIAL 0xB0 /* line or speeddial */
431 #define BT_CUST_HINT 0xB1 /* pipe dream */
433 struct button_template_res_message
{
434 uint32_t buttonOffset
;
435 uint32_t buttonCount
;
436 uint32_t totalButtonCount
;
437 struct button_definition definition
[42];
440 #define VERSION_RES_MESSAGE 0x0098
441 struct version_res_message
{
445 #define DISPLAYTEXT_MESSAGE 0x0099
446 struct displaytext_message
{
450 #define CLEAR_NOTIFY_MESSAGE 0x0115
451 #define CLEAR_DISPLAY_MESSAGE 0x009A
453 #define CAPABILITIES_REQ_MESSAGE 0x009B
455 #define REGISTER_REJ_MESSAGE 0x009D
456 struct register_rej_message
{
460 #define SERVER_RES_MESSAGE 0x009E
461 struct server_identifier
{
465 struct server_res_message
{
466 struct server_identifier server
[5];
467 uint32_t serverListenPort
[5];
468 uint32_t serverIpAddr
[5];
471 #define RESET_MESSAGE 0x009F
472 struct reset_message
{
476 #define KEEP_ALIVE_ACK_MESSAGE 0x0100
478 #define OPEN_RECEIVE_CHANNEL_MESSAGE 0x0105
479 struct open_receive_channel_message
{
480 uint32_t conferenceId
;
489 #define CLOSE_RECEIVE_CHANNEL_MESSAGE 0x0106
490 struct close_receive_channel_message
{
491 uint32_t conferenceId
;
496 #define SOFT_KEY_TEMPLATE_RES_MESSAGE 0x0108
498 struct soft_key_template_definition
{
499 char softKeyLabel
[16];
500 uint32_t softKeyEvent
;
503 #define KEYDEF_ONHOOK 0
504 #define KEYDEF_CONNECTED 1
505 #define KEYDEF_ONHOLD 2
506 #define KEYDEF_RINGIN 3
507 #define KEYDEF_OFFHOOK 4
508 #define KEYDEF_CONNWITHTRANS 5
509 #define KEYDEF_DADFD 6 /* Digits After Dialing First Digit */
510 #define KEYDEF_CONNWITHCONF 7
511 #define KEYDEF_RINGOUT 8
512 #define KEYDEF_OFFHOOKWITHFEAT 9
513 #define KEYDEF_UNKNOWN 10
515 #define SOFTKEY_NONE 0x00
516 #define SOFTKEY_REDIAL 0x01
517 #define SOFTKEY_NEWCALL 0x02
518 #define SOFTKEY_HOLD 0x03
519 #define SOFTKEY_TRNSFER 0x04
520 #define SOFTKEY_CFWDALL 0x05
521 #define SOFTKEY_CFWDBUSY 0x06
522 #define SOFTKEY_CFWDNOANSWER 0x07
523 #define SOFTKEY_BKSPC 0x08
524 #define SOFTKEY_ENDCALL 0x09
525 #define SOFTKEY_RESUME 0x0A
526 #define SOFTKEY_ANSWER 0x0B
527 #define SOFTKEY_INFO 0x0C
528 #define SOFTKEY_CONFRN 0x0D
529 #define SOFTKEY_PARK 0x0E
530 #define SOFTKEY_JOIN 0x0F
531 #define SOFTKEY_MEETME 0x10
532 #define SOFTKEY_PICKUP 0x11
533 #define SOFTKEY_GPICKUP 0x12
535 struct soft_key_template_definition soft_key_template_default
[] = {
541 { "CFwdBusy", 0x06 },
542 { "CFwdNoAnswer", 0x07 },
556 struct soft_key_definitions
{
558 const uint8_t *defaults
;
562 static const uint8_t soft_key_default_onhook
[] = {
571 static const uint8_t soft_key_default_connected
[] = {
580 static const uint8_t soft_key_default_onhold
[] = {
587 static const uint8_t soft_key_default_ringin
[] = {
593 static const uint8_t soft_key_default_offhook
[] = {
601 static const uint8_t soft_key_default_connwithtrans
[] = {
610 static const uint8_t soft_key_default_dadfd
[] = {
615 static const uint8_t soft_key_default_connwithconf
[] = {
619 static const uint8_t soft_key_default_ringout
[] = {
624 static const uint8_t soft_key_default_offhookwithfeat
[] = {
629 static const uint8_t soft_key_default_unknown
[] = {
633 static const struct soft_key_definitions soft_key_default_definitions
[] = {
634 {KEYDEF_ONHOOK
, soft_key_default_onhook
, sizeof(soft_key_default_onhook
) / sizeof(uint8_t)},
635 {KEYDEF_CONNECTED
, soft_key_default_connected
, sizeof(soft_key_default_connected
) / sizeof(uint8_t)},
636 {KEYDEF_ONHOLD
, soft_key_default_onhold
, sizeof(soft_key_default_onhold
) / sizeof(uint8_t)},
637 {KEYDEF_RINGIN
, soft_key_default_ringin
, sizeof(soft_key_default_ringin
) / sizeof(uint8_t)},
638 {KEYDEF_OFFHOOK
, soft_key_default_offhook
, sizeof(soft_key_default_offhook
) / sizeof(uint8_t)},
639 {KEYDEF_CONNWITHTRANS
, soft_key_default_connwithtrans
, sizeof(soft_key_default_connwithtrans
) / sizeof(uint8_t)},
640 {KEYDEF_DADFD
, soft_key_default_dadfd
, sizeof(soft_key_default_dadfd
) / sizeof(uint8_t)},
641 {KEYDEF_CONNWITHCONF
, soft_key_default_connwithconf
, sizeof(soft_key_default_connwithconf
) / sizeof(uint8_t)},
642 {KEYDEF_RINGOUT
, soft_key_default_ringout
, sizeof(soft_key_default_ringout
) / sizeof(uint8_t)},
643 {KEYDEF_OFFHOOKWITHFEAT
, soft_key_default_offhookwithfeat
, sizeof(soft_key_default_offhookwithfeat
) / sizeof(uint8_t)},
644 {KEYDEF_UNKNOWN
, soft_key_default_unknown
, sizeof(soft_key_default_unknown
) / sizeof(uint8_t)}
647 struct soft_key_template_res_message
{
648 uint32_t softKeyOffset
;
649 uint32_t softKeyCount
;
650 uint32_t totalSoftKeyCount
;
651 struct soft_key_template_definition softKeyTemplateDefinition
[32];
654 #define SOFT_KEY_SET_RES_MESSAGE 0x0109
656 struct soft_key_set_definition
{
657 uint8_t softKeyTemplateIndex
[16];
658 uint16_t softKeyInfoIndex
[16];
661 struct soft_key_set_res_message
{
662 uint32_t softKeySetOffset
;
663 uint32_t softKeySetCount
;
664 uint32_t totalSoftKeySetCount
;
665 struct soft_key_set_definition softKeySetDefinition
[16];
669 #define SELECT_SOFT_KEYS_MESSAGE 0x0110
670 struct select_soft_keys_message
{
673 uint32_t softKeySetIndex
;
674 uint32_t validKeyMask
;
677 #define CALL_STATE_MESSAGE 0x0111
678 struct call_state_message
{
680 uint32_t lineInstance
;
681 uint32_t callReference
;
685 #define DISPLAY_PROMPT_STATUS_MESSAGE 0x0112
686 struct display_prompt_status_message
{
687 uint32_t messageTimeout
;
688 char promptMessage
[32];
689 uint32_t lineInstance
;
690 uint32_t callReference
;
693 #define CLEAR_PROMPT_MESSAGE 0x0113
694 struct clear_prompt_message
{
695 uint32_t lineInstance
;
696 uint32_t callReference
;
699 #define DISPLAY_NOTIFY_MESSAGE 0x0114
700 struct display_notify_message
{
701 uint32_t displayTimeout
;
702 char displayMessage
[100];
705 #define ACTIVATE_CALL_PLANE_MESSAGE 0x0116
706 struct activate_call_plane_message
{
707 uint32_t lineInstance
;
710 #define DIALED_NUMBER_MESSAGE 0x011D
711 struct dialed_number_message
{
712 char dialedNumber
[24];
713 uint32_t lineInstance
;
714 uint32_t callReference
;
718 struct alarm_message alarm
;
719 struct speed_dial_stat_req_message speeddialreq
;
720 struct register_message reg
;
721 struct register_ack_message regack
;
722 struct register_rej_message regrej
;
723 struct capabilities_res_message caps
;
724 struct version_res_message version
;
725 struct button_template_res_message buttontemplate
;
726 struct displaytext_message displaytext
;
727 struct display_prompt_status_message displaypromptstatus
;
728 struct clear_prompt_message clearpromptstatus
;
729 struct definetimedate_message definetimedate
;
730 struct start_tone_message starttone
;
731 struct stop_tone_message stoptone
;
732 struct speed_dial_stat_res_message speeddial
;
733 struct line_state_req_message line
;
734 struct line_stat_res_message linestat
;
735 struct soft_key_set_res_message softkeysets
;
736 struct soft_key_template_res_message softkeytemplate
;
737 struct server_res_message serverres
;
738 struct reset_message reset
;
739 struct set_lamp_message setlamp
;
740 struct set_ringer_message setringer
;
741 struct call_state_message callstate
;
742 struct keypad_button_message keypad
;
743 struct select_soft_keys_message selectsoftkey
;
744 struct activate_call_plane_message activatecallplane
;
745 struct stimulus_message stimulus
;
746 struct offhook_message offhook
;
747 struct onhook_message onhook
;
748 struct set_speaker_message setspeaker
;
749 struct set_microphone_message setmicrophone
;
750 struct call_info_message callinfo
;
751 struct start_media_transmission_message startmedia
;
752 struct stop_media_transmission_message stopmedia
;
753 struct open_receive_channel_message openreceivechannel
;
754 struct open_receive_channel_ack_message openreceivechannelack
;
755 struct close_receive_channel_message closereceivechannel
;
756 struct display_notify_message displaynotify
;
757 struct dialed_number_message dialednumber
;
758 struct soft_key_event_message softkeyeventmessage
;
759 struct enbloc_call_message enbloccallmessage
;
762 /* packet composition */
767 union skinny_data data
;
770 /* XXX This is the combined size of the variables above. (len, res, e)
771 If more are added, this MUST change.
772 (sizeof(skinny_req) - sizeof(skinny_data)) DOES NOT WORK on all systems (amd64?). */
773 int skinny_header_size
= 12;
775 /*****************************
776 * Asterisk specific globals *
777 *****************************/
779 static int skinnydebug
= 0;
781 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
782 static struct sockaddr_in bindaddr
;
783 static char ourhost
[256];
785 static struct in_addr __ourip
;
786 struct ast_hostent ahp
;
788 static int skinnysock
= -1;
789 static pthread_t accept_t
;
790 static char context
[AST_MAX_CONTEXT
] = "default";
791 static char language
[MAX_LANGUAGE
] = "";
792 static char mohinterpret
[MAX_MUSICCLASS
] = "default";
793 static char mohsuggest
[MAX_MUSICCLASS
] = "";
794 static char cid_num
[AST_MAX_EXTENSION
] = "";
795 static char cid_name
[AST_MAX_EXTENSION
] = "";
796 static char linelabel
[AST_MAX_EXTENSION
] ="";
798 static ast_group_t cur_callergroup
= 0;
799 static ast_group_t cur_pickupgroup
= 0;
800 static int immediate
= 0;
801 static int callwaiting
= 0;
802 static int callreturn
= 0;
803 static int threewaycalling
= 0;
804 static int mwiblink
= 0;
805 /* This is for flashhook transfers */
806 static int transfer
= 0;
807 static int cancallforward
= 0;
808 /* static int busycount = 3;*/
809 static char accountcode
[AST_MAX_ACCOUNT_CODE
] = "";
810 static char mailbox
[AST_MAX_EXTENSION
];
811 static int amaflags
= 0;
812 static int callnums
= 1;
814 #define SKINNY_DEVICE_UNKNOWN -1
815 #define SKINNY_DEVICE_NONE 0
816 #define SKINNY_DEVICE_30SPPLUS 1
817 #define SKINNY_DEVICE_12SPPLUS 2
818 #define SKINNY_DEVICE_12SP 3
819 #define SKINNY_DEVICE_12 4
820 #define SKINNY_DEVICE_30VIP 5
821 #define SKINNY_DEVICE_7910 6
822 #define SKINNY_DEVICE_7960 7
823 #define SKINNY_DEVICE_7940 8
824 #define SKINNY_DEVICE_7935 9
825 #define SKINNY_DEVICE_ATA186 12 /* Cisco ATA-186 */
826 #define SKINNY_DEVICE_7941 115
827 #define SKINNY_DEVICE_7971 119
828 #define SKINNY_DEVICE_7914 124 /* Expansion module */
829 #define SKINNY_DEVICE_7985 302
830 #define SKINNY_DEVICE_7911 307
831 #define SKINNY_DEVICE_7961GE 308
832 #define SKINNY_DEVICE_7941GE 309
833 #define SKINNY_DEVICE_7931 348
834 #define SKINNY_DEVICE_7921 365
835 #define SKINNY_DEVICE_7906 369
836 #define SKINNY_DEVICE_7962 404 /* Not found */
837 #define SKINNY_DEVICE_7937 431
838 #define SKINNY_DEVICE_7942 434
839 #define SKINNY_DEVICE_7945 435
840 #define SKINNY_DEVICE_7965 436
841 #define SKINNY_DEVICE_7975 437
842 #define SKINNY_DEVICE_7905 20000
843 #define SKINNY_DEVICE_7920 30002
844 #define SKINNY_DEVICE_7970 30006
845 #define SKINNY_DEVICE_7912 30007
846 #define SKINNY_DEVICE_7902 30008
847 #define SKINNY_DEVICE_CIPC 30016 /* Cisco IP Communicator */
848 #define SKINNY_DEVICE_7961 30018
849 #define SKINNY_DEVICE_7936 30019
850 #define SKINNY_DEVICE_SCCPGATEWAY_AN 30027 /* ??? */
851 #define SKINNY_DEVICE_SCCPGATEWAY_BRI 30028 /* ??? */
853 #define SKINNY_SPEAKERON 1
854 #define SKINNY_SPEAKEROFF 2
856 #define SKINNY_MICON 1
857 #define SKINNY_MICOFF 2
859 #define SKINNY_OFFHOOK 1
860 #define SKINNY_ONHOOK 2
861 #define SKINNY_RINGOUT 3
862 #define SKINNY_RINGIN 4
863 #define SKINNY_CONNECTED 5
864 #define SKINNY_BUSY 6
865 #define SKINNY_CONGESTION 7
866 #define SKINNY_HOLD 8
867 #define SKINNY_CALLWAIT 9
868 #define SKINNY_TRANSFER 10
869 #define SKINNY_PARK 11
870 #define SKINNY_PROGRESS 12
871 #define SKINNY_INVALID 14
873 #define SKINNY_SILENCE 0x00
874 #define SKINNY_DIALTONE 0x21
875 #define SKINNY_BUSYTONE 0x23
876 #define SKINNY_ALERT 0x24
877 #define SKINNY_REORDER 0x25
878 #define SKINNY_CALLWAITTONE 0x2D
879 #define SKINNY_NOTONE 0x7F
881 #define SKINNY_LAMP_OFF 1
882 #define SKINNY_LAMP_ON 2
883 #define SKINNY_LAMP_WINK 3
884 #define SKINNY_LAMP_FLASH 4
885 #define SKINNY_LAMP_BLINK 5
887 #define SKINNY_RING_OFF 1
888 #define SKINNY_RING_INSIDE 2
889 #define SKINNY_RING_OUTSIDE 3
890 #define SKINNY_RING_FEATURE 4
895 /* Skinny rtp stream modes. Do we really need this? */
896 #define SKINNY_CX_SENDONLY 0
897 #define SKINNY_CX_RECVONLY 1
898 #define SKINNY_CX_SENDRECV 2
899 #define SKINNY_CX_CONF 3
900 #define SKINNY_CX_CONFERENCE 3
901 #define SKINNY_CX_MUTE 4
902 #define SKINNY_CX_INACTIVE 4
905 static char *skinny_cxmodes
[] = {
914 /* driver scheduler */
915 static struct sched_context
*sched
= NULL
;
916 static struct io_context
*io
;
918 /* Protect the monitoring thread, so only one process can kill or start it, and not
919 when it's doing something critical. */
920 AST_MUTEX_DEFINE_STATIC(monlock
);
921 /* Protect the network socket */
922 AST_MUTEX_DEFINE_STATIC(netlock
);
923 /* Protect the session list */
924 AST_MUTEX_DEFINE_STATIC(sessionlock
);
925 /* Protect the device list */
926 AST_MUTEX_DEFINE_STATIC(devicelock
);
928 /* Protect the paging device list */
929 AST_MUTEX_DEFINE_STATIC(pagingdevicelock
);
932 /* This is the thread for the monitor which checks for input on the channels
933 which are not currently in use. */
934 static pthread_t monitor_thread
= AST_PTHREADT_NULL
;
936 /* Wait up to 16 seconds for first digit */
937 static int firstdigittimeout
= 16000;
939 /* How long to wait for following digits */
940 static int gendigittimeout
= 8000;
942 /* How long to wait for an extra digit, if there is an ambiguous match */
943 static int matchdigittimeout
= 3000;
945 struct skinny_subchannel
{
947 struct ast_channel
*owner
;
949 struct ast_rtp
*vrtp
;
951 /* time_t lastouttime; */ /* Unused */
955 /* int lastout; */ /* Unused */
961 struct skinny_subchannel
*next
;
962 struct skinny_line
*parent
;
968 char label
[24]; /* Label that shows next to the line buttons */
969 char accountcode
[AST_MAX_ACCOUNT_CODE
];
970 char exten
[AST_MAX_EXTENSION
]; /* Extension where to start */
971 char context
[AST_MAX_CONTEXT
];
972 char language
[MAX_LANGUAGE
];
973 char cid_num
[AST_MAX_EXTENSION
]; /* Caller*ID */
974 char cid_name
[AST_MAX_EXTENSION
]; /* Caller*ID */
975 char lastcallerid
[AST_MAX_EXTENSION
]; /* Last Caller*ID */
976 char call_forward
[AST_MAX_EXTENSION
];
977 char mailbox
[AST_MAX_EXTENSION
];
978 char mohinterpret
[MAX_MUSICCLASS
];
979 char mohsuggest
[MAX_MUSICCLASS
];
980 char lastnumberdialed
[AST_MAX_EXTENSION
]; /* Last number that was dialed - used for redial */
981 int curtone
; /* Current tone being played */
982 ast_group_t callgroup
;
983 ast_group_t pickupgroup
;
990 int dnd
; /* How does this affect callwait? Do we just deny a skinny_request if we're dnd? */
999 int nonCodecCapability
;
1001 int msgstate
; /* voicemail message state */
1006 struct ast_codec_pref prefs
;
1007 struct skinny_subchannel
*sub
;
1008 struct skinny_line
*next
;
1009 struct skinny_device
*parent
;
1012 struct skinny_speeddial
{
1015 char exten
[AST_MAX_EXTENSION
];
1018 struct skinny_speeddial
*next
;
1019 struct skinny_device
*parent
;
1022 struct skinny_addon
{
1026 struct skinny_addon
*next
;
1027 struct skinny_device
*parent
;
1030 static struct skinny_device
{
1031 /* A device containing one or more lines */
1034 char version_id
[16];
1037 int lastlineinstance
;
1038 int lastcallreference
;
1041 char exten
[AST_MAX_EXTENSION
];
1042 struct sockaddr_in addr
;
1043 struct in_addr ourip
;
1044 struct skinny_line
*lines
;
1045 struct skinny_speeddial
*speeddials
;
1046 struct skinny_addon
*addons
;
1047 struct ast_codec_pref prefs
;
1049 struct skinnysession
*session
;
1050 struct skinny_device
*next
;
1053 struct skinny_paging_device
{
1056 struct skinny_device
** devices
;
1057 struct skinny_paging_device
*next
;
1060 static struct skinnysession
{
1063 struct sockaddr_in sin
;
1065 char inbuf
[SKINNY_MAX_PACKET
];
1066 char outbuf
[SKINNY_MAX_PACKET
];
1067 struct skinny_device
*device
;
1068 struct skinnysession
*next
;
1071 static struct ast_channel
*skinny_request(const char *type
, int format
, void *data
, int *cause
);
1072 static int skinny_call(struct ast_channel
*ast
, char *dest
, int timeout
);
1073 static int skinny_hangup(struct ast_channel
*ast
);
1074 static int skinny_answer(struct ast_channel
*ast
);
1075 static struct ast_frame
*skinny_read(struct ast_channel
*ast
);
1076 static int skinny_write(struct ast_channel
*ast
, struct ast_frame
*frame
);
1077 static int skinny_indicate(struct ast_channel
*ast
, int ind
, const void *data
, size_t datalen
);
1078 static int skinny_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
);
1079 static int skinny_senddigit_begin(struct ast_channel
*ast
, char digit
);
1080 static int skinny_senddigit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
);
1081 static int handle_time_date_req_message(struct skinny_req
*req
, struct skinnysession
*s
);
1083 static const struct ast_channel_tech skinny_tech
= {
1085 .description
= tdesc
,
1086 .capabilities
= ((AST_FORMAT_MAX_AUDIO
<< 1) - 1),
1087 .properties
= AST_CHAN_TP_WANTSJITTER
| AST_CHAN_TP_CREATESJITTER
,
1088 .requester
= skinny_request
,
1089 .call
= skinny_call
,
1090 .hangup
= skinny_hangup
,
1091 .answer
= skinny_answer
,
1092 .read
= skinny_read
,
1093 .write
= skinny_write
,
1094 .indicate
= skinny_indicate
,
1095 .fixup
= skinny_fixup
,
1096 .send_digit_begin
= skinny_senddigit_begin
,
1097 .send_digit_end
= skinny_senddigit_end
,
1098 /* .bridge = ast_rtp_bridge, */
1101 static void *get_button_template(struct skinnysession
*s
, struct button_definition_template
*btn
)
1103 struct skinny_device
*d
= s
->device
;
1104 struct skinny_addon
*a
= d
->addons
;
1108 case SKINNY_DEVICE_30SPPLUS
:
1109 case SKINNY_DEVICE_30VIP
:
1110 /* 13 rows, 2 columns */
1111 for (i
= 0; i
< 4; i
++)
1112 (btn
++)->buttonDefinition
= BT_LINE
;
1113 (btn
++)->buttonDefinition
= BT_REDIAL
;
1114 (btn
++)->buttonDefinition
= BT_VOICEMAIL
;
1115 (btn
++)->buttonDefinition
= BT_CALLPARK
;
1116 (btn
++)->buttonDefinition
= BT_FORWARDALL
;
1117 (btn
++)->buttonDefinition
= BT_CONFERENCE
;
1118 for (i
= 0; i
< 4; i
++)
1119 (btn
++)->buttonDefinition
= BT_NONE
;
1120 for (i
= 0; i
< 13; i
++)
1121 (btn
++)->buttonDefinition
= BT_SPEEDDIAL
;
1124 case SKINNY_DEVICE_12SPPLUS
:
1125 case SKINNY_DEVICE_12SP
:
1126 case SKINNY_DEVICE_12
:
1127 /* 6 rows, 2 columns */
1128 for (i
= 0; i
< 2; i
++)
1129 (btn
++)->buttonDefinition
= BT_LINE
;
1130 (btn
++)->buttonDefinition
= BT_REDIAL
;
1131 for (i
= 0; i
< 3; i
++)
1132 (btn
++)->buttonDefinition
= BT_SPEEDDIAL
;
1133 (btn
++)->buttonDefinition
= BT_HOLD
;
1134 (btn
++)->buttonDefinition
= BT_TRANSFER
;
1135 (btn
++)->buttonDefinition
= BT_FORWARDALL
;
1136 (btn
++)->buttonDefinition
= BT_CALLPARK
;
1137 (btn
++)->buttonDefinition
= BT_VOICEMAIL
;
1138 (btn
++)->buttonDefinition
= BT_CONFERENCE
;
1140 case SKINNY_DEVICE_7910
:
1141 (btn
++)->buttonDefinition
= BT_LINE
;
1142 (btn
++)->buttonDefinition
= BT_HOLD
;
1143 (btn
++)->buttonDefinition
= BT_TRANSFER
;
1144 (btn
++)->buttonDefinition
= BT_DISPLAY
;
1145 (btn
++)->buttonDefinition
= BT_VOICEMAIL
;
1146 (btn
++)->buttonDefinition
= BT_CONFERENCE
;
1147 (btn
++)->buttonDefinition
= BT_FORWARDALL
;
1148 for (i
= 0; i
< 2; i
++)
1149 (btn
++)->buttonDefinition
= BT_SPEEDDIAL
;
1150 (btn
++)->buttonDefinition
= BT_REDIAL
;
1152 case SKINNY_DEVICE_7960
:
1153 case SKINNY_DEVICE_7961
:
1154 case SKINNY_DEVICE_7961GE
:
1155 case SKINNY_DEVICE_7962
:
1156 case SKINNY_DEVICE_7965
:
1157 for (i
= 0; i
< 6; i
++)
1158 (btn
++)->buttonDefinition
= BT_CUST_LINESPEEDDIAL
;
1160 case SKINNY_DEVICE_7940
:
1161 case SKINNY_DEVICE_7941
:
1162 case SKINNY_DEVICE_7941GE
:
1163 case SKINNY_DEVICE_7942
:
1164 case SKINNY_DEVICE_7945
:
1165 for (i
= 0; i
< 2; i
++)
1166 (btn
++)->buttonDefinition
= BT_CUST_LINESPEEDDIAL
;
1168 case SKINNY_DEVICE_7935
:
1169 case SKINNY_DEVICE_7936
:
1170 for (i
= 0; i
< 2; i
++)
1171 (btn
++)->buttonDefinition
= BT_LINE
;
1173 case SKINNY_DEVICE_ATA186
:
1174 (btn
++)->buttonDefinition
= BT_LINE
;
1176 case SKINNY_DEVICE_7970
:
1177 case SKINNY_DEVICE_7971
:
1178 case SKINNY_DEVICE_7975
:
1179 case SKINNY_DEVICE_CIPC
:
1180 for (i
= 0; i
< 8; i
++)
1181 (btn
++)->buttonDefinition
= BT_CUST_LINESPEEDDIAL
;
1183 case SKINNY_DEVICE_7985
:
1184 /* XXX I have no idea what the buttons look like on these. */
1185 ast_log(LOG_WARNING
, "Unsupported device type '%d (7985)' found.\n", d
->type
);
1187 case SKINNY_DEVICE_7912
:
1188 case SKINNY_DEVICE_7911
:
1189 case SKINNY_DEVICE_7905
:
1190 (btn
++)->buttonDefinition
= BT_LINE
;
1191 (btn
++)->buttonDefinition
= BT_HOLD
;
1193 case SKINNY_DEVICE_7920
:
1194 /* XXX I don't know if this is right. */
1195 for (i
= 0; i
< 4; i
++)
1196 (btn
++)->buttonDefinition
= BT_CUST_LINESPEEDDIAL
;
1198 case SKINNY_DEVICE_7921
:
1199 for (i
= 0; i
< 6; i
++)
1200 (btn
++)->buttonDefinition
= BT_CUST_LINESPEEDDIAL
;
1202 case SKINNY_DEVICE_7902
:
1203 ast_log(LOG_WARNING
, "Unsupported device type '%d (7902)' found.\n", d
->type
);
1205 case SKINNY_DEVICE_7906
:
1206 ast_log(LOG_WARNING
, "Unsupported device type '%d (7906)' found.\n", d
->type
);
1208 case SKINNY_DEVICE_7931
:
1209 ast_log(LOG_WARNING
, "Unsupported device type '%d (7931)' found.\n", d
->type
);
1211 case SKINNY_DEVICE_7937
:
1212 ast_log(LOG_WARNING
, "Unsupported device type '%d (7937)' found.\n", d
->type
);
1214 case SKINNY_DEVICE_7914
:
1215 ast_log(LOG_WARNING
, "Unsupported device type '%d (7914)' found. Expansion module registered by itself?\n", d
->type
);
1217 case SKINNY_DEVICE_SCCPGATEWAY_AN
:
1218 case SKINNY_DEVICE_SCCPGATEWAY_BRI
:
1219 ast_log(LOG_WARNING
, "Unsupported device type '%d (SCCP gateway)' found.\n", d
->type
);
1222 ast_log(LOG_WARNING
, "Unknown device type '%d' found.\n", d
->type
);
1226 for (a
= d
->addons
; a
; a
= a
->next
) {
1227 if (!strcasecmp(a
->type
, "7914")) {
1228 for (i
= 0; i
< 14; i
++)
1229 (btn
++)->buttonDefinition
= BT_CUST_LINESPEEDDIAL
;
1231 ast_log(LOG_WARNING
, "Unknown addon type '%s' found. Skipping.\n", a
->type
);
1238 static struct skinny_req
*req_alloc(size_t size
, int response_message
)
1240 struct skinny_req
*req
;
1242 if (!(req
= ast_calloc(1, skinny_header_size
+ size
+ 4)))
1245 req
->len
= htolel(size
+4);
1246 req
->e
= htolel(response_message
);
1251 static struct skinny_line
*find_line_by_instance(struct skinny_device
*d
, int instance
)
1253 struct skinny_line
*l
;
1258 for (l
= d
->lines
; l
; l
= l
->next
) {
1259 if (l
->instance
== instance
)
1264 ast_log(LOG_WARNING
, "Could not find line with instance '%d' on device '%s'\n", instance
, d
->name
);
1269 static struct skinny_line
*find_line_by_name(const char *dest
)
1271 struct skinny_line
*l
;
1272 struct skinny_device
*d
;
1277 ast_copy_string(line
, dest
, sizeof(line
));
1278 at
= strchr(line
, '@');
1280 ast_log(LOG_NOTICE
, "Device '%s' has no @ (at) sign!\n", dest
);
1285 ast_mutex_lock(&devicelock
);
1286 for (d
= devices
; d
; d
= d
->next
) {
1287 if (!strcasecmp(d
->name
, device
)) {
1289 ast_verbose("Found device: %s\n", d
->name
);
1290 /* Found the device */
1291 for (l
= d
->lines
; l
; l
= l
->next
) {
1292 /* Search for the right line */
1293 if (!strcasecmp(l
->name
, line
)) {
1294 ast_mutex_unlock(&devicelock
);
1300 /* Device not found */
1301 ast_mutex_unlock(&devicelock
);
1305 /* It's quicker/easier to find the subchannel when we know the instance number too */
1306 static struct skinny_subchannel
*find_subchannel_by_instance_reference(struct skinny_device
*d
, int instance
, int reference
)
1308 struct skinny_line
*l
= find_line_by_instance(d
, instance
);
1309 struct skinny_subchannel
*sub
;
1318 for (sub
= l
->sub
; sub
; sub
= sub
->next
) {
1319 if (sub
->callid
== reference
)
1325 ast_log(LOG_WARNING
, "Could not find subchannel with reference '%d' on '%s'\n", reference
, d
->name
);
1330 /* Find the subchannel when we only have the callid - this shouldn't happen often */
1331 static struct skinny_subchannel
*find_subchannel_by_reference(struct skinny_device
*d
, int reference
)
1333 struct skinny_line
*l
;
1334 struct skinny_subchannel
*sub
= NULL
;
1336 for (l
= d
->lines
; l
; l
= l
->next
) {
1337 for (sub
= l
->sub
; sub
; sub
= sub
->next
) {
1338 if (sub
->callid
== reference
)
1346 ast_log(LOG_WARNING
, "Could not find any lines that contained a subchannel with reference '%d' on device '%s'\n", reference
, d
->name
);
1349 ast_log(LOG_WARNING
, "Could not find subchannel with reference '%d' on '%s@%s'\n", reference
, l
->name
, d
->name
);
1355 static struct skinny_speeddial
*find_speeddial_by_instance(struct skinny_device
*d
, int instance
)
1357 struct skinny_speeddial
*sd
;
1359 for (sd
= d
->speeddials
; sd
; sd
= sd
->next
) {
1360 if (sd
->instance
== instance
)
1365 ast_log(LOG_WARNING
, "Could not find speeddial with instance '%d' on device '%s'\n", instance
, d
->name
);
1370 static int codec_skinny2ast(enum skinny_codecs skinnycodec
)
1372 switch (skinnycodec
) {
1373 case SKINNY_CODEC_ALAW
:
1374 return AST_FORMAT_ALAW
;
1375 case SKINNY_CODEC_ULAW
:
1376 return AST_FORMAT_ULAW
;
1377 case SKINNY_CODEC_G723_1
:
1378 return AST_FORMAT_G723_1
;
1379 case SKINNY_CODEC_G729A
:
1380 return AST_FORMAT_G729A
;
1381 case SKINNY_CODEC_G726_32
:
1382 return AST_FORMAT_G726_AAL2
; /* XXX Is this right? */
1383 case SKINNY_CODEC_H261
:
1384 return AST_FORMAT_H261
;
1385 case SKINNY_CODEC_H263
:
1386 return AST_FORMAT_H263
;
1392 static int codec_ast2skinny(int astcodec
)
1395 case AST_FORMAT_ALAW
:
1396 return SKINNY_CODEC_ALAW
;
1397 case AST_FORMAT_ULAW
:
1398 return SKINNY_CODEC_ULAW
;
1399 case AST_FORMAT_G723_1
:
1400 return SKINNY_CODEC_G723_1
;
1401 case AST_FORMAT_G729A
:
1402 return SKINNY_CODEC_G729A
;
1403 case AST_FORMAT_G726_AAL2
: /* XXX Is this right? */
1404 return SKINNY_CODEC_G726_32
;
1405 case AST_FORMAT_H261
:
1406 return SKINNY_CODEC_H261
;
1407 case AST_FORMAT_H263
:
1408 return SKINNY_CODEC_H263
;
1415 static int skinny_register(struct skinny_req
*req
, struct skinnysession
*s
)
1417 struct skinny_device
*d
;
1418 struct sockaddr_in sin
;
1421 ast_mutex_lock(&devicelock
);
1422 for (d
= devices
; d
; d
= d
->next
) {
1423 if (!strcasecmp(req
->data
.reg
.name
, d
->id
)
1424 && ast_apply_ha(d
->ha
, &(s
->sin
))) {
1426 d
->type
= letohl(req
->data
.reg
.type
);
1427 if (ast_strlen_zero(d
->version_id
)) {
1428 ast_copy_string(d
->version_id
, version_id
, sizeof(d
->version_id
));
1434 if (getsockname(s
->fd
, (struct sockaddr
*)&sin
, &slen
)) {
1435 ast_log(LOG_WARNING
, "Cannot get socket name\n");
1436 sin
.sin_addr
= __ourip
;
1438 d
->ourip
= sin
.sin_addr
;
1442 ast_mutex_unlock(&devicelock
);
1449 static int skinny_unregister(struct skinny_req
*req
, struct skinnysession
*s
)
1451 struct skinny_device
*d
;
1460 return -1; /* main loop will destroy the session */
1463 static int transmit_response(struct skinnysession
*s
, struct skinny_req
*req
)
1468 ast_log(LOG_WARNING
, "Asked to transmit to a non-existant session!\n");
1472 ast_mutex_lock(&s
->lock
);
1475 ast_log(LOG_VERBOSE
, "writing packet type %04X (%d bytes) to socket %d\n", letohl(req
->e
), letohl(req
->len
)+8, s
->fd
);
1477 if (letohl(req
->len
> SKINNY_MAX_PACKET
) || letohl(req
->len
< 0)) {
1478 ast_log(LOG_WARNING
, "transmit_response: the length of the request is out of bounds\n");
1479 ast_mutex_unlock(&s
->lock
);
1483 memset(s
->outbuf
,0,sizeof(s
->outbuf
));
1484 memcpy(s
->outbuf
, req
, skinny_header_size
);
1485 memcpy(s
->outbuf
+skinny_header_size
, &req
->data
, letohl(req
->len
));
1487 res
= write(s
->fd
, s
->outbuf
, letohl(req
->len
)+8);
1489 if (res
!= letohl(req
->len
)+8) {
1490 ast_log(LOG_WARNING
, "Transmit: write only sent %d out of %d bytes: %s\n", res
, letohl(req
->len
)+8, strerror(errno
));
1493 ast_log(LOG_WARNING
, "Transmit: Skinny Client was lost, unregistering\n");
1494 skinny_unregister(NULL
, s
);
1499 ast_mutex_unlock(&s
->lock
);
1503 static void transmit_speaker_mode(struct skinnysession
*s
, int mode
)
1505 struct skinny_req
*req
;
1507 if (!(req
= req_alloc(sizeof(struct set_speaker_message
), SET_SPEAKER_MESSAGE
)))
1510 req
->data
.setspeaker
.mode
= htolel(mode
);
1511 transmit_response(s
, req
);
1514 static void transmit_microphone_mode(struct skinnysession *s, int mode)
1516 struct skinny_req *req;
1518 if (!(req = req_alloc(sizeof(struct set_microphone_message), SET_MICROPHONE_MESSAGE)))
1521 req->data.setmicrophone.mode = htolel(mode);
1522 transmit_response(s, req);
1526 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
)
1528 struct skinny_req
*req
;
1530 if (!(req
= req_alloc(sizeof(struct call_info_message
), CALL_INFO_MESSAGE
)))
1534 ast_verbose("Setting Callinfo to %s(%s) from %s(%s) on %s(%d)\n", fromname
, fromnum
, toname
, tonum
, s
->device
->name
, instance
);
1537 ast_copy_string(req
->data
.callinfo
.callingPartyName
, fromname
, sizeof(req
->data
.callinfo
.callingPartyName
));
1540 ast_copy_string(req
->data
.callinfo
.callingParty
, fromnum
, sizeof(req
->data
.callinfo
.callingParty
));
1543 ast_copy_string(req
->data
.callinfo
.calledPartyName
, toname
, sizeof(req
->data
.callinfo
.calledPartyName
));
1546 ast_copy_string(req
->data
.callinfo
.calledParty
, tonum
, sizeof(req
->data
.callinfo
.calledParty
));
1548 req
->data
.callinfo
.instance
= htolel(instance
);
1549 req
->data
.callinfo
.reference
= htolel(callid
);
1550 req
->data
.callinfo
.type
= htolel(calltype
);
1551 transmit_response(s
, req
);
1554 static void transmit_connect(struct skinnysession
*s
, struct skinny_subchannel
*sub
)
1556 struct skinny_req
*req
;
1557 struct skinny_line
*l
= sub
->parent
;
1558 struct ast_format_list fmt
;
1560 if (!(req
= req_alloc(sizeof(struct open_receive_channel_message
), OPEN_RECEIVE_CHANNEL_MESSAGE
)))
1563 fmt
= ast_codec_pref_getsize(&l
->prefs
, ast_best_codec(l
->capability
));
1565 req
->data
.openreceivechannel
.conferenceId
= htolel(sub
->callid
);
1566 req
->data
.openreceivechannel
.partyId
= htolel(sub
->callid
);
1567 req
->data
.openreceivechannel
.packets
= htolel(fmt
.cur_ms
);
1568 req
->data
.openreceivechannel
.capability
= htolel(codec_ast2skinny(fmt
.bits
));
1569 req
->data
.openreceivechannel
.echo
= htolel(0);
1570 req
->data
.openreceivechannel
.bitrate
= htolel(0);
1571 transmit_response(s
, req
);
1574 static void transmit_tone(struct skinnysession
*s
, int tone
, int instance
, int reference
)
1576 struct skinny_req
*req
;
1578 if (tone
== SKINNY_NOTONE
) {
1579 /* This is bad, mmm'kay? */
1584 if (!(req
= req_alloc(sizeof(struct start_tone_message
), START_TONE_MESSAGE
)))
1586 req
->data
.starttone
.tone
= htolel(tone
);
1587 req
->data
.starttone
.instance
= htolel(instance
);
1588 req
->data
.starttone
.reference
= htolel(reference
);
1590 if (!(req
= req_alloc(sizeof(struct stop_tone_message
), STOP_TONE_MESSAGE
)))
1592 req
->data
.stoptone
.instance
= htolel(instance
);
1593 req
->data
.stoptone
.reference
= htolel(reference
);
1597 req
->data
.starttone
.tone
= htolel(tone
);
1599 transmit_response(s
, req
);
1602 static void transmit_selectsoftkeys(struct skinnysession
*s
, int instance
, int callid
, int softkey
)
1604 struct skinny_req
*req
;
1606 if (!(req
= req_alloc(sizeof(struct select_soft_keys_message
), SELECT_SOFT_KEYS_MESSAGE
)))
1609 req
->data
.selectsoftkey
.instance
= htolel(instance
);
1610 req
->data
.selectsoftkey
.reference
= htolel(callid
);
1611 req
->data
.selectsoftkey
.softKeySetIndex
= htolel(softkey
);
1612 req
->data
.selectsoftkey
.validKeyMask
= htolel(0xFFFFFFFF);
1613 transmit_response(s
, req
);
1616 static void transmit_lamp_indication(struct skinnysession
*s
, int stimulus
, int instance
, int indication
)
1618 struct skinny_req
*req
;
1620 if (!(req
= req_alloc(sizeof(struct set_lamp_message
), SET_LAMP_MESSAGE
)))
1623 req
->data
.setlamp
.stimulus
= htolel(stimulus
);
1624 req
->data
.setlamp
.stimulusInstance
= htolel(instance
);
1625 req
->data
.setlamp
.deviceStimulus
= htolel(indication
);
1626 transmit_response(s
, req
);
1629 static void transmit_ringer_mode(struct skinnysession
*s
, int mode
)
1631 struct skinny_req
*req
;
1634 ast_verbose("Setting ringer mode to '%d'.\n", mode
);
1636 if (!(req
= req_alloc(sizeof(struct set_ringer_message
), SET_RINGER_MESSAGE
)))
1639 req
->data
.setringer
.ringerMode
= htolel(mode
);
1640 /* XXX okay, I don't quite know what this is, but here's what happens (on a 7960).
1641 Note: The phone will always show as ringing on the display.
1643 1: phone will audibly ring over and over
1644 2: phone will audibly ring only once
1645 any other value, will NOT cause the phone to audibly ring
1647 req
->data
.setringer
.unknown1
= htolel(1);
1648 /* XXX the value here doesn't seem to change anything. Must be higher than 0.
1649 Perhaps a packet capture can shed some light on this. */
1650 req
->data
.setringer
.unknown2
= htolel(1);
1651 transmit_response(s
, req
);
1654 static void transmit_displaymessage(struct skinnysession
*s
, const char *text
, int instance
, int reference
)
1656 struct skinny_req
*req
;
1659 if (!(req
= req_alloc(0, CLEAR_DISPLAY_MESSAGE
)))
1662 req
->data
.clearpromptstatus
.lineInstance
= instance
;
1663 req
->data
.clearpromptstatus
.callReference
= reference
;
1666 ast_verbose("Clearing Display\n");
1668 if (!(req
= req_alloc(sizeof(struct displaytext_message
), DISPLAYTEXT_MESSAGE
)))
1671 ast_copy_string(req
->data
.displaytext
.text
, text
, sizeof(req
->data
.displaytext
.text
));
1673 ast_verbose("Displaying message '%s'\n", req
->data
.displaytext
.text
);
1676 transmit_response(s
, req
);
1679 static void transmit_displaynotify(struct skinnysession
*s
, const char *text
, int t
)
1681 struct skinny_req
*req
;
1683 if (!(req
= req_alloc(sizeof(struct display_notify_message
), DISPLAY_NOTIFY_MESSAGE
)))
1686 ast_copy_string(req
->data
.displaynotify
.displayMessage
, text
, sizeof(req
->data
.displaynotify
.displayMessage
));
1687 req
->data
.displaynotify
.displayTimeout
= htolel(t
);
1690 ast_verbose("Displaying notify '%s'\n", text
);
1692 transmit_response(s
, req
);
1695 static void transmit_displaypromptstatus(struct skinnysession
*s
, const char *text
, int t
, int instance
, int callid
)
1697 struct skinny_req
*req
;
1700 if (!(req
= req_alloc(sizeof(struct clear_prompt_message
), CLEAR_PROMPT_MESSAGE
)))
1703 req
->data
.clearpromptstatus
.lineInstance
= htolel(instance
);
1704 req
->data
.clearpromptstatus
.callReference
= htolel(callid
);
1707 ast_verbose("Clearing Prompt\n");
1709 if (!(req
= req_alloc(sizeof(struct display_prompt_status_message
), DISPLAY_PROMPT_STATUS_MESSAGE
)))
1712 ast_copy_string(req
->data
.displaypromptstatus
.promptMessage
, text
, sizeof(req
->data
.displaypromptstatus
.promptMessage
));
1713 req
->data
.displaypromptstatus
.messageTimeout
= htolel(t
);
1714 req
->data
.displaypromptstatus
.lineInstance
= htolel(instance
);
1715 req
->data
.displaypromptstatus
.callReference
= htolel(callid
);
1718 ast_verbose("Displaying Prompt Status '%s'\n", text
);
1721 transmit_response(s
, req
);
1724 static void transmit_dialednumber(struct skinnysession
*s
, const char *text
, int instance
, int callid
)
1726 struct skinny_req
*req
;
1728 if (!(req
= req_alloc(sizeof(struct dialed_number_message
), DIALED_NUMBER_MESSAGE
)))
1731 ast_copy_string(req
->data
.dialednumber
.dialedNumber
, text
, sizeof(req
->data
.dialednumber
.dialedNumber
));
1732 req
->data
.dialednumber
.lineInstance
= htolel(instance
);
1733 req
->data
.dialednumber
.callReference
= htolel(callid
);
1735 transmit_response(s
, req
);
1738 static void transmit_callstate(struct skinnysession
*s
, int instance
, int state
, unsigned callid
)
1740 struct skinny_req
*req
;
1742 if (state
== SKINNY_ONHOOK
) {
1743 if (!(req
= req_alloc(sizeof(struct close_receive_channel_message
), CLOSE_RECEIVE_CHANNEL_MESSAGE
)))
1746 req
->data
.closereceivechannel
.conferenceId
= htolel(callid
);
1747 req
->data
.closereceivechannel
.partyId
= htolel(callid
);
1748 transmit_response(s
, req
);
1750 if (!(req
= req_alloc(sizeof(struct stop_media_transmission_message
), STOP_MEDIA_TRANSMISSION_MESSAGE
)))
1753 req
->data
.stopmedia
.conferenceId
= htolel(callid
);
1754 req
->data
.stopmedia
.passThruPartyId
= htolel(callid
);
1755 transmit_response(s
, req
);
1757 transmit_speaker_mode(s
, SKINNY_SPEAKEROFF
);
1759 transmit_displaypromptstatus(s
, NULL
, 0, instance
, callid
);
1762 if (!(req
= req_alloc(sizeof(struct call_state_message
), CALL_STATE_MESSAGE
)))
1765 req
->data
.callstate
.callState
= htolel(state
);
1766 req
->data
.callstate
.lineInstance
= htolel(instance
);
1767 req
->data
.callstate
.callReference
= htolel(callid
);
1768 transmit_response(s
, req
);
1770 if (state
== SKINNY_ONHOOK
) {
1771 transmit_selectsoftkeys(s
, 0, 0, KEYDEF_ONHOOK
);
1774 if (state
== SKINNY_OFFHOOK
|| state
== SKINNY_ONHOOK
) {
1775 if (!(req
= req_alloc(sizeof(struct activate_call_plane_message
), ACTIVATE_CALL_PLANE_MESSAGE
)))
1778 req
->data
.activatecallplane
.lineInstance
= htolel(instance
);
1779 transmit_response(s
, req
);
1784 static int has_voicemail(struct skinny_line *l)
1786 return ast_app_has_voicemail(l->mailbox, NULL);
1790 static void do_housekeeping(struct skinnysession
*s
)
1795 struct skinny_device *d = s->device;
1796 struct skinny_line *l;
1799 /* Update time on device */
1800 handle_time_date_req_message(NULL
, s
);
1803 for (l = d->lines; l; l = l->next) {
1804 if (has_voicemail(l)) {
1806 ast_verbose("Checking for voicemail Skinny %s@%s\n", l->name, d->name);
1807 ast_app_inboxcount(l->mailbox, &new, &old);
1809 ast_verbose("Skinny %s@%s has voicemail!\n", l->name, d->name);
1810 transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, l->mwiblink?SKINNY_LAMP_BLINK:SKINNY_LAMP_ON);
1812 transmit_lamp_indication(s, STIMULUS_VOICEMAIL, l->instance, SKINNY_LAMP_OFF);
1818 /* I do not believe skinny can deal with video.
1819 Anyone know differently? */
1820 /* Yes, it can. Currently 7985 and Cisco VT Advantage do video. */
1821 static enum ast_rtp_get_result
skinny_get_vrtp_peer(struct ast_channel
*c
, struct ast_rtp
**rtp
)
1823 struct skinny_subchannel
*sub
= NULL
;
1825 if (!(sub
= c
->tech_pvt
) || !(sub
->vrtp
))
1826 return AST_RTP_GET_FAILED
;
1830 return AST_RTP_TRY_NATIVE
;
1833 static enum ast_rtp_get_result
skinny_get_rtp_peer(struct ast_channel
*c
, struct ast_rtp
**rtp
)
1835 struct skinny_subchannel
*sub
= NULL
;
1837 if (!(sub
= c
->tech_pvt
) || !(sub
->rtp
))
1838 return AST_RTP_GET_FAILED
;
1842 return AST_RTP_TRY_NATIVE
;
1845 static int skinny_set_rtp_peer(struct ast_channel
*c
, struct ast_rtp
*rtp
, struct ast_rtp
*vrtp
, int codecs
, int nat_active
)
1847 struct skinny_subchannel
*sub
;
1850 /* transmit_modify_with_sdp(sub, rtp); @@FIXME@@ if needed */
1856 static struct ast_rtp_protocol skinny_rtp
= {
1858 .get_rtp_info
= skinny_get_rtp_peer
,
1859 .get_vrtp_info
= skinny_get_vrtp_peer
,
1860 .set_rtp_peer
= skinny_set_rtp_peer
,
1863 static int skinny_do_debug(int fd
, int argc
, char *argv
[])
1866 return RESULT_SHOWUSAGE
;
1869 ast_cli(fd
, "Skinny Debugging Enabled\n");
1870 return RESULT_SUCCESS
;
1873 static int skinny_no_debug(int fd
, int argc
, char *argv
[])
1876 return RESULT_SHOWUSAGE
;
1879 ast_cli(fd
, "Skinny Debugging Disabled\n");
1880 return RESULT_SUCCESS
;
1883 static char *complete_skinny_reset(const char *line
, const char *word
, int pos
, int state
)
1885 struct skinny_device
*d
;
1887 char *result
= NULL
;
1888 int wordlen
= strlen(word
);
1892 for (d
= devices
; d
&& !result
; d
= d
->next
) {
1893 if (!strncasecmp(word
, d
->id
, wordlen
) && ++which
> state
)
1894 result
= ast_strdup(d
->id
);
1901 static int skinny_reset_device(int fd
, int argc
, char *argv
[])
1903 struct skinny_device
*d
;
1904 struct skinny_req
*req
;
1906 if (argc
< 3 || argc
> 4) {
1907 return RESULT_SHOWUSAGE
;
1909 ast_mutex_lock(&devicelock
);
1911 for (d
= devices
; d
; d
= d
->next
) {
1912 int fullrestart
= 0;
1913 if (!strcasecmp(argv
[2], d
->id
) || !strcasecmp(argv
[2], "all")) {
1917 if (!(req
= req_alloc(sizeof(struct reset_message
), RESET_MESSAGE
)))
1920 if (argc
== 4 && !strcasecmp(argv
[3], "restart"))
1924 req
->data
.reset
.resetType
= 2;
1926 req
->data
.reset
.resetType
= 1;
1928 if (option_verbose
> 2)
1929 ast_verbose(VERBOSE_PREFIX_3
"%s device %s.\n", (fullrestart
) ? "Restarting" : "Resetting", d
->id
);
1930 transmit_response(d
->session
, req
);
1933 ast_mutex_unlock(&devicelock
);
1934 return RESULT_SUCCESS
;
1937 static char *device2str(int type
)
1942 case SKINNY_DEVICE_NONE
:
1944 case SKINNY_DEVICE_30SPPLUS
:
1946 case SKINNY_DEVICE_12SPPLUS
:
1948 case SKINNY_DEVICE_12SP
:
1950 case SKINNY_DEVICE_12
:
1952 case SKINNY_DEVICE_30VIP
:
1954 case SKINNY_DEVICE_7910
:
1956 case SKINNY_DEVICE_7960
:
1958 case SKINNY_DEVICE_7940
:
1960 case SKINNY_DEVICE_7935
:
1962 case SKINNY_DEVICE_ATA186
:
1964 case SKINNY_DEVICE_7941
:
1966 case SKINNY_DEVICE_7971
:
1968 case SKINNY_DEVICE_7914
:
1970 case SKINNY_DEVICE_7985
:
1972 case SKINNY_DEVICE_7911
:
1974 case SKINNY_DEVICE_7961GE
:
1976 case SKINNY_DEVICE_7941GE
:
1978 case SKINNY_DEVICE_7931
:
1980 case SKINNY_DEVICE_7921
:
1982 case SKINNY_DEVICE_7906
:
1984 case SKINNY_DEVICE_7962
:
1986 case SKINNY_DEVICE_7937
:
1988 case SKINNY_DEVICE_7942
:
1990 case SKINNY_DEVICE_7945
:
1992 case SKINNY_DEVICE_7965
:
1994 case SKINNY_DEVICE_7975
:
1996 case SKINNY_DEVICE_7905
:
1998 case SKINNY_DEVICE_7920
:
2000 case SKINNY_DEVICE_7970
:
2002 case SKINNY_DEVICE_7912
:
2004 case SKINNY_DEVICE_7902
:
2006 case SKINNY_DEVICE_CIPC
:
2007 return "IP Communicator";
2008 case SKINNY_DEVICE_7961
:
2010 case SKINNY_DEVICE_7936
:
2012 case SKINNY_DEVICE_SCCPGATEWAY_AN
:
2013 return "SCCPGATEWAY_AN";
2014 case SKINNY_DEVICE_SCCPGATEWAY_BRI
:
2015 return "SCCPGATEWAY_BRI";
2016 case SKINNY_DEVICE_UNKNOWN
:
2019 if (!(tmp
= ast_threadstorage_get(&device2str_threadbuf
, DEVICE2STR_BUFSIZE
)))
2021 snprintf(tmp
, DEVICE2STR_BUFSIZE
, "UNKNOWN-%d", type
);
2026 static int skinny_show_devices(int fd
, int argc
, char *argv
[])
2028 struct skinny_device
*d
;
2029 struct skinny_line
*l
;
2033 return RESULT_SHOWUSAGE
;
2035 ast_mutex_lock(&devicelock
);
2037 ast_cli(fd
, "Name DeviceId IP Type R NL\n");
2038 ast_cli(fd
, "-------------------- ---------------- --------------- --------------- - --\n");
2039 for (d
= devices
; d
; d
= d
->next
) {
2041 for (l
= d
->lines
; l
; l
= l
->next
) {
2045 ast_cli(fd
, "%-20s %-16s %-15s %-15s %c %2d\n",
2048 d
->session
?ast_inet_ntoa(d
->session
->sin
.sin_addr
):"",
2049 device2str(d
->type
),
2050 d
->registered
?'Y':'N',
2053 ast_mutex_unlock(&devicelock
);
2054 return RESULT_SUCCESS
;
2057 static int skinny_show_lines(int fd
, int argc
, char *argv
[])
2059 struct skinny_device
*d
;
2060 struct skinny_line
*l
;
2063 return RESULT_SHOWUSAGE
;
2065 ast_mutex_lock(&devicelock
);
2067 ast_cli(fd
, "Device Name Instance Name Label \n");
2068 ast_cli(fd
, "-------------------- -------- -------------------- --------------------\n");
2069 for (d
= devices
; d
; d
= d
->next
) {
2070 for (l
= d
->lines
; l
; l
= l
->next
) {
2071 ast_cli(fd
, "%-20s %8d %-20s %-20s\n",
2079 ast_mutex_unlock(&devicelock
);
2080 return RESULT_SUCCESS
;
2083 static char show_devices_usage
[] =
2084 "Usage: skinny show devices\n"
2085 " Lists all devices known to the Skinny subsystem.\n";
2087 static char show_lines_usage
[] =
2088 "Usage: skinny show lines\n"
2089 " Lists all lines known to the Skinny subsystem.\n";
2091 static char debug_usage
[] =
2092 "Usage: skinny set debug\n"
2093 " Enables dumping of Skinny packets for debugging purposes\n";
2095 static char no_debug_usage
[] =
2096 "Usage: skinny set debug off\n"
2097 " Disables dumping of Skinny packets for debugging purposes\n";
2099 static char reset_usage
[] =
2100 "Usage: skinny reset <DeviceId|all> [restart]\n"
2101 " Causes a Skinny device to reset itself, optionally with a full restart\n";
2103 static struct ast_cli_entry cli_skinny
[] = {
2104 { { "skinny", "show", "devices", NULL
},
2105 skinny_show_devices
, "List defined Skinny devices",
2106 show_devices_usage
},
2108 { { "skinny", "show", "lines", NULL
},
2109 skinny_show_lines
, "List defined Skinny lines per device",
2112 { { "skinny", "set", "debug", NULL
},
2113 skinny_do_debug
, "Enable Skinny debugging",
2116 { { "skinny", "set", "debug", "off", NULL
},
2117 skinny_no_debug
, "Disable Skinny debugging",
2120 { { "skinny", "reset", NULL
},
2121 skinny_reset_device
, "Reset Skinny device(s)",
2122 reset_usage
, complete_skinny_reset
},
2126 static struct skinny_paging_device
*build_paging_device(const char *cat
, struct ast_variable
*v
)
2132 static struct skinny_device
*build_device(const char *cat
, struct ast_variable
*v
)
2134 struct skinny_device
*d
;
2135 struct skinny_line
*l
;
2136 struct skinny_speeddial
*sd
;
2137 struct skinny_addon
*a
;
2138 int lineInstance
= 1;
2139 int speeddialInstance
= 1;
2142 if (!(d
= ast_calloc(1, sizeof(struct skinny_device
)))) {
2145 ast_copy_string(d
->name
, cat
, sizeof(d
->name
));
2146 d
->lastlineinstance
= 1;
2147 d
->capability
= default_capability
;
2148 d
->prefs
= default_prefs
;
2151 if (!strcasecmp(v
->name
, "host")) {
2152 if (ast_get_ip(&d
->addr
, v
->value
)) {
2156 } else if (!strcasecmp(v
->name
, "port")) {
2157 d
->addr
.sin_port
= htons(atoi(v
->value
));
2158 } else if (!strcasecmp(v
->name
, "device")) {
2159 ast_copy_string(d
->id
, v
->value
, sizeof(d
->id
));
2160 } else if (!strcasecmp(v
->name
, "permit") || !strcasecmp(v
->name
, "deny")) {
2161 d
->ha
= ast_append_ha(v
->name
, v
->value
, d
->ha
);
2162 } else if (!strcasecmp(v
->name
, "context")) {
2163 ast_copy_string(context
, v
->value
, sizeof(context
));
2164 } else if (!strcasecmp(v
->name
, "allow")) {
2165 ast_parse_allow_disallow(&d
->prefs
, &d
->capability
, v
->value
, 1);
2166 } else if (!strcasecmp(v
->name
, "disallow")) {
2167 ast_parse_allow_disallow(&d
->prefs
, &d
->capability
, v
->value
, 0);
2168 } else if (!strcasecmp(v
->name
, "version")) {
2169 ast_copy_string(d
->version_id
, v
->value
, sizeof(d
->version_id
));
2170 } else if (!strcasecmp(v
->name
, "earlyrtp")) {
2171 d
->earlyrtp
= ast_true(v
->value
);
2172 } else if (!strcasecmp(v
->name
, "nat")) {
2173 nat
= ast_true(v
->value
);
2174 } else if (!strcasecmp(v
->name
, "callerid")) {
2175 if (!strcasecmp(v
->value
, "asreceived")) {
2179 ast_callerid_split(v
->value
, cid_name
, sizeof(cid_name
), cid_num
, sizeof(cid_num
));
2181 } else if (!strcasecmp(v
->name
, "language")) {
2182 ast_copy_string(language
, v
->value
, sizeof(language
));
2183 } else if (!strcasecmp(v
->name
, "accountcode")) {
2184 ast_copy_string(accountcode
, v
->value
, sizeof(accountcode
));
2185 } else if (!strcasecmp(v
->name
, "amaflags")) {
2186 y
= ast_cdr_amaflags2int(v
->value
);
2188 ast_log(LOG_WARNING
, "Invalid AMA flags: %s at line %d\n", v
->value
, v
->lineno
);
2192 } else if (!strcasecmp(v
->name
, "mohinterpret") || !strcasecmp(v
->name
, "musiconhold")) {
2193 ast_copy_string(mohinterpret
, v
->value
, sizeof(mohinterpret
));
2194 } else if (!strcasecmp(v
->name
, "mohsuggest")) {
2195 ast_copy_string(mohsuggest
, v
->value
, sizeof(mohsuggest
));
2196 } else if (!strcasecmp(v
->name
, "callgroup")) {
2197 cur_callergroup
= ast_get_group(v
->value
);
2198 } else if (!strcasecmp(v
->name
, "pickupgroup")) {
2199 cur_pickupgroup
= ast_get_group(v
->value
);
2200 } else if (!strcasecmp(v
->name
, "immediate")) {
2201 immediate
= ast_true(v
->value
);
2202 } else if (!strcasecmp(v
->name
, "cancallforward")) {
2203 cancallforward
= ast_true(v
->value
);
2204 } else if (!strcasecmp(v
->name
, "mailbox")) {
2205 ast_copy_string(mailbox
, v
->value
, sizeof(mailbox
));
2206 } else if (!strcasecmp(v
->name
, "hasvoicemail")) {
2207 if (ast_true(v
->value
) && ast_strlen_zero(mailbox
)) {
2208 ast_copy_string(mailbox
, cat
, sizeof(mailbox
));
2210 } else if (!strcasecmp(v
->name
, "callreturn")) {
2211 callreturn
= ast_true(v
->value
);
2212 } else if (!strcasecmp(v
->name
, "callwaiting")) {
2213 callwaiting
= ast_true(v
->value
);
2214 } else if (!strcasecmp(v
->name
, "transfer")) {
2215 transfer
= ast_true(v
->value
);
2216 } else if (!strcasecmp(v
->name
, "threewaycalling")) {
2217 threewaycalling
= ast_true(v
->value
);
2218 } else if (!strcasecmp(v
->name
, "mwiblink")) {
2219 mwiblink
= ast_true(v
->value
);
2220 } else if (!strcasecmp(v
->name
, "linelabel")) {
2221 ast_copy_string(linelabel
, v
->value
, sizeof(linelabel
));
2222 } else if (!strcasecmp(v
->name
, "speeddial")) {
2223 if (!(sd
= ast_calloc(1, sizeof(struct skinny_speeddial
)))) {
2226 char *stringp
, *exten
, *label
;
2228 exten
= strsep(&stringp
, ",");
2229 label
= strsep(&stringp
, ",");
2230 ast_mutex_init(&sd
->lock
);
2231 ast_copy_string(sd
->exten
, exten
, sizeof(sd
->exten
));
2233 ast_copy_string(sd
->label
, label
, sizeof(sd
->label
));
2235 ast_copy_string(sd
->label
, exten
, sizeof(sd
->label
));
2236 sd
->instance
= speeddialInstance
++;
2240 sd
->next
= d
->speeddials
;
2243 } else if (!strcasecmp(v
->name
, "addon")) {
2244 if (!(a
= ast_calloc(1, sizeof(struct skinny_addon
)))) {
2247 ast_mutex_init(&a
->lock
);
2248 ast_copy_string(a
->type
, v
->value
, sizeof(a
->type
));
2250 a
->next
= d
->addons
;
2253 } else if (!strcasecmp(v
->name
, "trunk") || !strcasecmp(v
->name
, "line")) {
2254 if (!(l
= ast_calloc(1, sizeof(struct skinny_line
)))) {
2257 ast_mutex_init(&l
->lock
);
2258 ast_copy_string(l
->name
, v
->value
, sizeof(l
->name
));
2260 /* XXX Should we check for uniqueness?? XXX */
2261 ast_copy_string(l
->context
, context
, sizeof(l
->context
));
2262 ast_copy_string(l
->cid_num
, cid_num
, sizeof(l
->cid_num
));
2263 ast_copy_string(l
->cid_name
, cid_name
, sizeof(l
->cid_name
));
2264 ast_copy_string(l
->label
, linelabel
, sizeof(l
->label
));
2265 ast_copy_string(l
->language
, language
, sizeof(l
->language
));
2266 ast_copy_string(l
->mohinterpret
, mohinterpret
, sizeof(l
->mohinterpret
));
2267 ast_copy_string(l
->mohsuggest
, mohsuggest
, sizeof(l
->mohsuggest
));
2268 ast_copy_string(l
->mailbox
, mailbox
, sizeof(l
->mailbox
));
2269 if (!ast_strlen_zero(mailbox
)) {
2270 if (option_verbose
> 2)
2271 ast_verbose(VERBOSE_PREFIX_3
"Setting mailbox '%s' on %s@%s\n", mailbox
, d
->name
, l
->name
);
2274 l
->capability
= d
->capability
;
2275 l
->prefs
= d
->prefs
;
2277 if (!strcasecmp(v
->name
, "trunk")) {
2278 l
->type
= TYPE_TRUNK
;
2280 l
->type
= TYPE_LINE
;
2282 l
->immediate
= immediate
;
2283 l
->callgroup
= cur_callergroup
;
2284 l
->pickupgroup
= cur_pickupgroup
;
2285 l
->callreturn
= callreturn
;
2286 l
->cancallforward
= cancallforward
;
2287 l
->callwaiting
= callwaiting
;
2288 l
->transfer
= transfer
;
2289 l
->threewaycalling
= threewaycalling
;
2290 l
->mwiblink
= mwiblink
;
2291 l
->onhooktime
= time(NULL
);
2292 l
->instance
= lineInstance
++;
2293 /* ASSUME we're onhook at this point */
2294 l
->hookstate
= SKINNY_ONHOOK
;
2301 ast_log(LOG_WARNING
, "Don't know keyword '%s' at line %d\n", v
->name
, v
->lineno
);
2307 ast_log(LOG_ERROR
, "A Skinny device must have at least one line!\n");
2310 if (/*d->addr.sin_addr.s_addr && */!ntohs(d
->addr
.sin_port
)) {
2311 d
->addr
.sin_port
= htons(DEFAULT_SKINNY_PORT
);
2314 /* I don't think we need this anymore at all, since d->ourip is set in skinny_register now */
2315 if (d
->addr
.sin_addr
.s_addr
) {
2316 /* XXX See note above, in 'host' option. */
2317 if (ast_ouraddrfor(&d
->addr
.sin_addr
, &d
->ourip
)) {
2328 static void start_rtp(struct skinny_subchannel
*sub
)
2330 struct skinny_line
*l
= sub
->parent
;
2331 struct skinny_device
*d
= l
->parent
;
2334 ast_mutex_lock(&sub
->lock
);
2335 /* Allocate the RTP */
2336 sub
->rtp
= ast_rtp_new_with_bindaddr(sched
, io
, 1, 0, bindaddr
.sin_addr
);
2338 sub
->vrtp
= ast_rtp_new_with_bindaddr(sched
, io
, 1, 0, bindaddr
.sin_addr
);
2340 if (sub
->rtp
&& sub
->owner
) {
2341 sub
->owner
->fds
[0] = ast_rtp_fd(sub
->rtp
);
2342 sub
->owner
->fds
[1] = ast_rtcp_fd(sub
->rtp
);
2344 if (hasvideo
&& sub
->vrtp
&& sub
->owner
) {
2345 sub
->owner
->fds
[2] = ast_rtp_fd(sub
->vrtp
);
2346 sub
->owner
->fds
[3] = ast_rtcp_fd(sub
->vrtp
);
2349 ast_rtp_setnat(sub
->rtp
, l
->nat
);
2352 ast_rtp_setnat(sub
->vrtp
, l
->nat
);
2354 /* Set Frame packetization */
2356 ast_rtp_codec_setpref(sub
->rtp
, &l
->prefs
);
2358 /* Create the RTP connection */
2359 transmit_connect(d
->session
, sub
);
2360 ast_mutex_unlock(&sub
->lock
);
2363 static void *skinny_newcall(void *data
)
2365 struct ast_channel
*c
= data
;
2366 struct skinny_subchannel
*sub
= c
->tech_pvt
;
2367 struct skinny_line
*l
= sub
->parent
;
2368 struct skinny_device
*d
= l
->parent
;
2369 struct skinnysession
*s
= d
->session
;
2372 ast_copy_string(l
->lastnumberdialed
, c
->exten
, sizeof(l
->lastnumberdialed
));
2374 l
->hidecallerid
? "" : l
->cid_num
,
2375 l
->hidecallerid
? "" : l
->cid_name
,
2376 c
->cid
.cid_ani
? NULL
: l
->cid_num
);
2377 ast_setstate(c
, AST_STATE_RING
);
2381 res
= ast_pbx_run(c
);
2383 ast_log(LOG_WARNING
, "PBX exited non-zero\n");
2384 transmit_tone(s
, SKINNY_REORDER
, l
->instance
, sub
->callid
);
2389 static void *skinny_ss(void *data
)
2391 struct ast_channel
*c
= data
;
2392 struct skinny_subchannel
*sub
= c
->tech_pvt
;
2393 struct skinny_line
*l
= sub
->parent
;
2394 struct skinny_device
*d
= l
->parent
;
2395 struct skinnysession
*s
= d
->session
;
2397 int timeout
= firstdigittimeout
;
2400 int loop_pause
= 100;
2402 if (option_verbose
> 2)
2403 ast_verbose( VERBOSE_PREFIX_3
"Starting simple switch on '%s@%s'\n", l
->name
, d
->name
);
2404 len
= strlen(d
->exten
);
2406 while (len
< AST_MAX_EXTENSION
-1) {
2408 res
= 1; /* Assume we will get a digit */
2409 while (strlen(d
->exten
) == len
) {
2410 ast_safe_sleep(c
, loop_pause
);
2411 timeout
-= loop_pause
;
2418 len
= strlen(d
->exten
);
2420 if (!ast_ignore_pattern(c
->context
, d
->exten
)) {
2421 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
2424 if (ast_exists_extension(c
, c
->context
, d
->exten
, 1, l
->cid_num
)) {
2425 if (!res
|| !ast_matchmore_extension(c
, c
->context
, d
->exten
, 1, l
->cid_num
)) {
2427 /* Record this as the forwarding extension */
2428 ast_copy_string(l
->call_forward
, d
->exten
, sizeof(l
->call_forward
));
2429 if (option_verbose
> 2)
2430 ast_verbose(VERBOSE_PREFIX_3
"Setting call forward to '%s' on channel %s\n",
2431 l
->call_forward
, c
->name
);
2432 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
2436 ast_safe_sleep(c
, 500);
2437 ast_indicate(c
, -1);
2438 ast_safe_sleep(c
, 1000);
2439 memset(d
->exten
, 0, sizeof(d
->exten
));
2440 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
2444 ast_copy_string(c
->exten
, d
->exten
, sizeof(c
->exten
));
2445 ast_copy_string(l
->lastnumberdialed
, d
->exten
, sizeof(l
->lastnumberdialed
));
2446 memset (d
->exten
, 0, sizeof(d
->exten
));
2451 /* It's a match, but they just typed a digit, and there is an ambiguous match,
2452 so just set the timeout to matchdigittimeout and wait some more */
2453 timeout
= matchdigittimeout
;
2455 } else if (res
== 0) {
2456 ast_log(LOG_DEBUG
, "Not enough digits (and no ambiguous match)...\n");
2457 memset(d
->exten
, 0, sizeof(d
->exten
));
2458 transmit_tone(s
, SKINNY_REORDER
, l
->instance
, sub
->callid
);
2459 if (sub
->owner
&& sub
->owner
->_state
!= AST_STATE_UP
) {
2460 ast_indicate(c
, -1);
2464 } else if (!ast_canmatch_extension(c
, c
->context
, d
->exten
, 1, c
->cid
.cid_num
) &&
2465 ((d
->exten
[0] != '*') || (!ast_strlen_zero(d
->exten
) > 2))) {
2466 ast_log(LOG_WARNING
, "Can't match [%s] from '%s' in context %s\n", d
->exten
, c
->cid
.cid_num
? c
->cid
.cid_num
: "<Unknown Caller>", c
->context
);
2467 memset(d
->exten
, 0, sizeof(d
->exten
));
2468 transmit_tone(s
, SKINNY_REORDER
, l
->instance
, sub
->callid
);
2469 /* hang out for 3 seconds to let congestion play */
2470 ast_safe_sleep(c
, 3000);
2474 timeout
= gendigittimeout
;
2476 if (len
&& !ast_ignore_pattern(c
->context
, d
->exten
)) {
2477 ast_indicate(c
, -1);
2483 memset(d
->exten
, 0, sizeof(d
->exten
));
2489 static int skinny_call(struct ast_channel
*ast
, char *dest
, int timeout
)
2493 struct skinny_subchannel
*sub
= ast
->tech_pvt
;
2494 struct skinny_line
*l
= sub
->parent
;
2495 struct skinny_device
*d
= l
->parent
;
2496 struct skinnysession
*s
= d
->session
;
2498 if (!d
->registered
) {
2499 ast_log(LOG_ERROR
, "Device not registered, cannot call %s\n", dest
);
2503 if ((ast
->_state
!= AST_STATE_DOWN
) && (ast
->_state
!= AST_STATE_RESERVED
)) {
2504 ast_log(LOG_WARNING
, "skinny_call called on %s, neither down nor reserved\n", ast
->name
);
2509 ast_verbose(VERBOSE_PREFIX_3
"skinny_call(%s)\n", ast
->name
);
2512 ast_queue_control(ast
, AST_CONTROL_BUSY
);
2516 switch (l
->hookstate
) {
2517 case SKINNY_OFFHOOK
:
2518 tone
= SKINNY_CALLWAITTONE
;
2521 tone
= SKINNY_ALERT
;
2524 ast_log(LOG_ERROR
, "Don't know how to deal with hookstate %d\n", l
->hookstate
);
2528 transmit_callstate(s
, l
->instance
, SKINNY_RINGIN
, sub
->callid
);
2529 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_RINGIN
);
2530 transmit_displaypromptstatus(s
, "Ring-In", 0, l
->instance
, sub
->callid
);
2531 transmit_callinfo(s
, ast
->cid
.cid_name
, ast
->cid
.cid_num
, l
->cid_name
, l
->cid_num
, l
->instance
, sub
->callid
, 1);
2532 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_BLINK
);
2533 transmit_ringer_mode(s
, SKINNY_RING_INSIDE
);
2535 ast_setstate(ast
, AST_STATE_RINGING
);
2536 ast_queue_control(ast
, AST_CONTROL_RINGING
);
2541 static int skinny_hangup(struct ast_channel
*ast
)
2543 struct skinny_subchannel
*sub
= ast
->tech_pvt
;
2544 struct skinny_line
*l
;
2545 struct skinny_device
*d
;
2546 struct skinnysession
*s
;
2549 ast_log(LOG_DEBUG
, "Asked to hangup channel not connected\n");
2556 ast_verbose("skinny_hangup(%s) on %s@%s\n", ast
->name
, l
->name
, d
->name
);
2558 if (d
->registered
) {
2559 if ((l
->type
= TYPE_LINE
) && (l
->hookstate
== SKINNY_OFFHOOK
)) {
2560 l
->hookstate
= SKINNY_ONHOOK
;
2561 transmit_callstate(s
, l
->instance
, SKINNY_ONHOOK
, sub
->callid
);
2562 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_OFF
);
2563 transmit_speaker_mode(s
, SKINNY_SPEAKEROFF
);
2564 } else if ((l
->type
= TYPE_LINE
) && (l
->hookstate
== SKINNY_ONHOOK
)) {
2565 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
2566 transmit_callstate(s
, l
->instance
, SKINNY_ONHOOK
, sub
->callid
);
2567 transmit_ringer_mode(s
, SKINNY_RING_OFF
);
2568 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_OFF
);
2572 ast_mutex_lock(&sub
->lock
);
2574 ast
->tech_pvt
= NULL
;
2575 sub
->alreadygone
= 0;
2578 ast_rtp_destroy(sub
->rtp
);
2581 ast_mutex_unlock(&sub
->lock
);
2585 static int skinny_answer(struct ast_channel
*ast
)
2588 struct skinny_subchannel
*sub
= ast
->tech_pvt
;
2589 struct skinny_line
*l
= sub
->parent
;
2590 struct skinny_device
*d
= l
->parent
;
2591 struct skinnysession
*s
= d
->session
;
2592 char exten
[AST_MAX_EXTENSION
] = "";
2594 ast_copy_string(exten
, S_OR(ast
->macroexten
, ast
->exten
), sizeof(exten
));
2596 sub
->cxmode
= SKINNY_CX_SENDRECV
;
2601 ast_verbose("skinny_answer(%s) on %s@%s-%d\n", ast
->name
, l
->name
, d
->name
, sub
->callid
);
2602 if (ast
->_state
!= AST_STATE_UP
) {
2603 ast_setstate(ast
, AST_STATE_UP
);
2606 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
2607 /* order matters here...
2608 for some reason, transmit_callinfo must be before transmit_callstate,
2609 or you won't get keypad messages in some situations. */
2610 transmit_callinfo(s
, ast
->cid
.cid_name
, ast
->cid
.cid_num
, exten
, exten
, l
->instance
, sub
->callid
, 2);
2611 transmit_callstate(s
, l
->instance
, SKINNY_CONNECTED
, sub
->callid
);
2612 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_CONNECTED
);
2613 transmit_dialednumber(s
, exten
, l
->instance
, sub
->callid
);
2614 transmit_displaypromptstatus(s
, "Connected", 0, l
->instance
, sub
->callid
);
2618 /* Retrieve audio/etc from channel. Assumes sub->lock is already held. */
2619 static struct ast_frame
*skinny_rtp_read(struct skinny_subchannel
*sub
)
2621 struct ast_channel
*ast
= sub
->owner
;
2622 struct ast_frame
*f
;
2625 /* We have no RTP allocated for this channel */
2626 return &ast_null_frame
;
2631 f
= ast_rtp_read(sub
->rtp
); /* RTP Audio */
2634 f
= ast_rtcp_read(sub
->rtp
); /* RTCP Control Channel */
2637 f
= ast_rtp_read(sub
->vrtp
); /* RTP Video */
2640 f
= ast_rtcp_read(sub
->vrtp
); /* RTCP Control Channel for video */
2644 /* Not yet supported */
2645 f
= ast_udptl_read(sub
->udptl
); /* UDPTL for T.38 */
2649 f
= &ast_null_frame
;
2653 /* We already hold the channel lock */
2654 if (f
->frametype
== AST_FRAME_VOICE
) {
2655 if (f
->subclass
!= ast
->nativeformats
) {
2656 ast_log(LOG_DEBUG
, "Oooh, format changed to %d\n", f
->subclass
);
2657 ast
->nativeformats
= f
->subclass
;
2658 ast_set_read_format(ast
, ast
->readformat
);
2659 ast_set_write_format(ast
, ast
->writeformat
);
2666 static struct ast_frame
*skinny_read(struct ast_channel
*ast
)
2668 struct ast_frame
*fr
;
2669 struct skinny_subchannel
*sub
= ast
->tech_pvt
;
2670 ast_mutex_lock(&sub
->lock
);
2671 fr
= skinny_rtp_read(sub
);
2672 ast_mutex_unlock(&sub
->lock
);
2676 static int skinny_write(struct ast_channel
*ast
, struct ast_frame
*frame
)
2678 struct skinny_subchannel
*sub
= ast
->tech_pvt
;
2680 if (frame
->frametype
!= AST_FRAME_VOICE
) {
2681 if (frame
->frametype
== AST_FRAME_IMAGE
) {
2684 ast_log(LOG_WARNING
, "Can't send %d type frames with skinny_write\n", frame
->frametype
);
2688 if (!(frame
->subclass
& ast
->nativeformats
)) {
2689 ast_log(LOG_WARNING
, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
2690 frame
->subclass
, ast
->nativeformats
, ast
->readformat
, ast
->writeformat
);
2695 ast_mutex_lock(&sub
->lock
);
2697 res
= ast_rtp_write(sub
->rtp
, frame
);
2699 ast_mutex_unlock(&sub
->lock
);
2704 static int skinny_fixup(struct ast_channel
*oldchan
, struct ast_channel
*newchan
)
2706 struct skinny_subchannel
*sub
= newchan
->tech_pvt
;
2707 ast_log(LOG_NOTICE
, "skinny_fixup(%s, %s)\n", oldchan
->name
, newchan
->name
);
2708 if (sub
->owner
!= oldchan
) {
2709 ast_log(LOG_WARNING
, "old channel wasn't %p but was %p\n", oldchan
, sub
->owner
);
2712 sub
->owner
= newchan
;
2716 static int skinny_senddigit_begin(struct ast_channel
*ast
, char digit
)
2718 return -1; /* Start inband indications */
2721 static int skinny_senddigit_end(struct ast_channel
*ast
, char digit
, unsigned int duration
)
2724 struct skinny_subchannel
*sub
= ast
->tech_pvt
;
2725 struct skinny_line
*l
= sub
->parent
;
2726 struct skinny_device
*d
= l
->parent
;
2729 sprintf(tmp
, "%d", digit
);
2730 transmit_tone(d
->session
, digit
, l
->instance
, sub
->callid
);
2732 return -1; /* Stop inband indications */
2735 static char *control2str(int ind
) {
2739 case AST_CONTROL_HANGUP
:
2740 return "Other end has hungup";
2741 case AST_CONTROL_RING
:
2742 return "Local ring";
2743 case AST_CONTROL_RINGING
:
2744 return "Remote end is ringing";
2745 case AST_CONTROL_ANSWER
:
2746 return "Remote end has answered";
2747 case AST_CONTROL_BUSY
:
2748 return "Remote end is busy";
2749 case AST_CONTROL_TAKEOFFHOOK
:
2750 return "Make it go off hook";
2751 case AST_CONTROL_OFFHOOK
:
2752 return "Line is off hook";
2753 case AST_CONTROL_CONGESTION
:
2754 return "Congestion (circuits busy)";
2755 case AST_CONTROL_FLASH
:
2756 return "Flash hook";
2757 case AST_CONTROL_WINK
:
2759 case AST_CONTROL_OPTION
:
2760 return "Set a low-level option";
2761 case AST_CONTROL_RADIO_KEY
:
2763 case AST_CONTROL_RADIO_UNKEY
:
2764 return "Un-Key Radio";
2765 case AST_CONTROL_PROGRESS
:
2766 return "Remote end is making Progress";
2767 case AST_CONTROL_PROCEEDING
:
2768 return "Remote end is proceeding";
2769 case AST_CONTROL_HOLD
:
2771 case AST_CONTROL_UNHOLD
:
2776 if (!(tmp
= ast_threadstorage_get(&control2str_threadbuf
, CONTROL2STR_BUFSIZE
)))
2778 snprintf(tmp
, CONTROL2STR_BUFSIZE
, "UNKNOWN-%d", ind
);
2784 static int skinny_indicate(struct ast_channel
*ast
, int ind
, const void *data
, size_t datalen
)
2786 struct skinny_subchannel
*sub
= ast
->tech_pvt
;
2787 struct skinny_line
*l
= sub
->parent
;
2788 struct skinny_device
*d
= l
->parent
;
2789 struct skinnysession
*s
= d
->session
;
2790 char exten
[AST_MAX_EXTENSION
] = "";
2793 ast_log(LOG_NOTICE
, "Asked to indicate '%s' condition on channel %s, but session does not exist.\n", control2str(ind
), ast
->name
);
2797 ast_copy_string(exten
, S_OR(ast
->macroexten
, ast
->exten
), sizeof(exten
));
2800 ast_verbose(VERBOSE_PREFIX_3
"Asked to indicate '%s' condition on channel %s\n", control2str(ind
), ast
->name
);
2802 case AST_CONTROL_RINGING
:
2803 if (ast
->_state
!= AST_STATE_UP
) {
2804 if (!sub
->progress
) {
2806 transmit_tone(s
, SKINNY_ALERT
, l
->instance
, sub
->callid
);
2808 transmit_callstate(s
, l
->instance
, SKINNY_RINGOUT
, sub
->callid
);
2809 transmit_dialednumber(s
, exten
, l
->instance
, sub
->callid
);
2810 transmit_displaypromptstatus(s
, "Ring Out", 0, l
->instance
, sub
->callid
);
2811 transmit_callinfo(s
, ast
->cid
.cid_name
, ast
->cid
.cid_num
, exten
, exten
, l
->instance
, sub
->callid
, 2); /* 2 = outgoing from phone */
2818 return -1; /* Tell asterisk to provide inband signalling */
2819 case AST_CONTROL_BUSY
:
2820 if (ast
->_state
!= AST_STATE_UP
) {
2822 transmit_tone(s
, SKINNY_BUSYTONE
, l
->instance
, sub
->callid
);
2824 transmit_callstate(s
, l
->instance
, SKINNY_BUSY
, sub
->callid
);
2825 sub
->alreadygone
= 1;
2826 ast_softhangup_nolock(ast
, AST_SOFTHANGUP_DEV
);
2831 return -1; /* Tell asterisk to provide inband signalling */
2832 case AST_CONTROL_CONGESTION
:
2833 if (ast
->_state
!= AST_STATE_UP
) {
2835 transmit_tone(s
, SKINNY_REORDER
, l
->instance
, sub
->callid
);
2837 transmit_callstate(s
, l
->instance
, SKINNY_CONGESTION
, sub
->callid
);
2838 sub
->alreadygone
= 1;
2839 ast_softhangup_nolock(ast
, AST_SOFTHANGUP_DEV
);
2844 return -1; /* Tell asterisk to provide inband signalling */
2845 case AST_CONTROL_PROGRESS
:
2846 if ((ast
->_state
!= AST_STATE_UP
) && !sub
->progress
&& !sub
->outgoing
) {
2848 transmit_tone(s
, SKINNY_ALERT
, l
->instance
, sub
->callid
);
2850 transmit_callstate(s
, l
->instance
, SKINNY_PROGRESS
, sub
->callid
);
2851 transmit_displaypromptstatus(s
, "Call Progress", 0, l
->instance
, sub
->callid
);
2852 transmit_callinfo(s
, ast
->cid
.cid_name
, ast
->cid
.cid_num
, exten
, exten
, l
->instance
, sub
->callid
, 2); /* 2 = outgoing from phone */
2858 return -1; /* Tell asterisk to provide inband signalling */
2859 case -1: /* STOP_TONE */
2860 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
2862 case AST_CONTROL_HOLD
:
2863 ast_moh_start(ast
, data
, l
->mohinterpret
);
2865 case AST_CONTROL_UNHOLD
:
2868 case AST_CONTROL_PROCEEDING
:
2870 case AST_CONTROL_SRCUPDATE
:
2871 ast_rtp_new_source(sub
->rtp
);
2874 ast_log(LOG_WARNING
, "Don't know how to indicate condition %d\n", ind
);
2875 return -1; /* Tell asterisk to provide inband signalling */
2880 static struct ast_channel
*skinny_new(struct skinny_line
*l
, int state
)
2882 struct ast_channel
*tmp
;
2883 struct skinny_subchannel
*sub
;
2884 struct skinny_device
*d
= l
->parent
;
2887 tmp
= ast_channel_alloc(1, state
, l
->cid_num
, l
->cid_name
, l
->accountcode
, l
->exten
, l
->context
, l
->amaflags
, "Skinny/%s@%s-%d", l
->name
, d
->name
, callnums
);
2889 ast_log(LOG_WARNING
, "Unable to allocate channel structure\n");
2892 sub
= ast_calloc(1, sizeof(struct skinny_subchannel
));
2894 ast_log(LOG_WARNING
, "Unable to allocate Skinny subchannel\n");
2897 ast_mutex_init(&sub
->lock
);
2900 sub
->callid
= callnums
++;
2901 d
->lastlineinstance
= l
->instance
;
2902 d
->lastcallreference
= sub
->callid
;
2903 sub
->cxmode
= SKINNY_CX_INACTIVE
;
2911 tmp
->tech
= &skinny_tech
;
2912 tmp
->tech_pvt
= sub
;
2913 tmp
->nativeformats
= l
->capability
;
2914 if (!tmp
->nativeformats
)
2915 tmp
->nativeformats
= default_capability
;
2916 fmt
= ast_best_codec(tmp
->nativeformats
);
2918 ast_verbose("skinny_new: tmp->nativeformats=%d fmt=%d\n", tmp
->nativeformats
, fmt
);
2920 tmp
->fds
[0] = ast_rtp_fd(sub
->rtp
);
2922 if (state
== AST_STATE_RING
) {
2925 tmp
->writeformat
= fmt
;
2926 tmp
->rawwriteformat
= fmt
;
2927 tmp
->readformat
= fmt
;
2928 tmp
->rawreadformat
= fmt
;
2929 if (!ast_strlen_zero(l
->language
))
2930 ast_string_field_set(tmp
, language
, l
->language
);
2931 if (!ast_strlen_zero(l
->accountcode
))
2932 ast_string_field_set(tmp
, accountcode
, l
->accountcode
);
2934 tmp
->amaflags
= l
->amaflags
;
2936 ast_module_ref(ast_module_info
->self
);
2937 tmp
->callgroup
= l
->callgroup
;
2938 tmp
->pickupgroup
= l
->pickupgroup
;
2939 ast_string_field_set(tmp
, call_forward
, l
->call_forward
);
2940 ast_copy_string(tmp
->context
, l
->context
, sizeof(tmp
->context
));
2941 ast_copy_string(tmp
->exten
, l
->exten
, sizeof(tmp
->exten
));
2943 /* Don't use ast_set_callerid() here because it will
2944 * generate a needless NewCallerID event */
2945 tmp
->cid
.cid_ani
= ast_strdup(l
->cid_num
);
2948 tmp
->adsicpe
= AST_ADSI_UNAVAILABLE
;
2951 ast_jb_configure(tmp
, &global_jbconf
);
2953 if (state
!= AST_STATE_DOWN
) {
2954 if (ast_pbx_start(tmp
)) {
2955 ast_log(LOG_WARNING
, "Unable to start PBX on %s\n", tmp
->name
);
2964 static int skinny_hold(struct skinny_subchannel
*sub
)
2966 struct skinny_line
*l
= sub
->parent
;
2967 struct skinny_device
*d
= l
->parent
;
2968 struct skinnysession
*s
= d
->session
;
2969 struct skinny_req
*req
;
2971 /* Don't try to hold a channel that doesn't exist */
2972 if (!sub
|| !sub
->owner
)
2975 /* Channel needs to be put on hold */
2977 ast_verbose("Putting on Hold(%d)\n", l
->instance
);
2979 ast_queue_control_data(sub
->owner
, AST_CONTROL_HOLD
,
2980 S_OR(l
->mohsuggest
, NULL
),
2981 !ast_strlen_zero(l
->mohsuggest
) ? strlen(l
->mohsuggest
) + 1 : 0);
2983 if (!(req
= req_alloc(sizeof(struct activate_call_plane_message
), ACTIVATE_CALL_PLANE_MESSAGE
)))
2986 req
->data
.activatecallplane
.lineInstance
= htolel(l
->instance
);
2987 transmit_response(s
, req
);
2989 if (!(req
= req_alloc(sizeof(struct close_receive_channel_message
), CLOSE_RECEIVE_CHANNEL_MESSAGE
)))
2992 req
->data
.closereceivechannel
.conferenceId
= htolel(sub
->callid
);
2993 req
->data
.closereceivechannel
.partyId
= htolel(sub
->callid
);
2994 transmit_response(s
, req
);
2996 if (!(req
= req_alloc(sizeof(struct stop_media_transmission_message
), STOP_MEDIA_TRANSMISSION_MESSAGE
)))
2999 req
->data
.stopmedia
.conferenceId
= htolel(sub
->callid
);
3000 req
->data
.stopmedia
.passThruPartyId
= htolel(sub
->callid
);
3001 transmit_response(s
, req
);
3003 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_BLINK
);
3008 static int skinny_unhold(struct skinny_subchannel
*sub
)
3010 struct skinny_line
*l
= sub
->parent
;
3011 struct skinny_device
*d
= l
->parent
;
3012 struct skinnysession
*s
= d
->session
;
3013 struct skinny_req
*req
;
3015 /* Don't try to unhold a channel that doesn't exist */
3016 if (!sub
|| !sub
->owner
)
3019 /* Channel is on hold, so we will unhold */
3021 ast_verbose("Taking off Hold(%d)\n", l
->instance
);
3023 ast_queue_control(sub
->owner
, AST_CONTROL_UNHOLD
);
3025 if (!(req
= req_alloc(sizeof(struct activate_call_plane_message
), ACTIVATE_CALL_PLANE_MESSAGE
)))
3028 req
->data
.activatecallplane
.lineInstance
= htolel(l
->instance
);
3029 transmit_response(s
, req
);
3031 transmit_connect(s
, sub
);
3032 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_ON
);
3037 static int handle_keep_alive_message(struct skinny_req
*req
, struct skinnysession
*s
)
3039 if (!(req
= req_alloc(0, KEEP_ALIVE_ACK_MESSAGE
)))
3042 transmit_response(s
, req
);
3047 static int handle_register_message(struct skinny_req
*req
, struct skinnysession
*s
)
3052 memcpy(&name
, req
->data
.reg
.name
, sizeof(name
));
3054 res
= skinny_register(req
, s
);
3056 ast_log(LOG_ERROR
, "Rejecting Device %s: Device not found\n", name
);
3057 if (!(req
= req_alloc(sizeof(struct register_rej_message
), REGISTER_REJ_MESSAGE
)))
3060 snprintf(req
->data
.regrej
.errMsg
, sizeof(req
->data
.regrej
.errMsg
), "No Authority: %s", name
);
3061 transmit_response(s
, req
);
3064 if (option_verbose
> 2)
3065 ast_verbose(VERBOSE_PREFIX_3
"Device '%s' successfully registered\n", name
);
3067 if (!(req
= req_alloc(sizeof(struct register_ack_message
), REGISTER_ACK_MESSAGE
)))
3070 req
->data
.regack
.res
[0] = '0';
3071 req
->data
.regack
.res
[1] = '\0';
3072 req
->data
.regack
.keepAlive
= htolel(keep_alive
);
3073 memcpy(req
->data
.regack
.dateTemplate
, date_format
, sizeof(req
->data
.regack
.dateTemplate
));
3074 req
->data
.regack
.res2
[0] = '0';
3075 req
->data
.regack
.res2
[1] = '\0';
3076 req
->data
.regack
.secondaryKeepAlive
= htolel(keep_alive
);
3077 transmit_response(s
, req
);
3079 ast_verbose("Requesting capabilities\n");
3081 if (!(req
= req_alloc(0, CAPABILITIES_REQ_MESSAGE
)))
3084 transmit_response(s
, req
);
3089 static int handle_ip_port_message(struct skinny_req
*req
, struct skinnysession
*s
)
3091 /* no response necessary */
3095 static int handle_keypad_button_message(struct skinny_req
*req
, struct skinnysession
*s
)
3097 struct skinny_subchannel
*sub
= NULL
;
3098 struct skinny_line
*l
;
3099 struct skinny_device
*d
= s
->device
;
3100 struct ast_frame f
= { 0, };
3106 digit
= letohl(req
->data
.keypad
.button
);
3107 lineInstance
= letohl(req
->data
.keypad
.lineInstance
);
3108 callReference
= letohl(req
->data
.keypad
.callReference
);
3112 } else if (digit
== 15) {
3114 } else if (digit
>= 0 && digit
<= 9) {
3117 /* digit=10-13 (A,B,C,D ?), or
3118 * digit is bad value
3120 * probably should not end up here, but set
3121 * value for backward compatibility, and log
3125 ast_log(LOG_WARNING
, "Unsupported digit %d\n", digit
);
3132 if (lineInstance
&& callReference
)
3133 sub
= find_subchannel_by_instance_reference(d
, lineInstance
, callReference
);
3135 sub
= find_subchannel_by_instance_reference(d
, d
->lastlineinstance
, d
->lastcallreference
);
3142 if (sub
->owner
->_state
== 0) {
3143 f
.frametype
= AST_FRAME_DTMF_BEGIN
;
3144 ast_queue_frame(sub
->owner
, &f
);
3146 /* XXX MUST queue this frame to all lines in threeway call if threeway call is active */
3147 f
.frametype
= AST_FRAME_DTMF_END
;
3148 ast_queue_frame(sub
->owner
, &f
);
3149 /* XXX This seriously needs to be fixed */
3150 if (sub
->next
&& sub
->next
->owner
) {
3151 if (sub
->owner
->_state
== 0) {
3152 f
.frametype
= AST_FRAME_DTMF_BEGIN
;
3153 ast_queue_frame(sub
->next
->owner
, &f
);
3155 f
.frametype
= AST_FRAME_DTMF_END
;
3156 ast_queue_frame(sub
->next
->owner
, &f
);
3160 ast_verbose("No owner: %s\n", l
->name
);
3165 static int handle_stimulus_message(struct skinny_req
*req
, struct skinnysession
*s
)
3167 struct skinny_device
*d
= s
->device
;
3168 struct skinny_line
*l
;
3169 struct skinny_subchannel
*sub
;
3170 /*struct skinny_speeddial *sd;*/
3171 struct ast_channel
*c
;
3178 event
= letohl(req
->data
.stimulus
.stimulus
);
3179 instance
= letohl(req
->data
.stimulus
.stimulusInstance
);
3180 callreference
= letohl(req
->data
.stimulus
.callreference
);
3182 ast_verbose("callreference in handle_stimulus_message is '%d'\n", callreference
);
3184 /* Note that this call should be using the passed in instance and callreference */
3185 sub
= find_subchannel_by_instance_reference(d
, d
->lastlineinstance
, d
->lastcallreference
);
3188 l
= find_line_by_instance(d
, d
->lastlineinstance
);
3197 case STIMULUS_REDIAL
:
3199 ast_verbose("Received Stimulus: Redial(%d/%d)\n", instance
, callreference
);
3202 if (ast_strlen_zero(l
->lastnumberdialed
)) {
3203 ast_log(LOG_WARNING
, "Attempted redial, but no previously dialed number found.\n");
3204 l
->hookstate
= SKINNY_ONHOOK
;
3205 transmit_speaker_mode(s
, SKINNY_SPEAKEROFF
);
3206 transmit_callstate(s
, l
->instance
, SKINNY_ONHOOK
, instance
);
3210 c
= skinny_new(l
, AST_STATE_DOWN
);
3212 ast_log(LOG_WARNING
, "Unable to create channel for %s@%s\n", l
->name
, d
->name
);
3215 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
3217 ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l
->name
, d
->name
);
3218 transmit_displaymessage(s
, NULL
, l
->instance
, sub
->callid
); /* clear display */
3219 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
3220 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_RINGOUT
);
3222 if (!ast_ignore_pattern(c
->context
, l
->lastnumberdialed
)) {
3223 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
3225 ast_copy_string(c
->exten
, l
->lastnumberdialed
, sizeof(c
->exten
));
3226 if (ast_pthread_create(&t
, NULL
, skinny_newcall
, c
)) {
3227 ast_log(LOG_WARNING
, "Unable to create new call thread: %s\n", strerror(errno
));
3233 case STIMULUS_SPEEDDIAL
:
3235 ast_verbose("Received Stimulus: SpeedDial(%d/%d)\n", instance
, callreference
);
3238 if (!(sd
= find_speeddial_by_instance(d
, instance
))) {
3242 if (ast_strlen_zero(l
->lastnumberdialed
)) {
3243 ast_log(LOG_WARNING
, "Attempted redial, but no previously dialed number found.\n");
3244 l
->hookstate
= SKINNY_ONHOOK
;
3245 transmit_speaker_mode(s
, SKINNY_SPEAKEROFF
);
3246 transmit_callstate(s
, l
->instance
, SKINNY_ONHOOK
, instance
);
3250 c
= skinny_new(l
, AST_STATE_DOWN
);
3254 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
3256 ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l
->name
, d
->name
);
3257 transmit_displaymessage(s
, NULL
, l
->instance
, sub
->callid
); /* clear display */
3258 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
3259 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_RINGOUT
);
3261 if (!ast_ignore_pattern(c
->context
, sd
->exten
)) {
3262 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
3264 if (ast_exists_extension(c
, c
->context
, sd
->exten
, 1, l
->cid_num
)) {
3265 if (!ast_matchmore_extension(c
, c
->context
, sd
->exten
, 1, l
->cid_num
)) {
3266 ast_copy_string(c
->exten
, sd
->exten
, sizeof(c
->exten
));
3267 ast_copy_string(l
->lastnumberdialed
, sd
->exten
, sizeof(l
->lastnumberdialed
));
3273 ast_log(LOG_WARNING
, "Unable to create channel for %s@%s\n", l
->name
, d
->name
);
3279 ast_verbose("Received Stimulus: Hold(%d/%d)\n", instance
, callreference
);
3290 case STIMULUS_TRANSFER
:
3292 ast_verbose("Received Stimulus: Transfer(%d/%d)\n", instance
, callreference
);
3293 /* XXX figure out how to transfer */
3295 case STIMULUS_CONFERENCE
:
3297 ast_verbose("Received Stimulus: Conference(%d/%d)\n", instance
, callreference
);
3298 /* XXX determine the best way to pull off a conference. Meetme? */
3300 case STIMULUS_VOICEMAIL
:
3302 ast_verbose("Received Stimulus: Voicemail(%d/%d)\n", instance
, callreference
);
3303 /* XXX Find and dial voicemail extension */
3305 case STIMULUS_CALLPARK
:
3307 ast_verbose("Received Stimulus: Park Call(%d/%d)\n", instance
, callreference
);
3308 /* XXX Park the call */
3310 case STIMULUS_FORWARDALL
:
3312 ast_verbose("Received Stimulus: Forward All(%d/%d)\n", instance
, callreference
);
3313 /* Why is DND under FORWARDALL? */
3314 /* Because it's the same thing. */
3316 /* Do not disturb */
3318 if (option_verbose
> 2)
3319 ast_verbose(VERBOSE_PREFIX_3
"Disabling DND on %s@%s\n", l
->name
, d
->name
);
3321 transmit_lamp_indication(s
, STIMULUS_FORWARDALL
, 1, SKINNY_LAMP_ON
);
3322 transmit_displaynotify(s
, "DnD disabled", 10);
3324 if (option_verbose
> 2)
3325 ast_verbose(VERBOSE_PREFIX_3
"Enabling DND on %s@%s\n", l
->name
, d
->name
);
3327 transmit_lamp_indication(s
, STIMULUS_FORWARDALL
, 1, SKINNY_LAMP_OFF
);
3328 transmit_displaynotify(s
, "DnD enabled", 10);
3331 case STIMULUS_FORWARDBUSY
:
3333 ast_verbose("Received Stimulus: Forward Busy (%d/%d)\n", instance
, callreference
);
3335 case STIMULUS_FORWARDNOANSWER
:
3337 ast_verbose("Received Stimulus: Forward No Answer (%d/%d)\n", instance
, callreference
);
3339 case STIMULUS_DISPLAY
:
3340 /* Not sure what this is */
3342 ast_verbose("Received Stimulus: Display(%d/%d)\n", instance
, callreference
);
3346 ast_verbose("Received Stimulus: Line(%d/%d)\n", instance
, callreference
);
3348 l
= find_line_by_instance(s
->device
, instance
);
3354 /* turn the speaker on */
3355 transmit_speaker_mode(s
, SKINNY_SPEAKERON
);
3356 transmit_ringer_mode(s
, SKINNY_RING_OFF
);
3357 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_ON
);
3359 l
->hookstate
= SKINNY_OFFHOOK
;
3361 if (sub
&& sub
->outgoing
) {
3362 /* We're answering a ringing call */
3363 ast_queue_control(sub
->owner
, AST_CONTROL_ANSWER
);
3364 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
3365 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
3366 transmit_callstate(s
, l
->instance
, SKINNY_CONNECTED
, sub
->callid
);
3367 transmit_displaypromptstatus(s
, "Connected", 0, l
->instance
, sub
->callid
);
3368 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_CONNECTED
);
3370 ast_setstate(sub
->owner
, AST_STATE_UP
);
3372 if (sub
&& sub
->owner
) {
3373 ast_log(LOG_DEBUG
, "Current subchannel [%s] already has owner\n", sub
->owner
->name
);
3375 c
= skinny_new(l
, AST_STATE_DOWN
);
3378 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
3380 ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l
->name
, d
->name
);
3381 transmit_displaymessage(s
, NULL
, l
->instance
, sub
->callid
); /* clear display */
3382 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
3383 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_OFFHOOK
);
3385 /* start the switch thread */
3386 if (ast_pthread_create(&t
, NULL
, skinny_ss
, c
)) {
3387 ast_log(LOG_WARNING
, "Unable to create switch thread: %s\n", strerror(errno
));
3391 ast_log(LOG_WARNING
, "Unable to create channel for %s@%s\n", l
->name
, d
->name
);
3398 ast_verbose("RECEIVED UNKNOWN STIMULUS: %d(%d/%d)\n", event
, instance
, callreference
);
3404 static int handle_offhook_message(struct skinny_req
*req
, struct skinnysession
*s
)
3406 struct skinny_device
*d
= s
->device
;
3407 struct skinny_line
*l
;
3408 struct skinny_subchannel
*sub
;
3409 struct ast_channel
*c
;
3414 unknown1
= letohl(req
->data
.offhook
.unknown1
);
3415 unknown2
= letohl(req
->data
.offhook
.unknown2
);
3417 sub
= find_subchannel_by_instance_reference(d
, d
->lastlineinstance
, d
->lastcallreference
);
3420 l
= find_line_by_instance(d
, d
->lastlineinstance
);
3428 transmit_ringer_mode(s
, SKINNY_RING_OFF
);
3429 l
->hookstate
= SKINNY_OFFHOOK
;
3431 if (sub
&& sub
->onhold
) {
3435 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_ON
);
3437 if (sub
&& sub
->outgoing
) {
3438 /* We're answering a ringing call */
3439 ast_queue_control(sub
->owner
, AST_CONTROL_ANSWER
);
3440 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
3441 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
3442 transmit_callstate(s
, l
->instance
, SKINNY_CONNECTED
, sub
->callid
);
3443 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_CONNECTED
);
3445 ast_setstate(sub
->owner
, AST_STATE_UP
);
3447 if (sub
&& sub
->owner
) {
3448 ast_log(LOG_DEBUG
, "Current sub [%s] already has owner\n", sub
->owner
->name
);
3450 c
= skinny_new(l
, AST_STATE_DOWN
);
3453 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
3455 ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l
->name
, d
->name
);
3456 transmit_displaymessage(s
, NULL
, l
->instance
, sub
->callid
); /* clear display */
3457 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
3458 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_OFFHOOK
);
3460 /* start the switch thread */
3461 if (ast_pthread_create(&t
, NULL
, skinny_ss
, c
)) {
3462 ast_log(LOG_WARNING
, "Unable to create switch thread: %s\n", strerror(errno
));
3466 ast_log(LOG_WARNING
, "Unable to create channel for %s@%s\n", l
->name
, d
->name
);
3473 static int handle_onhook_message(struct skinny_req
*req
, struct skinnysession
*s
)
3475 struct skinny_device
*d
= s
->device
;
3476 struct skinny_line
*l
;
3477 struct skinny_subchannel
*sub
;
3481 unknown1
= letohl(req
->data
.onhook
.unknown1
);
3482 unknown2
= letohl(req
->data
.onhook
.unknown2
);
3484 sub
= find_subchannel_by_instance_reference(d
, d
->lastlineinstance
, d
->lastcallreference
);
3491 if (l
->hookstate
== SKINNY_ONHOOK
) {
3492 /* Something else already put us back on hook */
3495 l
->hookstate
= SKINNY_ONHOOK
;
3501 sub
->cxmode
= SKINNY_CX_RECVONLY
;
3502 transmit_callstate(s
, l
->instance
, l
->hookstate
, sub
->callid
);
3504 ast_verbose("Skinny %s@%s went on hook\n", l
->name
, d
->name
);
3505 if (l
->transfer
&& (sub
->owner
&& sub
->next
&& sub
->next
->owner
) && ((!sub
->outgoing
) || (sub
->next
&& !sub
->next
->outgoing
))) {
3506 /* We're allowed to transfer, we have two active calls and
3507 we made at least one of the calls. Let's try and transfer */
3510 if ((res
= attempt_transfer(p
)) < 0) {
3511 if (sub
->next
&& sub
->next
->owner
) {
3512 sub
->next
->alreadygone
= 1;
3513 ast_queue_hangup(sub
->next
->owner
,1);
3516 ast_log(LOG_WARNING
, "Transfer attempt failed\n");
3521 /* Hangup the current call */
3522 /* If there is another active call, skinny_hangup will ring the phone with the other call */
3524 sub
->alreadygone
= 1;
3525 ast_queue_hangup(sub
->owner
);
3527 ast_log(LOG_WARNING
, "Skinny(%s@%s-%d) channel already destroyed\n",
3528 l
->name
, d
->name
, sub
->callid
);
3531 if ((l
->hookstate
== SKINNY_ONHOOK
) && (sub
->next
&& !sub
->next
->rtp
)) {
3537 static int handle_capabilities_res_message(struct skinny_req
*req
, struct skinnysession
*s
)
3539 struct skinny_device
*d
= s
->device
;
3540 struct skinny_line
*l
;
3545 count
= letohl(req
->data
.caps
.count
);
3546 if (count
> SKINNY_MAX_CAPABILITIES
) {
3547 count
= SKINNY_MAX_CAPABILITIES
;
3548 ast_log(LOG_WARNING
, "Received more capabilities than we can handle (%d). Ignoring the rest.\n", SKINNY_MAX_CAPABILITIES
);
3551 for (i
= 0; i
< count
; i
++) {
3554 scodec
= letohl(req
->data
.caps
.caps
[i
].codec
);
3555 acodec
= codec_skinny2ast(scodec
);
3557 ast_verbose("Adding codec capability '%d (%d)'\n", acodec
, scodec
);
3561 d
->capability
&= codecs
;
3562 ast_verbose("Device capability set to '%d'\n", d
->capability
);
3563 for (l
= d
->lines
; l
; l
= l
->next
) {
3564 ast_mutex_lock(&l
->lock
);
3565 l
->capability
= d
->capability
;
3566 ast_mutex_unlock(&l
->lock
);
3572 static int handle_speed_dial_stat_req_message(struct skinny_req
*req
, struct skinnysession
*s
)
3574 struct skinny_device
*d
= s
->device
;
3575 struct skinny_speeddial
*sd
;
3578 instance
= letohl(req
->data
.speeddialreq
.speedDialNumber
);
3580 sd
= find_speeddial_by_instance(d
, instance
);
3586 if (!(req
= req_alloc(sizeof(struct speed_dial_stat_res_message
), SPEED_DIAL_STAT_RES_MESSAGE
)))
3589 req
->data
.speeddialreq
.speedDialNumber
= htolel(instance
);
3590 snprintf(req
->data
.speeddial
.speedDialDirNumber
, sizeof(req
->data
.speeddial
.speedDialDirNumber
), sd
->exten
);
3591 snprintf(req
->data
.speeddial
.speedDialDisplayName
, sizeof(req
->data
.speeddial
.speedDialDisplayName
), sd
->label
);
3593 transmit_response(s
, req
);
3597 static int handle_line_state_req_message(struct skinny_req
*req
, struct skinnysession
*s
)
3599 struct skinny_device
*d
= s
->device
;
3600 struct skinny_line
*l
;
3603 instance
= letohl(req
->data
.line
.lineNumber
);
3605 ast_mutex_lock(&devicelock
);
3607 l
= find_line_by_instance(d
, instance
);
3613 ast_mutex_unlock(&devicelock
);
3615 if (!(req
= req_alloc(sizeof(struct line_stat_res_message
), LINE_STAT_RES_MESSAGE
)))
3618 req
->data
.linestat
.lineNumber
= letohl(instance
);
3619 memcpy(req
->data
.linestat
.lineDirNumber
, l
->name
,
3620 sizeof(req
->data
.linestat
.lineDirNumber
));
3621 memcpy(req
->data
.linestat
.lineDisplayName
, l
->label
,
3622 sizeof(req
->data
.linestat
.lineDisplayName
));
3623 transmit_response(s
,req
);
3627 static int handle_time_date_req_message(struct skinny_req
*req
, struct skinnysession
*s
)
3632 if (!(req
= req_alloc(sizeof(struct definetimedate_message
), DEFINETIMEDATE_MESSAGE
)))
3636 cmtime
= localtime(&timer
);
3637 req
->data
.definetimedate
.year
= htolel(cmtime
->tm_year
+1900);
3638 req
->data
.definetimedate
.month
= htolel(cmtime
->tm_mon
+1);
3639 req
->data
.definetimedate
.dayofweek
= htolel(cmtime
->tm_wday
);
3640 req
->data
.definetimedate
.day
= htolel(cmtime
->tm_mday
);
3641 req
->data
.definetimedate
.hour
= htolel(cmtime
->tm_hour
);
3642 req
->data
.definetimedate
.minute
= htolel(cmtime
->tm_min
);
3643 req
->data
.definetimedate
.seconds
= htolel(cmtime
->tm_sec
);
3644 req
->data
.definetimedate
.milliseconds
= htolel(0);
3645 req
->data
.definetimedate
.timestamp
= htolel(timer
);
3646 transmit_response(s
, req
);
3650 static int handle_button_template_req_message(struct skinny_req
*req
, struct skinnysession
*s
)
3652 struct skinny_device
*d
= s
->device
;
3653 struct skinny_line
*l
;
3656 struct skinny_speeddial
*sd
;
3657 struct button_definition_template btn
[42];
3658 int lineInstance
= 1;
3659 int speeddialInstance
= 1;
3660 int buttonCount
= 0;
3662 if (!(req
= req_alloc(sizeof(struct button_template_res_message
), BUTTON_TEMPLATE_RES_MESSAGE
)))
3665 memset(&btn
, 0, sizeof(btn
));
3667 get_button_template(s
, btn
);
3669 for (i
=0; i
<42; i
++) {
3671 switch (btn
[i
].buttonDefinition
) {
3672 case BT_CUST_LINESPEEDDIAL
:
3673 /* assume failure */
3674 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= BT_NONE
;
3675 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= htolel(0);
3677 for (l
= d
->lines
; l
; l
= l
->next
) {
3678 if (l
->instance
== lineInstance
) {
3679 ast_verbose("Adding button: %d, %d\n", BT_LINE
, lineInstance
);
3680 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= BT_LINE
;
3681 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= htolel(lineInstance
);
3690 for (sd
= d
->speeddials
; sd
; sd
= sd
->next
) {
3691 if (sd
->instance
== speeddialInstance
) {
3692 ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL
, speeddialInstance
);
3693 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= BT_SPEEDDIAL
;
3694 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= htolel(speeddialInstance
);
3695 speeddialInstance
++;
3704 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= htolel(BT_NONE
);
3705 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= htolel(0);
3707 for (l
= d
->lines
; l
; l
= l
->next
) {
3708 if (l
->instance
== lineInstance
) {
3709 ast_verbose("Adding button: %d, %d\n", BT_LINE
, lineInstance
);
3710 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= BT_LINE
;
3711 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= htolel(lineInstance
);
3720 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= BT_NONE
;
3721 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= 0;
3723 for (sd
= d
->speeddials
; sd
; sd
= sd
->next
) {
3724 if (sd
->instance
== speeddialInstance
) {
3725 ast_verbose("Adding button: %d, %d\n", BT_SPEEDDIAL
, speeddialInstance
);
3726 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= BT_SPEEDDIAL
;
3727 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= htolel(speeddialInstance
);
3728 speeddialInstance
++;
3740 ast_verbose("Adding button: %d, %d\n", btn
[i
].buttonDefinition
, 0);
3741 req
->data
.buttontemplate
.definition
[i
].buttonDefinition
= htolel(btn
[i
].buttonDefinition
);
3742 req
->data
.buttontemplate
.definition
[i
].instanceNumber
= htolel(0);
3749 req
->data
.buttontemplate
.buttonOffset
= htolel(0);
3750 req
->data
.buttontemplate
.buttonCount
= htolel(buttonCount
);
3751 req
->data
.buttontemplate
.totalButtonCount
= htolel(buttonCount
);
3754 ast_verbose("Sending %d template to %s\n",
3757 transmit_response(s
, req
);
3761 static int handle_version_req_message(struct skinny_req
*req
, struct skinnysession
*s
)
3763 struct skinny_device
*d
= s
->device
;
3764 if (!(req
= req_alloc(sizeof(struct version_res_message
), VERSION_RES_MESSAGE
)))
3767 snprintf(req
->data
.version
.version
, sizeof(req
->data
.version
.version
), d
->version_id
);
3768 transmit_response(s
, req
);
3772 static int handle_server_request_message(struct skinny_req
*req
, struct skinnysession
*s
)
3774 struct skinny_device
*d
= s
->device
;
3775 if (!(req
= req_alloc(sizeof(struct server_res_message
), SERVER_RES_MESSAGE
)))
3778 memcpy(req
->data
.serverres
.server
[0].serverName
, ourhost
,
3779 sizeof(req
->data
.serverres
.server
[0].serverName
));
3780 req
->data
.serverres
.serverListenPort
[0] = htolel(ourport
);
3781 req
->data
.serverres
.serverIpAddr
[0] = htolel(d
->ourip
.s_addr
);
3782 transmit_response(s
, req
);
3786 static int handle_alarm_message(struct skinny_req
*req
, struct skinnysession
*s
)
3788 /* no response necessary */
3790 ast_verbose("Received Alarm Message: %s\n", req
->data
.alarm
.displayMessage
);
3795 static int handle_open_receive_channel_ack_message(struct skinny_req
*req
, struct skinnysession
*s
)
3797 struct skinny_device
*d
= s
->device
;
3798 struct skinny_line
*l
;
3799 struct skinny_subchannel
*sub
;
3800 struct ast_format_list fmt
;
3801 struct sockaddr_in sin
;
3802 struct sockaddr_in us
;
3808 status
= letohl(req
->data
.openreceivechannelack
.status
);
3810 ast_log(LOG_ERROR
, "Open Receive Channel Failure\n");
3813 addr
= letohl(req
->data
.openreceivechannelack
.ipAddr
);
3814 port
= letohl(req
->data
.openreceivechannelack
.port
);
3815 passthruid
= letohl(req
->data
.openreceivechannelack
.passThruId
);
3817 sin
.sin_family
= AF_INET
;
3818 sin
.sin_addr
.s_addr
= addr
;
3819 sin
.sin_port
= htons(port
);
3821 sub
= find_subchannel_by_reference(d
, passthruid
);
3829 ast_rtp_set_peer(sub
->rtp
, &sin
);
3830 ast_rtp_get_us(sub
->rtp
, &us
);
3832 ast_log(LOG_ERROR
, "No RTP structure, this is very bad\n");
3837 ast_verbose("ipaddr = %s:%d\n", ast_inet_ntoa(sin
.sin_addr
), ntohs(sin
.sin_port
));
3838 ast_verbose("ourip = %s:%d\n", ast_inet_ntoa(d
->ourip
), ntohs(us
.sin_port
));
3841 if (!(req
= req_alloc(sizeof(struct start_media_transmission_message
), START_MEDIA_TRANSMISSION_MESSAGE
)))
3844 fmt
= ast_codec_pref_getsize(&l
->prefs
, ast_best_codec(l
->capability
));
3847 ast_verbose("Setting payloadType to '%d' (%d ms)\n", fmt
.bits
, fmt
.cur_ms
);
3849 req
->data
.startmedia
.conferenceId
= htolel(sub
->callid
);
3850 req
->data
.startmedia
.passThruPartyId
= htolel(sub
->callid
);
3851 req
->data
.startmedia
.remoteIp
= htolel(d
->ourip
.s_addr
);
3852 req
->data
.startmedia
.remotePort
= htolel(ntohs(us
.sin_port
));
3853 req
->data
.startmedia
.packetSize
= htolel(fmt
.cur_ms
);
3854 req
->data
.startmedia
.payloadType
= htolel(codec_ast2skinny(fmt
.bits
));
3855 req
->data
.startmedia
.qualifier
.precedence
= htolel(127);
3856 req
->data
.startmedia
.qualifier
.vad
= htolel(0);
3857 req
->data
.startmedia
.qualifier
.packets
= htolel(0);
3858 req
->data
.startmedia
.qualifier
.bitRate
= htolel(0);
3859 transmit_response(s
, req
);
3864 static int handle_enbloc_call_message(struct skinny_req
*req
, struct skinnysession
*s
)
3866 struct skinny_device
*d
= s
->device
;
3867 struct skinny_line
*l
;
3868 struct skinny_subchannel
*sub
= NULL
;
3869 struct ast_channel
*c
;
3873 ast_verbose("Received Enbloc Call: %s\n", req
->data
.enbloccallmessage
.calledParty
);
3875 sub
= find_subchannel_by_instance_reference(d
, d
->lastlineinstance
, d
->lastcallreference
);
3878 l
= find_line_by_instance(d
, d
->lastlineinstance
);
3886 c
= skinny_new(l
, AST_STATE_DOWN
);
3889 ast_log(LOG_WARNING
, "Unable to create channel for %s@%s\n", l
->name
, d
->name
);
3891 l
->hookstate
= SKINNY_OFFHOOK
;
3894 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
3896 ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l
->name
, d
->name
);
3897 transmit_displaymessage(s
, NULL
, l
->instance
, sub
->callid
); /* clear display */
3898 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
3900 if (!ast_ignore_pattern(c
->context
, req
->data
.enbloccallmessage
.calledParty
)) {
3901 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
3903 ast_copy_string(c
->exten
, req
->data
.enbloccallmessage
.calledParty
, sizeof(c
->exten
));
3904 if (ast_pthread_create(&t
, NULL
, skinny_newcall
, c
)) {
3905 ast_log(LOG_WARNING
, "Unable to create new call thread: %s\n", strerror(errno
));
3914 static int handle_soft_key_set_req_message(struct skinny_req
*req
, struct skinnysession
*s
)
3919 const struct soft_key_definitions
*softkeymode
= soft_key_default_definitions
;
3921 if (!(req
= req_alloc(sizeof(struct soft_key_set_res_message
), SOFT_KEY_SET_RES_MESSAGE
)))
3924 req
->data
.softkeysets
.softKeySetOffset
= htolel(0);
3925 req
->data
.softkeysets
.softKeySetCount
= htolel(11);
3926 req
->data
.softkeysets
.totalSoftKeySetCount
= htolel(11);
3927 for (x
= 0; x
< sizeof(soft_key_default_definitions
) / sizeof(struct soft_key_definitions
); x
++) {
3928 const uint8_t *defaults
= softkeymode
->defaults
;
3929 /* XXX I wanted to get the size of the array dynamically, but that wasn't wanting to work.
3930 This will have to do for now. */
3931 for (y
= 0; y
< softkeymode
->count
; y
++) {
3932 for (i
= 0; i
< (sizeof(soft_key_template_default
) / sizeof(struct soft_key_template_definition
)); i
++) {
3933 if (defaults
[y
] == i
+1) {
3934 req
->data
.softkeysets
.softKeySetDefinition
[softkeymode
->mode
].softKeyTemplateIndex
[y
] = htolel(i
+1);
3935 req
->data
.softkeysets
.softKeySetDefinition
[softkeymode
->mode
].softKeyInfoIndex
[y
] = htolel(i
+301);
3941 transmit_response(s
,req
);
3942 transmit_selectsoftkeys(s
, 0, 0, KEYDEF_ONHOOK
);
3946 static int handle_soft_key_event_message(struct skinny_req
*req
, struct skinnysession
*s
)
3948 struct skinny_device
*d
= s
->device
;
3949 struct skinny_line
*l
;
3950 struct skinny_subchannel
*sub
= NULL
;
3951 struct ast_channel
*c
;
3957 event
= letohl(req
->data
.softkeyeventmessage
.softKeyEvent
);
3958 instance
= letohl(req
->data
.softkeyeventmessage
.instance
);
3959 callreference
= letohl(req
->data
.softkeyeventmessage
.callreference
);
3962 l
= find_line_by_instance(d
, instance
);
3963 if (callreference
) {
3964 sub
= find_subchannel_by_instance_reference(d
, instance
, callreference
);
3966 sub
= find_subchannel_by_instance_reference(d
, instance
, d
->lastcallreference
);
3969 l
= find_line_by_instance(d
, d
->lastlineinstance
);
3974 ast_verbose("Received Softkey Event: %d(%d/%d)\n", event
, instance
, callreference
);
3981 ast_verbose("Received Softkey Event: None(%d/%d)\n", instance
, callreference
);
3983 case SOFTKEY_REDIAL
:
3985 ast_verbose("Received Softkey Event: Redial(%d/%d)\n", instance
, callreference
);
3988 if (!sub
|| !sub
->owner
) {
3989 c
= skinny_new(l
, AST_STATE_DOWN
);
3995 ast_log(LOG_WARNING
, "Unable to create channel for %s@%s\n", l
->name
, d
->name
);
3998 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
4000 ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l
->name
, d
->name
);
4001 transmit_displaymessage(s
, NULL
, l
->instance
, sub
->callid
); /* clear display */
4002 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
4003 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_RINGOUT
);
4005 if (!ast_ignore_pattern(c
->context
, l
->lastnumberdialed
)) {
4006 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
4008 ast_copy_string(c
->exten
, l
->lastnumberdialed
, sizeof(c
->exten
));
4009 if (ast_pthread_create(&t
, NULL
, skinny_newcall
, c
)) {
4010 ast_log(LOG_WARNING
, "Unable to create new call thread: %s\n", strerror(errno
));
4016 case SOFTKEY_NEWCALL
: /* Actually the DIAL softkey */
4018 ast_verbose("Received Softkey Event: New Call(%d/%d)\n", instance
, callreference
);
4020 if (!sub
|| !sub
->owner
) {
4021 c
= skinny_new(l
, AST_STATE_DOWN
);
4027 ast_log(LOG_WARNING
, "Unable to create channel for %s@%s\n", l
->name
, d
->name
);
4030 if (l
->hookstate
== SKINNY_ONHOOK
) {
4031 l
->hookstate
= SKINNY_OFFHOOK
;
4032 transmit_speaker_mode(s
, SKINNY_SPEAKERON
);
4033 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
4037 ast_verbose("Attempting to Clear display on Skinny %s@%s\n", l
->name
, d
->name
);
4038 transmit_displaymessage(s
, NULL
, l
->instance
, sub
->callid
); /* clear display */
4039 transmit_tone(s
, SKINNY_DIALTONE
, l
->instance
, sub
->callid
);
4040 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_OFFHOOK
);
4042 /* start the switch thread */
4043 if (ast_pthread_create(&t
, NULL
, skinny_ss
, c
)) {
4044 ast_log(LOG_WARNING
, "Unable to create switch thread: %s\n", strerror(errno
));
4051 ast_verbose("Received Softkey Event: Hold(%d/%d)\n", instance
, callreference
);
4062 case SOFTKEY_TRNSFER
:
4064 ast_verbose("Received Softkey Event: Transfer(%d/%d)\n", instance
, callreference
);
4065 /* XXX figure out how to transfer */
4067 case SOFTKEY_CFWDALL
:
4069 ast_verbose("Received Softkey Event: Forward All(%d/%d)\n", instance
, callreference
);
4071 /* Do not disturb */
4073 if (option_verbose
> 2)
4074 ast_verbose(VERBOSE_PREFIX_3
"Disabling DND on %s@%s\n", l
->name
, d
->name
);
4076 transmit_lamp_indication(s
, STIMULUS_FORWARDALL
, 1, SKINNY_LAMP_ON
);
4077 transmit_displaynotify(s
, "DnD disabled", 10);
4079 if (option_verbose
> 2)
4080 ast_verbose(VERBOSE_PREFIX_3
"Enabling DND on %s@%s\n", l
->name
, d
->name
);
4082 transmit_lamp_indication(s
, STIMULUS_FORWARDALL
, 1, SKINNY_LAMP_OFF
);
4083 transmit_displaynotify(s
, "DnD enabled", 10);
4086 case SOFTKEY_CFWDBUSY
:
4088 ast_verbose("Received Softkey Event: Forward Busy (%d/%d)\n", instance
, callreference
);
4090 case SOFTKEY_CFWDNOANSWER
:
4092 ast_verbose("Received Softkey Event: Forward No Answer (%d/%d)\n", instance
, callreference
);
4096 ast_verbose("Received Softkey Event: Backspace(%d/%d)\n", instance
, callreference
);
4098 case SOFTKEY_ENDCALL
:
4100 ast_verbose("Received Softkey Event: End Call(%d/%d)\n", instance
, callreference
);
4102 if (l
->hookstate
== SKINNY_ONHOOK
) {
4103 /* Something else already put us back on hook */
4107 sub
->cxmode
= SKINNY_CX_RECVONLY
;
4108 l
->hookstate
= SKINNY_ONHOOK
;
4109 transmit_callstate(s
, l
->instance
, l
->hookstate
, sub
->callid
);
4111 ast_verbose("Skinny %s@%s went on hook\n", l
->name
, d
->name
);
4112 if (l
->transfer
&& (sub
->owner
&& sub
->next
&& sub
->next
->owner
) && ((!sub
->outgoing
) || (sub
->next
&& !sub
->next
->outgoing
))) {
4113 /* We're allowed to transfer, we have two active calls and
4114 we made at least one of the calls. Let's try and transfer */
4117 if ((res
= attempt_transfer(p
)) < 0) {
4118 if (sub
->next
&& sub
->next
->owner
) {
4119 sub
->next
->alreadygone
= 1;
4120 ast_queue_hangup(sub
->next
->owner
, 1);
4123 ast_log(LOG_WARNING
, "Transfer attempt failed\n");
4128 /* Hangup the current call */
4129 /* If there is another active call, skinny_hangup will ring the phone with the other call */
4131 sub
->alreadygone
= 1;
4132 ast_queue_hangup(sub
->owner
);
4134 ast_log(LOG_WARNING
, "Skinny(%s@%s-%d) channel already destroyed\n",
4135 l
->name
, d
->name
, sub
->callid
);
4138 if ((l
->hookstate
== SKINNY_ONHOOK
) && (sub
->next
&& !sub
->next
->rtp
)) {
4143 case SOFTKEY_RESUME
:
4145 ast_verbose("Received Softkey Event: Resume(%d/%d)\n", instance
, callreference
);
4147 case SOFTKEY_ANSWER
:
4149 ast_verbose("Received Softkey Event: Answer(%d/%d)\n", instance
, callreference
);
4151 transmit_ringer_mode(s
,SKINNY_RING_OFF
);
4152 transmit_lamp_indication(s
, STIMULUS_LINE
, l
->instance
, SKINNY_LAMP_ON
);
4154 l
->hookstate
= SKINNY_OFFHOOK
;
4156 if (sub
&& sub
->outgoing
) {
4157 /* We're answering a ringing call */
4158 ast_queue_control(sub
->owner
, AST_CONTROL_ANSWER
);
4159 transmit_callstate(s
, l
->instance
, SKINNY_OFFHOOK
, sub
->callid
);
4160 transmit_tone(s
, SKINNY_SILENCE
, l
->instance
, sub
->callid
);
4161 transmit_callstate(s
, l
->instance
, SKINNY_CONNECTED
, sub
->callid
);
4162 transmit_selectsoftkeys(s
, l
->instance
, sub
->callid
, KEYDEF_CONNECTED
);
4164 ast_setstate(sub
->owner
, AST_STATE_UP
);
4169 ast_verbose("Received Softkey Event: Info(%d/%d)\n", instance
, callreference
);
4171 case SOFTKEY_CONFRN
:
4173 ast_verbose("Received Softkey Event: Conference(%d/%d)\n", instance
, callreference
);
4174 /* XXX determine the best way to pull off a conference. Meetme? */
4178 ast_verbose("Received Softkey Event: Park Call(%d/%d)\n", instance
, callreference
);
4179 /* XXX Park the call */
4183 ast_verbose("Received Softkey Event: Join(%d/%d)\n", instance
, callreference
);
4185 case SOFTKEY_MEETME
:
4186 /* XXX How is this different from CONFRN? */
4188 ast_verbose("Received Softkey Event: Meetme(%d/%d)\n", instance
, callreference
);
4190 case SOFTKEY_PICKUP
:
4192 ast_verbose("Received Softkey Event: Pickup(%d/%d)\n", instance
, callreference
);
4194 case SOFTKEY_GPICKUP
:
4196 ast_verbose("Received Softkey Event: Group Pickup(%d/%d)\n", instance
, callreference
);
4200 ast_verbose("Received unknown Softkey Event: %d(%d/%d)\n", event
, instance
, callreference
);
4206 static int handle_unregister_message(struct skinny_req
*req
, struct skinnysession
*s
)
4208 return skinny_unregister(req
, s
);
4211 static int handle_soft_key_template_req_message(struct skinny_req
*req
, struct skinnysession
*s
)
4213 if (!(req
= req_alloc(sizeof(struct soft_key_template_res_message
), SOFT_KEY_TEMPLATE_RES_MESSAGE
)))
4216 req
->data
.softkeytemplate
.softKeyOffset
= htolel(0);
4217 req
->data
.softkeytemplate
.softKeyCount
= htolel(sizeof(soft_key_template_default
) / sizeof(struct soft_key_template_definition
));
4218 req
->data
.softkeytemplate
.totalSoftKeyCount
= htolel(sizeof(soft_key_template_default
) / sizeof(struct soft_key_template_definition
));
4219 memcpy(req
->data
.softkeytemplate
.softKeyTemplateDefinition
,
4220 soft_key_template_default
,
4221 sizeof(soft_key_template_default
));
4222 transmit_response(s
,req
);
4226 static int handle_headset_status_message(struct skinny_req
*req
, struct skinnysession
*s
)
4228 /* XXX umm...okay? Why do I care? */
4232 static int handle_register_available_lines_message(struct skinny_req
*req
, struct skinnysession
*s
)
4234 /* XXX I have no clue what this is for, but my phone was sending it, so... */
4238 static int handle_message(struct skinny_req
*req
, struct skinnysession
*s
)
4241 struct skinny_device
*d
= s
->device
;
4242 struct skinny_subchannel
*sub
;
4246 if ((!s
->device
) && (letohl(req
->e
) != REGISTER_MESSAGE
&& letohl(req
->e
) != ALARM_MESSAGE
)) {
4247 ast_log(LOG_WARNING
, "Client sent message #%d without first registering.\n", req
->e
);
4252 switch(letohl(req
->e
)) {
4253 case KEEP_ALIVE_MESSAGE
:
4254 res
= handle_keep_alive_message(req
, s
);
4256 case REGISTER_MESSAGE
:
4258 ast_verbose("Device %s is attempting to register\n", req
->data
.reg
.name
);
4260 res
= handle_register_message(req
, s
);
4262 case IP_PORT_MESSAGE
:
4263 res
= handle_ip_port_message(req
, s
);
4265 case KEYPAD_BUTTON_MESSAGE
:
4267 ast_verbose("Collected digit: [%d]\n", letohl(req
->data
.keypad
.button
));
4269 lineInstance
= letohl(req
->data
.keypad
.lineInstance
);
4270 callReference
= letohl(req
->data
.keypad
.callReference
);
4272 sub
= find_subchannel_by_instance_reference(d
, lineInstance
, callReference
);
4274 if (sub
&& (sub
->owner
&& sub
->owner
->_state
< AST_STATE_UP
)) {
4276 int digit
= letohl(req
->data
.keypad
.button
);
4281 } else if (digit
== 15) {
4283 } else if (digit
>= 0 && digit
<= 9) {
4286 /* digit=10-13 (A,B,C,D ?), or
4287 * digit is bad value
4289 * probably should not end up here, but set
4290 * value for backward compatibility, and log
4294 ast_log(LOG_WARNING
, "Unsupported digit %d\n", digit
);
4297 len
= strlen(d
->exten
);
4298 if (len
< sizeof(d
->exten
) - 1) {
4299 d
->exten
[len
] = dgt
;
4300 d
->exten
[len
+1] = '\0';
4302 ast_log(LOG_WARNING
, "Dropping digit with value %d because digit queue is full\n", dgt
);
4305 res
= handle_keypad_button_message(req
, s
);
4307 case ENBLOC_CALL_MESSAGE
:
4308 res
= handle_enbloc_call_message(req
, s
);
4310 case STIMULUS_MESSAGE
:
4311 res
= handle_stimulus_message(req
, s
);
4313 case OFFHOOK_MESSAGE
:
4314 res
= handle_offhook_message(req
, s
);
4316 case ONHOOK_MESSAGE
:
4317 res
= handle_onhook_message(req
, s
);
4319 case CAPABILITIES_RES_MESSAGE
:
4321 ast_verbose("Received CapabilitiesRes\n");
4323 res
= handle_capabilities_res_message(req
, s
);
4325 case SPEED_DIAL_STAT_REQ_MESSAGE
:
4327 ast_verbose("Received SpeedDialStatRequest\n");
4329 res
= handle_speed_dial_stat_req_message(req
, s
);
4331 case LINE_STATE_REQ_MESSAGE
:
4333 ast_verbose("Received LineStatRequest\n");
4334 res
= handle_line_state_req_message(req
, s
);
4336 case TIME_DATE_REQ_MESSAGE
:
4338 ast_verbose("Received Time/Date Request\n");
4340 res
= handle_time_date_req_message(req
, s
);
4342 case BUTTON_TEMPLATE_REQ_MESSAGE
:
4344 ast_verbose("Buttontemplate requested\n");
4346 res
= handle_button_template_req_message(req
, s
);
4348 case VERSION_REQ_MESSAGE
:
4350 ast_verbose("Version Request\n");
4352 res
= handle_version_req_message(req
, s
);
4354 case SERVER_REQUEST_MESSAGE
:
4356 ast_verbose("Received Server Request\n");
4358 res
= handle_server_request_message(req
, s
);
4361 res
= handle_alarm_message(req
, s
);
4363 case OPEN_RECEIVE_CHANNEL_ACK_MESSAGE
:
4365 ast_verbose("Received Open Receive Channel Ack\n");
4367 res
= handle_open_receive_channel_ack_message(req
, s
);
4369 case SOFT_KEY_SET_REQ_MESSAGE
:
4371 ast_verbose("Received SoftKeySetReq\n");
4373 res
= handle_soft_key_set_req_message(req
, s
);
4375 case SOFT_KEY_EVENT_MESSAGE
:
4376 res
= handle_soft_key_event_message(req
, s
);
4378 case UNREGISTER_MESSAGE
:
4380 ast_verbose("Received Unregister Request\n");
4382 res
= handle_unregister_message(req
, s
);
4384 case SOFT_KEY_TEMPLATE_REQ_MESSAGE
:
4386 ast_verbose("Received SoftKey Template Request\n");
4388 res
= handle_soft_key_template_req_message(req
, s
);
4390 case HEADSET_STATUS_MESSAGE
:
4391 res
= handle_headset_status_message(req
, s
);
4393 case REGISTER_AVAILABLE_LINES_MESSAGE
:
4394 res
= handle_register_available_lines_message(req
, s
);
4398 ast_verbose("RECEIVED UNKNOWN MESSAGE TYPE: %x\n", letohl(req
->e
));
4401 if (res
>= 0 && req
)
4406 static void destroy_session(struct skinnysession
*s
)
4408 struct skinnysession
*cur
, *prev
= NULL
;
4409 ast_mutex_lock(&sessionlock
);
4420 prev
->next
= cur
->next
;
4422 sessions
= cur
->next
;
4427 ast_mutex_destroy(&s
->lock
);
4430 ast_log(LOG_WARNING
, "Trying to delete nonexistent session %p?\n", s
);
4432 ast_mutex_unlock(&sessionlock
);
4435 static int get_input(struct skinnysession
*s
)
4439 struct pollfd fds
[1];
4442 fds
[0].events
= POLLIN
;
4444 res
= poll(fds
, 1, (keep_alive
* 1100)); /* If nothing has happen, client is dead */
4445 /* we add 10% to the keep_alive to deal */
4446 /* with network delays, etc */
4448 if (errno
!= EINTR
) {
4449 ast_log(LOG_WARNING
, "Select returned error: %s\n", strerror(errno
));
4452 } else if (res
== 0) {
4454 ast_verbose("Skinny Client was lost, unregistering\n");
4455 skinny_unregister(NULL
, s
);
4459 if (fds
[0].revents
) {
4460 ast_mutex_lock(&s
->lock
);
4461 memset(s
->inbuf
,0,sizeof(s
->inbuf
));
4462 res
= read(s
->fd
, s
->inbuf
, 4);
4464 ast_log(LOG_WARNING
, "read() returned error: %s\n", strerror(errno
));
4467 ast_verbose("Skinny Client was lost, unregistering\n");
4469 skinny_unregister(NULL
,s
);
4470 ast_mutex_unlock(&s
->lock
);
4472 } else if (res
!= 4) {
4473 ast_log(LOG_WARNING
, "Skinny Client sent less data than expected. Expected 4 but got %d.\n", res
);
4474 ast_mutex_unlock(&s
->lock
);
4478 ast_verbose("Skinny Client was lost, unregistering\n");
4479 skinny_unregister(NULL
, s
);
4485 dlen
= letohl(*(int *)s
->inbuf
);
4487 ast_log(LOG_WARNING
, "Skinny Client sent invalid data.\n");
4488 ast_mutex_unlock(&s
->lock
);
4491 if (dlen
+8 > sizeof(s
->inbuf
)) {
4492 dlen
= sizeof(s
->inbuf
) - 8;
4494 *(int *)s
->inbuf
= htolel(dlen
);
4496 res
= read(s
->fd
, s
->inbuf
+4, dlen
+4);
4497 ast_mutex_unlock(&s
->lock
);
4499 ast_log(LOG_WARNING
, "read() returned error: %s\n", strerror(errno
));
4501 } else if (res
!= (dlen
+4)) {
4502 ast_log(LOG_WARNING
, "Skinny Client sent less data than expected.\n");
4510 static struct skinny_req
*skinny_req_parse(struct skinnysession
*s
)
4512 struct skinny_req
*req
;
4514 if (!(req
= ast_calloc(1, SKINNY_MAX_PACKET
)))
4517 ast_mutex_lock(&s
->lock
);
4518 memcpy(req
, s
->inbuf
, skinny_header_size
);
4519 memcpy(&req
->data
, s
->inbuf
+skinny_header_size
, letohl(*(int*)(s
->inbuf
))-4);
4521 ast_mutex_unlock(&s
->lock
);
4523 if (letohl(req
->e
) < 0) {
4524 ast_log(LOG_ERROR
, "Event Message is NULL from socket %d, This is bad\n", s
->fd
);
4532 static void *skinny_session(void *data
)
4535 struct skinny_req
*req
;
4536 struct skinnysession
*s
= data
;
4538 if (option_verbose
> 2)
4539 ast_verbose(VERBOSE_PREFIX_3
"Starting Skinny session from %s\n", ast_inet_ntoa(s
->sin
.sin_addr
));
4549 if (!(req
= skinny_req_parse(s
))) {
4554 res
= handle_message(req
, s
);
4561 ast_log(LOG_NOTICE
, "Skinny Session returned: %s\n", strerror(errno
));
4569 static void *accept_thread(void *ignore
)
4572 struct sockaddr_in sin
;
4574 struct skinnysession
*s
;
4577 pthread_attr_t attr
;
4578 pthread_t tcp_thread
;
4580 pthread_attr_init(&attr
);
4581 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
4584 sinlen
= sizeof(sin
);
4585 as
= accept(skinnysock
, (struct sockaddr
*)&sin
, &sinlen
);
4587 ast_log(LOG_NOTICE
, "Accept returned -1: %s\n", strerror(errno
));
4590 p
= getprotobyname("tcp");
4592 if( setsockopt(as
, p
->p_proto
, TCP_NODELAY
, (char *)&arg
, sizeof(arg
) ) < 0 ) {
4593 ast_log(LOG_WARNING
, "Failed to set Skinny tcp connection to TCP_NODELAY mode: %s\n", strerror(errno
));
4596 if (!(s
= ast_calloc(1, sizeof(struct skinnysession
))))
4599 memcpy(&s
->sin
, &sin
, sizeof(sin
));
4600 ast_mutex_init(&s
->lock
);
4602 ast_mutex_lock(&sessionlock
);
4605 ast_mutex_unlock(&sessionlock
);
4607 if (ast_pthread_create(&tcp_thread
, &attr
, skinny_session
, s
)) {
4612 ast_verbose("killing accept thread\n");
4614 pthread_attr_destroy(&attr
);
4618 static void *do_monitor(void *data
)
4622 /* This thread monitors all the interfaces which are not yet in use
4623 (and thus do not have a separate thread) indefinitely */
4624 /* From here on out, we die whenever asked */
4626 pthread_testcancel();
4627 /* Wait for sched or io */
4628 res
= ast_sched_wait(sched
);
4629 if ((res
< 0) || (res
> 1000)) {
4632 res
= ast_io_wait(io
, res
);
4633 ast_mutex_lock(&monlock
);
4635 ast_sched_runq(sched
);
4637 ast_mutex_unlock(&monlock
);
4644 static int restart_monitor(void)
4646 /* If we're supposed to be stopped -- stay stopped */
4647 if (monitor_thread
== AST_PTHREADT_STOP
)
4650 ast_mutex_lock(&monlock
);
4651 if (monitor_thread
== pthread_self()) {
4652 ast_mutex_unlock(&monlock
);
4653 ast_log(LOG_WARNING
, "Cannot kill myself\n");
4656 if (monitor_thread
!= AST_PTHREADT_NULL
) {
4657 /* Wake up the thread */
4658 pthread_kill(monitor_thread
, SIGURG
);
4660 /* Start a new monitor */
4661 if (ast_pthread_create_background(&monitor_thread
, NULL
, do_monitor
, NULL
) < 0) {
4662 ast_mutex_unlock(&monlock
);
4663 ast_log(LOG_ERROR
, "Unable to start monitor thread.\n");
4667 ast_mutex_unlock(&monlock
);
4671 static struct ast_channel
*skinny_request(const char *type
, int format
, void *data
, int *cause
)
4675 struct skinny_line
*l
;
4676 struct ast_channel
*tmpc
= NULL
;
4682 if (!(format
&= ((AST_FORMAT_MAX_AUDIO
<< 1) - 1))) {
4683 ast_log(LOG_NOTICE
, "Asked to get a channel of unsupported format '%d'\n", format
);
4687 ast_copy_string(tmp
, dest
, sizeof(tmp
));
4688 if (ast_strlen_zero(tmp
)) {
4689 ast_log(LOG_NOTICE
, "Skinny channels require a device\n");
4692 l
= find_line_by_name(tmp
);
4694 ast_log(LOG_NOTICE
, "No available lines on: %s\n", dest
);
4697 if (option_verbose
> 2) {
4698 ast_verbose(VERBOSE_PREFIX_3
"skinny_request(%s)\n", tmp
);
4700 tmpc
= skinny_new(l
, AST_STATE_DOWN
);
4702 ast_log(LOG_WARNING
, "Unable to make channel for '%s'\n", tmp
);
4708 static int reload_config(void)
4711 struct ast_config
*cfg
;
4712 struct ast_variable
*v
;
4714 struct skinny_device
*d
;
4715 int oldport
= ntohs(bindaddr
.sin_port
);
4717 if (gethostname(ourhost
, sizeof(ourhost
))) {
4718 ast_log(LOG_WARNING
, "Unable to get hostname, Skinny disabled\n");
4721 cfg
= ast_config_load(config
);
4723 /* We *must* have a config file otherwise stop immediately */
4725 ast_log(LOG_NOTICE
, "Unable to load config %s, Skinny disabled\n", config
);
4728 memset(&bindaddr
, 0, sizeof(bindaddr
));
4729 memset(&default_prefs
, 0, sizeof(default_prefs
));
4731 /* Copy the default jb config over global_jbconf */
4732 memcpy(&global_jbconf
, &default_jbconf
, sizeof(struct ast_jb_conf
));
4734 /* load the general section */
4735 v
= ast_variable_browse(cfg
, "general");
4737 /* handle jb conf */
4738 if (!ast_jb_read_conf(&global_jbconf
, v
->name
, v
->value
)) {
4743 /* Create the interface list */
4744 if (!strcasecmp(v
->name
, "bindaddr")) {
4745 if (!(hp
= ast_gethostbyname(v
->value
, &ahp
))) {
4746 ast_log(LOG_WARNING
, "Invalid address: %s\n", v
->value
);
4748 memcpy(&bindaddr
.sin_addr
, hp
->h_addr
, sizeof(bindaddr
.sin_addr
));
4750 } else if (!strcasecmp(v
->name
, "keepalive")) {
4751 keep_alive
= atoi(v
->value
);
4752 } else if (!strcasecmp(v
->name
, "dateformat")) {
4753 memcpy(date_format
, v
->value
, sizeof(date_format
));
4754 } else if (!strcasecmp(v
->name
, "allow")) {
4755 ast_parse_allow_disallow(&default_prefs
, &default_capability
, v
->value
, 1);
4756 } else if (!strcasecmp(v
->name
, "disallow")) {
4757 ast_parse_allow_disallow(&default_prefs
, &default_capability
, v
->value
, 0);
4758 } else if (!strcasecmp(v
->name
, "bindport") || !strcasecmp(v
->name
, "port")) {
4759 if (sscanf(v
->value
, "%d", &ourport
) == 1) {
4760 bindaddr
.sin_port
= htons(ourport
);
4762 ast_log(LOG_WARNING
, "Invalid bindport '%s' at line %d of %s\n", v
->value
, v
->lineno
, config
);
4764 if (!strcasecmp(v
->name
, "port")) { /*! \todo Remove 'port' option after 1.4 */
4765 ast_log(LOG_WARNING
, "Option 'port' at line %d of %s has been deprecated. Please use 'bindport' instead.\n", v
->lineno
, config
);
4771 if (ntohl(bindaddr
.sin_addr
.s_addr
)) {
4772 __ourip
= bindaddr
.sin_addr
;
4774 hp
= ast_gethostbyname(ourhost
, &ahp
);
4776 ast_log(LOG_WARNING
, "Unable to get our IP address, Skinny disabled\n");
4777 ast_config_destroy(cfg
);
4780 memcpy(&__ourip
, hp
->h_addr
, sizeof(__ourip
));
4782 if (!ntohs(bindaddr
.sin_port
)) {
4783 bindaddr
.sin_port
= ntohs(DEFAULT_SKINNY_PORT
);
4785 bindaddr
.sin_family
= AF_INET
;
4787 /* load the device sections */
4788 cat
= ast_category_browse(cfg
, NULL
);
4790 if (!strcasecmp(cat
, "general")) {
4793 } else if (!strncasecmp(cat
, "paging-", 7)) {
4794 p
= build_paging_device(cat
, ast_variable_browse(cfg
, cat
));
4799 d
= build_device(cat
, ast_variable_browse(cfg
, cat
));
4801 if (option_verbose
> 2)
4802 ast_verbose(VERBOSE_PREFIX_3
"Added device '%s'\n", d
->name
);
4803 ast_mutex_lock(&devicelock
);
4806 ast_mutex_unlock(&devicelock
);
4809 cat
= ast_category_browse(cfg
, cat
);
4811 ast_mutex_lock(&netlock
);
4812 if ((skinnysock
> -1) && (ntohs(bindaddr
.sin_port
) != oldport
)) {
4816 if (skinnysock
< 0) {
4817 skinnysock
= socket(AF_INET
, SOCK_STREAM
, 0);
4818 if(setsockopt(skinnysock
, SOL_SOCKET
, SO_REUSEADDR
, &on
, sizeof(on
)) == -1) {
4819 ast_log(LOG_ERROR
, "Set Socket Options failed: errno %d, %s\n", errno
, strerror(errno
));
4820 ast_config_destroy(cfg
);
4821 ast_mutex_unlock(&netlock
);
4824 if (skinnysock
< 0) {
4825 ast_log(LOG_WARNING
, "Unable to create Skinny socket: %s\n", strerror(errno
));
4827 if (bind(skinnysock
, (struct sockaddr
*)&bindaddr
, sizeof(bindaddr
)) < 0) {
4828 ast_log(LOG_WARNING
, "Failed to bind to %s:%d: %s\n",
4829 ast_inet_ntoa(bindaddr
.sin_addr
), ntohs(bindaddr
.sin_port
),
4833 ast_config_destroy(cfg
);
4834 ast_mutex_unlock(&netlock
);
4837 if (listen(skinnysock
,DEFAULT_SKINNY_BACKLOG
)) {
4838 ast_log(LOG_WARNING
, "Failed to start listening to %s:%d: %s\n",
4839 ast_inet_ntoa(bindaddr
.sin_addr
), ntohs(bindaddr
.sin_port
),
4843 ast_config_destroy(cfg
);
4844 ast_mutex_unlock(&netlock
);
4847 if (option_verbose
> 1)
4848 ast_verbose(VERBOSE_PREFIX_2
"Skinny listening on %s:%d\n",
4849 ast_inet_ntoa(bindaddr
.sin_addr
), ntohs(bindaddr
.sin_port
));
4850 ast_pthread_create_background(&accept_t
,NULL
, accept_thread
, NULL
);
4853 ast_mutex_unlock(&netlock
);
4854 ast_config_destroy(cfg
);
4858 static void delete_devices(void)
4860 struct skinny_device
*d
, *dlast
;
4861 struct skinny_line
*l
, *llast
;
4862 struct skinny_speeddial
*sd
, *sdlast
;
4863 struct skinny_addon
*a
, *alast
;
4865 ast_mutex_lock(&devicelock
);
4867 /* Delete all devices */
4868 for (d
=devices
;d
;) {
4869 /* Delete all lines for this device */
4870 for (l
=d
->lines
;l
;) {
4873 ast_mutex_destroy(&llast
->lock
);
4876 /* Delete all speeddials for this device */
4877 for (sd
=d
->speeddials
;sd
;) {
4880 ast_mutex_destroy(&sdlast
->lock
);
4883 /* Delete all addons for this device */
4884 for (a
=d
->addons
;a
;) {
4887 ast_mutex_destroy(&alast
->lock
);
4895 ast_mutex_unlock(&devicelock
);
4900 * XXX This never worked properly anyways.
4901 * Let's get rid of it, until we can fix it.
4903 static int reload(void)
4912 static int load_module(void)
4916 for (; res
< (sizeof(soft_key_template_default
) / sizeof(soft_key_template_default
[0])); res
++) {
4917 soft_key_template_default
[res
].softKeyEvent
= htolel(soft_key_template_default
[res
].softKeyEvent
);
4919 /* load and parse config */
4920 res
= reload_config();
4922 return AST_MODULE_LOAD_DECLINE
;
4925 /* Make sure we can register our skinny channel type */
4926 if (ast_channel_register(&skinny_tech
)) {
4927 ast_log(LOG_ERROR
, "Unable to register channel class 'Skinny'\n");
4931 ast_rtp_proto_register(&skinny_rtp
);
4932 ast_cli_register_multiple(cli_skinny
, sizeof(cli_skinny
) / sizeof(struct ast_cli_entry
));
4933 sched
= sched_context_create();
4935 ast_log(LOG_WARNING
, "Unable to create schedule context\n");
4937 io
= io_context_create();
4939 ast_log(LOG_WARNING
, "Unable to create I/O context\n");
4941 /* And start the monitor for the first time */
4947 static int unload_module(void)
4949 struct skinnysession
*s
, *slast
;
4950 struct skinny_device
*d
;
4951 struct skinny_line
*l
;
4952 struct skinny_subchannel
*sub
;
4954 ast_mutex_lock(&sessionlock
);
4955 /* Destroy all the interfaces and free their memory */
4960 for (d
= slast
->device
; d
; d
= d
->next
) {
4961 for (l
= d
->lines
; l
; l
= l
->next
) {
4962 ast_mutex_lock(&l
->lock
);
4963 for (sub
= l
->sub
; sub
; sub
= sub
->next
) {
4964 ast_mutex_lock(&sub
->lock
);
4966 sub
->alreadygone
= 1;
4967 ast_softhangup(sub
->owner
, AST_SOFTHANGUP_APPUNLOAD
);
4969 ast_mutex_unlock(&sub
->lock
);
4971 ast_mutex_unlock(&l
->lock
);
4976 ast_mutex_destroy(&slast
->lock
);
4980 ast_mutex_unlock(&sessionlock
);
4984 ast_mutex_lock(&monlock
);
4985 if ((monitor_thread
!= AST_PTHREADT_NULL
) && (monitor_thread
!= AST_PTHREADT_STOP
)) {
4986 pthread_cancel(monitor_thread
);
4987 pthread_kill(monitor_thread
, SIGURG
);
4988 pthread_join(monitor_thread
, NULL
);
4990 monitor_thread
= AST_PTHREADT_STOP
;
4991 ast_mutex_unlock(&monlock
);
4993 ast_mutex_lock(&netlock
);
4994 if (accept_t
&& (accept_t
!= AST_PTHREADT_STOP
)) {
4995 pthread_cancel(accept_t
);
4996 pthread_kill(accept_t
, SIGURG
);
4997 pthread_join(accept_t
, NULL
);
4999 accept_t
= AST_PTHREADT_STOP
;
5000 ast_mutex_unlock(&netlock
);
5002 ast_rtp_proto_unregister(&skinny_rtp
);
5003 ast_channel_unregister(&skinny_tech
);
5004 ast_cli_unregister_multiple(cli_skinny
, sizeof(cli_skinny
) / sizeof(struct ast_cli_entry
));
5008 sched_context_destroy(sched
);
5013 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, "Skinny Client Control Protocol (Skinny)",
5014 .load
= load_module
,
5015 .unload
= unload_module
,