Notes on the interface to the Actiontec IPW. Getting it going under Linux. Jamie Honan, jhonan (at) optushome.com.au. 3 Nov 2004. The Actiontec Internet Phone Wizard (IPW) is a USB telephony device. The device allows you to connect an ordinary telephone handset to your computer and make VoIP calls, using the receiver handset and dialing keypad. Making a VoIP call feels like a traditional phone call, rather than having to use a headset, and dial with a mouse and computer keyboard. One connector is for a telephone handset. Another, optional, is for the telephone line, or line to the telephone wall plug. The third is the USB connector. The device comes with no drivers, although you can download software that specifically supports it. The device is a standard USB audio device. When you plug it into your computer, the operating system detects it and automatically configures it for audio. Under Linux, you can use Gnomephone or Kphone, set the /dev/dsp device and use the handset to talk and listen. You still have to dial and answer VoIP calls using your computer keyboard and mouse. Similarly under Windows, you can download such packages as Xten Lite, configure and operate in much the same way as under Linux. The device is capable of more. Terracall have a modified version of Xten-lite that allows you to use the full capabilities of your handset; off and on hook, DTMF dialing, and flash hook -- also known as recall. Actiontec has an SDK for the device. I asked for a copy so I could write a Linux driver, with no response. Fortunately there is a simple way of detecting what the control sequences for the device are, and providing support for packages such as the SIP based ones like Kphone, and IAX ones such as Gnomephone and Asterisk. USB-Sniff is a piece of software that can log USB traffic in Windows. It's then a simple matter of matching the sniffer log to events. For the Actiontec, this is made easier by the simple protocol used and the fact that most events happen in the handset and are reported back to the host. For Linux, there is a library called libusb and tools such as usb-robot which allows user level interaction with a USB device. The first thing I tried is the Linux utility lsusb. This will display the configuration for the device. This is presented at the end of this report. The configuration shows that this is a standard USB audio device, with a microphone and speakers. The vendor id is 0x1668 and product id is 0x358. There is a third USB interface, non standard. This is the one we should claim when trying to use the handset signaling. The handset signaling is done with USB control transfer messages. To change registers, make the phone ring or other outgoing activities, we use request 0x40, index 0, value 0. Using libusb terminology, we'd use the function prototype: usb_control_msg(device, requesttype, request, value, index, buffer, len, timeout) To set values in the IPW we would call the function thus: usb_control_msg(device, 0x40, 0x40, 0, 0, 5, buf, timeout) Our buffer will be 5 bytes long, request 0x40 as indicated above and our requesttype will be USB_ENDPOINT_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE. To poll data from IPW we call the function thus: usb_control_msg(device, 0xc0, 0x41, 0, 0x200, 255, buf, timeout) Our buffer will be a maximum of 255 bytes, request 0x41, index 0x200, value 0. The requesttype 0xc0 consists of USB_ENDPOINT_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE. The return will be the length of buffer returned from the IPW, or negative for error. The buffer will be filled in. So what is the data that is transferred in the buffer? What follows now is some educated guess work, or detective surmising. It is enough to get the device going, but there may be more capabilities that I haven't been able to see. Messages in the buffer start with the character 0xf1. If there is nothing for the IPW to report for a poll, it will return a length of 1 and the character 0xf1 in the buffer. There may be multiple messages in a returned buffer. If there is data, a length byte will follow the 0xf1. This length byte indicates the length of this message packet including the length byte and the 0xf1 character. As I said, there may be multiple message packets returned in a poll buffer. Here is an example. The user has taken the handset off hook. This will generate the follow for the next poll: length : 10 bytes buffer : f1 05 01 0b 06 f1 05 01 00 01 There are two message packets. The first is to show that the user has lifted the receiver (off hook), the second is that the handset is considered connected to the computer, not the telephone line. The b/6 is off hook, 0/1 is connected to iphone. When the user places the receiver back (on hook), the following buffer will be returned for the next poll. length : 10 bytes buffer : f1 05 01 0b 07 f1 05 01 00 00 The 0b/07 is to show the handset going on-hook, the 0/0 is disconnect to the iphone. Here is a more complete description of message packets. All values hex. Outgoing: f1 05 01 29 01 This is used by windows software to initialise the device in some way. f1 05 01 04 01 This tells the device that we want to see keypad DTMF messages when we poll. Presumably 4/0 turns this off. At the end, the windows driver issues: f1 05 01 1a 1 which seems like a good idea to emulate! f1 05 01 1c 1 causes the phone to ring. 1c/0 stops the ringing. There are other values which cause things to happen. These I discovered by trial and error, by simply poking values. f1 05 01 18 1 and 18/0 seem to generate the following packet for the next poll: f1 14 00 08 00 11 08 00 09 00 0b 00 00 00 00 00 00 00 00 00 f1 05 01 21 (1 or 0) generates the following packet the next poll: f1 05 01 0b 00 f1 05 01 0b 03 (status report of device?) f1 05 01 27 1 and 27/0 produce the following for the next poll: f1 05 01 0e 89 POLL REPORTS: Remember, multiple message packets can come back in one buffer. OFF HOOK, no phone line plugged in: f1 05 01 0b 06 (b/6 off hook) f1 05 01 00 01 (0/1 connect to iphone - led comes on) ON HOOK, no phone line plugged in: f1 05 01 0b 07 (b/7 on hook) f1 05 01 00 00 (0/0 disconnect to iphone, led goes off) FLASH HOOK, RECALL f1 05 01 0b 06 (b/6 off hook) DTMF DIALLING These only come back if 4/1 has been issued - see above. f1 05 01 01 01 (This for 1 on keypad. 1/digit) Last number is digit. 0 is A * is B # is C Multiple DTMF packets will come in one buffer if the user has pressed several since the last poll. The device has other modes if the line is plugged in. The default behaviour is to connect the handset to the line. If the user issues two hash keys (##), then the device is connected to the iphone and a response to the poll is generated. Sequences: Plug line phone in: f1 05 01 0b 00 f1 05 01 0b 03 Off Hook with Line connected: f1 05 01 0b 06 f1 05 01 0b 02 On Hook with Line connected: f1 05 01 0b 03 f1 05 01 0b 07 Pull Line out, then go off hook: f1 05 01 0b 02 f1 05 01 0b 01 f1 05 01 0b 06 f1 05 01 00 01 Pull Line out, then pull out handset: f1 05 01 0b 02 f1 05 01 0b 01 Off Hook with Line connected: f1 05 01 0b 06 f1 05 01 0b 02 Then issue two '##' keys: f1 05 01 0c 00 f1 05 01 00 01 f1 05 01 0b 03 Strategies: keep polling until nothing returned. Issue 29/1, 4/1 Disconnected state: Keep polling until 0/1, Off hook state, or incoming call. Off hook state: Wait for 0/0 - hangup, disconnected state 1/* - DTMF digits, pass them on until connected state Connected: ignore DTMF b/6 = flash hook or recall, recall state 0/0 - hangup, disconnected state Incoming Call, issue ring 1c/1: 0/1 off hook, issue 1c/0, state Connected timeout, issue 1c/0, disconnected state APPENDIX A ========== lsusb output: Bus 004 Device 007: ID 1668:0358 Actiontec Electronics, Inc. [hex] Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 1.00 bDeviceClass 0 Interface bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 8 idVendor 0x1668 Actiontec Electronics, Inc. [hex] idProduct 0x0358 bcdDevice 1.00 iManufacturer 1 iProduct 2 iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 208 bNumInterfaces 4 bConfigurationValue 1 iConfiguration 0 bmAttributes 0x80 MaxPower 500mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 1 Audio bInterfaceSubClass 1 Control Device bInterfaceProtocol 0 iInterface 0 AudioControl Interface Descriptor: bLength 10 bDescriptorType 36 bDescriptorSubtype 1 (HEADER) bcdADC 1.00 wTotalLength 70 bInCollection 2 baInterfaceNr( 0) 1 baInterfaceNr( 1) 2 AudioControl Interface Descriptor: bLength 12 bDescriptorType 36 bDescriptorSubtype 2 (INPUT_TERMINAL) bTerminalID 1 wTerminalType 0x0101 USB Streaming bAssocTerminal 4 bNrChannels 1 wChannelConfig 0x0000 iChannelNames 0 iTerminal 0 AudioControl Interface Descriptor: bLength 12 bDescriptorType 36 bDescriptorSubtype 2 (INPUT_TERMINAL) bTerminalID 2 wTerminalType 0x0201 Microphone bAssocTerminal 3 bNrChannels 1 wChannelConfig 0x0000 iChannelNames 0 iTerminal 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 3 (OUTPUT_TERMINAL) bTerminalID 3 wTerminalType 0x0301 Speaker bAssocTerminal 2 bSourceID 6 iTerminal 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 3 (OUTPUT_TERMINAL) bTerminalID 4 wTerminalType 0x0101 USB Streaming bAssocTerminal 1 bSourceID 5 iTerminal 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 6 (FEATURE_UNIT) bUnitID 5 bSourceID 2 bControlSize 1 bmaControls( 0) 0x03 Mute Volume bmaControls( 0) 0x00 iFeature 0 AudioControl Interface Descriptor: bLength 9 bDescriptorType 36 bDescriptorSubtype 6 (FEATURE_UNIT) bUnitID 6 bSourceID 1 bControlSize 1 bmaControls( 0) 0x03 Mute Volume bmaControls( 0) 0x00 iFeature 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 1 bAlternateSetting 1 bNumEndpoints 1 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 0 AudioControl Interface Descriptor: bLength 7 bDescriptorType 36 bDescriptorSubtype 1 (AS_GENERAL) bTerminalLink 4 bDelay 1 frames wFormatTag 1 PCM AudioControl Interface Descriptor: bLength 11 bDescriptorType 36 bDescriptorSubtype 2 (FORMAT_TYPE) bFormatType 1 (FORMAT_TYPE_I) bNrChannels 1 bSubframeSize 2 bBitResolution 16 bSamFreqType 1 Discrete tSamFreq[ 0] 8000 Endpoint Descriptor: bLength 9 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 1 Transfer Type Isochronous Synch Type none wMaxPacketSize 16 bInterval 1 bRefresh 0 bSynchAddress 0 AudioControl Endpoint Descriptor: bLength 7 bDescriptorType 37 bDescriptorSubtype 1 (EP_GENERAL) bmAttributes 0x00 bLockDelayUnits 0 Undefined wLockDelay 0 Undefined Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 0 bNumEndpoints 0 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 0 Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 2 bAlternateSetting 1 bNumEndpoints 1 bInterfaceClass 1 Audio bInterfaceSubClass 2 Streaming bInterfaceProtocol 0 iInterface 0 AudioControl Interface Descriptor: bLength 7 bDescriptorType 36 bDescriptorSubtype 1 (AS_GENERAL) bTerminalLink 1 bDelay 1 frames wFormatTag 1 PCM AudioControl Interface Descriptor: bLength 11 bDescriptorType 36 bDescriptorSubtype 2 (FORMAT_TYPE) bFormatType 1 (FORMAT_TYPE_I) bNrChannels 1 bSubframeSize 2 bBitResolution 16 bSamFreqType 1 Discrete tSamFreq[ 0] 8000 Endpoint Descriptor: bLength 9 bDescriptorType 5 bEndpointAddress 0x02 EP 2 OUT bmAttributes 1 Transfer Type Isochronous Synch Type none wMaxPacketSize 16 bInterval 1 bRefresh 0 bSynchAddress 0 AudioControl Endpoint Descriptor: bLength 7 bDescriptorType 37 bDescriptorSubtype 1 (EP_GENERAL) bmAttributes 0x00 bLockDelayUnits 0 Undefined wLockDelay 0 Undefined Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 3 bAlternateSetting 0 bNumEndpoints 1 bInterfaceClass 0 Interface bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x83 EP 3 IN bmAttributes 3 Transfer Type Interrupt Synch Type none wMaxPacketSize 8 bInterval 5 Language IDs: (length=4) 0409 English(US) APPENDIX B ========== SAMPLE usb-robot-slave session: Lines starting with usb-robot> are commands I issued. $ ./usb-robot-slave vendor=0x1668 product=0x358 ./usb-robot-slave: starting usb-robot version 0.2.1pre (c) 2000, 2001 John Fremlin Licensed under the GNU Public License version 2, see file COPYING. You didn't pay me for this program. You have no rights. doing bus scan for: idVendor 0x1668 idProduct 0x358 found bus 005 scanning bus 005 device 001 on bus 005 does not match found bus 004 scanning bus 004 device 001 on bus 004 does not match found device 007 on bus 004 (idVendor 0x1668 idProduct 0x358) opening device 007 on bus 004 OK: id=0 Type help and press return for a list of commands usb-robot> interface 3 OK: id=1 usb-robot> encoding hex Output format changed to hex OK: id=2 usb-robot> decoding hex Input format changed to hex OK: id=3 usb-robot> transfer type=control dir=out requesttype=0x40 request=0x40 index=0 value=0 size=5 Enter data in format FF FB 00 FC f1 05 01 29 01 doing control message id 4 to device, size 5, timeout 10000 frames, 40:40:0:0 OK: id=4 usb-robot> transfer type=control dir=out requesttype=0x40 request=0x40 index=0 value=0 size=5 Enter data in format FF FB 00 FC f1 5 1 4 1 doing control message id 5 to device, size 5, timeout 10000 frames, 40:40:0:0 OK: id=5 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 6 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=6 length=10 0000: f1 05 01 0b 01 f1 05 01 0b 02 OK: id=6 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 7 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=7 length=1 0000: f1 OK: id=7 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 8 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=8 length=1 0000: f1 OK: id=8 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 9 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=9 length=10 0000: f1 05 01 0b 06 f1 05 01 00 01 OK: id=9 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 10 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=10 length=5 0000: f1 05 01 01 01 OK: id=10 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 11 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=11 length=1 0000: f1 OK: id=11 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 12 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=12 length=10 0000: f1 05 01 0b 07 f1 05 01 00 00 OK: id=12 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 13 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=13 length=1 0000: f1 OK: id=13 usb-robot> transfer type=control dir=out requesttype=0x40 request=0x40 index=0 value=0 size=5 Enter data in format FF FB 00 FC f1 5 1 29 0 doing control message id 14 to device, size 5, timeout 10000 frames, 40:40:0:0 OK: id=14 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 15 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=15 length=1 0000: f1 OK: id=15 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 16 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=16 length=10 0000: f1 05 01 0b 06 f1 05 01 00 01 OK: id=16 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 17 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=17 length=1 0000: f1 OK: id=17 usb-robot> transfer type=control dir=in requesttype=0xc0 request=0x41 index=0x200 value=0 size=255 doing control message id 18 from device, size 255, timeout 10000 frames, c0:41:0:200 DATA: id=18 length=1 0000: f1 OK: id=18 usb-robot> quit quiting on request ./usb-robot-slave: exiting happily