Panasonic KX-TDA PBX support for Linux

Why and how

I needed to integrate the Panasonic KX-TDA30 PBX with our custom-made Linux-based CRM system to receive information about incoming calls and other events (to find the calling number in the database and display information about the customer that called us). Unfortunately there was only Windows TSP driver available and other Windows-based software communicated with that driver using TAPI.
(note: you'll need also an USB driver to use TSP)

I tried to contact Panasonic, but people I was talking to weren't too technical and offered only typical sales help. I was unable to reach anybody who could (or wanted, or was permitted to) explain the communication protocol.

So I decided to do some reverse-engineering. I installed an USB sniffer (SnoopyPro, local copy of version 0.22) and began watching the traffic exchanged by Blitz Caller ID (which uses Panasonic TSP) and the PBX. I quickly realized the simple fragmentation protocol and then it took some time to discover, that the rest of the packet is encoded using ASN.1. Then on Blitz user forum I found the magic keyword - "CSTA" - and everything became clear.

USB communication

The PBX exchanges data with PC using 64-byte packets. Following USB Linux driver enables access to the PBX through /dev/usb/skel0 character special file (the name 'skel' comes from 'USB skeleton' - an USB driver example included in Linux kernel, I haven't cleaned it up yet). Create that file using:

    mknod /dev/usb/skel0 c 180 192
Apply patch (this one was created for kernel 2.6.14.2, but should work with newer versions) and compile the kernel module kxtda.ko. Don't forget to load UHCI/OHCI module before the kxtda.ko! The driver probably contains a lot of bugs, but should be enough to see how communication with PBX works.

Updated 08.03.2016: kernel patch for 3.0.17 (an possibly newer) kernels

Packet fragmentation

Due to 64-byte USB packet size limitation larger packets are fragmented into smaller ones. USB packets may start with following bytes:

  • 0x00 - a small packet that wasn't fragmented, next byte contains packet length (counted from third byte)
  • 0x01 - first fragment, next byte contains fragment length (counted from fifth byte, i.e. it is usually equal to 0x3c), following byte is equal to 0x00 (like in a non-fragmented packet) and fourth byte contains total packet length (also counted from fifth byte)
  • 0x02 - intermediate fragments (not first and not last), next byte contains fragment length (counted from third byte, i.e. 0x3e)
  • 0x03 - last packet, next byte also contains fragment length counted from third byte

Example:

  • small 16-byte packet:

      0x00 0x10 ... (16 bytes of payload)

  • 140-byte packet:

      0x01 0x3c 0x00 0x8c ... (60 bytes of payload)
      0x02 0x3e ... (62 bytes of payload)
      0x03 0x12 ... (18 bytes of payload)

Next layer

The remaining payload starts with a byte that - as far as I observed - may have following values:

  • 0x00 - CSTA packets (call control, monitoring, etc.)
  • 0x02 - packets sent by KX-TDA Maintenance Console
Following byte contains the length of the payload behind this header, i.e. the total packet length minus 2.

CSTA

The rest of the packet contains CSTA message encoded using ASN.1. The best way to work with such messages is to download specification file and compile it using asn1c compiler (local copy of v0.9.20). The compiler generates a lot of files containing C structs, that can be used to decode and encode CSTA messages easily.

To generate these files I had to issue two commands:

  • asn1c -fcompound-names kxtda.asn1
  • asn1c -fcompound-names -fnative-types kxtda.asn1

The second one was necessary to simplify regular types (encode int as int, not as a complex struct), but it didn't create links to ENUMERATED.h and similar files, so the first command was also needed.

The specification file was downloaded from ECMA site as a part of ECMA-285 standard. Unfortunately asn1c compiler had some problems with name clashes and I had to copy only these definitions which I needed (so use kxtda.asn1 from the link in previous paragraph, but be aware that something may be missing). I also needed to add some ROSE protocol structures.

First the message has to be decoded as a ROIVapdu or similar ROSE structure containing sequence number (invokeID), upper layer identifier (serviceID) and payload (serviceArgs). Then depending on the serviceID the payload may be further decoded (as a monitoring packet for example).

Other packets I noticed that PBX sends a kind of keepalive packets (serviceID 211) in random time intervals (ca 60 s). You have to reply with specific packet, otherwise the PBX will stop sending monitoring events.

Example communication program

Following code opens USB port, initializes the PBX and starts monitoring of "101", "102", "103" and "104" extensions. It should be compiled using a Makefile based on an example generated by asn1c.

  • first we have to send an 'initialize' packet - I wasn't checking its exact meaning, I just noticed that it is sent by Windows driver and it works :)
  • then we have to read the reply, wait for the keepalive packet and DO NOT REPLY TO IT. I don't know why, perhaps I'll figure that out later - but if I sent a keepalive reply, then later the PBX didn't respond to my subsequent requests (like MonitorStart command).
  • send MonitorStart packets for each number you want to monitor. I don't know what exactly you can monitor except extension numbers, ringing groups, etc. (I wonder if I can monitor such events like power supply failure)
  • listen to incoming packets and reply to keepalive packets

The MonitorStart command is first encoded using MonitorStartArgument_t struct generated by asn1c compiler, then it is encapsulated into ROIVapdu_t struct (ROSE header containing sequence number increased by one with each packet sent).

Received packets are decoded in opposite direction - first stripped off the ROSE header (ROIVapdu_t), then (depending on the serviceID field in ROIVapdu) decoded using appropriate struct (CSTAEventReportArgument_t in case of monitoring events).

Example program output

Extension 101 calls 103:

[2006.06.08 11:31:24] ---- PBX -> PC ----
invokeID 17, serviceID 21
<CSTAEventReportArgument>
    <crossRefIdentifier>01 11 00 02</crossRefIdentifier>
    <eventSpecificInfo>
        <callControlEvents>
            <originated>
                <originatedConnection>
                    <both>
                        <callID>00ED</callID>
                        <deviceID>
                            <staticID>
                                <deviceIdentifier>
                                    <dialingNumber>101</dialingNumber>
                                </deviceIdentifier>
                            </staticID>
                        </deviceID>
                    </both>
                </originatedConnection>
                <callingDevice>
                    <deviceIdentifier>
                        <deviceIdentifier>
                            <dialingNumber>101</dialingNumber>
                        </deviceIdentifier>
                    </deviceIdentifier>
                </callingDevice>
                <calledDevice>
                    <deviceIdentifier>
                        <deviceIdentifier>
                            <dialingNumber>103</dialingNumber>
                        </deviceIdentifier>
                    </deviceIdentifier>
                </calledDevice>
                <localConnectionInfo><connected/></localConnectionInfo>
                <cause><newCall/></cause>
            </originated>
        </callControlEvents>
    </eventSpecificInfo>
</CSTAEventReportArgument>

Contact

If you have any comments or questions please write to piotr <at> tahoe.pl