字符設備驅動有關(三)

下面是用了自定義char_dev結構體,利用container_of來指向。內存空間用kmalloc來申請。練手用的。

#include<linux/module.h>
#include<linux/init.h>
#include<linux/errno.h>
#include<linux/fs.h>
#include<asm/uaccess.h>
#include<linux/kernel.h>//container_of
#include<linux/device.h>
#include<linux/cdev.h>
#include<linux/types.h>
#include<linux/slab.h>//kmalloc
//#include<asm/device.h>
MODULE_LICENSE("Dual BSD/GPL");

#define SIZE 1024

dev_t devno;//設備號
struct cdev cdev;
struct class *char_class;
struct char_dev *dev;


/*char_dev結構體中定義了cdev,方便多設備時cdev指定;內核空間data;空間大小size*/

struct char_dev//dev結構,未包含量子qset什麼的。
{
    struct cdev cdev;
    char *data;
//定義data內存空間然後kmalloc分配
    unsigned long size;
    /*struct char_qset *data;
    unsigned long size;*/
    //unsigned char data[size];//這個還是靜態分配,和上次一樣
};

/*struct char_qset//量子集qset結構
{
    void **data;
    struct char_qset *next;
}*/


//open
int char_open(struct inode *inode,struct file *filp)
{
    dev=container_of(inode->i_cdev,struct char_dev,cdev);
    filp->private_data=dev;
   //dev->size=SIZE;
    return 0;
}
//release
int char_release(struct inode *inode,struct file *filp)
{
    return 0;
}
//read
ssize_t char_read(struct file *filp,char *buf,size_t count,loff_t *f_pos)
{
    dev=filp->private_data;
    if(*f_pos>=dev->size){
        *f_pos=dev->size;
        return 0;
    }
    if(*f_pos+count>dev->size)
        count=dev->size-*f_pos;
    if(copy_to_user(buf,dev->data+*f_pos,count)){
        printk(KERN_WARNING"copy_to_user error!\n");
        return -EFAULT;
    }else{
        *f_pos+=count;
    }
    return count;
}
//write
ssize_t char_write(struct file *filp,const char*buf,size_t count,loff_t *f_pos)
{
    dev=filp->private_data;
    if(*f_pos>=dev->size){
        *f_pos=dev->size;
        return 0;
    }
    if(*f_pos+count>dev->size)
        count=dev->size-*f_pos;
    if(copy_from_user(dev->data+*f_pos,buf,count)){
        printk(KERN_WARNING"copy_from_user error!\n");
        return -EFAULT;
    }else{
        *f_pos+=count;
    }
    return count;
}
//llseek,比上一篇多實現了lseek,來控制偏移量
loff_t char_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=dev->size-1+offset;
        break;
    default://can't happen
        return -EINVAL;
    }
    filp->f_pos=newpos;
    return newpos;
}

//file_operations,如果不聲明上面的open等,必須這個放它們後面
struct file_operations char_fops=
{
    .owner=THIS_MODULE,
    .open=char_open,
    .release=char_release,
    .read=char_read,
    .write=char_write,
    .llseek=char_llseek,//多了這個
};


//加載
static int char_init(void)
{
    int result;

    /*alloc_chrdev_region(dev_t *dev,第一個次設備號,設備個數,名字)*/
    result=alloc_chrdev_region(&devno,0,1,"char_module");
    if(result<0){
        printk(KERN_WARNING"alloc_chrdev_region error!\n");
        return -ENODEV;
    }
    printk(KERN_INFO"major:%d,minor:%d",MAJOR(devno),MINOR(devno));

    /*下面來分配內核空間,這個必須在cdev_init之前申請空間,會用到,不然雖然編譯沒錯誤,測試時會出錯
     可以用kzalloc來代替下面的kmalloc和memset,申請後初始化
     dev=kzalloc(sizeof(struct char_dev),GFP_KERNEL)
     */
    /*因爲dev是結構體指針,所以要申請空間,如果是struct char_dev dev,則不必*/
    dev=kmalloc(sizeof(struct char_dev),GFP_KERNEL);//申請dev結構體空間
    if(dev==NULL){
        printk(KERN_WARNING"kmalloc error!\n");
        return -ENOMEM;
    }
    memset(dev,0,sizeof(struct char_dev));//初始化空間
   /*dev->data申請空間*/
    dev->size=SIZE;
    dev->data=kmalloc(SIZE,GFP_KERNEL);
    if(dev->data==NULL){
        printk(KERN_WARNING"kzalloc error!\n");
        return -ENOMEM;
    }
    memset(dev->data,0,SIZE);//初始化

    /*cdev_init(struct cdev *cdev,struct file_oppations *fops)*/
    cdev_init(&dev->cdev,&char_fops);
    dev->cdev.owner=THIS_MODULE;

    /*cdev_add(struct cdev *dev,dev_t num,unsigned int count一般是1)*/
    cdev_add(&dev->cdev,devno,1);
    if(result){
        /*unregister_chrdev_region(dev_t first,unsigned int count)*/
        unregister_chrdev_region(devno,1);
        return -EINVAL;
    }
    
    /*class_creat(owner,name)*/
    char_class=class_create(THIS_MODULE,"char_module");
    if(char_class==0){
        printk(KERN_WARNING"class_create error\n");
        unregister_chrdev_region(devno,1);
        /*cdev_dev(struct cdev *dev)*/
        cdev_del(&dev->cdev);
        return -EINVAL;
    }
    /*device_create(上面的class,NULL,dev_t,NULL,device name)*/
    device_create(char_class,NULL,devno,NULL,"char_module");

    return 0;
}

//卸載
static void char_exit(void)
{
    /*unregister_chrdev_region(dev_t first,unsigned int count)*/
    unregister_chrdev_region(devno,1);
   /*device_destroy(myclass,dev_t)*/
    device_destroy(char_class,devno);
    /*class_destroy(myclass)*/
    class_destroy(char_class);
    /*cdev_dev(struct cdev *dev)*/
    cdev_del(&dev->cdev);
    /*釋放空間*/
    kfree(dev->data);
    kfree(dev);
    dev->data=NULL;
    dev=NULL;
}

//聲明module
module_init(char_init);
module_exit(char_exit);

------------------------------------------------------------------------------------------

紅色的就是和上篇不太一樣的東西。這個順序也有調整。修改了printk的輸出級別等。

下面是測試程序

#include<sys/types.h>
#include<sys/stat.h>
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<string.h>

int main()
{
    int fd,size;
    char buff_write[]="test data";
    char buff_read[128];
    if((fd=open("/dev/char_module",2))==-1){//open
        printf("open error!\n");
        exit(1);
    }
    
    if((size=read(fd,buff_read,strlen(buff_read)))<0){//read第一次
        printf("read error!\n");
        exit(1);
    }
    printf("read:%dbytes\n",size);
    printf("R:%s\n",buff_read);

    if((size=write(fd,buff_write,strlen(buff_write)))!=strlen(buff_write)){//write
        printf("write error!\n");
        exit(1);
    }
    printf("W:%s\n",buff_write);

    if(lseek(fd,0,SEEK_SET)==-1){//重置偏移量
        printf("lseek error\n");
        exit(1);
    }


    if((size=read(fd,buff_read,strlen(buff_write)))<0){//read第二次
        printf("read error!\n");
        exit(1);
    }
    printf("read:%dbytes\n",size);
    printf("R:%s\n",buff_read);
    
    close(fd);
    exit(0);
}
---------------------------------------------------------------------------------------

這裏的測試程序比上一篇的測試程序要好點。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章