Linux 子系统介绍
常见子系统
常见的子系统主要可以归为一下几类:
1、Misc 杂项设备
2、常见的子系统有IIO 子系统
3、Input 输入子系统
4、RTC 子系统
5、显示子系统
6、MTD 子系统
7、V4L2 与 ASoC 子系统
这里由于难易程度不一样,我们从简单的类型开始介绍
Misc 杂项设备
在linux系统中,每个字符设备都会有自己的设备号 (如串口设备、输入设备),他们被称为标准设备,但是在开发的过程中经常会有一些模块难以归类到标准设备中,而这些设备就是杂项设备
Misc 结构体
这里有两个核心的结构体
struct miscdevice是描述设备信息的,
struct file_operations是表述该设备的操作的,当linux用户层执行某些操作的时候,内核会调用这里绑定的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| static struct miscdevice xxx_miscdev = { .minor = MISC_DYNAMIC_MINOR, .name = "xxx_device", .fops = &xxx_fops, };
static const struct file_operations xxx_fops = { .owner = THIS_MODULE, .read = xxx_read, .write = xxx_write, .open = xxx_open, .unlocked_ioctl = xxx_ioctl, .release = xxx_release, };
|
struct file_operations结构体中对应的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static int xxx_open(struct inode *inode, struct file *file) {
struct xxx_dev *dev = container_of(file->private_data, struct xxx_dev, miscdev); file->private_data = dev;
return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| static ssize_t xxx_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { struct xxx_dev *dev = file->private_data;
char data_buf[64]; if (count > sizeof(data_buf)) { count = sizeof(data_buf); }
if (copy_from_user(data_buf, buf, count)) { return -EFAULT; }
i2c_smbus_write_byte_data(dev->i2c, reg, data_buf[0]); spi_write_reg(dev->spi, reg, data_buf[0]);
return count; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| static long xxx_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct xxx_dev *dev = file->private_data; switch (cmd) { case 0x01: break; case 0x02: break; default: return -EINVAL; } return 0; }
|
1 2 3 4 5
| static int xxx_release(struct inode *inode, struct file *file) { return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| static ssize_t xxx_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ struct xxx_dev *dev = file->private_data;
u8 valueA = i2c_smbus_read_byte_data(dev->i2c, reg); u8 valueB = 0; int ret = spi_read_reg(dev->spi, reg, &valueB); if (ret < 0) { return ret; }
char data[64]; int len = snprintf(data, sizeof(data), "valueA: %d, valueB: %d\n", valueA, valueB);
if(copy_to_user(buf, data, len)){ return -EFAULT; } return len; }
|
probe函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| static int xxx_probe() {
dev->miscdev.minor = MISC_DYNAMIC_MINOR; dev->miscdev.name = devm_kasprintf(&client->dev, GFP_KERNEL, "xxx-%d-%02X", client->adapter->nr, client->addr); dev->miscdev.fops = &xxx_fops;
ret = misc_register(&dev->miscdev); if(ret < 0){ printk("xxx 设备注册失败!\n"); return ret; }
}
|
remove函数
1 2 3 4 5 6 7 8 9 10 11 12
| static int xxx_remove(){
misc_deregister(&dev->miscdev); printk("xxx 设备正在被移除!正在执行 remove 函数...\n");
return 0; }
|
总结
其实从代码结构来看SPI 和 I2C驱动的主体结构体是相互对应,主要是它们所操作的对应的硬件的不同,需要构建相应的硬件操作对象(为了解耦)。这里是一个复杂点,关于操作的硬件对象我们在下一章节介绍