【openwrt】按键

文章目录

1.原理

1.1.硬件电路

【openwrt】按键【openwrt】按键

1.2.管脚信息

【openwrt】按键

1.3.GPIO内存映射图

【openwrt】按键

1.4 管脚中断号

【openwrt】按键

2.实现

2.1.设备树配置

2.1.1.mt7621.dtsi

gpio@600 {
	#address-cells = <1>;
	#size-cells = <0>;

	compatible = "mtk,mt7621-gpio";
	reg = <0x600 0x100>;

	interrupt-parent = <&gic>;
	interrupts = <GIC_SHARED 12 IRQ_TYPE_LEVEL_HIGH>;

	gpio0: bank@0 {
		reg = <0>;
		compatible = "mtk,mt7621-gpio-bank";
		gpio-controller;
		#gpio-cells = <2>;
	};

	gpio1: bank@1 {
		reg = <1>;
		compatible = "mtk,mt7621-gpio-bank";
		gpio-controller;
		#gpio-cells = <2>;
	};

	gpio2: bank@2 {
		reg = <2>;
		compatible = "mtk,mt7621-gpio-bank";
		gpio-controller;
		#gpio-cells = <2>;
	};
};

2.1.2 ctunite-t2.dts


/ {
	compatible = "mediatek,ctunite-t2", "mediatek,mt7621-soc";
	model = "Ctunite-T2";

	chosen {
		bootargs = "console=ttyS0,115200";
	};

	gpio-keys-polled {
		compatible = "gpio-keys-polled";
		#address-cells = <1>;
		#size-cells = <0>;
		poll-interval = <20>;

		reset {
			label = "reset";
			gpios = <&gpio0 18 1>;
			linux,code = <KEY_RESTART>;
		};
	};
};

2.2.配置

make menuconfig

1、开启按键热插拔驱动
【openwrt】按键

2.3.驱动

2.3.1.mt7621 gpio驱动源码

linux-4.4.198/drivers/gpio/gpio-mt7621.c

static int
mediatek_gpio_probe(struct platform_device *pdev)
{
	struct device_node *bank, *np = pdev->dev.of_node;
	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

	mediatek_gpio_membase = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(mediatek_gpio_membase))
		return PTR_ERR(mediatek_gpio_membase);

	mediatek_gpio_irq = irq_of_parse_and_map(np, 0);
	if (mediatek_gpio_irq) {
		mediatek_gpio_irq_domain = irq_domain_add_linear(np,
			MTK_MAX_BANK * MTK_BANK_WIDTH,
			&irq_domain_ops, NULL);
		if (!mediatek_gpio_irq_domain)
			dev_notice(&pdev->dev, "irq_domain_add_linear failed\n");
	}

	for_each_child_of_node(np, bank)
		if (of_device_is_compatible(bank, "mtk,mt7621-gpio-bank"))
			mediatek_gpio_bank_probe(pdev, bank);

	if (mediatek_gpio_irq_domain)
		irq_set_chained_handler(mediatek_gpio_irq, mediatek_gpio_irq_handler);

	return 0;
}

static const struct of_device_id mediatek_gpio_match[] = {
	{ .compatible = "mtk,mt7621-gpio" },
	{},
};
MODULE_DEVICE_TABLE(of, mediatek_gpio_match);

static struct platform_driver mediatek_gpio_driver = {
	.probe = mediatek_gpio_probe,
	.driver = {
		.name = "mt7621_gpio",
		.owner = THIS_MODULE,
		.of_match_table = mediatek_gpio_match,
	},
};

static int __init
mediatek_gpio_init(void)
{
	return platform_driver_register(&mediatek_gpio_driver);
}

arch_initcall(mediatek_gpio_init);

static void
mediatek_gpio_irq_handler(struct irq_desc *desc)
{
	int i;

	for (i = 0; i < MTK_MAX_BANK; i++) {
		struct mtk_gc *rg = gc_map[i];
		unsigned long pending;
		int bit;

		if (!rg)
			continue;

		pending = mtk_gpio_r32(rg, GPIO_REG_STAT);

		for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
			u32 map = irq_find_mapping(mediatek_gpio_irq_domain, (MTK_BANK_WIDTH * i) + bit);

			generic_handle_irq(map);
			mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
		}
	}
}

2.3.2 按键热插拔驱动

static struct platform_driver gpio_keys_driver = {
	.probe	= gpio_keys_probe,
	.remove	= gpio_keys_remove,
	.driver	= {
		.name	= "gpio-keys",
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(gpio_keys_of_match),
	},
};

static struct platform_driver gpio_keys_polled_driver = {
	.probe	= gpio_keys_polled_probe,
	.remove	= gpio_keys_remove,
	.driver	= {
		.name	= "gpio-keys-polled",
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(gpio_keys_polled_of_match),
	},
};

static int __init gpio_button_init(void)
{
	int ret;

	ret = platform_driver_register(&gpio_keys_driver);
	if (ret)
		return ret;

	ret = platform_driver_register(&gpio_keys_polled_driver);
	if (ret)
		platform_driver_unregister(&gpio_keys_driver);

	return ret;
}

static void __exit gpio_button_exit(void)
{
	platform_driver_unregister(&gpio_keys_driver);
	platform_driver_unregister(&gpio_keys_polled_driver);
}

module_init(gpio_button_init);
module_exit(gpio_button_exit);

按键中断服务函数

static irqreturn_t button_handle_irq(int irq, void *_bdata)
{
	struct gpio_keys_button_data *bdata = (struct gpio_keys_button_data *) _bdata;

	button_hotplug_event(bdata, bdata->b->type ?: EV_KEY, gpio_button_get_value(bdata));

	return IRQ_HANDLED;
}

创建按键热插拔事件

static void button_hotplug_event(struct gpio_keys_button_data *data,
			   unsigned int type, int value)
{
	struct bh_priv *priv = &data->bh;
	unsigned long seen = jiffies;
	int btn;

	BH_DBG("event type=%u, code=%u, value=%d\n", type, data->b->code, value);

	if ((type != EV_KEY) && (type != EV_SW))
		return;

	btn = button_get_index(data->b->code);
	if (btn < 0)
		return;

	button_hotplug_create_event(button_map[btn].name, type,
			(seen - priv->seen) / HZ, value);
	priv->seen = seen;
}

热插拔事件类型

static struct bh_map button_map[] = {
	BH_MAP(BTN_0,			"BTN_0"),
	BH_MAP(BTN_1,			"BTN_1"),
	BH_MAP(BTN_2,			"BTN_2"),
	BH_MAP(BTN_3,			"BTN_3"),
	BH_MAP(BTN_4,			"BTN_4"),
	BH_MAP(BTN_5,			"BTN_5"),
	BH_MAP(BTN_6,			"BTN_6"),
	BH_MAP(BTN_7,			"BTN_7"),
	BH_MAP(BTN_8,			"BTN_8"),
	BH_MAP(BTN_9,			"BTN_9"),
	BH_MAP(KEY_BRIGHTNESS_ZERO,	"brightness_zero"),
	BH_MAP(KEY_CONFIG,		"config"),
	BH_MAP(KEY_COPY,		"copy"),
	BH_MAP(KEY_EJECTCD,		"eject"),
	BH_MAP(KEY_HELP,		"help"),
	BH_MAP(KEY_LIGHTS_TOGGLE,	"lights_toggle"),
	BH_MAP(KEY_PHONE,		"phone"),
	BH_MAP(KEY_POWER,		"power"),
	BH_MAP(KEY_RESTART,		"reset"),
	BH_MAP(KEY_RFKILL,		"rfkill"),
	BH_MAP(KEY_VIDEO,		"video"),
	BH_MAP(KEY_WIMAX,		"wwan"),
	BH_MAP(KEY_WLAN,		"wlan"),
	BH_MAP(KEY_WPS_BUTTON,		"wps"),
};

组装netlink报文,以广播形式发送netlink报文


static int button_hotplug_fill_event(struct bh_event *event)
{
	int ret;

	ret = bh_event_add_var(event, 0, "HOME=%s", "/");
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "PATH=%s",
					"/sbin:/bin:/usr/sbin:/usr/bin");
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button");
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "ACTION=%s", event->action);
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "BUTTON=%s", event->name);
	if (ret)
		return ret;

	if (event->type == EV_SW) {
		ret = bh_event_add_var(event, 0, "TYPE=%s", "switch");
		if (ret)
			return ret;
	}

	ret = bh_event_add_var(event, 0, "SEEN=%ld", event->seen);
	if (ret)
		return ret;

	ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());

	return ret;
}

static void button_hotplug_work(struct work_struct *work)
{
	struct bh_event *event = container_of(work, struct bh_event, work);
	int ret = 0;

	event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL);
	if (!event->skb)
		goto out_free_event;

	ret = bh_event_add_var(event, 0, "%s@", event->action);
	if (ret)
		goto out_free_skb;

	ret = button_hotplug_fill_event(event);
	if (ret)
		goto out_free_skb;

	NETLINK_CB(event->skb).dst_group = 1;
	broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);

 out_free_skb:
	if (ret) {
		BH_ERR("work error %d\n", ret);
		kfree_skb(event->skb);
	}
 out_free_event:
	kfree(event);
}

2.3.3.procd捕获按键热插拔驱动发送的netlink消息

procd在捕获到按键事件netlink消息后,解析出的数据,通过匹配/etc/hotplug.json中的json数据,进行相应脚本的调用。在/etc/hotplug.json中,包含对button事件的处理信息,其中%BUTTON%为reset,因此会执行/etc/rc.button/reset脚本。

[
	...
	[ "if",
		[ "and",
			[ "has", "BUTTON" ],
			[ "eq", "SUBSYSTEM", "button" ],
		],
		[ "button", "/etc/rc.button/%BUTTON%" ]
	],
	...
]

3.参考文档

MT7621A.PDF
MT7621_ProgrammingGuide_Preliminary_Platform.pdf

上一篇:ISP、IAP、ICP烧写方式的区别


下一篇:ios15 pop框架的使用