Linux系统将设备分为三种类型,每个模块通常实现为其中的某一类:字符模块、块模块、网络模块,这三种类型如下:
字符设备和块设备区别
两个方法:
zhangbin@zhangbin-virtual-machine:~/mini6410/linux-2.6.38/$: cd drivers/char/
zhangbin@zhangbin-virtual-machine:~/mini6410/linux-2.6.38/drivers/char$ :vi Kconfig
zhangbin@zhangbin-virtual-machine:~/mini6410/linux-2.6.38/$ make menuconfig ARCH=arm
配置如下(光标处)
(3)在 drivers/char/ 中 的Makefile 进行如下配置(红线处)
(4)最后进行内核的编译和运行
zhangbin@zhangbin-virtual-machine:~/mini6410/linux-2.6.38/$ make uImage ARCH=arm CROSS_COMPILE=arm-linux-
驱动程序的使用 :linux 用户程序通过设备文件来使用驱动程序操作字符设备和块设备。
在linux内核中描述设备号:dev_t 结构 为unsigned int 32位整数,高12位为主设备号,低20位为次设备号
linux内核给设备分配主设备号,可以采用 静态申请和动态分配
**注销设备号:**void unregister_chrdev_region(dev_t from,unsigned count)
功能:释放从from 开始的count个设备号
struct class *myclass = class_create(THIS_MODULE,"my_device_driver");
device_create(myclass,NULL,MKDEV(major_num,0),NULL,"my_device");
当驱动被加载时,udev(mdev)就会自动在/dev/下创建 my_device 设备文件。
前提:要配置busybox支持 种(udev)。
struct file_operations mem_fops=
{
.owner = THIS_MODULE,
.llseek = mem_seek,
.read = mem_read,
.write = mem_write,
.unlocked_ioctl = mem_ioctl, //2.6.10内核后把.ioctl改为.unlocked_ioctl了
.open = mem_open,
.release = mem_release,
};
内核提供了专门的函数用于访问用户空间指针如:
字符设备使用 struct cdev 来描述
字符设备注册分为三个步骤:
注销函数:int cdev_del(struct cdev *p)
参数:
p:要注销的字符设备结构
工程名为 memdev ,分为三部分构成:
#ifndef _MEMDEV_H_
#define _MEMDEV_H_
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 99 //预设mem的主设备号
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2 //设备号
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 4096
#endif
//mem设备描述结构体
struct mem_dev
{
char *data;
unsigned long size;
};
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "memdev.h"
static 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; //获取设备结构体指针
//判断读位置是否有效
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 from %d\n",count,p);
}
return ret;
}
//写函数
static ssize_t mem_write(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; //获取设备结构体指针
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 "write %d bytes from %d\n",count,p);
return ret;
}
//seek 文件定位函数
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: //can't happen
return -EINVAL;
}
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,2,"memdev");
else
{
result = alloc_chrdev_region(&devno,0,2,"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;
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);
}
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result;
}
//模块卸载函数
static void memdev_exit(void)
{
cdev_del(&cdev); //注销设备
kfree(mem_devp); //释放设备结构体内存
unregister_chrdev_region(MKDEV(mem_major,0),2); //释放设备号
}
MODULE_AUTHOR("zhangbin");
MODULE_LICENSE("GPL");
module_init(memdev_init);
module_exit(memdev_exit);
ifneq ($(KERNELRELEASE),)
obj-m := memdev.o
else
KDIR := /home/zhangbin/mini6410/linux-2.6.38
all:
make -C $(KDIR) M=$(PWD)
clean:
rm -f *.ko *.o *.mod.c *.symvers *.order
endif
#include
#include
int main()
{
FILE *fp0 = NULL;
char Buf[4096];
//初始化Buf
strcpy(Buf,"Mem is char dev!");
printf("BUF:%s\n",Buf);
//打开设备文件
fp0 = fopen("/dev/memdev0","r+");
if(fp0 == NULL)
{
printf("Open Memdev0 Error!\n");
return -1;
}
//写入设备
fwrite(Buf,sizeof(Buf),1,fp0);
//重新定位文件位置
fseek(fp0,0,SEEK_SET);
//清除Buf
strcpy(Buf,"Buf is NULL!");
printf("BUF :%s\n",Buf);
fread(Buf,sizeof(Buf),1,fp0);
//检测结果
printf("BUF: %s\n",Buf);
return 0;
}
执行 make 命令后,把生成的 memdev.ko 文件下载到开发板系统中
之后手动创建 memdev 设备(创建方法前边有讲)
简单的一个字符驱动程序就算实现了,功能是实现设备的读写,更多控制功能在以后的博客中会更新出来,最近有点忙,更新可能较慢!!!