支持2個globalmem程序

接上一篇

還有一個變化:globalmem_ioctl的聲明由原來的4個參數變成3個參數,如下(取消第一個參數struct inode *inode):

static int globalmem_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)


以下獻上我的代碼,內核3.16親測沒問題

源程序:

/*************************************************************************
    > File Name: globalmem.c
    > Author: zhyang
    > Mail: [email protected]
    > Created Time: 2015年04月16日 星期四 09時32分51秒
 ************************************************************************/

//#include "globalmem.h"
#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<linux/uaccess.h>
#include <linux/slab.h>

#define GLOBALMEM_SIZE 0X1000 //memory size 4K
//#define MEN_CLEAR 0X1 //clear memory
#define GLOBALMEM_MAJOR 110 //預設的主設備號
#define GLOBALMEM_MAGIC 0Xdf
#define MEM_CLEAR _IO(GLOBALMEM_MAGIC,0x11)

static int globalmem_major = GLOBALMEM_MAJOR;

struct globalmem_dev
{
    struct cdev cdev;
    unsigned char mem[GLOBALMEM_SIZE];  //全局內存
};

struct globalmem_dev *devp;

//讀函數
static ssize_t globalmem_read(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
    unsigned long int p = *ppos;
    int ret = 0;
    struct globalmem_dev *dev = filp->private_data;

    //分析和獲取有效的讀取長度
    if(p >= GLOBALMEM_SIZE)   //要讀的偏移位置越界
        return count ? - ENXIO:0;
    if(count > GLOBALMEM_SIZE - p) //要讀的字節數太大
        count = GLOBALMEM_SIZE - p ;
    //賦值數據從內核空間到用戶空間
    if(copy_to_user(buf,dev->mem+p,count)) //拷貝內核空間的dev.mem+p到用戶空間的buf
        ret = - EFAULT; //拷貝成功返回0,失敗返回沒有拷貝成功的字節數
    else {
        *ppos +=count; //更新文件位置
        ret = count;
        printk(KERN_INFO "READ %d bytes from %ld\n",count,p);
    }
    return ret; //返回成功讀取的字節數
}

//寫函數
static ssize_t globalmem_write(struct file *filp,char __user *buf,size_t count,loff_t *ppos)
{
    unsigned long int p = *ppos;
    int ret = 0;
    struct globalmem_dev *dev = filp->private_data;

    if(p >= GLOBALMEM_SIZE)
        return count? - ENXIO:0;
    if(count > GLOBALMEM_SIZE -p)
        count = GLOBALMEM_SIZE -p;
    if(copy_from_user(dev->mem+p,buf,count))
        ret = - EFAULT;
    else{
        *ppos += count;
        ret = count;
        printk(KERN_INFO "WRITE %d bytes to %ld\n",count,p);
    }
    return ret;
}

//定位函數
static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig)
{
    loff_t ret;
    switch (orig)
    {
        case 0: //從文件開頭開始偏移
            if(offset < 0){
                ret = - EINVAL;
                break;
            }
            if((unsigned int)offset > GLOBALMEM_SIZE) //偏移越界
            {
                ret = - EINVAL;
                break;
            }
            filp->f_pos = (unsigned int)offset;
            ret = filp->f_pos;
            break;
        case 1: //從當前位置開始偏移
            if((filp->f_pos + offset) > GLOBALMEM_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;
    }

    return ret;
}

static int globalmem_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)
{
    struct globalmem_dev *dev = filp->private_data;

    switch (cmd)
    {
        case MEM_CLEAR:
            memset(dev->mem,0,GLOBALMEM_SIZE);
            printk(KERN_INFO "globalmem is set to zero\n");
            break;
        default: return - EINVAL;
    }
    return 0;
}

//文件打開函數
int globalmem_open(struct inode *inode,struct file *filp)
{
    struct globalmem_dev *dev;
    dev = container_of(inode->i_cdev,struct globalmem_dev,cdev);
    
    filp->private_data = dev;
    return 0;
}

int globalmem_release(struct inode *inode,struct file *filp)
{
    return 0;
}

static const struct file_operations globalmem_fops =
{
    .owner = THIS_MODULE,
    .llseek = globalmem_llseek,
    .read = globalmem_read,
    .write = globalmem_write,
    .unlocked_ioctl = globalmem_ioctl,
    .open = globalmem_open,
    .release = globalmem_release,
};

//初始化並添加cdev結構體
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{
    int err,devno = MKDEV(globalmem_major,0);
    cdev_init(&dev->cdev,&globalmem_fops);
    dev->cdev.owner = THIS_MODULE;
    dev->cdev.ops = &globalmem_fops;
    err = cdev_add(&dev->cdev,devno,2);   //2表示允許2個次設備,一般這一位爲1
    if(err)
        printk(KERN_NOTICE "ERROR %d adding globalmem\n",err);
}

//模塊加載函數
static __init int globalmem_init(void)
{
    int result;
    dev_t devno = MKDEV(globalmem_major,0);  //把major,minor轉換爲dev_t
    //申請設備
    if(globalmem_major)//靜態申請
        result = register_chrdev_region(devno,2,"globalmem"); //申請字符設備驅動區域,1表示申請的連續設備編號的個數,字符串是設備名稱
    else{//靜態申請失敗
        result = alloc_chrdev_region(&devno,0,2,"globalmem"); //0表示請求的第一個次設備號
        globalmem_major = MAJOR(devno); //賦給globalmem_major
    }

    if(result < 0)
        return result;
    devp = kmalloc(2*sizeof(struct globalmem_dev),GFP_KERNEL); //動態申請設備結構體內存
    if(!devp)
    {
        result = - ENOMEM;
        goto fail_malloc;
    }
    memset(devp,0,2*sizeof(struct globalmem_dev));
    globalmem_setup_cdev(&devp[0],0); //cdev的初始化和添加
    globalmem_setup_cdev(&devp[1],1);

    return 0;
fail_malloc:unregister_chrdev_region(devno,2);
    return result;
}

//驅動模塊卸載函數
static __exit void globalmem_exit(void)
{
    cdev_del(&devp[0].cdev); //刪除cdev結構 dev爲struct globalmem_cdev
    cdev_del(&devp[1].cdev);
    kfree(devp); //釋放設備結構體內存
    unregister_chrdev_region(MKDEV(globalmem_major,0),2); //註銷設備區域
}



MODULE_AUTHOR("ZHYANG");
MODULE_LICENSE("Dual BSD/GPL");

module_param(globalmem_major,int,S_IRUGO);
module_init(globalmem_init);
module_exit(globalmem_exit);


測試程序:

/*************************************************************************
    > File Name: testglobal.c
    > Author: zhyang
    > Mail: [email protected]
    > Created Time: 2015年04月17日 星期五 20時59分49秒
 ************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<linux/ioctl.h>

#define GLOBALMEM_MAGIC 0Xdf
#define MEM_CLEAR _IO(GLOBALMEM_MAGIC,0x11)

int main()
{
    int fd0 = 0,fd1 = 0;
    int ret = 0,length = 0;
    char buffer[1024];
    //globalmem0
    fd0 = open("/dev/globalmem0",O_RDWR);
    if(fd0 < 0){
        printf("can not open /dev/globalmem0\n");
        close(fd0);
        return 0;
    }
    //write string
    memset(buffer,0,1024);
    strcpy(buffer,"test globalmem0\n");
    length = strlen(buffer);
    printf("\n0 寫入的字符length=%d,%s",length,buffer);
    ret = lseek(fd0,0,SEEK_SET);
    ret = write(fd0,buffer,length);
    //read string
    memset(buffer,0,1024);
    ret = lseek(fd0,0,SEEK_SET);
    ret = read(fd0,buffer,length);
    if(ret>0)
        printf("0 清楚內存前讀出的字符length=%d,%s",ret,buffer);
    //clear memory
    memset(buffer,0,1024);
    ret = lseek(fd0,0,SEEK_SET);
    ret = ioctl(fd0,MEM_CLEAR,0);
    ret = read(fd0,buffer,length);
    if(ret > 0)
        printf("0 清楚內存後讀出的字符length=%d,%s",ret,buffer);
    close(fd0);



    //globalmem1
    fd1 = open("/dev/globalmem1",O_RDWR);
    if(fd1 < 0){
        printf("can not open /dev/globalmem1");
        close(fd1);
        return 0;
    }
    //write string
    memset(buffer,0,1024);
    strcpy(buffer,"test globalmem1\n");
    length = strlen(buffer);
    printf("\n\n1 寫入的字符length=%d,%s",length,buffer);
    ret = lseek(fd1,0,SEEK_SET);
    ret = write(fd1,buffer,length);
    //read string
    memset(buffer,0,1024);
    ret = lseek(fd1,0,SEEK_SET);
    ret = read(fd1,buffer,length);
    if(ret>0)
        printf("1 清楚內存前讀出的字符length=%d,%s",ret,buffer);
    //clear memory
    memset(buffer,0,1024);
    ret = lseek(fd1,0,SEEK_SET);
    ret = ioctl(fd1,MEM_CLEAR,0);
    ret = read(fd1,buffer,length);
    if(ret > 0)
        printf("1 清楚內存後讀出的字符length=%d,%s\n",ret,buffer);
    close(fd1);

    return 0;
}


在用戶空間的驗證:

make

sudo insmod globalmem.ko

mknod /dev/globalmem0 c 110 0

mknod /dev/globalmem1 c 110 1

gcc -o test testglobal.c

sudo ./test


0 寫入的字符length=16,test globalmem0
0 清楚內存前讀出的字符length=16,test globalmem0
0 清楚內存後讀出的字符length=16,

1 寫入的字符length=16,test globalmem1
1 清楚內存前讀出的字符length=16,test globalmem1
1 清楚內存後讀出的字符length=16,


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