----------------------------------------------------------------------------------------------------------------------------
内核版本:linux 5.2.8根文件系统:busybox 1.25.0u-boot:2016.05----------------------------------------------------------------------------------------------------------------------------
在前面我们已经介绍了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 (IS_ERR(myled.state)) {
dev_dbg(myled.device, "no default pinctrl state\n");
ret = PTR_ERR(myled.state);
return ret;
}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 ret;
}else{
printk("pinctrl select state success\n");
}
}
else{
/* 熄灭 */
myled.state = pinctrl_lookup_state(myled.pinctrl, "myled_off"); //获取pinctrl-1的配置状态
if (IS_ERR(myled.state)) {
dev_dbg(myled.device, "no myled_off pinctrl state\n");
ret = PTR_ERR(myled.state);
return ret;
}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 ret;
}else{
printk("pinctrl select state success\n");
}
}
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.write = led_write,
};
2.2 platform driver定义
这里我们采用platform设备驱动模型,因此需要定义platform_driver:
/*
* 用于设备树匹配
*/
static const struct of_device_id led_dt_match[] = {
{ .compatible = DTSLED_NAME, },
{},
};
/*
* platform驱动
*/
static struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = DTSLED_NAME,
.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;
printk("%s enter.\n", __func__);
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 (IS_ERR(myled.pinctrl)){
printk("retrieves the pinctrl handle for a device failed\n");
ret = PTR_ERR(myled.pinctrl);
goto faile_devid;
}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 (IS_ERR(myled.state)){
printk("pinctrl lookup state failed\n");
ret = PTR_ERR(myled.state);
goto faile_devid;
}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");
goto faile_devid;
}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");
ret = PTR_ERR(myled.class);
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");
ret = PTR_ERR(myled.device);
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;
}
实际上该函数的前三个步骤是完全多余的,因此在platform设备和驱动匹配的时候,会调用pinctrl_bind_pins函数,该函数通过调用pinctrl_lookup_state函数获取状态名为default和init的状态,并分别保存到default_state和 init_state成员变量中。如果找不到init状态,则选择default状态作为设备引脚的状态。
这里我主要的目的是为了输出设备default状态以及该状态下的配置信息。
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 (IS_ERR(myled.state)) {
dev_dbg(myled.device, "no default pinctrl state\n");
ret = PTR_ERR(myled.state);
return ret;
}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 ret;
}else{
printk("pinctrl select state success\n");
}
}
else{
/* 熄灭 */
myled.state = pinctrl_lookup_state(myled.pinctrl, "myled_off"); //获取pinctrl-1的配置状态
if (IS_ERR(myled.state)) {
dev_dbg(myled.device, "no myled_off pinctrl state\n");
ret = PTR_ERR(myled.state);
return ret;
}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 ret;
}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;
printk("%s enter.\n", __func__);
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 (IS_ERR(myled.pinctrl)){
printk("retrieves the pinctrl handle for a device failed\n");
ret = PTR_ERR(myled.pinctrl);
goto faile_devid;
}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 (IS_ERR(myled.state)){
printk("pinctrl lookup state failed\n");
ret = PTR_ERR(myled.state);
goto faile_devid;
}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");
goto faile_devid;
}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");
ret = PTR_ERR(myled.class);
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");
ret = PTR_ERR(myled.device);
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 = DTSLED_NAME, },
{},
};
/*
* platform驱动
*/
static struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = DTSLED_NAME,
.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");
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]
参考文章: