实现功能
- 自动申请设备号
- 自动创建设备节点
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/device.h>
dev_t devno; //设备号,高12为为主设备号, 低20位为次设备号
static const unsigned int baseminor=0; //次设备号起始
static const unsigned int dev_count=3; //设备个数
static char *demo_name="demo_dev"; // 设备名称
static struct cdev *demo_cdev; //cdev 结构体指针
static struct class* demo_class; //class 结构体指针
static int demo_open (struct inode *inode, struct file *file)
{
printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
return 0;
}
static ssize_t demo_write (struct file *file, const char __user *user, size_t size, loff_t *loff_t)
{
printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
return 0;
}
static int demo_release (struct inode *inode, struct file *file)
{
printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
return 0;
}
//Linux使用file_operations结构访问驱动程序的函数,这个结构的每一个成员的名字都对应着一个调用
//将驱动程序操作连接到设备编号
struct file_operations fops={
.owner=THIS_MODULE,
.open=demo_open,
.write=demo_write,
.release=demo_release,
};
// 每个函数的的输入输出信息都可以使用source insight 在linux 源码中找到
static int __init demo_init(void)
{
int ret;
//0. 让内核分配给我们一个尚未使用的主设备号
ret=alloc_chrdev_region(&devno,baseminor,dev_count,demo_name);
// 错误判断
if (ret<0){
printk(KERN_INFO "alloc_chrdev_region failed------\n");
goto err1;
}
printk(KERN_INFO"major=%d\n",MAJOR(devno)); //打印出申请的主设备号
//1. 申请cdev 结构体内存
demo_cdev=cdev_alloc();
if(NULL==demo_cdev){
printk(KERN_INFO"demo_cdev failed --\n");
goto err2;
}
//2. 对cdev 结构体 进行初始化
cdev_init(demo_cdev,&fops);
//3. 将cdev结构体加入到系统当中
ret=cdev_add(demo_cdev,devno,dev_count);
if (ret<0){
printk(KERN_INFO"cdev_add failed --\n");
goto err2;
}
//一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建 一个类,这个类存放于sysfs下面,
demo_class=class_create(THIS_MODULE,demo_name);
if(IS_ERR(demo_class)){
printk(KERN_INFO"class_create failed --\n");
goto err3;
}
//调用device_create(…)函数来在/dev目录下创建相应的设备节点
device_create(demo_class,NULL,devno,NULL,demo_name);
printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
return 0;
err3:
cdev_del(demo_cdev);
err2:
unregister_chrdev_region(devno,dev_count);
err1:
return ret;
}
static void __exit demo_exit(void)
{
class_destroy(demo_class);
device_destroy(demo_class,devno);
cdev_del(demo_cdev);
unregister_chrdev_region(devno,dev_count);
printk(KERN_INFO "---%s---%s---%d---\n",__FILE__,__func__,__LINE__);
}
module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL"); //描述模块的许可证
编译驱动模块
Makefile 文件如下
KDIR=/home/qiu/linux2.6.35-source/linux-2.6.35.3 #内核路径
all:
make -C $(KDIR) M=`pwd` modules
clean:
make -C $(KDIR) M=`pwd` modules clean
rm -f modules.order
obj-m+=led_drv1.o #obj-m +=xxx.o意思是该模块不会编译到zImage
在该目录下使用make命令完成对 文件的编译
驱动测试代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
int main(int argc, char **argv)
{
int fd; //声明设备描述符
int val = 1; //随便定义变量传入到
fd = open("/dev/xxx", O_RDWR); //根据路径打开设备
if(fd < 0) //打开失败
printf("can't open\n");
write(fd, &val, 4); //根据文件描述符调用write
return 0;
}
字符设备驱动这一部分还是学了很久的,算是自己linux驱动的入门吧,Makefile这一部分忘的差不多了,接下来补一补,接下来学习更多驱动相关的内容。
网友评论