【linux操作系统驱动分类】
字符设备: 串口,led,按键,一次只读取一个字节数据
块设备: 一次读取多个字节(512字节), 硬盘,内存
网络设备: 一次读取多字节数据
本文引用地址://www.hqyj.com/emb/Column/7464.html
ls -l /dev 查看文件属性
c开头的叫字符设备文件(char), 串口,Led,I2C
b开头的叫块设备文件 (block)
sudo mknod /dev/led c 500 0 //生成一个设备,跟char-read.ko关联
制作一个字符设备
【设备号】
设备和驱动是怎么关联到一起的?
是通过500 0 关联到一起的
500 主设备号(linux系统将一些常用的硬件设备进行了编号,比如串口主设备号4 ...)
0 次设备号(第一个串口 次设备号0, 1 2.....)
设备号能唯一表示一个设备,所以不可以重复
fs4412_led_drv.c 驱动文件
【如何实现一个字符设备驱动】
1 因为字符设备驱动属于一个内核模块,所以也使用内核模块模板
2 字符设备驱动使用cdev架构
【cdev中重要的宏】
设备编号:
MKDEV(major, minor);
//功能:构造设备编号
major 主设备号, minor 此设备号
执行完此函数,返回一个32位的设备号,其中前12位是主设备号,后20位是次设备号
500 1
00011111 0100 000000000000000000001
#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
MAJOR(dev_t);
//功能:取主设备号
从32位的设备号中取出主设备号
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
MINOR(dev_t); //取次设备号
从32位的设备号中取出次设备号
申请设备编号:
int register_chrdev_region(dev_t from, unsigned count, const char *name)
功能:将模块与主次设备号关联
参数:
from,起始设备编号
count,次设备号个数
name,设备名
返回值:
成功返回0,失败返回错误码。
释放设备编号:
void unregister_chrdev_region(dev_t from, unsigned count)
功能:取消模块与主次设备号关联
参数:
from,起始设备编号
count,次设备号个数
驱动模块和250 0 设备关联
int hello_major = 250;
int hello_minor = 0;
int number_of_devices = 1;
static int __init hello_2_init (void)
{
int result;
dev_t dev = 0;
dev = MKDEV (hello_major, hello_minor);
result = register_chrdev_region (dev, number_of_devices, "hello");
if (result<0) {
printk (KERN_WARNING "hello: can't get major number %d\n", hello_major);
return result;
}
printk (KERN_INFO "Registered character driver\n");
return 0;
}
static void __exit hello_2_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);
unregister_chrdev_region (devno, number_of_devices);
printk (KERN_INFO "Char driver cleaned up\n");
}
module_init (hello_2_init);
module_exit (hello_2_exit);
sudo insmod hello.ko
cat /proc/devices | grep hello //linux会将注册好的设备添加进/proc/devices文件中
sudo rmmod hello
struct cdev{
const struct file_operations *ops; //字符设备驱动结构体
dev_t dev; //主次设备号
unsigned int count; //设备数量
};
初始化字符设备:
void cdev_init(struct cdev *cdev, const struct file_operations *fops);
添加设备:
int cdev_add(struct cdev *p, dev_t dev, unsigned count)
删除设备:
void cdev_del(struct cdev *p)
下面的代码是完成一个简单的字符设备驱动程序,此程序没有实际功能,知识完成了open, close函数
//定义主设备号,次设备号
#include
#include
#include
#include
#include
#include
int hello_major = 250; //主设备号
int hello_minor = 0; //次设备号
struct cdev cdev; //c -> char dev->device 字符设备的主要结构体
static int hello_open (struct inode *inode, struct file *file) //应用成执行open函数会调用到它
{
printk ("11111111111111111111111111 device opened\n");
return 0;
}
struct file_operations hello_fops = { //将hello_open hello_close 与 open , close 关联
.owner = THIS_MODULE,
.open = hello_open,
};
static void char_reg_setup_cdev (void) //完成cdev结构体的初始化
{
dev_t devno;
devno = MKDEV (hello_major, hello_minor); //MKDEV宏能将主次设备号放在一个32位的变量中,变成一个编号
cdev_init (&cdev, &hello_fops); //将hello_fops 放到cdev结构体中
cdev.owner = THIS_MODULE;
cdev_add (&cdev, devno , 1); //将设备号再放到cdev结构体中, 1代表 子设备数量1个
}
static int __init hello_init (void) //注册模块时要执行此函数
{
int result;
dev_t devno;
devno = MKDEV (hello_major, hello_minor);
result = register_chrdev_region (devno, 1, "hello");
char_reg_setup_cdev ();
printk ("char device registered\n");
return 0;
}
static void __exit hello_exit (void)
{
dev_t devno = MKDEV (hello_major, hello_minor);
cdev_del (&cdev); //删除cdev
unregister_chrdev_region (devno, 1); //删除250, 0号设备
}
module_init (hello_init);
module_exit (hello_exit);
完成一个应用程序,测试驱动
#include
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
fd = open("/dev/hello", O_RDWR);
if(fd > 0)
{
printf("file open success\n");
}
else
{
printf("file open failed\n");
}
}
制作字符设备的过程
1 制作驱动模块
1)将驱动代码 写到hello.c中
2)make (会生成hello.ko文件)
2 sudo insmod hello.ko
3 sudo mknod /dev/hello c 250 0 //生成一个设备,跟char-read.ko关联
4 gcc -o test test.c //将应用层的test.c 编译生成test可执行文件
5 sudo ./test
6 dmesg