旧接口注册LED字符驱动设备(动态映射)

#include <linux/init.h>            // __init   __exit
#include <linux/module.h>      // module_init  module_exit

#include <linux/fs.h>          //file_operations

#include <asm/uaccess.h>          //copy_from_user  copy_to_user

#include <mach/regs-gpio.h>
#include <mach/gpio-bank.h>

#include <asm/string.h> 

#include <linux/ioport.h>          //request_mem_region
#include <asm/io.h>                 //ioremap

#define S5PV210_PA_GPIOJ0CON 0xe0200240

volatile unsigned int *rGPJ0CON  = NULL; 
volatile unsigned int *rGPJ0DAT = NULL;  

static int led_open(struct inode *inode, struct file *file);
ssize_t led_read(struct file *file, char __user *user, size_t count, loff_t *loff);
ssize_t led_write(struct file *file, const char __user *user, size_t count, loff_t *loff);
static int led_release(struct inode *inode, struct file *file);

static int led_major = -1;
static char kbuf[100] = {0};
static const struct file_operations led_fops = {
    .owner        = THIS_MODULE,
    .open        = led_open,
    .read       = led_read,
    .write      = led_write,
    .release    = led_release,
};

int led_open(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "led_open successful\n");
    return 0;
}

ssize_t led_read(struct file *file, char __user *user, size_t ucount, loff_t *loff)
{
    printk(KERN_INFO "led_read successful\n");
    if (copy_to_user(user,kbuf , ucount))
    {
        printk(KERN_INFO "copy_to_user fail\n");
        return -EINVAL;
    }
    printk(KERN_INFO "copy_to_user successful\n");
    return strlen(kbuf);
}

ssize_t led_write(struct file *file, const char __user *user, size_t ucount, loff_t *loff)
{
    printk(KERN_INFO "led_write successful\n");
    memset(kbuf,0,sizeof(kbuf));
    if (copy_from_user(kbuf, user, ucount))
    {
        printk(KERN_INFO "copy_from_user fail\n");
        return -EINVAL;
    }
    
    if(!strcmp(kbuf,"on"))
    {
        *rGPJ0CON &=0xff000fff;
        *rGPJ0CON |=0x00111000;
        *rGPJ0DAT &=~((0x01<<3)|(0x01<<4)|(0x01<<5));
    }
    else if(!strcmp(kbuf,"off"))
    {
        *rGPJ0CON &=0xff000fff;
        *rGPJ0CON |=0x00111000;
        *rGPJ0DAT |=((0x01<<3)|(0x01<<4)|(0x01<<5));
    }
    return ucount;
    printk(KERN_INFO "copy_from_user successful\n");
}

int led_release(struct inode *inode, struct file *file)
{
    printk(KERN_INFO "led_release successful\n");
    return 0;    
}

// 模块安装函数
static int __init chrdev_init(void)
{    
    int ret = -1;
    printk(KERN_INFO "chrdev_init successful\n");
    
    if ((led_major = register_chrdev (0, "led_dev", &led_fops)) < 0)
    {
        printk(KERN_WARNING "led_module.c: Failed to register character device.");
        ret = -EINVAL;
    }
    if (request_mem_region(S5PV210_PA_GPIOJ0CON, 8, "GPIOJ0CON") == NULL) 
    {
        printk(KERN_WARNING "failed to get memory region\n");
        ret = -ENOENT;
        goto err_req;
    }
    rGPJ0CON = ioremap(S5PV210_PA_GPIOJ0CON,8);
    if (rGPJ0CON ==  NULL) 
    {
        printk(KERN_WARNING "fail to ioremap() region\n");
        ret = -ENOENT;
        goto err_map;
    }
    rGPJ0DAT = rGPJ0CON+1;
err_map:
    iounmap(rGPJ0CON);
    
err_req:
    release_mem_region(S5PV210_PA_GPIOJ0CON,8);
    
    return 0;
}

// 模块卸载函数
static void __exit chrdev_exit(void)
{
    iounmap(rGPJ0CON);
    release_mem_region(S5PV210_PA_GPIOJ0CON,8);
    unregister_chrdev(led_major,"led_dev");
    printk(KERN_INFO "chrdev_exit successful\n");

}


module_init(chrdev_init);
module_exit(chrdev_exit);

// MODULE_xxx这种宏作用是用来添加模块描述信息
MODULE_LICENSE("GPL");                // 描述模块的许可证
MODULE_AUTHOR("musk");                // 描述模块的作者
MODULE_DESCRIPTION("module test");    // 描述模块的介绍信息
MODULE_ALIAS("alias xxx");            // 描述模块的别名信息
View Code

    上述程序是led非常简陋,手动注册的字符驱动代码。真正的内核驱动不是这么简单!

    一. 关于字符设备注册,静态虚拟地址请查看上篇文章《注册LED字符驱动设备(静态映射)》

    二. 关于动态映射

        2.1. 虚拟地址到物理地址的映射可以多对1,但是不能1对多,故可以多个动态虚拟地址和一个静态虚拟地址同事指向同一个物理地址寄存器。

        2.2. 向内核申请&释放需要映射的内存资源

            2.2.1. 由于soc所有的资源都是内核管理的,使用时应先申请,这样如果资源以及被其他驱动使用了则申请不到。这样可以很好的避免一个资源在两个驱动中同时被使用造成错误。

            2.2.2. 申请函数request_mem_region

#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)

                a. 参数start 是要申请资源的物理地址

                b. 参数n是要申请资源的大小(单位是byte)

                c. 参数name是给申请的资源起个别名

                d. 成功返回非NULL失败返回NULL

        2.2.2. 申请函数release_mem_region

#define release_mem_region(start,n)__release_region(&iomem_resource, (start), (n))

                a. 参数start 是要申请资源的物理地址

                b. 参数n是要申请资源的大小(单位是byte)

        2.2.3. IO内存映射/去映射

            2.2.3.1. release_mem_region申请了IO资源,接下来就是进行物理地址到虚拟地址的映射

            2.2.3.2. IO内存映射函数:ioremap(cookie,size)

#define ioremap(cookie,size)		__arm_ioremap(cookie, size, MT_DEVICE)

                a. 参数cookie是映射资源的物理地址

                b. 参数size是映射的大小

            2.2.3.3. IO内存映射函数:iounmap(cookie)

#define iounmap(cookie)__iounmap(cookie)

                a. 参数cookie是映射资源的物理地址

      三. 应用层调用内核

        3.1. 相关代码如下

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <string.h>

#define DEVFILE "/dev/led_dev"

int main(void)
{
    char buf[100] = {0};
    int fd = -1;
    if((fd =open(DEVFILE, O_RDWR))<0)
    {
        perror("open");
        return -1;   
    }
    printf("open successful fd = %d\n",fd);
    if(write(fd, "on", strlen("on"))<0)
    {
        perror("write");
        return -1;
    }   
    sleep(3);
    memset(buf,0,sizeof(buf));
    if(read(fd, buf, 3)<0)
    {
        perror("read");
        return -1;
    }
    printf("read data = %s\n",buf);
    
    
    if(write(fd, "off", strlen("off"))<0)
    {
        perror("write");
        return -1;
    }   
    sleep(3);
    memset(buf,0,sizeof(buf));
    if(read(fd, buf, 4)<0)
    {
        perror("read");
        return -1;
    }
    printf("read data = %s\n",buf);
    
    close(fd);
    return 0;
}
View Code

 

posted @ 2019-01-04 09:28  三七鸽  阅读(247)  评论(0)    收藏  举报