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