linux设备树-基于pinctrl的LED字符设备驱动

发布时间 2023-05-03 17:32:18作者: 大奥特曼打小怪兽

在前面我们已经介绍了pinctrl subsystem相关的基础知识,这一节我们尝试修改设备树,在pin controller node下添加两个子节点分别用来控制LED1~LED4的全亮/全灭。然后我们编写LED驱动程序,配置LED的两种状态:

  • default:默认状态,LED1~LED4全亮;
  • myled-off:LED1~LED4全灭;

一、修改设备树

1.1 修改s3c2440-pinctrl.dtsi

修改内核arch/arm/boot/dts/s3c2440-pinctrl.dtsi文件,在pinctrl节点下添加两个引脚配置节点:

myled_on: myled-on {
       samsung,pins = "gpb-5","gpb-6","gpb-7","gpb-8";   /* GPB5~8 */
       samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; /* 设置为输出模式  */
       samsung,pin-val = <0x0>;         /* 初始值输出低电平 */
       samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
};

myled_off: myled-off {
     samsung,pins = "gpb-5","gpb-6","gpb-7","gpb-8";  /* 引用GPB5~8*/
     samsung,pin-function = <EXYNOS_PIN_FUNC_OUTPUT>; /* 设置为输出模式 */
     samsung,pin-val = <0x1>;         /* 初始值输出高电平 */
     samsung,pin-pud = <EXYNOS_PIN_PULL_UP>;
};

其中:

  • myled_on节点配置GPB5~GPB8默认为输出模式,并且输出低电平,从而达到LED1~LED4点亮的效果;
  • myled_off节点配置GPB5~GPB8默认为输出模式,并且输出高电平,从而达到LED1~LED4熄灭的效果。

1.2 修改s3c2440-smdk2440.dts

在内核arch/arm/boot/dts/s3c2440-smdk2440.dts文件中添加myled设备节点

myled: myled {
      compatible = "myled";
      status = "okay";
      pinctrl-names = "default", "myled_off";    /* 定义两种状态 */
      pinctrl-0 = <&myled_on>;                   /* 当使用default状态时,就会使用所引用节点的配置*/
      pinctrl-1 = <&myled_off>;                  /* 当使用myled_off状态时,就会使用所引用节点的配置*/
};

这里定义了myled设备的两种状态:

  • default:默认状态,引脚配置设置为&myled_on;
  • myled_off:引脚配置设置为&myled_off;

二、LED驱动程序

这里我们仍然以led驱动程序为例,进行讲解。在/work/sambashare/drivers下创建24.led_dev_pinctr文件夹。用来保存LED驱动程序以及测试应用程序。

2.1 编写led_open、led_write函数

#define DTSLED_CNT      1
#define DTSLED_NAME     "myled"

/*  下面这个几个类型由于内核没有提供给client device driver使用,因此我们只能自己定义 */
struct pinctrl_setting_mux {
        unsigned group;
        unsigned func;
};
struct pinctrl_setting_configs {
        unsigned group_or_pin;
        unsigned long *configs;
        unsigned num_configs;
};
struct pinctrl_setting {
    struct list_head node;
    enum pinctrl_map_type type;
    struct pinctrl_dev *pctldev;
    const char *dev_name;
    union {
        struct pinctrl_setting_mux mux;   // mux配置数据
        struct pinctrl_setting_configs configs;  // config配置数据
    } data;
};
struct pinctrl_state {
        struct list_head node;
        const char *name;
        struct list_head settings;
};

/* 定义一个led设备 */
struct led_dev myled;

/* 定义led结构体 */
struct led_dev {
    dev_t devid;                   /* 字符设备编号 */
    struct cdev cdev;              /* 保存操作结构体的字符设备 */
    struct class *class;           /* class */
    struct device *device;         /* 设备类 */
    struct device_node *nd;        /* 设备节点 */
    struct pinctrl *pinctrl;       /* pin control state holder */
    struct pinctrl_state *state;   /* 当前pin control state */
};

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

/* 点亮/熄灭 LED01 */
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    int val,ret;

    copy_from_user(&val, buf, count);   // 用户空间到内核空间传递数据

    printk("value %d",val);

    if(val == 1){
        /* 点亮 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
        if (myled.state == NULL){
            printk("pinctrl lookup state failed\n");
            return -1;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return -1;
        }else{
            printk("pinctrl select state success\n");
        }
    }
    else{
        /* 熄灭 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "myled_off"); //获取pinctrl-1的配置状态
        if (myled.state == NULL){
            printk("pinctrl lookup state failed\n");
            return -1;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return -1;
        }else{
            printk("pinctrl select state success\n");
        }
    }

    return 0;
}

2.2 platform driver定义

这里我们采用platform设备驱动模型,因此需要定义platform_driver:

/*
 * 用于设备树匹配
 */
static const struct of_device_id led_dt_match[] = {
    { .compatible = "myled", },
    {},
};

/*
 * platform驱动
 */
static struct platform_driver led_driver = {
    .probe = led_probe,
    .remove = led_remove,

    .driver = {
        .name = "myled",
        .of_match_table = led_dt_match,  // 匹配列表
    }
};

2.3 led_probe

/*
 * 当驱动和硬件信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中
 */
static int led_probe(struct platform_device *pdev)
{
      int ret = 0,i = 0;
      struct pinctrl_setting *setting;
      struct device_node *np;
      const char *statename;
      struct dev_pin_info *pins;


      if(pdev->name != NULL){
            printk("platform device name %s",pdev->name);   // myled
      }

      /* 获取设备引脚状态信息 */
      np = pdev->dev.of_node;
      ret = of_property_read_string_index(np, "pinctrl-names", 0, &statename);  // 读取pinctrl-names属性第0个值
      if (ret == 0) {
           printk("pinctrl-names index 0 value %s",statename);
      }

       /* 1.获取与设备相关联的pinctrl句柄 */
       pins = pdev->dev.pins;
       myled.pinctrl = pins->p;
       if(myled.pinctrl == NULL){
            printk("retrieves the pinctrl handle for a device failed\n");
            return -1;
       }else{
            printk("retrieves the pinctrl handle for a device success\n");
       }

       /* 2. 获取指定的name的state */
       myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
       if (myled.state == NULL){
           printk("pinctrl lookup state failed\n");
           return -1;
       }else{
           printk("pinctrl lookup state success\n");
       }

        /*3、设置引脚状态 */
       ret = pinctrl_select_state(myled.pinctrl, myled.state);
       if (ret < 0){
           printk("pinctrl select state failed\n");
           return -1;
       }else{
           printk("pinctrl select state success\n");
       }

       /* 输出状态state以及状态setting信息 */
       if(myled.state->name != NULL){
            printk("state name %s", myled.state->name);    // 状态名称为default
       }

        /* 遍历当前状态下的所有setting */
       list_for_each_entry(setting, &(myled.state->settings), node) {
               printk("setting type %d", setting->type);
               printk("setting dev_name %s", setting->dev_name);    // 设备名称为myled
               if(setting->type == PIN_MAP_TYPE_MUX_GROUP ){        // 引脚复用 枚举值为2
                     printk("setting mux group %d", setting->data.mux.group);
                     printk("setting mux func %d", setting->data.mux.func);
               }else{                                              // 配置引脚电气特性
                    printk("--------------configs start--------");
                    printk("setting configs group_or_pin %d", setting->data.configs.group_or_pin);
                    for(i=0; i<setting->data.configs.num_configs; i++){
                        printk("setting configs configs %d", setting->data.configs.configs[i]);
                    }
                    printk("--------------configs end--------");
               }
        }

       /* 4. 动态分配字符设备号 */
       ret = alloc_chrdev_region(&myled.devid, 0, 1,DTSLED_NAME);  // ls /proc/devices看到的名字
       /* 返回值为负数,表示操作失败 */
       if (ret < 0) {
           printk("alloc char device region failed\n");
           goto faile_devid;
       }else{
           printk("alloc char device region success\n");
       }

     /* 5.初始化字符设备,添加字符设备 */
      cdev_init(&myled.cdev, &led_fops);
      ret = cdev_add(&myled.cdev, myled.devid, DTSLED_CNT);
      /* 返回值为负数,表示操作失败 */
      if (ret < 0) {
          printk("char device add failed\n");
          goto fail_cdev;
      }else{
          printk("char device add success\n");
      }

    /* 6.创建类,它会在sys目录下创建/sys/class/dtsled这个类  */
     myled.class = class_create(THIS_MODULE, DTSLED_NAME);
     if(IS_ERR(myled.class)){
         printk("create class failed\n");
         goto fail_class;
     }else{
          printk("create class success\n");
      }

    /* 7. 在/sys/class/led下创建dtsled设备,然后mdev通过这个自动创建/dev/dtsled这个设备节点 */
     myled.device = device_create(myled.class, NULL, myled.devid, NULL, DTSLED_NAME);
     if(IS_ERR(myled.device)){
         printk("create device failed\n");
         goto fail_device;
     }else{
         printk("create device success\n");
    }

     return 0;

fail_findnd:
    device_destroy(myled.class, myled.devid);
fail_device:
    class_destroy(myled.class);
fail_class:
    cdev_del(&myled.cdev);
fail_cdev:
    unregister_chrdev_region(myled.devid, DTSLED_CNT);
faile_devid:
    return ret;
}

 在这个函数中我输出了当前设备的状态,以及状态下的配置信息。

2.4 led_remove

/*
 * 硬件信息被移除了,或者驱动被卸载了,全部要释放,释放资源的操作就放在该函数中
 */
static int led_remove(struct platform_device * pdev)
{
    printk("led driver exit\n");

    /* 注销类、以及类设备 */
    device_destroy(myled.class, myled.devid);
    class_destroy(myled.class);

    /* 删除设备,卸载注册的设备编号 */
    cdev_del(&myled.cdev);
    unregister_chrdev_region(myled.devid, DTSLED_CNT);

    return 0;
}

2.5 led_drv.c完整代码   

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/io.h>
#include <linux/errno.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/devinfo.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/list.h>

#define DTSLED_CNT      1
#define DTSLED_NAME     "myled"

/*  下面这个几个类型由于内核没有提供给client device driver使用,因此我们只能自己定义 */
struct pinctrl_setting_mux {
        unsigned group;
        unsigned func;
};
struct pinctrl_setting_configs {
        unsigned group_or_pin;
        unsigned long *configs;
        unsigned num_configs;
};
struct pinctrl_setting {
    struct list_head node;
    enum pinctrl_map_type type;
    struct pinctrl_dev *pctldev;
    const char *dev_name;
    union {
        struct pinctrl_setting_mux mux;   // mux配置数据
        struct pinctrl_setting_configs configs;  // config配置数据
    } data;
};
struct pinctrl_state {
        struct list_head node;
        const char *name;
        struct list_head settings;
};

/* 定义一个led设备 */
struct led_dev myled;

/* 定义led结构体 */
struct led_dev {
    dev_t devid;                   /* 字符设备编号 */
    struct cdev cdev;              /* 保存操作结构体的字符设备 */
    struct class *class;           /* class */
    struct device *device;         /* 设备类 */
    struct device_node *nd;        /* 设备节点 */
    struct pinctrl *pinctrl;       /* pin control state holder */
    struct pinctrl_state *state;   /* 当前pin control state */
};

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

/* 点亮/熄灭 LED01 */
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
    int val,ret;

    copy_from_user(&val, buf, count);   // 用户空间到内核空间传递数据

    printk("value %d",val);

    if(val == 1){
        /* 点亮 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
        if (myled.state == NULL){
            printk("pinctrl lookup state failed\n");
            return -1;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return -1;
        }else{
            printk("pinctrl select state success\n");
        }
    }
    else{
        /* 熄灭 */
        myled.state = pinctrl_lookup_state(myled.pinctrl, "myled_off"); //获取pinctrl-1的配置状态
        if (myled.state == NULL){
            printk("pinctrl lookup state failed\n");
            return -1;
        }else{
            printk("pinctrl lookup state success\n");
        }

         /*3、设置引脚状态*/
        ret = pinctrl_select_state(myled.pinctrl, myled.state);
        if (ret < 0){
           printk("pinctrl select state failed\n");
           return -1;
        }else{
            printk("pinctrl select state success\n");
        }
    }

    return 0;
}

static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,
    .open   =   led_open,
    .write  =   led_write,
};

/*
 * 当驱动和硬件信息匹配成功之后,就会调用probe函数,驱动所有的资源的注册和初始化全部放在probe函数中
 */
static int led_probe(struct platform_device *pdev)
{
      int ret = 0,i = 0;
      struct pinctrl_setting *setting;
      struct device_node *np;
      const char *statename;
      struct dev_pin_info *pins;


      if(pdev->name != NULL){
            printk("platform device name %s",pdev->name);   // myled
      }

      /* 获取设备引脚状态信息 */
      np = pdev->dev.of_node;
      ret = of_property_read_string_index(np, "pinctrl-names", 0, &statename);  // 读取pinctrl-names属性第0个值
      if (ret == 0) {
           printk("pinctrl-names index 0 value %s",statename);
      }

       /* 1.获取与设备相关联的pinctrl句柄 */
       pins = pdev->dev.pins;
       myled.pinctrl = pins->p;
       if(myled.pinctrl == NULL){
            printk("retrieves the pinctrl handle for a device failed\n");
            return -1;
       }else{
            printk("retrieves the pinctrl handle for a device success\n");
       }

       /* 2. 获取指定的name的state */
       myled.state = pinctrl_lookup_state(myled.pinctrl, "default"); //获取pinctrl-0的配置状态
       if (myled.state == NULL){
           printk("pinctrl lookup state failed\n");
           return -1;
       }else{
           printk("pinctrl lookup state success\n");
       }

        /*3、设置引脚状态 */
       ret = pinctrl_select_state(myled.pinctrl, myled.state);
       if (ret < 0){
           printk("pinctrl select state failed\n");
           return -1;
       }else{
           printk("pinctrl select state success\n");
       }

       /* 输出状态state以及状态setting信息 */
       if(myled.state->name != NULL){
            printk("state name %s", myled.state->name);    // 状态名称为default
       }

        /* 遍历当前状态下的所有setting */
       list_for_each_entry(setting, &(myled.state->settings), node) {
               printk("setting type %d", setting->type);
               printk("setting dev_name %s", setting->dev_name);    // 设备名称为myled
               if(setting->type == PIN_MAP_TYPE_MUX_GROUP ){        // 引脚复用 枚举值为2
                     printk("setting mux group %d", setting->data.mux.group);
                     printk("setting mux func %d", setting->data.mux.func);
               }else{                                              // 配置引脚电气特性
                    printk("--------------configs start--------");
                    printk("setting configs group_or_pin %d", setting->data.configs.group_or_pin);
                    for(i=0; i<setting->data.configs.num_configs; i++){
                        printk("setting configs configs %d", setting->data.configs.configs[i]);
                    }
                    printk("--------------configs end--------");
               }
        }

       /* 4. 动态分配字符设备号 */
       ret = alloc_chrdev_region(&myled.devid, 0, 1,DTSLED_NAME);  // ls /proc/devices看到的名字
       /* 返回值为负数,表示操作失败 */
       if (ret < 0) {
           printk("alloc char device region failed\n");
           goto faile_devid;
       }else{
           printk("alloc char device region success\n");
       }

     /* 5.初始化字符设备,添加字符设备 */
      cdev_init(&myled.cdev, &led_fops);
      ret = cdev_add(&myled.cdev, myled.devid, DTSLED_CNT);
      /* 返回值为负数,表示操作失败 */
      if (ret < 0) {
          printk("char device add failed\n");
          goto fail_cdev;
      }else{
          printk("char device add success\n");
      }

    /* 6.创建类,它会在sys目录下创建/sys/class/dtsled这个类  */
     myled.class = class_create(THIS_MODULE, DTSLED_NAME);
     if(IS_ERR(myled.class)){
         printk("create class failed\n");
         goto fail_class;
     }else{
          printk("create class success\n");
      }

    /* 7. 在/sys/class/led下创建dtsled设备,然后mdev通过这个自动创建/dev/dtsled这个设备节点 */
     myled.device = device_create(myled.class, NULL, myled.devid, NULL, DTSLED_NAME);
     if(IS_ERR(myled.device)){
         printk("create device failed\n");
         goto fail_device;
     }else{
         printk("create device success\n");
    }

     return 0;

fail_findnd:
    device_destroy(myled.class, myled.devid);
fail_device:
    class_destroy(myled.class);
fail_class:
    cdev_del(&myled.cdev);
fail_cdev:
    unregister_chrdev_region(myled.devid, DTSLED_CNT);
faile_devid:
    return ret;
}

/*
 * 硬件信息被移除了,或者驱动被卸载了,全部要释放,释放资源的操作就放在该函数中
 */
static int led_remove(struct platform_device * pdev)
{
    printk("led driver exit\n");

    /* 注销类、以及类设备 */
    device_destroy(myled.class, myled.devid);
    class_destroy(myled.class);

    /* 删除设备,卸载注册的设备编号 */
    cdev_del(&myled.cdev);
    unregister_chrdev_region(myled.devid, DTSLED_CNT);

    return 0;
}


/*
 * 用于设备树匹配
 */
static const struct of_device_id led_dt_match[] = {
    { .compatible = "myled", },
    {},
};

/*
 * platform驱动
 */
static struct platform_driver led_driver = {
    .probe = led_probe,
    .remove = led_remove,

    .driver = {
        .name = "myled",
        .of_match_table = led_dt_match,  // 匹配列表
    }
};

/*
 * platform驱动模块入口
 */
static int led_drv_init(void)
{
   // platform驱动注册
   int err = platform_driver_register(&led_driver);
   if (err) {
          printk("platform driver registered failed\n");
   } else {
          printk("platform driver registered successfully\n");
   }
   return err;
}

/*
 * platform驱动模块出口
 */
static void __exit led_drv_exit(void)
{
    printk("platform driver unregistered\n");
    // platform驱动卸载
    platform_driver_unregister(&led_driver);
}

module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
View Code

2.6 Makefile

KERN_DIR :=/work/sambashare/linux-5.2.8-dt
all:
    make -C $(KERN_DIR) M=`pwd` modules 
clean:
    make -C $(KERN_DIR) M=`pwd` modules clean
    rm -rf modules.order

obj-m += led_drv.o

三、LED驱动测试应用程序

在24.led_dev_pinctr下创建test文件夹,保存测试应用程序。

3.1 main.c

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

void print_usage(char *file)
{
    printf("Usage:\n");
    printf("%s <dev> <on|off>\n",file);
    printf("eg. \n");
    printf("%s /dev/myled on\n", file);
    printf("%s /dev/myled off\n", file);
}

int main(int argc,char **argv)
{
    int fd;
    int val;
    char *filename;

    if (argc != 3){
        print_usage(argv[0]);
        return 0;
    }

    filename = argv[1];
    fd = open(filename,O_RDWR);
    if(fd == -1){
        printf("can't open %s!\n",filename);
        return 0;
    }

   if (!strcmp("on", argv[2])){
        // 亮灯
        val = 1;
        printf("%s on!\n",filename);
        write(fd, &val, 4);
    }else if (!strcmp("off", argv[2])){
        // 灭灯
        val = 0;
        printf("%s off!\n",filename);
        write(fd, &val, 4);
    }else{
        print_usage(argv[0]);
    }

    return 0;
}

3.2 Makefile

all:
    arm-linux-gcc -march=armv4t -o main main.c
clean:
    rm -rf *.o main

四、烧录开发板测试

4.1 编译设备树

root@zhengyang:/work/sambashare/linux-5.2.8-dt# make dtbs
  DTC     arch/arm/boot/dts/s3c2416-smdk2416.dtb
  DTC     arch/arm/boot/dts/s3c2440-smdk2440.dtb

编译设备树文件,把前面配置过的arch/arm/boot/dts里的dts文件编译成dtb文件。

将s3c2440-smdk2440.dtb复制到tftp服务器路径下:

root@zhengyang:/work/sambashare/linux-5.2.8-dt# cp /work/sambashare/linux-5.2.8-dt/arch/arm/boot/dts/s3c2440-smdk2440.dtb /work/tftpboot/

4.2 编译驱动

执行make命令编译驱动,并将驱动程序拷贝到nfs文件系统:

root@zhengyang:/work/sambashare/drivers/24.led_dev_pinctr# cd /work/sambashare/drivers/24.led_dev_pinctr/
root@zhengyang:/work/sambashare/drivers/24.led_dev_pinctr# make
root@zhengyang:/work/sambashare/drivers/24.led_dev_pinctr# cp /work/sambashare/drivers/24.led_dev_pinctr/led_drv.ko /work/nfs_root/rootfs/

4.3 启动内核

uboot启动后,将dtb下载到内存地址0x30001000中:

SMDK2440 # tftp 0x30001000 s3c2440-smdk2440.dtb

然后将内核镜像加载到内存0x30008000地址:

nand read 0x30008000 kernel;

然后可以使用如下命令启动内核:

SMDK2440 # bootm 0x30008000 - 0x30001000   // 无设备树时,直接bootm 0x30008000
//bootm  uImage地址  ramdisk地址  设备树镜像地址

安装驱动:

[root@zy:/]# insmod led_drv.ko
samsung-pinctrl 56000000.pinctrl: found group selector 30 for gpb-5
samsung-pinctrl 56000000.pinctrl: found group selector 30 for gpb-5
samsung-pinctrl 56000000.pinctrl: found group selector 31 for gpb-6
samsung-pinctrl 56000000.pinctrl: found group selector 31 for gpb-6
samsung-pinctrl 56000000.pinctrl: found group selector 32 for gpb-7
samsung-pinctrl 56000000.pinctrl: found group selector 32 for gpb-7
samsung-pinctrl 56000000.pinctrl: found group selector 33 for gpb-8
samsung-pinctrl 56000000.pinctrl: found group selector 33 for gpb-8
samsung-pinctrl 56000000.pinctrl: found group selector 30 for gpb-5
samsung-pinctrl 56000000.pinctrl: found group selector 30 for gpb-5
samsung-pinctrl 56000000.pinctrl: found group selector 31 for gpb-6
samsung-pinctrl 56000000.pinctrl: found group selector 31 for gpb-6
samsung-pinctrl 56000000.pinctrl: found group selector 32 for gpb-7
samsung-pinctrl 56000000.pinctrl: found group selector 32 for gpb-7
samsung-pinctrl 56000000.pinctrl: found group selector 33 for gpb-8
samsung-pinctrl 56000000.pinctrl: found group selector 33 for gpb-8
myled myled: no init pinctrl state
samsung-pinctrl 56000000.pinctrl: request pin 30 (gpb-5) for myled
samsung-pinctrl 56000000.pinctrl: request pin 31 (gpb-6) for myled
samsung-pinctrl 56000000.pinctrl: request pin 32 (gpb-7) for myled
samsung-pinctrl 56000000.pinctrl: request pin 33 (gpb-8) for myled
myled myled: no sleep pinctrl state
myled myled: no idle pinctrl state
OF: no dma-ranges found for node(/myled)
myled myled: device is not dma coherent
myled myled: device is not behind an iommu
platform device name myled
pinctrl-names index 0 value default
retrieves the pinctrl handle for a device success
pinctrl lookup state success
pinctrl select state success
state name default               // 输出状态名称
setting type 2                   // ① pinctl_map类型为2  引脚复用
setting dev_name myled
setting mux group 30             // 对应引脚gpb-5
setting mux func 11
setting type 4                  //  ② pinctrl_map类型为4  配置电气特性
setting dev_name myled
--------------configs start--------
setting configs group_or_pin 30
setting configs configs 770
setting configs configs 1
--------------configs end--------
setting type 2                  // ①     
setting dev_name myled
setting mux group 31            // 对应引脚gpb-6 
setting mux func 11
setting type 4                  // ② 
setting dev_name myled
--------------configs start--------
setting configs group_or_pin 31
setting configs configs 770
setting configs configs 1
--------------configs end--------
setting type 2
setting dev_name myled
setting mux group 32
setting mux func 11
setting type 4
setting dev_name myled
--------------configs start--------
setting configs group_or_pin 32
setting configs configs 770
setting configs configs 1
--------------configs end--------
setting type 2
setting dev_name myled
setting mux group 33
setting mux func 11
setting type 4
setting dev_name myled
--------------configs start--------
setting configs group_or_pin 33
setting configs configs 770
setting configs configs 1
--------------configs end--------
alloc char device region success
char device add success
device class 'myled': registering
create class success
PM: Adding info for No Bus:myled
create device success
platform driver registered successfully

 

这里输出了大量的调试信息,主要是因为我开启了调试日志

查看设备节点文件:

[root@zy:/]# ls /dev/myled -l
crw-rw----    1 0        0         249,   0 Jan  1 00:56 /dev/myled

 

查看类:

[root@zy:/]# ls /sys/class/myled -l
total 0
lrwxrwxrwx    1 0        0                0 Jan  1 02:52 myled -> ../../devices/virtual/myled/myled
[root@zy:/]# ls /sys/class/myled/myled/ -l
total 0
-r--r--r--    1 0        0             4096 Jan  1 00:56 dev
drwxr-xr-x    2 0        0                0 Jan  1 02:52 power
lrwxrwxrwx    1 0        0                0 Jan  1 02:52 subsystem -> ../../../../class/myled
-rw-r--r--    1 0        0             4096 Jan  1 02:52 uevent

 

在linux的/sys/firmware/devicetree/base目录下可以查看到 myled节点,如下图所示:

[root@zy:/]#  ls /sys/firmware/devicetree/base/
#address-cells                 model
#size-cells                    myled
aliases                        name
chosen                         nand@4e000000
clock-controller@4c000000      pinctrl@56000000
clocks                         rtc@57000000
compatible                     serial@50000000
cpus                           serial@50004000
i2c@54000000                   serial@50008000
interrupt-controller@4a000000  srom-cs4@20000000
interrupt-parent               timer@51000000
memory@30000000                watchdog@53000000

 

进入myled节点的目录下可以查看到led节点的属性,如下图所示:

[root@zy:/]#  ls /sys/firmware/devicetree/base/myled/ -l
total 0
-r--r--r--    1 0        0                6 Jan  1 02:53 compatible
-r--r--r--    1 0        0                6 Jan  1 02:53 name
-r--r--r--    1 0        0                4 Jan  1 02:53 pinctrl-0
-r--r--r--    1 0        0                4 Jan  1 02:53 pinctrl-1
-r--r--r--    1 0        0               18 Jan  1 02:53 pinctrl-names
-r--r--r--    1 0        0                5 Jan  1 02:53 status

4.4 编译测试应用程序

执行make命令编译测试应用程序,并将测试应用程序拷贝到nfs文件系统:

root@zhengyang:/work/sambashare/drivers/24.led_dev_pinctr# cd test/
root@zhengyang:/work/sambashare/drivers/24.led_dev_pinctr/test# make clean
rm -rf *.o main
root@zhengyang:/work/sambashare/drivers/24.led_dev_pinctr/test# make
arm-linux-gcc -march=armv4t -o main main.c
root@zhengyang:/work/sambashare/drivers/24.led_dev_pinctr/test# cp ./main /work/nfs_root/rootfs

 

运行应用程序:

[root@zy:/]# ./main /dev/myled on
/dev/myled on!
value 1
pinctrl lookup state success
pinctrl select state success
[root@zy:/]# ./main /dev/myled off
/dev/myled off!
value 0
pinctrl lookup state success
samsung-pinctrl 56000000.pinctrl: request pin 30 (gpb-5) for myled
samsung-pinctrl 56000000.pinctrl: request pin 31 (gpb-6) for myled
samsung-pinctrl 56000000.pinctrl: request pin 32 (gpb-7) for myled
samsung-pinctrl 56000000.pinctrl: request pin 33 (gpb-8) for myled
pinctrl select state success

 

可以看到LED1~LED4同时点亮和同时熄灭。

4.5 卸载LED驱动

通过用lsmod可以查看当前安装了哪些驱动:

[root@zy:/]# lsmod
led_drv 2476 0 - Live 0xbf000000 (O)

卸载时直接运行:

[root@zy:/]# rmmod led_drv.ko
platform driver unregistered
led driver exit
PM: Removing info for No Bus:myled
device class 'myled': unregistering
class 'myled': release.
class_create_release called for myled

 

五、代码下载

Young / s3c2440_project[drivers]