一、開發環境
內核版本:linux-3.0
開發板:FL2440(nandflash:K9F1G08 128M)
編譯器:arm-linux-gcc 4.3.2
開發板:FL2440(nandflash:K9F1G08 128M)
編譯器:arm-linux-gcc 4.3.2
二、預先分析:
globalmem 意味着“全局內存”,在globalmem 字符設備驅動中會分配一片大小爲GLOBALMEM_SIZE(這裏是4K)的內存空間,並在驅動中提供該片內存的讀寫和定位函數,以供用戶空間的進程能通過linux系統調用訪問這片內存。本例這裏是爲了學習文件私有數據指針private_data,幷包括兩個設備,使得文件私有數據指針private_data的優勢集中顯現出來。
四、編寫驅動並編譯:
編寫驅動程序globalmem.c文件,並編寫Makefile文件,編譯生成globalmem.ko文件,並下載到開發板/目錄下面。
(所有代碼實現都在後面貼出)
四、globalmem驅動在用戶空間的驗證:
[root@root
/]# ls
apps dev_adc init mnt sys var
bin etc jbs.mp3 proc tmp yw.mp3
data globalmem.ko lib root tslib
dev info linuxrc sbin usr
apps dev_adc init mnt sys var
bin etc jbs.mp3 proc tmp yw.mp3
data globalmem.ko lib root tslib
dev info linuxrc sbin usr
[root@root
/]# insmod globalmem.ko
[root@root
/]# mknod dev/globalmem0 c 250 0
[root@root /]# echo "hello" >/dev/globalmem0
written 6 bytes from 0
[root@root /]# cat /dev/globalmem0
read 4096 bytes from 0
hello
[root@root /]# mknod dev/globalmem1 c 250 1
[root@root /]# echo "world" >/dev/globalmem1
written 6 bytes from 0
[root@root /]# cat /dev/globalmem1
read 4096 bytes from 0
world
[root@root /]# echo "hello" >/dev/globalmem0
written 6 bytes from 0
[root@root /]# cat /dev/globalmem0
read 4096 bytes from 0
hello
[root@root /]# mknod dev/globalmem1 c 250 1
[root@root /]# echo "world" >/dev/globalmem1
written 6 bytes from 0
[root@root /]# cat /dev/globalmem1
read 4096 bytes from 0
world
至此完畢,一下是相關代碼:
/*********************************************************************************
* Copyright: (C) 2014 liuchengdeng <[email protected]>
* All rights reserved.
*
* Filename: globalmem.c
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <[email protected]>
* ChangeLog: 1, Release initial version on "08/28/2014 09:42:22 AM"
*
********************************************************************************/
#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 <asm/uaccess.h>
#include <linux/slab.h>
#include <asm/ioctl.h>
#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 250
static int globalmem_major=GLOBALMEM_MAJOR;
struct globalmem_dev{
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmem_dev *globalmem_devp;
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 int globalmem_ioctl(struct inode *inodep,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;
}
static ssize_t globalmem_read(struct file *filp, char __user *buf ,size_t size ,loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count=size;
int ret=0;
struct globalmem_dev *dev=filp->private_data;
if(p>=GLOBALMEM_SIZE)
return 0;
if(count>GLOBALMEM_SIZE-p)
count=GLOBALMEM_SIZE-p;
if(copy_to_user(buf,(void *)(dev->mem+p),count)){
ret=-EFAULT;
}
else{
*ppos+=count;
ret=count;
printk(KERN_INFO "read %u bytes from %lu\n",count,p);
}
return ret;
}
static ssize_t globalmem_write(struct file *filp,const char __user *buf ,size_t size,loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count=size;
int ret=0;
struct globalmem_dev *dev=filp->private_data;
if(p>=GLOBALMEM_SIZE)
return 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 "written %u bytes from %lu\n",count,p);
}
return ret;
}
static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig )
{
loff_t ret =0;
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;
break;
}
return ret;
}
static const struct file_operations globalmem_fops={
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
// .ioctl=globalmem_ioctl,
.open = globalmem_open,
.release= globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{
int err,devno=MKDEV(globalmem_major,index);
cdev_init(&dev->cdev,&globalmem_fops);
dev->cdev.owner=THIS_MODULE;
err=cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "error %d adding globalmem %d",err,index);
}
int globalmem_init(void)
{
int result;
dev_t devno =MKDEV(globalmem_major,0);
if(globalmem_major)
result=register_chrdev_region(devno,2,"globalmem");
else
{
result=alloc_chrdev_region(&devno,0,2,"globalmem");
globalmem_major=MAJOR(devno);
}
if(result<0)
return result;
globalmem_devp=kmalloc(2*sizeof(struct globalmem_dev),GFP_KERNEL);
if(!globalmem_devp)
{
result = -ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp,0,2*sizeof(struct globalmem_dev));
globalmem_setup_cdev(&globalmem_devp[0],0);
globalmem_setup_cdev(&globalmem_devp[1],1);
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result ;
}
void globalmem_exit(void)
{
cdev_del(&globalmem_devp[0].cdev);
cdev_del(&globalmem_devp[1].cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major,0),2);
}
module_init(globalmem_init);
module_exit(globalmem_exit);
module_param(globalmem_major,int ,S_IRUGO);
MODULE_AUTHOR("liu chengdeng <[email protected]>");
MODULE_LICENSE("Dual BSD/GPL");KERNEL_VER = linux-3.0
* Copyright: (C) 2014 liuchengdeng <[email protected]>
* All rights reserved.
*
* Filename: globalmem.c
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <[email protected]>
* ChangeLog: 1, Release initial version on "08/28/2014 09:42:22 AM"
*
********************************************************************************/
#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 <asm/uaccess.h>
#include <linux/slab.h>
#include <asm/ioctl.h>
#define GLOBALMEM_SIZE 0x1000
#define MEM_CLEAR 0x1
#define GLOBALMEM_MAJOR 250
static int globalmem_major=GLOBALMEM_MAJOR;
struct globalmem_dev{
struct cdev cdev;
unsigned char mem[GLOBALMEM_SIZE];
};
struct globalmem_dev *globalmem_devp;
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 int globalmem_ioctl(struct inode *inodep,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;
}
static ssize_t globalmem_read(struct file *filp, char __user *buf ,size_t size ,loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count=size;
int ret=0;
struct globalmem_dev *dev=filp->private_data;
if(p>=GLOBALMEM_SIZE)
return 0;
if(count>GLOBALMEM_SIZE-p)
count=GLOBALMEM_SIZE-p;
if(copy_to_user(buf,(void *)(dev->mem+p),count)){
ret=-EFAULT;
}
else{
*ppos+=count;
ret=count;
printk(KERN_INFO "read %u bytes from %lu\n",count,p);
}
return ret;
}
static ssize_t globalmem_write(struct file *filp,const char __user *buf ,size_t size,loff_t *ppos)
{
unsigned long p=*ppos;
unsigned int count=size;
int ret=0;
struct globalmem_dev *dev=filp->private_data;
if(p>=GLOBALMEM_SIZE)
return 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 "written %u bytes from %lu\n",count,p);
}
return ret;
}
static loff_t globalmem_llseek(struct file *filp,loff_t offset,int orig )
{
loff_t ret =0;
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;
break;
}
return ret;
}
static const struct file_operations globalmem_fops={
.owner = THIS_MODULE,
.llseek = globalmem_llseek,
.read = globalmem_read,
.write = globalmem_write,
// .ioctl=globalmem_ioctl,
.open = globalmem_open,
.release= globalmem_release,
};
static void globalmem_setup_cdev(struct globalmem_dev *dev,int index)
{
int err,devno=MKDEV(globalmem_major,index);
cdev_init(&dev->cdev,&globalmem_fops);
dev->cdev.owner=THIS_MODULE;
err=cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "error %d adding globalmem %d",err,index);
}
int globalmem_init(void)
{
int result;
dev_t devno =MKDEV(globalmem_major,0);
if(globalmem_major)
result=register_chrdev_region(devno,2,"globalmem");
else
{
result=alloc_chrdev_region(&devno,0,2,"globalmem");
globalmem_major=MAJOR(devno);
}
if(result<0)
return result;
globalmem_devp=kmalloc(2*sizeof(struct globalmem_dev),GFP_KERNEL);
if(!globalmem_devp)
{
result = -ENOMEM;
goto fail_malloc;
}
memset(globalmem_devp,0,2*sizeof(struct globalmem_dev));
globalmem_setup_cdev(&globalmem_devp[0],0);
globalmem_setup_cdev(&globalmem_devp[1],1);
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return result ;
}
void globalmem_exit(void)
{
cdev_del(&globalmem_devp[0].cdev);
cdev_del(&globalmem_devp[1].cdev);
kfree(globalmem_devp);
unregister_chrdev_region(MKDEV(globalmem_major,0),2);
}
module_init(globalmem_init);
module_exit(globalmem_exit);
module_param(globalmem_major,int ,S_IRUGO);
MODULE_AUTHOR("liu chengdeng <[email protected]>");
MODULE_LICENSE("Dual BSD/GPL");KERNEL_VER = linux-3.0
LINUX_SRC ?=../linuxrom/$(KERNEL_VER)
/*********************************************************************************
* Copyright: (C) 2014 liuchengdeng <[email protected]>
* All rights reserved.
*
* Filename: Make
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <[email protected]>
* ChangeLog: 1, Release initial version on "08/28/2014 12:23:22 AM"
*
********************************************************************************/
PWD := $(shell pwd)
#Kernel module
obj-m +=globalmem.o
/*********************************************************************************
* Copyright: (C) 2014 liuchengdeng <[email protected]>
* All rights reserved.
*
* Filename: Make
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <[email protected]>
* ChangeLog: 1, Release initial version on "08/28/2014 12:23:22 AM"
*
********************************************************************************/
PWD := $(shell pwd)
#Kernel module
obj-m +=globalmem.o
#Specify flags for the module compilation
#EXTRA_CFLAGS=-g -O0
mohule:
make -C $(LINUX_SRC) M=$(PWD) modules
make clear
clear:
@rm -rf *.o *.cmd *.mod.c .*ko.cmd .*.o.cmd .*.o.d
@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
clean: clear
@rm -f *.ko
#EXTRA_CFLAGS=-g -O0
mohule:
make -C $(LINUX_SRC) M=$(PWD) modules
make clear
clear:
@rm -rf *.o *.cmd *.mod.c .*ko.cmd .*.o.cmd .*.o.d
@rm -rf *~ core .depend .tmp_versions Module.symvers modules.order -f
clean: clear
@rm -f *.ko