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>