一字符驱动管理多个设备及不同设备数据隔离

1、每个设备一份数据,不同设备的数据通过minor号从全局数据读取
2、hrtimer通过指针达到数据隔离

#include <linux/delay.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/of_platform.h>
#include <linux/sys_config.h>

#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/cdev.h>

#include <linux/err.h>
#include <linux/hrtimer.h>

#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/time.h>

#include <linux/sched.h>
#include <linux/poll.h>

#include <linux/ioctl.h>

#define LOG_TAG	 "itech_led:"
#define DBUG_BLOCK	1
#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__)
#define debug_block(__block__) do {if(DBUG_BLOCK) {__block__}} while(0)

#define LED_MAGIC 'T'
#define GET_LED_CTRL_IO_LEVEL  	_IOR(LED_MAGIC, 0, int)
#define SET_LED_BRIGHTNESS  	_IOR(LED_MAGIC, 1, int)

#define LED_GPIO_LOW 			0
#define LED_GPIO_HIGH 			1
#define DEV_NAME_MAX_LEN		19

#define LED_DEV_NUM				2
#define CHECK_VALID_INDEX(__X__) do {if(__X__ < 0) return -1;} while(0)

typedef enum led_step_flag{
    led_inactivated_part,
    led_activated_part,
}led_step_flag;

typedef struct st_led_param_tag {
    unsigned int freq;
    unsigned int active_keep;
    unsigned int one_cycle_keep;
}st_led_param;

typedef struct led_brightness_tag {
	// led_timer 必须为结构体的第一个变量,
	// timer的执行函数的参数timer和初始化hrtimer时赋予的hrtimer变量地址相同
	// 此为每个设备的定时器使用各自数据的基础
	struct hrtimer led_timer;
	int num;
    unsigned long led_one_cycle;
    unsigned long led_active; /* activated duty cycle */
    led_step_flag led_step; /* sign activated and inactivated status */
	int led_gpio;
	int flag_timer; /* timer function flag. */
}led_brightness_param;

typedef struct itech_led_device {
	dev_t devno;
	struct device *dev;
	struct class *cls;
	struct cdev c_dev;
	char name[DEV_NAME_MAX_LEN + 1];
	int ctrl_gpio; // signal pin for control led
    int flag;
	led_brightness_param param;
	led_brightness_param latest_param;
} st_itech_led_dev;

static st_itech_led_dev itech_leds[LED_DEV_NUM];

extern int request_itech_misc_gpio(const char* pin_bank, const char* user);
extern int init_one_gpio(struct platform_device *pdev, char* name,
			struct gpio_config* const pconfig);
// timer执行的地址和初始化时hrtimer_init(&itech_leds[dev_index].param.led_timer, ...);
// 中的led_timer地址相同,基于同一变量的内存是连续的,因此在定时器的回调函数中
// 使用timer指针转换为设备数据变量类型,则获取到设备私有数据
static enum hrtimer_restart led_one_cycle(struct hrtimer *timer) {
	led_brightness_param* p_led_brightness_param = NULL;
	ktime_t led_kt;

	p_led_brightness_param = (led_brightness_param*)timer;

    led_step_flag current_setp = p_led_brightness_param->led_step;
    int led_gpio = p_led_brightness_param->led_gpio;

    if (p_led_brightness_param->flag_timer == 1) {
		print_dbg("------------------------------\n");
		print_dbg("p_led_brightness_param->num = %d\n", p_led_brightness_param->num);
		print_dbg("latest_param.led_active = %d\n", itech_leds[p_led_brightness_param->num].latest_param.led_active);
		print_dbg("p_led_brightness_param->led_gpio = %d\n", p_led_brightness_param->led_gpio);
		print_dbg("------------------------------\n");

        p_led_brightness_param->led_active =
			itech_leds[p_led_brightness_param->num].latest_param.led_active;
        p_led_brightness_param->flag_timer = 0;
    }
    current_setp = p_led_brightness_param->led_step;
    led_gpio = p_led_brightness_param->led_gpio;

    if (current_setp == led_inactivated_part) {
        gpio_set_value(led_gpio, LED_GPIO_LOW);
        p_led_brightness_param->led_step = led_activated_part;
        led_kt = ktime_set(0, p_led_brightness_param->led_one_cycle - p_led_brightness_param->led_active);
    } else if (current_setp == led_activated_part) {
        gpio_set_value(led_gpio, LED_GPIO_HIGH);
        p_led_brightness_param->led_step = led_inactivated_part;
        led_kt = ktime_set(0, p_led_brightness_param->led_active);
    } else {
        print_err("...invalid led status!!!\n");
        return HRTIMER_NORESTART;
    }

    hrtimer_forward(timer, timer->base->get_time(), led_kt);

    return HRTIMER_RESTART;
}

static void start_led(int dev_index,st_led_param *led_param) {
	ktime_t led_kt;

    unsigned long one_cycle_time = 1000*1000*1000L/led_param->freq; //unit is nano second.
    unsigned long led_activited_time = (unsigned long)(led_param->active_keep*one_cycle_time/led_param->one_cycle_keep);
    print_dbg("params ...[%d][%d][%d]!!!\n",led_param->active_keep, led_param->one_cycle_keep, led_param->freq);
	print_dbg("params ...[%lu]!!!\n", led_activited_time);

	print_dbg("dev_index = %d\n", dev_index);
	print_dbg("&itech_leds[%d].param.led_timer = %p\n", dev_index, &itech_leds[dev_index].param.led_timer);

    if (itech_leds[dev_index].flag == 0) {
        itech_leds[dev_index].flag = 1;
        itech_leds[dev_index].param.flag_timer = 0;

		itech_leds[dev_index].param.led_step = led_inactivated_part;
    	itech_leds[dev_index].param.led_active = led_activited_time;
		//(1*1000*1000*1000L)/LED_FREQ; /* unit: nano second */
    	itech_leds[dev_index].param.led_one_cycle = (1*1000*1000*1000L)/led_param->freq;
    	print_dbg("params ...[%lu][%lu]!!!\n", itech_leds[dev_index].param.led_active,
			itech_leds[dev_index].param.led_one_cycle);
		// 初始化定时器
        hrtimer_init(&itech_leds[dev_index].param.led_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
        itech_leds[dev_index].param.led_timer.function = led_one_cycle;
        led_kt = ktime_set(0, 10); // 10 nano seconds
        hrtimer_start(&itech_leds[dev_index].param.led_timer,  led_kt, HRTIMER_MODE_REL);
    } else {
        //latest_param.freq = led_param->freq;
        itech_leds[dev_index].latest_param.led_active = led_activited_time;
		print_dbg("latest_param.led_active = %d\n", led_activited_time);
        itech_leds[dev_index].param.flag_timer = 1;
        while(itech_leds[dev_index].param.flag_timer) {
            udelay(1000);
        }
    }
}

// 根据子设备号获取相应设备在全局变量数组的下标,获取设备数据
// 注意:注册字符设备时子设备号的设置
static int find_dev_index(struct file *fp)
{
	int index = -1;
	struct inode *inode = fp->f_path.dentry->d_inode;
	index = MINOR(inode->i_rdev);
	if(index < 0 || index >= LED_DEV_NUM ||
		itech_leds[index].param.num != index) {
		print_err("dev index is invalid.\n");
		return -1;
	}
	return index;
}

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

static ssize_t led_wirte(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	print_dbg("enter.\n");
    return 0;
}

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

static long led_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
    int ctrl_io_level;
    int ret;
    st_led_param* led_data; // For led brightness control.

	int dev_index = find_dev_index(fp);
	CHECK_VALID_INDEX(dev_index);

    switch(cmd) {
        case GET_LED_CTRL_IO_LEVEL:
            ctrl_io_level = gpio_get_value(itech_leds[dev_index].ctrl_gpio);
            ret = copy_to_user((void *)arg, &ctrl_io_level, sizeof(int));
            print_dbg("...pir_level[%d]...ret[%d]\n", ctrl_io_level, ret);
            break;
        case SET_LED_BRIGHTNESS:
            led_data = kmalloc(sizeof(st_led_param), GFP_KERNEL);
            if (!led_data) {
                print_err("kmalloc Error \033[0m\n");
                return -1;
            }

            if(!copy_from_user(led_data, (st_led_param*)arg, sizeof(st_led_param))) {
                start_led(dev_index, led_data);
            }
            kfree(led_data);
            break;
        default:
            print_err("...invalid cmd!!!\n");
            break;
    }

    return 0;
}

struct file_operations itech_led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .write = led_wirte,
    .release = led_release,
    .unlocked_ioctl = led_ioctl,
};

static int itech_led_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node;
	int val;
	const char* dev_name;
	struct gpio_config config;
	dev_t major;
	dev_t minor;

	st_itech_led_dev* p_itech_led = NULL;

	if (of_property_read_u32(np, "led_num", &val)) {
		print_err("get led_num fail.\n");
		return -1;
	}

	if (of_property_read_string(np, "dev_name", &dev_name)) {
		print_err("get resourcce dev_name fail.\n");
		return -1;
	}
	if(val >= LED_DEV_NUM || val < 0) {
		print_err("%s led_num(%d) is out of valid value(0-%d)\n",
			dev_name, val, LED_DEV_NUM-1);
		return -1;
	}
	p_itech_led = &itech_leds[val];
	p_itech_led->param.num = val;

	val = strlen(dev_name);
	val = val < DEV_NAME_MAX_LEN?val:DEV_NAME_MAX_LEN;
	strncpy(p_itech_led->name, dev_name, val);
	p_itech_led->name[val] = '\0';
	print_dbg("name = %s\n", p_itech_led->name);

	p_itech_led->ctrl_gpio =
		request_itech_misc_gpio("PI9", "ctrl_leds");
	if(p_itech_led->ctrl_gpio < 0) {
		print_err("ctrl_gpio PI9 request fail.\n");
		return -1;
	}
	print_dbg("itech_led.ctrl_gpio = %d\n", p_itech_led->ctrl_gpio);

	if(init_one_gpio(pdev, "analog_pwm_gpio", &config) < 0) {
		print_err("get resourcce analog_pwm_gpio fail.\n");
		return -1;
	}
	p_itech_led->param.led_gpio = config.gpio;
	print_dbg("led_pwm_gpio = %d\n", p_itech_led->param.led_gpio);

	if(alloc_chrdev_region(&(p_itech_led->devno), 0, 1, p_itech_led->name)) {
		print_err("alloc_chrdev_region %s fail.\n", p_itech_led->name);
        goto out;
	}

	// 初始化主设备号和子设备号,主设备号是系统分配的;
	// 子设备号是程序指定的且是连续的
	major = MAJOR(p_itech_led->devno);
	minor = p_itech_led->param.num;

	print_dbg("major = %d minor = %d\n", major, minor);
	p_itech_led->devno = MKDEV(major, minor);

    cdev_init(&p_itech_led->c_dev, &itech_led_fops);
    p_itech_led->c_dev.owner = THIS_MODULE;
	p_itech_led->c_dev.ops = &itech_led_fops;
    if(cdev_add(&p_itech_led->c_dev, p_itech_led->devno, 1)) {
		print_err("cdev_add %s fail.\n", p_itech_led->name);
        goto out_unreg_chrdev;
    }

    p_itech_led->cls = class_create(THIS_MODULE, p_itech_led->name);
    if (IS_ERR(p_itech_led->cls)) {
        print_err("class_create %s fail.\n", p_itech_led->name);
        goto out_cdev_del;
    }

    p_itech_led->dev = device_create(p_itech_led->cls, NULL, p_itech_led->devno,
            NULL, p_itech_led->name);
    if (IS_ERR(p_itech_led->dev)) {
        print_err("device_create %s fail.\n", p_itech_led->name);
        goto out_class_destroy;
    }

	return 0;

    out_class_destroy:
        class_destroy(p_itech_led->cls);
		p_itech_led->cls = NULL;
    out_cdev_del:
        cdev_del(&p_itech_led->c_dev);
    out_unreg_chrdev:
        unregister_chrdev_region(p_itech_led->devno, 1);
    out:
        print_err("Initialisation failed\n");
    return -1;
}

static int itech_led_remove(struct platform_device *pdev)
{
	int i = 0;
	for(i = 0;i < LED_DEV_NUM;i++) {
		if(itech_leds[i].cls != NULL) {
			if(itech_leds[i].dev != NULL) {
				cdev_del(&itech_leds[i].c_dev);
				device_destroy(itech_leds[i].cls, itech_leds[i].devno);
			}
			class_destroy(itech_leds[i].cls);
			unregister_chrdev_region(itech_leds[i].devno, 1);
		}
	}

	return 0;
}

static const struct of_device_id itech_led_ids[] = {
	{ .compatible = "compony, misc-led" },
	{ /* Sentinel */ }
};

static struct platform_driver itech_led_driver = {
	.probe	= itech_led_probe,
	.remove	= itech_led_remove,
	.driver	= {
		.owner	= THIS_MODULE,
		.name	= "misc-led",
		.of_match_table	= itech_led_ids,
	},
};

module_platform_driver(itech_led_driver);

MODULE_AUTHOR("xxxx");
MODULE_DESCRIPTION("led config driver");
MODULE_LICENSE("GPL");

全志a40i配置

;--------------------------------------------------------------------------------
; misc_led configuration
; dev_name: 		cdev name for ioctl control
; control_gpio: 	control led lamp state
; analog_pwm_gpio:	analog pwm to control lamp brightness
;--------------------------------------------------------------------------------
[misc_led0]
compatible      = "compony, misc-led"
led_num			= 0
dev_name 		= "led_white"
control_gpio	= "PI9"
analog_pwm_gpio = port:PI08<1><default><2><0>

[misc_led1]
compatible      = "icetech, misc-led"
led_num			= 1
dev_name 		= "led_ir"
control_gpio	= "PI9"
analog_pwm_gpio = port:PI07<1><default><2><0>
发布了93 篇原创文章 · 获赞 12 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/mcsbary/article/details/101433642