字符设备驱动初阶学习
//以下为linux下的头文件
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uacess.h>
#define CHARDRIVER_SIZE 0X1000 //全局最大内存4KB
#define MEM_CLR 0X01 //清零全局内存
#define CHARDRIVER_MAJOR 240 //预设字符设备的主设备号
static int chardriver_major = CHARDRIVER_MAJOR ;
//chardriver 设备结构体
struct chardriver_dev {
struct cdev cdev ; //cdev结构体
unsigned char mem[CHARDRIVER_SIZE]; //全局内存
}
struct chardriver_dev * chardriver_devp; //设备结构指针
//文件打开函数
int chardriver_open(struct inode *inode , struct file *filp)
{
//将设备结构指针赋值给文件私有数据指针
filp->private_data = chardriver_devp;
return 0;
}
//文件释放函数
int chardriver_release(struct inode *inode , struct file *filp)
{
return 0;
}
//ioctl设备控制函数
static int chardriver_ioctl(struct inode *inode, struct file *filp , unsigned int cmd , unsigned long arg)
{
//获得设备结构指针
struct chardriver_dev * dev = filp->private_data;
switch (cmd) {
case MEM_CLR:
memset(dev->mem, 0 , CHARDRIVER_SIZE);
printk(KERN_INFO "chardriver is set to zero \n");
break;
default:
return -EINVAL;
}
return 0;
}
//读函数
static ssize_t chardriver_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 chardiver_dev *dev = filp->private_data;
//分析和获取有效的写长度
if(p > = CHARDRIVER_SIZE)
return 0;
if(count > CHARDRIVER_SIZE - p)
count = CHARDRIVER- p;
//内核空间的数据复制到用户空间
if(copy_to_user(buf, (void *) (dev -> mem+ p) , count ))
ret = - EFAULT;
else {
*ppos += count;
ret = count;
printk(KERN_INFO "read %u bytes from %lu\n",count , p);
}
return ret;
}
//写函数
stacit ssize_t chardriver_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 chardriver_dev *dev = filp->private_data;
//分析有效写长度
if(p>= CHARDRIVER_SIZE)
return 0;
if(count > CHARDRIVER_SIZE - p)
count = CHARDIVER_SIZE -p;
//将数据从用户空间发送到内核空间,确切的说应该是发送给设备
if(copy_from_user(dev->mem+p ,buf , conut))
ret = - EFAULT;
else {
*ppos += count;
ret = count;
printk(KERN_INFO "written %u bytes from %lu\n",count ,p);
}
return ret;
}
//seek文件定位函数
static loff_t chardriver_llseek(struct file *filp, loff_t offset , int orig)
{
loff_t ret =0;
switch (orig) {
case 0: //相对于文件头开始位置的位移
if(offset < 0 ) {
ret = -EINVAL;
break;
}
if((unsigned int )offset >CHARDRIVER_SIZE ) {
ret = - EINVAL;
break;
}
filp ->f_pos = (unsigned int )offset;
ret = filp->f_ops;
break;
case 1: //相对于当前位置的偏移
if((filp->f_pos+offset) > CHARDRIVER_SIZE) {
ret = - EINVAL;
break;
}
if((filp->f_pos+offset) < 0) {
ret = -EINVAL;
break;
}
filp->f_pos += offset;
ret = filp->f_pos;
break;
default:
ret = - EINVAL;
break;
}
return ret ;
}
//文件操作结构体
static const struct file_operations chardriver_fops = {
.owner = THIS_MODULE;
.llseek = chardriver_llseek;
.read = chardriver_read;
.write =chardriver_write;
.ioctl = chardriver_ioctl;
.open = chardriver_open;
.release = chardriver_release;
};
//创建cdev
static void chardriver_setup_cdev(struct chardriver_dev *devp , int index)
{
int err , devno = MKDEV (chardriver_major , index);
cdev_init (&dev->cdev , &chardriver_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&cdev->cdev, devno , 1);
if(err)
printk(KERN_NOTICE "errof %d adding chardriver %d\n",err ,index);
}
//设备驱动模块加载函数
int chardriver_init(void)
{
int result ;
dev_t devno = MKDEV(chardriver_major,0);
//申请设备
if(chardriver_major)
result = register_chrdev_region(devno , 1 ,"chardriver");
else { //动态申请
result = alloc_chrdev_region(&devno , 0 , 1 ,"chardriver");
chardriver_major= MAJOR(devno);
}
if (result < 0)
return result ;
//动态申请设备结构体的内存
chardriver_devp = kmalloc(sizeof (struct chardriver_dev),GFP_KERNEL);
if(!chardriver_devp) { //申请失败
result = - ENOMEM;
goto fail_malloc;
}
memset(chardriver_devp , 0 , sizeof (struct chardriver_dev));
chardiver_setup_cdev(chardriver_devp,0);
return 0;
fail_malloc:
unreister_chrdev_region(devno, 1);
return result ;
}
//函数卸载模块
void chardriver_exit(void )
{
cdev_del(&chardriver_devp->cdev); //删除cdev
kfree (chardriver_devp); //释放设备结构体内存
unregister_chrdev_region(MKDEV(chardriver_major,0),1) ; //注销设备号
}
//模块的一些版本、许可证、以及加载卸载函数注册与注销
MODULE_LICENSE("Dual BSD/GPL");
module_param(chardriver_major , int , S_IRUGO);
module_init(chardriver_init);
module_exit(chardriver_exit);
}
由此总结:字符设备驱动程序应该分为四部分
首先是设备结构体说明
struct chardriver_dev {
struct cdev cdev ; //cdev结构体
unsigned char mem[CHARDRIVER_SIZE]; //全局内存
}
其次是设备的操作函数
static const struct file_operations chardriver_fops = {
.owner = THIS_MODULE;
.llseek = chardriver_llseek;
.read = chardriver_read;
.write =chardriver_write;
.ioctl = chardriver_ioctl;
.open = chardriver_open;
.release = chardriver_release;
};
然后是字符设备的创建
static void chardriver_setup_cdev(struct chardriver_dev *devp , int index)
{
int err , devno = MKDEV (chardriver_major , index);
cdev_init (&dev->cdev , &chardriver_fops);
dev->cdev.owner = THIS_MODULE;
err = cdev_add(&cdev->cdev, devno , 1);
if(err)
printk(KERN_NOTICE "errof %d adding chardriver %d\n",err ,index);
}
最后是驱动模块的加载与卸载
module_init(chardriver_init);
module_exit(chardriver_exit);