Chinaunix首页 | 论坛 | 博客
  • 博客访问: 283735
  • 博文数量: 58
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 600
  • 用 户 组: 普通用户
  • 注册时间: 2015-11-27 08:37
个人简介

从linux了解世界

文章分类
文章存档

2017年(5)

2016年(51)

2015年(2)

我的朋友

分类: 嵌入式

2016-01-12 10:18:24

    经过最近这几天学习字符设备驱动,我发现学习字符设备驱动模块编写是一个剥洋葱的过程,不考虑设备文件是手动生成还是利用mdev生成,仅仅考虑注册字符设备,编写模块加载函数仅仅需要一个核心函数register_chrdev()这个函数里面又包含了很多函数,一层一层的播下去就能搞懂驱动在linux中的工作原理。我现在对字符设备理解的还不透彻,深入的学习笔记以后再写,今天主要记录根据led的驱动熟悉字符设备驱动框架这个洋葱最外层的函数。
这部分的学习主要是模仿韦东山的视频,具体问题看宋宝华的《linux设备驱动开发详解》,如果说韦东山视频中的代码算洋葱的第一层,name宋宝华的模板代码就算第二层,今天先看第一层。首先cat /proc/devices下使用的主设备号(要先挂载proc文件系统),在/dev目录下用未使用的设备号(假设为xxx)建立设备文件。其实设备文件可以让linux自动生成,这涉及到busybox中使用mdev的知识,使用mdev的方法并不难百度上的资料说的都很清楚。
头文件在博客里显示不出来,就不贴了
volatile unsigned long *gpbcon = NULL;
volatile unsigned long *gpbdat = NULL;//mini2440上的led接在gpb上
int major=xxx;
static int first_drv_open(struct inode *inode, struct file *file);
static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos);

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
    .write    =    first_drv_write,       
};//这里的file_operations结构体对应的就是/dev下代表led的设备文件,它记录了该设备文件所使用的模块和函数。

static int first_drv_init(void)
{
    register_chrdev(major, "first_drv", &first_drv_fops); // 注册设备,把主设备号为major的设备文件和first_drv_fops这个文件结构体绑定
    gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);//ioremap(baseaddress, length)物理地址映射
    gpbdat = gpbcon + 1;
    return 0;
}

static int first_drv_open(struct inode *inode, struct file *file)
{
    printk("first_drv_open\n");
    *gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2))| (0x3<<(8*2)));
    *gpbcon |= ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2))| (0x1<<(8*2)));//配置led
    //*gpbcon &= ~(0x3<<0) ;
    //*gpbcon |= (0x1<<0);//配置蜂鸣器
    return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    char val;
    printk("first_drv_write\n");
    copy_from_user(&val, buf, count); //因为我们最终操作硬件是在用户空间,在用户空间向内核空间写值需要copy_from_user()
    if (val == 1)
    {
        // 点灯
        *gpbdat &= ~((1<<5) | (1<<6) | (1<<7)| (1<<8));//led
        //*gpbdat |= (1<<0);//蜂鸣器
    }
    else
    {
        // 灭灯
        *gpbdat |= ((1<<5) | (1<<6) | (1<<7)| (1<<8));//led
        //*gpbdat &= ~(1<<0);//蜂鸣器
    }
    
    return 0;
}

static void first_drv_exit(void)
{
    unregister_chrdev(major, "first_drv"); // 卸载设备
    iounmap(gpbcon);//解除物理地址映射
}

module_init(first_drv_init);
module_exit(first_drv_exit);

MODULE_LICENSE("GPL");
当我们编写测试程序检验驱动模块时,发现只有两个led被控制,另外两个从内核启动时就一直在像心脏一样闪,应该是linux内核中自动加载了什么模块
果然在linux配置中发现有led support这一项,里面有两个选项被选中了,led并不重要直接m掉,重新编译内核,烧写后重启发现果然那两个led不闪了
阅读(1887) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~