虚拟字符驱动,申请n页内存,使用mmap映射到应用程序空间,用户就可以直接访问不需要任何同步机制

编写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("[email protected]");

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


编译驱动模块

make M=drivers/char/ modules

插入内核模块char_mmap.ko
insmod drivers/char/char_mmap.ko

查看模块
cat /proc/devices 
可以看到 212  isap5000_vio

建立设备节点
mknod  /dev/isap5000_vio c 212 0

删除驱动模块(等测试完了再删除)
rmmod drivers/char/char_mmap.ko

测试驱动

把char_mmap_test.c拷贝到当前目录下

编译
gcc -o test  char_mmap_test.c

执行:
./test

会有显示消息

如果想看prink打印的消息,可以这样操作:
ctrl + arl + F2~F6
会进入字符终端
进入root用户:su
执行 echo 8 > /proc/sys/kernel/printk

再执行

编译驱动模块

下面的东东!




你可能感兴趣的:(字符驱动,printk问题,mmap映射,VP信号量,内核用户数据同步)