Linux設備驅動程序學習筆記4——簡單的字符設備實現

 
這一節,我將對上一節建立的字符設備框架進行實現。這裏只對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——模塊加載和卸載”中所描述。
 
文中只是一個字符設備驅動的框架的簡單實現。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章