接上一篇
還有一個變化: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,