1.4 简单的LED驱动

1、驱动操作硬件的方法

驱动操作硬件时需要用到硬件物理地址在内核虚拟地址空间相对应的虚拟地址

2、内核虚拟地址映射方法

静态映射:内核移植时直接使用编码的方式,如果需要修改地址映射则要修改内核源代码,还要重新编译内核。

动态映射:驱动根据需要动态建立映射。

3、静态映射操作LED

struct cdev s_cdev;   /* 字符设备 */
dev_t s_devNo;        /* 设备号 */

#define GPJ0CON    *((volatile unsigned int *)S5PV210_GPJ0CON)    //GPJ0CON映射的虚拟地址
#define GPJ0DAT    *((volatile unsigned int *)S5PV210_GPJ0DAT)    //GPJ0DAT映射的虚拟地址

static int LED_test_open(struct inode *inode, struct file *file)
{
    return 0;
}

static int LED_test_release(struct inode *inode, struct file *file)
{
    return 0;
}

//应用层和驱动之间的数据交换,copy_from_user:从用户空间复制到内核空间,copy_to_user:从内核空间复制到用户空间
static ssize_t LED_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    char kBuf[1024] = "module_test_read";
    int ret = copy_to_user(ubuf, kBuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_to_user success");
    return count;
}
static ssize_t LED_test_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
    char kBuf[1024];
    int ret = copy_from_user(kBuf, ubuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_from_user success");
        
    GPJ0CON = 0x11111111; 
    if (kBuf[0] = '1')
        GPJ0DAT = (0 << 3);
    else if (kBuf[0] = '0')
        GPJ0DAT = (1 << 3);
        
    return count;
}

struct file_operations LED_test_fops =
{
    .owner = THIS_MODULE,
    .read = LED_test_read,
    .write = LED_test_write,
    .open = LED_test_open,
    .release = LED_test_realse,
};

static int __init chrdev_LED_init(void)
{
    //printk是内核源码中用来打印信息的函数,KERN_DEBUG是打印级别
    printk(KERN_DEBUG "chrdev_LED_init");
    
    /*************** 动态申请设备号 *****************/
    int ret = alloc_chrdev_region(&s_devNo, 0, 1, "LED_MODULE_TEST");
    if (ret < 0)
    {
        printk(KERN_ERR "alloc_chrdev_region failed");
        return -1;
    }
    
    /*************** 注册字符设备驱动 **************/
    //建立cdev和fops连接
    s_cdev.owner = THIS_MODULE;
    s_cdev.fops = LED_test_fops;
    cdev_init(&s_cdev, &LED_test_fops);
    
    //向内核注册字符设备驱动
    ret = cdev_add(&s_cdev, s_devNo, 1);
    if (ret < 0)
    {
        printk(KERN_ERR "cdev_add failed");
        unregister_chrdev_region(s_devNo, 1);
        retun -1;
    }
    
    return 0;
}

static void __exit chrdev_LED_exit(void)
{
    printk(KERN_DEBUG "chrdev_LED_exit");
    
    //注销字符设备驱动
    cdev_del(&s_cdev);
    
    //释放设备号
    unregister_chrdev_region(s_devNo, 1);
}

module_init(chrdev_LED_init);
module_exit(chrdev_LED_exit);

//宏定义
MODULE_LICENSE("GPL")                     //模块的许可证
MODULE_AUTHOR("xy_L")                     //模块的作者
MODULE_DESCIPTION("chrdev LED module")    //模块的描述
MODULE_ALIAS("LED_test")                  //模块的别名

4、动态映射操作LED

(1)建立动态映射

//向内核申请需要映射的内存资源
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0) 

//检查物理地址的合法性,建立页表(包括访问权限),完成物理地址到虚拟地址的转换
void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);

(2)销毁动态映射

//释放内存资源
#define release_mem_region(start,n) __release_region(&iomem_resource, (start), (n))

//取消ioremap的映射
void iounmap(void * addr);

//需要先iounmap再进行release_mem_region

(3)示例

struct cdev s_cdev;   /* 字符设备 */
dev_t s_devNo;        /* 设备号 */

#define GPJ0CON    0xE0200240    //GPJ0CON物理地址
#define GPJ0DAT    0xE0200244    //GPJ0DAT物理地址

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;

static int LED_test_open(struct inode *inode, struct file *file)
{
    /***************** 动态映射操作寄存器 *************/
    //动态映射GPJ0CON
    if (!request_mem_region(GPJ0CON, 4, "GPJ0CON"))
        return -EINVAL;
    pGPJ0CON = ioremap(GPJ0CON, 4);
    
    //动态映射GPJ0DAT                                                  
    if (!request_mem_region(GPJ0DAT, 4, "GPJ0DAT"))
        return -EINVAL;

     pGPJ0DAT = ioremap(GPJ0DAT, 4);
 
    return 0;
}

static int LED_test_release(struct inode *inode, struct file *file)
{
    // 解除映射
    iounmap(pGPJ0CON);
    release_mem_region(GPJ0CON, 4);
    
    iounmap(pGPJ0DAT);

    release_mem_region(GPJ0DAT, 4);
    
    return 0;
}

//应用层和驱动之间的数据交换,copy_from_user:从用户空间复制到内核空间,copy_to_user:从内核空间复制到用户空间
static ssize_t LED_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
    char kBuf[1024] = "module_test_read";
    int ret = copy_to_user(ubuf, kBuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_to_user success");
    return count;
}
static ssize_t LED_test_write(struct file *file, const char __user *ubuf, size_t count, loff_t *ppos)
{
    char kBuf[1024];
    int ret = copy_from_user(kBuf, ubuf, count);
    if (ret == 0)
        printk(KERN_DEBUG, "copy_from_user success");
        
    *pGPJ0CON = 0x11111111; 
    if (kBuf[0] = '1')
        *pGPJ0DAT = (0 << 3);
    else if (kBuf[0] = '0')
        *pGPJ0DAT = (1 << 3);
        
    return count;
}

struct file_operations LED_test_fops =
{
    .owner = THIS_MODULE,
    .read = LED_test_read,
    .write = LED_test_write,
    .open = LED_test_open,
    .release = LED_test_realse,
};

static int __init chrdev_LED_init(void)
{
    //printk是内核源码中用来打印信息的函数,KERN_DEBUG是打印级别
    printk(KERN_DEBUG "chrdev_LED_init");
    
    /*************** 动态申请设备号 *****************/
    int ret = alloc_chrdev_region(&s_devNo, 0, 1, "LED_MODULE_TEST");
    if (ret < 0)
    {
        printk(KERN_ERR "alloc_chrdev_region failed");
        return -1;
    }
    
    /*************** 注册字符设备驱动 **************/
    //建立cdev和fops连接
    s_cdev.owner = THIS_MODULE;
    s_cdev.fops = LED_test_fops;
    cdev_init(&s_cdev, &LED_test_fops);
    
    //向内核注册字符设备驱动
    ret = cdev_add(&s_cdev, s_devNo, 1);
    if (ret < 0)
    {
        printk(KERN_ERR "cdev_add failed");
        unregister_chrdev_region(s_devNo, 1);
        retun -1;
    }
    
    return 0;
}

static void __exit chrdev_LED_exit(void)
{
    printk(KERN_DEBUG "chrdev_LED_exit");
    
    //注销字符设备驱动
    cdev_del(&s_cdev);
    
    //释放设备号
    unregister_chrdev_region(s_devNo, 1);
}

module_init(chrdev_LED_init);
module_exit(chrdev_LED_exit);

//宏定义
MODULE_LICENSE("GPL")                     //模块的许可证
MODULE_AUTHOR("xy_L")                     //模块的作者
MODULE_DESCIPTION("chrdev LED module")    //模块的描述
MODULE_ALIAS("LED_test")                  //模块的别名

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值