编写char_mmap.c文件
#include <linux/version.h>
#include <linux/module.h>#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <asm/io.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <linux/semaphore.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#define EB_ISAP5000_MAJOR_VIO 212
#define EB_ISAP5000_DEVICE_NAME "isap5000_vio"
#define EB_ISAP5000_MAP_SIZE (1000 * 4096) //1000页 *4096
typedef struct eb_isap5000_vio /*字符设备结构体*/
{
unsigned long *dr; //dr[x] 保存了指向第x页的地址,每页的大小是4096
unsigned long msize; //所有申请内存的大小
}eb_isap5000_vio_t;
eb_isap5000_vio_t vio; //申请的内存空间
struct cdev cdev; //字符设备
int devnum = EB_ISAP5000_MAJOR_VIO; //设备节点
void *vio_alloc(unsigned long size)
{
void *mem;
mem = (void *)__get_free_page(GFP_KERNEL);
return mem;
}
void vio_free(void *mem, unsigned long size)
{
free_page((size_t)mem);
}
static int eb_isap5000_vio_map_mem(eb_isap5000_vio_t *pvio, struct vm_area_struct *vma)
{
int i = 0;
int ret = 0;
unsigned long addr; int mcount;
addr = vma->vm_start;
mcount = (vma->vm_end - vma->vm_start) / PAGE_SIZE;
if((mcount <= 0) || (mcount > (EB_ISAP5000_MAP_SIZE / PAGE_SIZE)))
{
return -ENOMEM;
}
for(i = 0; i < mcount; i++)
{
ret = remap_pfn_range(vma, addr, virt_to_phys((volatile void *)pvio->dr[i]) >> PAGE_SHIFT, PAGE_SIZE, vma->vm_page_prot);
if(ret < 0)
break;
addr += PAGE_SIZE;
}
return ret;
}
//#endif
/*初始化eb_isap5000_fobj_t类型变量fo,把字符设备赋给fo, s并把fo赋给file->private_data*/
static int eb_isap5000_vio_open (struct inode * node, struct file *file)
{
printk("\ndev open\n");
/*将设备描述结构指针赋值给文件私有数据指针*/
file->private_data = &vio;
return 0;
}
static ssize_t eb_isap5000_vio_read (struct file *file, char *buf, size_t count, loff_t *pos)
{
return 0;
}
static ssize_t eb_isap5000_vio_write (struct file *file, const char *buf, size_t count, loff_t *pos)
{
return 0;
}
static long eb_isap5000_vio_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
{
return 0;
}
/*
vm_area_struct描述的是一个文件映射的虚存空间vm_file指向被映射的文件的file结构
*/
static int eb_isap5000_vio_mmap(struct file *file, struct vm_area_struct *vma)
{
vma->vm_flags |= VM_IO | VM_RESERVED | VM_MAYSHARE | VM_MAYREAD | VM_MAYWRITE | VM_LOCKED;
vma->vm_page_prot.pgprot |= PAGE_SHARED.pgprot;/*mmap共享读取*/
printk("mmap\n");
return eb_isap5000_vio_map_mem(&vio, vma);
}
static int eb_isap5000_vio_release(struct inode *node, struct file *file)
{
return 0;
}
static struct file_operations eb_isap5000_vio_fops =
{
.owner = THIS_MODULE,
.read = eb_isap5000_vio_read,
.write = eb_isap5000_vio_write,
.unlocked_ioctl = eb_isap5000_vio_ioctl,
.mmap = eb_isap5000_vio_mmap,
.open = eb_isap5000_vio_open,
.release = eb_isap5000_vio_release
};
static int eb_isap5000_free_mem(eb_isap5000_vio_t *pvio)
{
int count = 0;
int i = 0;
count = pvio->msize / PAGE_SIZE;
for(i = 0; i < count; i++)
{
if(pvio->dr[i])
{
vio_free((void *)pvio->dr[i], PAGE_SIZE);//free_pages(pvio->dr[i], 0);
}
}
if(pvio->dr != NULL)
{
kfree(pvio->dr);
}
return 0;
}
static int eb_isap5000_get_mem(eb_isap5000_vio_t *pvio)
{
int count = 0;
int i = 0;
int flag = 0;
pvio->dr = (unsigned long *)kmalloc(PAGE_SIZE, GFP_KERNEL);
if(pvio->dr == NULL)
{
printk("no mmeory\n");
return -ENOMEM;
}
count = pvio->msize / PAGE_SIZE;
if(count > PAGE_SIZE / sizeof(unsigned long))
return -ENOMEM;
for(i = 0; i < count; i++)
{
pvio->dr[i] = (unsigned long)vio_alloc(PAGE_SIZE);/*vio的成员dr数组中分配若干个页*/
if(pvio->dr[i] == 0)
{
flag = i;
break;
}
memset((void *)pvio->dr[i], 0x77, PAGE_SIZE);
}
if(flag != 0)
{
pvio->msize = i * PAGE_SIZE;
eb_isap5000_free_mem(pvio);
return -ENOMEM;
}
return 0;
}
static int __init eb_isap5000_vio_init(void)
{
int ret;
int result;
dev_t devno = MKDEV(devnum, 0); //获得设备编号
/* 静态申请设备号*/
if (devnum)
result = register_chrdev_region(devno, 1, EB_ISAP5000_DEVICE_NAME);
else /* 动态分配设备号 */{
result = alloc_chrdev_region(&devno, 0, 1, EB_ISAP5000_DEVICE_NAME);
devnum = MAJOR(devno);
}
if (result < 0)
return result;
/*初始化cdev结构*/
cdev_init(&cdev, &eb_isap5000_vio_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &eb_isap5000_vio_fops;
/* 注册字符设备 */
cdev_add(&cdev, MKDEV(devnum, 0), 1);
memset(&vio, 0, sizeof(eb_isap5000_vio_t));
vio.msize = EB_ISAP5000_MAP_SIZE;
printk("\ninit modules\n");
ret = eb_isap5000_get_mem(&vio); /*字符设备分配内存*/
if(ret < 0){
printk("\nget memory failed\n");
return ret;
}
return 0;
}
static void __exit eb_isap5000_vio_exit(void)
{
printk("\nexit modules\n");
cdev_del(&cdev); /*注销设备*/
eb_isap5000_free_mem(&vio);
unregister_chrdev_region(MKDEV(devnum, 0), 1); /*释放设备号*/
}
module_init(eb_isap5000_vio_init);
module_exit(eb_isap5000_vio_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ushyun@163.com");
MODULE_DESCRIPTION("EB V5 Device Virtual IO Driver.");
编写char_mmap_test.c文件
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <pthread.h>
#include <errno.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/time.h>
#include <signal.h>
#include <semaphore.h>
#include <errno.h>
#include <sys/ipc.h>
/***********************************************
val : v/p信号量的初始值
return : semid
***********************************************/
int my_sem_init(int val)
{
int sem;
sem = semget(IPC_PRIVATE, 1, 0600);
if (sem ==-1) {
printf("\nsemget failed\n");
//exit(1);
return 0;
}
semctl(sem, 0, SETVAL, val);
return sem;
}
/*****************************************
semid:v/p信号量id
n: 0 单信号量 >0 多信号量
******************************************/
void my_sem_lock(int semid, int n)
{
struct sembuf op;
op.sem_num = n;
op.sem_op = -1;
op.sem_flg = 0;
while (semop(semid, &op, 1) < 0) {
if (errno != EINTR) {
printf("\nmy_sem_lock semop()\n");
//exit(1);
return ;
}
}
return;
}
/*****************************************
semid:v/p信号量id
n: 0 单信号量 >0 多信号量
return:-1 错误返回
-2 没有信号量可用
0 成功
******************************************/
int my_sem_trylock(int semid, int n)
{
struct sembuf op;
op.sem_num = n;
op.sem_op = -1;
op.sem_flg = IPC_NOWAIT;
while (semop(semid, &op, 1) < 0) {
if (errno != EINTR) {
if (errno == EAGAIN) {
printf("\nmy_sem_trylock error\n");
return -1;
}
printf("\nmy_sem_trylock no sem\n");
return -2;
}
}
return 0;
}
void my_sem_unlock(int semid, int n)
{
struct sembuf op;
int ret;
op.sem_num = n;
op.sem_op = +1;
op.sem_flg = 0;
do {
ret = semop(semid, &op, 1);
} while (ret < 0 && errno == EINTR);
return;
}
#define EB_ISAP5000_DEVICE_NAME "/dev/isap5000_vio"
#define EB_ISAP5000_MAP_SIZE (1000 * 4096)
typedef struct{
int semid[20];
int sival[20];
}eb_dev_t;
eb_dev_t *ptr;
void *ebf_dev_map_init(void)
{
int fd = -1;
void *pm;
fd = open(EB_ISAP5000_DEVICE_NAME,O_RDWR);
if(fd < 0){
printf("open failure %d\n",fd);
return NULL;
}
pm = mmap(NULL, EB_ISAP5000_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(pm == MAP_FAILED){
printf("mmap failure\n");
return NULL;
}
return pm;
}
int chaild_progress( eb_dev_t *ptr)
{
int i;
while(1){
for(i=0;i<20;i++){
printf("other %d %d\n",i,ptr->sival[i]);
my_sem_lock(ptr->semid[i], 0);
ptr->sival[i] = i;
sleep(2);
printf("chaild %d %d\n",i,ptr->sival[i]);
my_sem_unlock(ptr->semid[i], 0);
}
}
return 0;
}
int other_progress( eb_dev_t *ptr)
{
int i;
while(1){
for(i=0;i<20;i++){
printf("chaild %d %d\n",i,ptr->sival[i]);
my_sem_lock(ptr->semid[i], 0);
ptr->sival[i] = 20-i;
sleep(1);
printf("other %d %d\n",i,ptr->sival[i]);
my_sem_unlock(ptr->semid[i], 0);
}
}
return 0;
}
void ebf_sem_init(void)
{
int i;
for(i=0;i<20;i++){
ptr->semid[i] = my_sem_init(1);
printf("\nsemget %d\n",i);
}
}
int main()
{
pid_t pid;
ptr = (eb_dev_t *)ebf_dev_map_init();
if(NULL == ptr){
printf("dev mmap failure\n");
return -1;
}
memset(ptr,0,sizeof(eb_dev_t));
ebf_sem_init();
pid = fork();
if(pid==0){
chaild_progress(ptr);
}
other_progress(ptr);
return 0;
}
下载内核源码
在ubunto终端命令行上操作:
切换到root用户:su
apt-get install linux-source
在/usr/src/下会有linux-source-3.2.0内核源码 我的版本是3.2.0
cd /usr/src/linux-source-3.2.0
添加驱动文件到内核源码
把上面的char_mmap.c拷贝到 当前目录下的drivers/char/下
vi drivers/char/Kconfig
在menu “Character devices”下添加下面内容
config CHARMMP
tristate “Don't need something support”
help
wellcome my char mmap device driver
vi drivers/char/Makefile
在开头添加下面内容
obj-m += char_mmap.o