Softmodem hardware: dmodemd on ALSA and USB: technical details¶
This is the technical companion to Softmodem hardware, the plain-English overview. Start there; this page keeps the full detail: the code's names, the Recommendations' clauses, the numbers and the test lists.
Normally dmodemd's line is a SIP call. Two options give it a real telephone line instead:
- With
-a, the line is a softmodem's codec, driven through ALSA (the Advanced Linux Sound Architecture, Linux's sound drivers). - With
--usb, the line is a USB modem, driven through libusb. dmodemd knows five kinds: a Smart Link SmartUSB56 (below), a pico-serial Line stick (below), a pico-serial WB stick, an Agere USB 2.0 soft modem (below) and an Apple USB Modem (below).
The WB stick is not for a telephone line: it sits on a bare copper pair and runs D-Modem's wideband mode. Any ALSA codec that runs at 96 kHz can do the same (pico-serial WB).
In every case the modem itself is D-Modem's open DSP (digital signal processing) code, which runs on any processor architecture. The vendors' own programs for this hardware (Smart Link's slmodemd, Agere's agrsm and others like them) were closed, and mostly ran only on 32-bit x86 (i386). dmodemd can take their place.
dmodemd -a hw:1 -r 9600
dmodemd --usb=auto
The hardware¶
Linux still drives these modem codecs at the level a softmodem needs: a stream of samples each way, and switches for the hook and for the line monitor.
| Kind | Kernel driver | Codecs |
|---|---|---|
| AC'97 modem (MC97, AMR and CNR risers; laptops of about 2000 to 2006) | snd-intel8x0m (Intel ICH to ICH7, AMD-768/8111, nForce, SiS 7013, ALi), snd-atiixp-modem (ATI IXP), snd-via82xx-modem (VIA) |
any codec whose hook is the AC'97 modem GPIO: Silicon Labs Si3036/38, Agere, Motorola and others; Conexant's SmartDAA (below) |
| HD-audio modem (laptops of about 2005 to 2010) | snd-hda-intel with snd-hda-codec-si3054 |
Silicon Labs Si3054/3055 (163c:3055, 3155), Agere (11c1:3026, 3055, 3155), Motorola (1057:3055, 3057, 3155), SM56 (1543:3155), LG (1854:0018), a VIA board's (1106:3288) |
The codec appears as one of the sound card's PCM devices. Among the card's controls are two switches, "Off-hook Switch" and "Caller ID Switch". To find them:
aplay -l # a "Modem" device: card 1, device 0 is hw:1
amixer -c 1 controls | grep -i -e hook -e 'caller id'
An HD-audio modem is a device of the audio card, for example hw:0,6.
dmodemd needs the "Off-hook Switch": without it, it says the card is probably not a modem codec and stops. Without the "Caller ID Switch" it goes on, but warns that rings and caller ID may not be heard on hook.
Si3054 line settings¶
On an HD-audio Si3054 with a Si3018 line side, dmodemd also writes the
country's line settings (from -c or AT+GCI) into the codec's DAA
(data access arrangement, the line interface) registers. It reaches them
through the kernel's HD-audio hwdep device (/dev/snd/hwC<card>D<codec>),
which needs a kernel built with CONFIG_SND_HDA_HWDEP. The code is in
frontend/dmodemd/si3054.cpp, frontend/dmodemd/hda_codec.cpp and
frontend/dmodemd/alsa_line.cpp.
The values come from the datasheet (Si3054/18 Rev. 1.0, Table 19, the HD-audio columns). They are:
| Setting | Bits |
|---|---|
| on-hook speed | OHS, OHS2 |
| ringer impedance and threshold | RZ, RT |
| AC termination | ACT |
| DC termination | DCV, MINI |
| loop current limit | ILIM |
They go into nodes 13, 15, 16 and 18 of the codec. Node 18 holds the spark quenching bits that OHS needs. dmodemd reads each register first and changes only the country's bits.
- Where the table allows ACT 0 or 1 (the TBR21 countries, and Egypt), dmodemd takes 1, the complex impedance.
- RZ is cleared off hook, as the datasheet asks for pulse dialling, and set again on hook.
- The settings are written again before each off-hook, because the kernel's driver resets the codec on resume.
- A country that the table does not list gets the USA's settings.
dmodemd writes nothing, and logs line settings: the kernel driver's,
in three cases:
- on an AC'97 modem;
- on a kernel without the hwdep device;
- on a codec whose chip ID (node 12) is not a Si3054 with a Si3018, such as the other vendors' chips that the Si3054 driver binds.
Those codecs keep the kernel driver's settings, which are the USA's.
Conexant AC'97 codecs¶
Conexant's (and Rockwell's) AC'97 modem codecs have vendor ID CX with
a device ID 0x54xx, or RS with 0x53xx. They keep their hook in their
SmartDAA, not in the AC'97 modem GPIO that the kernel's "Off-hook
Switch" drives. The kernel does give their samples, so -a works with
them, except for the hook.
On an Intel ICH modem controller (snd-intel8x0m, on x86 Linux), dmodemd
started as root also drives the hook in the SmartDAA. It reaches the
codec's registers through the controller's I/O ports, under the codec
access semaphore that the kernel's driver also respects
(frontend/dmodemd/ac97_ports.cpp). It then logs the hook in its SmartDAA,
with the line side's version. Without root, on another controller, or on
another codec, it logs why, and the hook stays with the kernel's switch.
The code is frontend/dmodemd/cx_daa.cpp and frontend/dmodemd/cx_country.cpp.
How the SmartDAA's registers are reached¶
The SmartDAA is two chips: the system side (SSD) in the codec, and the
line side (LSD) beyond the isolation barrier. What dmodemd knows of them
comes from Conexant's HSF driver for ICH modems (hsfmc97ich.o and its
DAA library hsfsoar.o): from the code and from its debug messages.
- An SSD register is a byte, numbered 0 to 63, reached through codec
register 0x7a. A write puts
(reg << 8) | valueinto 0x7a. A read writes0x8000 | (reg << 8)and then reads 0x7a's low byte; 0xffff means there is no SSD. SSD register 3 needs a 40 µs wait before each access. - An LSD register goes through SSD registers 1 to 3 and a status register:
- the byte to write goes into SSD register 2;
- the command goes into SSD register 3: the LSD register in bits 5:0, and bit 7 set for a write;
- the answer comes back in SSD register 1;
- the status is SSD register 0 (register 4 on some SSDs). Bit 4 means done and bit 3 an error; both are cleared before each command. Bit 2 toggles at each frame over the barrier, and bit 1 means the link is up.
A write to one of the line's control registers (0x03, 0x14, 0x15, 0x1c to 0x25) first sets bit 1 of SSD register 0x17. One kind of SSD (SOAR's kind 0x1a) uses bit 6 of the command for them instead. A write is good when its answer is the byte written. The driver tries 5 times (LSD register 0x17 only once). - The hook is LSD register 0x15, bits 7:6: 01 on hook, 10 off hook, and 00 and 11 a dial pulse's make and break. - The kinds of SSD. The driver's library knows 39 kinds of SSD, which differ in how they reach the LSD. Some prime SSD register 1 before each command (0x55 for a read, the byte's inverse for a write) so that an absent LSD can be told. Some have the status in register 4. Some send a read's command before clearing the status. The ICH driver takes the kind from the codec's DAA type, the low byte of register 0x7e:
| DAA type | 0x20 | 0x21 | 0x22 | 0x23 | 0x28 | 0x29 | 0x30 | 0x31 | others |
|---|---|---|---|---|---|---|---|---|---|
| SSD kind | 5 | 0x0c | 0x13 | 0x15 | 0x0d | 0x12 | 0x17 | 0x20 | 5 |
Type 0x24's SSD is in the codec's own registers, not behind 0x7a.
dmodemd does the same (cx_kind()), and leaves a type 0x24 codec's
hook to the kernel.
What Conexant's library writes¶
The HSF interop harness (half_hsf, interop.md) runs
Conexant's library against a simulated SmartDAA of this kind (codec
0x5429, SSD kind 0x12). The simulated LSD answers as above, reports
version 5, and its register 2 reads 0x0f once powered. For the USA, the
library then sets the line up with these LSD writes, in order:
- Start: 0x03 = 00 and 0x04 = 2f, then it waits for 0x02 to read 0x0f (the LSD powered). Then 0x20 = 00, 0x25 = 29, 0x24 = 28, 0x21 = 80, 0x19 = 80, 0x08 = 00, 0x0a = d9, 0x0e = 00, 0x0c = 07, 0x12 = 00, 0x10 = 00, 0x19 = c0, 0x1f = 08, 0x15 = 40 (on hook), 0x1c = 78, 0x14 = e0, 0x07 = 04, 0x18 = 43, 0x19 = cb, 0x06 = 02. Then 0x0c = 06, 0x12 = 60, 0x1c = 30, 0x0e = 00, 0x18 = 43, 0x10 = 00, 0x1c = 68, 0x1c = 6a, 0x0c = 02, 0x0a = df, 0x10 = 00, 0x0e = 02, 0x10 = 00.
- ATA (going off hook): 0x15 = 41, 0x12 = 40, 0x0e = 00, 0x14 = 20, 0x18 = 43, 0x10 = 00, 0x21 = 82, 0x1c = 32, 0x15 = 40, then 0x15 = 80 (off hook) and 0x19 = 8b.
Before the LSD start it writes SSD registers directly: 0x1c = 40, 0x17 = 04, 0x14 = 08, 0x0b = 5f, 0x06 = c0, 0x19 = 20; 0x05, 0x04 and 0x07 to 0x0a = 00; 0x0d = 10, 0x1e = 00, 0x19 = 28, 0x1c = 7f; 0x1a, 0x1b and 0x1d = 00; 0x25 = 5a, 0x23 = 43, 0x24 = 80, 0x22 = 5a, 0x20 = 63, 0x21 = a8.
The simulated LSD's status registers (0x16, 0x1a, 0x1e, 0x1f) read 0. On a real line the library may write other values where it adjusts to what it measures (the loop voltage, the drive).
Two runs, with the simulated line side's registers starting at 0 and at
0xff (HSF_DAA_FILL=0xff), show which bits each of the library's writes
keeps. A third run, over two calls, shows its hang-up:
- SSD register 0x14's bits 1:0 back to 0;
- LSD 0x12's bits 6:5 = 11, 0x15 = 40, 0x19 = cb, 0x0e = 00, 0x21 = 80, 0x14's bits 7:2 = 111000, 0x18 = 43, 0x10 = 00, 0x1c's bits 6:0 = 6a, 0x10 = 00, 0x0e = 02, 0x10 = 00.
Going off hook ends with SSD 0x14's bits 1:0 = 01.
What dmodemd does¶
For a codec of kind 0x12, dmodemd does the same as the library
(CxDaa::power_up(), go_off_hook(), go_on_hook()):
- the SSD's writes;
- the LSD's power-up, and reading its version (0 to 0x11, the versions the library takes);
- the line's writes, keeping the bits the library keeps;
- the hook's sequences, including the 100 ms the library waits after 0x15 = 41.
A dial pulse changes the hook bits alone. cx_daa_test checks the LSD
writes against the library's, from both starting states.
For the other kinds of SSD the setup is not known. dmodemd reads the line side's version and changes only the hook bits. That holds the line only if something else has set the line side up.
The country and the board¶
The country's and the board's parameters are Conexant's, just as the
other backends' come from their datasheets. cx_country.cpp is
generated from the HSF driver's files.
- The country. Of the line settings above, only LSD 0x12's bits 6:5
change with the country. That was found by running the library for a
country from each of
hsf.cty's 14SMART_RELAYStables (HSF_COUNTRY=<T.35 code>). The two bits are bits 7:6 of the low byte of the table's off-hook and on-hook state words. There are three settings:
| Setting | On hook | Off hook |
|---|---|---|
| US (the USA, Japan, ...) | 11 | 10 |
| ITU (most countries) | 10 | 10 |
| TBR21 (its countries) | 00 | 00 |
The table has all 242 of hsf.cty's regions under their own names,
and also D-Modem's names for the three it names differently (BAHREIN,
SOUTHKOREA, UAE). -c and +GCI choose the row, as for the Si3054.
On hook, a new setting goes to the line side at once.
- The board. dmodemd takes the .inf's SmartDAAParamsK3 block for
DAA types 0x22 to 0x24, 0x29, 0x30 and 0x31, and its SmartDAAParams
block for the others, as the ICH driver chooses (95 bytes each).
Changing each byte of the block in turn (HSF_NVM with an edited
.inf) showed that three bytes change the library's writes:
| Byte | Goes to |
|---|---|
| 87 | SSD 0x21, as 0x80 | byte |
| 93 | SSD 0x0b |
| 94 | SSD 0x06's bits 7:4 |
The others (its resistors and DC tables) change nothing on the line sides dmodemd sets up. The library uses them only for a line side of version 0x11, which it measures (below).
cx_daa_test checks the writes against the library's for the USA,
Germany and Canada, from both starting states.
Line side versions¶
The line side's version (LSD 0x22's bits 5:0) changes what the library
does. Each version was tried with HSF_LSD_VERSION in the harness:
| Version | What the library does |
|---|---|
| 1, and 3 to 0x0f | the setup above |
| 2 | the same, except for three things: no writes to 0x24 and 0x25; 0x21's bit 7 clear in each of its writes (00 and 02, not 80 and 82); and 0x19's plain writes with bit 5 set (eb and ab, not cb and 8b) |
| 0x10 and 0x11 | another setup altogether (0x1d, 0x1f = 30, 0x15 = c0 on hook and 00 off hook: the hook's bit 7 the other way round); for 0x11, the line is also measured (below) |
| 0 | it reads the monitor ADC over and over |
Version 2 is one that real line sides have: Conexant's own
hsfconfig --info has shown SmartDAA SSD=10 LSD=0x2 for an HSF PCI
modem.
dmodemd does the library's setup for versions 1 to 0x0f, including
version 2's differences (cx_daa_test checks version 2's writes too).
For versions 0, 0x10 and 0x11 it reads the version and changes only the
hook bits, with bit 7 the other way round for 0x10 and 0x11.
The line side's monitor ADC¶
- LSD 0x16's bits 6:4 choose the channel. Its bit 3 starts a conversion and reads 1 when it is done.
- LSD 0x17 is the reading (8 bits, written back when read).
- The library takes it as reading × 2000 / 255 mV plus an offset of 500 mV. For version 0x10 the offset is calibrated on channel 3, against a reference it wants at 1350 to 1650 mV. Versions 0 and 2 have 4000 mV for 255 (and version 0 7 bits).
Only on version 0x11 does the library measure the line with it:
- on hook, the battery on channel 1 (Vbatt = 28.2 × the reading's mV + 400; the factor comes from the board's resistors);
- off hook, channel 0 (the receiver's DC offset), then channel 1 again (the tip-ring voltage, from which it works out the loop's resistance, current and power);
- a DAC (LSD 0x19), calibrated on channel 4 and then set so that the tip-ring voltage meets the DC curve, and adjusted while the line settles ("adaptive offhook").
dmodemd does none of this, since it does no version 0x11 setup.
None of the Conexant code has run on a real codec yet.
HD-audio modems outside the Si3054 list¶
snd-hda-codec-si3054 binds only the vendor IDs listed above. A modem
codec made to the same interface under another ID gets no driver, and so
no ALSA device. snd-hda-intel can give such a codec the Si3054's
vendor ID through its early patching (CONFIG_SND_HDA_PATCH_LOADER,
enabled in most distributions' kernels), and the Si3054 driver then
binds it. hda-modem-setup (installed beside dmodemd, on Linux) does
this:
hda-modem-setup # list the modem codecs
sudo hda-modem-setup write # the patch for those with no driver
sudo reboot
hda-modem-setup list # now driven by snd_hda_codec_si3054?
The three actions:
list(the default) shows each codec with a modem function group (from/sys/bus/hdaudio/devices): its card and address, its IDs, its driver, and whether it is in the Si3054 list.writetakes the modem codecs that have no driver and are not in the list, or those named with--codec hdaudioC<card>D<addr>. For each it writes a[codec]section, with[vendor_id] 0x163c3055, to/lib/firmware/dmodem-hda-modem.fw. It also writes the module option that loads the file for each such codec's card to/etc/modprobe.d/dmodem-hda-modem.conf(options snd-hda-intel patch=...). Before writing, it checks the running kernel's configuration for the patch loader and the Si3054 driver. It warns if another file already givessnd-hda-intelapatch=option, as only one takes effect.removedeletes both files again.
The change takes effect the next time snd-hda-intel loads. If the
module is in the initramfs, rebuild that first (update-initramfs -u,
dracut -f).
Only the driver can tell whether the codec really is Si3054-compatible,
once it has bound it. At its init it checks the link to the line side
chip and logs the chip's ID. "Link Frame Detect(FDT) is not ready" in
dmesg means the codec is not compatible: run
sudo hda-modem-setup remove, and reboot.
Conexant's HD-audio modems (14f1:2bfa, 14f1:2c06 and the others of
vendor 14f1) are of another kind. Their DAA is behind Conexant's own
verbs, and this does not cover them.
frontend/dmodemd/test/hda_modem_setup_test.py (ctest's hda_modem_setup, label
quick) runs the helper on a fake sysfs and kernel configuration. The
patch itself has not been tried on a real codec.
Smart Link SmartUSB56¶
Smart Link's USB modem (the SmartUSB56, and the modems built on its
ST7554 bridge, USB 0483:7554) needs no kernel driver: dmodemd drives
it through libusb. The ST7554 is a USB bridge to an STLC7550 codec and a
DAA:
- its registers are vendor control requests;
- its samples are isochronous transfers both ways;
- its GPIOs are the hook and the ring detector.
lsusb -d 0483:7554 # Bus 001 Device 007: ID 0483:7554 ...
dmodemd --usb=auto # the first one; --usb=1:7 that one
Started as root, dmodemd opens the device before it goes on as nobody.
To start it as an ordinary user, give that user the device with a udev
rule, for example /etc/udev/rules.d/60-smartusb56.rules:
SUBSYSTEM=="usb", ATTR{idVendor}=="0483", ATTR{idProduct}=="7554", MODE="0660", GROUP="dialout"
If Smart Link's own kernel driver (slusb) is loaded, dmodemd detaches
it from the device while it runs. dmodemd is built with --usb when
pkg-config finds libusb-1.0 (the libusb-1.0-0-dev package).
dmodemd --usb list lists the SmartUSB56s, pico-serial Lines and WBs
and Agere modems there are, and whether each can be opened. On Windows
dmodemd.exe drives them the same way, on WinUSB (or on libusbK or UsbDk
on Windows 7 and 8, whose WinUSB cannot stream)
(windows.md).
The ST7554's register values and start-up are those of Smart Link's
Linux driver for it (drivers/st7554.c of slmodem 2.9.11, under a BSD
licence). The code is in frontend/dmodemd/usb_line.cpp:
- Samples. S16 little-endian mono at 9600 Hz (the default) or 8000 Hz, in isochronous packets of 1 ms. Three transfers of five packets are in flight each way. Each OUT transfer is sent when an IN transfer completes, with that IN transfer's packet lengths, so the playback keeps to the codec's clock. The OUT transfers take the modem's queued samples; the queue starts a frame and a half (30 ms) ahead. The modem's clock is the codec's: the number of samples it has delivered.
- The hook. GPIO 3, with the hook LED on GPIO 7. A hook flash clears GPIO 3 for the flash's length.
- Rings. The ring detector (GPIO 0) changes with the ring signal.
Each change comes on the interrupt endpoint and is a pulse to the
core's
ring(), which checks the cadence. A change that comes with a FIFO underrun or overrun is not counted, since the ring bit can come set with those whether the line rings or not. - Caller ID. On hook the codec hears nothing unless GPIO 5 (CID1)
connects it to the line. With
AT#CID=1, dmodemd sets CID1 from the first ring while the core's caller ID receiver listens, and clears it before the hook changes, as Smart Link's Windows driver does (its Linux driver never did). So caller ID is heard between the first two rings. - The codec. Either an STLC7550, or an ST75951/2 "silicon DAA"
(
MONOLITHIC, bit 14 of the GPIO status). The ST75951's control words name one of its four registers; dmodemd starts it up as the Windows driver does. The hook, ring and caller ID use the same GPIOs. - LEDs. The hook LED (GPIO 7) follows the hook. With
--usb-leds, dmodemd also drives two more LEDs, as Smart Link's Windows driver does on boards whose INF says they have them (Creative's Modem Blaster V.92 USB): - the carrier LED (GPIO 6), lit while the call is online;
- the data LED (GPIO 13), lit once data has passed in the call.
Both go off on hook. Without the option dmodemd leaves those GPIOs
alone, as another board may use them for something else.
- Wake on ring. With --usb-wake, dmodemd arms the device's USB
remote wakeup (SET_FEATURE) if its configuration says it can wake, as
Smart Link's Windows driver does when the PC sleeps. The ring
detector's interrupt stays on while on hook, so a ring wakes the
computer. The kernel must allow it too:
- On Linux, dmodemd writes enabled to the device's
/sys/bus/usb/devices/<bus>-<ports>/power/wakeup while it is still
root. Started as a user, add ATTR{power/wakeup}="enabled" to the
udev rule above.
- On Windows, it is Device Manager's "Allow this device to wake the
computer".
The remote wakeup is cleared when dmodemd exits.
- Suspend and resume. While the computer sleeps, the transfers fail.
dmodemd keeps them and, every second, sets the device up again (its
registers, the remote wakeup and the FIFO, since a resume may have
reset it) and submits them again. It gives up after 30 s, as for a
device that has gone. A call is lost across a sleep. The rings after
the one that woke the computer are heard as usual, so the modem can
still answer.
- The handset detector. With --usb-handset, dmodemd enables the
interrupt of GPIO 2 (GPIO_HANDSET in Smart Link's Linux source; the
Windows driver enables it too, and does nothing with it). On each
change it reads the GPIO. Set means that the phone on the modem's
PHONE jack is off hook, as far as is known (no driver says). The core
reports it in voice mode as V.253's local phone event: <DLE>H off
hook and <DLE>h on hook. It lists event 8 in +VLS=?.
- International line controls. With --usb-international, dmodemd
drives three more outputs, as Smart Link's Windows driver does with its
Options.International (off in Creative's INF):
- DP (GPIO 10): set off hook; cleared on hook, together with RFC.
- DC (GPIO 12): set off hook in France, Algeria and Tunisia, the
countries whose record asks for it (byte 90, bit 0, of Smart Link's
table). On an ST75951 it is the other way round: set off hook in
every other country.
- RFC (GPIO 8): set for 350 ms after each off-hook in Australia
(bit 1, "the Australian timer"), and all through pulse dialling.
During pulse dialling DP and RFC are left alone, and DC is cleared
on each break.
What the three switch in the DAA is not known: only Smart Link's names
for them are. They are for a board built for these countries. The
country is the one set by -c or +GCI. dmodemd learns when pulse
dialling begins and ends from the call progress pump
(MDMPRM_PULSE_DIALLING).
- The voice codec (a handset). With --usb-voice-codec=<law>,
dmodemd takes alternate setting 3 instead of 1. Its second pair of
isochronous endpoints is the ST7554's SSI2 codec (Smart Link's
K7554VoiceCodecInterface: 8000 octets a second, powered up with
SSI_CTRL = 0x21, its period in register 6). In voice mode +VLS=? then
lists two more V.253 devices:
- 14 ("H"): the handset alone, the line on hook. The voice
engine's samples are the handset's: its microphone goes to +VRX,
and +VTX goes to its earpiece.
- 15 ("HT"): the handset on the call, off hook. The voice
engine's samples are the line's, and dmodemd mixes the handset in
both ways: the line and the modem's samples go to the earpiece, and
the microphone goes to the line and to +VRX.
The two codecs' rates are bridged by linear interpolation. What the
octets are is not known: the Windows driver calls its format 3 and
never uses it, and streams this codec only from its test interface.
So the option takes ulaw, alaw or linear (signed 8-bit).
Creative's INF uses alternate setting 1, so the DE5671 may have no
codec there. A board whose alternate setting 3 lacks these endpoints is
refused.
- RHOM. With --usb-rhom, dmodemd sets FIFO_CONTROL2's bit 15 (and
bit 7, the voice codec's, with --usb-voice-codec) while the modem
runs (from the line's start to its stop). This is what Smart Link's
Windows driver does with Options.RHOM (ConfigureRHOM,
EnableRHOM), which Creative's INF leaves off. A chip whose revision
(register 0) is below 0x200 never gets it, as on Windows. What RHOM
does is not known.
- The buzzer. With --usb-buzzer, the modem's speaker is GPIO 11
(BUZEN). It is on whenever the core has the speaker on at any volume,
and off otherwise:
- ATM1 (the default): on from dialling or answering until the CONNECT;
- ATM2: always on;
- ATM0, or ATL0: never on.
This is what Smart Link's Windows driver does in SetSpeakerVolume
with Options.Buzzer (off in Creative's INF). A buzzer has no volume,
so ATL1 to ATL3 are the same. Not every board has one.
- Unplugged. dmodemd says so and exits.
Limits: the device's line settings are its own. dmodemd has not yet been tried on a real SmartUSB56. Caller ID and the ST75951 follow the Windows driver (read from its code, smartusb56-windows.md) and have been tested on the fake device only.
pico-serial Line¶
The pico-serial project's phone-line stick (SPEC-L.md) is a USB-C stick with an RJ11 socket, made for D-Modem. It is a softmodem's line and nothing more: a Skyworks Si3056 DAA and its Si3018 line side behind an RP2040 microcontroller, at 9600 Hz so that the open DSP's V.90 works. dmodemd drives it through libusb, as it does the SmartUSB56, and it needs no kernel driver.
lsusb -d 1209:0002 # Bus 001 Device 008: ID 1209:0002 ...
dmodemd --usb=auto # the first SmartUSB56 or stick; --usb=1:8 that one
A udev rule to start dmodemd as a user, for example
/etc/udev/rules.d/60-pico-serial-line.rules:
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0002", MODE="0660", GROUP="dialout"
Its protocol is its firmware's (firmware/PROTOCOL.md, "Line stick"),
shaped like the ST7554's on purpose. The code is in
frontend/dmodemd/usb_line.cpp:
- Samples. S16 little-endian mono at 9600 Hz (the default) or 8000
Hz, isochronous both ways in interface 0's alternate setting 1.
Selecting that alternate setting starts them; the rate is set first
(
SET_RATE). Each IN packet holds the samples that the stick's codec gave in that millisecond, and each OUT transfer is sent with an IN transfer's packet lengths, as for the ST7554. - The hook. A vendor request (
SET_HOOK). The stick times a hook flash itself (HOOK_FLASH, 1 to 2000 ms). Pulse dialling sendsSET_HOOKfor each break and make, as the modem's frames are processed. - Rings and caller ID. On hook the stick's line monitor is on
(
SET_MONITOR), so it hears the line: caller ID comes in its samples, as on an ALSA codec. Rings come from its ring detector. Each change it counts arrives in an 8-octet event on the interrupt endpoint and is a pulse to the core'sring(), as each change of the ST7554's ring GPIO is. - At start-up dmodemd reads the stick's protocol version, firmware
version and line side (
GET_INFO). It refuses a protocol version other than 1. If the line side does not answer across the isolation barrier, it says so and goes on. ATI3 sayspico-serial Line (Si3056). - The country's line. dmodemd writes the country's settings (from
-c, and again on eachAT+GCI) into the DAA: - the AC termination: ACT and ACT2 on the stick's Si3018 line side, or ACIM on a Si3019;
- the DC termination (DCV, MINI);
- the TBR21 current limit (ILIM);
- the ringer impedance and threshold (RZ, RT);
- the on-hook speed (OHS, OHS2, with Australia's spark quenching).
The values are the Si3056 datasheet's (Rev. 1.03, Table 15, "Country
Specific Register Settings"), in frontend/dmodemd/si3056.cpp. Each register
(16, 26, 31 and 59, and 30 on a Si3019) is read first (DAA_READ), and
only the country's bits change (DAA_WRITE). A change made while off
hook waits until the line is on hook again.
Countries the table has no row for (Algeria, Brunei, Estonia,
Lithuania, Sri Lanka, Tunisia, Turkey, Uruguay, Vietnam and most of the
ITU list's) keep the USA's settings, which are the Si3056's reset
values, and dmodemd's log says so (-d1). The datasheet's TBR21 row is
-c CTR21EUROPE.
- Unplugged. dmodemd says so and exits.
Limits:
- The transmit and receive gains stay at their reset values (Table 15 lists none).
- Australia keeps its Japan-mode DC termination throughout the call. The datasheet's option of switching to FCC mode 500 ms after going off hook, for a higher transmit level, is not taken.
- The stick's USB ID is a pid.codes test PID until it has its own.
- It has been tested against a fake of it (below), not yet on a real one.
pico-serial WB (wideband, bare pair)¶
pico-serial's wideband stick (SPEC-WB.md) is the Line stick's sibling for a bare copper pair: a dry pair, an alarm or leased circuit, or a pair of your own between buildings. It is not for a telephone line. Two WB sticks talk to each other point to point through a wideband transformer, and sample the pair at 96 kHz instead of 9.6 kHz. They use a band of about 4 to 36 kHz.
This is the line that D-Modem's wideband mode is for: V.wb, AT+MS=VWB
(dsp/vwb.md), at 48000 Hz and up (-r 96000). It runs
with AT&L1, a leased line with no dial tone or ringing: ATD calls,
and ATA or ATS0=1 answers. In simulation it carries about 149
kbit/s each way on a short pair, and about 70 kbit/s on 5 km.
dmodemd drives the stick through libusb, as it drives the Line stick,
and it needs no kernel driver. --usb=auto finds it beside the other USB
modems. With no -r it runs at the stick's own 96000 Hz:
lsusb -d 1209:0003 # Bus 001 Device 009: ID 1209:0003 ...
dmodemd --usb=auto # 96000 Hz; -r 48000 or -r 192000 for those
A udev rule to start dmodemd as a user, for example
/etc/udev/rules.d/60-pico-serial-wb.rules:
SUBSYSTEM=="usb", ATTR{idVendor}=="1209", ATTR{idProduct}=="0003", MODE="0660", GROUP="dialout"
At the two ends of the pair, one end calls and the other answers by itself:
AT&L1S0=1 (the answering end, once)
AT&L1 (the calling end)
ATD
CONNECT 148656
At 48000 Hz and up, V.wb is the modulation by default (AT+MS? says
200), since no other runs there; AT+MS=VWB is not needed. A leased
line has no hang-up signal of its own. When one end hangs up (ATH), the
other sees NO CARRIER when V.42 tells it, or once the carrier has been
gone for S7 seconds (ATS7=10 for 10).
Its protocol is its firmware's (firmware/PROTOCOL.md, "WB stick", and
src/core/wb_proto.h): the Line stick's USB device, with the WB stick's
own requests. The code is in frontend/dmodemd/usb_wb.cpp, on top of
frontend/dmodemd/usb_line.cpp's transfers:
- Samples. S16 little-endian mono at 96000 Hz (48000 and 192000 Hz
too), isochronous both ways in interface 0's alternate setting 1,
which starts them. The packets are 1 ms, of up to 480 octets. Each IN
packet holds the samples the stick's codec gave in that millisecond,
and each OUT transfer is sent with an IN transfer's packet lengths, as
for the Line stick.
SET_RATE(in kHz) is sent first. A rate the stick does not have (-r 8000or9600) is refused. - At start-up dmodemd reads the stick's protocol version and board
(
GET_INFO), and refuses a protocol version other than 1 or a board that is not the WB stick's. It then turns any loopback off (SET_LOOPBACK), streams the hybrid's input (SET_INPUT: the line less our own signal, not the line itself), sets the rate and the gains, turns the LINK LED off, and says so if the codec does not run (GET_STATUS). ATI3 sayspico-serial WB (TLV320AIC3204). - Gains.
--usb-wb-tx-gain=<dB>sets the transmit level: the codec's DAC volume (SET_TX_GAIN), -63.5 to +24 dB in 0.5 dB steps.--usb-wb-rx-gain=<dB>sets the hybrid input's gain: its MicPGA (SET_RX_GAIN), 0 to 47.5 dB.
Both are 0 by default, the stick's own levels: about +3 dBm on the
line for a full-scale sine, which puts V.wb's signal (11 dB below full
scale) at about -8 dBm.
- The LINK LED shows the modem's carrier: on while the call is
online (through a retrain too), off otherwise (SET_CARRIER). At the
end dmodemd gives it back to the stick, which lights it while its
samples run.
- No hook, ring or caller ID. The modem's hook only starts and stops
the call. Nothing rings, and ATS0=1 answers only with AT&L1.
- Events. Over current on the line's drivers is logged as an error.
The codec's overflows (the transmit level or the hybrid's gain too
high) and the stick's underruns and overruns are logged at -d1.
- Unplugged. dmodemd says so and exits.
It has not yet been tried on a real stick or a real pair. It has been tested against a fake of the stick (below) on a simulated pair, with the firmware's protocol as its sources have it. What is still to be checked on hardware:
- The stick's real delay. Its codec's filters are not counted in
dmodemd's
io_delay. V.wb measures its own echo and needs none. - The levels. The default gains (0 dB) are untried against a real hybrid's residue and a real pair's loss; the receive gain may need raising on a long pair.
- V.42 over the host's buffers. dmodemd sends its samples 4672 samples ahead at 96 kHz, about 50 ms: a frame and a half of 20 ms, 15 ms of transfers, and the stick's own buffering. LAPM's window of 15 frames of 128 octets waits a round trip. In simulation, with that lead at both ends, V.wb's 148656 bit/s carried 83 kbit/s each way through V.42, against 137 kbit/s with no buffers.
- The USB ID. The stick's USB ID is a pid.codes test PID until it has its own.
Any ALSA codec that runs at 96 kHz (-a hw:N -r 96000, through a line
transformer) is a line for V.wb too. libdmodem runs it at any of the
three rates (dmodem_create_rate()).
Agere USB 2.0 soft modem¶
Agere's (later LSI's) USB soft modem, USB 047e:2892 (and 047e:2828),
is sold under other names too. It has no kernel driver: dmodemd drives it
through libusb. It is an Agere USB controller with an ARM core in it,
and behind that either a Mercury codec or a "Polaris" DAA. Its ROM
streams the samples itself on isochronous endpoints, as Agere's Windows
driver has it, so nothing is downloaded into it. It needs a USB 2.0
(high speed) port; on a full speed port dmodemd says so and stops.
lsusb -d 047e: # Bus 001 Device 009: ID 047e:2892 ...
dmodemd --usb=auto
The line runs at 8000 Hz, whatever -r says. A udev rule
(/etc/udev/rules.d/60-agere-usb.rules) lets dmodemd open it without
root:
SUBSYSTEM=="usb", ATTR{idVendor}=="047e", ATTR{idProduct}=="2892", MODE="0660", GROUP="dialout"
agere-usb.md has the full detail, read from the
device's ROM and Agere's drivers. In short, frontend/dmodemd/usb_agere.cpp
does this:
- Start-up. Interface 0's alternate setting 1 (isochronous IN and OUT), the ROM's version, its GPIOs and clock, and its codec's registers as Agere's drivers write them. Codec register 0xc0's bit 15 tells a Polaris from a Mercury. A Polaris gets its link pulsed up, its calibration and its country's DAA registers. Then on hook. Then the ROM's function table is put back as the ROM has it (any patch left by an earlier driver is removed), the clock is raised, and the ROM's stream is started (command 0x0c).
- Samples. A packet each way every 2 microframes (4000 a second): a header (the samples' count and a status word), then the samples. Sixteen transfers of 24 packets are in flight each way. Each OUT transfer carries 48 samples (2 a packet), the codec's own pace, with one sample more or less now and then to follow the drift between the USB's clock and the codec's.
- Hook. Codec register 0xc0: 0 is off hook on a Mercury, 1 on a Polaris. Pulse dialling and the hook flash break and make it.
- Rings. Bit 0 of the IN packets' status word: a pulse to the core's
ring()for each packet that has it. On hook dmodemd also reads codec register 0x0a8 every 100 ms, since a Polaris does not set the status bit. - Caller ID. The codec hears the line on hook (a Polaris's caller ID path is switched on while on hook), so caller ID comes in the samples.
- LEDs. The OH LED (GPIO 4, lit while set) is lit off hook and blinks fast while the line rings. The board's READY/DATA LED is not on any GPIO dmodemd has found, so it is not driven.
- Unplugged. dmodemd says so and exits.
ATI3 says Agere USB 2.0 (Mercury) for either codec. The modem has been
tested against a fake of it (below), but
does not yet work reliably on a real one: agere-usb.md
says what to look for, and what to send, on a run.
Apple USB Modem¶
Apple's USB modem for Macs (A1101, MA034Z/A), USB 05ac:1401 (and
05ac:8220, which Apple's driver also takes), is Motorola's SM56 USB
soft modem with a Silicon Labs Si3056 DAA. dmodemd drives it through
libusb (frontend/dmodemd/usb_apple.cpp).
lsusb -d 05ac:1401 # Bus 001 Device 007: ID 05ac:1401 Apple, Inc. Modem
dmodemd --usb=auto
It works as Apple's Mac OS X drivers do:
- Samples: USB audio class 1, an isochronous stream each way
(interfaces 3 and 4, alternate setting 1), 16-bit mono at the
-rrate, set withSET_CURon each endpoint. A rate the device does not list is refused, with the ones it has. - Hook: CDC's
SET_HOOK_STATEto interface 0 (Direct Line Control). - Rings and the phone cord: interface 0's interrupt notifications
(
RING_DETECT, and 0x29 for the cord). - DAA: the Si3056's registers through 3-octet commands to interface
1 (
SEND_ENCAPSULATED_COMMAND, a read's value withGET_ENCAPSULATED_RESPONSE): the line side powered up, its link checked, the on-hook line monitor for caller ID, the country's registers (-c,AT+GCI).
A udev rule (/etc/udev/rules.d/60-apple-usb-modem.rules) lets dmodemd
open it without root:
SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="1401", MODE="0660", GROUP="dialout"
ATI3 says Apple USB Modem (Si3056). It has been tested against a fake
of it (below), not yet on a real one.
apple-usb-modem-details.md has the full
protocol and where each part was found.
Not covered¶
These are not covered yet, because Linux has no driver that gives their samples:
- HD-audio modems that are not Si3054-compatible, such as Conexant's.
(Compatible ones outside the Si3054 list can use
hda-modem-setup, above.) - Modems with their own PCI controller: Smart Link SmartPCI, PCTel HSP, Lucent/Agere PCI, Conexant HSF PCI, Broadcom BCM4212.
Smart Link's PCI modems are of two kinds:
- Those on an AC'97 controller (Intel ICH, VIA, SiS, ALi, ATI, nForce)
are AC'97 modem codecs, covered by
-aabove. - The SmartPCI cards (SL1900, SL2800; PCI vendors 163c, 10a5, 2000 and
2003) have Smart Link's own controller. Its registers are documented
only in Smart Link's closed
amrlibs.o, so there is no open driver to start from.
Options¶
| Option | What it does |
|---|---|
-a <pcm>, --alsa=<pcm> |
The line is the codec's PCM (hw:1, hw:0,6, plughw:...): a telephone line, not SIP. |
--alsa-ctl=<ctl> |
The codec's controls, if not the card's. By default hw:<card> for hw: and plughw: PCMs (hw:1 for hw:1,0 or plughw:1,0), and the PCM's own name otherwise. |
--usb=<dev> |
The line is a SmartUSB56, pico-serial Line or WB, or Agere USB modem: auto (the first one), or <bus>:<address> as lsusb and --usb list show it. The line is a telephone line (a bare pair for the WB), not SIP. --usb list lists the devices and exits. |
--misdn=<dev> |
The line is an ISDN basic rate interface through Linux's mISDN (its number, or its name such as hfc-pci.1), not SIP (isdn.md). |
--misdn-msn=<msn> |
With --misdn: take calls for this MSN alone, and send it as our number. |
--misdn-noflip |
With --misdn: take the B-channel's octets as the card gives them, not bit reversed. |
--usb-leds |
Light a SmartUSB56's carrier and data LEDs (GPIO 6 and 13), as on Creative's Modem Blaster. Only for a board that has them. |
--usb-wake |
Let a ring wake the computer: arm the SmartUSB56's USB remote wakeup (and on Linux, as root, enable the kernel's power/wakeup for it). |
--usb-handset |
Use the SmartUSB56's handset detector (GPIO 2): the phone on its PHONE jack off or on hook gives <DLE>H and <DLE>h in voice mode. |
--usb-voice-codec=<law> |
Use the SmartUSB56's second codec (SSI2, alternate setting 3) as a handset: +VLS=14 and 15 in voice mode. Its octets are ulaw, alaw or linear. |
--usb-buzzer |
Use the SmartUSB56's buzzer (GPIO 11, BUZEN) as the speaker: on while ATM/ATL have the speaker on, as Windows' Options.Buzzer. |
--usb-rhom |
Set the SmartUSB56's RHOM bit (FIFO_CONTROL2 bit 15, and the voice codec's bit 7) while the modem runs, as Windows' Options.RHOM. Not below chip revision 0x200. |
--usb-international |
Drive the SmartUSB56's international line controls (GPIO 8, 10 and 12) as Smart Link's Windows driver does: the country's DC bit, Australia's 350 ms after going off hook, and pulse dialling's. |
--usb-wb-tx-gain=<dB> |
A pico-serial WB's transmit level: its DAC's volume, -63.5 to +24 dB in 0.5 dB steps (0, the default, is the stick's own). |
--usb-wb-rx-gain=<dB> |
A pico-serial WB's hybrid input gain: its MicPGA, 0 (the default) to 47.5 dB in 0.5 dB steps. |
-r <Hz>, --rate=<Hz> |
The codec's rate. 9600 (the default) or 8000 for the voice-band data pumps; an Agere USB modem always runs at 8000, whatever this says. 48000, 96000 or 192000 for the wideband mode on a bare pair (V.wb, dsp/vwb.md), on a line that has the rate: an ALSA codec, or a pico-serial WB (whose default is 96000), but not the SmartUSB56 or the Line stick. At those rates V.wb is the default modulation (AT+MS=VWB), the voice-band pumps are refused (NO CARRIER), and ATD dials nothing. Any other value is refused. |
Only one of -a, --usb and --misdn can be given.
Started as root, dmodemd opens the codec before it goes on as nobody,
so the device's group (usually audio) does not matter. Started as an
ordinary user, that user needs access to /dev/snd/*.
SIP_LOGIN is not needed with -a or --usb.
How it works¶
The ALSA line is frontend/dmodemd/alsa_line.cpp:
- Samples. The PCM runs both ways from the start, on hook too: S16 mono at the line's rate, in periods of 20 ms, with a buffer of 8 periods. The playback runs two periods ahead of the capture. Each captured frame's answer is written at once, so the two keep their distance. An overrun or underrun (dmodemd held up) restarts both ways, and the log says so. The modem's clock is the codec's: the number of samples it has delivered.
- The hook. Off hook (a call, or voice), the "Off-hook Switch" is on
and the "Caller ID Switch" is off. On hook, the hook switch is off and
the line monitor is on, so the codec hears the line: rings and caller
ID. A hook flash (
AT+VTS=!) turns the hook switch off for its length. - Rings. On hook every sample goes through
dsp::RingDetect. The ring signal, heard through the line monitor, is a large wave of 15 to 70 Hz, and each cycle of a burst is a pulse to the core'sring(). That is what a ring indicator line gives. The core checks the cadence (RING, answering byATS0, andS1): - a burst of four pulses or more, after a gap of 1.5 s, is a ring;
- ringing with no such gap (such as Egypt's 2 s on and 1 s off) rings every 6 s;
- a ring counts in
S1for 8 s.
Every country of the ITU's list rings this way (ring_tones_test,
interop.md).
- Caller ID. At the first ring the core runs its caller ID receiver
(AT#CID) on the monitor's samples for 4 s.
- Dialling. The modem dials the number itself, in DTMF, or in pulses
with ATP or ATDP. A P or T in the dial string switches for the
rest of it. With ATX4 (the default) the modem waits for a dial tone
first; with ATX3 it waits 2 s instead.
- Pulse dialling. The call progress pump times the pulses from the
country's table (-c, AT+GCI):
- the break and the make: 64 and 36 ms for the USA, 67 and 33 ms for
New Zealand, about 60 and 40 ms for most of Europe;
- the gap between digits: 800 ms;
- the digit pattern: n pulses for n and ten for 0; n + 1 in Sweden;
10 - n in New Zealand.
The line breaks and makes its hook (Host::pulse_hook()): the
"Off-hook Switch" on ALSA, GPIO 3 on the SmartUSB56. The switch is
written as the modem's frames are processed, so an edge comes when its
frame does (20 ms apart on ALSA). 60/40 pulses keep their times; others
come out within one frame of theirs. *, # and A to D have no pulses
and are skipped. A line that cannot pulse (SIP) dials in DTMF.
- Busy tone. The pump takes the country's busy and congestion tones
(dsp/tones.h, the ITU's list of 192 countries). It needs four cycles
of either cadence, within 15% or 80 ms either side, and not the
country's ringing tone. A country with neither would use the table's
bounds of on and off times, or 0.2 to 0.8 s.
- Voice mode. In voice mode, ATD waits 2 s, dials, then takes the
call as answered when the ringing tone ends (+VRA), or after +VRN
if there is no ringing tone (V.253 9.1.1).
- Modulations. The core treats the line as an analogue one
(Host::analog_line()): the modem is the analogue end of the call.
- +MS's V.90 and V.92 (the default) run PCM as the analogue modem at
9600 Hz (-r 9600, the default): up to 56000 bit/s down and 33600 up
from a V.90 server. With V.92 (and a V.92 server) it adds PCM
upstream up to 48000 bit/s, quick connect and modem-on-hold (see
V.90 analogue modem).
- With any other modem it runs V.34 (up to 33600 bit/s) and below.
- V.91, which is between two digital modems, becomes V.34.
- At 8000 Hz (-r 8000), V.90 is there only with DSP_V90A=1. A
codec at 8000 Hz on a clock of its own loses the PCM signal every few
seconds (see 8000 Hz lines), so
keep 9600.
- It has fax (class 1) and voice too.
Limits¶
- Speed. As the analogue modem, the open DSP has V.90 with its retrains and rate renegotiations: a line that changes mid-call renegotiates, and one that PCM cannot use falls back to V.34. It also has V.92's PCM upstream (with its renegotiations), quick connect and modem-on-hold. The analogue modem has been tested against our own digital modem on the simulated network only, not yet against an ISP's server.
- Pulse timing. Pulses keep the modem's frames' timing (20 ms on
ALSA). They have not yet been tried on a real exchange.
AT&P(S28's pulse ratio) is kept, but the country's table sets the times. - Country rules. The country (
-c,AT+GCI) sets the pulse dialling, the busy cadence, and the dial pause and hook flash times (S8, S24).-ctakes any country of the ITU's list too: one the table does not have dials with the USA's settings and its own busy tones. The codec's line settings (impedance, ring thresholds, current limit) are the country's on an HD-audio Si3054 with a Si3018 (above), and partly on a Conexant SmartDAA (above). Any other ALSA codec keeps its kernel driver's settings, the USA's, since ALSA gives no control of them. The DTMF levels stay the pump's own. - Real hardware. The code has been tested against the fake card below, not yet on a real modem codec.
Testing without hardware¶
The ALSA line¶
frontend/dmodemd/test/alsa_test.py (ctest's dmodemd_alsa, label alsa) runs
two dmodemd on fake ALSA modem cards whose lines belong to a simulated
telephone exchange. It needs libasound2-dev.
frontend/dmodemd/test/fake_modem_card.cppis alsa-lib's external PCM (ioplug) and control (ctl_ext) plugins,dmline. They are loaded through an ALSA configuration of the test's own (ALSA_CONFIG_PATH), so dmodemd goes through the same ALSA calls as with a real card. The capture is what the exchange sends, the playback's position runs on the clock like a DMA's, and the control has the two switches.frontend/dmodemd/test/line_exchange.cppis the exchange. Each number's line gets:- a dial tone, then its DTMF or pulse digits collected and the number routed;
- for the number called, the ring signal (2 s of 20 Hz, 4 s off, heard through the line monitor) and caller ID between the first two rings (Bell 202, MDMF); for the caller, the ringing tone;
- a busy tone if the line called is off hook or unknown;
- once answered, the call, with a loss of 3 dB, noise, and an echo at
-24 dB (
LINE_LOSS=<dB>,LINE_ECHO=<dB>|off); - a busy tone after the other end hangs up.
frontend/dmodemd/test/fake_hda_hwdep.cppis an HD-audio Si3054's hwdep device, put in front of alsa-lib withLD_PRELOAD.
In real time, the test checks:
- a data call: the dial tone, DTMF, rings, caller ID, the answer at the second ring, CONNECT at both ends, a line of text each way, and the hang-up;
- a stall of 0.6 s (SIGSTOP), after which the codec's restart is logged;
- a busy line;
- a call dialled in pulses (
ATDP), which the exchange counts (a break of 200 ms at most is a pulse; 300 ms made again ends the digit); - a voice call: the ringing tone, caller ID in voice mode's packet
(
<DLE>X...<DLE>.), OK at+VRA,+VTSDTMF across, and the busy tone after the other end hangs up; - with the fake hwdep device, the Si3054 line settings:
-c UK's at start-up, South Africa's afterAT+GCI=9F(with RZ cleared off hook and set again on hook), and Australia's; nothing written to a codec with another chip ID; and the kernel driver's settings kept on a card with no hwdep device.
RATE=8000 runs it at 8000 Hz.
The line settings and the SmartDAA also have unit tests (label quick):
si3054_test (the Si3054's rows and bits), si3056_test (the pico-serial
Line's, on a Si3018 and a Si3019) and cx_daa_test (the Conexant
SmartDAA's register access, kinds, hook, and the kind 0x12 setup against
the library's writes, above).
The SmartUSB56¶
frontend/dmodemd/test/usb_test.py (ctest's dmodemd_usb, label alsa) runs
two dmodemd with --usb on the same exchange. It needs libusb-1.0-0-dev.
frontend/dmodemd/test/fake_st7554.cpp is the part of libusb-1.0 that dmodemd
uses, put in front of the real one with LD_PRELOAD. Its one device is
an ST7554 whose line is the exchange's:
- its registers set the hook;
- its IN transfers take the line's samples as the exchange sends them, and its OUT transfers' samples go to the exchange;
- its ring detector changes on each half cycle of the ring signal;
- it hears the line on hook only with CID1 set.
One of the two fakes says it is an ST75951/2 (FAKE_ST7554_DAA) and
checks its start-up. The test checks:
- that a wrong
<bus>:<address>is refused; - a data call: rings, caller ID between the first two rings, the answer
at the second ring, CONNECT at both ends, text each way; the LEDs of
the modem with
--usb-leds, and none on the other; the buzzer of the modem with--usb-buzzer, on as it dials and off at the CONNECT; and no RING from FIFO underruns; - a busy line, a call dialled in pulses, and a voice call;
- the handset detector in voice mode;
- the international line controls: Australia's timer and pulse dialling, France's DC bit, and the ST75951's inverted one;
- the voice codec as a handset (the fake's
FAKE_ST7554_VOICE: its microphone a 1000 Hz tone, its earpiece written to a file):+VLS=14and15,+VRXand+VTX; - RHOM (
--usb-rhom): on while the modem runs, and none on a revision 0x0100 chip (FAKE_ST7554_REVISION); - a call that wakes a suspended 1002 (
--usb-wake): the fake's ring with its remote wakeup armed, the device reset on resume and set up again, and the call answered; - that both dmodemd exit once the exchange (and so the devices) has gone.
The pico-serial Line¶
frontend/dmodemd/test/pico_line_test.py (ctest's dmodemd_pico_line, label
alsa) does the same with two pico-serial Line sticks.
frontend/dmodemd/test/fake_pico_line.cpp is a stick (1209:0002) whose line is
the exchange's, speaking its firmware's protocol: SET_HOOK and
SET_MONITOR are the exchange's switches, alternate setting 1 starts its
samples, and its ring detector's changes come as events. Besides the
calls, it checks:
- the country's line settings:
-c UK's at start-up, Australia's afterAT+GCI=09, and the USA's for a country the Si3056 datasheet has none for; - a stick whose line side does not answer (said, and dmodemd goes on);
- under Wine, a Windows whose WinUSB cannot stream (said, with what to do), and UsbDk taken where it is installed.
The pico-serial WB¶
frontend/dmodemd/test/pico_wb_test.py (ctest's dmodemd_pico_wb, labels alsa
and quick; about 20 s) runs two pico-serial WB sticks on a bare pair
instead of an exchange:
frontend/dmodemd/test/fake_pico_wb.cppis a stick (1209:0003) speaking its firmware's protocol.frontend/dmodemd/test/wb_pair.cppis the pair between the two, in real time at 96 kHz. It is the V.wb tests' simulated pair (dsp/test/vwb_line.cpp: 1 km of cable, the transformers, noise, each end's hybrid echo, and the two clocks 100 ppm apart). Each end's samples are played as the stick plays them: after a head start, and silence when they run dry.PAIR_KM,PAIR_SNR,PAIR_PPMandPAIR_ECHOchange the pair.
It checks:
- that a wrong
<bus>:<address>is refused, and that--usb listshows the stick; - that a rate the stick does not have is refused, and the others are set
(
SET_RATE); the 96000 Hz default; the gains' options (and one out of range refused); V.wb as the default modulation; a codec that does not come up (said); - a call:
AT&L1at both ends, the answerer by itself (S0=1), CONNECT at V.wb's rates at both ends, the LINK LEDs on, 32 kB each way at once byte for byte through V.42, and the hang-up (NO CARRIER at the other end, the LEDs off); - that both exit once the pair (the devices) has gone.
The Agere USB modem¶
frontend/dmodemd/test/agere_test.py (ctest's dmodemd_agere, label alsa)
runs two Agere USB modems at 8000 Hz. frontend/dmodemd/test/fake_agere.cpp is
one (047e:2892) whose line is the exchange's. It has:
- its vendor commands, its ARM's memory and function table, and its codec registers (the hook register 0xc0 is the exchange's switch);
- its ROM's isochronous stream: every 2 of the line's samples is a packet's time, in which it takes an OUT packet and answers it with an IN packet of the samples that have come. The status word's bit 0 is set while the ring signal is there;
- a transmit ring of 48 samples, which reports its overruns and
underruns, and a codec clock that can run off the USB's
(
FAKE_AGERE_DRIFT, ppm); - a Polaris DAA (
FAKE_AGERE_POLARIS), whose samples stay 0x8000 until its link is pulsed up, and whose ring bit can stick after a call (FAKE_AGERE_RING_STICKS).
It refuses a patch (command 9 stalls), and gives no samples while a patch's entry is in its function table. It reports its OH LED as it changes. The test checks:
- that dmodemd says what is wrong with a full speed device;
- that the samples come at 8000 a second: with a bulk patch's entries
left in the table (put back), with a ROM 1.0.6 (its clock left alone),
with no active configuration reported, with
DMODEM_AGERE_TRANSFERSandDMODEM_AGERE_DUMP, and after the LED probe (DMODEM_AGERE_LED_PROBE); - 8000 Hz with
-r 9600; - that a Polaris DAA's link comes up and is calibrated (1002 has one);
- a data call: rings (a Polaris's from codec register 0x0a8), caller ID,
the answer at the second ring, CONNECT at both ends, text each way;
the OH LED through it;
DMODEM_USB_TX_GAIN(autoand a fixed gain); the transmit ring kept with the codec's clock 300 ppm off (no overrun or underrun); and the call recorded (DMODEM_USB_REC); - no ring from a ring bit stuck after the call;
- a call dialled in pulses;
- that both exit once the exchange has gone.
The Apple USB Modem¶
frontend/dmodemd/test/apple_test.py (ctest's dmodemd_apple, label alsa)
runs two Apple USB Modems. frontend/dmodemd/test/fake_apple.cpp is one
(05ac:1401) whose line is the exchange's: its six interfaces and their
descriptors (the streams' format type I rates), the DAA's commands on
its registers (register 5's ONHM the exchange's monitor switch),
SET_HOOK_STATE the exchange's hook, and its ring detector's and phone
cord's notifications. It checks the interfaces found, a line side that
does not answer, a rate it has not, the country's registers, and the
same calls as the pico-serial Line's test
(apple-usb-modem-details.md).