Linux字符设备驱动框架
来源:互联网 发布:yy会员签到软件 编辑:程序博客网 时间:2024/05/16 13:56
推荐周立功先生的书籍《嵌入式Linux开发教程(下册)》,该书籍用于学习开发是不错的参考资料。
字符设备框架
char_dev_frame.c:
a 驱动程序
#include <linux/init.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/cdev.h>#include <linux/device.h>static int major = 0; /* 主设备号默认值 */static int minor = 0; /* 次设备号默认值 */module_param(major, int, S_IRUGO);module_param(minor, int ,S_IRUGO);struct cdev *char_cdev; /* c_dev数据结构 */static dev_t dev_no; /* 设备编号 */static struct class *char_cdev_class;static struct class_device *char_cdev_class_device;#define DEVICE_NAME "char_cdev"#define DEVICE_FILE_NAME "/dev/char_cdev"static int char_cdev_open(struct inode *inode, struct file *file){ try_module_get(THIS_MODULE); printk(KERN_INFO DEVICE_NAME"opened! \n"); return 0;}static int char_cdev_release(struct inode *inode, struct file *file){ printk(KERN_INFO DEVICE_NAME"closed! \n"); module_put(THIS_MODULE); return 0;}static ssize_t char_cdev_read(struct file *file, char *buf, size_t count, loff_t *f_ops){ printk(KERN_INFO DEVICE_NAME"read method! \n"); return count;}static ssize_t char_cdev_write(struct file *file, const char *buf, size_t count, loff_t *f_ops){ printk(KERN_INFO DEVICE_NAME"write method! \n"); return count;}static int char_cdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ printk(KERN_INFO DEVICE_NAME"ioctl method! \n"); return 0;}struct file_operations char_cdev_fops = { .owner = THIS_MODULE, .read = char_cdev_read, .write = char_cdev_write, .ioctl = char_cdev_ioctl, .open = char_cdev_open, .release = char_cdev_release};static int __init char_cdev_init(void){ int ret; /* 分配设备号 */ if(major > 0) { /* 静态分配 */ dev_no = MKDEV(major, minor); ret = register_chrdev_region(dev_no, 1, DEVICE_NAME); } else { ret = alloc_chrdev_region(&dev_no, minor, 1, DEVICE_NAME); major = MAJOR(dev_no); } if(ret < 0) { printk(KERN_ERR"can't get major %d \n", major); return -1; } /* 注册设备 */ char_cdev = cdev_alloc(); /* 分配cdev_设备结构 */ if(char_cdev != NULL) { cdev_init(char_cdev, &char_cdev_fops); /* 初始化char_cdev结构 */ char_cdev->owner = THIS_MODULE; if(cdev_add(char_cdev, dev_no, 1) != 0) { printk(KERN_ERR"add cdev error!"); goto error; } } else { printk(KERN_ERR"cdev_alloc_error! \n"); return -1; } /* 注册设备节点 */ char_cdev_class = class_create(THIS_MODULE, "char_cdev_class"); if(IS_ERR(char_cdev_class)) { printk(KERN_INFO"create class error \n"); return -1; } char_cdev_class_device = class_device_create(char_cdev_class, NULL, dev_no, NULL, "char_cdev"); return 0;error: unregister_chrdev_region(dev_no, 1); return ret;}static void __exit char_cdev_exit(void){ cdev_del(char_cdev); unregister_chrdev_region(dev_no, 1); class_device_unregister(char_cdev_class_device); class_destroy(char_cdev_class);}module_init(char_cdev_init);module_exit(char_cdev_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("huchunrong");
添加设备节点时,因为kernel版本为2.6.22.6
,应使用class_device_create
,否则一直提示警告。后期的某个版本开始,class_device_create
被device_create
替代。关于这两个函数的分析,网上分析很多。
b 驱动程序Makefile
# Makefile2.6ifneq ($(KERNELRELEASE),)# kbuild syntax. dependency relationshsip of files and target modules are listed here.obj-m := char_dev_frame.oelsePWD := $(shell pwd)KVER := 2.6.22.6KDIR := /home/eva/Developer/Linux/kernel/linux-2.6.22.6all: $(MAKE) -C $(KDIR) M=$(PWD) modulesclean: rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versionsendif
上面的Makefile为Linux2.6内核下一段比较通用的Makefile。
aKERNELRELEASE
是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE
没有被定义,所以make将读取执行else之后的内容;
b 当make的目标为all时,-C $(KDIR)
跳转到内核源码目录下读取Makefile;M=$(PWD) 然后返回到当前目录继续读入、执行当前的Makefile。
c 测试程序
char_dev_test.c:
#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/ioctl.h>#include <errno.h>#include <fcntl.h>#define DEVICE_FILE_NAME "/dev/char_cdev"int main(int argc, char *argv[]){ int fd, ret; char data; fd = open(DEVICE_FILE_NAME, O_RDWR); if(fd < 0) { perror("Open "DEVICE_FILE_NAME" Failed\n"); exit(1); } ret = read(fd, &data, 1); if(!ret) { perror("Read "DEVICE_FILE_NAME" Failed\n"); exit(1); } data = 1; ret = write(fd, &data, 1); if(!ret) { perror("Write "DEVICE_FILE_NAME" Failed\n"); exit(1); } ret = ioctl(fd, 0, NULL); if(ret) { perror("Ioctl "DEVICE_FILE_NAME" Failed\n"); exit(1); } close(fd); return 0;}
编译
a 编译驱动
因为事先已经写好了Makefile,直接执行make
命令即可
make
b 编译测试程序
arm-linux-gcc char_dev_test.c -o char_dev_test.o
加载和运行
拷贝可执行文件到nfs共享目录:cp char_dev_frame.ko char_dev_test.o ../../../bin/
在嵌入式设备上挂载nfs共享目录:mount -t nfs -o intr,nolock,rsize=1024,wsize=1024 192.168.1.12:/home/eva/Developer/Linux/bin /mnt
当文件比较大时,使用该指令成功率比较高
安装驱动程序:insmod char_dev_frame.ko
执行测试程序:./char_dev_test.o
如果驱动程序里没有进行创建设备文件,即没有执行class_device_create
,也可以根据主设备号和次设备号,手动创建字符设备节点mknod /dev/char_cdev c 252 0
查看当前系统支持的设备cat /proc/devices
查看设备节点ls /dev
警告及错误
现象描述:
编译时在read
和write
函数附近,提示warning: initialization from incompatible pointer type
,一般原因为数据类型不匹配
解决措施:
定义read
函数时,第三个参数类型为size_t
,而不是ssize_t,改正后如下:static ssize_t char_cdev_read(struct file *file, char *buf, ssize_t count, loff_t *f_ops)
,write
函数如此
现象描述:
编译时device_create
函数提示警告warning: too many arguments for format
,原因为jz2440的内核版本linux2.6.22.6,还不支持device_create
函数
解决措施:
改用class_device_create
函数,完整示例如下char_cdev_class_device = class_device_create(char_cdev_class, NULL, dev_no, NULL, "char_cdev");
现象描述: insmod
安装驱动后,无法在/dev/
路径下找到设备节点,只能手动创建设备文件节点,但驱动程序里已经正确调用class_device_create
函数
解决措施:
和烧写的文件系统有关,烧写fs_mini_mdev.yaffs2
文件系统,而不是 fs_mini.yaffs2
,具体原因未知
- Linux字符设备驱动框架
- linux 字符设备驱动框架
- Linux字符设备驱动框架
- linux ------ 字符设备驱动框架
- linux 字符设备驱动框架
- Linux字符设备驱动框架
- Linux字符设备驱动框架
- linux驱动字符设备框架
- 一步步理解linux字符设备驱动框架
- linux字符设备驱动框架理解
- linux字符设备驱动框架(一)
- linux字符设备驱动框架(二)
- linux驱动开发之字符设备框架
- linux驱动开发之字符设备框架
- 字符设备驱动框架
- linux设备驱动--globalmem字符设备框架分析
- linux设备驱动--globalmem字符设备框架分析
- linux设备驱动--globalmem字符设备框架分析
- iOS平台下闪退原因汇总(一):"Ran out of trampolines of type 0/1/2" 运行时间错误
- 类型转换函数 & 转换构造函数
- Groovy中对xml的操作补充
- laravel5.5 常用 artisan 命令
- Java基础2:JVM学习总结
- Linux字符设备驱动框架
- 进度条
- POJ 2653 Pick-up sticks(线段规范相交)
- 20170907
- oracle替换掉字段中存在的特殊符号以及空格
- 491. Increasing Subsequences
- Koa学习1
- NIO使用实例
- 怎么让文字不再div溢出,文字在div里面