Mod skinny
From FreeSWITCH Wiki
Synopsis
SCCP (or Skinny), is a ToIP protocol used in Cisco products like Cisco IP Phones 79xx.
For moreinfo, see Skinny Call Control Protocol on Wikipedia.
Table of Contents
Contents |
Features
- Features:
- Incoming and outgoing calls (early media supported)
- Shared lines and multiple calls per lines
- CallInfo set as early as possible
- New call made from: OffHook, Softkeys (Redial, NewCall), Stimulus (LastNumberRedial, VoiceMail, SpeedDial)
- Answering calls via: OffHook, Softkeys (Answer)
- Holding/Unholding calls via Softkeys (Hold, Resume) or Stimulus (Hold), with MOH (music on hold)
- Transfer via Softkeys (Transfer) or Stimulus (Transfer). Blind transfer (with OnHook)
- Ending call via: OnHook, Softkeys (EndCall)
- Misc: SoftKeys (Backspace)
- Handle firmware version request (VersionReqMessage) per device type or per device
- Patterns for dialed numbers to process (patterns-dialplan and patterns-context)
- Custom applications can be made using the API and events: See #Custom applications
- Custom Events: See #Custom Events
- API Commands: See #API Commands
- Message Waiting indicator (MWI) on first line of each device
- Working on x86 and x86_64
Missing features: mod_skinny/Development#TODO
Interop List
This list is incomplete, please add any tested SCCP phone.
| Model | DeviceTypeId | Status |
|---|---|---|
| Cisco IP Phone 7910 | 6 | OK(1) |
| Cisco IP Phone 7940 | 8 | OK(2) |
| Cisco IP Phone 7960 | 7 | OK(2) |
| Cisco IP Phone 7961 | 30018 | OK |
| Cisco IP Phone 7975 | 437 | OK |
| Nokia Call Connect 2.0 | ? | Not tested(3) |
Footnotes:
- The six buttons (msgs, conf, forward, speed 1, speed 2 and redial) have button positions 5 to 10 respectively.
- Also tested with Cisco Unified IP Phone Expansion Module 7914: adds 14 definable buttons with working lights. Button number starts with 3 for the 7940 and 7 for the 7960.
- Mobile Business Solutions From Cisco and Nokia Downloads
"DeviceTypeId" can be found when issuing on CLI :
skinny status profile internal device <your_device>
Building
There is no additional dependency needed to build mod_skinny. You just need to edit modules.conf (uncomment endpoints/mod_skinny) and run:
make mod_skinny
Don't forget to uncomment <load module="mod_skinny"/> in ~/freeswitch/conf/auto_configs/modules.conf.xml
Configuration
- 192.168.0.2 is the IP of the TFTP server
- 192.168.0.3 is the IP of the FreeSWITCH server (with mod_skinny)
- SEP001120AABBCC is the phone id (based on the MAC address)
DHCP and TFTP Configuration
This is widely discussed at voip-info.org (http://www.voip-info.org/wiki/view/Asterisk+phone+cisco+79xx), here is a quick overview.
DHCP
First, the phone get its IP and other network configs through DHCP.
The important setting is the tftp:
- in udhcp (the most lightweight) , this is set in /etc/udhcpd.conf:
opt tftp 192.168.0.2
- in ISC DHCP (widely used), this is set in /etc/dhcp3/dhcpd.conf:
option tftp-server-name 192.168.0.2;
TFTP
There are plenty of TFTP server implementations. In the root of the tftp directory (usually /srv/tftp), put the following files:
- XMLDefault.cnf.xml
<Default>
<callManagerGroup>
<members>
<member priority="0">
<callManager>
<ports>
<ethernetPhonePort>2000</ethernetPhonePort>
</ports>
<processNodeName>192.168.0.3</processNodeName>
</callManager>
</member>
</members>
</callManagerGroup>
<loadInformation30002 model="Cisco 7960">P00307010100</loadInformation30002>
</Default>
- SEP001120AABBCC.cnf.xml (minimal)
<device>
<devicePool>
<callManagerGroup>
<members>
<member priority="0">
<callManager>
<ports>
<ethernetPhonePort>2000</ethernetPhonePort>
</ports>
<processNodeName>192.168.0.3</processNodeName>
</callManager>
</member>
</members>
</callManagerGroup>
</devicePool>
</device>
- SEP001120AABBCC.cnf.xml (extended)
<device>
<devicePool>
<callManagerGroup>
<members>
<member priority="0">
<callManager>
<ports>
<ethernetPhonePort>2000</ethernetPhonePort>
</ports>
<processNodeName>192.168.0.3</processNodeName>
</callManager>
</member>
</members>
</callManagerGroup>
<dateTimeSetting>
<dateTemplate>M/D/YA</dateTemplate>
<timeZone>Central Europe Standard/Daylight Time</timeZone>
</dateTimeSetting>
</devicePool>
<versionStamp>{Jan 20 01 00:00:00}</versionStamp>
<userLocale>
<name>French_France</name>
<uid>2</uid>
<langCode>fr</langCode>
<version>4.0(1)SR1FRA</version>
<winCharSet>iso-8859-1</winCharSet>
</userLocale>
<networkLocale>France</networkLocale>
<networkLocaleInfo>
<name>France</name>
<uid>14</uid>
<version>4.0(1)SR1</version>
</networkLocaleInfo>
<idleTimeout>0</idleTimeout>
<authenticationURL></authenticationURL>
<directoryURL></directoryURL>
<idleURL></idleURL>
<informationURL></informationURL>
<messagesURL></messagesURL>
<proxyServerURL></proxyServerURL>
<servicesURL></servicesURL>
</device>
Notes :
- dateTemplate seems to be ignored. Use date-format from the profile configuration
- timeZone are listed on http://www.cisco.com/en/US/docs/voice_ip_comm/cucme/command/reference/cme_t1ht.html
- You need the corresponding files on your TFTP server if you are using userLocale and networkLocale. More info on the Cisco website (You must have an account on Cisco.com to download locale files)
- All *URL options may point to a Cisco 79XX XML Service
FreeSWITCH Configuration
The configuration is loaded from autolad_configs (nothing exciting here). Nothing to change here.
- conf/autoload_configs/skinny.conf.xml:
<configuration name="skinny.conf" description="Skinny Profiles">
<profiles>
<X-PRE-PROCESS cmd="include" data="../skinny_profiles/*.xml"/>
</profiles>
</configuration>
Profile Configurations
Each profile is put in conf/skinny_profiles. The default configuration should work without modifications.
- conf/skinny_profiles/internal.xml
<profile name="internal">
<settings>
<param name="domain" value="$${domain}"/>
<param name="ip" value="$${local_ip_v4}"/>
<param name="port" value="2000"/>
<param name="patterns-dialplan" value="XML"/>
<param name="patterns-context" value="skinny-patterns"/>
<param name="dialplan" value="XML"/>
<param name="context" value="default"/>
<param name="keep-alive" value="60"/>
<param name="date-format" value="D/M/Y"/>
<param name="odbc-dsn" value=""/>
<param name="debug" value="4"/>
<param name="auto-restart" value="true"/>
</settings>
<soft-key-set-sets>
<soft-key-set-set name="default">
<soft-key-set name="KeySetOnHook" value="SoftkeyNewcall,SoftkeyRedial"/>
<soft-key-set name="KeySetConnected" value="SoftkeyEndcall,SoftkeyHold,SoftkeyNewcall,SoftkeyTransfer"/>
<soft-key-set name="KeySetOnHold" value="SoftkeyNewcall,SoftkeyResume,SoftkeyEndcall"/>
<soft-key-set name="KeySetRingIn" value="SoftkeyAnswer,SoftkeyEndcall,SoftkeyNewcall"/>
<soft-key-set name="KeySetOffHook" value=",SoftkeyRedial,SoftkeyEndcall"/>
<soft-key-set name="KeySetConnectedWithTransfer" value="SoftkeyEndcall,SoftkeyHold,SoftkeyNewcall,SoftkeyTransfer"/>
<soft-key-set name="KeySetDigitsAfterDialingFirstDigit" value="SoftkeyBackspace,,SoftkeyEndcall"/>
<!-- <soft-key-set name="KeySetConnectedWithConference" value=""/> -->
<soft-key-set name="KeySetRingOut" value=",,SoftkeyEndcall,SoftkeyTransfer"/>
<soft-key-set name="KeySetOffHookWithFeatures" value=",SoftkeyRedial,SoftkeyEndcall"/>
<soft-key-set name="KeySetInUseHint" value="SoftkeyNewcall,SoftkeyRedial"/>
</soft-key-set-set>
</soft-key-set-sets>
<device-types>
<device-type id="Cisco ATA 186">
<param name="firmware-version" value="ATA030101SCCP04"/>
</device-type>
</device-types>
</profile>
NB :
- if ip="", mod_skinny will listen on all IP addresses. Il you want to force IPv6, use ip="::"
- mod_skinny logs a lot under [DEBUG]: Every skinny message received or sent. keepAlive and keepAliveAck messages are only logged if profile's debug is >=10.
- when issuing a reloadxml, those values are not reloaded. Use skinny profile <profile_name> set <name> <value> instead.
- You mostly don't have to bother about firmware-versions unless your phone ask it through skinny (most Cisco phones use XML files from tftp). If you have missed one, mod_skinny will complain in the log:
[DEBUG] skinny_server.c:1501 Device SEP001120AABBCC:0 is requesting for firmware version, but none is set.
- The auto-restart param is used when network interface ip changes. If set to true, the socket will be closed (including all connected listeners) and reopened on the new IP.
Device Configuration
Devices are set up as users in directory. The critical parameter here is id, which corresponds to the MAC address.
- conf/directory/default/skinny-example.xml
<include>
<user id="SEP001120AABBCC">
<params>
<!-- for devices requesting firmware via SCCP, like ATA186
<param name="skinny-firmware-version" value="ATA030101SCCP04"
<param name="skinny-soft-key-set-set" value="default"
-->
<param name="foo" value="bar"/>
</params>
<skinny>
<buttons>
<!--
position: 1..
type: one of line, speed-dial
label: button label
-->
<!--
value is the directory number (or user)
caller-name is shown to the calling party during call
-->
<button position="1" type="Line" label="Line 1" value="1101" caller-name="Calling as 1101"/>
<button position="3" type="Line" label="Shared Line 10" value="1110" caller-name="Calling as 1110"/>
<!--
value is the directory number to call
-->
<button position="5" type="SpeedDial" label="Call 1001" value="1001"/>
<!--
value is the URL
-->
<button position="6" type="ServiceUrl" label="Some URL" value="http://phone-xml.berbee.com/menu.xml"/>
</buttons>
</skinny>
</user>
</include>
More information on ServiceUrls at http://www.voip-info.org/wiki/view/Asterisk+Cisco+79XX+XML+Services
Patterns Dialplan configuration
This dialplan is used when calling from a skinny phone. Default configuration will allow you to call SIP phones and some tests like the tetris one (9998).
As all the call process is made on the server, we need a way to know how many digits are necessary to route the call. Executing the dialplan each time a digit is pressed is not correct. An other dialplan context is used to simply say what mod_skinny should do:
- conf/dialplan/skinny-patterns.xml
<?xml version="1.0" encoding="utf-8"?>
<!--
NOTICE:
This context is used for skinny to match dialed number
The special applications:
- skinny-route tells skinny to route the call
- skinny-drop tells skinny to drop the call
- skinny-wait tells skinny to wait 'data' seconds for more digits before drop
-->
<!-- http://wiki.freeswitch.org/wiki/Mod_skinny -->
<include>
<context name="skinny-patterns">
<!--
Wait 10 seconds for another digit by default
-->
<extension name="Default">
<condition>
<action application="skinny-wait" data="10"/>
</condition>
</extension>
<!--
You can place files in the skinny-patterns directory to get included.
-->
<X-PRE-PROCESS cmd="include" data="skinny-patterns/*.xml"/>
</context>
</include>
You can select the diaplan context by setting patterns-dialplan and patterns-context in the skinny profile configuration.
Dialplan configuration
This dialplan snippet is necessary when you need to call a skinny phone. Without this, you will not be able to call your skinny phones!
Add something like this to conf/dialplan/default.xml (Example with 11xx):
<extension name="Local_Extension_Skinny">
<condition field="destination_number" expression="^(11[01][0-9])$">
<action application="bridge" data="skinny/internal/${destination_number}"/>
</condition>
</extension>
Outgoing Channel Syntax
Basic form:
skinny/<profile>/<number>
API Commands
Note: most of the command arguments can be auto completed on CLI by using tab (for example <ring_type>).
USAGE: -------------------------------------------------------------------------------- skinny help skinny status profile <profile_name> skinny status profile <profile_name> device <device_name> skinny profile <profile_name> device <device_name> send ResetMessage [DeviceReset|DeviceRestart] skinny profile <profile_name> device <device_name> send SetRingerMessage <ring_type> <ring_mode> skinny profile <profile_name> device <device_name> send SetLampMessage <stimulus> <instance> <lamp_mode> skinny profile <profile_name> device <device_name> send SetSpeakerModeMessage <speaker_mode> skinny profile <profile_name> device <device_name> send CallStateMessage <call_state> <line_instance> <call_id> skinny profile <profile_name> device <device_name> send <UserToDeviceDataMessage|UserToDeviceDataVersion1Message> [ <param>=<value>;... ] <data> skinny profile <profile_name> set <name> <value> --------------------------------------------------------------------------------
(More TODO)
Custom Events
skinny::register
Event-Subclass: skinny%3A%3Aregister Event-Name: CUSTOM Core-UUID: 1f9d861e-e5cc-4b5c-b32d-9a7a1dd0b540 FreeSWITCH-Hostname: netthieu FreeSWITCH-IPv4: 192.168.0.3 FreeSWITCH-IPv6: %3A%3A1 Event-Date-Local: 2010-09-24%2002%3A19%3A16 Event-Date-GMT: Fri,%2024%20Sep%202010%2000%3A19%3A16%20GMT Event-Date-Timestamp: 1285287556518629 Event-Calling-File: skinny_protocol.c Event-Calling-Function: skinny_device_event Event-Calling-Line-Number: 224 Skinny-Profile-Name: internal Skinny-Device-Name: SEP001120AABBCC Skinny-Station-User-Id: 0 Skinny-Station-Instance: 0 Skinny-IP-Address: 192.168.0.4 Skinny-Device-Type: 30018 Skinny-Max-Streams: 5 Skinny-Port: (null) Skinny-Codecs: _undef_
skinny::unregister
Event-Subclass: skinny%3A%3Aunregister Event-Name: CUSTOM Core-UUID: 1f9d861e-e5cc-4b5c-b32d-9a7a1dd0b540 FreeSWITCH-Hostname: netthieu FreeSWITCH-IPv4: 192.168.0.3 FreeSWITCH-IPv6: %3A%3A1 Event-Date-Local: 2010-09-24%2002%3A18%3A39 Event-Date-GMT: Fri,%2024%20Sep%202010%2000%3A18%3A39%20GMT Event-Date-Timestamp: 1285287519036825 Event-Calling-File: skinny_protocol.c Event-Calling-Function: skinny_device_event Event-Calling-Line-Number: 224 Skinny-Profile-Name: internal Skinny-Device-Name: SEP001120AABBCC Skinny-Station-User-Id: 0 Skinny-Station-Instance: 0 Skinny-IP-Address: 192.168.0.4 Skinny-Device-Type: 30018 Skinny-Max-Streams: 5 Skinny-Port: 3500 Skinny-Codecs: WIDEBAND,ULAW,ALAW,G729,G729,G729,G729,RFC2833_DYNPAYLOAD
skinny::expire
skinny::alarm
Event-Subclass: skinny%3A%3Aalarm Event-Name: CUSTOM Core-UUID: 1f9d861e-e5cc-4b5c-b32d-9a7a1dd0b540 FreeSWITCH-Hostname: netthieu FreeSWITCH-IPv4: 192.168.0.3 FreeSWITCH-IPv6: %3A%3A1 Event-Date-Local: 2010-09-24%2002%3A19%3A16 Event-Date-GMT: Fri,%2024%20Sep%202010%2000%3A19%3A16%20GMT Event-Date-Timestamp: 1285287556312672 Event-Calling-File: skinny_protocol.c Event-Calling-Function: skinny_device_event Event-Calling-Line-Number: 224 Skinny-Alarm-Severity: 2 Skinny-Alarm-DisplayMessage: 22%3A%20Nom%SEP001120AABBCC%20Image%3D%20SCCP41.8-3-1S%20Dernier%3DR%E9init.-R%E9init. Skinny-Alarm-Param1: 0 Skinny-Alarm-Param2: 0
skinny::call_state
this is for internal use and might change in the future.
skinny::user_to_device
Event-Subclass: skinny%3A%3Auser_to_device Event-Name: CUSTOM Core-UUID: 1f9d861e-e5cc-4b5c-b32d-9a7a1dd0b540 FreeSWITCH-Hostname: netthieu FreeSWITCH-IPv4: 192.168.0.3 FreeSWITCH-IPv6: %3A%3A1 Event-Date-Local: 2010-09-24%2002%3A10%3A01 Event-Date-GMT: Fri,%2024%20Sep%202010%2000%3A10%3A01%20GMT Event-Date-Timestamp: 1285287001570252 Event-Calling-File: skinny_protocol.c Event-Calling-Function: skinny_device_event Event-Calling-Line-Number: 224 Skinny-Profile-Name: internal Skinny-Device-Name: SEP001120AABBCC Skinny-Station-User-Id: 0 Skinny-Station-Instance: 0 Skinny-IP-Address: 192.168.0.4 Skinny-Device-Type: 30018 Skinny-Max-Streams: 5 Skinny-Port: 3500 Skinny-Codecs: WIDEBAND,ULAW,ALAW,G729,G729,G729,G729,RFC2833_DYNPAYLOAD Skinny-UserToDevice-Message-Id-String: UserToDeviceDataVersion1Message Skinny-UserToDevice-sequence-flag: 2 Content-Length: 111 <CiscoIPPhoneText><Title>Hello</Title><Prompt>Amazing</Prompt><Text>This is some text</Text></CiscoIPPhoneText>
skinny::device_to_user
Event-Subclass: skinny%3A%3Adevice_to_user Event-Name: CUSTOM Core-UUID: 1f9d861e-e5cc-4b5c-b32d-9a7a1dd0b540 FreeSWITCH-Hostname: netthieu FreeSWITCH-IPv4: 192.168.0.3 FreeSWITCH-IPv6: %3A%3A1 Event-Date-Local: 2010-09-24%2002%3A10%3A01 Event-Date-GMT: Fri,%2024%20Sep%202010%2000%3A10%3A01%20GMT Event-Date-Timestamp: 1285287001696990 Event-Calling-File: skinny_protocol.c Event-Calling-Function: skinny_device_event Event-Calling-Line-Number: 224 Skinny-Profile-Name: internal Skinny-Device-Name: SEP001120AABBCC Skinny-Station-User-Id: 0 Skinny-Station-Instance: 0 Skinny-IP-Address: 192.168.0.4 Skinny-Device-Type: 30018 Skinny-Max-Streams: 5 Skinny-Port: 3500 Skinny-Codecs: WIDEBAND,ULAW,ALAW,G729,G729,G729,G729,RFC2833_DYNPAYLOAD Skinny-DeviceToUser-Message-Id: 66 Skinny-DeviceToUser-Message-Id-String: DeviceToUserDataResponseVersion1Message Skinny-DeviceToUser-Application-Id: 0 Skinny-DeviceToUser-Line-Instance: 0 Skinny-DeviceToUser-Call-Id: 0 Skinny-DeviceToUser-Transaction-Id: 0 Skinny-DeviceToUser-Data-Length: 172 Skinny-DeviceToUser-Sequence-Flag: 2 Skinny-DeviceToUser-Display-Priority: 0 Skinny-DeviceToUser-Conference-Id: 0 Skinny-DeviceToUser-App-Instance-Id: 0 Skinny-DeviceToUser-Routing-Id: 0 Content-Length: 171 <?xml version="1.0" encoding="iso8859-1"?> <CiscoIPPhoneResponse> <ResponseItem URL="cip.xml.XmlTextObject@e771b" Data="SUCCESS" Status="0" /> </CiscoIPPhoneResponse>
skinny::xml_alarm
Event-Subclass: skinny%3A%3Axml_alarm Event-Name: CUSTOM Core-UUID: a4aaa78c-9cfe-4e15-abb8-52e222a361d8 FreeSWITCH-Hostname: servthieu FreeSWITCH-IPv4: 192.168.0.3 FreeSWITCH-IPv6: %3A%3A1 Event-Date-Local: 2010-12-19%2022%3A10%3A43 Event-Date-GMT: Sun,%2019%20Dec%202010%2021%3A10%3A43%20GMT Event-Date-Timestamp: 1292793043629627 Event-Calling-File: skinny_protocol.c Event-Calling-Function: skinny_device_event Event-Calling-Line-Number: 224 Content-Length: 1232 <?xml version="1.0" encoding="UTF-8"?> <x-cisco-alarm> <Alarm Name="LastOutOfServiceInformation"> <ParameterList> <String name="DeviceName">SEP001120AABBCC</String> <String name="DeviceIPv4Address">192.168.0.4</String> <String name="IPv4DefaultGateway">192.168.0.254</String> <String name="DeviceIPv6Address"></String> <String name="IPv6DefaultGateway"></String> <String name="ModelNumber">CP-7961G</String> <String name="NeighborIPv4Address">192.168.0.253</String> <String name="NeighborIPv6Address"></String> <String name="NeighborDeviceID">sw2.local</String> <String name="NeighborPortID">3</String> <Enum name="DHCPv4Status">1</Enum> <Enum name="DHCPv6Status">0</Enum> <Enum name="TFTPCfgStatus">0</Enum> <Enum name="DNSStatusUnifiedCM1">0</Enum> <Enum name="DNSStatusUnifiedCM2">0</Enum> <Enum name="DNSStatusUnifiedCM3">0</Enum> <String name="VoiceVLAN">4095</String> <String name="UnifiedCMIPAddress"><not open></String> <String name="LocalPort">-1</String> <String name="TimeStamp">1289313813826</String> <Enum name="ReasonForOutOfService"></Enum> <String name="LastProtocolEventSent">1:Register</String> <String name="LastProtocolEventReceived">129:RegisterAck</String> </ParameterList> </Alarm> </x-cisco-alarm>
More
If you want one more, file a bug.
Custom applications
If you want to write a custom application, you can control the phone by using the API. For example:
skinny profile internal device SEP001120AABBCC send UserToDeviceDataVersion1Message sequence-flag=2 '<CiscoIPPhoneText><Title>Hello</Title><Prompt>Amazing</Prompt><Text>This is some text</Text></CiscoIPPhoneText>'
You can also register to event skinny::device_to_user (and others).
Related documentation:
- Complete reference to CiscoIPPhone XML Objects from Cisco's Cisco Unified IP Phone Services Application Development Notes, Release 8.5(1)
- Cisco 79XX XML Services from voip-info.org
- Cisco Unified Communications Manager XML Developers Guide, Release 8.5(1)
Example implementations :
- There is an example XML Directory for Cisco IP Phones PHP application in freeswitch-contrib.git/sathieu/xml-directory.
- Perl's Cisco::IPPhone
- http://fisheye.freeswitch.org/browse/freeswitch-contrib/sathieu/cisco-xml

