1/* 2 * Button Hotplug driver 3 * 4 * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org> 5 * 6 * Based on the diag.c - GPIO interface driver for Broadcom boards 7 * Copyright (C) 2006 Mike Baker <mbm@openwrt.org>, 8 * Copyright (C) 2006-2007 Felix Fietkau <nbd@nbd.name> 9 * Copyright (C) 2008 Andy Boyett <agb@openwrt.org> 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License version 2 as published 13 * by the Free Software Foundation. 14 */ 15 16#include <linux/module.h> 17#include <linux/version.h> 18#include <linux/kmod.h> 19#include <linux/input.h> 20 21#include <linux/workqueue.h> 22#include <linux/skbuff.h> 23#include <linux/netlink.h> 24#include <linux/kobject.h> 25 26#define DRV_NAME "button-hotplug" 27#define DRV_VERSION "0.4.1" 28#define DRV_DESC "Button Hotplug driver" 29 30#define BH_SKB_SIZE 2048 31 32#define PFX DRV_NAME ": " 33 34#undef BH_DEBUG 35 36#ifdef BH_DEBUG 37#define BH_DBG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, DRV_NAME, ##args ) 38#else 39#define BH_DBG(fmt, args...) do {} while (0) 40#endif 41 42#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, DRV_NAME, ##args ) 43 44#ifndef BIT_MASK 45#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) 46#endif 47 48struct bh_priv { 49 unsigned long *seen; 50 struct input_handle handle; 51}; 52 53struct bh_event { 54 const char *name; 55 char *action; 56 unsigned long seen; 57 58 struct sk_buff *skb; 59 struct work_struct work; 60}; 61 62struct bh_map { 63 unsigned int code; 64 const char *name; 65}; 66 67extern u64 uevent_next_seqnum(void); 68 69#define BH_MAP(_code, _name) \ 70 { \ 71 .code = (_code), \ 72 .name = (_name), \ 73 } 74 75static struct bh_map button_map[] = { 76 BH_MAP(BTN_0, "BTN_0"), 77 BH_MAP(BTN_1, "BTN_1"), 78 BH_MAP(BTN_2, "BTN_2"), 79 BH_MAP(BTN_3, "BTN_3"), 80 BH_MAP(BTN_4, "BTN_4"), 81 BH_MAP(BTN_5, "BTN_5"), 82 BH_MAP(BTN_6, "BTN_6"), 83 BH_MAP(BTN_7, "BTN_7"), 84 BH_MAP(BTN_8, "BTN_8"), 85 BH_MAP(BTN_9, "BTN_9"), 86 BH_MAP(KEY_RESTART, "reset"), 87 BH_MAP(KEY_POWER, "power"), 88 BH_MAP(KEY_RFKILL, "rfkill"), 89 BH_MAP(KEY_WPS_BUTTON, "wps"), 90 BH_MAP(KEY_WIMAX, "wwan"), 91}; 92 93/* -------------------------------------------------------------------------*/ 94 95static int bh_event_add_var(struct bh_event *event, int argv, 96 const char *format, ...) 97{ 98 static char buf[128]; 99 char *s; 100 va_list args; 101 int len; 102 103 if (argv) 104 return 0; 105 106 va_start(args, format); 107 len = vsnprintf(buf, sizeof(buf), format, args); 108 va_end(args); 109 110 if (len >= sizeof(buf)) { 111 BH_ERR("buffer size too small\n"); 112 WARN_ON(1); 113 return -ENOMEM; 114 } 115 116 s = skb_put(event->skb, len + 1); 117 strcpy(s, buf); 118 119 BH_DBG("added variable '%s'\n", s); 120 121 return 0; 122} 123 124static int button_hotplug_fill_event(struct bh_event *event) 125{ 126 int ret; 127 128 ret = bh_event_add_var(event, 0, "HOME=%s", "/"); 129 if (ret) 130 return ret; 131 132 ret = bh_event_add_var(event, 0, "PATH=%s", 133 "/sbin:/bin:/usr/sbin:/usr/bin"); 134 if (ret) 135 return ret; 136 137 ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button"); 138 if (ret) 139 return ret; 140 141 ret = bh_event_add_var(event, 0, "ACTION=%s", event->action); 142 if (ret) 143 return ret; 144 145 ret = bh_event_add_var(event, 0, "BUTTON=%s", event->name); 146 if (ret) 147 return ret; 148 149 ret = bh_event_add_var(event, 0, "SEEN=%ld", event->seen); 150 if (ret) 151 return ret; 152 153 ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum()); 154 155 return ret; 156} 157 158static void button_hotplug_work(struct work_struct *work) 159{ 160 struct bh_event *event = container_of(work, struct bh_event, work); 161 int ret = 0; 162 163 event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL); 164 if (!event->skb) 165 goto out_free_event; 166 167 ret = bh_event_add_var(event, 0, "%s@", event->action); 168 if (ret) 169 goto out_free_skb; 170 171 ret = button_hotplug_fill_event(event); 172 if (ret) 173 goto out_free_skb; 174 175 NETLINK_CB(event->skb).dst_group = 1; 176 broadcast_uevent(event->skb, 0, 1, GFP_KERNEL); 177 178 out_free_skb: 179 if (ret) { 180 BH_ERR("work error %d\n", ret); 181 kfree_skb(event->skb); 182 } 183 out_free_event: 184 kfree(event); 185} 186 187static int button_hotplug_create_event(const char *name, unsigned long seen, 188 int pressed) 189{ 190 struct bh_event *event; 191 192 BH_DBG("create event, name=%s, seen=%lu, pressed=%d\n", 193 name, seen, pressed); 194 195 event = kzalloc(sizeof(*event), GFP_KERNEL); 196 if (!event) 197 return -ENOMEM; 198 199 event->name = name; 200 event->seen = seen; 201 event->action = pressed ? "pressed" : "released"; 202 203 INIT_WORK(&event->work, (void *)(void *)button_hotplug_work); 204 schedule_work(&event->work); 205 206 return 0; 207} 208 209/* -------------------------------------------------------------------------*/ 210 211static int button_get_index(unsigned int code) 212{ 213 int i; 214 215 for (i = 0; i < ARRAY_SIZE(button_map); i++) 216 if (button_map[i].code == code) 217 return i; 218 219 return -1; 220} 221static void button_hotplug_event(struct input_handle *handle, 222 unsigned int type, unsigned int code, int value) 223{ 224 struct bh_priv *priv = handle->private; 225 unsigned long seen = jiffies; 226 int btn; 227 228 BH_DBG("event type=%u, code=%u, value=%d\n", type, code, value); 229 230 if (type != EV_KEY) 231 return; 232 233 btn = button_get_index(code); 234 if (btn < 0) 235 return; 236 237 button_hotplug_create_event(button_map[btn].name, 238 (seen - priv->seen[btn]) / HZ, value); 239 priv->seen[btn] = seen; 240} 241 242static int button_hotplug_connect(struct input_handler *handler, 243 struct input_dev *dev, const struct input_device_id *id) 244{ 245 struct bh_priv *priv; 246 int ret; 247 int i; 248 249 for (i = 0; i < ARRAY_SIZE(button_map); i++) 250 if (test_bit(button_map[i].code, dev->keybit)) 251 break; 252 253 if (i == ARRAY_SIZE(button_map)) 254 return -ENODEV; 255 256 priv = kzalloc(sizeof(*priv) + 257 (sizeof(unsigned long) * ARRAY_SIZE(button_map)), 258 GFP_KERNEL); 259 if (!priv) 260 return -ENOMEM; 261 262 priv->seen = (unsigned long *) &priv[1]; 263 priv->handle.private = priv; 264 priv->handle.dev = dev; 265 priv->handle.handler = handler; 266 priv->handle.name = DRV_NAME; 267 268 ret = input_register_handle(&priv->handle); 269 if (ret) 270 goto err_free_priv; 271 272 ret = input_open_device(&priv->handle); 273 if (ret) 274 goto err_unregister_handle; 275 276 BH_DBG("connected to %s\n", dev->name); 277 278 return 0; 279 280 err_unregister_handle: 281 input_unregister_handle(&priv->handle); 282 283 err_free_priv: 284 kfree(priv); 285 return ret; 286} 287 288static void button_hotplug_disconnect(struct input_handle *handle) 289{ 290 struct bh_priv *priv = handle->private; 291 292 input_close_device(handle); 293 input_unregister_handle(handle); 294 295 kfree(priv); 296} 297 298static const struct input_device_id button_hotplug_ids[] = { 299 { 300 .flags = INPUT_DEVICE_ID_MATCH_EVBIT, 301 .evbit = { BIT_MASK(EV_KEY) }, 302 }, 303 { 304 /* Terminating entry */ 305 }, 306}; 307 308MODULE_DEVICE_TABLE(input, button_hotplug_ids); 309 310static struct input_handler button_hotplug_handler = { 311 .event = button_hotplug_event, 312 .connect = button_hotplug_connect, 313 .disconnect = button_hotplug_disconnect, 314 .name = DRV_NAME, 315 .id_table = button_hotplug_ids, 316}; 317 318/* -------------------------------------------------------------------------*/ 319 320static int __init button_hotplug_init(void) 321{ 322 int ret; 323 324 printk(KERN_INFO DRV_DESC " version " DRV_VERSION "\n"); 325 ret = input_register_handler(&button_hotplug_handler); 326 if (ret) 327 BH_ERR("unable to register input handler\n"); 328 329 return ret; 330} 331module_init(button_hotplug_init); 332 333static void __exit button_hotplug_exit(void) 334{ 335 input_unregister_handler(&button_hotplug_handler); 336} 337module_exit(button_hotplug_exit); 338 339MODULE_DESCRIPTION(DRV_DESC); 340MODULE_VERSION(DRV_VERSION); 341MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>"); 342MODULE_LICENSE("GPL v2"); 343 344