kernel中hrtimer与kthread_run

这里不做两种异步工作的实现说明,主要针对工作中实际使用总结:

方式实际使用
hrtimer当定时器时间到,以timeout唤醒,执行回调函数,如果模拟一个波形,高频率的产生中断,系统稳定性不够好;占用系统资源较高,但定时准确性高,优先级高;适合处理短时间的事务
kthread在线程函数中以delay形式循环执行,delay的形式,可以是释放cpu的,也可以是不释放cpu的。当delay时间比较久,使用释放cpu的较好;占用资源较低,优先级可设置;适用于长时间运行

实际使用情况

占用资源方面

同样是wiegand输入和输出,使用线程方式异步处理,接受数据的准确率更高。如果使用线程长时间运行,delay使用不释放cpu的函数,cpu占用率也会很高。

系统稳定性方面

在使用gpio口,按照一定波形拉高拉低电平,模拟pwm中的驱动中,注册一个io的上升沿和下降沿的中断,运行一段时间会产生中断错误,导致kernel挂掉;使用kthread方式不会存在系统挂掉的问题。

总结

对时序要求很高的,使用hrtimer比较好。但是,如果cpu资源并不充足,频繁的hrtimer超时唤醒使用中断,系统稳定性较差。

Demo

#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/kthread.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_info(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)

#define USE_THREAD 1

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

typedef enum {
	APPLY_OVER,
	RUNNING,
	CLOSE_LED,
} enum_change_type;

typedef struct {
	#if USE_THREAD
	struct task_struct* g_pwm_thread;
	#else
	struct hrtimer led_timer;
	ktime_t led_kt;
	#endif
	int num;
	int led_gpio;
    volatile unsigned long led_active; /* activated duty cycle */
	volatile unsigned long led_inactive; /* for changing brightness */
    volatile bool is_pwm_high; /* sign activated and inactivated status */
	volatile enum_change_type change_type; /* timer function flag. */
}led_brightness_param;

typedef struct {
	unsigned int dev_num;
	unsigned int status;
} led_sensor_status;

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 crtl_irq;
	bool occur_irq;
	int irq_result;
	led_brightness_param param;
} st_itech_led_dev;

static DECLARE_WAIT_QUEUE_HEAD(led_irq_in_waitq);
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);

static void reset_pwm(int gpio)
{
	gpio_direction_output(gpio, LED_GPIO_LOW);
}

#if USE_THREAD
static int pwm_thread_func(void *args)
{
	led_brightness_param* pLedParam = (led_brightness_param*)args;
	while(true) {
		if(pLedParam->change_type == CLOSE_LED) {
			reset_pwm(pLedParam->led_gpio);
			print_info("close led(%d)", pLedParam->num);
			pLedParam->change_type = APPLY_OVER;
			return 0;
		}

		if(pLedParam->is_pwm_high) {
			gpio_direction_output(pLedParam->led_gpio, LED_GPIO_HIGH);
			pLedParam->is_pwm_high = false;
			ndelay(pLedParam->led_active);
		} else {
			gpio_direction_output(pLedParam->led_gpio, LED_GPIO_LOW);
			pLedParam->is_pwm_high = true;
			ndelay(pLedParam->led_inactive);
		}
	}
	return 0;
}
#else
static enum hrtimer_restart led_one_cycle(struct hrtimer *timer) {
	led_brightness_param* pLedParam = (led_brightness_param*)timer;

	if(pLedParam->change_type == CLOSE_LED) {
		reset_pwm(pLedParam->led_gpio);
		print_info("close led(%d)", pLedParam->num);
		pLedParam->change_type = APPLY_OVER;
		return HRTIMER_NORESTART;
	}

	if(pLedParam->is_pwm_high) {
		gpio_direction_output(pLedParam->led_gpio, LED_GPIO_HIGH);
    	pLedParam->is_pwm_high = false;
    	pLedParam->led_kt = ktime_set(0, pLedParam->led_active);
	} else {
		gpio_direction_output(pLedParam->led_gpio, LED_GPIO_LOW);
    	pLedParam->is_pwm_high = true;
    	pLedParam->led_kt = ktime_set(0, pLedParam->led_inactive);
	}

	hrtimer_forward_now(timer, pLedParam->led_kt);

    return HRTIMER_RESTART;
}
#endif

static void updata_led_brightness(int dev_index,st_led_param *led_param) {
    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);
	unsigned long led_inactivited_time = one_cycle_time - led_activited_time;
	enum_change_type pwmStatus = itech_leds[dev_index].param.change_type;

	print_dbg("dev_index = %d\n", dev_index);
	print_dbg("led_activited_time = %lu", led_activited_time);

	if(led_activited_time != 0) {
		if(pwmStatus != CLOSE_LED) {
			while(itech_leds[dev_index].param.is_pwm_high) {
				ndelay(one_cycle_time/led_param->one_cycle_keep);
			}
		}
		itech_leds[dev_index].param.led_active = led_activited_time;
		itech_leds[dev_index].param.led_inactive = led_inactivited_time;

		if(pwmStatus == CLOSE_LED) {
			itech_leds[dev_index].param.change_type = RUNNING;
			itech_leds[dev_index].param.is_pwm_high = false;

			#if USE_THREAD
			itech_leds[dev_index].param.g_pwm_thread =
						kthread_run(pwm_thread_func,&itech_leds[dev_index].param,"pwm_func");
			if(IS_ERR(itech_leds[dev_index].param.g_pwm_thread)) {
				print_err("pwm_func thread create failed!");
			}
			#else
			itech_leds[dev_index].param.led_kt = ktime_set(0, 10); // 10 nano seconds
			hrtimer_start(&itech_leds[dev_index].param.led_timer,
				itech_leds[dev_index].param.led_kt, HRTIMER_MODE_REL);
			#endif
		}
	} else {
		itech_leds[dev_index].param.change_type = CLOSE_LED;
		while(pwmStatus != CLOSE_LED
				&& itech_leds[dev_index].param.change_type) {
	        udelay(1000);
	    }
		pwmStatus = CLOSE_LED;
		itech_leds[dev_index].param.change_type = pwmStatus;
	}
}

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;
}

irqreturn_t irq_thread_handler(int irq, void *dev_id)
{
	st_itech_led_dev* dev = (st_itech_led_dev*)dev_id;
	print_dbg("irq = %d, dev->crtl_irq: %d", irq, dev->crtl_irq);
	if(irq != dev->crtl_irq) {
		return IRQ_NONE;
	}
	dev->occur_irq = true;
	udelay(500);
	dev->irq_result = gpio_get_value(dev->ctrl_gpio);

	print_dbg("gpio_level: %d", gpio_get_value(dev->ctrl_gpio));
	print_dbg("irq_result: %d", dev->irq_result);

	wake_up_interruptible(&led_irq_in_waitq);
	return IRQ_HANDLED;
}

irqreturn_t sensor_irq_handler(int irq, void *dev_id)
{
	return IRQ_WAKE_THREAD;
}

static void init_light_sensor_irq(st_itech_led_dev* dev)
{
	int ret = -1;
	int irq_tmp = gpio_to_irq(dev->ctrl_gpio);
    if (ENXIO == irq_tmp) {
		print_err("get irq num failed.\n");
        return;
    }
	dev->crtl_irq = irq_tmp;
	print_info("request led(%d) sensor irq(%d)", dev->param.num, irq_tmp);

	ret = request_threaded_irq(irq_tmp, sensor_irq_handler, irq_thread_handler,
						IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "light_sensor_irq_up", dev);
    if (ret) {
        print_err("request_threaded_irq irq_rising_handler ERROR!!!\n");
        return ;
    }
}

static int init_led_pwm(struct platform_device *pdev, st_itech_led_dev* pLed)
{
	int val;
	const char* buf;
	struct gpio_config config;
	struct device_node *np = pdev->dev.of_node;

	if (of_property_read_string(np, "dev_name", &buf)) {
		print_err("get resourcce dev_name fail.\n");
		return -1;
	}
	val = strlen(buf);
	val = val < DEV_NAME_MAX_LEN?val:DEV_NAME_MAX_LEN;
	strncpy(pLed->name, buf, val);
	pLed->name[val] = '\0';
	print_dbg("name = %s\n", pLed->name);

	if (init_one_gpio(pdev, "control_gpio", &config) < 0) {
		print_info("get resourcce control_gpio fail.\n");
		pLed->ctrl_gpio = -1;
	} else {
		pLed->ctrl_gpio = config.gpio;
		print_dbg("itech_led.ctrl_gpio = %d\n", pLed->ctrl_gpio);
		init_light_sensor_irq(pLed);
	}

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

	pLed->param.change_type = CLOSE_LED;
	#if !USE_THREAD
    hrtimer_init(&pLed->param.led_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
	pLed->param.led_timer.function = led_one_cycle;
	#endif
	return 0;
}


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

static unsigned int led_poll(struct file *fp, struct poll_table_struct *wait)
{
	unsigned int mask = POLLIN | POLLRDNORM;
	int dev_index = find_dev_index(fp);
	CHECK_VALID_INDEX(dev_index);

	if(!itech_leds[dev_index].occur_irq) {
    	poll_wait(fp, &led_irq_in_waitq, wait);
		mask = 0;
	}
	itech_leds[dev_index].occur_irq = false;
    return mask;
}

static ssize_t led_read (struct file *fp, char __user *buf, size_t length, loff_t *ppos)
{
	int err = -1;
	int dev_index = find_dev_index(fp);
	led_sensor_status stStatus;
	CHECK_VALID_INDEX(dev_index);

	if(length < sizeof(led_sensor_status)) {
		print_err("param is error, buffer size is not enough.");
		return -EINVAL;
	}

	stStatus.dev_num = dev_index;
	stStatus.status = itech_leds[dev_index].irq_result;

	err = copy_to_user(buf, &stStatus, sizeof(led_sensor_status));
	print_dbg("err = %d", err);

	return err==0?sizeof(led_sensor_status):-1;
}

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) {
    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:
    	{
    		int ctrl_io_level = 0;
			int ret = -1;
			if(itech_leds[dev_index].ctrl_gpio < 0) {
				print_info("no control io is attach to this led.");
				return -1;
			}
            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:
            if(!copy_from_user(&led_data, (st_led_param*)arg, sizeof(st_led_param))) {
                updata_led_brightness(dev_index, &led_data);
            }
            break;
        default:
            print_err("...invalid cmd!!!\n");
            break;
    }

    return 0;
}
// ------------------------------------

/*
 * 2018/12/15 by atom.zhou
 * For some history reasons, the "pir" lable not only means PIR, it conatins
 * the common IO control now, we have added IO control as below:
 * 1. LED brightness control.
*/
struct file_operations itech_led_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .poll = led_poll,
    .read = led_read,
    .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* buf = NULL;
	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(val >= LED_DEV_NUM || val < 0) {
		print_err("%s led_num(%d) is out of valid value(0-%d)\n",
			buf, val, LED_DEV_NUM-1);
		return -1;
	}
	p_itech_led = &itech_leds[val];
	p_itech_led->param.num = val;
	if(init_led_pwm(pdev, p_itech_led) < 0) {
		print_err("init led(%d) fail.", val);
		return -1;
	}

	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 = "icetech, 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("binn.chen@icetech-china.com");
MODULE_DESCRIPTION("led config driver");
MODULE_LICENSE("GPL");
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值