diff options
| -rw-r--r-- | package/button-hotplug/Makefile | 53 | ||||
| -rw-r--r-- | package/button-hotplug/src/Kconfig | 2 | ||||
| -rw-r--r-- | package/button-hotplug/src/Makefile | 1 | ||||
| -rw-r--r-- | package/button-hotplug/src/button-hotplug.c | 318 | 
4 files changed, 374 insertions, 0 deletions
diff --git a/package/button-hotplug/Makefile b/package/button-hotplug/Makefile new file mode 100644 index 000000000..64bf94394 --- /dev/null +++ b/package/button-hotplug/Makefile @@ -0,0 +1,53 @@ +# +# Copyright (C) 2008 OpenWrt.org +# +# This is free software, licensed under the GNU General Public License v2. +# See /LICENSE for more information. +# + +include $(TOPDIR)/rules.mk +include $(INCLUDE_DIR)/kernel.mk + +PKG_NAME:=button-hotplug +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define KernelPackage/button-hotplug +  SUBMENU:=Other modules +  TITLE:=Button Hotplug driver +  FILES:=$(PKG_BUILD_DIR)/button-hotplug.$(LINUX_KMOD_SUFFIX) +  KCONFIG:= +endef + +define KernelPackage/button-hotplug/description +  Kernel module to generate button hotplug events +endef + +EXTRA_KCONFIG:= \ +	CONFIG_BUTTON_HOTPLUG=m + +EXTRA_CFLAGS:= \ +	$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=m,%,$(filter %=m,$(EXTRA_KCONFIG)))) \ +	$(patsubst CONFIG_%, -DCONFIG_%=1, $(patsubst %=y,%,$(filter %=y,$(EXTRA_KCONFIG)))) \ + +MAKE_OPTS:= \ +	ARCH="$(LINUX_KARCH)" \ +	CROSS_COMPILE="$(TARGET_CROSS)" \ +	SUBDIRS="$(PKG_BUILD_DIR)" \ +	EXTRA_CFLAGS="$(EXTRA_CFLAGS)" \ +	LINUXINCLUDE="-I$(LINUX_DIR)/include -include linux/autoconf.h" \ +	$(EXTRA_KCONFIG) + +define Build/Prepare +	mkdir -p $(PKG_BUILD_DIR) +	$(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Compile +	$(MAKE) -C "$(LINUX_DIR)" \ +		$(MAKE_OPTS) \ +		modules +endef + +$(eval $(call KernelPackage,button-hotplug)) diff --git a/package/button-hotplug/src/Kconfig b/package/button-hotplug/src/Kconfig new file mode 100644 index 000000000..aa292e9c1 --- /dev/null +++ b/package/button-hotplug/src/Kconfig @@ -0,0 +1,2 @@ +config BUTTON_HOTPLUG +	tristate "Button Hotplug driver" diff --git a/package/button-hotplug/src/Makefile b/package/button-hotplug/src/Makefile new file mode 100644 index 000000000..230d604f8 --- /dev/null +++ b/package/button-hotplug/src/Makefile @@ -0,0 +1 @@ +obj-${CONFIG_BUTTON_HOTPLUG}	+= button-hotplug.o
\ No newline at end of file diff --git a/package/button-hotplug/src/button-hotplug.c b/package/button-hotplug/src/button-hotplug.c new file mode 100644 index 000000000..3b5527fcf --- /dev/null +++ b/package/button-hotplug/src/button-hotplug.c @@ -0,0 +1,318 @@ +/* + *  Button Hotplug driver + * + *  Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org> + * + *  Based on the diag.c - GPIO interface driver for Broadcom boards + *    Copyright (C) 2006 Mike Baker <mbm@openwrt.org>, + *    Copyright (C) 2006-2007 Felix Fietkau <nbd@openwrt.org> + *    Copyright (C) 2008 Andy Boyett <agb@openwrt.org> + * + *  This program is free software; you can redistribute it and/or modify it + *  under the terms of the GNU General Public License version 2 as published + *  by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/version.h> +#include <linux/kmod.h> +#include <linux/input.h> + +#include <linux/workqueue.h> +#include <linux/skbuff.h> +#include <linux/netlink.h> +#include <net/sock.h> + +#define DRV_NAME	"button-hotplug" +#define DRV_VERSION	"0.3.0" +#define DRV_DESC	"Button Hotplug driver" + +#define BH_SKB_SIZE	2048 + +#define BH_BTN_MIN	BTN_0 +#define BH_BTN_MAX	BTN_9 + +#define BH_BTN_COUNT	(BH_BTN_MAX - BH_BTN_MIN + 1) + +#define PFX	DRV_NAME ": " + +/*#define BH_DEBUG*/ + +#ifdef BH_DEBUG +#define BH_DBG(fmt, args...) printk(KERN_DEBUG "%s" fmt, ##args ) +#else +#define BH_DBG(fmt, args...) do {} while (0) +#endif + +#define BH_ERR(fmt, args...) printk(KERN_ERR "%s" fmt, ##args ) + +struct bh_priv { +	unsigned long		seen[BH_BTN_COUNT]; +	struct input_handle	handle; +}; + +struct bh_event { +	char			*name; +	char			*action; +	unsigned long		seen; + +	struct sk_buff		*skb; +	struct work_struct	work; +}; + +extern struct sock *uevent_sock; +extern u64 uevent_next_seqnum(void); + +static char *button_names[BH_BTN_COUNT] = { +	"BTN_0", "BTN_1", "BTN_2", "BTN_3", "BTN_4", +	"BTN_5", "BTN_6", "BTN_7", "BTN_8", "BTN_9" +}; + +/* -------------------------------------------------------------------------*/ + +static int bh_event_add_var(struct bh_event *event, int argv, +		const char *format, ...) +{ +	static char buf[128]; +	char *s; +	va_list args; +	int len; + +	if (argv) +		return 0; + +	va_start(args, format); +	len = vsnprintf(buf, sizeof(buf), format, args); +	va_end(args); + +	if (len >= sizeof(buf)) { +		BH_ERR("buffer size too small\n"); +		WARN_ON(1); +		return -ENOMEM; +	} + +	s = skb_put(event->skb, len + 1); +	strcpy(s, buf); + +	BH_DBG("added variable '%s'\n", s); + +	return 0; +} + +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; + +	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; + +	if (!uevent_sock) +		goto out_free_event; + +	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; +	netlink_broadcast(uevent_sock, 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); +} + +static int button_hotplug_create_event(char *name, unsigned long seen, +		int pressed) +{ +	struct bh_event *event; + +	BH_DBG("create event, name=%s, seen=%lu, pressed=%d\n", +		name, seen, pressed); + +	event = kzalloc(sizeof(*event), GFP_KERNEL); +	if (!event) +		return -ENOMEM; + +	event->name = name; +	event->seen = seen; +	event->action = pressed ? "pressed" : "released"; + +	INIT_WORK(&event->work, (void *)(void *)button_hotplug_work); +	schedule_work(&event->work); + +	return 0; +} + +/* -------------------------------------------------------------------------*/ + +#ifdef	CONFIG_HOTPLUG +static void button_hotplug_event(struct input_handle *handle, +			   unsigned int type, unsigned int code, int value) +{ +	struct bh_priv *priv = handle->private; +	unsigned long seen = jiffies; +	unsigned int btn; + +	BH_DBG("event type=%u, code=%u, value=%d\n", type, code, value); + +	if (type != EV_KEY) +		return; + +	if (code < BH_BTN_MIN || code > BH_BTN_MAX) +		return; + +	btn = code - BH_BTN_MIN; +	button_hotplug_create_event(button_names[btn], +			(seen - priv->seen[btn]) / HZ, value); +	priv->seen[btn] = seen; +} +#else +static void button_hotplug_event(struct input_handle *handle, +			   unsigned int type, unsigned int code, int value) +{ +} +#endif	/* CONFIG_HOTPLUG */ + +static int button_hotplug_connect(struct input_handler *handler, +		struct input_dev *dev, const struct input_device_id *id) +{ +	struct bh_priv *priv; +	int ret; +	int i; + +	for (i = BH_BTN_MIN; i <= BH_BTN_MAX; i++) +		if (test_bit(i, dev->keybit)) +			break; + +	if (i > BH_BTN_MAX) +		return -ENODEV; + +	priv = kzalloc(sizeof(*priv), GFP_KERNEL); +	if (!priv) +		return -ENOMEM; + +	priv->handle.private = priv; +	priv->handle.dev = dev; +	priv->handle.handler = handler; +	priv->handle.name = DRV_NAME; + +	ret = input_register_handle(&priv->handle); +	if (ret) +		goto err_free_priv; + +	ret = input_open_device(&priv->handle); +	if (ret) +		goto err_unregister_handle; + +	BH_DBG("connected to %s\n", dev->name); + +	return 0; + + err_unregister_handle: +	input_unregister_handle(&priv->handle); + + err_free_priv: +	kfree(priv); +	return ret; +} + +static void button_hotplug_disconnect(struct input_handle *handle) +{ +	struct bh_priv *priv = handle->private; + +	input_close_device(handle); +	input_unregister_handle(handle); + +	kfree(priv); +} + +static const struct input_device_id button_hotplug_ids[] = { +	{ +                .flags = INPUT_DEVICE_ID_MATCH_EVBIT, +                .evbit = { BIT_MASK(EV_KEY) }, +        }, +	{ +		/* Terminating entry */ +	}, +}; + +MODULE_DEVICE_TABLE(input, button_hotplug_ids); + +static struct input_handler button_hotplug_handler = { +	.event =	button_hotplug_event, +	.connect =	button_hotplug_connect, +	.disconnect =	button_hotplug_disconnect, +	.name =		DRV_NAME, +	.id_table =	button_hotplug_ids, +}; + +/* -------------------------------------------------------------------------*/ + +static int __init button_hotplug_init(void) +{ +	int ret; + +	printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n"); +	ret = input_register_handler(&button_hotplug_handler); +	if (ret) +		BH_ERR("unable to register input handler\n"); + +	return ret; +} +module_init(button_hotplug_init); + +static void __exit button_hotplug_exit(void) +{ +	input_unregister_handler(&button_hotplug_handler); +} +module_exit(button_hotplug_exit); + +MODULE_DESCRIPTION(DRV_DESC); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); +MODULE_LICENSE("GPL v2"); +  | 
