这一节,我将对上一节建立的字符设备框架进行实现。这里只对init,exit,open,release,read和write函数做了实现。
在Linux下用vi建立一DemoCharDev.c文件,其内容书写如下:
//DemoCharDev.c start///
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/kernel.h> //for container_of
#include <linux/cdev.h> //与cdev相关
#include “scull.h” //《
Linux
设备驱动程序》自带的例子代码
#define MAJOR_NUM 14 //主设备号
#define MINOR_NUM 3 //次设备号
#define NAME “DemoCharDev” //设备名称
MODULE_LECENSE(“Dual BSD/GPL”);
static int DemoCharDev_open(struct inode *inode, struct file *filp);
static int DemoCharDev_release(struct inode *inode, struct file *filp);
ssize_t DemoCharDev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos);
ssize_t DemoCharDev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos);
static int major = MAJOR_NUM;
statci struct cdev DemoCharDev;
struct file_operations DemoCharDev_fops = {
.owner = THIS_MODULE,
.open = DemoCharDev_open,
.release = DemoCharDev_release,
.read = DemoCharDev_read,
.write = DemoCharDev_write, //注意此处的逗号
};
static int __init DemoCharDev_init(void)
{//__init标记表示该函数只在初始化期间使用
int ret;
dev_t dev_id;
printk(“Hello, enter the DemoCharDev_init function!/n”);
//获取主设备号
if(major)
{
//静态分配
dev_id = MKDEV(major, MINOR_NUM); //将主次设备号转化为dev_t类型
ret = register_chrdev_region(dev_id, 3, NAME);
}
else
{
//动态分配
ret = alloc_chrdev_region(&dev_id, 0, 3, NAME);
major = MAJOR(dev_id);
}
if(ret < 0)
{
printk(“DemoCharDev: can’t get major %d/n”, major);
return ret;
}
printk(“MAJOR: %d/n”,major);
cdev_init(&DemoCharDev, &char_fops); //初始化分配到struct cdev结构
ret = cdev_add(&DemoCharDev, dev_id, 1); //告诉内核有关struct cdev结构的信息
if(ret < 0)
{
printk(“DemoCharDev: can’t add the device! Error no is %d/n”, ret);
return ret;
}
printk(“Goodbye, the DemoCharDev_init is over!/n”);
return 0;
}
static void __exit DemoCharDev_exit(void)
{//__exit表示该函数只能在模块被卸载或者系统关闭时被调用
dev_t dev_id;
printk(“Goodbye, cruel world!/n”);
cdev_del(&DemoCharDev); //从系统中移除字符设备
dev_id = MKDEV(major, MINOR_NUM);
unregister_chrdev_region(dev_id, 3); //释放设备编号
printk(“Goodbye, the DemoCharDev_exit function is over!/n”);
}
static int DemoCharDev_open(struct inode *inode, struct file *filp)
{
struct scull_dev *dev;
printk(“Hello, enter the DemoCharDev_open function!/n”);
dev = container_of(inode->i_cdev, struct scull_dev, cdev);
filp->private_data = dev;
if((filp->f_flags & O_ACCMODE) == O_WRONLY)
{
scull_trim(dev);
}
printk(“Goodbye, the DemoCharDev_open function is over!/n”);
return 0;
}
static int DemoCharDev_release(struct inode *inode, struct file *filp)
{
//release函数暂不做任何处理
printk(“Hello, enter the DemoCharDev_release function!/n”);
printk(“Goodbye, the DemoCharDev_release function is over!/n”);
return 0;
}
ssize_t DemoCharDev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr = NULL; //第一个链表项
int quantum = dev->quantum;
int qset = dev->qset;
int itemsize = quantum * qset; //该链表项有多少字节
int item, s_pos, q_pos, rest;
ssize_t retval = 0;
printk(“Hello, enter the DemoCharDev_read function!/n”);
if(down_interruptible(&dev->sem))
{
return –ERESTARTSYS;
}
if(*f_pos >= dev->size)
goto out;
if(*f_pos + count > dev->size)
count = dev->size - *f_pos;
//在量子集中寻找链表项,qset索引及偏移量
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
//沿该链表前行,直到正确的位置
dptr = scull_follow(dev, item);
if(NULL == dptr || !dptr->data || !dptr->data[s_pos])
goto out;
//读取该量子的数据直到结尾
if(count > quantum – q_pos)
count = quantum – q_pos;
if(copy_to_user(buf, dptr->data[s_pos] + q_pos, count))
{
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
printk(“Goodbye, the DemoCharDev_read function is over!/n”);
return retval;
}
ssize_t DemoCharDev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr = NULL;
int quantum = dev->quantum;
int qset = dev->qset;
int itemsize = quantum * qset;
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM;
printf(KERN_ALERT “Hello, enter the DemoCharDev_write function!/n”);
if(down_interruptible(&dev->sem))
{
return –ERESTARTSYS;
}
//在量子集中寻找链表项,qset索引及偏移量
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
//沿该链表前行,直到正确的位置
dptr = scull_follow(dev, item);
if(NULL == dptr)
{
goto out;
}
if(!dptr->data)
{
dptr->data = kmalloc(qset*sizeof(char *), GFP_KERNEL);
if(!dptr->data)
goto out;
memset(dptr->data, 0, qset*sizeof(char*));
}
If(!dptr->data[s_pos])
{
dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
If(!dptr->data[s_pos])
goto out;
}
//读取该量子的数据直到结尾
if(count > quantum – q_pos)
count = quantum – q_pos;
if(copy_from_user(dptr->data[s_pos]+q_pos,buf,count))
{
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
if(dev->size < *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
printk(“Goodbye, the DemoCharDev_write function is over!/n”);
return retval;
}
module_init(DemoCharDev_init);
module_exit(DemoCharDev_exit);
//DemoCharDev.c end///
其Makefile文件的书写如“
Linux设备驱动程序学习笔记1——模块加载和卸载”中所描述。