android7.1(a40i)驱动开发--使用sys文件系统与用户空间交互

思路


1、创建字符设备
2、创建于字符设备关联的sys文件系统
3、在用户空间,通关串口命令查看驱动状态

使用示例


在这里插入图片描述
代码


#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/miscdevice.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/of_platform.h>
#include <linux/string.h>
#include <linux/err.h>
#include <linux/pinctrl/pinconf-sunxi.h>
#include <linux/sys_config.h>
#include <linux/ctype.h>
#include <linux/crypto.h>
#include <linux/err.h>
#include <linux/scatterlist.h>
#include <linux/ioctl.h>
#include <linux/fs.h>

#define LOG_TAG	 "misc_gpio:"
#define print_dbg(fmt, ...) printk(KERN_DEBUG LOG_TAG "%s:%d->" fmt, \
					__func__, __LINE__, ##__VA_ARGS__)
#define print_err(fmt, ...) printk(KERN_ERR LOG_TAG "%s:%d->" fmt, \
					__func__, __LINE__, ##__VA_ARGS__)
#define print_war(fmt, ...) printk(KERN_WARNING LOG_TAG "%s:%d->" fmt, \
					__func__, __LINE__, ##__VA_ARGS__)

typedef struct {
	int gpio;
	int value;
}st_gpio_param;

#define GPIO_MAGIC 'G'
#define GET_GPIO_LEVEL  	_IOR(GPIO_MAGIC, 0, st_gpio_param*)
#define SET_GPIO_LEVEL  	_IOR(GPIO_MAGIC, 1, st_gpio_param*)

#define INPUT_GPIO					0
#define OUTPUT_GPIO 				1
#define PIN_BANK_NAME_MAX_LEN		7
#define GPIO_USER_NAME_MAX_LEN		15

struct misc_gpio_config {
	char pin_bank[PIN_BANK_NAME_MAX_LEN + 1];
	char user[GPIO_USER_NAME_MAX_LEN + 1];
	struct gpio_config config;
};

static int g_gpio_num = 0;
static struct misc_gpio_config* g_gpio_array = NULL;
struct gpio_config g_attr_gpio_config = {0, 0, 0, 0, 0};

static DEFINE_MUTEX(itech_gpio_mutex);

static int get_parameter(const char *buf, int *data, size_t size)
{
	char *after;
	size_t count;
	int tmp;

	tmp = simple_strtoul(buf, &after, 10);
	count = after - buf;

	if (isspace(*after))
		count++;

	if (count == size){
		*data = tmp;
		return size;
	}

	return -EINVAL;
}

static int find_attr_gpio(void)
{
	int i = 0;
	for(i = 0;i <= g_gpio_num;i++) {
		if(g_gpio_array[i].config.gpio == g_attr_gpio_config.gpio) {
			g_attr_gpio_config.mul_sel = g_gpio_array[i].config.mul_sel;
			return g_gpio_array[i].config.gpio;
		}
	}

	if(i > g_gpio_num) {
		return -1;
	}
	return -1;
}


static ssize_t show_all_gpio(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count)
{
	int i = 0;
	struct gpio_config* pconfig;
	printk("%-6s%-8s%-16s%-6s%-4s%-6s%-6s%-5s\n",
			"index", "pin", "user", "gpio", "sel", "pull", "level", "data");

	for(i = 0;i <= g_gpio_num;i++) {
		pconfig = &(g_gpio_array[i].config);

		printk("%-6d%-8s%-16s%-6d%-4d%-6d%-6d%-5d\n",
			i, g_gpio_array[i].pin_bank, g_gpio_array[i].user, pconfig->gpio,
			pconfig->mul_sel, pconfig->pull, pconfig->drv_level, gpio_get_value(pconfig->gpio));
	}

	return count;
}

static ssize_t set_gpio_num(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count)
{
	return get_parameter(buf, &g_attr_gpio_config.gpio, count);
}

static ssize_t set_gpio_value(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count)
{
	int gpio = -1;
	get_parameter(buf, &g_attr_gpio_config.data, count);
	if(g_attr_gpio_config.data != 0 && g_attr_gpio_config.data != 1) {
		printk(KERN_ERR "gpio num(%d),the set value(%d) is invalid.\n",
			g_attr_gpio_config.gpio, g_attr_gpio_config.data);
		return count;
	}

	gpio = find_attr_gpio();
	if(gpio < 0) {
		printk(KERN_ERR "gpio num(%d) is invalid.\n", g_attr_gpio_config.gpio);
		return count;
	}
	if(g_attr_gpio_config.mul_sel != OUTPUT_GPIO) {
		printk(KERN_ERR "gpio num(%d) mul_sel(%d) is not output.",
			g_attr_gpio_config.gpio, g_attr_gpio_config.mul_sel);
		return count;
	}
	gpio_set_value(g_attr_gpio_config.gpio, g_attr_gpio_config.data);
	return count;
}

static ssize_t show_gpio_value(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count)
{
	int gpio = -1;
	int value = -1;
	get_parameter(buf, &g_attr_gpio_config.gpio, count);

	gpio = find_attr_gpio();
	if(gpio < 0) {
		printk(KERN_ERR "gpio num(%d) is invalid.\n", g_attr_gpio_config.gpio);
		return count;
	}
	/*
	if(g_attr_gpio_config.mul_sel != OUTPUT_GPIO) {
		printk(KERN_ERR "gpio num(%d) mul_sel(%d) is not output.",
			g_attr_gpio_config.gpio, g_attr_gpio_config.mul_sel);
		return count;
	}
	*/
	value = gpio_get_value(g_attr_gpio_config.gpio);
	printk("gpio(%d) value is %d.", g_attr_gpio_config.gpio, value);
	return count;
}

static DEVICE_ATTR(show_all_gpio, 0666,		NULL, show_all_gpio);
static DEVICE_ATTR(set_gpio_num, 0666, NULL, set_gpio_num);
static DEVICE_ATTR(set_gpio_value, 0666, NULL, set_gpio_value);
static DEVICE_ATTR(show_gpio_value, 0666, NULL, show_gpio_value);

static int _open (struct inode *node, struct file *fp)
{
	print_dbg("enter\n");
	return 0;
}

static int _release (struct inode *node, struct file *fp)
{
	print_dbg("enter\n");
	return 0;
}

static bool checkGpioAccess(int gpio)
{
	int i = 0;
	for(i = 0; i <= g_gpio_num; i++)
	{
		if(g_gpio_array[i].config.gpio == gpio) {
			return true;
		}
	}

	return false;
}

static int set_gpio_level(int gpio, int value)
{
	print_dbg("enter\n");
	if(!checkGpioAccess(gpio)) {
		print_err("gpio(%d) is not configured.Please configure it firstly.\n", gpio);
		return -1;
	}
	print_dbg("gpio = %d  value = %d\n", gpio, value);
	gpio_set_value(gpio, value);
	return 0;
}

static int get_gpio_level(int gpio)
{
	print_dbg("enter\n");
	if(!checkGpioAccess(gpio)) {
		print_err("gpio(%d) is not configured.Please configure it firstly.\n", gpio);
		return -1;
	}
	print_dbg("gpio = %d\n", gpio);
	return gpio_get_value(gpio);
}

static long _ioctl (struct file *fp, unsigned int cmd, unsigned long args)
{
	long ret = 0;
	switch(cmd) {
		case SET_GPIO_LEVEL:
		{
			st_gpio_param stGpioParam = {0,0};
			if(copy_from_user(&stGpioParam, (st_gpio_param*)args, sizeof(st_gpio_param))) {
				print_err("SET_GPIO_LEVEL copy_from_user fail.\n");
				return -1;
			}
			ret = set_gpio_level(stGpioParam.gpio, stGpioParam.value);
		}
			break;
		case GET_GPIO_LEVEL:
		{
			st_gpio_param stGpioParam = {0,0};
			if(copy_from_user(&stGpioParam, (st_gpio_param*)args, sizeof(st_gpio_param))) {
				print_err("SET_GPIO_LEVEL copy_from_user fail.\n");
				return -1;
			}
			stGpioParam.value = get_gpio_level(stGpioParam.gpio);
			ret = copy_to_user((void *)args, &stGpioParam, sizeof(st_gpio_param));
		}
			break;
		default:
			print_err("command is error.");
			return -1;
	}
	return ret;
}


static struct attribute *misc_attributes[] = {
	&dev_attr_show_all_gpio.attr,
	&dev_attr_set_gpio_num.attr,
	&dev_attr_set_gpio_value.attr,
	&dev_attr_show_gpio_value.attr,
	NULL,
};

static struct attribute_group misc_attribute_group = {
	.name  = "itech_gpio",
	.attrs = misc_attributes,
};

struct file_operations itech_gpio_fops = {
    .owner = THIS_MODULE,
    .open = _open,
    .release = _release,
    .unlocked_ioctl = _ioctl,
};


static struct miscdevice itech_misc_dev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name  = "itech_gpio",
	.fops  = &itech_gpio_fops,
};

int request_itech_misc_gpio(const char* pin_bank, const char* user)
{
	int index = 0;
	int pin_bank_len = 0;
	int user_len = 0;
	if(pin_bank == NULL || user == NULL) {
		print_err("params are error.\n");
		return -1;
	}

	pin_bank_len = strlen(pin_bank);
	user_len = strlen(user);
	if(pin_bank_len == 0 ||  pin_bank_len > PIN_BANK_NAME_MAX_LEN
		|| user_len == 0 || user_len > GPIO_USER_NAME_MAX_LEN) {
		print_err("params's value is invalid, string len is too short or too long.\n");
		return -1;
	}

	mutex_lock(&itech_gpio_mutex);
	for(index = 0; index <= g_gpio_num; index++) {
		if(!strcmp(pin_bank, g_gpio_array[index].pin_bank)) break;
	}

	if(index > g_gpio_num) {
		print_err("%s is not a gpio.Please config it firstly.\n", pin_bank);
		goto fail;
	}

	if(g_gpio_array[index].user[0] != '\0'
		&& (strlen(g_gpio_array[index].user) != strlen(user)
		|| strcmp(g_gpio_array[index].user, user))) {
		print_err("Pin %s has been used, Please change another pin.\n", pin_bank);
		goto fail;
	}
	strcpy(g_gpio_array[index].user, user);
	mutex_unlock(&itech_gpio_mutex);

	return g_gpio_array[index].config.gpio;

	fail:
		mutex_unlock(&itech_gpio_mutex);
		return -1;
}
EXPORT_SYMBOL_GPL(request_itech_misc_gpio);


int init_one_gpio(struct platform_device *pdev, char* name,
			struct gpio_config* const pconfig) {
	struct device_node *gpio_node = pdev->dev.of_node;
	int ret = -1;
	int gpio = -1;

	if(name == NULL || pconfig == NULL) {
		print_err("%s\n", "param is error.");
		return -1;
	}

	mutex_lock(&itech_gpio_mutex);
	gpio = of_get_named_gpio_flags(gpio_node, name, 0, (enum of_gpio_flags *)pconfig);
    if(!gpio_is_valid(gpio)) {
		print_err("get gpio %s failed\n", name);
		goto fail;
    }

    ret = gpio_request(gpio, name);
	if (ret < 0) {
		print_err("can't request %s gpio %d\n", name, gpio);
		goto fail;
	}

    if(pconfig->mul_sel == 0){
        ret = gpio_direction_input(gpio);
		if (ret < 0) {
			print_err("can't request input direction %s gpio %d\n", name, gpio);
			goto fail;
		}
    }else if(pconfig->mul_sel == 1){
        ret = gpio_direction_output(gpio, pconfig->data);
        if (ret < 0) {
			print_err("can't request output direction %s gpio %d\n",
				name, gpio);
			goto fail;
		}
    } else {
        print_war("misc-GPIO : %s config.mul_sel:%d is not a gpio.\n", name, pconfig->mul_sel);
    }

	print_dbg("MISC-GPIO : %s gpio = %d\n", name, gpio);
	mutex_unlock(&itech_gpio_mutex);
	return gpio;

	fail:
		mutex_unlock(&itech_gpio_mutex);
		return -1;
}
EXPORT_SYMBOL_GPL(init_one_gpio);

static int misc_gpio_probe(struct platform_device *pdev) {
    int ret = -1;
	int gpio_nums = 0;
	int index = 0;
	const char *pins_string = NULL;
	const char* p = NULL;
	char temp[PIN_BANK_NAME_MAX_LEN + 1] = "\0";
	struct gpio_config config;
	struct device_node *np = pdev->dev.of_node;
	print_dbg("enter\n");

	if (of_property_read_u32(np, "gpio_nums", &gpio_nums)) {
		print_err("%s.\n","get resourcce gpio_nums fail");
		return -1;
	}
	g_gpio_num = gpio_nums;
	print_dbg("g_gpio_num = %d\n", g_gpio_num);

	g_gpio_array = devm_kzalloc(&pdev->dev, gpio_nums*sizeof(struct misc_gpio_config), GFP_KERNEL);
	if (!g_gpio_array) {
		print_err("no memory.");
		return -ENOMEM;
	}

	if (of_property_read_string(np, "pin_banks", &pins_string)) {
		print_err("get resourcce pin_banks fail.\n");
		return -1;
	}
	print_dbg("pin_banks = %s\n", pins_string);

	if(pins_string == NULL || strlen(pins_string) == 0) {
		print_err("value of pin_banks is error.");
		return -1;
	}

	p = pins_string;
	for(index = 0; index <= gpio_nums; index++) {
		if(sscanf(p, "%s", temp) <= 0) break;
		print_dbg("index = %d  name = %s", index, temp);
		p += (strlen(temp) + 1);

		init_one_gpio(pdev, temp, &config);
		memcpy(g_gpio_array[index].pin_bank, temp, PIN_BANK_NAME_MAX_LEN);
		g_gpio_array[index].pin_bank[PIN_BANK_NAME_MAX_LEN] = '\0';
		g_gpio_array[index].user[0] = '\0';
		memcpy(&g_gpio_array[index].config, &config, sizeof(struct gpio_config));

		if(*(p-1) == '\0') break;
		while(*p == ' ') p++;
	}

	// 1.注册字符设备
	ret = misc_register(&itech_misc_dev);
	if (ret) {
		print_err("register driver as misc device error!\n");
		return ret;
	}
	// 2.创建sys文件系统操作文件
	ret = sysfs_create_group(&itech_misc_dev.this_device->kobj,
			&misc_attribute_group);
	if (ret) {
		print_err("register sysfs create group failed!\n");
		return ret;
	}
    return 0;
}

static int misc_gpio_remove(struct platform_device *pdev)
{
	WARN_ON(0 != misc_deregister(&itech_misc_dev));
	sysfs_remove_group(&(itech_misc_dev.this_device->kobj),
			&misc_attribute_group);
	return 0;
}

static const struct of_device_id of_misc_gpio_match[] = {
    { .compatible = "icetech, misc-gpio", },
    {},
};

static struct platform_driver misc_gpio_driver = {
    .probe          = misc_gpio_probe,
    .remove         = misc_gpio_remove,
    .driver         = {
        .name   = "misc-gpio",
        .owner  = THIS_MODULE,
        .of_match_table = of_match_ptr(of_misc_gpio_match),
    },
};

static int __init module_misc_gpio_init(void)
{
	print_dbg("enter\n");
	return platform_driver_register(&misc_gpio_driver);
}

static void __exit module_misc_gpio_exit(void)
{
	platform_driver_unregister(&misc_gpio_driver);
}

// module_platform_driver(misc_gpio_driver);
fs_initcall(module_misc_gpio_init);
module_exit(module_misc_gpio_exit);

MODULE_AUTHOR("[email protected]");
MODULE_DESCRIPTION("icetech misc device gpio config driver");
MODULE_LICENSE("GPL");
发布了93 篇原创文章 · 获赞 12 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/mcsbary/article/details/102477769
今日推荐