linux 字符设备基本框架
实现功能
自动申请设备号
自动创建设备节点
#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这一部分忘的差不多了,接下来补一补,接下来学习更多驱动相关的内容。
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!