linux驱动学习笔记1--字符型驱动的编写
来源:互联网 发布:巴基斯坦人 知乎 编辑:程序博客网 时间:2024/05/22 17:27
本文用两个char型数组来模拟字符型设备,驱动源码如下:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
int dev1_registers[5]; //两个数组模拟两个字符型设备
int dev2_registers[5];
struct cdev cdev; //定义一个设备变量
dev_t devno; //定义一个设备号变量
/*文件打开函数*/
int mem_open(struct inode *inode, struct file *filp)
{
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
if (num==0)
filp->private_data = dev1_registers; //把设备地址赋值给filp->private_data,在下面的read函数里需要调用设 //备的地址
else if(num == 1)
filp->private_data = dev2_registers;
else
return -ENODEV; //无效的次设备号
return 0;
}
/*文件释放函数*/
int mem_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*读函数*/
static ssize_t mem_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; //在open函数里,已经把设备的地址赋值给了filp->private_data
/*判断读位置是否有效*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*读数据到用户空间*/
if (copy_to_user(buf, register_addr+p, count))
{
ret = -EFAULT;
}
else
{
*ppos += count;
ret = count;
}
return ret;
}
/*写函数*/
static ssize_t mem_write(struct file *filp, const char __user *buf, size_t size, loff_t *ppos)
{
unsigned long p = *ppos;
unsigned int count = size;
int ret = 0;
int *register_addr = filp->private_data; //在open函数里,已经把设备的地址赋值给了filp->private_data
/*分析和获取有效的写长度*/
if (p >= 5*sizeof(int))
return 0;
if (count > 5*sizeof(int) - p)
count = 5*sizeof(int) - p;
/*从用户空间写入数据*/
if (copy_from_user(register_addr + p, buf, count))
ret = -EFAULT;
else
{
*ppos += count;
ret = count;
}
return ret;
}
/* seek文件定位函数 */
static loff_t mem_llseek(struct file *filp, loff_t offset, int whence)
{
loff_t newpos;
switch(whence) {
case SEEK_SET:
newpos = offset;
break;
case SEEK_CUR:
newpos = filp->f_pos + offset;
break;
case SEEK_END:
newpos = 5*sizeof(int)-1 + offset;
break;
default:
return -EINVAL;
}
if ((newpos<0) || (newpos>5*sizeof(int)))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
/*文件操作结构体*/
static const struct file_operations mem_fops =
{
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
};
/*设备驱动模块加载函数*/
static int memdev_init(void)
{
/*初始化cdev结构*/
cdev_init(&cdev, &mem_fops);
/* 注册字符设备 */
alloc_chrdev_region(&devno, 0, 2, "memdev"); //注册两个设备,此设备号的起始值为0
cdev_add(&cdev, devno, 2); //向系统添加设备
}
/*模块卸载函数*/
static void memdev_exit(void)
{
cdev_del(&cdev); /*注销设备*/
unregister_chrdev_region(devno, 2); /*释放设备号*/
}
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
对应的应用程序如下:(write-mem.c为写设备,read-mem.c为读设备)
/**************** write-mem.c***************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = 0;
int src = 2013;
/*打开设备文件*/
fd = open("/dev/memdev0",O_RDWR);
/*写入数据*/
write(fd, &src, sizeof(int));
/*关闭设备*/
close(fd);
return 0;
}
/*********************read-mem.c****************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = 0;
int dst = 0;
/*打开设备文件*/
fd = open("/dev/memdev0",O_RDWR);
/*写入数据*/
read(fd, &dst, sizeof(int));
printf("dst is %d\n",dst);
/*关闭设备*/
close(fd);
return 0;
}
整套驱动及应用软件的开发流程如下:
1.写好驱动程序
2.为驱动写Makefile,且Makefile文件要与驱动放在同一目录下
3.执行make命令,将驱动memdev.c编译成memdev.ko
4.执行命令insmod memdev.ko加载驱动到内核
执行命令lsmod可以查看到加载的所有驱动
执行命令rmmodmemdev.ko卸载驱动
5.为设备创建设备文件
执行命令mknod/dev/设备名 c 主设备号 次设备号
参数说明:
设备名可以自己定义 (设备名是在应用程序在调用open活write时要用的,例 如fd=open("/dev/memdev0",O_RDWR),系统根据设备名取找到相应的驱动)
c表示此设备为字符型设备(b表示为块设备)
主设备号在本例中是系统自己分配的,通过命令cat/proc/devices可以查看驱动的主设备号
次设备号也是用户自己定义,范围必须在[0,255]
本例中的具体命令:mknod /dev/memdev0 c 123 0 (假设查看到的是123)
由于本例中注册了两个设备
/* 以下两行为注册字符设备,在驱动代码里完成 */
alloc_chrdev_region(&devno, 0, 2, "memdev");
cdev_add(&cdev, devno, 2);
由于注册了两个设备,所以需要创建两个设备文件
现在创建第二个设备文件mknod /dev/memdev1 c 123 1
创建第二个设备文件时要保证第二个设备文件的名字和第一个设备文件名字不同,且次设备号加1
驱动中open函数的原型如下:
int mem_open(struct inode *inode, struct file *filp)
{
/*获取次设备号*/
int num =MINOR(inode->i_rdev);
if (num==0)
filp->private_data = dev1_registers; //把设备1的地址赋值给//filp->private_data
else if(num== 1)
filp->private_data = dev2_registers;//把设备2的地址赋值//filp->private_data
else
return-ENODEV; //无效的次设备号
return 0;
}
当应用程序调用open("/dev/memdev0",O_RDWR)时,打开的是设备文件memdev0,对应的次设备号为0, 所以num=0;
当应用程序调用open("/dev/memdev1",O_RDWR)时,打开的是设备文件memdev1,对应的次设备号为1,所以num=1。
具体Makefile如下:
obj-m := memdev.o
KDIR := /usr/src/linux-headers-3.2.0-24-generic-pae
all:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko*.o *.mod.o *.mod.c *.symvers *.bak *.order
Makefile的说明:
obj-m 表示将驱动编译成模块(modues)
KDIR 是本系统内核的路径,因为在编译驱动时要用到里面的文件。若编译的驱动要运行在其他版本的linux系统上,还需下载其他版本的系统文件,并把KDIR改为相应版本linux内核的路径。
$(KDIR)是对路径的引用 make -C $(KDIR)等价与 make -C/usr/src/linux-headers-3.2.0-24-generic-pae
$(PWD)是对当前路径的引用 ,也是编译后生成.ko文就存放的路径
clean :删除编译过程中产生的中间文件
- linux驱动学习笔记1--字符型驱动的编写
- Linux驱动学习笔记;字符设备驱动
- linux字符设备驱动学习笔记1
- linux设备驱动学习笔记(1)-字符设备驱动
- 学习笔记 --- LINUX USB设备驱动的编写
- linux字符设备驱动学习笔记2
- linux字符设备驱动学习笔记3
- [Linux驱动]字符设备驱动学习笔记(一)
- Linux驱动学习笔记(3)字符设备驱动
- linux字符设备驱动-重新学习-笔记-1
- ARM开发之linux字符型驱动的编写----LED驱动为例
- linux设备驱动学习笔记7------led流水灯的驱动编写
- linux设备驱动学习笔记6------AD驱动的编写入门
- 阻塞型字符设备驱动的编写
- 嵌入式学习笔记——字符设备驱动编写
- Linux的驱动编写入门——hello的块驱动(字符驱动)
- Linux驱动学习笔记(2)----字符型设备驱动基本框架
- Linux驱动编写的方法学习
- apache2.4模块开发学习
- (转)SQL更新删除数据
- uva 846 Steps(找规律)
- 使用java开发的2048游戏(附代码)
- wikioi高精度练习之减法c++
- linux驱动学习笔记1--字符型驱动的编写
- HDU 1847 Good Luck in CET-4 Everybody! (博弈论sg)
- Android RecyclerView And CardView
- ListView的Item不可点击和Item布局重复问题
- <<C和指针>>第1章之编程练习心得20140704
- HDU4089-Activation(概率DP)
- php pconnect 长连接原理
- 背景设计
- 后缀数组(长度不小于k的公共子串的个数)