• GPIO硬件资源的申请,内核空间和用户空间的数据交换,ioctl(.....),设备文件的自动创建


    1、通过GPIO库函数控制LED
       open("/dev/myleds",...)       close(fd)
       ---------------------------------------------
       sys_open                      sys_close
           led_open(...)                led_release(...)
           {                            {
               亮灯                        灭灯
           }                            }
       
       电路原理图:
           控制LED1 实则控制CPU上的管脚GPIOC12输出低/高电平
       如何控制GPIOC12输出低/高电平:cpu datasheet
           GPIOCALTFN0  0xc001c020
           GPIOCOUTENB  0xc001c004
           GPIOCOUT     0xc001c000
       linux系统中如何控制GPIO管脚输出:本质上就是操作以上特殊功能寄存器
           1)将物理地址映射为虚拟地址
              然后通过虚拟地址访问特殊功能寄存器
           2)内核中提供了一套控制GPIO的库函数   
       
       GPIO库函数控制GPIO管脚有固定的步骤:
           1)申请GPIO管脚 (可选的)
              int gpio_request(unsigned gpio, const char *name)
                  gpio,要申请的管脚编号
                       arch/arm/plat-s5p6818/common/cfg_type.h
                  name,名称
                  返回值,0 申请成功
                          非0 失败
                          
                          
           2)使用GPIO管脚
              //将参数指定的管脚设置为输入模式
              int gpio_direction_input(unsigned gpio)
              //获取指定管脚的电平状态
              // 0/1, 低/高
              int gpio_get_value(unsigned gpio)
              
              
              //将参数指定的管脚设置为输出模式
              //value 为0 默认输出低电平
                      非0 默认输出高电平
              int gpio_direction_output(unsigned gpio, int value)
              //设置指定管脚输出高/低电平
              void gpio_set_value(unsigned gpio, int value)   
           3)释放GPIO管脚 (可选的)
              //释放管脚
              void gpio_free(unsigned gpio)          
        
       安装模块前将内核中自带的LED驱动裁剪掉
          cd kernel/
          make menuconfig
             Device Drivers  --->
               -*- LED Support  --->
                  < >   LED Support for GPIO connected LEDs
                  < >   PWM driven LED Support
                  [ ]   LED Trigger support
          make uImage
          
          让开发板使用新内核        
             cp arch/arm/boot/uImage /tftpboot/
             setenv bootcmd ping 192.168.1.8;ping 192.168.1.8 ; tftp 48000000 uImage ; bootm 48000000
             saveenv
             
          insmod led_drv.ko           
          mknod /dev/myleds c 244 5
          ./test
          观察LED1的变化     
       练习:LED1/2/3/4    

    2、内核空间和用户空间的数据交换
       用户空间代码不能(直接)访问内核空间
       内核空间代码访问用户空间时也加了限制
           
       long copy_to_user(void __user *to,
            const void *from, unsigned long n)
        to, 目标地址
            该地址应该介于0~3G
        from, 源数据地址
             该地址应该介于3G~4G
        n, 希望连续拷贝的字节数
        返回值,拷贝失败的字节数        
            
            
       long copy_from_user(void *to,
            const void __user * from, unsigned long n)    
            to, 目标地址 (3G~4G)
            from,源数据地址(0~3G)
            n, 连续拷贝的字节数
            返回值,操作失败的字节数
    练习:
         用户空间向led1设备写入1时灭灯
                               0  亮
                           读取 1 灯是灭            
     
    3、ioctl
       对LED写操作实现LED亮灭控制不合理?
       
       如果需要去实现uart驱动程序
           应用程序通过uart发数据 应该实现write
                           收             read
           如何在用户空间改变通信时的波特率?
               用户空间使用ioctl----->unlocked_ioctl
     
       ioctl,用于设置或者获取设备工作属性信息
            例如uart 通信时 设置使用8n1 115200
                查询当前uart通信时使用的波特率        
       
       函数原型:int ioctl(int d, int request, ...)
       通常使用
                1)ioctl(fd, cmd)
                2)ioctl(fd, cmd, &arg)
                
       练习:./test on/off   1/2/3/4         

    4、设备文件的自动创建  
       1) 根文件系统中有mdev
       
       2) 挂载了proc sys 文件系统
          rootfs/etc/init.d/rcS
             mount -a
          rootfs/etc/fstab
             proc    /proc   proc    defaults    0   0
             sysfs   /sys    sysfs   defaults    0   0
       3)配置热插拔事件产生后要执行的动作
          echo /sbin/mdev > /proc/sys/kernel/hotplug
                       产生热插拔事件后,自动到proc/sys/kernel/hotplug执行/sbin/mdev产生相应的设备文件。
          热插拔事件:
              狭义: U盘的插入和拔出
              广义: 也包括/sys目录的文件变化
          
       4)编程产生热插拔事件
          
          class_create
          struct device *device_create(struct class *class, struct device *parent,
                           dev_t devt, void *drvdata, const char *fmt, ...)
                 class, 该device属于那种类型的设备
                        该果实挂在哪个树枝上
                 parent, 该device的父设备
                 devt, 设备号
                 drvdata,创建设备时传递给设备的附加信息
                         通常给NULL                       
                 fmt, ...:决定了将来/dev目录下产生的设备文件的名称
                         例如:  "ttySAC%d",i
                         
                         for(i=0; i<4; i++)
                         {
                             device_create(...., "ttySAC%d",i);
                         }
                         
                         
          device_destroy
          class_destroy

    4) 设备的文件自动创建
          真正创建设备文件的软件:mdev
          
          挂载proc sys文件系统 /proc /sys
          
          procfs
          sysyfs都是基于内存的文件系统
                其中的内容都是linux在执行过程中动态创建的
          
          procfs, 用来向用户空间导出内核态的执行信息
                 常用的一些系统软件 例如ps基于该文件系统实现的
                 
                 每个进程都会在proc目录对应一个其进程ID命名的文件夹
                 
                 cat /proc/cmdline
                 cat /proc/partitions
                 cat /proc/cpuinfo
                 cat /proc/meminfo
                 cat /proc/devices
                 ....  
                 cat /proc/sys/kernel/printk
                 echo 7 >/proc/sys/kernel/printk
                 echo /sbin/mdev >/proc/sys/kernel/hotplug
           sysfs, 它是从procfs分家出来的  
                  专门用来展示硬件驱动模型 层次关系  
                  
                 安装xxx.ko模块
                 会自动产生 /sys/module/xxx/    
                 
           触发热插拔事件
               class_create  device_create
               class_destroy device_destroy

     1 #include "../../global.h"
     2 #include <linux/fs.h>
     3 #include <linux/cdev.h>
     4 #include <linux/gpio.h>
     5 #include <mach/platform.h>
     6 
     7 unsigned int major = 0;
     8 unsigned int minor = 5;
     9 dev_t dev ; //设备号
    10 
    11 /*1 定义一个struct cdev变量*/
    12 struct cdev led_cdev;
    13 
    14 
    15 static int led_open(struct inode *inode, 
    16                     struct file *filp)
    17 {
    18     /*输出低电平*/
    19     gpio_set_value(PAD_GPIO_C+12, 0);
    20     return 0;
    21 }
    22 static int led_close(struct inode *inode, 
    23                     struct file *filp)
    24 {
    25     gpio_set_value(PAD_GPIO_C+12, 1);
    26     return 0;
    27 }
    28 
    29 struct file_operations led_fops =
    30 {
    31     .owner = THIS_MODULE,
    32     .open  = led_open,
    33     .release = led_close,
    34 };
    35 int __init led_drv_init(void)
    36 {
    37     if(major) //静态
    38     {
    39         //dev = major<<20|minor;
    40         dev = MKDEV(major, minor);
    41 
    42         register_chrdev_region(dev, 1, "myleds");
    43     }
    44     else //动态注册
    45     {
    46         alloc_chrdev_region(&dev, minor, 1, "myleds");
    47         printk("<1>" "major=%d minor=%d
    ",
    48                 MAJOR(dev), MINOR(dev));
    49     }
    50     /*2 初始化cdev变量*/
    51     cdev_init(&led_cdev, &led_fops);
    52     /*3 注册cdev变量*/
    53     cdev_add(&led_cdev, dev, 1);
    54     /*申请GPIO管脚*/
    55     gpio_request(PAD_GPIO_C+12, "led1");
    56     /*使用GPIO管脚*/
    57     gpio_direction_output(PAD_GPIO_C+12, 1);
    58 
    59     return 0;
    60 }
    61 void __exit led_drv_exit(void)
    62 {
    63     /*释放GPIO管脚*/
    64     gpio_free(PAD_GPIO_C+12);
    65     /*4 注销cdev*/
    66     cdev_del(&led_cdev);
    67 
    68     unregister_chrdev_region(dev, 1);
    69 }
    70 module_init(led_drv_init);
    71 module_exit(led_drv_exit);
      1 #include "../../global.h"
      2 #include <linux/fs.h>
      3 #include <linux/cdev.h>
      4 #include <linux/gpio.h>
      5 #include <mach/platform.h>
      6 #include <linux/uaccess.h>
      7 
      8 unsigned int major = 0;
      9 unsigned int minor = 5;
     10 dev_t dev ; //设备号
     11 
     12 /*1 定义一个struct cdev变量*/
     13 struct cdev led_cdev;
     14 
     15 
     16 static int led_open(struct inode *inode, 
     17                     struct file *filp)
     18 {
     19     return 0;
     20 }
     21 static int led_close(struct inode *inode, 
     22                     struct file *filp)
     23 {
     24     return 0;
     25 }
     26 int k_stat = 0; //记录灯的当前状态
     27 ssize_t led_write(struct file *filp, 
     28                   const char __user *buf,
     29                   size_t len, loff_t *offset)
     30 {
     31     int ret = 0;
     32     int k_cmd = 0;
     33     //buf中保存的是用户空间地址 
     34     //*buf在直接读取用户空间内存 没有权限检查 不安全
     35     //*buf
     36     ret = copy_from_user(&k_cmd, buf, len);
     37     /*
     38      *cmd=0 亮灯
     39      *   =1 灭灯
     40      * */
     41     gpio_set_value(PAD_GPIO_C+12, k_cmd);
     42     /*
     43      * 1,灭的状态
     44      * 0,亮的状态
     45      * */
     46     k_stat = k_cmd;
     47     return len;
     48 
     49 }
     50 ssize_t led_read(struct file *filp, 
     51                  char __user *buf,
     52                  size_t len, loff_t *offset)
     53 {
     54     int ret = 0;
     55     /*
     56      *buf 保存的是用户空间stat变量的地址
     57      *内核需要向其中写入数据应该先检查是否有写权限
     58      *以下做法不安全
     59      * */
     60     //*buf = k_stat;
     61     
     62     ret = copy_to_user(buf, &k_stat, len);
     63 
     64     return len;
     65 
     66 }
     67 struct file_operations led_fops =
     68 {
     69     .owner = THIS_MODULE,
     70     .open  = led_open,
     71     .release = led_close,
     72     .write = led_write,
     73     .read = led_read,
     74 };
     75 int __init led_drv_init(void)
     76 {
     77     if(major) //静态
     78     {
     79         //dev = major<<20|minor;
     80         dev = MKDEV(major, minor);
     81 
     82         register_chrdev_region(dev, 1, "myleds");
     83     }
     84     else //动态注册
     85     {
     86         alloc_chrdev_region(&dev, minor, 1, "myleds");
     87         printk("<1>" "major=%d minor=%d
    ",
     88                 MAJOR(dev), MINOR(dev));
     89     }
     90     /*2 初始化cdev变量*/
     91     cdev_init(&led_cdev, &led_fops);
     92     /*3 注册cdev变量*/
     93     cdev_add(&led_cdev, dev, 1);
     94     /*申请GPIO管脚*/
     95     gpio_request(PAD_GPIO_C+12, "led1");
     96     /*使用GPIO管脚*/
     97     gpio_direction_output(PAD_GPIO_C+12, 1);
     98 
     99     return 0;
    100 }
    101 void __exit led_drv_exit(void)
    102 {
    103     /*释放GPIO管脚*/
    104     gpio_free(PAD_GPIO_C+12);
    105     /*4 注销cdev*/
    106     cdev_del(&led_cdev);
    107 
    108     unregister_chrdev_region(dev, 1);
    109 }
    110 module_init(led_drv_init);
    111 module_exit(led_drv_exit);
    #include "../../global.h"
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/gpio.h>
    #include <mach/platform.h>
    #include <linux/uaccess.h>
    
    #define CMD_LED_ON  (0x10001)
    #define CMD_LED_OFF (0x10002)
    
    unsigned int major = 0;
    unsigned int minor = 5;
    dev_t dev ; //设备号
    
    /*1 定义一个struct cdev变量*/
    struct cdev led_cdev;
    
    typedef struct led_desc
    {
        int gpio;//管脚编号
        char *name;//名称
    }led_desc_t;
    
    led_desc_t leds[]=
    {
        {PAD_GPIO_C+12, "led1"},
        {PAD_GPIO_C+7 , "led2"},
        {PAD_GPIO_C+11, "led3"},
        {PAD_GPIO_B+26, "led4"},
    };
    
    
    static int led_open(struct inode *inode, 
                        struct file *filp)
    {
        return 0;
    }
    static int led_close(struct inode *inode, 
                        struct file *filp)
    {
        return 0;
    }
    ssize_t led_write(struct file *filp, 
                      const char __user *buf,
                      size_t len, loff_t *offset)
    {
        return len;
    }
    ssize_t led_read(struct file *filp, 
                     char __user *buf,
                     size_t len, loff_t *offset)
    {
        return len;
    }
    
    long led_ioctl(struct file *filp, 
                   unsigned int cmd,
                   unsigned long arg)
    {
        int ret = 0;
        int k_index = 0;
    
        ret = copy_from_user(&k_index, 
                             (const void *)arg, 4);
        
        if(k_index> 4 || k_index<1)
        {
            return -EINVAL;
        }
    
        switch(cmd)
        {
            case CMD_LED_ON:
                gpio_set_value(leds[k_index-1].gpio, 0);
                break;
            case CMD_LED_OFF:
                gpio_set_value(leds[k_index-1].gpio, 1);
                break;
            default:
                return -EINVAL;
        }
    
        return 0;
    }
    
    struct file_operations led_fops =
    {
        .owner = THIS_MODULE,
        .open  = led_open,
        .release = led_close,
        .write = led_write,
        .read = led_read,
        .unlocked_ioctl = led_ioctl,
    };
    int __init led_drv_init(void)
    {
        int i = 0;
        if(major) //静态
        {
            //dev = major<<20|minor;
            dev = MKDEV(major, minor);
    
            register_chrdev_region(dev, 1, "myleds");
        }
        else //动态注册
        {
            alloc_chrdev_region(&dev, minor, 1, "myleds");
            printk("<1>" "major=%d minor=%d
    ",
                    MAJOR(dev), MINOR(dev));
        }
        /*2 初始化cdev变量*/
        cdev_init(&led_cdev, &led_fops);
        /*3 注册cdev变量*/
        cdev_add(&led_cdev, dev, 1);
        
        for(i=0; i<ARRAY_SIZE(leds); i++)
        {
            /*申请GPIO管脚*/
            gpio_request(leds[i].gpio, leds[i].name);
            /*使用GPIO管脚*/
            gpio_direction_output(leds[i].gpio, 1);
        }
        return 0;
    }
    void __exit led_drv_exit(void)
    {
        int i = 0;
        for(i=0; i<ARRAY_SIZE(leds); i++)
        {
            /*释放GPIO管脚*/
            gpio_free(leds[i].gpio);
        }
        /*4 注销cdev*/
        cdev_del(&led_cdev);
    
        unregister_chrdev_region(dev, 1);
    }
    module_init(led_drv_init);
    module_exit(led_drv_exit);
      1 #include "../../global.h"
      2 #include <linux/fs.h>
      3 #include <linux/cdev.h>
      4 #include <linux/gpio.h>
      5 #include <mach/platform.h>
      6 #include <linux/uaccess.h>
      7 #include <linux/device.h>
      8 
      9 #define CMD_LED_ON  (0x10001)
     10 #define CMD_LED_OFF (0x10002)
     11 
     12 struct class *cls = NULL;
     13 
     14 unsigned int major = 0;
     15 unsigned int minor = 5;
     16 dev_t dev ; //设备号
     17 
     18 /*1 定义一个struct cdev变量*/
     19 struct cdev led_cdev;
     20 
     21 typedef struct led_desc
     22 {
     23     int gpio;//管脚编号
     24     char *name;//名称
     25 }led_desc_t;
     26 
     27 led_desc_t leds[]=
     28 {
     29     {PAD_GPIO_C+12, "led1"},
     30     {PAD_GPIO_C+7 , "led2"},
     31     {PAD_GPIO_C+11, "led3"},
     32     {PAD_GPIO_B+26, "led4"},
     33 };
     34 
     35 
     36 static int led_open(struct inode *inode, 
     37                     struct file *filp)
     38 {
     39     return 0;
     40 }
     41 static int led_close(struct inode *inode, 
     42                     struct file *filp)
     43 {
     44     return 0;
     45 }
     46 ssize_t led_write(struct file *filp, 
     47                   const char __user *buf,
     48                   size_t len, loff_t *offset)
     49 {
     50     return len;
     51 }
     52 ssize_t led_read(struct file *filp, 
     53                  char __user *buf,
     54                  size_t len, loff_t *offset)
     55 {
     56     return len;
     57 }
     58 
     59 long led_ioctl(struct file *filp, 
     60                unsigned int cmd,
     61                unsigned long arg)
     62 {
     63     int ret = 0;
     64     int k_index = 0;
     65 
     66     ret = copy_from_user(&k_index, 
     67                          (const void *)arg, 4);
     68     
     69     if(k_index> 4 || k_index<1)
     70     {
     71         return -EINVAL;
     72     }
     73 
     74     switch(cmd)
     75     {
     76         case CMD_LED_ON:
     77             gpio_set_value(leds[k_index-1].gpio, 0);
     78             break;
     79         case CMD_LED_OFF:
     80             gpio_set_value(leds[k_index-1].gpio, 1);
     81             break;
     82         default:
     83             return -EINVAL;
     84     }
     85 
     86     return 0;
     87 }
     88 
     89 struct file_operations led_fops =
     90 {
     91     .owner = THIS_MODULE,
     92     .open  = led_open,
     93     .release = led_close,
     94     .write = led_write,
     95     .read = led_read,
     96     .unlocked_ioctl = led_ioctl,
     97 };
     98 int __init led_drv_init(void)
     99 {
    100     int i = 0;
    101     if(major) //静态
    102     {
    103         //dev = major<<20|minor;
    104         dev = MKDEV(major, minor);
    105 
    106         register_chrdev_region(dev, 1, "myleds");
    107     }
    108     else //动态注册
    109     {
    110         alloc_chrdev_region(&dev, minor, 1, "myleds");
    111         printk("<1>" "major=%d minor=%d
    ",
    112                 MAJOR(dev), MINOR(dev));
    113     }
    114     /*2 初始化cdev变量*/
    115     cdev_init(&led_cdev, &led_fops);
    116     /*3 注册cdev变量*/
    117     cdev_add(&led_cdev, dev, 1);
    118     /*自动产生设备文件*/
    119     /*会产生 /sys/class/LEDS/*/
    120     cls = class_create(THIS_MODULE, "LEDS");
    121     /*会产生 /sys/class/LEDS/myleds/ */
    122     device_create(cls,NULL,dev, NULL,"myleds");
    123 
    124     for(i=0; i<ARRAY_SIZE(leds); i++)
    125     {
    126         /*申请GPIO管脚*/
    127         gpio_request(leds[i].gpio, leds[i].name);
    128         /*使用GPIO管脚*/
    129         gpio_direction_output(leds[i].gpio, 1);
    130     }
    131     return 0;
    132 }
    133 void __exit led_drv_exit(void)
    134 {
    135     int i = 0;
    136     for(i=0; i<ARRAY_SIZE(leds); i++)
    137     {
    138         /*释放GPIO管脚*/
    139         gpio_free(leds[i].gpio);
    140     }
    141     /*自动销毁设备文件*/
    142     device_destroy(cls, dev);
    143     class_destroy(cls);
    144 
    145     /*4 注销cdev*/
    146     cdev_del(&led_cdev);
    147 
    148     unregister_chrdev_region(dev, 1);
    149 }
    150 module_init(led_drv_init);
    151 module_exit(led_drv_exit);
  • 相关阅读:
    迭代器特性
    没有一代人的青春是容易的『白岩松,演讲』
    编程趣话
    重新给PPT排序
    打印长图
    罗永浩答网友问:“能跟我们分享一件印象深刻至今你都记得的牛逼么?”
    活成加菲这样真是绝了!加菲语录大搜罗!
    加菲猫经典语录收录
    那些难以忘记的加菲猫经典语录
    做好这5点基本要求 才能算一个合格的HTML5动画
  • 原文地址:https://www.cnblogs.com/DXGG-Bond/p/11844763.html
Copyright © 2020-2023  润新知