Introduction to I2C on Android
The Inter-Integrated Circuit (I2C) bus is a fundamental component of almost every modern embedded system, including Android devices. It’s a two-wire serial bus, primarily used for connecting low-speed peripheral ICs to processors. On Android phones and tablets, I2C buses are the backbone for communicating with an array of critical hardware: accelerometers, gyroscopes, magnetometers, touch screen controllers, ambient light sensors, proximity sensors, power management ICs (PMICs), camera modules, and even some secure elements. Understanding and enumerating these devices is crucial for hardware reverse engineering, security analysis, and low-level debugging.
This article will guide you through the process of identifying I2C buses, scanning for connected devices, interpreting the results, and mapping these devices to their functions on a typical Android system. We’ll cover both standard `i2c-tools` utilities and a programmatic approach for deeper analysis.
Understanding I2C Fundamentals
The I2C Protocol Briefly Explained
I2C operates on just two wires: Serial Data Line (SDA) and Serial Clock Line (SCL). It uses a master-slave architecture, where a master (typically the SoC) initiates data transfers with slave devices, each identified by a unique 7-bit (or sometimes 10-bit) address. Communication involves sending a START condition, the slave’s address, a read/write bit, and then data bytes, all synchronized by the SCL line. An ACK/NACK mechanism confirms data reception.
For reverse engineering, the key is that each device has a unique address on a specific bus. If we can scan all possible addresses on all accessible buses, we can uncover connected hardware.
Identifying I2C Buses on Your Android Device
On Linux-based systems like Android, I2C buses are typically exposed as device files under `/dev`. These files are usually named `i2c-0`, `i2c-1`, and so on. The number indicates the bus ID. Accessing these requires root privileges, as they are often protected by default.
To list the available I2C buses, you can use the following command via `adb shell` (after gaining root access, e.g., with `adb root` or `su` on the device):
ls -l /dev/i2c-*
You might see output similar to this:
crw-rw---- 1 root i2c 89, 0 2023-10-26 10:00 /dev/i2c-0crw-rw---- 1 root i2c 89, 1 2023-10-26 10:00 /dev/i2c-1crw-rw---- 1 root i2c 89, 2 2023-10-26 10:00 /dev/i2c-2crw-rw---- 1 root i2c 89, 3 2023-10-26 10:00 /dev/i2c-3
This indicates four I2C buses (`i2c-0` to `i2c-3`) are present and accessible.
Scanning I2C Devices: The i2cdetect Utility
Prerequisites: Root Access and i2c-tools
The `i2cdetect` utility is part of the `i2c-tools` package, a standard set of utilities for communicating with I2C devices. It probes all addresses on a specified bus and reports which ones respond. Many Android custom ROMs or development builds might include `i2c-tools`. If not, you’ll need to compile it for your device’s architecture or push pre-compiled binaries.
Ensure you have root access on your device. You can get a root shell using `adb shell` followed by `su` if your device is rooted, or use `adb root` if it’s an engineering/debug build.
Executing the Scan
Once you have `i2cdetect` available, you can scan an I2C bus. The `-y` flag disables interactive prompts, and the number after it specifies the bus ID (e.g., `0` for `/dev/i2c-0`).
su# i2cdetect -y 0
A typical output from `i2cdetect` looks like this:
0 1 2 3 4 5 6 7 8 9 a b c d e f00: -- -- -- -- -- -- -- -- -- -- -- -- --10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --20: -- -- -- -- -- -- -- -- 28 -- -- -- -- -- -- --30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --50: 50 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --70: -- -- -- -- -- -- -- --
In this example, addresses `0x28` and `0x50` responded on bus `0`. You’d repeat this for all discovered buses (e.g., `i2cdetect -y 1`, `i2cdetect -y 2`, etc.).
Manual I2C Scanning with C/C++
Why Manual Scanning?
While `i2cdetect` is convenient, there are scenarios where a programmatic approach is necessary: if `i2c-tools` isn’t available, for custom probing sequences, or for integration into larger analysis frameworks. A simple C program can replicate `i2cdetect`’s basic functionality.
A Simple C Scanner Program
This program opens an I2C bus device file, iterates through all 7-bit addresses, and attempts to set each address as a slave address. If the `ioctl` call succeeds, it indicates a device is present.
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include <sys/ioctl.h>#include <linux/i2c-dev.h> // For I2C_SLAVE and I2C_PEC#define I2C_BUS_PATH_FORMAT "/dev/i2c-%d"int main(int argc, char *argv[]) { if (argc != 2) { fprintf(stderr, "Usage: %s <bus_number>n", argv[0]); return 1; } int bus_num = atoi(argv[1]); char i2c_bus_path[32]; snprintf(i2c_bus_path, sizeof(i2c_bus_path), I2C_BUS_PATH_FORMAT, bus_num); int file = open(i2c_bus_path, O_RDWR); if (file < 0) { perror("Failed to open I2C bus"); fprintf(stderr, "Ensure you have root permissions and bus %d exists.n", bus_num); return 1; } fprintf(stdout, "Scanning I2C bus %d (%s):n", bus_num, i2c_bus_path); fprintf(stdout, " 0 1 2 3 4 5 6 7 8 9 a b c d e fn"); for (int i = 0; i < 128; i += 16) { fprintf(stdout, "%02x: ", i); for (int j = 0; j < 16; j++) { int addr = i + j; if (addr < 0x03 || addr > 0x77) { // Reserved addresses fprintf(stdout, " "); continue; } // Try to set the slave address if (ioctl(file, I2C_SLAVE, addr) < 0) { // Device not found or error fprintf(stdout, " --"); } else { // Device found fprintf(stdout, " %02x", addr); } } fprintf(stdout, "n"); } close(file); return 0;}
Compile this on your Android device (or cross-compile) using `aarch64-linux-android-gcc` (or similar for your architecture) and push the executable to the device. Then run:
adb push scan_i2c /data/local/tmp/su/data/local/tmp/scan_i2c 0
This provides a similar output to `i2cdetect` but gives you full control over the probing logic.
Interpreting Scan Results and Device Mapping
Once you have a list of active I2C addresses per bus, the next step is to identify what hardware those addresses correspond to. This is often the most challenging part of hardware reverse engineering.
Common I2C Device Addresses
Many I2C devices use standardized or commonly known addresses:
- 0x18 – 0x1F: Typically accelerometers (e.g., Bosch BMA250E, STMicroelectronics LIS3DH)
- 0x68 – 0x69: Gyroscopes and IMUs (e.g., InvenSense MPU-6050 series)
- 0x0C – 0x0F: Magnetometers (e.g., AKM AK8963)
- 0x38 – 0x3F: Touchscreen controllers (e.g., various Synaptics, FocalTech, Goodix ICs)
- 0x50 – 0x57: EEPROMs or flash memory (e.g., serial NOR/NAND chips)
- 0x40 – 0x4F: Power Management ICs (PMICs) or charging controllers
- 0x70 – 0x7F: Specific display controllers or expanders
These are just common ranges; actual addresses can vary by manufacturer and model. Datasheets are invaluable here.
Leveraging the Device Tree (DTB/DTS)
For modern Android devices (using Linux kernel 3.x and above), the Device Tree Blob (DTB) is the definitive source of hardware information. It describes all hardware components, including I2C buses and the devices connected to them, their addresses, and driver bindings. The DTB is loaded by the bootloader and passed to the kernel.
You can often find the Device Tree on your device at `/sys/firmware/devicetree/base/`. You can pull the entire `base` directory or specific files. The main DTB is usually part of the boot image, but its contents are exposed via `/sys/firmware/devicetree/base/` at runtime.
To get a human-readable Device Tree Source (DTS) file, you’ll need the `dtc` (Device Tree Compiler) utility. You might find a `.dtb` file in `/proc/device-tree` or extract it from the boot image. Once you have a `.dtb` file:
adb pull /proc/device-tree . # Or wherever your dtb might be locateddtc -I dtb -O dts -o device_tree.dts device_tree.dtb
Search the `device_tree.dts` file for `i2c@` nodes. These sections will define the I2C controllers and their child nodes, which represent the slave devices, complete with their `reg` property (the I2C address) and `compatible` string (which often hints at the device’s type or manufacturer).
i2c@78b5000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x78b5000 0x0 0x200>; status = "okay"; #address-cells = <1>; #size-cells = <0>; accelerometer@18 { compatible = "bosch,bma250e"; reg = <0x18>; interrupt-parent = <&gpio_tlmm>; interrupts = <95 0x2000>; }; pmic@40 { compatible = "qcom,pm8004"; reg = <0x40>; };};
This snippet clearly shows an accelerometer at `0x18` and a PMIC at `0x40` on an I2C bus. Cross-referencing these addresses with your `i2cdetect` output provides definitive identification.
Advanced Analysis: Kernel Modules and Drivers
The Linux kernel uses modules (drivers) to interact with hardware. You can inspect loaded kernel modules to find out which I2C drivers are active. This can provide clues about the types of devices present.
su# lsmod
Look for modules with names related to common sensors (e.g., `bma250e_i2c`, `mpu6050_i2c`), PMICs (e.g., `pm8004_i2c`), or touchscreen controllers. The `/sys/bus/i2c/drivers` directory can also list registered I2C drivers:
su# cat /sys/bus/i2c/drivers/*/name
This might yield names like `bma250e`, `mpu6050`, etc., directly indicating the drivers in use for I2C devices.
Conclusion
Mastering I2C enumeration on Android is a powerful skill for anyone delving into embedded systems, hardware reverse engineering, or platform security. By systematically identifying I2C buses, scanning for devices, and correlating addresses with information from the Device Tree and kernel modules, you can build a comprehensive map of your device’s internal hardware landscape. This knowledge opens doors to deeper understanding, custom driver development, and uncovering potential vulnerabilities in low-level hardware interactions.
Android Mobile Specs & Compare Directory
Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!
Compare Devices Specs →