0%

MT7603U 移植问题

Linux 4.9 移植 MT7603U 时遇到 no file read method 错误导致读取配置出错,设备工作不正常,本文记录解决方法及相关知识点

现象

Linux 4.9 编译 MT7603U 之后,复制 MT7603USTA.dat,启动之后出现错误

[ 2804.262246] no file read method
[ 2804.265509] Read file "/etc/Wireless/RT2870STA/MT7603USTA.dat" failed(errCode=-1)!
[ 2804.273097] ERROR!!! [ 2804.275221] RTMPReadParametersHook failed, Status[=0xffffffff]
[ 2804.338301] !!! rt28xx init fail !!!

分析

内核中可以使用 printk("func name:%pF\n", ptr) 来打印函数名

搜索驱动代码找到出错函数代码并添加打印如下

int RtmpOSFileRead(RTMP_OS_FD osfd, char *pDataPtr, int readLen)
{
    printk("read:%p\n", osfd->f_op->read);
    printk("open:%pF\n", osfd->f_op->open);
    printk("name:%s\n", osfd->f_op->owner->name);
    /* The object must have a read method */
    if (osfd->f_op && osfd->f_op->read) {
        return osfd->f_op->read(osfd, pDataPtr, readLen, &osfd->f_pos);
    } else {
      DBGPRINT(RT_DEBUG_ERROR, ("no file read method\n"));
      return -1;
    }
}

打印信息如下

[ 3886.638124] read:  (null)
[ 3886.640808] open:nfs_file_open+0x0/0x54
[ 3886.644741] name:(null)
[ 3886.647211] no file read method
[ 3886.650381] Read file "/etc/Wireless/RT2870STA/MT7603USTA.dat" failed(errCode=-1)!
[ 3886.658037] ERROR!!! [ 3886.660161] RTMPReadParametersHook failed, Status[=0xffffffff]
[ 3886.722205] !!! rt28xx init fail !!!

内核中找到 kernel/4.9.y/fs/nfs/file.c

const struct file_operations nfs_file_operations = {
    .llseek     = nfs_file_llseek,
    .read_iter  = nfs_file_read,
    .write_iter = nfs_file_write,
    .mmap       = nfs_file_mmap,
    .open       = nfs_file_open,
    .flush      = nfs_file_flush,
    .release    = nfs_file_release,
    .fsync      = nfs_file_fsync,
    .lock       = nfs_lock,
    .flock      = nfs_flock,
    .splice_read    = generic_file_splice_read,
    .splice_write   = iter_file_splice_write,
    .check_flags    = nfs_check_flags,
    .setlease   = simple_nosetlease,
};

nfs 文件系统下确实不存在 read/write 导致不正常,但是 Linux 2.6 下工作正常,代码如下

const struct file_operations nfs_file_operations = {
    .llseek     = nfs_file_llseek,
    .read       = do_sync_read,
    .write      = do_sync_write,
    .aio_read   = nfs_file_read,
    .aio_write  = nfs_file_write,
#ifdef CONFIG_MMU
    .mmap       = nfs_file_mmap,
#else
    .mmap       = generic_file_mmap,
#endif
    .open       = nfs_file_open,
    .flush      = nfs_file_flush,
    .release    = nfs_file_release,
    .fsync      = nfs_file_fsync,
    .lock       = nfs_lock,
    .flock      = nfs_flock,
    .splice_read    = nfs_file_splice_read,
    .check_flags    = nfs_check_flags,
    .setlease   = nfs_setlease,
};

查找 do_sync_read/do_sync_write 在文件 fs/read_write.c,在 Linux 4.9 中查找替换函数如下

static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
{
    struct iovec iov = { .iov_base = (void __user *)buf, .iov_len = len };
    struct kiocb kiocb;
    struct iov_iter iter;
    ssize_t ret;

    init_sync_kiocb(&kiocb, filp);
    kiocb.ki_pos = *ppos;
    iov_iter_init(&iter, WRITE, &iov, 1, len);

    ret = filp->f_op->write_iter(&kiocb, &iter);
    BUG_ON(ret == -EIOCBQUEUED);
    if (ret > 0)
        *ppos = kiocb.ki_pos;
    return ret;
}

ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,
            loff_t *pos)
{
    if (file->f_op->write)
        return file->f_op->write(file, p, count, pos);
    else if (file->f_op->write_iter)
        return new_sync_write(file, p, count, pos);
    else
        return -EINVAL;
}

EXPORT_SYMBOL(__vfs_write);

ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
{
    mm_segment_t old_fs;
    const char __user *p;
    ssize_t ret;

    if (!(file->f_mode & FMODE_CAN_WRITE))
        return -EINVAL;

    old_fs = get_fs();
    set_fs(get_ds());
    p = (__force const char __user *)buf;
    if (count > MAX_RW_COUNT)
        count =  MAX_RW_COUNT;
    ret = __vfs_write(file, p, count, pos);
    set_fs(old_fs);
    if (ret > 0) {
        fsnotify_modify(file);
        add_wchar(current, ret);
    }
    inc_syscw(current);
    return ret;
}

EXPORT_SYMBOL(__kernel_write);

ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{
    ssize_t ret;

    if (!(file->f_mode & FMODE_WRITE))
        return -EBADF;
    if (!(file->f_mode & FMODE_CAN_WRITE))
        return -EINVAL;
    if (unlikely(!access_ok(VERIFY_READ, buf, count)))
        return -EFAULT;

    ret = rw_verify_area(WRITE, file, pos, count);
    if (!ret) {
        if (count > MAX_RW_COUNT)
            count =  MAX_RW_COUNT;
        file_start_write(file);
        ret = __vfs_write(file, buf, count, pos);
        if (ret > 0) {
            fsnotify_modify(file);
            add_wchar(current, ret);
        }
        inc_syscw(current);
        file_end_write(file);
    }

    return ret;
}

EXPORT_SYMBOL(vfs_write);

使用 vfs_write/vfs_read 来替换之前的函数解决这个问题,修改如下

int RtmpOSFileRead(RTMP_OS_FD osfd, char *pDataPtr, int readLen)
{
    /* The object must have a read method */
    if (osfd->f_op && osfd->f_op->read) {
        return vfs_read(osfd, pDataPtr, readLen, &osfd->f_pos);
    } else {
      DBGPRINT(RT_DEBUG_ERROR, ("no file read method\n"));
      return -1;
    }
}

Ref

  1. Compilation failed for rtl8723be driver on Xubuntu 16.04.2
  2. Broadcom Wireless Driver for Linux Kernel 4.0