linux 字符设备——驱动框架(一)
字符设备驱动代码编写
一、驱动模块代码编写
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/uaccess.h>
static int data;
static int chr_open (struct inode * inode, struct file * file){
printk("character device open\n");
return 0;
}
static int chr_release (struct inode * inode, struct file * file){
printk("character device close\n");
return 0;
}
ssize_t chr_read (struct file * file, char __user * buf, size_t size, loff_t * offset){
if(copy_to_user(buf, &data,sizeof(data))){
printk("copy failed\n");
return -EFAULT;
}
return sizeof(data);
}
ssize_t chr_write (struct file * file, const char __user * buf, size_t size, loff_t * offset){
if(copy_from_user(&data, buf, sizeof(data))){
printk("copy failed\n");
return -EFAULT;
}
return sizeof(data);
}
/* file_operations 用来存储驱动内核模块提供的对设备进行各种操作的函数的指针。
* 该结构体的每个域都对应着驱动内核模块用来处理某个被请求的事务的函数的地址。
*/
static struct file_operations chr_fop = {
.owner = THIS_MODULE,
.open = chr_open,
.release = chr_release,
.read = chr_read,
.write = chr_write,
};
static struct cdev *my_chr_cdev;
static struct class *chr_class;
static dev_t device;
/* 驱动模块的入口函数 */
static int chr_test_init(void){
if (device){
/* 静态申请设备号 */
register_chrdev_region(device,1,"chr_dev");
}else{
/* 动态申请设备号 */
alloc_chrdev_region(&device,0, 1,"chr_dev");
}
/* 申请空间 */
my_chr_cdev = cdev_alloc();
/* 注册字符设备驱动 */
cdev_init(my_chr_cdev,&chr_fop);
cdev_add(my_chr_cdev, device, 1);
/* 创建设备节点 */
chr_class = class_create(THIS_MODULE, "mydev");
class_device_create(chr_class, NULL, device, NULL,"mydev");
return 0;
}
/* 驱动模块的退出函数 */
static void chr_test_exit(void){
/* 删除设备节点 */
class_device_destroy(chr_class, device);
class_destroy(chr_class);
/* 注销字符设备驱动 */
cdev_del(my_chr_cdev);
/* 释放空间 */
cdev_put(my_chr_cdev);
/* 释放设备号和相应的设备名 */
unregister_chrdev_region(device, 1);
}
/* 这个宏将 chr_test_init 函数修饰为模块的入口函数 */
module_init(chr_test_init);
/* 这个宏将 chr_test_exit 函数修饰为模块的退出函数 */
module_exit(chr_test_exit);
/* 遵循GPL协议 */
MODULE_LICENSE("GPL");
二、编写Makefile
#内核源码树路径
KERN_DIR = /work/system/kernel/source/linux-2.6.22.6
#目标文件
obj-m += first.o
all:
make -C $(KERN_DIR) M=`pwd` modules
clean:
make -C $(KERN_DIR) M=`pwd` modules clean
三、测试应用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int buf;
int main(int argc, char** argv){
int fd;
if((fd = open("/dev/mydev",O_RDWR))<0){
printf("open device failed\n");
return -1;
}
/* 将数据写到驱动程序,再读出来验证 */
for(int i=0; i<10; i++){
write(fd,&i,sizeof(i));
read(fd,&buf,sizeof(buf));
printf("read the data is:%d\n",buf);
sleep(1);
}
close(fd);
return 0;
}
四、编译测试
1、编译内核驱动
将源代码和Makefile考到linux系统,然后执行
make
将frist.ko文件copy 到开发板上,这里我使用的是nfs(网络文件系统)
cp first.ko /work/system/nfs/
2、编译测试程序
编译
arm-linux-gcc -o main main.c
将main文件copy 到开发板上
cp main /work/system/nfs/
3、测试
插入内核模块
insmod frist.ko
查看模块信息
lsmod
执行应用程序
./main·
五、小结
(在设备驱动中常用的命令)
加载模块
insmod xxx.ko
查看内核中已插入的模块
lsmod
卸载模块
rmmod xxx
查看模块的描述信息
modinfo xxx.ko