[RK3128][Android 6.0] u-blox GPS芯片 MAX-7Q调试记录



硬件平台:RK3128
Android版本:6.0
Kernel版本:3.10.92

最近调试一款gps芯片,u-blox的MAX-7Q,支持I2C及UART接口,由于UART口不够用,只能用I2C接口,之前没有接触过I2C接口的gps芯片,并且在hardware层调用I2C也是第一次接触,故记录在此。

1、在hardware目录下新建文件夹u-blox,将客户提供的hal层源码复制到该目录下。
修改hardware/u-blox/gps/u-blox.conf中SERIAL_DEVICE为 /dev/i2c-2,如下:

# Device to be used by the driver #SERIAL_DEVICE/dev/ttyACM0 # USB (Default interface) #SERIAL_DEVICE/dev/ttymxc4 # UART on Sabre SD (IMX6Q) SERIAL_DEVICE/dev/i2c-2# I2C on Sabre SD (IMX6Q) #SERIAL_DEVICE/dev/ttyO3# UART on Panda board #SERIAL_DEVICE/dev/i2c-4# I2C on Panda board



2、复制ubx-setup.sh到device/rockchip/rk312x/目录下,并在device/rockchip/rk312x/rk312x.mk中添加如下内容:

# add for u-blox PRODUCT_COPY_FILES += \ device/rockchip/rk312x/ubx-setup.sh:system/etc/ubx-setup.sh \ hardware/u-blox/gps/gps.conf:system/etc/gps.conf \ hardware/u-blox/gps/u-blox.conf:system/etc/u-blox.conf



3、在device/rockchip/common/init.rockchip.rc中添加服务:

# add for u-blox service ubx-setup /system/etc/ubx-setup.sh user root group root oneshot



并在on boot中开启服务:

# add for u-blox chmod 0755 /system/etc/ubx-setup.sh start ubx-setup



4、使用 mmm hardware/u-blox/gps/编译hal,然后make一下。这个地方比较奇怪,我用make全编hardware/u-blox/gps/并没有编译到,又用mmm才编到。生成out/target/product/rk312x/system/lib/hw/gps.default.so文件说明编译成功。

5、烧录后,先用打印logcat搜索u-blox确认服务运行,如果没有运行,可能是selinux导致,用adb shell setenforce 0来确认是否是selinux问题。

遇到的问题:
移植完成后,logcat中有大量以下错误:

01-18 08:51:06.2742891027 D u-blox: IOCTL for reading nob registers failed. io = -1 01-18 08:51:06.3242891027 D u-blox: IOCTL for reading nob registers failed. io = -1 01-18 08:51:06.3752891027 D u-blox: IOCTL for reading nob registers failed. io = -1 01-18 08:51:06.4252891027 D u-blox: IOCTL for reading nob registers failed. io = -1



先来看hardware层出错的地方代码:
ubx_serial.cpp

ssize_t CSerialPort::i2cGetNumAvailBytes(void) const { if (m_fd <= 0) return -1; struct i2c_rdwr_ioctl_data { struct i2c_msg __user *msgs; __u32 nmsgs; }; struct i2c_rdwr_ioctl_data rdwr; struct i2c_msg out_in[2], *out, *in; unsigned char out_data; unsigned char in_data[2]; ssize_t result = -1; out = out_in; in = out_in + 1; out_data = https://www.it610.com/article/0xFD; out->addr = s_i2cRecvAddr; out->flags = 0; // indicates a write out->len = 1; out->buf = &out_data; out->scl_rate = 200 * 1000; in->addr = s_i2cRecvAddr; in->flags = I2C_M_RD; in->len = 2; in->buf = in_data; in->scl_rate = 200 * 1000; in_data[0] = 0; in_data[1] = 0; rdwr.nmsgs = 2; rdwr.msgs = out_in; int io = ioctl(m_fd, I2C_RDWR, &rdwr); if (io < 0) { UBXSERLOG("IOCTL for reading nob registers failed. io = %d",io); } else { result = in_data[0] * 256 + in_data[1]; } return result; }



说明ioctl出错了,在kernel中添加打印,发现在i2cdev_ioctl_rdrw出错,rdwr_pa[1].len是一个超大值,导致校验不过,代码如下:
I2c-dev.c

static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client, unsigned long arg) { struct i2c_rdwr_ioctl_data rdwr_arg; struct i2c_msg *rdwr_pa; u8 __user **data_ptrs; int i, res; if (copy_from_user(&rdwr_arg, (struct i2c_rdwr_ioctl_data __user *)arg, sizeof(rdwr_arg))) return -EFAULT; /* Put an arbitrary limit on the number of messages that can * be sent at once */ if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS) return -EINVAL; rdwr_pa = memdup_user(rdwr_arg.msgs, rdwr_arg.nmsgs * sizeof(struct i2c_msg)); if (IS_ERR(rdwr_pa)) return PTR_ERR(rdwr_pa); data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL); if (data_ptrs == NULL) { kfree(rdwr_pa); return -ENOMEM; } res = 0; for (i = 0; i < rdwr_arg.nmsgs; i++) { /* Limit the size of the message to a sane amount */ if (rdwr_pa[i].len > 8192) { //这里出错 res = -EINVAL; break; } data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf; rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len); if (IS_ERR(rdwr_pa[i].buf)) { res = PTR_ERR(rdwr_pa[i].buf); break; } /* * If the message length is received from the slave (similar * to SMBus block read), we must ensure that the buffer will * be large enough to cope with a message length of * I2C_SMBUS_BLOCK_MAX as this is the maximum underlying bus * drivers allow. The first byte in the buffer must be * pre-filled with the number of extra bytes, which must be * at least one to hold the message length, but can be * greater (for example to account for a checksum byte at * the end of the message.) */ if (rdwr_pa[i].flags & I2C_M_RECV_LEN) { if (!(rdwr_pa[i].flags & I2C_M_RD) || rdwr_pa[i].buf[0] < 1 || rdwr_pa[i].len < rdwr_pa[i].buf[0] + I2C_SMBUS_BLOCK_MAX) { res = -EINVAL; break; } rdwr_pa[i].len = rdwr_pa[i].buf[0]; } } if (res < 0) { int j; for (j = 0; j < i; ++j) kfree(rdwr_pa[j].buf); kfree(data_ptrs); kfree(rdwr_pa); return res; } res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs); while (i-- > 0) { if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) { if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf, rdwr_pa[i].len)) res = -EFAULT; } kfree(rdwr_pa[i].buf); } kfree(data_ptrs); kfree(rdwr_pa); return res; }




查了好久,明明hardware层len=2,怎么到kernel就不对了?

【[RK3128][Android 6.0] u-blox GPS芯片 MAX-7Q调试记录】原因在于i2c_msg这个结构体,它在kernel中定义在:
kernel/include/uapi/linux/i2c.h
而hardware层引用的定义在:
bionic/libc/kernel/uapi/linux/i2c.h

在hardware层调用I2C,i2c_msg的定义必须与内核一致,rockchip修改了内核,在kernel/include/uapi/linux/i2c.h中为i2c_msg添加了一个成员,并且没有同步到bionic。所以两边引用的i2c_msg定义不一样,结果就是当然出错了。
bionic/libc/kernel/uapi/linux/i2c.h这个文件是自动生成的,当修改了内核后,运行bionic/libc/kernel/tools/update_all.py脚本来同步(注意:此脚本慎用,因为我发现rockchip并没有用此脚本同步,而是直接修改了bionic下文件)。
将bionic/libc/kernel/uapi/linux/i2c.h中i2c_msg定义修改成跟内核一样就可以了。

注意:可能还要在device/rockchip/common/ueventd.rockchip.rc中添加i2c-2的权限。
/dev/i2c-20666systemsystem




    推荐阅读