• 第一个驱动 led_drive


    前面记录了根文件系统,现在开始写驱动程序,关于框架什么的都是一些概念的东西,这里就不详述,先写一个最简单的LED驱动

    从现在开始进入Linux驱动的大门,如何自已写,参考内核自带的字符驱动,仿照写出自已的驱动,这些驱动只适合自已使用,想

    做成通用的驱动,后面会慢慢深入,现在先了解和熟悉

    linux驱动有以下几个组成

      1:file_operations 结构体

        包含open write read 等操作函数

      2:入口和出口函数

        入口 注册驱动 创建类 类下创建设备 地址映射

        出口 同入口相反 卸载驱动 类 类设备 取消地址映射

      3:功能操作函数

        当应用程序/测试程序 执行对就函数时,就会调用到具体的操作函数,在这里函数里实现具体功能实现

    下面看代码比较直观:参考其它驱动和不断调试修改的最终代码

    #include <linux/interrupt.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/types.h>
    #include <linux/miscdevice.h>
    #include <linux/ioport.h>
    #include <linux/fcntl.h>
    #include <linux/mc146818rtc.h>
    #include <linux/init.h>
    #include <linux/poll.h>
    #include <linux/proc_fs.h>
    #include <linux/seq_file.h>
    #include <linux/spinlock.h>
    #include <linux/sched.h>
    #include <linux/sysctl.h>
    #include <linux/wait.h>
    #include <linux/bcd.h>
    #include <linux/delay.h>
    #include <linux/uaccess.h>
    #include <linux/ratelimit.h>
    #include <linux/bfs_fs.h>

    #include <asm/current.h>


    static struct class *leddev_class; /* 定义一个类用于自动创建设备节点 */
    static struct device *dev_led;

    volatile unsigned long *gpfcon = NULL;
    volatile unsigned long *gpfdat = NULL;

    /* 测试程序执行open 时会调用这个函数 

     * 这个函数实现 GPIO的设置

     */

    static int led_dev_open(struct inode *inode , struct file *file)
    {
      printk("open function test! ");
      *gpfcon &= ~ ((0x03 << 8) | (0x03 << 10) | (0x03 << 12));
      *gpfcon |= ((0x01 << 8) | (0x01 << 10) | (0x01 << 12));
      return 0;
    }

    /* 测试程序执行write时 调用驱动的这个函数 

     * copy_from_user 拷贝测试程序的数据到驱动

     * 根据数据执行对就的硬件操作

     */

    static int led_dev_write(struct file *file, char __user *buf,size_t count, loff_t *ppos)
    {
      int val;

      copy_from_user(&val,buf,count); //copy_to_user;

      if(val == 1)
      {
        *gpfdat &= ~((1 << 4) | (1 << 5) | (1 << 6));
        printk("led1 led2 led3 on! ");
      }
      else
      {
        *gpfdat |= ((1 << 4) | (1 << 5) | (1 << 6));
        printk("led1 led2 led3 off! ");
      }
      return 0;
    }

    /* 定义一个file_operations结构体
    * 实现 open write 两个函数
    */
    static struct file_operations led_dev_per = {
      .owner = THIS_MODULE,
      .open = led_dev_open,
      .write = led_dev_write,
    };
    /* 驱动程序入口函数 入口函数 注册字符设备 装载驱时调用
    * 创建类 并在类下创建设备 用于mdev 自动创建设备节点
    * 寄存器地址映射 用虚拟地址操作GPIO
    */
    static int major; /* 定义一个变量用于主设备号 */
    static int led_dev_init(void)
    {
      major = register_chrdev(0, "led_dev", &led_dev_per);
      leddev_class = class_create(THIS_MODULE, "led_dev");
      dev_led = device_create(leddev_class, NULL, MKDEV(major,0), NULL, "leds");
      gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
      gpfdat = gpfcon + 1;
      return 0;
    }
    /* 驱动程序出口函数 卸载驱动时调用
    * 卸载字符设备 卸载类设备 卸载类
    * 取消寄存器地址映射
    */
    static void led_dev_exit(void)
    {
      unregister_chrdev(major, "led_dev");
      device_destroy(dev_led, MKDEV(major,0));
      class_destroy(leddev_class);
      iounmap(gpfcon);
    }
    /* 入口函数和出口函数需要用下面的宏修饰
    * 还需要加入 "GPL" 协议
    */
    module_init(led_dev_init);
    module_exit(led_dev_exit);
    MODULE_LICENSE("GPL");

    下面列出测试程序

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

    int main(int argc,char **argv)
    {
    int fd;
    int val = 1;
    fd = open("/dev/leds",O_RDWR); /* 打开设备/dev/leds 可读可写 */
    if(fd < 0) /* 判断打开是否成功 */
    printf("can't open this dev! "); /* 失败就打印这条信息 */
    if(argc != 2) /* 判断串口输入参数 不等于2时 */
    {
    printf("Usage: "); /* 打印使用帮助信息 */
    printf(" %s <on/off> ",argv[0]);
    }
    if(strcmp(argv[1],"on") == 0) /* 判断第二个参数 等ON 打开LED */
    val = 1;
    else if(strcmp(argv[1],"off") == 0) /* 判断第二个参数 等OFF 关闭LED */
    val = 0;
    write(fd,&val,4); /* 把值发送给驱动程序 */
    return 0;
    }

    这里列出Makefile

    KERN_DIR = /work/linux/linux-3.4.2

    all:
    make -C $(KERN_DIR) M=`pwd` modules

    clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

    obj-m += led_char_dev.o

    驱动个人认为主要是记住需要的东西和流程即可,内核如何操作,后面会学到

    钻木取火!拼的是体力?耐心?智慧?
  • 相关阅读:
    hdu 4801模拟题
    ASP.NET程序中动态修改web.config中的设置项目(后台CS代码)
    缓存依赖语句
    ajax post提交数据, input type=submit 返回prompt aborted by user
    JQuery Ajax调用asp.net后台方法
    ASP.NET Cache
    c#字符串及数组操作
    C#字符串与char数组互转!
    c# equals与==的区别
    如何将DataTable转换成List<T>呢?
  • 原文地址:https://www.cnblogs.com/x2i0e19linux/p/11688289.html
Copyright © 2020-2023  润新知