cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

winmate-fm07-keys.c (4011B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// Driver for the Winmate FM07 front-panel keys
      4//
      5// Author: Daniel Beer <daniel.beer@tirotech.co.nz>
      6
      7#include <linux/init.h>
      8#include <linux/module.h>
      9#include <linux/input.h>
     10#include <linux/ioport.h>
     11#include <linux/platform_device.h>
     12#include <linux/dmi.h>
     13#include <linux/io.h>
     14
     15#define DRV_NAME	"winmate-fm07keys"
     16
     17#define PORT_CMD	0x6c
     18#define PORT_DATA	0x68
     19
     20#define EC_ADDR_KEYS	0x3b
     21#define EC_CMD_READ	0x80
     22
     23#define BASE_KEY	KEY_F13
     24#define NUM_KEYS	5
     25
     26/* Typically we're done in fewer than 10 iterations */
     27#define LOOP_TIMEOUT	1000
     28
     29static void fm07keys_poll(struct input_dev *input)
     30{
     31	uint8_t k;
     32	int i;
     33
     34	/* Flush output buffer */
     35	i = 0;
     36	while (inb(PORT_CMD) & 0x01) {
     37		if (++i >= LOOP_TIMEOUT)
     38			goto timeout;
     39		inb(PORT_DATA);
     40	}
     41
     42	/* Send request and wait for write completion */
     43	outb(EC_CMD_READ, PORT_CMD);
     44	i = 0;
     45	while (inb(PORT_CMD) & 0x02)
     46		if (++i >= LOOP_TIMEOUT)
     47			goto timeout;
     48
     49	outb(EC_ADDR_KEYS, PORT_DATA);
     50	i = 0;
     51	while (inb(PORT_CMD) & 0x02)
     52		if (++i >= LOOP_TIMEOUT)
     53			goto timeout;
     54
     55	/* Wait for data ready */
     56	i = 0;
     57	while (!(inb(PORT_CMD) & 0x01))
     58		if (++i >= LOOP_TIMEOUT)
     59			goto timeout;
     60	k = inb(PORT_DATA);
     61
     62	/* Notify of new key states */
     63	for (i = 0; i < NUM_KEYS; i++) {
     64		input_report_key(input, BASE_KEY + i, (~k) & 1);
     65		k >>= 1;
     66	}
     67
     68	input_sync(input);
     69	return;
     70
     71timeout:
     72	dev_warn_ratelimited(&input->dev, "timeout polling IO memory\n");
     73}
     74
     75static int fm07keys_probe(struct platform_device *pdev)
     76{
     77	struct device *dev = &pdev->dev;
     78	struct input_dev *input;
     79	int ret;
     80	int i;
     81
     82	input = devm_input_allocate_device(dev);
     83	if (!input) {
     84		dev_err(dev, "no memory for input device\n");
     85		return -ENOMEM;
     86	}
     87
     88	if (!devm_request_region(dev, PORT_CMD, 1, "Winmate FM07 EC"))
     89		return -EBUSY;
     90	if (!devm_request_region(dev, PORT_DATA, 1, "Winmate FM07 EC"))
     91		return -EBUSY;
     92
     93	input->name = "Winmate FM07 front-panel keys";
     94	input->phys = DRV_NAME "/input0";
     95
     96	input->id.bustype = BUS_HOST;
     97	input->id.vendor = 0x0001;
     98	input->id.product = 0x0001;
     99	input->id.version = 0x0100;
    100
    101	__set_bit(EV_KEY, input->evbit);
    102
    103	for (i = 0; i < NUM_KEYS; i++)
    104		__set_bit(BASE_KEY + i, input->keybit);
    105
    106	ret = input_setup_polling(input, fm07keys_poll);
    107	if (ret) {
    108		dev_err(dev, "unable to set up polling, err=%d\n", ret);
    109		return ret;
    110	}
    111
    112	/* These are silicone buttons. They can't be pressed in rapid
    113	 * succession too quickly, and 50 Hz seems to be an adequate
    114	 * sampling rate without missing any events when tested.
    115	 */
    116	input_set_poll_interval(input, 20);
    117
    118	ret = input_register_device(input);
    119	if (ret) {
    120		dev_err(dev, "unable to register polled device, err=%d\n",
    121			ret);
    122		return ret;
    123	}
    124
    125	input_sync(input);
    126	return 0;
    127}
    128
    129static struct platform_driver fm07keys_driver = {
    130	.probe		= fm07keys_probe,
    131	.driver		= {
    132		.name	= DRV_NAME
    133	},
    134};
    135
    136static struct platform_device *dev;
    137
    138static const struct dmi_system_id fm07keys_dmi_table[] __initconst = {
    139	{
    140		/* FM07 and FM07P */
    141		.matches = {
    142			DMI_MATCH(DMI_SYS_VENDOR, "Winmate Inc."),
    143			DMI_MATCH(DMI_PRODUCT_NAME, "IP30"),
    144		},
    145	},
    146	{ }
    147};
    148
    149MODULE_DEVICE_TABLE(dmi, fm07keys_dmi_table);
    150
    151static int __init fm07keys_init(void)
    152{
    153	int ret;
    154
    155	if (!dmi_check_system(fm07keys_dmi_table))
    156		return -ENODEV;
    157
    158	ret = platform_driver_register(&fm07keys_driver);
    159	if (ret) {
    160		pr_err("fm07keys: failed to register driver, err=%d\n", ret);
    161		return ret;
    162	}
    163
    164	dev = platform_device_register_simple(DRV_NAME, -1, NULL, 0);
    165	if (IS_ERR(dev)) {
    166		ret = PTR_ERR(dev);
    167		pr_err("fm07keys: failed to allocate device, err = %d\n", ret);
    168		goto fail_register;
    169	}
    170
    171	return 0;
    172
    173fail_register:
    174	platform_driver_unregister(&fm07keys_driver);
    175	return ret;
    176}
    177
    178static void __exit fm07keys_exit(void)
    179{
    180	platform_driver_unregister(&fm07keys_driver);
    181	platform_device_unregister(dev);
    182}
    183
    184module_init(fm07keys_init);
    185module_exit(fm07keys_exit);
    186
    187MODULE_AUTHOR("Daniel Beer <daniel.beer@tirotech.co.nz>");
    188MODULE_DESCRIPTION("Winmate FM07 front-panel keys driver");
    189MODULE_LICENSE("GPL");