字符设备驱动学习(1)

首先,以查询方式的按键驱动开始字符设备驱动的学习。
目的:按键驱动查询方式获取按键值:
1、写出驱动框架
2、硬件操作,相关实现。

一.写出框架:对于驱动的学习,框架思想非常重要
1.1.file_operation: file_operation 结构是一个字符驱动如何应用程序建立连接. 这个结构, 定义在 linux/fs.h中, 是一个函数指针的集合. 每个打开文件与它自身的函数集合相关连( 通过包含一个称为 f_op 的成员, 它指向一个 file_operations 结构). 这些操作大部分负责实现系统调用。

1.2.相关初始化,注册函数、卸载函数
1、首先。要定义cdev结构体:内核在内部使用类型 struct cdev 的结构来代表字符设备. 在内核调用你的设备操作前, 你编写分配并注册一个或几个这些结构.
并定义主设备号、以及dev_t类型的设备号变量。dev_t类型设备号通过MKDEV宏获得。

2、对于设备号可以根据主设备号进行静态申请设备号(register_chrdev_region())和动态申请设备号(alloc_chrdev_region()主设备号为零时动态申请)

3、设备号申请完成后,将cdev初始化并与file_operation关联起来,在将我们定义的cdev这个结构体加入内核中cdev链表(在内核里,有cdev类型的元素的链表,使用cdev与物理设备及其驱动进行绑定,我们在用户空间对设备文件操作,因为设备文件含有主次设备号,就能找到cdev链表中找到与这个设备文件相关的cdev结构体,进而可以使用与它绑定的file_operation中的驱动程序去操作物理设备)。

4、卸载函数:注销字符设备 cdev_del( &cdev );
注销设备号 unregister_chrdev_region( dev_t num, 1 );因为设备号有限,卸载驱动时需要注销设备号。

5、对于以上的初始化、卸载函数现在系统还识别不了,需要告知操作系统
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE(“Dual BSD/GPL”);//开源协议

6、为了让加载驱动模块是,系统自己创建设备文件,就需要给内核sysfs(一个虚拟的文件系统)提供信息,udev机制,可以自动创建设备结点,能自动创建的前提是根文件系统有挂载sysfs虚拟文件系统,利用mdev/udev机制,根据/sys中的系统信息自动创建设备节点。方法:创建一个class,然后再class下创建一个设备class device (具体实现看代码)。

二、硬件操作实现

1、看原理图:确定相关使用的引脚
2、看手册:相关寄存器
3、编程实现功能(在有系统的情况下使用的是虚拟地址:ioremap()在入口函数里进行地址映射,在init函数中实现)
4、在open函数里配置引脚,
5、在read函数里

#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <asm/irq.h>


#define KEY_DRIVER1_MAJOR 0



/*声明用到的结构体和变量*/
struct cdev key_driver1_cdev;
static int key_driver1_major = KEY_DRIVER1_MAJOR;
dev_t key_dev_num;//dev_t类型设备号
static struct class *key_drv_class;
static struct class_device *key_drv_class_dev;
/*用于地址映射*/
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;

volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat;

#define rGPFCON 0X56000050
#define rGPGCON 0X56000060

static int key_driver1_open(struct inode *node, struct file *file)
{

    /*采用查询方式,将按键的引脚进行配置为输入*/
    *gpfcon &= ~((0x3 << (0*2)) | (0x3 << (2*2)));

    *gpgcon &= ~((0x3 << (3*2)) | (0x3 << (11*2)));

    return 0;
}

static int key_driver1_read (struct file * file, char __user * buf, size_t size, loff_t *ppos)
{
    unsigned char key_val[4];
    int regval;
    int ret;

    if(size != sizeof(key_val))
        return -EINVAL;
    /*读取GPF0,2的值*/
    regval = *gpfdat;
    key_val[0] = (regval & (1<<0)) ? 1 : 0;
    key_val[1] = (regval & (1<<2)) ? 1 : 0;
    /*读取GPF3,11的值*/
    regval = *gpgdat;
    key_val[2] = (regval & (1<<3)) ? 1 : 0;
    key_val[3] = (regval & (1<<11)) ? 1 : 0;

    ret = copy_to_user(buf,key_val,sizeof(key_val));//从设备拷贝数据到用户空间


    return sizeof(key_val);

}


/*文件操作结构体*/
static const struct file_operations key_driver1_fops =
{
  .owner = THIS_MODULE,
  .read = key_driver1_read,
  .open = key_driver1_open,
};


/*设备驱动模块加载函数*/
int key_driver1_init(void)
{
  int result;
  int err;
  key_dev_num = MKDEV(key_driver1_major, 0);

  /* 申请设备号*/
  if (key_driver1_major)
    result = register_chrdev_region(key_dev_num, 1, "key_driver1");
  else  /* 动态申请设备号 */
  {
    result = alloc_chrdev_region(&key_dev_num, 0, 1, "key_driver1");
    key_driver1_major = MAJOR(key_dev_num);
  }  
  if (result < 0)
    return result;
  /*
    *进行cdev的初始化,将cdev与key_driver1_fops关联


  */
  cdev_init(&key_driver1_cdev, &key_driver1_fops);
  key_driver1_cdev.owner = THIS_MODULE;
  key_driver1_cdev.ops = &key_driver1_fops;
  err = cdev_add(&key_driver1_cdev, key_dev_num, 1);
  if (err)
    printk(KERN_NOTICE "Error %d ", err);

  /*
       * 为设备创建类
       * 加载驱动模块时在/sys/class   目录下自动创建car_class  文件夹
       */
      key_drv_class = class_create(THIS_MODULE, "key_driver1");
      if(IS_ERR(key_drv_class))
      {
          printk("cannot create key_drv_class!\n");
          return 0;
      }
      /*创建设备文件节点,避免需要手动创建*/
      key_drv_class_dev = class_device_create(key_drv_class, NULL,key_dev_num, NULL, "key_driver1");

    /*将GPIO  的物理地址映射到内核空间,*/
    gpfcon = (volatile unsigned long *)ioremap(rGPFCON, 16);
    gpfdat = gpfcon + 1;

    gpgcon = (volatile unsigned long *)ioremap(rGPGCON, 16);
    gpgdat = gpgcon + 1;


  return 0;

}

/*模块卸载函数*/
void key_driver1_exit(void)
{

  cdev_del(&key_driver1_cdev);   /*注销cdev*/
  unregister_chrdev_region(key_dev_num, 1); /*释放设备号*/

  /*删除设备文件和dev/class  目录下的相应文件夹*/
    class_device_unregister(key_drv_class_dev);
    class_destroy(key_drv_class);
    /*解除GPIO  映射*/
    iounmap(gpfcon);
    iounmap(gpgcon);

}

MODULE_AUTHOR("maomao");
MODULE_LICENSE("Dual BSD/GPL");

module_init(key_driver1_init);
module_exit(key_driver1_exit);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值