Linux字符设备驱动-LED-pinctrl子系统和gpio子系统

概述

Linux驱动讲究驱动分离和分层,pinctrl和gpio子系统就是驱动分离和分层思想下的产物。驱动分离与分层是按照面向对象编程的设计思想设计的驱动框架。pinctrl和gpio子系统统一管理SOC引脚,避免了直接操作寄存器的驱动开发方式,提高了驱动的可移植性。

1.pinctrl子系统

大多数SOC的pin都支持引脚复用,需要配置复用功能,同时还要配置pin的电气特性等。传统配置pin的方式是直接操作寄存器,过程比较繁琐,而且容易出错。pinctrl子系统为了解决上述问题而引入,pinctrl子系统的主要工作内容如下:
(1)获取设备树中的pin信息
(2)根据获取的pin信息,设置pin的复用功能
(3)根据获取的pin信息,设置pin的电气特性
对于驱动的开发者,只需要在设备树中设置好某个pin的相关属性即可,剩余的初始化工作由pinctrl子系统完成。

1.1. imx6ull的pinctrl配置

要使用pinctrl子系统,需要在设备树里配置pin的信息,imx6ull.dtsi文件中有iomuxc的节点,此节点用于配置pin信息,imx6ull-14x14-evk.dts文件中引用了这个节点。向引用的节点添加子节点,以配置需要的pin信息。

//  imx6ull.dtsi文件中pinctrl设置节点
iomuxc: iomuxc@020e0000 {
	compatible = "fsl,imx6ul-iomuxc";
	reg = <0x020e0000 0x4000>;
};
// imx6ull-14x14-evk.dt文件中引用了iomuxc节点,可以向iomuxc节点添加子节点
&iomuxc {
	pinctrl-names = "default";
	imx6ul-evk {
		pinctrl_flexcan1: flexcan1grp{  // 添加的子节点
			fsl,pins = < // 子节点用于配置具体的pin
				//宏定义                           配置引脚的电气属性 
				MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX	0x1b020
				MX6UL_PAD_UART3_CTS_B__FLEXCAN1_TX	0x1b020
			>;
		};

		pinctrl_i2c1: i2c1grp {
			fsl,pins = <
				MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
				MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
			>;
		};
};

fsl,pins属性中有两列,第一列为宏定义,具体定义在imx6ul-pinfunc.h文件中。

	#define MX6UL_PAD_UART3_RTS_B__FLEXCAN1_RX 0x00B0 0x033C 0x0584 0x2 0x0
	// <
	// mux_reg		0x00B0        配置复用功能寄存器偏移地址
	// conf_reg 	0x033C		  配置电气属性寄存器偏移地址
	// input_reg 	0x0584		  输入选择寄存器的偏移地址,输出时为0
	// mux_mode 	0x2			  复用模式配置位的值
	// input_val	0x0			  输入选择寄存器的值,输出时为0
	// >
1.2. imx6ull的pinctrl驱动

pinctrl驱动程序根据设备树中设置的pinctrl信息初始化pin,驱动文件的路径为drivers/pinctrl/freescale/pinctrl-imx6ul.cimx6ul_pinctrl_of_match存放了此驱动程序的兼容性,用于和设备树中pinctrl的compatible属性匹配。匹配成功后执行imx6ul_pinctrl_probe函数。

	// 匹配字符串数组
	static struct of_device_id imx6ul_pinctrl_of_match[] = {
	{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },
	{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },
	{ /* sentinel */ }
	};
	
	imx6ul_pinctrl_probe()
	  ->imx_pinctrl_probe()
		// 向struct pinctrl_desc结构体中注册三个pinctrl操作函数集合
		imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
		imx_pinctrl_desc->pmxops = &imx_pmx_ops;
		imx_pinctrl_desc->confops = &imx_pinconf_ops;
		->imx_pinctrl_probe_dt()
		  ->imx_pinctrl_parse_functions()
			->imx_pinctrl_parse_groups() // 提取fsl,pins属性并进行解析
	  ->pinctrl_register()  // 向内核注册pinctrl子系统
1.3. 使用pinctrl编写rgbled设备树节点

在imx6ull-14x14-evk.dts文件中引用的iomuxc节点下添加rgbled的pinctrl信息:

pinctrl_rgb_led:rgb_led {
	fsl,pins = <
		MX6UL_PAD_GPIO1_IO04__GPIO1_IO04  0x000010B1  /* read led */
		MX6UL_PAD_CSI_HSYNC__GPIO4_IO20   0x000010B1  /* green led */
		MX6UL_PAD_CSI_VSYNC__GPIO4_IO19   0x000010B1  /* blue led */
	>;
};

在imx6ull-14x14-evk-emmc.dts文件中添加rgbled设备节点:

/{
	/* 在根节点添加rgb led节点 */
	rgb_led {
		#address-cells = <1>;
		#size-cells = <1>;
		pinctrl-names = "default";
		compatible = "my,rgb-led";
		status = "okay";
		pinctrl-0 = <&pinctrl_rgb_led>;
		red = <&gpio1 4 GPIO_ACTIVE_LOW>;
		green = <&gpio4 20 GPIO_ACTIVE_LOW>;
		blue = <&gpio4 19 GPIO_ACTIVE_LOW>;
	};
};

2.gpio子系统

gpio子系统用于初始化gpio,并提供操作gpio的API函数,比如设置gpio为输入或者输出,读取gpio数据寄存器的值等。gpio子系统主要目的是给驱动开发者提供便利,增加驱动的可移植性。开发者只要在设备树中添加gpio相关信息,然后在驱动程序中直接使用gpio子系统提供的API操作gpio即可。Linux内核屏蔽了gpio的操作过程,使驱动开发者专注于功能的实现,而不用关心gpio繁琐的操作过程。
gpio子系统提供的API操作函数都需要注册在struct gpio_chip结构体中。

	include <linux/gpio/driver.h>
	struct gpio_chip {
		const char		*label;
		struct device		*dev;
		struct module		*owner;
		struct list_head        list;
		int	(*request)(struct gpio_chip *chip, unsigned offset);
		void (*free)(struct gpio_chip *chip, unsigned offset);
		int	(*get_direction)(struct gpio_chip *chip,unsigned offset);
		int	(*direction_input)(struct gpio_chip *chip,unsigned offset);
		int	(*direction_output)(struct gpio_chip *chip,unsigned offset, int value);
		int	(*get)(struct gpio_chip *chip, unsigned offset);
		void (*set)(struct gpio_chip *chip, unsigned offset, int value);
	};

gpio子系统提供的通过gpio操作函数位于/drivers/gpio/gpiolib-legacy.c中。参数gpio为要申请的gpio标号,使用of_get_named_gpio函数从设备树获取指定gpio属性信息,此函数会返回这个gpio标号。label为给gpio设置的名字。

	include <linux/gpio.h>
	// 申请一个GPIO引脚
	int gpio_request(unsigned gpio, const char *label)
	// 释放一个GPIO引脚
	void gpio_free(unsigned gpio)
	// 设置某个GPIO引脚为输入
	int gpio_direction_input(unsigned gpio)
	// 设置某个GPIO引脚为输出,并输出值设置为value
	int gpio_direction_output(unsigned gpio, int value)
	// 用于获取某个GPIO的值
	int gpio_get_value(unsigned int gpio)
	// 设置某个GPIO的值
	void gpio_set_value(unsigned int gpio, int value)

of_get_named_gpio函数获取GPIO编号,Linux内核中关于GPIO的API函数都要使用GPIO编号,此函数会将设备树中类似<&gpio5 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的GPIO编号,此函数在驱动中使用很频繁。np为设备节点,propname为要获取GPIO信息的属性名,indexGPIO索引,因为一个属性里面可能包含多个GPIO,此参数指定要获取哪个GPIO的编号,如果只有一个GPIO信息的话此参数为0。返回正值成功,返回负值失败。

	int of_get_named_gpio(struct device_node *np, const char *propname, int index)

2.字符设备驱动源码

	/*===========================my_led_pinctrl_gpio_subsystem.h================================*/
	#include <linux/cdev.h>
	#include <linux/device.h>
	#include <linux/mutex.h>
	#include <linux/atomic.h>
	#include <asm/io.h>
	#include <linux/of.h> 		  // 设备树 
	
	// GPIO1_4 红灯
	// GPIO4_20 绿灯
	// GPIO4_19 蓝灯
	
	#define LED_ON	0
	#define LED_OFF 1
	
	#define READ_PIN_NAME "read_pin"
	#define GREEN_PIN_NAME "green_pin"
	#define BLUE_PIN_NAME "blue_pin"
	
	// 设备结构体
	struct my_led_dev {
		struct cdev cdev;  // 字符设备结构体
		struct device_node* node; // rgb_led设备树节点指针
		int r_gpio;  // 红色led引脚的GPIO编号
		int g_gpio;  // 绿色led引脚的GPIO编号
		int b_gpio;  // 蓝色led引脚的GPIO编号
		char r_name[12];  // 红色led引脚的名字
		char g_name[12];  // 绿色led引脚的名字
		char b_name[12];  // 蓝色led引脚的名字
		int r_status;     // 红色led状态,0亮,1灭
		int g_status;	  // 绿色led状态,0亮,1灭
		int b_status;	  // 蓝色led状态,0亮,1灭
		dev_t devno;  // 设备号
		struct class* my_led_class;
		struct device* my_led_device;
	    struct mutex mutex;  // 用于同步的互斥体
		unsigned int r_on_cnt; // 红色led亮的次数
		unsigned int g_on_cnt; // 红色led亮的次数
		unsigned int b_on_cnt; // 红色led亮的次数
	};
	/*===========================my_led_pinctrl_gpio_subsystem.c================================*/
	#include <linux/init.h>
	#include <linux/module.h>
	#include <linux/kernel.h>
	#include <linux/fs.h>
	#include <asm/uaccess.h>
	#include <linux/slab.h>
	#include <linux/stat.h>
	#include <linux/sysfs.h>
	#include <linux/string.h>
	#include <linux/gpio.h>
	#include <linux/of_gpio.h>
	#include "my_led_pinctrl_gpio_subsystem.h"
	static struct my_led_dev* my_led = NULL;
	
	static int my_led_open(struct inode* inode, struct file* filp)
	{
		struct my_led_dev* dev = container_of(inode->i_cdev, 
									struct my_led_dev, cdev); 
		filp->private_data = dev;
		return 0;
	}
	// 设置led对应的gpio值
	static void set_led_gpio(int gpio, int val, int* status,int* cnt)
	{
		// 非0-灯亮,0-灯灭,GPIO引脚输出低电平灯亮,输出高点平灯灭
		if (val == 0) {
			gpio_set_value(gpio, LED_OFF);
			*status = LED_OFF;		
		}
		else {
			gpio_set_value(gpio, LED_ON);
			if (*status != LED_ON) {
				*cnt = *cnt + 1;
				*status = LED_ON;
			} 
		}	
	}
	
	static ssize_t my_led_write(struct file* filp, const char __user* buf, 
					size_t size, loff_t* ppos)
	{
		struct my_led_dev* dev = filp->private_data;
		int ret, val[3];
		if (size != sizeof(val)) return -EINVAL; 
		// 成功返回0,失败返回失败的数目
		ret = copy_from_user(&val, buf, sizeof(val)); 
		if (0 != ret) return -EFAULT;
	
		set_led_gpio(dev->r_gpio, val[0], &dev->r_status, &dev->r_on_cnt);
		set_led_gpio(dev->g_gpio, val[1], &dev->g_status, &dev->g_on_cnt);
		set_led_gpio(dev->b_gpio, val[2], &dev->b_status, &dev->b_on_cnt);
	
		return sizeof(val);
	}
	// 文件操作函数结构体
	static const struct file_operations my_led_fops = {
		.owner = THIS_MODULE,
		.write = my_led_write,
		.open = my_led_open,
	};
	
	// 初始化和注册cdev结构体
	static int set_up_my_led_cdev(struct my_led_dev* dev, int cnt)
	{
		int err;
		cdev_init(&dev->cdev, &my_led_fops);
		dev->cdev.owner = THIS_MODULE;
		err = cdev_add(&dev->cdev, dev->devno, cnt); // 出错返回负值
		if (err < 0)
			printk(KERN_ERR "adding my_led cdev %d error, errno %d\n", cnt, err);
		return err;
	}
	// 定义设备的属性
	static ssize_t red_show(struct device* dev, struct device_attribute* attr,char* buf)
	{
		return sprintf(buf, "red led lighting times %u\n", my_led->r_on_cnt);
	} // echo 非0,灯亮,echo 0,灯灭
	static ssize_t red_store(struct device *dev, struct device_attribute *attr,  
	                				const char *buf, size_t count)
	{	int err;
		unsigned int val;
		// 将echo输入得字符串转换为无符号整数,10表示十进制
		err = kstrtouint(buf, 10, &val); 
		if (err)
			return err;
		set_led_gpio(my_led->r_gpio, val, &my_led->r_status, &my_led->r_on_cnt);
		return count;
	} 
	static ssize_t green_show(struct device *dev, struct device_attribute *attr,char *buf)
	{
		return sprintf(buf, "green led lighting times %u\n", my_led->g_on_cnt);
	}
	static ssize_t green_store(struct device *dev, struct device_attribute *attr,  
	                				const char *buf, size_t count)
	{	int err;
		unsigned int val;
		err = kstrtouint(buf, 10, &val);
		if (err)
			return err;
		set_led_gpio(my_led->g_gpio, val, &my_led->g_status, &my_led->g_on_cnt);
		return count;
	} 
	static ssize_t blue_show(struct device *dev, struct device_attribute *attr,char *buf)
	{
		return sprintf(buf, "blue led lighting times %u\n", my_led->b_on_cnt);
	}
	static ssize_t blue_store(struct device *dev, struct device_attribute *attr,  
	                				const char *buf, size_t count)
	{	int err;
		unsigned int val; 
		err = kstrtouint(buf, 10, &val);
		if (err)
			return err;
		set_led_gpio(my_led->b_gpio, val, &my_led->b_status, &my_led->b_on_cnt);
		return count;
	} 
	// 红色led设备属性,生成的属性结构体名称为dev_attr_red,类型为struct device_attribute,
	// mode为0644,所属者可以读写,其他只能读
	static DEVICE_ATTR(red, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, red_show, red_store);
	// 绿色led设备属性,生成的属性结构体名称为dev_attr_green,类型为struct device_attribute,
	// mode为0644,所属者可以读写,其他只能读
	static DEVICE_ATTR(green, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, green_show, green_store);
	// 蓝色led设备属性,生成的属性结构体名称为dev_attr_blue,类型为struct device_attribute,
	// mode为0644,所属者可以读写,其他只能读
	static DEVICE_ATTR(blue, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, blue_show, blue_store);
	static struct attribute* led_colour_attrs[] = {
		&dev_attr_red.attr,
		&dev_attr_green.attr,
		&dev_attr_blue.attr,
		NULL,
	};
	/* 创建led颜色属性组,生成的属性结构体名称为led_colour_group,类型为static const 
	   struct attribute_group,使用led_colour_attrs初始化
	   ATTRIBUTE_GROUPS(led_colour); */
	// 使用ATTRIBUTE_GROUPS宏创建属性组时编译会出现警告,这里直接定义led颜色属性组
	static struct attribute_group led_colour_group = {
	    .attrs = led_colour_attrs,
	};
	
	// 从设备树中提取信息,初始化gpio
	static int led_gpio_init(struct my_led_dev* dev)
	{
		int ret = 0, ret1 = 0, ret2 = 0, ret3 = 0;
		struct device_node* n;
		const char* status;
		// 设置led引脚的名字
		memcpy(dev->r_name, READ_PIN_NAME, strlen(READ_PIN_NAME) + 1);
		memcpy(dev->g_name, GREEN_PIN_NAME, strlen(GREEN_PIN_NAME) + 1);
		memcpy(dev->b_name, BLUE_PIN_NAME, strlen(BLUE_PIN_NAME) + 1);
	
		// 从设备树中获取设备节点
		n = of_find_node_by_path("/rgb_led");
		if (NULL == n) {
			printk(KERN_ERR "find led node error by /rgb_led\n");
			return -EINVAL;
		}
		dev->node = n;
		// 根据设备树中的gpio属性,获取led对应的gpio编号
		dev->r_gpio = of_get_named_gpio(dev->node, "red", 0);
		if (dev->r_gpio < 0) {
			printk(KERN_ERR "can't get %s gpio number\n", dev->r_name);
			return -EINVAL;
		}
		printk(KERN_INFO "%s gpio number %#x\n", dev->r_name, (unsigned int)dev->r_gpio);
		dev->g_gpio = of_get_named_gpio(dev->node, "green", 0);
		if (dev->g_gpio < 0) {
			printk(KERN_ERR "can't get %s gpio number\n", dev->g_name);
			return -EINVAL;
		}
		printk(KERN_INFO "%s gpio number %#x\n", dev->g_name, (unsigned int)dev->g_gpio);
		dev->b_gpio = of_get_named_gpio(dev->node, "blue", 0);
		if (dev->b_gpio < 0) {
			printk(KERN_ERR "can't get %s gpio number\n", dev->b_name);
			return -EINVAL;
		}
		printk(KERN_INFO "%s gpio number %#x\n", dev->b_name, (unsigned int)dev->b_gpio);
	
		// 申请gpio
		ret = gpio_request(dev->r_gpio, dev->r_name);
		if (ret < 0) {
			printk(KERN_ERR "request %s gpio error\n", dev->r_name);
			return -EINVAL;		
		}
		ret = gpio_request(dev->g_gpio, dev->g_name);
		if (ret < 0) {
			printk(KERN_ERR "request %s gpio error\n", dev->g_name);
			ret = -EINVAL;
			goto free_read_gpio;		
		}
		ret = gpio_request(dev->b_gpio, dev->b_name);
		if (ret < 0) {
			printk(KERN_ERR "request %s gpio error\n", dev->b_name);
			ret = -EINVAL;
			goto free_green_gpio;	
		}
	
		// 从设备树中获取status属性
		ret = of_property_read_string(dev->node, "status", &status);
		if (ret < 0) {
			printk(KERN_ERR "find rgb_led node status property error\n");
			ret = -EINVAL;
			goto free_blue_gpio;
		}
		
		// 设置GPIO为输入,并根据status属性设置输出值
		ret = strcmp(status, "okay");
		if (ret == 0) {  
			ret1 = gpio_direction_output(dev->r_gpio, LED_ON);
			dev->r_status = LED_ON;
			ret2 = gpio_direction_output(dev->g_gpio, LED_ON);
			dev->g_status = LED_ON;
			ret3 = gpio_direction_output(dev->b_gpio, LED_ON);
			dev->b_status = LED_ON;
		} else {
			ret1 = gpio_direction_output(dev->r_gpio, LED_OFF);
			dev->r_status = LED_OFF;
			ret2 = gpio_direction_output(dev->g_gpio, LED_OFF);
			dev->g_status = LED_OFF;
			ret3 = gpio_direction_output(dev->b_gpio, LED_OFF);
			dev->b_status = LED_OFF;
		}
		if (ret1 < 0 || ret2 < 0 || ret3 < 0) {
			printk(KERN_ERR "gpio set direction output error\n");
			ret = -EINVAL;
			goto free_blue_gpio;
		}
		return 0;
	free_blue_gpio:
		gpio_free(dev->b_gpio);
	free_green_gpio:
		gpio_free(dev->g_gpio);
	free_read_gpio:
		gpio_free(dev->r_gpio);
		return ret;
	}
	// 模块初始化
	static int __init my_led_init(void)
	{
		int ret = 0;
		dev_t devno = 0;
		// 动态分配设备号,传入的devno参数为0,使用unregister_chrdev_region注销动态分配的设备号
		ret = alloc_chrdev_region(&devno, 0, 1, "my_led");
		if (ret < 0) {
			printk(KERN_ERR "alloc_chrdev_region() failed %d\n", ret);
			return ret;	
		}
		// 分配设备结构体内存并将分配的内存清0
		my_led = kzalloc(sizeof(struct my_led_dev), GFP_KERNEL);
		if (NULL == my_led) {
			ret = -ENOMEM;
			printk(KERN_ERR "kzalloc() failed %d\n", ret);
			goto unreg_chrdev;
		}
		my_led->devno = devno;
	
		// 从设备树获取资源
		ret = led_gpio_init(my_led);
		if (0 != ret) goto free_dev;
	
		ret = set_up_my_led_cdev(my_led, 1);
		if (ret < 0) goto free_dev;
		// 创建类和设备,当模块加载后会自动在/dev目录下生成设备节点
		my_led->my_led_class = class_create(THIS_MODULE, "my_led_class");
		if (IS_ERR(my_led->my_led_class)) {
			ret = PTR_ERR(my_led->my_led_class);
			printk(KERN_ERR "class_create() failed %d\n", ret);
			goto del_cdev;
		}
		my_led->my_led_device = device_create(my_led->my_led_class, NULL, 
									devno, NULL, "my_led");
		if (IS_ERR(my_led->my_led_device)) {
			ret = PTR_ERR(my_led->my_led_device);
			printk(KERN_ERR "device_create() failed %d\n", ret);
			goto clean_class;
		}
		// 使用sysfs_create_group可以创建一组属性文件,sysfs_remove_group移除一组属性文件
		// 使用sysfs_create_groups可以创建多组属性文件,ysfs_remove_groups移除多组属性文件
		ret = sysfs_create_group(&my_led->my_led_device->kobj, &led_colour_group);
		if(ret != 0) goto clean_device;
	
		// 初始化互斥体
		mutex_init(&my_led->mutex);
		printk(KERN_INFO "my_led module init OK, major %u, minor %u\n", 
				MAJOR(devno), MINOR(devno));
		return 0;
	clean_device:
		device_destroy(my_led->my_led_class, devno);
	clean_class: 
		class_destroy(my_led->my_led_class);
	del_cdev:
		cdev_del(&my_led->cdev);
	free_dev:
		kfree(my_led);
		my_led = NULL;
	unreg_chrdev:
		unregister_chrdev_region(devno, 1);
	    return ret;
	}
	
	// 模块注销
	static void __exit my_led_exit(void)
	{
		sysfs_remove_group(&my_led->my_led_device->kobj, &led_colour_group);
		device_destroy(my_led->my_led_class, my_led->devno);
		class_destroy(my_led->my_led_class);
		cdev_del(&my_led->cdev);
		gpio_free(my_led->b_gpio);  // 释放gpio
		gpio_free(my_led->g_gpio);
		gpio_free(my_led->r_gpio);
		unregister_chrdev_region(my_led->devno, 1);
		kfree(my_led);
		my_led = NULL;
		printk(KERN_INFO "my_led module exit\n");
	}
	
	module_init(my_led_init);
	module_exit(my_led_exit);
	
	MODULE_LICENSE("GPL");
	MODULE_AUTHOR("liyang.plus@foxmail.com");
	MODULE_VERSION("v1.00");

3.测试程序源码

	#include <sys/types.h>
	#include <sys/stat.h>
	#include <string.h>
	#include <fcntl.h>
	#define PATH "/dev/my_led"
	#define OPT_STRING    ":r:g:b:"   // 选项字符串
	void print_usage()
	{
	    printf("Usage: ./test -r <on/off> -g <on/off> -b <on/off>\n"
	           "-r: Control red led\n"
	           "-g: Control green led\n"
	           "-b: Control blue led\n" 
	           "on: Light on(default)\n"
	           "off: Light off\n");
	}
	
	extern char* optarg;  // 指向参数值
	extern int optind;    // 记录getopt函数处理argv[]数组的位置,一般不需要设置
	extern int opterr;    // 保存出错信息,非0 getopt会向stderr打印出错信息,如为0时则不打印
	/* 遇到无法识别的选项,getopt返回?号,并将?存储到optopt中,如果将选项字符串第一个字符设置
	   为:号,那么getopt函数在用户未提供值的情况下返回:号而不是?号 */
	extern int optopt; 
	int parse_option(int argc, char* argv[], int* rgb)
	{
	    int opt;
	    while (-1 != (opt = getopt(argc, argv, OPT_STRING))) {
	        switch (opt) {
	        case 'r':
	            if(0 == strcmp(optarg, "on")) rgb[0] = 1;
	            else if (0 == strcmp(optarg, "off")) rgb[0] = 0; 
	            else return -1; 
	            break;
	        case 'g':
	            if(0 == strcmp(optarg, "on")) rgb[1] = 1;
	            else if (0 == strcmp(optarg, "off")) rgb[1] = 0; 
	            else return -1; 
	            break;
	        case 'b':
	            if(0 == strcmp(optarg, "on")) rgb[2] = 1;
	            else if (0 == strcmp(optarg, "off")) rgb[2] = 0; 
	            else return -1; 
	            break;
	        case ':':
	            print_usage();
	            return -1;
	            break;
	        case '?':
	            print_usage();
	            return -1;
	            break;
	        default:
	            print_usage();
	            return -1;
	            break;
	        }
	    }
	    return 0;
	}
	
	int main(int argc, char* argv[])
	{
	    int fd, ret, val[3] = {0};
	    if (7 != argc) {
	        print_usage();
	        return -1;
	    }
	    ret = parse_option(argc, argv, val);
	    if (0 != ret) {
	        print_usage();
	        printf("parse option error\n");
	        return -1;
	    }
	
	    fd = open(PATH, O_RDWR);
	    if (fd < 0){
	        printf("my_led open error\n");
	        return -1;
	    }
	    ret = write(fd, &val, sizeof(val));
	    if (ret < 0) {
	        printf("write error\n");
	        close(fd);
	        return -1;
	    }
	    close(fd);
	    return 0;
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值