本次只概述字符设备驱动开发,直接上干货
linux驱动开发流程如下:
实例:增加一个新的系统调用
1、添加新的内核函数 打开/arch/arm/sys_arm.c文件
asmlinkage int sys_add(int x,int y)
{
printk("enter sys_add\n");
return x+y;
}
2、更新头文件 arch/arm/include/asm/unistd.h
#define __NR_add (__NR_SYSCALL_BASE+374)
3、更新系统调用表 arch/arm/kernel/calls.S
CALL(sys_add)
4、make
5、开发板加载新内核
测试程序:
#include <stdio.h>
/*
int add(int x,int y)
{
return syscall(374,x,y);
}
*/
int main()
{
int result = 0;
result = syscall(374,12,13);
printf("sys_add call result = %d\n",result);
return 0;
}
字符设备(顺序读写,不带缓冲)
块设备 (读写的顺序不固定,带有读写缓冲)
网络设备
例如字符设备驱动框架,硬件上有一个字符设备,内核中就有一个cdev结构与之对应。
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev; //设备号
unsigned int count;
};
每一个cdev都有一个设备号,设备号(32bit)= 主设备号(高12bit)+次设备号(低20bit)
主设备号:代表一个类型的设备
次设备号:用于区分该设备中的不同个体
设备号的选取:
-》静态分配:
看现在的内核中那些主设备号没有被使用,选择一个使用
cat /proc/devices
Documentation\divice.txt
#include <linux/init.h>
#include <linux/module.h>
#include CDD_MAJOR 253
#include CDD_MINOR 0
/*声明一个设备号*/
dev_t dev = 0;
int __init add_init(void)
{
int ret = 0;
//dev = CDD_MAJOR << 20 + CDD_MINOR;
dev = MKDEV(CDD_MAJOR,CDD_MINOR);
/*注册设备号
第一个参数:注册的气势设备号,
第二个参数:连续注册的设备号的个数,
第三个参数:the name of the device or driver.
*/
ret = register_chrdev_region(dev, 1, "cdd_dome");
if(ret<0)
{
printk("register_chrdev_region failed\n");
goto failure_register_chrdev;
}
return 0;
failure_register_chrdev:
return ret;
}
void __exit cdd_exit(void)
{
/*注销设备号*/
unregister_chrdev_region(dev, 1);
}
module_init(cdd_init);
module_exit(cdd_exit);
-》动态分配:
u32 cdd_major = 0;
u32 cdd_minor = 0;
cdev的操作函数
cdev_init(...)//初始化cdev
cdev_add(...)//注册cdev
cdev_del(...)//注销cdev
至此在linux下字符设备的注册流程已完成。