18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci// rc-main.c - Remote Controller core module 38c2ecf20Sopenharmony_ci// 48c2ecf20Sopenharmony_ci// Copyright (C) 2009-2010 by Mauro Carvalho Chehab 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <media/rc-core.h> 98c2ecf20Sopenharmony_ci#include <linux/bsearch.h> 108c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/input.h> 138c2ecf20Sopenharmony_ci#include <linux/leds.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/idr.h> 168c2ecf20Sopenharmony_ci#include <linux/device.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include "rc-core-priv.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */ 218c2ecf20Sopenharmony_ci#define IR_TAB_MIN_SIZE 256 228c2ecf20Sopenharmony_ci#define IR_TAB_MAX_SIZE 8192 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const struct { 258c2ecf20Sopenharmony_ci const char *name; 268c2ecf20Sopenharmony_ci unsigned int repeat_period; 278c2ecf20Sopenharmony_ci unsigned int scancode_bits; 288c2ecf20Sopenharmony_ci} protocols[] = { 298c2ecf20Sopenharmony_ci [RC_PROTO_UNKNOWN] = { .name = "unknown", .repeat_period = 125 }, 308c2ecf20Sopenharmony_ci [RC_PROTO_OTHER] = { .name = "other", .repeat_period = 125 }, 318c2ecf20Sopenharmony_ci [RC_PROTO_RC5] = { .name = "rc-5", 328c2ecf20Sopenharmony_ci .scancode_bits = 0x1f7f, .repeat_period = 114 }, 338c2ecf20Sopenharmony_ci [RC_PROTO_RC5X_20] = { .name = "rc-5x-20", 348c2ecf20Sopenharmony_ci .scancode_bits = 0x1f7f3f, .repeat_period = 114 }, 358c2ecf20Sopenharmony_ci [RC_PROTO_RC5_SZ] = { .name = "rc-5-sz", 368c2ecf20Sopenharmony_ci .scancode_bits = 0x2fff, .repeat_period = 114 }, 378c2ecf20Sopenharmony_ci [RC_PROTO_JVC] = { .name = "jvc", 388c2ecf20Sopenharmony_ci .scancode_bits = 0xffff, .repeat_period = 125 }, 398c2ecf20Sopenharmony_ci [RC_PROTO_SONY12] = { .name = "sony-12", 408c2ecf20Sopenharmony_ci .scancode_bits = 0x1f007f, .repeat_period = 100 }, 418c2ecf20Sopenharmony_ci [RC_PROTO_SONY15] = { .name = "sony-15", 428c2ecf20Sopenharmony_ci .scancode_bits = 0xff007f, .repeat_period = 100 }, 438c2ecf20Sopenharmony_ci [RC_PROTO_SONY20] = { .name = "sony-20", 448c2ecf20Sopenharmony_ci .scancode_bits = 0x1fff7f, .repeat_period = 100 }, 458c2ecf20Sopenharmony_ci [RC_PROTO_NEC] = { .name = "nec", 468c2ecf20Sopenharmony_ci .scancode_bits = 0xffff, .repeat_period = 110 }, 478c2ecf20Sopenharmony_ci [RC_PROTO_NECX] = { .name = "nec-x", 488c2ecf20Sopenharmony_ci .scancode_bits = 0xffffff, .repeat_period = 110 }, 498c2ecf20Sopenharmony_ci [RC_PROTO_NEC32] = { .name = "nec-32", 508c2ecf20Sopenharmony_ci .scancode_bits = 0xffffffff, .repeat_period = 110 }, 518c2ecf20Sopenharmony_ci [RC_PROTO_SANYO] = { .name = "sanyo", 528c2ecf20Sopenharmony_ci .scancode_bits = 0x1fffff, .repeat_period = 125 }, 538c2ecf20Sopenharmony_ci [RC_PROTO_MCIR2_KBD] = { .name = "mcir2-kbd", 548c2ecf20Sopenharmony_ci .scancode_bits = 0xffffff, .repeat_period = 100 }, 558c2ecf20Sopenharmony_ci [RC_PROTO_MCIR2_MSE] = { .name = "mcir2-mse", 568c2ecf20Sopenharmony_ci .scancode_bits = 0x1fffff, .repeat_period = 100 }, 578c2ecf20Sopenharmony_ci [RC_PROTO_RC6_0] = { .name = "rc-6-0", 588c2ecf20Sopenharmony_ci .scancode_bits = 0xffff, .repeat_period = 114 }, 598c2ecf20Sopenharmony_ci [RC_PROTO_RC6_6A_20] = { .name = "rc-6-6a-20", 608c2ecf20Sopenharmony_ci .scancode_bits = 0xfffff, .repeat_period = 114 }, 618c2ecf20Sopenharmony_ci [RC_PROTO_RC6_6A_24] = { .name = "rc-6-6a-24", 628c2ecf20Sopenharmony_ci .scancode_bits = 0xffffff, .repeat_period = 114 }, 638c2ecf20Sopenharmony_ci [RC_PROTO_RC6_6A_32] = { .name = "rc-6-6a-32", 648c2ecf20Sopenharmony_ci .scancode_bits = 0xffffffff, .repeat_period = 114 }, 658c2ecf20Sopenharmony_ci [RC_PROTO_RC6_MCE] = { .name = "rc-6-mce", 668c2ecf20Sopenharmony_ci .scancode_bits = 0xffff7fff, .repeat_period = 114 }, 678c2ecf20Sopenharmony_ci [RC_PROTO_SHARP] = { .name = "sharp", 688c2ecf20Sopenharmony_ci .scancode_bits = 0x1fff, .repeat_period = 125 }, 698c2ecf20Sopenharmony_ci [RC_PROTO_XMP] = { .name = "xmp", .repeat_period = 125 }, 708c2ecf20Sopenharmony_ci [RC_PROTO_CEC] = { .name = "cec", .repeat_period = 0 }, 718c2ecf20Sopenharmony_ci [RC_PROTO_IMON] = { .name = "imon", 728c2ecf20Sopenharmony_ci .scancode_bits = 0x7fffffff, .repeat_period = 114 }, 738c2ecf20Sopenharmony_ci [RC_PROTO_RCMM12] = { .name = "rc-mm-12", 748c2ecf20Sopenharmony_ci .scancode_bits = 0x00000fff, .repeat_period = 114 }, 758c2ecf20Sopenharmony_ci [RC_PROTO_RCMM24] = { .name = "rc-mm-24", 768c2ecf20Sopenharmony_ci .scancode_bits = 0x00ffffff, .repeat_period = 114 }, 778c2ecf20Sopenharmony_ci [RC_PROTO_RCMM32] = { .name = "rc-mm-32", 788c2ecf20Sopenharmony_ci .scancode_bits = 0xffffffff, .repeat_period = 114 }, 798c2ecf20Sopenharmony_ci [RC_PROTO_XBOX_DVD] = { .name = "xbox-dvd", .repeat_period = 64 }, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/* Used to keep track of known keymaps */ 838c2ecf20Sopenharmony_cistatic LIST_HEAD(rc_map_list); 848c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(rc_map_lock); 858c2ecf20Sopenharmony_cistatic struct led_trigger *led_feedback; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* Used to keep track of rc devices */ 888c2ecf20Sopenharmony_cistatic DEFINE_IDA(rc_ida); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct rc_map_list *seek_rc_map(const char *name) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct rc_map_list *map = NULL; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci spin_lock(&rc_map_lock); 958c2ecf20Sopenharmony_ci list_for_each_entry(map, &rc_map_list, list) { 968c2ecf20Sopenharmony_ci if (!strcmp(name, map->map.name)) { 978c2ecf20Sopenharmony_ci spin_unlock(&rc_map_lock); 988c2ecf20Sopenharmony_ci return map; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci spin_unlock(&rc_map_lock); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return NULL; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct rc_map *rc_map_get(const char *name) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci struct rc_map_list *map; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci map = seek_rc_map(name); 1128c2ecf20Sopenharmony_ci#ifdef CONFIG_MODULES 1138c2ecf20Sopenharmony_ci if (!map) { 1148c2ecf20Sopenharmony_ci int rc = request_module("%s", name); 1158c2ecf20Sopenharmony_ci if (rc < 0) { 1168c2ecf20Sopenharmony_ci pr_err("Couldn't load IR keymap %s\n", name); 1178c2ecf20Sopenharmony_ci return NULL; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci msleep(20); /* Give some time for IR to register */ 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci map = seek_rc_map(name); 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci#endif 1248c2ecf20Sopenharmony_ci if (!map) { 1258c2ecf20Sopenharmony_ci pr_err("IR keymap %s not found\n", name); 1268c2ecf20Sopenharmony_ci return NULL; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci printk(KERN_INFO "Registered IR keymap %s\n", map->map.name); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci return &map->map; 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_map_get); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ciint rc_map_register(struct rc_map_list *map) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci spin_lock(&rc_map_lock); 1388c2ecf20Sopenharmony_ci list_add_tail(&map->list, &rc_map_list); 1398c2ecf20Sopenharmony_ci spin_unlock(&rc_map_lock); 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_map_register); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_civoid rc_map_unregister(struct rc_map_list *map) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci spin_lock(&rc_map_lock); 1478c2ecf20Sopenharmony_ci list_del(&map->list); 1488c2ecf20Sopenharmony_ci spin_unlock(&rc_map_lock); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_map_unregister); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic struct rc_map_table empty[] = { 1548c2ecf20Sopenharmony_ci { 0x2a, KEY_COFFEE }, 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic struct rc_map_list empty_map = { 1588c2ecf20Sopenharmony_ci .map = { 1598c2ecf20Sopenharmony_ci .scan = empty, 1608c2ecf20Sopenharmony_ci .size = ARRAY_SIZE(empty), 1618c2ecf20Sopenharmony_ci .rc_proto = RC_PROTO_UNKNOWN, /* Legacy IR type */ 1628c2ecf20Sopenharmony_ci .name = RC_MAP_EMPTY, 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci}; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci/** 1678c2ecf20Sopenharmony_ci * scancode_to_u64() - converts scancode in &struct input_keymap_entry 1688c2ecf20Sopenharmony_ci * @ke: keymap entry containing scancode to be converted. 1698c2ecf20Sopenharmony_ci * @scancode: pointer to the location where converted scancode should 1708c2ecf20Sopenharmony_ci * be stored. 1718c2ecf20Sopenharmony_ci * 1728c2ecf20Sopenharmony_ci * This function is a version of input_scancode_to_scalar specialized for 1738c2ecf20Sopenharmony_ci * rc-core. 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_cistatic int scancode_to_u64(const struct input_keymap_entry *ke, u64 *scancode) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci switch (ke->len) { 1788c2ecf20Sopenharmony_ci case 1: 1798c2ecf20Sopenharmony_ci *scancode = *((u8 *)ke->scancode); 1808c2ecf20Sopenharmony_ci break; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci case 2: 1838c2ecf20Sopenharmony_ci *scancode = *((u16 *)ke->scancode); 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci case 4: 1878c2ecf20Sopenharmony_ci *scancode = *((u32 *)ke->scancode); 1888c2ecf20Sopenharmony_ci break; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci case 8: 1918c2ecf20Sopenharmony_ci *scancode = *((u64 *)ke->scancode); 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci default: 1958c2ecf20Sopenharmony_ci return -EINVAL; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci/** 2028c2ecf20Sopenharmony_ci * ir_create_table() - initializes a scancode table 2038c2ecf20Sopenharmony_ci * @dev: the rc_dev device 2048c2ecf20Sopenharmony_ci * @rc_map: the rc_map to initialize 2058c2ecf20Sopenharmony_ci * @name: name to assign to the table 2068c2ecf20Sopenharmony_ci * @rc_proto: ir type to assign to the new table 2078c2ecf20Sopenharmony_ci * @size: initial size of the table 2088c2ecf20Sopenharmony_ci * 2098c2ecf20Sopenharmony_ci * This routine will initialize the rc_map and will allocate 2108c2ecf20Sopenharmony_ci * memory to hold at least the specified number of elements. 2118c2ecf20Sopenharmony_ci * 2128c2ecf20Sopenharmony_ci * return: zero on success or a negative error code 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic int ir_create_table(struct rc_dev *dev, struct rc_map *rc_map, 2158c2ecf20Sopenharmony_ci const char *name, u64 rc_proto, size_t size) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci rc_map->name = kstrdup(name, GFP_KERNEL); 2188c2ecf20Sopenharmony_ci if (!rc_map->name) 2198c2ecf20Sopenharmony_ci return -ENOMEM; 2208c2ecf20Sopenharmony_ci rc_map->rc_proto = rc_proto; 2218c2ecf20Sopenharmony_ci rc_map->alloc = roundup_pow_of_two(size * sizeof(struct rc_map_table)); 2228c2ecf20Sopenharmony_ci rc_map->size = rc_map->alloc / sizeof(struct rc_map_table); 2238c2ecf20Sopenharmony_ci rc_map->scan = kmalloc(rc_map->alloc, GFP_KERNEL); 2248c2ecf20Sopenharmony_ci if (!rc_map->scan) { 2258c2ecf20Sopenharmony_ci kfree(rc_map->name); 2268c2ecf20Sopenharmony_ci rc_map->name = NULL; 2278c2ecf20Sopenharmony_ci return -ENOMEM; 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Allocated space for %u keycode entries (%u bytes)\n", 2318c2ecf20Sopenharmony_ci rc_map->size, rc_map->alloc); 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/** 2368c2ecf20Sopenharmony_ci * ir_free_table() - frees memory allocated by a scancode table 2378c2ecf20Sopenharmony_ci * @rc_map: the table whose mappings need to be freed 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * This routine will free memory alloctaed for key mappings used by given 2408c2ecf20Sopenharmony_ci * scancode table. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic void ir_free_table(struct rc_map *rc_map) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci rc_map->size = 0; 2458c2ecf20Sopenharmony_ci kfree(rc_map->name); 2468c2ecf20Sopenharmony_ci rc_map->name = NULL; 2478c2ecf20Sopenharmony_ci kfree(rc_map->scan); 2488c2ecf20Sopenharmony_ci rc_map->scan = NULL; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci/** 2528c2ecf20Sopenharmony_ci * ir_resize_table() - resizes a scancode table if necessary 2538c2ecf20Sopenharmony_ci * @dev: the rc_dev device 2548c2ecf20Sopenharmony_ci * @rc_map: the rc_map to resize 2558c2ecf20Sopenharmony_ci * @gfp_flags: gfp flags to use when allocating memory 2568c2ecf20Sopenharmony_ci * 2578c2ecf20Sopenharmony_ci * This routine will shrink the rc_map if it has lots of 2588c2ecf20Sopenharmony_ci * unused entries and grow it if it is full. 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * return: zero on success or a negative error code 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_cistatic int ir_resize_table(struct rc_dev *dev, struct rc_map *rc_map, 2638c2ecf20Sopenharmony_ci gfp_t gfp_flags) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci unsigned int oldalloc = rc_map->alloc; 2668c2ecf20Sopenharmony_ci unsigned int newalloc = oldalloc; 2678c2ecf20Sopenharmony_ci struct rc_map_table *oldscan = rc_map->scan; 2688c2ecf20Sopenharmony_ci struct rc_map_table *newscan; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (rc_map->size == rc_map->len) { 2718c2ecf20Sopenharmony_ci /* All entries in use -> grow keytable */ 2728c2ecf20Sopenharmony_ci if (rc_map->alloc >= IR_TAB_MAX_SIZE) 2738c2ecf20Sopenharmony_ci return -ENOMEM; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci newalloc *= 2; 2768c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Growing table to %u bytes\n", newalloc); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci if ((rc_map->len * 3 < rc_map->size) && (oldalloc > IR_TAB_MIN_SIZE)) { 2808c2ecf20Sopenharmony_ci /* Less than 1/3 of entries in use -> shrink keytable */ 2818c2ecf20Sopenharmony_ci newalloc /= 2; 2828c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Shrinking table to %u bytes\n", newalloc); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (newalloc == oldalloc) 2868c2ecf20Sopenharmony_ci return 0; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci newscan = kmalloc(newalloc, gfp_flags); 2898c2ecf20Sopenharmony_ci if (!newscan) 2908c2ecf20Sopenharmony_ci return -ENOMEM; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci memcpy(newscan, rc_map->scan, rc_map->len * sizeof(struct rc_map_table)); 2938c2ecf20Sopenharmony_ci rc_map->scan = newscan; 2948c2ecf20Sopenharmony_ci rc_map->alloc = newalloc; 2958c2ecf20Sopenharmony_ci rc_map->size = rc_map->alloc / sizeof(struct rc_map_table); 2968c2ecf20Sopenharmony_ci kfree(oldscan); 2978c2ecf20Sopenharmony_ci return 0; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/** 3018c2ecf20Sopenharmony_ci * ir_update_mapping() - set a keycode in the scancode->keycode table 3028c2ecf20Sopenharmony_ci * @dev: the struct rc_dev device descriptor 3038c2ecf20Sopenharmony_ci * @rc_map: scancode table to be adjusted 3048c2ecf20Sopenharmony_ci * @index: index of the mapping that needs to be updated 3058c2ecf20Sopenharmony_ci * @new_keycode: the desired keycode 3068c2ecf20Sopenharmony_ci * 3078c2ecf20Sopenharmony_ci * This routine is used to update scancode->keycode mapping at given 3088c2ecf20Sopenharmony_ci * position. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * return: previous keycode assigned to the mapping 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_cistatic unsigned int ir_update_mapping(struct rc_dev *dev, 3148c2ecf20Sopenharmony_ci struct rc_map *rc_map, 3158c2ecf20Sopenharmony_ci unsigned int index, 3168c2ecf20Sopenharmony_ci unsigned int new_keycode) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci int old_keycode = rc_map->scan[index].keycode; 3198c2ecf20Sopenharmony_ci int i; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Did the user wish to remove the mapping? */ 3228c2ecf20Sopenharmony_ci if (new_keycode == KEY_RESERVED || new_keycode == KEY_UNKNOWN) { 3238c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "#%d: Deleting scan 0x%04llx\n", 3248c2ecf20Sopenharmony_ci index, rc_map->scan[index].scancode); 3258c2ecf20Sopenharmony_ci rc_map->len--; 3268c2ecf20Sopenharmony_ci memmove(&rc_map->scan[index], &rc_map->scan[index+ 1], 3278c2ecf20Sopenharmony_ci (rc_map->len - index) * sizeof(struct rc_map_table)); 3288c2ecf20Sopenharmony_ci } else { 3298c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "#%d: %s scan 0x%04llx with key 0x%04x\n", 3308c2ecf20Sopenharmony_ci index, 3318c2ecf20Sopenharmony_ci old_keycode == KEY_RESERVED ? "New" : "Replacing", 3328c2ecf20Sopenharmony_ci rc_map->scan[index].scancode, new_keycode); 3338c2ecf20Sopenharmony_ci rc_map->scan[index].keycode = new_keycode; 3348c2ecf20Sopenharmony_ci __set_bit(new_keycode, dev->input_dev->keybit); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (old_keycode != KEY_RESERVED) { 3388c2ecf20Sopenharmony_ci /* A previous mapping was updated... */ 3398c2ecf20Sopenharmony_ci __clear_bit(old_keycode, dev->input_dev->keybit); 3408c2ecf20Sopenharmony_ci /* ... but another scancode might use the same keycode */ 3418c2ecf20Sopenharmony_ci for (i = 0; i < rc_map->len; i++) { 3428c2ecf20Sopenharmony_ci if (rc_map->scan[i].keycode == old_keycode) { 3438c2ecf20Sopenharmony_ci __set_bit(old_keycode, dev->input_dev->keybit); 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Possibly shrink the keytable, failure is not a problem */ 3498c2ecf20Sopenharmony_ci ir_resize_table(dev, rc_map, GFP_ATOMIC); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci return old_keycode; 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci/** 3568c2ecf20Sopenharmony_ci * ir_establish_scancode() - set a keycode in the scancode->keycode table 3578c2ecf20Sopenharmony_ci * @dev: the struct rc_dev device descriptor 3588c2ecf20Sopenharmony_ci * @rc_map: scancode table to be searched 3598c2ecf20Sopenharmony_ci * @scancode: the desired scancode 3608c2ecf20Sopenharmony_ci * @resize: controls whether we allowed to resize the table to 3618c2ecf20Sopenharmony_ci * accommodate not yet present scancodes 3628c2ecf20Sopenharmony_ci * 3638c2ecf20Sopenharmony_ci * This routine is used to locate given scancode in rc_map. 3648c2ecf20Sopenharmony_ci * If scancode is not yet present the routine will allocate a new slot 3658c2ecf20Sopenharmony_ci * for it. 3668c2ecf20Sopenharmony_ci * 3678c2ecf20Sopenharmony_ci * return: index of the mapping containing scancode in question 3688c2ecf20Sopenharmony_ci * or -1U in case of failure. 3698c2ecf20Sopenharmony_ci */ 3708c2ecf20Sopenharmony_cistatic unsigned int ir_establish_scancode(struct rc_dev *dev, 3718c2ecf20Sopenharmony_ci struct rc_map *rc_map, 3728c2ecf20Sopenharmony_ci u64 scancode, bool resize) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci unsigned int i; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * Unfortunately, some hardware-based IR decoders don't provide 3788c2ecf20Sopenharmony_ci * all bits for the complete IR code. In general, they provide only 3798c2ecf20Sopenharmony_ci * the command part of the IR code. Yet, as it is possible to replace 3808c2ecf20Sopenharmony_ci * the provided IR with another one, it is needed to allow loading 3818c2ecf20Sopenharmony_ci * IR tables from other remotes. So, we support specifying a mask to 3828c2ecf20Sopenharmony_ci * indicate the valid bits of the scancodes. 3838c2ecf20Sopenharmony_ci */ 3848c2ecf20Sopenharmony_ci if (dev->scancode_mask) 3858c2ecf20Sopenharmony_ci scancode &= dev->scancode_mask; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* First check if we already have a mapping for this ir command */ 3888c2ecf20Sopenharmony_ci for (i = 0; i < rc_map->len; i++) { 3898c2ecf20Sopenharmony_ci if (rc_map->scan[i].scancode == scancode) 3908c2ecf20Sopenharmony_ci return i; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Keytable is sorted from lowest to highest scancode */ 3938c2ecf20Sopenharmony_ci if (rc_map->scan[i].scancode >= scancode) 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci /* No previous mapping found, we might need to grow the table */ 3988c2ecf20Sopenharmony_ci if (rc_map->size == rc_map->len) { 3998c2ecf20Sopenharmony_ci if (!resize || ir_resize_table(dev, rc_map, GFP_ATOMIC)) 4008c2ecf20Sopenharmony_ci return -1U; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* i is the proper index to insert our new keycode */ 4048c2ecf20Sopenharmony_ci if (i < rc_map->len) 4058c2ecf20Sopenharmony_ci memmove(&rc_map->scan[i + 1], &rc_map->scan[i], 4068c2ecf20Sopenharmony_ci (rc_map->len - i) * sizeof(struct rc_map_table)); 4078c2ecf20Sopenharmony_ci rc_map->scan[i].scancode = scancode; 4088c2ecf20Sopenharmony_ci rc_map->scan[i].keycode = KEY_RESERVED; 4098c2ecf20Sopenharmony_ci rc_map->len++; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return i; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci/** 4158c2ecf20Sopenharmony_ci * ir_setkeycode() - set a keycode in the scancode->keycode table 4168c2ecf20Sopenharmony_ci * @idev: the struct input_dev device descriptor 4178c2ecf20Sopenharmony_ci * @ke: Input keymap entry 4188c2ecf20Sopenharmony_ci * @old_keycode: result 4198c2ecf20Sopenharmony_ci * 4208c2ecf20Sopenharmony_ci * This routine is used to handle evdev EVIOCSKEY ioctl. 4218c2ecf20Sopenharmony_ci * 4228c2ecf20Sopenharmony_ci * return: -EINVAL if the keycode could not be inserted, otherwise zero. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_cistatic int ir_setkeycode(struct input_dev *idev, 4258c2ecf20Sopenharmony_ci const struct input_keymap_entry *ke, 4268c2ecf20Sopenharmony_ci unsigned int *old_keycode) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci struct rc_dev *rdev = input_get_drvdata(idev); 4298c2ecf20Sopenharmony_ci struct rc_map *rc_map = &rdev->rc_map; 4308c2ecf20Sopenharmony_ci unsigned int index; 4318c2ecf20Sopenharmony_ci u64 scancode; 4328c2ecf20Sopenharmony_ci int retval = 0; 4338c2ecf20Sopenharmony_ci unsigned long flags; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci spin_lock_irqsave(&rc_map->lock, flags); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (ke->flags & INPUT_KEYMAP_BY_INDEX) { 4388c2ecf20Sopenharmony_ci index = ke->index; 4398c2ecf20Sopenharmony_ci if (index >= rc_map->len) { 4408c2ecf20Sopenharmony_ci retval = -EINVAL; 4418c2ecf20Sopenharmony_ci goto out; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci } else { 4448c2ecf20Sopenharmony_ci retval = scancode_to_u64(ke, &scancode); 4458c2ecf20Sopenharmony_ci if (retval) 4468c2ecf20Sopenharmony_ci goto out; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci index = ir_establish_scancode(rdev, rc_map, scancode, true); 4498c2ecf20Sopenharmony_ci if (index >= rc_map->len) { 4508c2ecf20Sopenharmony_ci retval = -ENOMEM; 4518c2ecf20Sopenharmony_ci goto out; 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci *old_keycode = ir_update_mapping(rdev, rc_map, index, ke->keycode); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ciout: 4588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rc_map->lock, flags); 4598c2ecf20Sopenharmony_ci return retval; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci/** 4638c2ecf20Sopenharmony_ci * ir_setkeytable() - sets several entries in the scancode->keycode table 4648c2ecf20Sopenharmony_ci * @dev: the struct rc_dev device descriptor 4658c2ecf20Sopenharmony_ci * @from: the struct rc_map to copy entries from 4668c2ecf20Sopenharmony_ci * 4678c2ecf20Sopenharmony_ci * This routine is used to handle table initialization. 4688c2ecf20Sopenharmony_ci * 4698c2ecf20Sopenharmony_ci * return: -ENOMEM if all keycodes could not be inserted, otherwise zero. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic int ir_setkeytable(struct rc_dev *dev, const struct rc_map *from) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct rc_map *rc_map = &dev->rc_map; 4748c2ecf20Sopenharmony_ci unsigned int i, index; 4758c2ecf20Sopenharmony_ci int rc; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci rc = ir_create_table(dev, rc_map, from->name, from->rc_proto, 4788c2ecf20Sopenharmony_ci from->size); 4798c2ecf20Sopenharmony_ci if (rc) 4808c2ecf20Sopenharmony_ci return rc; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci for (i = 0; i < from->size; i++) { 4838c2ecf20Sopenharmony_ci index = ir_establish_scancode(dev, rc_map, 4848c2ecf20Sopenharmony_ci from->scan[i].scancode, false); 4858c2ecf20Sopenharmony_ci if (index >= rc_map->len) { 4868c2ecf20Sopenharmony_ci rc = -ENOMEM; 4878c2ecf20Sopenharmony_ci break; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci ir_update_mapping(dev, rc_map, index, 4918c2ecf20Sopenharmony_ci from->scan[i].keycode); 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (rc) 4958c2ecf20Sopenharmony_ci ir_free_table(rc_map); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return rc; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int rc_map_cmp(const void *key, const void *elt) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci const u64 *scancode = key; 5038c2ecf20Sopenharmony_ci const struct rc_map_table *e = elt; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci if (*scancode < e->scancode) 5068c2ecf20Sopenharmony_ci return -1; 5078c2ecf20Sopenharmony_ci else if (*scancode > e->scancode) 5088c2ecf20Sopenharmony_ci return 1; 5098c2ecf20Sopenharmony_ci return 0; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci/** 5138c2ecf20Sopenharmony_ci * ir_lookup_by_scancode() - locate mapping by scancode 5148c2ecf20Sopenharmony_ci * @rc_map: the struct rc_map to search 5158c2ecf20Sopenharmony_ci * @scancode: scancode to look for in the table 5168c2ecf20Sopenharmony_ci * 5178c2ecf20Sopenharmony_ci * This routine performs binary search in RC keykeymap table for 5188c2ecf20Sopenharmony_ci * given scancode. 5198c2ecf20Sopenharmony_ci * 5208c2ecf20Sopenharmony_ci * return: index in the table, -1U if not found 5218c2ecf20Sopenharmony_ci */ 5228c2ecf20Sopenharmony_cistatic unsigned int ir_lookup_by_scancode(const struct rc_map *rc_map, 5238c2ecf20Sopenharmony_ci u64 scancode) 5248c2ecf20Sopenharmony_ci{ 5258c2ecf20Sopenharmony_ci struct rc_map_table *res; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci res = bsearch(&scancode, rc_map->scan, rc_map->len, 5288c2ecf20Sopenharmony_ci sizeof(struct rc_map_table), rc_map_cmp); 5298c2ecf20Sopenharmony_ci if (!res) 5308c2ecf20Sopenharmony_ci return -1U; 5318c2ecf20Sopenharmony_ci else 5328c2ecf20Sopenharmony_ci return res - rc_map->scan; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/** 5368c2ecf20Sopenharmony_ci * ir_getkeycode() - get a keycode from the scancode->keycode table 5378c2ecf20Sopenharmony_ci * @idev: the struct input_dev device descriptor 5388c2ecf20Sopenharmony_ci * @ke: Input keymap entry 5398c2ecf20Sopenharmony_ci * 5408c2ecf20Sopenharmony_ci * This routine is used to handle evdev EVIOCGKEY ioctl. 5418c2ecf20Sopenharmony_ci * 5428c2ecf20Sopenharmony_ci * return: always returns zero. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_cistatic int ir_getkeycode(struct input_dev *idev, 5458c2ecf20Sopenharmony_ci struct input_keymap_entry *ke) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct rc_dev *rdev = input_get_drvdata(idev); 5488c2ecf20Sopenharmony_ci struct rc_map *rc_map = &rdev->rc_map; 5498c2ecf20Sopenharmony_ci struct rc_map_table *entry; 5508c2ecf20Sopenharmony_ci unsigned long flags; 5518c2ecf20Sopenharmony_ci unsigned int index; 5528c2ecf20Sopenharmony_ci u64 scancode; 5538c2ecf20Sopenharmony_ci int retval; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci spin_lock_irqsave(&rc_map->lock, flags); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (ke->flags & INPUT_KEYMAP_BY_INDEX) { 5588c2ecf20Sopenharmony_ci index = ke->index; 5598c2ecf20Sopenharmony_ci } else { 5608c2ecf20Sopenharmony_ci retval = scancode_to_u64(ke, &scancode); 5618c2ecf20Sopenharmony_ci if (retval) 5628c2ecf20Sopenharmony_ci goto out; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci index = ir_lookup_by_scancode(rc_map, scancode); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci if (index < rc_map->len) { 5688c2ecf20Sopenharmony_ci entry = &rc_map->scan[index]; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci ke->index = index; 5718c2ecf20Sopenharmony_ci ke->keycode = entry->keycode; 5728c2ecf20Sopenharmony_ci ke->len = sizeof(entry->scancode); 5738c2ecf20Sopenharmony_ci memcpy(ke->scancode, &entry->scancode, sizeof(entry->scancode)); 5748c2ecf20Sopenharmony_ci } else if (!(ke->flags & INPUT_KEYMAP_BY_INDEX)) { 5758c2ecf20Sopenharmony_ci /* 5768c2ecf20Sopenharmony_ci * We do not really know the valid range of scancodes 5778c2ecf20Sopenharmony_ci * so let's respond with KEY_RESERVED to anything we 5788c2ecf20Sopenharmony_ci * do not have mapping for [yet]. 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci ke->index = index; 5818c2ecf20Sopenharmony_ci ke->keycode = KEY_RESERVED; 5828c2ecf20Sopenharmony_ci } else { 5838c2ecf20Sopenharmony_ci retval = -EINVAL; 5848c2ecf20Sopenharmony_ci goto out; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci retval = 0; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ciout: 5908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rc_map->lock, flags); 5918c2ecf20Sopenharmony_ci return retval; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci/** 5958c2ecf20Sopenharmony_ci * rc_g_keycode_from_table() - gets the keycode that corresponds to a scancode 5968c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 5978c2ecf20Sopenharmony_ci * @scancode: the scancode to look for 5988c2ecf20Sopenharmony_ci * 5998c2ecf20Sopenharmony_ci * This routine is used by drivers which need to convert a scancode to a 6008c2ecf20Sopenharmony_ci * keycode. Normally it should not be used since drivers should have no 6018c2ecf20Sopenharmony_ci * interest in keycodes. 6028c2ecf20Sopenharmony_ci * 6038c2ecf20Sopenharmony_ci * return: the corresponding keycode, or KEY_RESERVED 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_ciu32 rc_g_keycode_from_table(struct rc_dev *dev, u64 scancode) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct rc_map *rc_map = &dev->rc_map; 6088c2ecf20Sopenharmony_ci unsigned int keycode; 6098c2ecf20Sopenharmony_ci unsigned int index; 6108c2ecf20Sopenharmony_ci unsigned long flags; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci spin_lock_irqsave(&rc_map->lock, flags); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci index = ir_lookup_by_scancode(rc_map, scancode); 6158c2ecf20Sopenharmony_ci keycode = index < rc_map->len ? 6168c2ecf20Sopenharmony_ci rc_map->scan[index].keycode : KEY_RESERVED; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&rc_map->lock, flags); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci if (keycode != KEY_RESERVED) 6218c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "%s: scancode 0x%04llx keycode 0x%02x\n", 6228c2ecf20Sopenharmony_ci dev->device_name, scancode, keycode); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return keycode; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_g_keycode_from_table); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/** 6298c2ecf20Sopenharmony_ci * ir_do_keyup() - internal function to signal the release of a keypress 6308c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 6318c2ecf20Sopenharmony_ci * @sync: whether or not to call input_sync 6328c2ecf20Sopenharmony_ci * 6338c2ecf20Sopenharmony_ci * This function is used internally to release a keypress, it must be 6348c2ecf20Sopenharmony_ci * called with keylock held. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_cistatic void ir_do_keyup(struct rc_dev *dev, bool sync) 6378c2ecf20Sopenharmony_ci{ 6388c2ecf20Sopenharmony_ci if (!dev->keypressed) 6398c2ecf20Sopenharmony_ci return; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "keyup key 0x%04x\n", dev->last_keycode); 6428c2ecf20Sopenharmony_ci del_timer(&dev->timer_repeat); 6438c2ecf20Sopenharmony_ci input_report_key(dev->input_dev, dev->last_keycode, 0); 6448c2ecf20Sopenharmony_ci led_trigger_event(led_feedback, LED_OFF); 6458c2ecf20Sopenharmony_ci if (sync) 6468c2ecf20Sopenharmony_ci input_sync(dev->input_dev); 6478c2ecf20Sopenharmony_ci dev->keypressed = false; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci/** 6518c2ecf20Sopenharmony_ci * rc_keyup() - signals the release of a keypress 6528c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 6538c2ecf20Sopenharmony_ci * 6548c2ecf20Sopenharmony_ci * This routine is used to signal that a key has been released on the 6558c2ecf20Sopenharmony_ci * remote control. 6568c2ecf20Sopenharmony_ci */ 6578c2ecf20Sopenharmony_civoid rc_keyup(struct rc_dev *dev) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci unsigned long flags; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->keylock, flags); 6628c2ecf20Sopenharmony_ci ir_do_keyup(dev, true); 6638c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->keylock, flags); 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_keyup); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci/** 6688c2ecf20Sopenharmony_ci * ir_timer_keyup() - generates a keyup event after a timeout 6698c2ecf20Sopenharmony_ci * 6708c2ecf20Sopenharmony_ci * @t: a pointer to the struct timer_list 6718c2ecf20Sopenharmony_ci * 6728c2ecf20Sopenharmony_ci * This routine will generate a keyup event some time after a keydown event 6738c2ecf20Sopenharmony_ci * is generated when no further activity has been detected. 6748c2ecf20Sopenharmony_ci */ 6758c2ecf20Sopenharmony_cistatic void ir_timer_keyup(struct timer_list *t) 6768c2ecf20Sopenharmony_ci{ 6778c2ecf20Sopenharmony_ci struct rc_dev *dev = from_timer(dev, t, timer_keyup); 6788c2ecf20Sopenharmony_ci unsigned long flags; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* 6818c2ecf20Sopenharmony_ci * ir->keyup_jiffies is used to prevent a race condition if a 6828c2ecf20Sopenharmony_ci * hardware interrupt occurs at this point and the keyup timer 6838c2ecf20Sopenharmony_ci * event is moved further into the future as a result. 6848c2ecf20Sopenharmony_ci * 6858c2ecf20Sopenharmony_ci * The timer will then be reactivated and this function called 6868c2ecf20Sopenharmony_ci * again in the future. We need to exit gracefully in that case 6878c2ecf20Sopenharmony_ci * to allow the input subsystem to do its auto-repeat magic or 6888c2ecf20Sopenharmony_ci * a keyup event might follow immediately after the keydown. 6898c2ecf20Sopenharmony_ci */ 6908c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->keylock, flags); 6918c2ecf20Sopenharmony_ci if (time_is_before_eq_jiffies(dev->keyup_jiffies)) 6928c2ecf20Sopenharmony_ci ir_do_keyup(dev, true); 6938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->keylock, flags); 6948c2ecf20Sopenharmony_ci} 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/** 6978c2ecf20Sopenharmony_ci * ir_timer_repeat() - generates a repeat event after a timeout 6988c2ecf20Sopenharmony_ci * 6998c2ecf20Sopenharmony_ci * @t: a pointer to the struct timer_list 7008c2ecf20Sopenharmony_ci * 7018c2ecf20Sopenharmony_ci * This routine will generate a soft repeat event every REP_PERIOD 7028c2ecf20Sopenharmony_ci * milliseconds. 7038c2ecf20Sopenharmony_ci */ 7048c2ecf20Sopenharmony_cistatic void ir_timer_repeat(struct timer_list *t) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci struct rc_dev *dev = from_timer(dev, t, timer_repeat); 7078c2ecf20Sopenharmony_ci struct input_dev *input = dev->input_dev; 7088c2ecf20Sopenharmony_ci unsigned long flags; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->keylock, flags); 7118c2ecf20Sopenharmony_ci if (dev->keypressed) { 7128c2ecf20Sopenharmony_ci input_event(input, EV_KEY, dev->last_keycode, 2); 7138c2ecf20Sopenharmony_ci input_sync(input); 7148c2ecf20Sopenharmony_ci if (input->rep[REP_PERIOD]) 7158c2ecf20Sopenharmony_ci mod_timer(&dev->timer_repeat, jiffies + 7168c2ecf20Sopenharmony_ci msecs_to_jiffies(input->rep[REP_PERIOD])); 7178c2ecf20Sopenharmony_ci } 7188c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->keylock, flags); 7198c2ecf20Sopenharmony_ci} 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_cistatic unsigned int repeat_period(int protocol) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci if (protocol >= ARRAY_SIZE(protocols)) 7248c2ecf20Sopenharmony_ci return 100; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci return protocols[protocol].repeat_period; 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci/** 7308c2ecf20Sopenharmony_ci * rc_repeat() - signals that a key is still pressed 7318c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 7328c2ecf20Sopenharmony_ci * 7338c2ecf20Sopenharmony_ci * This routine is used by IR decoders when a repeat message which does 7348c2ecf20Sopenharmony_ci * not include the necessary bits to reproduce the scancode has been 7358c2ecf20Sopenharmony_ci * received. 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_civoid rc_repeat(struct rc_dev *dev) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci unsigned long flags; 7408c2ecf20Sopenharmony_ci unsigned int timeout = usecs_to_jiffies(dev->timeout) + 7418c2ecf20Sopenharmony_ci msecs_to_jiffies(repeat_period(dev->last_protocol)); 7428c2ecf20Sopenharmony_ci struct lirc_scancode sc = { 7438c2ecf20Sopenharmony_ci .scancode = dev->last_scancode, .rc_proto = dev->last_protocol, 7448c2ecf20Sopenharmony_ci .keycode = dev->keypressed ? dev->last_keycode : KEY_RESERVED, 7458c2ecf20Sopenharmony_ci .flags = LIRC_SCANCODE_FLAG_REPEAT | 7468c2ecf20Sopenharmony_ci (dev->last_toggle ? LIRC_SCANCODE_FLAG_TOGGLE : 0) 7478c2ecf20Sopenharmony_ci }; 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (dev->allowed_protocols != RC_PROTO_BIT_CEC) 7508c2ecf20Sopenharmony_ci lirc_scancode_event(dev, &sc); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->keylock, flags); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (dev->last_scancode <= U32_MAX) { 7558c2ecf20Sopenharmony_ci input_event(dev->input_dev, EV_MSC, MSC_SCAN, 7568c2ecf20Sopenharmony_ci dev->last_scancode); 7578c2ecf20Sopenharmony_ci input_sync(dev->input_dev); 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if (dev->keypressed) { 7618c2ecf20Sopenharmony_ci dev->keyup_jiffies = jiffies + timeout; 7628c2ecf20Sopenharmony_ci mod_timer(&dev->timer_keyup, dev->keyup_jiffies); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->keylock, flags); 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_repeat); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/** 7708c2ecf20Sopenharmony_ci * ir_do_keydown() - internal function to process a keypress 7718c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 7728c2ecf20Sopenharmony_ci * @protocol: the protocol of the keypress 7738c2ecf20Sopenharmony_ci * @scancode: the scancode of the keypress 7748c2ecf20Sopenharmony_ci * @keycode: the keycode of the keypress 7758c2ecf20Sopenharmony_ci * @toggle: the toggle value of the keypress 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * This function is used internally to register a keypress, it must be 7788c2ecf20Sopenharmony_ci * called with keylock held. 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_cistatic void ir_do_keydown(struct rc_dev *dev, enum rc_proto protocol, 7818c2ecf20Sopenharmony_ci u64 scancode, u32 keycode, u8 toggle) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci bool new_event = (!dev->keypressed || 7848c2ecf20Sopenharmony_ci dev->last_protocol != protocol || 7858c2ecf20Sopenharmony_ci dev->last_scancode != scancode || 7868c2ecf20Sopenharmony_ci dev->last_toggle != toggle); 7878c2ecf20Sopenharmony_ci struct lirc_scancode sc = { 7888c2ecf20Sopenharmony_ci .scancode = scancode, .rc_proto = protocol, 7898c2ecf20Sopenharmony_ci .flags = toggle ? LIRC_SCANCODE_FLAG_TOGGLE : 0, 7908c2ecf20Sopenharmony_ci .keycode = keycode 7918c2ecf20Sopenharmony_ci }; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (dev->allowed_protocols != RC_PROTO_BIT_CEC) 7948c2ecf20Sopenharmony_ci lirc_scancode_event(dev, &sc); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci if (new_event && dev->keypressed) 7978c2ecf20Sopenharmony_ci ir_do_keyup(dev, false); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (scancode <= U32_MAX) 8008c2ecf20Sopenharmony_ci input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci dev->last_protocol = protocol; 8038c2ecf20Sopenharmony_ci dev->last_scancode = scancode; 8048c2ecf20Sopenharmony_ci dev->last_toggle = toggle; 8058c2ecf20Sopenharmony_ci dev->last_keycode = keycode; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (new_event && keycode != KEY_RESERVED) { 8088c2ecf20Sopenharmony_ci /* Register a keypress */ 8098c2ecf20Sopenharmony_ci dev->keypressed = true; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08llx\n", 8128c2ecf20Sopenharmony_ci dev->device_name, keycode, protocol, scancode); 8138c2ecf20Sopenharmony_ci input_report_key(dev->input_dev, keycode, 1); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci led_trigger_event(led_feedback, LED_FULL); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* 8198c2ecf20Sopenharmony_ci * For CEC, start sending repeat messages as soon as the first 8208c2ecf20Sopenharmony_ci * repeated message is sent, as long as REP_DELAY = 0 and REP_PERIOD 8218c2ecf20Sopenharmony_ci * is non-zero. Otherwise, the input layer will generate repeat 8228c2ecf20Sopenharmony_ci * messages. 8238c2ecf20Sopenharmony_ci */ 8248c2ecf20Sopenharmony_ci if (!new_event && keycode != KEY_RESERVED && 8258c2ecf20Sopenharmony_ci dev->allowed_protocols == RC_PROTO_BIT_CEC && 8268c2ecf20Sopenharmony_ci !timer_pending(&dev->timer_repeat) && 8278c2ecf20Sopenharmony_ci dev->input_dev->rep[REP_PERIOD] && 8288c2ecf20Sopenharmony_ci !dev->input_dev->rep[REP_DELAY]) { 8298c2ecf20Sopenharmony_ci input_event(dev->input_dev, EV_KEY, keycode, 2); 8308c2ecf20Sopenharmony_ci mod_timer(&dev->timer_repeat, jiffies + 8318c2ecf20Sopenharmony_ci msecs_to_jiffies(dev->input_dev->rep[REP_PERIOD])); 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci input_sync(dev->input_dev); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/** 8388c2ecf20Sopenharmony_ci * rc_keydown() - generates input event for a key press 8398c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 8408c2ecf20Sopenharmony_ci * @protocol: the protocol for the keypress 8418c2ecf20Sopenharmony_ci * @scancode: the scancode for the keypress 8428c2ecf20Sopenharmony_ci * @toggle: the toggle value (protocol dependent, if the protocol doesn't 8438c2ecf20Sopenharmony_ci * support toggle values, this should be set to zero) 8448c2ecf20Sopenharmony_ci * 8458c2ecf20Sopenharmony_ci * This routine is used to signal that a key has been pressed on the 8468c2ecf20Sopenharmony_ci * remote control. 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_civoid rc_keydown(struct rc_dev *dev, enum rc_proto protocol, u64 scancode, 8498c2ecf20Sopenharmony_ci u8 toggle) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci unsigned long flags; 8528c2ecf20Sopenharmony_ci u32 keycode = rc_g_keycode_from_table(dev, scancode); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->keylock, flags); 8558c2ecf20Sopenharmony_ci ir_do_keydown(dev, protocol, scancode, keycode, toggle); 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci if (dev->keypressed) { 8588c2ecf20Sopenharmony_ci dev->keyup_jiffies = jiffies + usecs_to_jiffies(dev->timeout) + 8598c2ecf20Sopenharmony_ci msecs_to_jiffies(repeat_period(protocol)); 8608c2ecf20Sopenharmony_ci mod_timer(&dev->timer_keyup, dev->keyup_jiffies); 8618c2ecf20Sopenharmony_ci } 8628c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->keylock, flags); 8638c2ecf20Sopenharmony_ci} 8648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_keydown); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci/** 8678c2ecf20Sopenharmony_ci * rc_keydown_notimeout() - generates input event for a key press without 8688c2ecf20Sopenharmony_ci * an automatic keyup event at a later time 8698c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 8708c2ecf20Sopenharmony_ci * @protocol: the protocol for the keypress 8718c2ecf20Sopenharmony_ci * @scancode: the scancode for the keypress 8728c2ecf20Sopenharmony_ci * @toggle: the toggle value (protocol dependent, if the protocol doesn't 8738c2ecf20Sopenharmony_ci * support toggle values, this should be set to zero) 8748c2ecf20Sopenharmony_ci * 8758c2ecf20Sopenharmony_ci * This routine is used to signal that a key has been pressed on the 8768c2ecf20Sopenharmony_ci * remote control. The driver must manually call rc_keyup() at a later stage. 8778c2ecf20Sopenharmony_ci */ 8788c2ecf20Sopenharmony_civoid rc_keydown_notimeout(struct rc_dev *dev, enum rc_proto protocol, 8798c2ecf20Sopenharmony_ci u64 scancode, u8 toggle) 8808c2ecf20Sopenharmony_ci{ 8818c2ecf20Sopenharmony_ci unsigned long flags; 8828c2ecf20Sopenharmony_ci u32 keycode = rc_g_keycode_from_table(dev, scancode); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->keylock, flags); 8858c2ecf20Sopenharmony_ci ir_do_keydown(dev, protocol, scancode, keycode, toggle); 8868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->keylock, flags); 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_keydown_notimeout); 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/** 8918c2ecf20Sopenharmony_ci * rc_validate_scancode() - checks that a scancode is valid for a protocol. 8928c2ecf20Sopenharmony_ci * For nec, it should do the opposite of ir_nec_bytes_to_scancode() 8938c2ecf20Sopenharmony_ci * @proto: protocol 8948c2ecf20Sopenharmony_ci * @scancode: scancode 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_cibool rc_validate_scancode(enum rc_proto proto, u32 scancode) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci switch (proto) { 8998c2ecf20Sopenharmony_ci /* 9008c2ecf20Sopenharmony_ci * NECX has a 16-bit address; if the lower 8 bits match the upper 9018c2ecf20Sopenharmony_ci * 8 bits inverted, then the address would match regular nec. 9028c2ecf20Sopenharmony_ci */ 9038c2ecf20Sopenharmony_ci case RC_PROTO_NECX: 9048c2ecf20Sopenharmony_ci if ((((scancode >> 16) ^ ~(scancode >> 8)) & 0xff) == 0) 9058c2ecf20Sopenharmony_ci return false; 9068c2ecf20Sopenharmony_ci break; 9078c2ecf20Sopenharmony_ci /* 9088c2ecf20Sopenharmony_ci * NEC32 has a 16 bit address and 16 bit command. If the lower 8 bits 9098c2ecf20Sopenharmony_ci * of the command match the upper 8 bits inverted, then it would 9108c2ecf20Sopenharmony_ci * be either NEC or NECX. 9118c2ecf20Sopenharmony_ci */ 9128c2ecf20Sopenharmony_ci case RC_PROTO_NEC32: 9138c2ecf20Sopenharmony_ci if ((((scancode >> 8) ^ ~scancode) & 0xff) == 0) 9148c2ecf20Sopenharmony_ci return false; 9158c2ecf20Sopenharmony_ci break; 9168c2ecf20Sopenharmony_ci /* 9178c2ecf20Sopenharmony_ci * If the customer code (top 32-bit) is 0x800f, it is MCE else it 9188c2ecf20Sopenharmony_ci * is regular mode-6a 32 bit 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ci case RC_PROTO_RC6_MCE: 9218c2ecf20Sopenharmony_ci if ((scancode & 0xffff0000) != 0x800f0000) 9228c2ecf20Sopenharmony_ci return false; 9238c2ecf20Sopenharmony_ci break; 9248c2ecf20Sopenharmony_ci case RC_PROTO_RC6_6A_32: 9258c2ecf20Sopenharmony_ci if ((scancode & 0xffff0000) == 0x800f0000) 9268c2ecf20Sopenharmony_ci return false; 9278c2ecf20Sopenharmony_ci break; 9288c2ecf20Sopenharmony_ci default: 9298c2ecf20Sopenharmony_ci break; 9308c2ecf20Sopenharmony_ci } 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci return true; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/** 9368c2ecf20Sopenharmony_ci * rc_validate_filter() - checks that the scancode and mask are valid and 9378c2ecf20Sopenharmony_ci * provides sensible defaults 9388c2ecf20Sopenharmony_ci * @dev: the struct rc_dev descriptor of the device 9398c2ecf20Sopenharmony_ci * @filter: the scancode and mask 9408c2ecf20Sopenharmony_ci * 9418c2ecf20Sopenharmony_ci * return: 0 or -EINVAL if the filter is not valid 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_cistatic int rc_validate_filter(struct rc_dev *dev, 9448c2ecf20Sopenharmony_ci struct rc_scancode_filter *filter) 9458c2ecf20Sopenharmony_ci{ 9468c2ecf20Sopenharmony_ci u32 mask, s = filter->data; 9478c2ecf20Sopenharmony_ci enum rc_proto protocol = dev->wakeup_protocol; 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci if (protocol >= ARRAY_SIZE(protocols)) 9508c2ecf20Sopenharmony_ci return -EINVAL; 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci mask = protocols[protocol].scancode_bits; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (!rc_validate_scancode(protocol, s)) 9558c2ecf20Sopenharmony_ci return -EINVAL; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci filter->data &= mask; 9588c2ecf20Sopenharmony_ci filter->mask &= mask; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* 9618c2ecf20Sopenharmony_ci * If we have to raw encode the IR for wakeup, we cannot have a mask 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_ci if (dev->encode_wakeup && filter->mask != 0 && filter->mask != mask) 9648c2ecf20Sopenharmony_ci return -EINVAL; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci return 0; 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ciint rc_open(struct rc_dev *rdev) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci int rval = 0; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci if (!rdev) 9748c2ecf20Sopenharmony_ci return -EINVAL; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci mutex_lock(&rdev->lock); 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (!rdev->registered) { 9798c2ecf20Sopenharmony_ci rval = -ENODEV; 9808c2ecf20Sopenharmony_ci } else { 9818c2ecf20Sopenharmony_ci if (!rdev->users++ && rdev->open) 9828c2ecf20Sopenharmony_ci rval = rdev->open(rdev); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (rval) 9858c2ecf20Sopenharmony_ci rdev->users--; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci mutex_unlock(&rdev->lock); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci return rval; 9918c2ecf20Sopenharmony_ci} 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_cistatic int ir_open(struct input_dev *idev) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci struct rc_dev *rdev = input_get_drvdata(idev); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci return rc_open(rdev); 9988c2ecf20Sopenharmony_ci} 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_civoid rc_close(struct rc_dev *rdev) 10018c2ecf20Sopenharmony_ci{ 10028c2ecf20Sopenharmony_ci if (rdev) { 10038c2ecf20Sopenharmony_ci mutex_lock(&rdev->lock); 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci if (!--rdev->users && rdev->close && rdev->registered) 10068c2ecf20Sopenharmony_ci rdev->close(rdev); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci mutex_unlock(&rdev->lock); 10098c2ecf20Sopenharmony_ci } 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic void ir_close(struct input_dev *idev) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct rc_dev *rdev = input_get_drvdata(idev); 10158c2ecf20Sopenharmony_ci rc_close(rdev); 10168c2ecf20Sopenharmony_ci} 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci/* class for /sys/class/rc */ 10198c2ecf20Sopenharmony_cistatic char *rc_devnode(struct device *dev, umode_t *mode) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "rc/%s", dev_name(dev)); 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cistatic struct class rc_class = { 10258c2ecf20Sopenharmony_ci .name = "rc", 10268c2ecf20Sopenharmony_ci .devnode = rc_devnode, 10278c2ecf20Sopenharmony_ci}; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci/* 10308c2ecf20Sopenharmony_ci * These are the protocol textual descriptions that are 10318c2ecf20Sopenharmony_ci * used by the sysfs protocols file. Note that the order 10328c2ecf20Sopenharmony_ci * of the entries is relevant. 10338c2ecf20Sopenharmony_ci */ 10348c2ecf20Sopenharmony_cistatic const struct { 10358c2ecf20Sopenharmony_ci u64 type; 10368c2ecf20Sopenharmony_ci const char *name; 10378c2ecf20Sopenharmony_ci const char *module_name; 10388c2ecf20Sopenharmony_ci} proto_names[] = { 10398c2ecf20Sopenharmony_ci { RC_PROTO_BIT_NONE, "none", NULL }, 10408c2ecf20Sopenharmony_ci { RC_PROTO_BIT_OTHER, "other", NULL }, 10418c2ecf20Sopenharmony_ci { RC_PROTO_BIT_UNKNOWN, "unknown", NULL }, 10428c2ecf20Sopenharmony_ci { RC_PROTO_BIT_RC5 | 10438c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC5X_20, "rc-5", "ir-rc5-decoder" }, 10448c2ecf20Sopenharmony_ci { RC_PROTO_BIT_NEC | 10458c2ecf20Sopenharmony_ci RC_PROTO_BIT_NECX | 10468c2ecf20Sopenharmony_ci RC_PROTO_BIT_NEC32, "nec", "ir-nec-decoder" }, 10478c2ecf20Sopenharmony_ci { RC_PROTO_BIT_RC6_0 | 10488c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC6_6A_20 | 10498c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC6_6A_24 | 10508c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC6_6A_32 | 10518c2ecf20Sopenharmony_ci RC_PROTO_BIT_RC6_MCE, "rc-6", "ir-rc6-decoder" }, 10528c2ecf20Sopenharmony_ci { RC_PROTO_BIT_JVC, "jvc", "ir-jvc-decoder" }, 10538c2ecf20Sopenharmony_ci { RC_PROTO_BIT_SONY12 | 10548c2ecf20Sopenharmony_ci RC_PROTO_BIT_SONY15 | 10558c2ecf20Sopenharmony_ci RC_PROTO_BIT_SONY20, "sony", "ir-sony-decoder" }, 10568c2ecf20Sopenharmony_ci { RC_PROTO_BIT_RC5_SZ, "rc-5-sz", "ir-rc5-decoder" }, 10578c2ecf20Sopenharmony_ci { RC_PROTO_BIT_SANYO, "sanyo", "ir-sanyo-decoder" }, 10588c2ecf20Sopenharmony_ci { RC_PROTO_BIT_SHARP, "sharp", "ir-sharp-decoder" }, 10598c2ecf20Sopenharmony_ci { RC_PROTO_BIT_MCIR2_KBD | 10608c2ecf20Sopenharmony_ci RC_PROTO_BIT_MCIR2_MSE, "mce_kbd", "ir-mce_kbd-decoder" }, 10618c2ecf20Sopenharmony_ci { RC_PROTO_BIT_XMP, "xmp", "ir-xmp-decoder" }, 10628c2ecf20Sopenharmony_ci { RC_PROTO_BIT_CEC, "cec", NULL }, 10638c2ecf20Sopenharmony_ci { RC_PROTO_BIT_IMON, "imon", "ir-imon-decoder" }, 10648c2ecf20Sopenharmony_ci { RC_PROTO_BIT_RCMM12 | 10658c2ecf20Sopenharmony_ci RC_PROTO_BIT_RCMM24 | 10668c2ecf20Sopenharmony_ci RC_PROTO_BIT_RCMM32, "rc-mm", "ir-rcmm-decoder" }, 10678c2ecf20Sopenharmony_ci { RC_PROTO_BIT_XBOX_DVD, "xbox-dvd", NULL }, 10688c2ecf20Sopenharmony_ci}; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci/** 10718c2ecf20Sopenharmony_ci * struct rc_filter_attribute - Device attribute relating to a filter type. 10728c2ecf20Sopenharmony_ci * @attr: Device attribute. 10738c2ecf20Sopenharmony_ci * @type: Filter type. 10748c2ecf20Sopenharmony_ci * @mask: false for filter value, true for filter mask. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistruct rc_filter_attribute { 10778c2ecf20Sopenharmony_ci struct device_attribute attr; 10788c2ecf20Sopenharmony_ci enum rc_filter_type type; 10798c2ecf20Sopenharmony_ci bool mask; 10808c2ecf20Sopenharmony_ci}; 10818c2ecf20Sopenharmony_ci#define to_rc_filter_attr(a) container_of(a, struct rc_filter_attribute, attr) 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci#define RC_FILTER_ATTR(_name, _mode, _show, _store, _type, _mask) \ 10848c2ecf20Sopenharmony_ci struct rc_filter_attribute dev_attr_##_name = { \ 10858c2ecf20Sopenharmony_ci .attr = __ATTR(_name, _mode, _show, _store), \ 10868c2ecf20Sopenharmony_ci .type = (_type), \ 10878c2ecf20Sopenharmony_ci .mask = (_mask), \ 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci/** 10918c2ecf20Sopenharmony_ci * show_protocols() - shows the current IR protocol(s) 10928c2ecf20Sopenharmony_ci * @device: the device descriptor 10938c2ecf20Sopenharmony_ci * @mattr: the device attribute struct 10948c2ecf20Sopenharmony_ci * @buf: a pointer to the output buffer 10958c2ecf20Sopenharmony_ci * 10968c2ecf20Sopenharmony_ci * This routine is a callback routine for input read the IR protocol type(s). 10978c2ecf20Sopenharmony_ci * it is triggered by reading /sys/class/rc/rc?/protocols. 10988c2ecf20Sopenharmony_ci * It returns the protocol names of supported protocols. 10998c2ecf20Sopenharmony_ci * Enabled protocols are printed in brackets. 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * dev->lock is taken to guard against races between 11028c2ecf20Sopenharmony_ci * store_protocols and show_protocols. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_cistatic ssize_t show_protocols(struct device *device, 11058c2ecf20Sopenharmony_ci struct device_attribute *mattr, char *buf) 11068c2ecf20Sopenharmony_ci{ 11078c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 11088c2ecf20Sopenharmony_ci u64 allowed, enabled; 11098c2ecf20Sopenharmony_ci char *tmp = buf; 11108c2ecf20Sopenharmony_ci int i; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci enabled = dev->enabled_protocols; 11158c2ecf20Sopenharmony_ci allowed = dev->allowed_protocols; 11168c2ecf20Sopenharmony_ci if (dev->raw && !allowed) 11178c2ecf20Sopenharmony_ci allowed = ir_raw_get_allowed_protocols(); 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "%s: allowed - 0x%llx, enabled - 0x%llx\n", 11228c2ecf20Sopenharmony_ci __func__, (long long)allowed, (long long)enabled); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(proto_names); i++) { 11258c2ecf20Sopenharmony_ci if (allowed & enabled & proto_names[i].type) 11268c2ecf20Sopenharmony_ci tmp += sprintf(tmp, "[%s] ", proto_names[i].name); 11278c2ecf20Sopenharmony_ci else if (allowed & proto_names[i].type) 11288c2ecf20Sopenharmony_ci tmp += sprintf(tmp, "%s ", proto_names[i].name); 11298c2ecf20Sopenharmony_ci 11308c2ecf20Sopenharmony_ci if (allowed & proto_names[i].type) 11318c2ecf20Sopenharmony_ci allowed &= ~proto_names[i].type; 11328c2ecf20Sopenharmony_ci } 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci#ifdef CONFIG_LIRC 11358c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_IR_RAW) 11368c2ecf20Sopenharmony_ci tmp += sprintf(tmp, "[lirc] "); 11378c2ecf20Sopenharmony_ci#endif 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (tmp != buf) 11408c2ecf20Sopenharmony_ci tmp--; 11418c2ecf20Sopenharmony_ci *tmp = '\n'; 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_ci return tmp + 1 - buf; 11448c2ecf20Sopenharmony_ci} 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci/** 11478c2ecf20Sopenharmony_ci * parse_protocol_change() - parses a protocol change request 11488c2ecf20Sopenharmony_ci * @dev: rc_dev device 11498c2ecf20Sopenharmony_ci * @protocols: pointer to the bitmask of current protocols 11508c2ecf20Sopenharmony_ci * @buf: pointer to the buffer with a list of changes 11518c2ecf20Sopenharmony_ci * 11528c2ecf20Sopenharmony_ci * Writing "+proto" will add a protocol to the protocol mask. 11538c2ecf20Sopenharmony_ci * Writing "-proto" will remove a protocol from protocol mask. 11548c2ecf20Sopenharmony_ci * Writing "proto" will enable only "proto". 11558c2ecf20Sopenharmony_ci * Writing "none" will disable all protocols. 11568c2ecf20Sopenharmony_ci * Returns the number of changes performed or a negative error code. 11578c2ecf20Sopenharmony_ci */ 11588c2ecf20Sopenharmony_cistatic int parse_protocol_change(struct rc_dev *dev, u64 *protocols, 11598c2ecf20Sopenharmony_ci const char *buf) 11608c2ecf20Sopenharmony_ci{ 11618c2ecf20Sopenharmony_ci const char *tmp; 11628c2ecf20Sopenharmony_ci unsigned count = 0; 11638c2ecf20Sopenharmony_ci bool enable, disable; 11648c2ecf20Sopenharmony_ci u64 mask; 11658c2ecf20Sopenharmony_ci int i; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci while ((tmp = strsep((char **)&buf, " \n")) != NULL) { 11688c2ecf20Sopenharmony_ci if (!*tmp) 11698c2ecf20Sopenharmony_ci break; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (*tmp == '+') { 11728c2ecf20Sopenharmony_ci enable = true; 11738c2ecf20Sopenharmony_ci disable = false; 11748c2ecf20Sopenharmony_ci tmp++; 11758c2ecf20Sopenharmony_ci } else if (*tmp == '-') { 11768c2ecf20Sopenharmony_ci enable = false; 11778c2ecf20Sopenharmony_ci disable = true; 11788c2ecf20Sopenharmony_ci tmp++; 11798c2ecf20Sopenharmony_ci } else { 11808c2ecf20Sopenharmony_ci enable = false; 11818c2ecf20Sopenharmony_ci disable = false; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(proto_names); i++) { 11858c2ecf20Sopenharmony_ci if (!strcasecmp(tmp, proto_names[i].name)) { 11868c2ecf20Sopenharmony_ci mask = proto_names[i].type; 11878c2ecf20Sopenharmony_ci break; 11888c2ecf20Sopenharmony_ci } 11898c2ecf20Sopenharmony_ci } 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(proto_names)) { 11928c2ecf20Sopenharmony_ci if (!strcasecmp(tmp, "lirc")) 11938c2ecf20Sopenharmony_ci mask = 0; 11948c2ecf20Sopenharmony_ci else { 11958c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Unknown protocol: '%s'\n", 11968c2ecf20Sopenharmony_ci tmp); 11978c2ecf20Sopenharmony_ci return -EINVAL; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci count++; 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci if (enable) 12048c2ecf20Sopenharmony_ci *protocols |= mask; 12058c2ecf20Sopenharmony_ci else if (disable) 12068c2ecf20Sopenharmony_ci *protocols &= ~mask; 12078c2ecf20Sopenharmony_ci else 12088c2ecf20Sopenharmony_ci *protocols = mask; 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci if (!count) { 12128c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Protocol not specified\n"); 12138c2ecf20Sopenharmony_ci return -EINVAL; 12148c2ecf20Sopenharmony_ci } 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci return count; 12178c2ecf20Sopenharmony_ci} 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_civoid ir_raw_load_modules(u64 *protocols) 12208c2ecf20Sopenharmony_ci{ 12218c2ecf20Sopenharmony_ci u64 available; 12228c2ecf20Sopenharmony_ci int i, ret; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(proto_names); i++) { 12258c2ecf20Sopenharmony_ci if (proto_names[i].type == RC_PROTO_BIT_NONE || 12268c2ecf20Sopenharmony_ci proto_names[i].type & (RC_PROTO_BIT_OTHER | 12278c2ecf20Sopenharmony_ci RC_PROTO_BIT_UNKNOWN)) 12288c2ecf20Sopenharmony_ci continue; 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci available = ir_raw_get_allowed_protocols(); 12318c2ecf20Sopenharmony_ci if (!(*protocols & proto_names[i].type & ~available)) 12328c2ecf20Sopenharmony_ci continue; 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci if (!proto_names[i].module_name) { 12358c2ecf20Sopenharmony_ci pr_err("Can't enable IR protocol %s\n", 12368c2ecf20Sopenharmony_ci proto_names[i].name); 12378c2ecf20Sopenharmony_ci *protocols &= ~proto_names[i].type; 12388c2ecf20Sopenharmony_ci continue; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci ret = request_module("%s", proto_names[i].module_name); 12428c2ecf20Sopenharmony_ci if (ret < 0) { 12438c2ecf20Sopenharmony_ci pr_err("Couldn't load IR protocol module %s\n", 12448c2ecf20Sopenharmony_ci proto_names[i].module_name); 12458c2ecf20Sopenharmony_ci *protocols &= ~proto_names[i].type; 12468c2ecf20Sopenharmony_ci continue; 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci msleep(20); 12498c2ecf20Sopenharmony_ci available = ir_raw_get_allowed_protocols(); 12508c2ecf20Sopenharmony_ci if (!(*protocols & proto_names[i].type & ~available)) 12518c2ecf20Sopenharmony_ci continue; 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci pr_err("Loaded IR protocol module %s, but protocol %s still not available\n", 12548c2ecf20Sopenharmony_ci proto_names[i].module_name, 12558c2ecf20Sopenharmony_ci proto_names[i].name); 12568c2ecf20Sopenharmony_ci *protocols &= ~proto_names[i].type; 12578c2ecf20Sopenharmony_ci } 12588c2ecf20Sopenharmony_ci} 12598c2ecf20Sopenharmony_ci 12608c2ecf20Sopenharmony_ci/** 12618c2ecf20Sopenharmony_ci * store_protocols() - changes the current/wakeup IR protocol(s) 12628c2ecf20Sopenharmony_ci * @device: the device descriptor 12638c2ecf20Sopenharmony_ci * @mattr: the device attribute struct 12648c2ecf20Sopenharmony_ci * @buf: a pointer to the input buffer 12658c2ecf20Sopenharmony_ci * @len: length of the input buffer 12668c2ecf20Sopenharmony_ci * 12678c2ecf20Sopenharmony_ci * This routine is for changing the IR protocol type. 12688c2ecf20Sopenharmony_ci * It is triggered by writing to /sys/class/rc/rc?/[wakeup_]protocols. 12698c2ecf20Sopenharmony_ci * See parse_protocol_change() for the valid commands. 12708c2ecf20Sopenharmony_ci * Returns @len on success or a negative error code. 12718c2ecf20Sopenharmony_ci * 12728c2ecf20Sopenharmony_ci * dev->lock is taken to guard against races between 12738c2ecf20Sopenharmony_ci * store_protocols and show_protocols. 12748c2ecf20Sopenharmony_ci */ 12758c2ecf20Sopenharmony_cistatic ssize_t store_protocols(struct device *device, 12768c2ecf20Sopenharmony_ci struct device_attribute *mattr, 12778c2ecf20Sopenharmony_ci const char *buf, size_t len) 12788c2ecf20Sopenharmony_ci{ 12798c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 12808c2ecf20Sopenharmony_ci u64 *current_protocols; 12818c2ecf20Sopenharmony_ci struct rc_scancode_filter *filter; 12828c2ecf20Sopenharmony_ci u64 old_protocols, new_protocols; 12838c2ecf20Sopenharmony_ci ssize_t rc; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Normal protocol change requested\n"); 12868c2ecf20Sopenharmony_ci current_protocols = &dev->enabled_protocols; 12878c2ecf20Sopenharmony_ci filter = &dev->scancode_filter; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (!dev->change_protocol) { 12908c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Protocol switching not supported\n"); 12918c2ecf20Sopenharmony_ci return -EINVAL; 12928c2ecf20Sopenharmony_ci } 12938c2ecf20Sopenharmony_ci 12948c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 12958c2ecf20Sopenharmony_ci if (!dev->registered) { 12968c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 12978c2ecf20Sopenharmony_ci return -ENODEV; 12988c2ecf20Sopenharmony_ci } 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci old_protocols = *current_protocols; 13018c2ecf20Sopenharmony_ci new_protocols = old_protocols; 13028c2ecf20Sopenharmony_ci rc = parse_protocol_change(dev, &new_protocols, buf); 13038c2ecf20Sopenharmony_ci if (rc < 0) 13048c2ecf20Sopenharmony_ci goto out; 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_IR_RAW) 13078c2ecf20Sopenharmony_ci ir_raw_load_modules(&new_protocols); 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci rc = dev->change_protocol(dev, &new_protocols); 13108c2ecf20Sopenharmony_ci if (rc < 0) { 13118c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Error setting protocols to 0x%llx\n", 13128c2ecf20Sopenharmony_ci (long long)new_protocols); 13138c2ecf20Sopenharmony_ci goto out; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci if (new_protocols != old_protocols) { 13178c2ecf20Sopenharmony_ci *current_protocols = new_protocols; 13188c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Protocols changed to 0x%llx\n", 13198c2ecf20Sopenharmony_ci (long long)new_protocols); 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci /* 13238c2ecf20Sopenharmony_ci * If a protocol change was attempted the filter may need updating, even 13248c2ecf20Sopenharmony_ci * if the actual protocol mask hasn't changed (since the driver may have 13258c2ecf20Sopenharmony_ci * cleared the filter). 13268c2ecf20Sopenharmony_ci * Try setting the same filter with the new protocol (if any). 13278c2ecf20Sopenharmony_ci * Fall back to clearing the filter. 13288c2ecf20Sopenharmony_ci */ 13298c2ecf20Sopenharmony_ci if (dev->s_filter && filter->mask) { 13308c2ecf20Sopenharmony_ci if (new_protocols) 13318c2ecf20Sopenharmony_ci rc = dev->s_filter(dev, filter); 13328c2ecf20Sopenharmony_ci else 13338c2ecf20Sopenharmony_ci rc = -1; 13348c2ecf20Sopenharmony_ci 13358c2ecf20Sopenharmony_ci if (rc < 0) { 13368c2ecf20Sopenharmony_ci filter->data = 0; 13378c2ecf20Sopenharmony_ci filter->mask = 0; 13388c2ecf20Sopenharmony_ci dev->s_filter(dev, filter); 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci } 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci rc = len; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ciout: 13458c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 13468c2ecf20Sopenharmony_ci return rc; 13478c2ecf20Sopenharmony_ci} 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci/** 13508c2ecf20Sopenharmony_ci * show_filter() - shows the current scancode filter value or mask 13518c2ecf20Sopenharmony_ci * @device: the device descriptor 13528c2ecf20Sopenharmony_ci * @attr: the device attribute struct 13538c2ecf20Sopenharmony_ci * @buf: a pointer to the output buffer 13548c2ecf20Sopenharmony_ci * 13558c2ecf20Sopenharmony_ci * This routine is a callback routine to read a scancode filter value or mask. 13568c2ecf20Sopenharmony_ci * It is triggered by reading /sys/class/rc/rc?/[wakeup_]filter[_mask]. 13578c2ecf20Sopenharmony_ci * It prints the current scancode filter value or mask of the appropriate filter 13588c2ecf20Sopenharmony_ci * type in hexadecimal into @buf and returns the size of the buffer. 13598c2ecf20Sopenharmony_ci * 13608c2ecf20Sopenharmony_ci * Bits of the filter value corresponding to set bits in the filter mask are 13618c2ecf20Sopenharmony_ci * compared against input scancodes and non-matching scancodes are discarded. 13628c2ecf20Sopenharmony_ci * 13638c2ecf20Sopenharmony_ci * dev->lock is taken to guard against races between 13648c2ecf20Sopenharmony_ci * store_filter and show_filter. 13658c2ecf20Sopenharmony_ci */ 13668c2ecf20Sopenharmony_cistatic ssize_t show_filter(struct device *device, 13678c2ecf20Sopenharmony_ci struct device_attribute *attr, 13688c2ecf20Sopenharmony_ci char *buf) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 13718c2ecf20Sopenharmony_ci struct rc_filter_attribute *fattr = to_rc_filter_attr(attr); 13728c2ecf20Sopenharmony_ci struct rc_scancode_filter *filter; 13738c2ecf20Sopenharmony_ci u32 val; 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (fattr->type == RC_FILTER_NORMAL) 13788c2ecf20Sopenharmony_ci filter = &dev->scancode_filter; 13798c2ecf20Sopenharmony_ci else 13808c2ecf20Sopenharmony_ci filter = &dev->scancode_wakeup_filter; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci if (fattr->mask) 13838c2ecf20Sopenharmony_ci val = filter->mask; 13848c2ecf20Sopenharmony_ci else 13858c2ecf20Sopenharmony_ci val = filter->data; 13868c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci return sprintf(buf, "%#x\n", val); 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci/** 13928c2ecf20Sopenharmony_ci * store_filter() - changes the scancode filter value 13938c2ecf20Sopenharmony_ci * @device: the device descriptor 13948c2ecf20Sopenharmony_ci * @attr: the device attribute struct 13958c2ecf20Sopenharmony_ci * @buf: a pointer to the input buffer 13968c2ecf20Sopenharmony_ci * @len: length of the input buffer 13978c2ecf20Sopenharmony_ci * 13988c2ecf20Sopenharmony_ci * This routine is for changing a scancode filter value or mask. 13998c2ecf20Sopenharmony_ci * It is triggered by writing to /sys/class/rc/rc?/[wakeup_]filter[_mask]. 14008c2ecf20Sopenharmony_ci * Returns -EINVAL if an invalid filter value for the current protocol was 14018c2ecf20Sopenharmony_ci * specified or if scancode filtering is not supported by the driver, otherwise 14028c2ecf20Sopenharmony_ci * returns @len. 14038c2ecf20Sopenharmony_ci * 14048c2ecf20Sopenharmony_ci * Bits of the filter value corresponding to set bits in the filter mask are 14058c2ecf20Sopenharmony_ci * compared against input scancodes and non-matching scancodes are discarded. 14068c2ecf20Sopenharmony_ci * 14078c2ecf20Sopenharmony_ci * dev->lock is taken to guard against races between 14088c2ecf20Sopenharmony_ci * store_filter and show_filter. 14098c2ecf20Sopenharmony_ci */ 14108c2ecf20Sopenharmony_cistatic ssize_t store_filter(struct device *device, 14118c2ecf20Sopenharmony_ci struct device_attribute *attr, 14128c2ecf20Sopenharmony_ci const char *buf, size_t len) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 14158c2ecf20Sopenharmony_ci struct rc_filter_attribute *fattr = to_rc_filter_attr(attr); 14168c2ecf20Sopenharmony_ci struct rc_scancode_filter new_filter, *filter; 14178c2ecf20Sopenharmony_ci int ret; 14188c2ecf20Sopenharmony_ci unsigned long val; 14198c2ecf20Sopenharmony_ci int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); 14208c2ecf20Sopenharmony_ci 14218c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &val); 14228c2ecf20Sopenharmony_ci if (ret < 0) 14238c2ecf20Sopenharmony_ci return ret; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci if (fattr->type == RC_FILTER_NORMAL) { 14268c2ecf20Sopenharmony_ci set_filter = dev->s_filter; 14278c2ecf20Sopenharmony_ci filter = &dev->scancode_filter; 14288c2ecf20Sopenharmony_ci } else { 14298c2ecf20Sopenharmony_ci set_filter = dev->s_wakeup_filter; 14308c2ecf20Sopenharmony_ci filter = &dev->scancode_wakeup_filter; 14318c2ecf20Sopenharmony_ci } 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci if (!set_filter) 14348c2ecf20Sopenharmony_ci return -EINVAL; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 14378c2ecf20Sopenharmony_ci if (!dev->registered) { 14388c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 14398c2ecf20Sopenharmony_ci return -ENODEV; 14408c2ecf20Sopenharmony_ci } 14418c2ecf20Sopenharmony_ci 14428c2ecf20Sopenharmony_ci new_filter = *filter; 14438c2ecf20Sopenharmony_ci if (fattr->mask) 14448c2ecf20Sopenharmony_ci new_filter.mask = val; 14458c2ecf20Sopenharmony_ci else 14468c2ecf20Sopenharmony_ci new_filter.data = val; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_ci if (fattr->type == RC_FILTER_WAKEUP) { 14498c2ecf20Sopenharmony_ci /* 14508c2ecf20Sopenharmony_ci * Refuse to set a filter unless a protocol is enabled 14518c2ecf20Sopenharmony_ci * and the filter is valid for that protocol 14528c2ecf20Sopenharmony_ci */ 14538c2ecf20Sopenharmony_ci if (dev->wakeup_protocol != RC_PROTO_UNKNOWN) 14548c2ecf20Sopenharmony_ci ret = rc_validate_filter(dev, &new_filter); 14558c2ecf20Sopenharmony_ci else 14568c2ecf20Sopenharmony_ci ret = -EINVAL; 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci if (ret != 0) 14598c2ecf20Sopenharmony_ci goto unlock; 14608c2ecf20Sopenharmony_ci } 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci if (fattr->type == RC_FILTER_NORMAL && !dev->enabled_protocols && 14638c2ecf20Sopenharmony_ci val) { 14648c2ecf20Sopenharmony_ci /* refuse to set a filter unless a protocol is enabled */ 14658c2ecf20Sopenharmony_ci ret = -EINVAL; 14668c2ecf20Sopenharmony_ci goto unlock; 14678c2ecf20Sopenharmony_ci } 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci ret = set_filter(dev, &new_filter); 14708c2ecf20Sopenharmony_ci if (ret < 0) 14718c2ecf20Sopenharmony_ci goto unlock; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci *filter = new_filter; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ciunlock: 14768c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 14778c2ecf20Sopenharmony_ci return (ret < 0) ? ret : len; 14788c2ecf20Sopenharmony_ci} 14798c2ecf20Sopenharmony_ci 14808c2ecf20Sopenharmony_ci/** 14818c2ecf20Sopenharmony_ci * show_wakeup_protocols() - shows the wakeup IR protocol 14828c2ecf20Sopenharmony_ci * @device: the device descriptor 14838c2ecf20Sopenharmony_ci * @mattr: the device attribute struct 14848c2ecf20Sopenharmony_ci * @buf: a pointer to the output buffer 14858c2ecf20Sopenharmony_ci * 14868c2ecf20Sopenharmony_ci * This routine is a callback routine for input read the IR protocol type(s). 14878c2ecf20Sopenharmony_ci * it is triggered by reading /sys/class/rc/rc?/wakeup_protocols. 14888c2ecf20Sopenharmony_ci * It returns the protocol names of supported protocols. 14898c2ecf20Sopenharmony_ci * The enabled protocols are printed in brackets. 14908c2ecf20Sopenharmony_ci * 14918c2ecf20Sopenharmony_ci * dev->lock is taken to guard against races between 14928c2ecf20Sopenharmony_ci * store_wakeup_protocols and show_wakeup_protocols. 14938c2ecf20Sopenharmony_ci */ 14948c2ecf20Sopenharmony_cistatic ssize_t show_wakeup_protocols(struct device *device, 14958c2ecf20Sopenharmony_ci struct device_attribute *mattr, 14968c2ecf20Sopenharmony_ci char *buf) 14978c2ecf20Sopenharmony_ci{ 14988c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 14998c2ecf20Sopenharmony_ci u64 allowed; 15008c2ecf20Sopenharmony_ci enum rc_proto enabled; 15018c2ecf20Sopenharmony_ci char *tmp = buf; 15028c2ecf20Sopenharmony_ci int i; 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci allowed = dev->allowed_wakeup_protocols; 15078c2ecf20Sopenharmony_ci enabled = dev->wakeup_protocol; 15088c2ecf20Sopenharmony_ci 15098c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "%s: allowed - 0x%llx, enabled - %d\n", 15128c2ecf20Sopenharmony_ci __func__, (long long)allowed, enabled); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(protocols); i++) { 15158c2ecf20Sopenharmony_ci if (allowed & (1ULL << i)) { 15168c2ecf20Sopenharmony_ci if (i == enabled) 15178c2ecf20Sopenharmony_ci tmp += sprintf(tmp, "[%s] ", protocols[i].name); 15188c2ecf20Sopenharmony_ci else 15198c2ecf20Sopenharmony_ci tmp += sprintf(tmp, "%s ", protocols[i].name); 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci } 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci if (tmp != buf) 15248c2ecf20Sopenharmony_ci tmp--; 15258c2ecf20Sopenharmony_ci *tmp = '\n'; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_ci return tmp + 1 - buf; 15288c2ecf20Sopenharmony_ci} 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci/** 15318c2ecf20Sopenharmony_ci * store_wakeup_protocols() - changes the wakeup IR protocol(s) 15328c2ecf20Sopenharmony_ci * @device: the device descriptor 15338c2ecf20Sopenharmony_ci * @mattr: the device attribute struct 15348c2ecf20Sopenharmony_ci * @buf: a pointer to the input buffer 15358c2ecf20Sopenharmony_ci * @len: length of the input buffer 15368c2ecf20Sopenharmony_ci * 15378c2ecf20Sopenharmony_ci * This routine is for changing the IR protocol type. 15388c2ecf20Sopenharmony_ci * It is triggered by writing to /sys/class/rc/rc?/wakeup_protocols. 15398c2ecf20Sopenharmony_ci * Returns @len on success or a negative error code. 15408c2ecf20Sopenharmony_ci * 15418c2ecf20Sopenharmony_ci * dev->lock is taken to guard against races between 15428c2ecf20Sopenharmony_ci * store_wakeup_protocols and show_wakeup_protocols. 15438c2ecf20Sopenharmony_ci */ 15448c2ecf20Sopenharmony_cistatic ssize_t store_wakeup_protocols(struct device *device, 15458c2ecf20Sopenharmony_ci struct device_attribute *mattr, 15468c2ecf20Sopenharmony_ci const char *buf, size_t len) 15478c2ecf20Sopenharmony_ci{ 15488c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 15498c2ecf20Sopenharmony_ci enum rc_proto protocol = RC_PROTO_UNKNOWN; 15508c2ecf20Sopenharmony_ci ssize_t rc; 15518c2ecf20Sopenharmony_ci u64 allowed; 15528c2ecf20Sopenharmony_ci int i; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 15558c2ecf20Sopenharmony_ci if (!dev->registered) { 15568c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 15578c2ecf20Sopenharmony_ci return -ENODEV; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci allowed = dev->allowed_wakeup_protocols; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci if (!sysfs_streq(buf, "none")) { 15638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(protocols); i++) { 15648c2ecf20Sopenharmony_ci if ((allowed & (1ULL << i)) && 15658c2ecf20Sopenharmony_ci sysfs_streq(buf, protocols[i].name)) { 15668c2ecf20Sopenharmony_ci protocol = i; 15678c2ecf20Sopenharmony_ci break; 15688c2ecf20Sopenharmony_ci } 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(protocols)) { 15728c2ecf20Sopenharmony_ci rc = -EINVAL; 15738c2ecf20Sopenharmony_ci goto out; 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci if (dev->encode_wakeup) { 15778c2ecf20Sopenharmony_ci u64 mask = 1ULL << protocol; 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci ir_raw_load_modules(&mask); 15808c2ecf20Sopenharmony_ci if (!mask) { 15818c2ecf20Sopenharmony_ci rc = -EINVAL; 15828c2ecf20Sopenharmony_ci goto out; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci } 15858c2ecf20Sopenharmony_ci } 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci if (dev->wakeup_protocol != protocol) { 15888c2ecf20Sopenharmony_ci dev->wakeup_protocol = protocol; 15898c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Wakeup protocol changed to %d\n", protocol); 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci if (protocol == RC_PROTO_RC6_MCE) 15928c2ecf20Sopenharmony_ci dev->scancode_wakeup_filter.data = 0x800f0000; 15938c2ecf20Sopenharmony_ci else 15948c2ecf20Sopenharmony_ci dev->scancode_wakeup_filter.data = 0; 15958c2ecf20Sopenharmony_ci dev->scancode_wakeup_filter.mask = 0; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci rc = dev->s_wakeup_filter(dev, &dev->scancode_wakeup_filter); 15988c2ecf20Sopenharmony_ci if (rc == 0) 15998c2ecf20Sopenharmony_ci rc = len; 16008c2ecf20Sopenharmony_ci } else { 16018c2ecf20Sopenharmony_ci rc = len; 16028c2ecf20Sopenharmony_ci } 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ciout: 16058c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 16068c2ecf20Sopenharmony_ci return rc; 16078c2ecf20Sopenharmony_ci} 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_cistatic void rc_dev_release(struct device *device) 16108c2ecf20Sopenharmony_ci{ 16118c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci kfree(dev); 16148c2ecf20Sopenharmony_ci} 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_cistatic int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env) 16178c2ecf20Sopenharmony_ci{ 16188c2ecf20Sopenharmony_ci struct rc_dev *dev = to_rc_dev(device); 16198c2ecf20Sopenharmony_ci int ret = 0; 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 16228c2ecf20Sopenharmony_ci 16238c2ecf20Sopenharmony_ci if (!dev->registered) 16248c2ecf20Sopenharmony_ci ret = -ENODEV; 16258c2ecf20Sopenharmony_ci if (ret == 0 && dev->rc_map.name) 16268c2ecf20Sopenharmony_ci ret = add_uevent_var(env, "NAME=%s", dev->rc_map.name); 16278c2ecf20Sopenharmony_ci if (ret == 0 && dev->driver_name) 16288c2ecf20Sopenharmony_ci ret = add_uevent_var(env, "DRV_NAME=%s", dev->driver_name); 16298c2ecf20Sopenharmony_ci if (ret == 0 && dev->device_name) 16308c2ecf20Sopenharmony_ci ret = add_uevent_var(env, "DEV_NAME=%s", dev->device_name); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci return ret; 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci/* 16388c2ecf20Sopenharmony_ci * Static device attribute struct with the sysfs attributes for IR's 16398c2ecf20Sopenharmony_ci */ 16408c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_ro_protocols = 16418c2ecf20Sopenharmony_ci__ATTR(protocols, 0444, show_protocols, NULL); 16428c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_rw_protocols = 16438c2ecf20Sopenharmony_ci__ATTR(protocols, 0644, show_protocols, store_protocols); 16448c2ecf20Sopenharmony_cistatic DEVICE_ATTR(wakeup_protocols, 0644, show_wakeup_protocols, 16458c2ecf20Sopenharmony_ci store_wakeup_protocols); 16468c2ecf20Sopenharmony_cistatic RC_FILTER_ATTR(filter, S_IRUGO|S_IWUSR, 16478c2ecf20Sopenharmony_ci show_filter, store_filter, RC_FILTER_NORMAL, false); 16488c2ecf20Sopenharmony_cistatic RC_FILTER_ATTR(filter_mask, S_IRUGO|S_IWUSR, 16498c2ecf20Sopenharmony_ci show_filter, store_filter, RC_FILTER_NORMAL, true); 16508c2ecf20Sopenharmony_cistatic RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR, 16518c2ecf20Sopenharmony_ci show_filter, store_filter, RC_FILTER_WAKEUP, false); 16528c2ecf20Sopenharmony_cistatic RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR, 16538c2ecf20Sopenharmony_ci show_filter, store_filter, RC_FILTER_WAKEUP, true); 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_cistatic struct attribute *rc_dev_rw_protocol_attrs[] = { 16568c2ecf20Sopenharmony_ci &dev_attr_rw_protocols.attr, 16578c2ecf20Sopenharmony_ci NULL, 16588c2ecf20Sopenharmony_ci}; 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_cistatic const struct attribute_group rc_dev_rw_protocol_attr_grp = { 16618c2ecf20Sopenharmony_ci .attrs = rc_dev_rw_protocol_attrs, 16628c2ecf20Sopenharmony_ci}; 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_cistatic struct attribute *rc_dev_ro_protocol_attrs[] = { 16658c2ecf20Sopenharmony_ci &dev_attr_ro_protocols.attr, 16668c2ecf20Sopenharmony_ci NULL, 16678c2ecf20Sopenharmony_ci}; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_cistatic const struct attribute_group rc_dev_ro_protocol_attr_grp = { 16708c2ecf20Sopenharmony_ci .attrs = rc_dev_ro_protocol_attrs, 16718c2ecf20Sopenharmony_ci}; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_cistatic struct attribute *rc_dev_filter_attrs[] = { 16748c2ecf20Sopenharmony_ci &dev_attr_filter.attr.attr, 16758c2ecf20Sopenharmony_ci &dev_attr_filter_mask.attr.attr, 16768c2ecf20Sopenharmony_ci NULL, 16778c2ecf20Sopenharmony_ci}; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_cistatic const struct attribute_group rc_dev_filter_attr_grp = { 16808c2ecf20Sopenharmony_ci .attrs = rc_dev_filter_attrs, 16818c2ecf20Sopenharmony_ci}; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_cistatic struct attribute *rc_dev_wakeup_filter_attrs[] = { 16848c2ecf20Sopenharmony_ci &dev_attr_wakeup_filter.attr.attr, 16858c2ecf20Sopenharmony_ci &dev_attr_wakeup_filter_mask.attr.attr, 16868c2ecf20Sopenharmony_ci &dev_attr_wakeup_protocols.attr, 16878c2ecf20Sopenharmony_ci NULL, 16888c2ecf20Sopenharmony_ci}; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_cistatic const struct attribute_group rc_dev_wakeup_filter_attr_grp = { 16918c2ecf20Sopenharmony_ci .attrs = rc_dev_wakeup_filter_attrs, 16928c2ecf20Sopenharmony_ci}; 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_cistatic const struct device_type rc_dev_type = { 16958c2ecf20Sopenharmony_ci .release = rc_dev_release, 16968c2ecf20Sopenharmony_ci .uevent = rc_dev_uevent, 16978c2ecf20Sopenharmony_ci}; 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_cistruct rc_dev *rc_allocate_device(enum rc_driver_type type) 17008c2ecf20Sopenharmony_ci{ 17018c2ecf20Sopenharmony_ci struct rc_dev *dev; 17028c2ecf20Sopenharmony_ci 17038c2ecf20Sopenharmony_ci dev = kzalloc(sizeof(*dev), GFP_KERNEL); 17048c2ecf20Sopenharmony_ci if (!dev) 17058c2ecf20Sopenharmony_ci return NULL; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci if (type != RC_DRIVER_IR_RAW_TX) { 17088c2ecf20Sopenharmony_ci dev->input_dev = input_allocate_device(); 17098c2ecf20Sopenharmony_ci if (!dev->input_dev) { 17108c2ecf20Sopenharmony_ci kfree(dev); 17118c2ecf20Sopenharmony_ci return NULL; 17128c2ecf20Sopenharmony_ci } 17138c2ecf20Sopenharmony_ci 17148c2ecf20Sopenharmony_ci dev->input_dev->getkeycode = ir_getkeycode; 17158c2ecf20Sopenharmony_ci dev->input_dev->setkeycode = ir_setkeycode; 17168c2ecf20Sopenharmony_ci input_set_drvdata(dev->input_dev, dev); 17178c2ecf20Sopenharmony_ci 17188c2ecf20Sopenharmony_ci dev->timeout = IR_DEFAULT_TIMEOUT; 17198c2ecf20Sopenharmony_ci timer_setup(&dev->timer_keyup, ir_timer_keyup, 0); 17208c2ecf20Sopenharmony_ci timer_setup(&dev->timer_repeat, ir_timer_repeat, 0); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci spin_lock_init(&dev->rc_map.lock); 17238c2ecf20Sopenharmony_ci spin_lock_init(&dev->keylock); 17248c2ecf20Sopenharmony_ci } 17258c2ecf20Sopenharmony_ci mutex_init(&dev->lock); 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci dev->dev.type = &rc_dev_type; 17288c2ecf20Sopenharmony_ci dev->dev.class = &rc_class; 17298c2ecf20Sopenharmony_ci device_initialize(&dev->dev); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci dev->driver_type = type; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 17348c2ecf20Sopenharmony_ci return dev; 17358c2ecf20Sopenharmony_ci} 17368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_allocate_device); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_civoid rc_free_device(struct rc_dev *dev) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci if (!dev) 17418c2ecf20Sopenharmony_ci return; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci input_free_device(dev->input_dev); 17448c2ecf20Sopenharmony_ci 17458c2ecf20Sopenharmony_ci put_device(&dev->dev); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci /* kfree(dev) will be called by the callback function 17488c2ecf20Sopenharmony_ci rc_dev_release() */ 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 17518c2ecf20Sopenharmony_ci} 17528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_free_device); 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_cistatic void devm_rc_alloc_release(struct device *dev, void *res) 17558c2ecf20Sopenharmony_ci{ 17568c2ecf20Sopenharmony_ci rc_free_device(*(struct rc_dev **)res); 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_cistruct rc_dev *devm_rc_allocate_device(struct device *dev, 17608c2ecf20Sopenharmony_ci enum rc_driver_type type) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci struct rc_dev **dr, *rc; 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci dr = devres_alloc(devm_rc_alloc_release, sizeof(*dr), GFP_KERNEL); 17658c2ecf20Sopenharmony_ci if (!dr) 17668c2ecf20Sopenharmony_ci return NULL; 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_ci rc = rc_allocate_device(type); 17698c2ecf20Sopenharmony_ci if (!rc) { 17708c2ecf20Sopenharmony_ci devres_free(dr); 17718c2ecf20Sopenharmony_ci return NULL; 17728c2ecf20Sopenharmony_ci } 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci rc->dev.parent = dev; 17758c2ecf20Sopenharmony_ci rc->managed_alloc = true; 17768c2ecf20Sopenharmony_ci *dr = rc; 17778c2ecf20Sopenharmony_ci devres_add(dev, dr); 17788c2ecf20Sopenharmony_ci 17798c2ecf20Sopenharmony_ci return rc; 17808c2ecf20Sopenharmony_ci} 17818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_rc_allocate_device); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_cistatic int rc_prepare_rx_device(struct rc_dev *dev) 17848c2ecf20Sopenharmony_ci{ 17858c2ecf20Sopenharmony_ci int rc; 17868c2ecf20Sopenharmony_ci struct rc_map *rc_map; 17878c2ecf20Sopenharmony_ci u64 rc_proto; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci if (!dev->map_name) 17908c2ecf20Sopenharmony_ci return -EINVAL; 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci rc_map = rc_map_get(dev->map_name); 17938c2ecf20Sopenharmony_ci if (!rc_map) 17948c2ecf20Sopenharmony_ci rc_map = rc_map_get(RC_MAP_EMPTY); 17958c2ecf20Sopenharmony_ci if (!rc_map || !rc_map->scan || rc_map->size == 0) 17968c2ecf20Sopenharmony_ci return -EINVAL; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci rc = ir_setkeytable(dev, rc_map); 17998c2ecf20Sopenharmony_ci if (rc) 18008c2ecf20Sopenharmony_ci return rc; 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci rc_proto = BIT_ULL(rc_map->rc_proto); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_SCANCODE && !dev->change_protocol) 18058c2ecf20Sopenharmony_ci dev->enabled_protocols = dev->allowed_protocols; 18068c2ecf20Sopenharmony_ci 18078c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_IR_RAW) 18088c2ecf20Sopenharmony_ci ir_raw_load_modules(&rc_proto); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if (dev->change_protocol) { 18118c2ecf20Sopenharmony_ci rc = dev->change_protocol(dev, &rc_proto); 18128c2ecf20Sopenharmony_ci if (rc < 0) 18138c2ecf20Sopenharmony_ci goto out_table; 18148c2ecf20Sopenharmony_ci dev->enabled_protocols = rc_proto; 18158c2ecf20Sopenharmony_ci } 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci /* Keyboard events */ 18188c2ecf20Sopenharmony_ci set_bit(EV_KEY, dev->input_dev->evbit); 18198c2ecf20Sopenharmony_ci set_bit(EV_REP, dev->input_dev->evbit); 18208c2ecf20Sopenharmony_ci set_bit(EV_MSC, dev->input_dev->evbit); 18218c2ecf20Sopenharmony_ci set_bit(MSC_SCAN, dev->input_dev->mscbit); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci /* Pointer/mouse events */ 18248c2ecf20Sopenharmony_ci set_bit(INPUT_PROP_POINTING_STICK, dev->input_dev->propbit); 18258c2ecf20Sopenharmony_ci set_bit(EV_REL, dev->input_dev->evbit); 18268c2ecf20Sopenharmony_ci set_bit(REL_X, dev->input_dev->relbit); 18278c2ecf20Sopenharmony_ci set_bit(REL_Y, dev->input_dev->relbit); 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci if (dev->open) 18308c2ecf20Sopenharmony_ci dev->input_dev->open = ir_open; 18318c2ecf20Sopenharmony_ci if (dev->close) 18328c2ecf20Sopenharmony_ci dev->input_dev->close = ir_close; 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci dev->input_dev->dev.parent = &dev->dev; 18358c2ecf20Sopenharmony_ci memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id)); 18368c2ecf20Sopenharmony_ci dev->input_dev->phys = dev->input_phys; 18378c2ecf20Sopenharmony_ci dev->input_dev->name = dev->device_name; 18388c2ecf20Sopenharmony_ci 18398c2ecf20Sopenharmony_ci return 0; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ciout_table: 18428c2ecf20Sopenharmony_ci ir_free_table(&dev->rc_map); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci return rc; 18458c2ecf20Sopenharmony_ci} 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_cistatic int rc_setup_rx_device(struct rc_dev *dev) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci int rc; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci /* rc_open will be called here */ 18528c2ecf20Sopenharmony_ci rc = input_register_device(dev->input_dev); 18538c2ecf20Sopenharmony_ci if (rc) 18548c2ecf20Sopenharmony_ci return rc; 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci /* 18578c2ecf20Sopenharmony_ci * Default delay of 250ms is too short for some protocols, especially 18588c2ecf20Sopenharmony_ci * since the timeout is currently set to 250ms. Increase it to 500ms, 18598c2ecf20Sopenharmony_ci * to avoid wrong repetition of the keycodes. Note that this must be 18608c2ecf20Sopenharmony_ci * set after the call to input_register_device(). 18618c2ecf20Sopenharmony_ci */ 18628c2ecf20Sopenharmony_ci if (dev->allowed_protocols == RC_PROTO_BIT_CEC) 18638c2ecf20Sopenharmony_ci dev->input_dev->rep[REP_DELAY] = 0; 18648c2ecf20Sopenharmony_ci else 18658c2ecf20Sopenharmony_ci dev->input_dev->rep[REP_DELAY] = 500; 18668c2ecf20Sopenharmony_ci 18678c2ecf20Sopenharmony_ci /* 18688c2ecf20Sopenharmony_ci * As a repeat event on protocols like RC-5 and NEC take as long as 18698c2ecf20Sopenharmony_ci * 110/114ms, using 33ms as a repeat period is not the right thing 18708c2ecf20Sopenharmony_ci * to do. 18718c2ecf20Sopenharmony_ci */ 18728c2ecf20Sopenharmony_ci dev->input_dev->rep[REP_PERIOD] = 125; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci return 0; 18758c2ecf20Sopenharmony_ci} 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_cistatic void rc_free_rx_device(struct rc_dev *dev) 18788c2ecf20Sopenharmony_ci{ 18798c2ecf20Sopenharmony_ci if (!dev) 18808c2ecf20Sopenharmony_ci return; 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci if (dev->input_dev) { 18838c2ecf20Sopenharmony_ci input_unregister_device(dev->input_dev); 18848c2ecf20Sopenharmony_ci dev->input_dev = NULL; 18858c2ecf20Sopenharmony_ci } 18868c2ecf20Sopenharmony_ci 18878c2ecf20Sopenharmony_ci ir_free_table(&dev->rc_map); 18888c2ecf20Sopenharmony_ci} 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ciint rc_register_device(struct rc_dev *dev) 18918c2ecf20Sopenharmony_ci{ 18928c2ecf20Sopenharmony_ci const char *path; 18938c2ecf20Sopenharmony_ci int attr = 0; 18948c2ecf20Sopenharmony_ci int minor; 18958c2ecf20Sopenharmony_ci int rc; 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci if (!dev) 18988c2ecf20Sopenharmony_ci return -EINVAL; 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_ci minor = ida_simple_get(&rc_ida, 0, RC_DEV_MAX, GFP_KERNEL); 19018c2ecf20Sopenharmony_ci if (minor < 0) 19028c2ecf20Sopenharmony_ci return minor; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci dev->minor = minor; 19058c2ecf20Sopenharmony_ci dev_set_name(&dev->dev, "rc%u", dev->minor); 19068c2ecf20Sopenharmony_ci dev_set_drvdata(&dev->dev, dev); 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci dev->dev.groups = dev->sysfs_groups; 19098c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_SCANCODE && !dev->change_protocol) 19108c2ecf20Sopenharmony_ci dev->sysfs_groups[attr++] = &rc_dev_ro_protocol_attr_grp; 19118c2ecf20Sopenharmony_ci else if (dev->driver_type != RC_DRIVER_IR_RAW_TX) 19128c2ecf20Sopenharmony_ci dev->sysfs_groups[attr++] = &rc_dev_rw_protocol_attr_grp; 19138c2ecf20Sopenharmony_ci if (dev->s_filter) 19148c2ecf20Sopenharmony_ci dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp; 19158c2ecf20Sopenharmony_ci if (dev->s_wakeup_filter) 19168c2ecf20Sopenharmony_ci dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp; 19178c2ecf20Sopenharmony_ci dev->sysfs_groups[attr++] = NULL; 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_IR_RAW) { 19208c2ecf20Sopenharmony_ci rc = ir_raw_event_prepare(dev); 19218c2ecf20Sopenharmony_ci if (rc < 0) 19228c2ecf20Sopenharmony_ci goto out_minor; 19238c2ecf20Sopenharmony_ci } 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { 19268c2ecf20Sopenharmony_ci rc = rc_prepare_rx_device(dev); 19278c2ecf20Sopenharmony_ci if (rc) 19288c2ecf20Sopenharmony_ci goto out_raw; 19298c2ecf20Sopenharmony_ci } 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci dev->registered = true; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_ci rc = device_add(&dev->dev); 19348c2ecf20Sopenharmony_ci if (rc) 19358c2ecf20Sopenharmony_ci goto out_rx_free; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL); 19388c2ecf20Sopenharmony_ci dev_info(&dev->dev, "%s as %s\n", 19398c2ecf20Sopenharmony_ci dev->device_name ?: "Unspecified device", path ?: "N/A"); 19408c2ecf20Sopenharmony_ci kfree(path); 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci /* 19438c2ecf20Sopenharmony_ci * once the the input device is registered in rc_setup_rx_device, 19448c2ecf20Sopenharmony_ci * userspace can open the input device and rc_open() will be called 19458c2ecf20Sopenharmony_ci * as a result. This results in driver code being allowed to submit 19468c2ecf20Sopenharmony_ci * keycodes with rc_keydown, so lirc must be registered first. 19478c2ecf20Sopenharmony_ci */ 19488c2ecf20Sopenharmony_ci if (dev->allowed_protocols != RC_PROTO_BIT_CEC) { 19498c2ecf20Sopenharmony_ci rc = lirc_register(dev); 19508c2ecf20Sopenharmony_ci if (rc < 0) 19518c2ecf20Sopenharmony_ci goto out_dev; 19528c2ecf20Sopenharmony_ci } 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { 19558c2ecf20Sopenharmony_ci rc = rc_setup_rx_device(dev); 19568c2ecf20Sopenharmony_ci if (rc) 19578c2ecf20Sopenharmony_ci goto out_lirc; 19588c2ecf20Sopenharmony_ci } 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_IR_RAW) { 19618c2ecf20Sopenharmony_ci rc = ir_raw_event_register(dev); 19628c2ecf20Sopenharmony_ci if (rc < 0) 19638c2ecf20Sopenharmony_ci goto out_rx; 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "Registered rc%u (driver: %s)\n", dev->minor, 19678c2ecf20Sopenharmony_ci dev->driver_name ? dev->driver_name : "unknown"); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci return 0; 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ciout_rx: 19728c2ecf20Sopenharmony_ci rc_free_rx_device(dev); 19738c2ecf20Sopenharmony_ciout_lirc: 19748c2ecf20Sopenharmony_ci if (dev->allowed_protocols != RC_PROTO_BIT_CEC) 19758c2ecf20Sopenharmony_ci lirc_unregister(dev); 19768c2ecf20Sopenharmony_ciout_dev: 19778c2ecf20Sopenharmony_ci device_del(&dev->dev); 19788c2ecf20Sopenharmony_ciout_rx_free: 19798c2ecf20Sopenharmony_ci ir_free_table(&dev->rc_map); 19808c2ecf20Sopenharmony_ciout_raw: 19818c2ecf20Sopenharmony_ci ir_raw_event_free(dev); 19828c2ecf20Sopenharmony_ciout_minor: 19838c2ecf20Sopenharmony_ci ida_simple_remove(&rc_ida, minor); 19848c2ecf20Sopenharmony_ci return rc; 19858c2ecf20Sopenharmony_ci} 19868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_register_device); 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_cistatic void devm_rc_release(struct device *dev, void *res) 19898c2ecf20Sopenharmony_ci{ 19908c2ecf20Sopenharmony_ci rc_unregister_device(*(struct rc_dev **)res); 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ciint devm_rc_register_device(struct device *parent, struct rc_dev *dev) 19948c2ecf20Sopenharmony_ci{ 19958c2ecf20Sopenharmony_ci struct rc_dev **dr; 19968c2ecf20Sopenharmony_ci int ret; 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_ci dr = devres_alloc(devm_rc_release, sizeof(*dr), GFP_KERNEL); 19998c2ecf20Sopenharmony_ci if (!dr) 20008c2ecf20Sopenharmony_ci return -ENOMEM; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci ret = rc_register_device(dev); 20038c2ecf20Sopenharmony_ci if (ret) { 20048c2ecf20Sopenharmony_ci devres_free(dr); 20058c2ecf20Sopenharmony_ci return ret; 20068c2ecf20Sopenharmony_ci } 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci *dr = dev; 20098c2ecf20Sopenharmony_ci devres_add(parent, dr); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci return 0; 20128c2ecf20Sopenharmony_ci} 20138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_rc_register_device); 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_civoid rc_unregister_device(struct rc_dev *dev) 20168c2ecf20Sopenharmony_ci{ 20178c2ecf20Sopenharmony_ci if (!dev) 20188c2ecf20Sopenharmony_ci return; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (dev->driver_type == RC_DRIVER_IR_RAW) 20218c2ecf20Sopenharmony_ci ir_raw_event_unregister(dev); 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci del_timer_sync(&dev->timer_keyup); 20248c2ecf20Sopenharmony_ci del_timer_sync(&dev->timer_repeat); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci mutex_lock(&dev->lock); 20278c2ecf20Sopenharmony_ci if (dev->users && dev->close) 20288c2ecf20Sopenharmony_ci dev->close(dev); 20298c2ecf20Sopenharmony_ci dev->registered = false; 20308c2ecf20Sopenharmony_ci mutex_unlock(&dev->lock); 20318c2ecf20Sopenharmony_ci 20328c2ecf20Sopenharmony_ci rc_free_rx_device(dev); 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci /* 20358c2ecf20Sopenharmony_ci * lirc device should be freed with dev->registered = false, so 20368c2ecf20Sopenharmony_ci * that userspace polling will get notified. 20378c2ecf20Sopenharmony_ci */ 20388c2ecf20Sopenharmony_ci if (dev->allowed_protocols != RC_PROTO_BIT_CEC) 20398c2ecf20Sopenharmony_ci lirc_unregister(dev); 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_ci device_del(&dev->dev); 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci ida_simple_remove(&rc_ida, dev->minor); 20448c2ecf20Sopenharmony_ci 20458c2ecf20Sopenharmony_ci if (!dev->managed_alloc) 20468c2ecf20Sopenharmony_ci rc_free_device(dev); 20478c2ecf20Sopenharmony_ci} 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rc_unregister_device); 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci/* 20528c2ecf20Sopenharmony_ci * Init/exit code for the module. Basically, creates/removes /sys/class/rc 20538c2ecf20Sopenharmony_ci */ 20548c2ecf20Sopenharmony_ci 20558c2ecf20Sopenharmony_cistatic int __init rc_core_init(void) 20568c2ecf20Sopenharmony_ci{ 20578c2ecf20Sopenharmony_ci int rc = class_register(&rc_class); 20588c2ecf20Sopenharmony_ci if (rc) { 20598c2ecf20Sopenharmony_ci pr_err("rc_core: unable to register rc class\n"); 20608c2ecf20Sopenharmony_ci return rc; 20618c2ecf20Sopenharmony_ci } 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci rc = lirc_dev_init(); 20648c2ecf20Sopenharmony_ci if (rc) { 20658c2ecf20Sopenharmony_ci pr_err("rc_core: unable to init lirc\n"); 20668c2ecf20Sopenharmony_ci class_unregister(&rc_class); 20678c2ecf20Sopenharmony_ci return rc; 20688c2ecf20Sopenharmony_ci } 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci led_trigger_register_simple("rc-feedback", &led_feedback); 20718c2ecf20Sopenharmony_ci rc_map_register(&empty_map); 20728c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CEC_RC 20738c2ecf20Sopenharmony_ci rc_map_register(&cec_map); 20748c2ecf20Sopenharmony_ci#endif 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci return 0; 20778c2ecf20Sopenharmony_ci} 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_cistatic void __exit rc_core_exit(void) 20808c2ecf20Sopenharmony_ci{ 20818c2ecf20Sopenharmony_ci lirc_dev_exit(); 20828c2ecf20Sopenharmony_ci class_unregister(&rc_class); 20838c2ecf20Sopenharmony_ci led_trigger_unregister_simple(led_feedback); 20848c2ecf20Sopenharmony_ci#ifdef CONFIG_MEDIA_CEC_RC 20858c2ecf20Sopenharmony_ci rc_map_unregister(&cec_map); 20868c2ecf20Sopenharmony_ci#endif 20878c2ecf20Sopenharmony_ci rc_map_unregister(&empty_map); 20888c2ecf20Sopenharmony_ci} 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_cisubsys_initcall(rc_core_init); 20918c2ecf20Sopenharmony_cimodule_exit(rc_core_exit); 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mauro Carvalho Chehab"); 20948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2095