一、设备号的相关知识
1、设备号的作用
主设备号用来标识与设备文件相连的驱动程序。次设备号用来辨别操作的是哪个设备。
主设备号用来反映设备类型,次设备号用来区分同类型的设备。
2、设备号处理的相关用法
A、dev_t
内核中使用dev_t来描述一个设备号,其实质为unsigned int 32位整数,其中高12位位主设备 号,低20位为次设备号。
B、主次设备号的提取
分别使用MAJOR(dev_t dev)与MINOR(dev_t dev)分解出主设备号与次设备号。
C、设备号的申请
静态申请
方法:int register_chrdev_region(dev_t from,unsigned count,const char *name)
功能:申请使用从from开始的count个设备号(主设备号不变,次设备号增加)
参数:from:希望申请使用的设备号 count:希望申请使用的设备号数目 name:设备名(体现在proc/devices)
动态分配
方法:int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name)
功能:请求内核动态分配count个设备号,且此设备号从baseminor开始。
参数:dev:分配到的设备号 baseminor:起始此设备号 count:需要分配的设备号数目 name:设备名
D、设备号的注销
方法:void unregister_chrdev_region(dev_t from,unsigned count)
功能:释放从from开始的count个设备号
二、重要的字符设备驱动结构
1、struct file
功能:代表一个打开的文件。系统中每个打开的文件在内核空间都有一个关联的struct file它由内核在打开文件时创建,文件关闭后释放。一个文件可以对应多个file结构。
重要成员:
loff_t f_pos ;/*表示文件读写位置*/
struct file_operations *f_op
2、struct inode
功能:用来记录文件的物理上得信息。一个文件只有一个inode结构。
重要成员:dev_t i_rdev:设备号
3、struct file_operations
功能:一个函数指针的集合,定义能再设备上进行的操作。结构中德成员指向驱动中德函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL。
格式:
Struct file_operations mem_fops={
.owner = THIS_MODULE,
.llseek = xx_seek,
.read = xx_read,
.write = xx_write,
.ioctl = xx_ioctl,
.open = xx_open,
.release = xx_release,
}
三、字符设备注册
1、在linux 2.6内核中,字符设备使用struct cdev来描述。
2、步骤:
A、分配cdev
方法:struct cdev *cdev_alloc(void)
B、初始化cdev
方法:void cdev_init(struct cdev *cdev,counst struct file_operatings *fops)
C、添加cdev
方法:int cdev_add(struct cdev *p,dev_t dev,unsigned cont)
参数:
p:待添加到内核的字符设备结构
dev:设备号
count:添加的设备个数
四、设备操作
1、Open方法:在大部分驱动程序中主要实现初始化设备以及标明次设备号。
2、Release方法:关闭设备
3、Read、Write方法
函数:ssize_t xxx_read(struct file *filp,char __user *buff,size_t count,loff_t *offp);
ssize_t xxx_write(struct file *filp,char __user *buff,size_t count,loff_t *offp);
参数:filp是文件指针,count是请求传输的数据缓存,offp指出文件当前的访问位置。
参数buff是用户空间指针。因此,它不能被内核代码直接引用。原因如下:
用户空间指针在内核空间时可能根本无效的--没有那个地址的映射。
内核提供了专门的函数用于访问用户空间的指针,例如:
int copy_from_user(void *to,const void __user *from,int n)
int copy_to_user(void __user *to,const void *from,int n)
五、设备注销
方法:int cdev_del(struct cdev *p)
六、实例
1、memdev.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "memdev.h"
static int mem_major = MEMDEV_MAJOR;
module_param(mem_major, int, S_IRUGO);
struct mem_dev *mem_devp;
struct cdev cdev;
int mem_open(struct inode *inode,struct file *filp)
{
struct mem_dev *dev;
/*获取次设备号*/
int num = MINOR(inode->i_rdev);
if(num >= MEMDEV_NR_DEVS)
return -ENODEV;
dev = &mem_devp[num];
/*将设备描述结构指针赋值给文件私有数据指针*/
filp->private_data = dev;
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;
struct mem_dev *dev = filp->private_data; /*获取设备结构体指针,实质由open参数inode相关联*/;
if(p >= MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
if (copy_to_user(buf,(void*)(dev->data + p),count))
{
ret = - EFAULT;
}
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "read %d bytes(s) from %ld\n",count,p);
}
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;
struct mem_dev *dev = filp->private_data;
if(p >= MEMDEV_SIZE)
return 0;
if(count > MEMDEV_SIZE - p)
count = MEMDEV_SIZE - p;
if(copy_from_user(dev->data + p,buf,count))
ret = - EFAULT;
else
{
*ppos += count;
ret = count;
printk(KERN_INFO "written %d bytes(s) from %ld\n",count,p);
}
return ret;
}
static loff_t mem_llseek(struct file *filp,loff_t offset,int whence)
{
loff_t newpos;
switch(whence)
{
case 0: /*SEEK_SET*/
newpos = offset;
break;
case 1: /*SEEK_CUR*/
newpos = filp->f_pos + offset;
break;
case 2: /*SEEK_END*/
newpos = MEMDEV_SIZE - 1 + offset;
break;
default: /*cannt happen*/
return -EINVAL;
break;
}
if ((newpos < 0) || (newpos > MEMDEV_SIZE))
return -EINVAL;
filp->f_pos = newpos;
return newpos;
}
static const struct file_operations mem_fops =
{
.owner = THIS_MODULE,
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
};
static int memdev_init(void)
{
int result;
int i;
dev_t devno = MKDEV(mem_major,0);
/*静态申请设备号*/
if(mem_major)
{
result = register_chrdev_region(devno,5,"memdev");
}
else /*动态申请设备号*/
{
result = alloc_chrdev_region(&devno,0,5,"memdev");
mem_major = MAJOR(devno);
}
if(result < 0)
return result;
/*初始化cdev结构*/
cdev_init(&cdev, &mem_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
/*注册字符设备*/
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
/*为设备描述结构分配内存*/
mem_devp = kmalloc(MEMDEV_NR_DEVS*sizeof(struct mem_dev),GFP_KERNEL);
if (!mem_devp) /*申请失败*/
{
result = -ENOMEM;
printk("memdev register failure!\n");
goto fail_malloc;
}
memset(mem_devp,0,sizeof(struct mem_dev));
/*为设备分配内存*/
for(i = 0; i < MEMDEV_NR_DEVS; i++)
{
mem_devp[i].size = MEMDEV_SIZE;
mem_devp[i].data = kmalloc(MEMDEV_SIZE, GFP_KERNEL);
memset(mem_devp[i].data,0,MEMDEV_SIZE); //对内存进行清空
}
printk("memdev register success!\n");
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
}
static void memdev_exit(void)
{
cdev_del(&cdev); /*注销设备*/
kfree(mem_devp); /*释放设备结构体内存*/
unregister_chrdev_region(MKDEV(mem_major,0),2); /*释放设备号*/
}
MODULE_AUTHOR("wang xiaodong");
MODULE_LICENSE("Dual BSD/GPL");
module_init(memdev_init);
module_exit(memdev_exit);
2、memdev.h
#ifndef _MEMDEV_H_
#define _MEMDEV_H_
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 0
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 5
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
struct mem_dev
{
char *data;
unsigned long size;
};
#endif
3、Makefile
ifneq ($(KERNELRELEASE),)
obj-m:= memdev.o
memdev.o : memdev.c memdev.h
cc -c memdev.c
else
//KERNELDIR:=/lib/modules/2.6.32-33-generic/build
KERNELDIR:=/usr/src/linux-headers-2.6.32-33-generic
PWD:=$(shell pwd)
all:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(PWD) clean
endif
4、memdev_test.c
#include "stdio.h"
int main()
{
FILE *fp0 = NULL;
char Buf[4096];
strcpy(Buf,"Mem is char dev!");
printf("BUF:%s \n",Buf);
fp0 = fopen("/dev/memdev3","r+");
if(fp0 == NULL)
{
printf("Open Error!\n");
return -1;
}
fwrite(Buf,sizeof(Buf),1,fp0);
/*重新定位文件位置*/
fseek(fp0,0,SEEK_SET);
strcpy(Buf,"Buf is NULL!");
printf("BUF:%s\n",Buf);
fread(Buf,sizeof(Buf),1,fp0);
printf("BUF:%s\n",Buf);
return 0;
}
操作:
make
sudo insmod memdev.ko
查看dmesg|tail 输出:memdev register success!
查看主设备号:cat /proc/devices
建立设备节点 :sudo mknod /dev/memdev3 c 251 3
修改设备访问权限:sudo chmod 777 /dev/memdev3
gcc memdev_test.c -o test
./test
结果:
BUF:Mem is char dev!
BUF:Buf is NULL!
BUF:Mem is char dev!