[Android O] [RK3399] - Pulse signal detection

demand

The driver realizes the pulse signal detection of GPIO, and informs the system when the pulse signal comes and when the pulse signal ends.

Realization principle

1. When the pulse comes, the detection is triggered by the GPIO interrupt;

2. The pulse signal is processed as a key event, and the event is reported through the input subsystem;

3. Use the kernel timer to control the reporting of input events.

Implementation

The device tree is configured as:

&rk_key {
	status = "okay";
	compatible = "rockchip,key";
	io-channels = <&saradc 1>;

	vol-up-key {
		linux,code = <114>;
		label = "volume up";
		rockchip,adc_value = <170>;
	};

	vol-down-key {
		linux,code = <115>;
		label = "volume down";
		rockchip,adc_value = <1>;
	};

	power-key {
		gpios = <&gpio0 5 GPIO_ACTIVE_LOW>;
		linux,code = <116>;
		label = "power";
		gpio-key,wakeup;
	};

	left-light {
		gpios = <&gpio4 RK_PC1 GPIO_ACTIVE_LOW>;
                linux,code = <87>;
                label = "left_light";
                gpio-key,light;
        };

    right-light {
        gpios = <&gpio4 RK_PC0 GPIO_ACTIVE_LOW>;
               linux,code = <88>;
               label = "right_light";
               gpio-key,light;
        };
};

 

The specific implementation is in /kernel/drivers/input/keyboard/rk_keys.c, and refer to the related operations:

The keys_probe() function is as follows:

static int keys_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *np = pdev->dev.of_node;
	struct rk_keys_drvdata *ddata = NULL;
	struct input_dev *input = NULL;
	int i, error = 0;
	int wakeup, key_num = 0;

	key_num = of_get_child_count(np);
	if (key_num == 0)
		dev_info(&pdev->dev, "no key defined\n");

	ddata = devm_kzalloc(dev, sizeof(struct rk_keys_drvdata) +
			     key_num * sizeof(struct rk_keys_button),
			     GFP_KERNEL);

	input = devm_input_allocate_device(dev);
	if (!ddata || !input) {
		error = -ENOMEM;
		return error;
	}
	platform_set_drvdata(pdev, ddata);
	dev_set_drvdata(&pdev->dev, ddata);

	input->name = "rk29-keypad";	/* pdev->name; */
	input->phys = "gpio-keys/input0";
	input->dev.parent = dev;

	input->id.bustype = BUS_HOST;
	input->id.vendor = 0x0001;
	input->id.product = 0x0001;
	input->id.version = 0x0100;
	ddata->input = input;

	/* parse info from dt */
	ddata->nbuttons = key_num;
	error = rk_keys_parse_dt(ddata, pdev);
	if (error)
		goto fail0;

	/* Enable auto repeat feature of Linux input subsystem */
	if (ddata->rep)
		__set_bit(EV_REP, input->evbit);

	error = input_register_device(input);
	if (error) {
		pr_err("gpio-keys: Unable to register input device, error: %d\n",
		       error);
		goto fail0;
	}
	sinput_dev = input;

	for (i = 0; i < ddata->nbuttons; i++) {
		struct rk_keys_button *button = &ddata->button[i];

		if (button->code) {
            if(button->light){
			    setup_timer(&button->timer,
				        keys_light_timer, (unsigned long)button);
            }else{
			    setup_timer(&button->timer,
				        keys_timer, (unsigned long)button);
            }
		}

		if (button->wakeup)
			wakeup = 1;

		input_set_capability(input, EV_KEY, button->code);
	}

	wake_lock_init(&ddata->wake_lock, WAKE_LOCK_SUSPEND, input->name);
	device_init_wakeup(dev, wakeup);

	for (i = 0; i < ddata->nbuttons; i++) {
		struct rk_keys_button *button = &ddata->button[i];

		button->dev = &pdev->dev;
		if (button->type == TYPE_GPIO) {
			int irq;

			error =
			    devm_gpio_request(dev, button->gpio,
					      button->desc ? : "keys");
			if (error < 0) {
				pr_err("gpio-keys: failed to request GPIO %d, error %d\n",
				       button->gpio, error);
				goto fail1;
			}

			error = gpio_direction_input(button->gpio);
			if (error < 0) {
				pr_err("gpio-keys: failed to configure input direction for GPIO %d, error %d\n",
				       button->gpio, error);
				gpio_free(button->gpio);
				goto fail1;
			}

			irq = gpio_to_irq(button->gpio);
			if (irq < 0) {
				error = irq;
				pr_err("gpio-keys: Unable to get irq number for GPIO %d, error %d\n",
				       button->gpio, error);
				gpio_free(button->gpio);
				goto fail1;
			}
            if(button->light){
			    error = devm_request_irq(dev, irq, keys_light_isr,
						 button->active_low ?
						 IRQF_TRIGGER_FALLING :
						 IRQF_TRIGGER_RISING,
						 button->desc ?
						 button->desc : "keys",
						 button);
            }else{
			    error = devm_request_irq(dev, irq, keys_isr,
						 button->active_low ?
						 IRQF_TRIGGER_FALLING :
						 IRQF_TRIGGER_RISING,
						 button->desc ?
						 button->desc : "keys",
						 button);
            }
			if (error) {
				pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
				       irq, error);
				gpio_free(button->gpio);
				goto fail1;
			}
		}
	}

	input_set_capability(input, EV_KEY, KEY_WAKEUP);
	/* adc polling work */
	if (ddata->chan) {
		INIT_DELAYED_WORK(&ddata->adc_poll_work, adc_key_poll);
		schedule_delayed_work(&ddata->adc_poll_work,
				      ADC_SAMPLE_JIFFIES);
	}

	return error;

fail1:
	while (--i >= 0)
		del_timer_sync(&ddata->button[i].timer);
	device_init_wakeup(dev, 0);
	wake_lock_destroy(&ddata->wake_lock);
fail0:
	platform_set_drvdata(pdev, NULL);

	return error;
}

The keys_light_isr() function is as follows:

static irqreturn_t keys_light_isr(int irq, void *dev_id)
{
    struct rk_keys_button *button = (struct rk_keys_button *)dev_id;
    struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);
    struct input_dev *input = pdata->input;

    BUG_ON(irq != gpio_to_irq(button->gpio));

    if (!button->state){
        button->state = 1;
        input_event(input, EV_KEY, button->code, button->state);
        input_sync(input);
        mod_timer(&button->timer, jiffies + DEBOUNCE_LIGHT_JIFFIES);
    }else{
        mod_timer(&button->timer, jiffies +DEBOUNCE_LIGHT_JIFFIES);
    }
    return IRQ_HANDLED;
}

The keys_light_timer() function is as follows:

static void keys_light_timer(unsigned long _data)
{
    struct rk_keys_button *button = (struct rk_keys_button *)_data;
    struct rk_keys_drvdata *pdata = dev_get_drvdata(button->dev);
    struct input_dev *input = pdata->input;

    if(button->state) {
        button->state = 0;
        input_event(input, EV_KEY, button->code, button->state);
        input_sync(input);
    }else{
    }
}

 

Key function analysis

1. devm_kzalloc() and devm_kfree():

The function devm_kzalloc() and kzalloc() are both kernel memory allocation functions. But devm_kzalloc() is related to the device. When the device is removed or the driver is unloaded, the memory will be automatically released; in addition, when the memory is not in use, it can be released using the function devm_kfree(). And kzalloc() needs to be released manually (using kfree()), but if the engineer does not check carefully, it may cause memory leaks.

Note: That is, call devm_kzalloc() in the probe function of the driver, and call devm_kfree() in the removal function.

2、platform_set_drvdata() 和 platform_get_drvdata():

The two functions platform_set_drvdata() and platform_get_drvdata() are commonly used in drivers to save local variables.

The function prototype is as follows:

static inline void *platform_get_drvdata(const struct platform_device *pdev);

static inline void platform_set_drvdata(struct platform_device *pdev, void* data);

It is to assign data to pdev->dev->driver_data, pdev is the platform bus device, which is visible to the entire driver, so data can be obtained through platform_get_drvdata().

The kernel module generally applies for memory dynamically in the probe() function for use. In this case, the pointer must be stored in a location to prevent loss. Therefore, the kernel is designed to reserve a pointer in the platform_device structure for such a drive. Way of writing.

So we generally apply for the device structure (or local variable) dynamically in the probe() function, initialize it, and then save it to platform_device using the paltform_set_drvdata() function, and then use platform_get_drvdata() to get it when needed. . But this pointer definitely needs us to release the memory by ourselves.

3、dev_set_drvdata() 和 dev_get_drvdata():

Used to set the private data of the device and used to obtain the private data of the device.

4、for_each_child_of_node:

Traverse all child nodes.

5、of_get_gpio_flags():

例:gpio = of_get_gpio_flags(device_node *node, 0, &flags);

Read GPIO configuration number gpio and flags from node. Flags represent GPIO_ACTIVE_LOW and GPIO_ACTIVE_HIGHT.

6、of_get_property():

The function prototype is:

const void *of_get_property(const struct device_node *np, const char *name, int *lenp);

According to the name parameter, search for a matching property in the specified device node np, and return the property value of this property.

7 、 setup_timer () 和 mod_timer () :

The kernel timer is a mechanism used by the kernel to control the scheduling and execution of a function at a certain point in the future (based on jiffies), and its implementation is located in the <linux/timer.h> and time.c files.

After the kernel timer's scheduling function runs once, it will no longer be run (equivalent to automatic logout), but it can be run periodically by rescheduling itself in the scheduled function.

The setup_timer() function is used to initialize the timer and assign its members; to modify the scheduling time of a timer, you can call the mod_timer() function. The mod_timer() function will re-register the timer to the kernel, regardless of whether the timer function is Ran.

8、input_event():

Report new input events.

The function prototype is as follows:

input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
  • dev:device that generated the event;
  • type:type of the event;
  • code:event code;
  • value:value of the event

9、input_set_capability:

Set which input events the input device can report.

The function prototype is as follows:

input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code);
  • dev: the input_dev structure variable of the device;
  • type: indicates the type of event that the device can report;
  • code: Indicates which event in this type of time is reported.

Note: input_set_capability() can only set one specific event at a time. If the device can report multiple events, you need to call this function repeatedly to set it.

10、devm_input_allocate_device() 和 input_allocate_device():

Allocate an input_dev structure.

11、set_bit():

set_bit(int nr, int *addr);

Set the nr of addr (nr is 0-31) to 1.

Application examples:

set_bit(EV_REP, input->evbit);

12、input_set_abs_params(dev, axis, min, max, fuzz, flat):

This function call is actually set_bit(), for the parameters: fuzz has a filtering effect; min and max represent the range; axis represents the coordinate axis; flat does not know the purpose for the time being.

13、input_report_key() / input_report_rel() / input_report_abs():

Submit key event / submit relative coordinate event / submit absolute coordinate event.

14、input_sync():

Inform the input subsystem that the device driver has issued a complete report.

15、input_register_device():

Used to register an input device.

 

Summary of knowledge points

1. Input subsystem;

2. Interrupt the system;

3. Kernel timer.

 

 

 

 

 

 

 

 

 

 

Guess you like

Origin blog.csdn.net/u014674293/article/details/105817230