Motorola Droid 4 - Modem
Today I had a first look at the modems of the Droid 4.
Conclusion
- Modem 1 (2G/3G):
- Name: MDM 6600
- USB ID: 22b8:2a70
- Drivers:
- bpwake - wakeup ttyO0 via gpio
- mdm6600_ctrl - modem gpio control via sysfs
- mdm6600_modem - usb-serial driver (unused?)
- qcusbnet - qmi driver, mainline provides alternative
- ts27010mux - 3GPP 27.010 mux ldisc for ttyO0
- Modem 2 (4G)
- Name: W3GLTE
- USB ID: 22b8:4267
- Drivers:
- wrigley_ctrl - modem gpio control via sysfs
- oob-wake - usb wakeup via gpio
- cdc-ether - usb network device
Next Steps
- check
ttyO0
usage -> strace Android'srild
, intercept messages - check
qcusbnet
vsqmi_wwan
- check if modem usb devices appear on mainline if we implement a the control gpio handling
- check if mainline should use the same sysfs interface to control the modem gpios
Details
First of all I had a look at the running LineageOS.
# dmesg | grep mdm [ 2.660858,0] mdm6600_ctrl mdm6600_ctrl: mdm_ctrl_probe [ 2.661590,0] radio_dev_register: register mdm6600 [ 3.386383,0] mdm6600_ctrl: modem status: undefined -> panic [power on] [ 3.476806,0] mdm_ctrl_powerup: powered Up mdm6600 [ 4.159942,0] mdm_ctrl_powerup: re-register bpwake device [ 4.307159,0] usbcore: registered new interface driver mdm6600 [ 7.378082,1] mdm6600 2-1:1.0: MDM 6600 modem usb-serial driver converter detected [ 7.379638,1] mdm6600 2-1:1.1: MDM 6600 modem usb-serial driver converter detected [ 7.409698,1] mdm6600 2-1:1.2: MDM 6600 modem usb-serial driver converter detected [ 7.456695,1] mdm6600 2-1:1.3: MDM 6600 modem usb-serial driver converter detected [ 7.575286,1] mdm6600 2-1:1.4: MDM 6600 modem usb-serial driver converter detected [ 136.740722,0] wakeup wake lock: mdm6600_oob.4 [ 136.980255,0] active wake lock mdm6600_oob.4, time left 26 [ 137.526275,0] wake lock mdm6600.4, expired [ 137.526733,0] wake lock mdm6600.3, expired [ 137.526977,0] wake lock mdm6600.2, expired [ 137.527191,0] wake lock mdm6600.1, expired [ 137.527618,0] wake lock mdm6600.0, expired # note: rild = android's modem daemon # lsof | grep "^rild" | grep "/dev/" | grep CHR | grep -v binder | sed "s/^.* \///g" | sort | uniq dev/null dev/qcqmi0 dev/qcqmi1 dev/qcqmi2 dev/qcqmi3 dev/ts0710mux0 dev/ts0710mux1 dev/ts0710mux10 dev/ts0710mux11 dev/ts0710mux2 dev/ts0710mux3 dev/ts0710mux4 dev/ts0710mux5 dev/ts0710mux8 dev/ts0710mux9 dev/ttyO0 # lsusb Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub Bus 003 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub Bus 002 Device 002: ID 22b8:2a70 Motorola: MSM6600 Bus 001 Device 002: ID 22b8:4267 Motorola: W3GLTE # ls -1 /dev/ttyUSB* /dev/ttyUSB0 /dev/ttyUSB1 /dev/ttyUSB2 /dev/ttyUSB3 /dev/ttyUSB4 # ls -1 /dev/qcqmi* /dev/qcqmi0 /dev/qcqmi1 /dev/qcqmi2 /dev/qcqmi3 # ls /sys/devices/platform/ | grep mdm mdm6600_ctrl mdm6600_modem # ls /sys/devices/platform/ | grep bp mapphone_bpwake # ls /sys/class/radio mdm6600 w3g_lte # ls /sys/class/radio/mdm6600 command power power_status status subsystem uevent # cat /sys/class/radio/mdm6600/power_status on # cat /sys/class/radio/mdm6600/status awake
Next I had a look at the vendor/stock kernel source to check the related drivers.
mapphone_bpwake
This driver is located in
arch/arm/mach-omap2/board-mapphone-bpwake.c
and contains a very simple driver, that request apwake_trigger_gpio as IRQ and
prevents device sleep for a second. Notes: ap = application processor, bp = baseband
processor. Basically the modem uses it to wakeup the OMAP processor.
feature_mdm6600_spi
Next mdm is referenced in
arch/arm/mach-omap2/board-mapphone-spi.c
.
The code checks for "feature_mdm6600_spi"
in Motrola's weird DT
blob and only does something if it finds the property. Fortunately its not in
there.
mapphone-modem
So let's move on to
arch/arm/mach-omap2/board-mapphone-modem.c
.
It's obvious from a quick view, that this file takes care of registering the
mdm6600_ctrl device and everything is being used from the last function in the
file: mapphone_mdm_ctrl_init()
.
It starts with a quick check for the powerup reason. Nothing is done, if the
kernel was only started because of a charger connection. Then it registers
the mapphone_bpwake driver getting the gpio from DT via a new name
("ipc_apwake_trigger"). I already got this from LineageOS using grep "BP -> AP" /sys/kernel/debug/gpio
- the used GPIO is 149, so I did not bother to pull this out of DT.
Next it gets the modem count from DT (2 on maserati). Then it goes through
the modems in a for loop getting the type from DT. On maserati the first
modem has type 0x1e0001 (MAPPHONE_BP_MDM6600) and the second modem is of
type 0x3000f (MAPPHONE_BP_W3GLTE). The driver is ready for a few other
modems, which are most likely used by other Motorola mapphone phones.
Anyways in the driver we have two new entrypoint functions:
mapphone_mdm6600_mdm_ctrl_init
(2G/3G modem) and
mapphone_w3glte_mdm_ctrl_init
(4G modem).
So let's have a look at the mdm6600 first. The related function
acquires lots of GPIOs: ipc_o_bp_pwron
,
ipc_i_bp_resout
, ipc_i_bp_ready
,
ipc_i_bp_ready2
, ipc_i_bp_ready3
,
ipc_o_ap_ready
, ipc_o_ap_ready2
,
ipc_o_ap_ready3
, ipc_o_ap_reset_bp
,
ipc_bpwake_trigger
and ipc_apwake_trigger
.
Finally it registers a platform device named "mdm6600_ctrl" providing
all those gpios via platform_data.
The 4G modem on the other hand starts with modifying pad control
register OMAP4_CTRL_MODULE_PAD_WKUP_CONTROL_USIMIO and
OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_PBIASLITE. Then it also acquires
a few gpios: lte_w_disable_b
, lte_force_flash
,
lte_power_en
and lte_reset_mcu
. Last but not
least it registers "wrigley_ctrl" with the name "w3g_lte" and gpios in
platform_data.
DT GPIOs
I had a quick look how mapphone gets its GPIOs from DT and found out its using
a property named "signalmap", which uses a custom encoding:
<gpio> <little-endian 20 byte string >
So I wrote a small python script decoding all values. This way I have the GPIO
number for every call to get_gpio_by_name().
GPIO | Name |
---|---|
183 | touch_panel_int |
173 | touch_panel_rst |
175 | akm8975_int |
122 | slider_data |
154 | volume_gpio |
34 | lis3dh_int |
172 | lm3532_reset |
174 | bt_reset_b |
148 | ipc_bpwake_trigger |
149 | ipc_apwake_trigger |
54 | ipc_o_bp_pwron |
56 | ipc_o_bp_flash_en |
8 | ipc_i_bp_resout |
52 | ipc_i_bp_ready |
53 | ipc_i_bp_ready2 |
55 | ipc_i_bp_ready3 |
49 | ipc_o_ap_reset_bp |
103 | ipc_o_ap_ready |
104 | ipc_o_ap_ready2 |
142 | ipc_o_ap_ready3 |
60 | ap_usb_otg_en1 |
0 | ap_usb_otg_en2 |
28 | vib_control_en |
50 | power_off |
94 | wlan_pmena |
100 | wlan_irqena |
38 | kpd_backlight_en |
61 | lte_w_disable_b |
3 | lte_reset_mcu |
4 | lte_wan_usb_en |
102 | lte_force_flash |
1 | lte_power_en |
62 | lte_wan_hostwake |
84 | cpcap-ind-swst0 |
85 | cpcap-ind-swst1 |
93 | cpcap-ind-reven0 |
87 | cpcap-ind-reven1 |
118 | cpcap-ind-chrgcmpl |
121 | cpcap-ind-chrgterm |
176 | sd_det_n |
177 | isl29030_int |
26 | spdif_en |
111 | spdif_1v8 |
9 | fact_kill_override |
178 | tmp105_int |
34 | lis3dh_int |
115 | mdm6600_usb_rwkup |
mapphone-usb
Next mdm is referenced in
arch/arm/mach-omap2/board-mapphone-usb.c
.
Again this file has a single entry point function: mapphone_usbhost_init
.
That function starts with not directly modem related USB initialization (i.e. forcing host mode).
Then it tries to get GPIOs for mdm6600_usb_rwkup
and lte_wan_hostwake
.
If it gets them platform devices are created using the GPIO as irq. The related platform device
for mdm6600 is named "mdm6600_modem". For the LTE modem an "oob-wake" platform device is registered,
which gets the LTE module's USB id (0x22b8:0x4267) as parameter.
MDM6X00 Control Driver & Wrigley Modem Control
mdm6600_ctrl driver is located in
drivers/misc/radio_ctrl/mdm6600_ctrl.c
.
Also there is a companion header in include/linux/radio_ctrl/mdm6600_ctrl.h
.
This driver implements a state machine for the modem's gpio lines and is interfacing to
userspace via /sys/class/radio/<dev>/<file>
. It takes a few
commands via the command
file: "shutdown", "powerup", "bootmode_normal"
or "bootmode_flash". The LTE modem uses drivers/misc/radio_ctrl/wrigley_ctrl.c
,
which provides the same userspace interface.
MDM6600 Serial Driver
mdm6600_modem driver is located in
drivers/usb/serial/mdm6600.c
. It's
a usb-serial driver supporting mdm6600 and mdm9600. This drivers is responsible for
the /dev/ttyUSB<num> entries. As far as I can see the related ttyUSB devices
are not used by Android, so the driver may not be needed.
Qualcomm Gobi
The /dev/qcqmi<num> is created by a driver, which lives inside
drivers/net/usb/qcusbnet
. It comes with a nice README
file, which describes its purpose:
This directory contains a driver for the Qualcomm Gobi 2000 CDMA/GSM modem. The device speaks a protocol called QMI with its host; this driver's responsibility is to multiplex transactions over QMI connections. Note that the relatively simple encoding defined in qmi.h and qmi.c is only the encapsulating protocol; the device speaks a more complex set of protocols embodied in the closed-source Gobi SDK which are necessary to use advanced features of the device. Furthermore, firmware (also proprietary) is needed to make the device useful. An open-source firmware loader exists: http://www.codon.org.uk/~mjg59/gobi_loader/.The driver registers as usb_driver under the name "QCUSBNet2k" and among others binds the device
USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x2a70, 0xff, 0xfb, 0xff)
.
The mainline kernel also has a driver for QMI, which is located in
drivers/net/usb/qmi_wwan.c
and supported by ofono!
Mux Driver
The /dev/ts0710mux<num> is created by "Motorola TS 27.010 Mux driver",
which lives in
drivers/misc/ts27010mux
. It's a line discipline,
which can be assigned to a UART device speaking 3GPP 27.010 protocol.
Most likely ttyO0 is used with the n_ts27010
ldisc. It should
be possible to check this using strace. The relevant numbers from the stock
kernel are:
Name | ldisc # | Description |
---|---|---|
N_TD_TS2710_UART | 25 | 3GPP TS 27.010 MUX over UART |
N_TD_TS2710_USB | 26 | 3GPP TS 27.010 MUX over USB |
N_TS2710 | 27 | 3GPP TS 27.010 MUX |
USB Out-of-Bounds Wake
The driver lives in
drivers/usb/misc/oob_wake.c
and has a useful
description in the related Kconfig entry:
This driver provides a simple mechanism to support USB wakeups from
gpios. This is needed in cases where the usb host or device doesn't
properly support remote wakeups.
cdc-ether
The standard cdc-ether (USB network) driver contains a small section
for the LTE modem:
#ifdef CONFIG_USB_OOBWAKE { /* Motorola Wrigley LTE CDC Ethernet Device */ USB_DEVICE_AND_INTERFACE_INFO(0x22b8, 0x4267, USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &wrigley_cdc_info, }, #endif