龙旗科技Linux驱动开发 二面试题

1. 详细介绍你做过的Linux驱动项目,架构设计和技术难点

答案要点:

  1. 项目背景:开发XX设备驱动(传感器/显示屏/摄像头),基于XX平台(高通/MTK/RK),内核版本
  2. 驱动架构: 设备类型:字符设备/平台设备/I2C设备/SPI设备分层设计:应用层接口、驱动核心层、硬件抽象层使用的子系统:input子系统/V4L2/IIO等
  3. 技术难点: 中断处理:高频中断优化,上下半部设计并发控制:多进程访问同步,使用的锁机制性能优化:DMA传输、缓冲区设计、功耗优化调试定位:死锁、内存泄漏、时序问题
  4. 项目成果:稳定性测试结果、性能指标、解决的关键问题

2. I2C驱动的工作原理?如何编写I2C设备驱动?

答案要点:

1. I2C总线特点

  • 两线制:SCL(时钟)、SDA(数据),支持多主多从
  • 7位/10位设备地址,速率:标准100Kbps、快速400Kbps、高速3.4Mbps
  • 起始/停止条件、应答机制

2. Linux I2C驱动框架

  • I2C核心层:提供统一接口
  • I2C总线驱动(adapter):控制I2C控制器硬件
  • I2C设备驱动(client):控制具体I2C设备

3. 编写I2C设备驱动

// 定义设备ID表
static const struct i2c_device_id mydev_id[] = {
    { "mydevice", 0 },
    { }
};

// 设备树匹配表
static const struct of_device_id mydev_of_match[] = {
    { .compatible = "vendor,mydevice", },
    { }
};

// probe函数
static int mydev_probe(struct i2c_client *client,
                       const struct i2c_device_id *id) {
    struct mydev_data *data;
    u8 buf[2];
    
    // 分配私有数据
    data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
    i2c_set_clientdata(client, data);
    
    // 读取设备ID
    i2c_smbus_read_byte_data(client, REG_ID);
    
    // 或使用i2c_transfer
    struct i2c_msg msg = {
        .addr = client->addr,
        .flags = I2C_M_RD,
        .len = 2,
        .buf = buf,
    };
    i2c_transfer(client->adapter, &msg, 1);
    
    return 0;
}

// 驱动结构
static struct i2c_driver mydev_driver = {
    .driver = {
        .name = "mydevice",
        .of_match_table = mydev_of_match,
    },
    .probe = mydev_probe,
    .remove = mydev_remove,
    .id_table = mydev_id,
};

module_i2c_driver(mydev_driver);

4. 常用I2C操作函数

  • i2c_smbus_read_byte_data() / i2c_smbus_write_byte_data()
  • i2c_transfer():更灵活,支持复杂时序

3. SPI驱动的工作原理?与I2C有什么区别?

答案要点:

1. SPI总线特点

  • 四线制:SCLK(时钟)、MOSI(主出从入)、MISO(主入从出)、CS(片选)
  • 全双工通信,速度更快(可达几十MHz)
  • 主从模式,一主多从(通过不同CS选择)

2. SPI与I2C对比

线数

4线(+每个从设备1根CS)

2线

速度

更快(几十MHz)

较慢(400Kbps)

通信方式

全双工

半双工

硬件复杂度

较高(需要更多引脚)

较低

应用场景

高速设备(Flash、LCD)

低速设备(传感器、EEPROM)

3. SPI驱动编写

// 设备树匹配
static const struct of_device_id mydev_of_match[] = {
    { .compatible = "vendor,mydevice", },
    { }
};

// probe函数
static int mydev_probe(struct spi_device *spi) {
    struct mydev_data *data;
    u8 tx_buf[2] = {0x01, 0x02};
    u8 rx_buf[2];
    
    // 配置SPI参数
    spi->mode = SPI_MODE_0;  // CPOL=0, CPHA=0
    spi->bits_per_word = 8;
    spi->max_speed_hz = 1000000;  // 1MHz
    spi_setup(spi);
    
    // 写数据
    spi_write(spi, tx_buf, 2);
    
    // 读数据
    spi_read(spi, rx_buf, 2);
    
    // 全双工传输
    struct spi_transfer xfer = {
        .tx_buf = tx_buf,
        .rx_buf = rx_buf,
        .len = 2,
    };
    struct spi_message msg;
    spi_message_init(&msg);
    spi_message_add_tail(&xfer, &msg);
    spi_sync(spi, &msg);
    
    return 0;
}

static struct spi_driver mydev_driver = {
    .driver = {
        .name = "mydevice",
        .of_match_table = mydev_of_match,
    },
    .probe = mydev_probe,
    .remove = mydev_remove,
};

module_spi_driver(mydev_driver);

4. SPI模式

  • Mode 0:CPOL=0, CPHA=0(空闲低电平,第一个边沿采样)
  • Mode 1:CPOL=0, CPHA=1
  • Mode 2:CPOL=1, CPHA=0
  • Mode 3:CPOL=1, CPHA=1

4. 字符设备驱动的编写流程?file_operations结构体的作用?

答案要点:

1. 字符设备驱动框架

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

static dev_t dev_num;
static struct cdev my_cdev;
static struct class *my_class;

// 实现文件操作函数
static int my_open(struct inode *inode, struct file *filp) {
    printk("Device opened\n");
    return 0;
}

static ssize_t my_read(struct file *filp, char __user *buf,
                       size_t count, loff_t *f_pos) {
    char kernel_buf[100] = "Hello from kernel";
    if (copy_to_user(buf, kernel_buf, count))
        return -EFAULT;
    return count;
}

static ssize_t my_write(struct file *filp, const char __user *buf,
                        size_t count, loff_t *f_pos) {
    char kernel_buf[100];
    if (copy_from_user(kernel_buf, buf, count))
        return -EFAULT;
    printk("Received: %s\n", kernel_buf);
    return count;
}

static int my_release(struct inode *inode, struct file *filp) {
    printk("Device closed\n");
    return 0;
}

// file_operations结构体
static struct file_operations my_fops = {
    .owner = THIS_MODULE,
    .open = my_open,
    .read = my_read,
    .write = my_write,
    .release = my_release,
};

// 初始化函数
static int __init my_init(void) {
    // 1. 分配设备号
    alloc_chrdev_region(&dev_num, 0, 1, "mydevice");
    
    // 2. 初始化cdev
    cdev_init(&my_cdev, &my_fops);
    my_cdev.owner = THIS_MODULE;
    
    // 3. 添加cdev到系统
    cdev_add(&my_cdev, dev_num, 1);
    
    // 4. 创建设备类
    my_class = class_create(THIS_MODULE, "myclass");
    
    // 5. 创建设备节点 /dev/mydevice
    device_create(my_class, NULL, dev_num, NULL, "mydevice");
    
    return 0;
}

// 退出函数
static void __exit my_exit(void) {
    device_destroy(my_class, dev_num);
    class_destroy(my_class);
    cdev_del(&my_cdev);
    unregister_chrdev_region(dev_num, 1);
}

module_init(my_init);
module_exit(my_exit);
MODULE_LICENSE("GPL");

2. file_operations常用成员

  • open:打开设备
  • read:读取数据
  • write:写入数据
  • unlocked_ioctl:设备控制
  • mmap:内存映射
  • poll:轮询
  • release:关闭设备

5. 中断处理的上半部和下半部?tasklet和workqueue的区别?

答案要点:

1. 为什么分上下半部

  • 中断处理要快,不能长时间占用CPU
  • 上半部(硬中断):紧急处理,关中断执行,尽快返回
  • 下半部(软中断):延迟处理,开中断执行,可以被打断

2. tasklet实现

// 定义tasklet
static void my_tasklet_func(unsigned long data) {
    // 延迟处理的工作
    printk("Tasklet running\n");
}

DECLARE_TASKLET(my_tasklet, my_tasklet_func, 0);

// 中断处理函数
static irqreturn_t my_irq_handler(int irq, void *dev_id) {
    // 上半部:快速处理
    // 读取硬件状态,清除中断标志
    
    // 调度tasklet
    tasklet_schedule(&my_tasklet);
    
    return IRQ_HANDLED;
}

3. workqueue实现

// 定义工作队列
static struct work_struct my_work;

static void my_work_func(struct work_struct *work) {
    // 可以睡眠的延迟处理
    msleep(100);  // 允许睡眠
    printk("Work running\n");
}

// 初始化
INIT_WORK(&my_work, my_work_func);

// 中断中调度
static irqreturn_t my_irq_

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式面试八股文全集 文章被收录于专栏

这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

全部评论

相关推荐

01-30 09:45
燕山大学 Java
喵_coding:这种直接跑就完事了 哪有毕业了才签合同 任何offer和三方都没有的
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务