一、开发环境
内核版本: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 <1037398771@qq.com>
* All rights reserved.
*
* Filename: globalmem.c
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <1037398771@qq.com>
* 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 <1037398771@qq.com>");
MODULE_LICENSE("Dual BSD/GPL"); KERNEL_VER = linux-3.0
* Copyright: (C) 2014 liuchengdeng <1037398771@qq.com>
* All rights reserved.
*
* Filename: globalmem.c
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <1037398771@qq.com>
* 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 <1037398771@qq.com>");
MODULE_LICENSE("Dual BSD/GPL"); KERNEL_VER = linux-3.0
LINUX_SRC ?=../linuxrom/$(KERNEL_VER)
/*********************************************************************************
* Copyright: (C) 2014 liuchengdeng <1037398771@qq.com>
* All rights reserved.
*
* Filename: Make
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <1037398771@qq.com>
* 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 <1037398771@qq.com>
* All rights reserved.
*
* Filename: Make
* Description: This file
*
* Version: 0.0.0(08/28/2014~)
* Author: liuchengdeng <1037398771@qq.com>
* 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