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

rainshadow-cec.c (8540B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * RainShadow Tech HDMI CEC driver
      4 *
      5 * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl
      6 */
      7
      8/*
      9 * Notes:
     10 *
     11 * The higher level protocols are currently disabled. This can be added
     12 * later, similar to how this is done for the Pulse Eight CEC driver.
     13 *
     14 * Documentation of the protocol is available here:
     15 *
     16 * http://rainshadowtech.com/doc/HDMICECtoUSBandRS232v2.0.pdf
     17 */
     18
     19#include <linux/completion.h>
     20#include <linux/ctype.h>
     21#include <linux/delay.h>
     22#include <linux/init.h>
     23#include <linux/interrupt.h>
     24#include <linux/kernel.h>
     25#include <linux/module.h>
     26#include <linux/serio.h>
     27#include <linux/slab.h>
     28#include <linux/spinlock.h>
     29#include <linux/time.h>
     30#include <linux/workqueue.h>
     31
     32#include <media/cec.h>
     33
     34MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
     35MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver");
     36MODULE_LICENSE("GPL");
     37
     38#define DATA_SIZE 256
     39
     40struct rain {
     41	struct device *dev;
     42	struct serio *serio;
     43	struct cec_adapter *adap;
     44	struct completion cmd_done;
     45	struct work_struct work;
     46
     47	/* Low-level ringbuffer, collecting incoming characters */
     48	char buf[DATA_SIZE];
     49	unsigned int buf_rd_idx;
     50	unsigned int buf_wr_idx;
     51	unsigned int buf_len;
     52	spinlock_t buf_lock;
     53
     54	/* command buffer */
     55	char cmd[DATA_SIZE];
     56	unsigned int cmd_idx;
     57	bool cmd_started;
     58
     59	/* reply to a command, only used to store the firmware version */
     60	char cmd_reply[DATA_SIZE];
     61
     62	struct mutex write_lock;
     63};
     64
     65static void rain_process_msg(struct rain *rain)
     66{
     67	struct cec_msg msg = {};
     68	const char *cmd = rain->cmd + 3;
     69	int stat = -1;
     70
     71	for (; *cmd; cmd++) {
     72		if (!isxdigit(*cmd))
     73			continue;
     74		if (isxdigit(cmd[0]) && isxdigit(cmd[1])) {
     75			if (msg.len == CEC_MAX_MSG_SIZE)
     76				break;
     77			if (hex2bin(msg.msg + msg.len, cmd, 1))
     78				continue;
     79			msg.len++;
     80			cmd++;
     81			continue;
     82		}
     83		if (!cmd[1])
     84			stat = hex_to_bin(cmd[0]);
     85		break;
     86	}
     87
     88	if (rain->cmd[0] == 'R') {
     89		if (stat == 1 || stat == 2)
     90			cec_received_msg(rain->adap, &msg);
     91		return;
     92	}
     93
     94	switch (stat) {
     95	case 1:
     96		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK);
     97		break;
     98	case 2:
     99		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK);
    100		break;
    101	default:
    102		cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE);
    103		break;
    104	}
    105}
    106
    107static void rain_irq_work_handler(struct work_struct *work)
    108{
    109	struct rain *rain =
    110		container_of(work, struct rain, work);
    111
    112	while (true) {
    113		unsigned long flags;
    114		char data;
    115
    116		spin_lock_irqsave(&rain->buf_lock, flags);
    117		if (!rain->buf_len) {
    118			spin_unlock_irqrestore(&rain->buf_lock, flags);
    119			break;
    120		}
    121
    122		data = rain->buf[rain->buf_rd_idx];
    123		rain->buf_len--;
    124		rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff;
    125
    126		spin_unlock_irqrestore(&rain->buf_lock, flags);
    127
    128		if (!rain->cmd_started && data != '?')
    129			continue;
    130
    131		switch (data) {
    132		case '\r':
    133			rain->cmd[rain->cmd_idx] = '\0';
    134			dev_dbg(rain->dev, "received: %s\n", rain->cmd);
    135			if (!memcmp(rain->cmd, "REC", 3) ||
    136			    !memcmp(rain->cmd, "STA", 3)) {
    137				rain_process_msg(rain);
    138			} else {
    139				strscpy(rain->cmd_reply, rain->cmd,
    140					sizeof(rain->cmd_reply));
    141				complete(&rain->cmd_done);
    142			}
    143			rain->cmd_idx = 0;
    144			rain->cmd_started = false;
    145			break;
    146
    147		case '\n':
    148			rain->cmd_idx = 0;
    149			rain->cmd_started = false;
    150			break;
    151
    152		case '?':
    153			rain->cmd_idx = 0;
    154			rain->cmd_started = true;
    155			break;
    156
    157		default:
    158			if (rain->cmd_idx >= DATA_SIZE - 1) {
    159				dev_dbg(rain->dev,
    160					"throwing away %d bytes of garbage\n", rain->cmd_idx);
    161				rain->cmd_idx = 0;
    162			}
    163			rain->cmd[rain->cmd_idx++] = data;
    164			break;
    165		}
    166	}
    167}
    168
    169static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data,
    170				    unsigned int flags)
    171{
    172	struct rain *rain = serio_get_drvdata(serio);
    173
    174	if (rain->buf_len == DATA_SIZE) {
    175		dev_warn_once(rain->dev, "buffer overflow\n");
    176		return IRQ_HANDLED;
    177	}
    178	spin_lock(&rain->buf_lock);
    179	rain->buf_len++;
    180	rain->buf[rain->buf_wr_idx] = data;
    181	rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
    182	spin_unlock(&rain->buf_lock);
    183	schedule_work(&rain->work);
    184	return IRQ_HANDLED;
    185}
    186
    187static void rain_disconnect(struct serio *serio)
    188{
    189	struct rain *rain = serio_get_drvdata(serio);
    190
    191	cancel_work_sync(&rain->work);
    192	cec_unregister_adapter(rain->adap);
    193	dev_info(&serio->dev, "disconnected\n");
    194	serio_close(serio);
    195	serio_set_drvdata(serio, NULL);
    196	kfree(rain);
    197}
    198
    199static int rain_send(struct rain *rain, const char *command)
    200{
    201	int err = serio_write(rain->serio, '!');
    202
    203	dev_dbg(rain->dev, "send: %s\n", command);
    204	while (!err && *command)
    205		err = serio_write(rain->serio, *command++);
    206	if (!err)
    207		err = serio_write(rain->serio, '~');
    208
    209	return err;
    210}
    211
    212static int rain_send_and_wait(struct rain *rain,
    213			      const char *cmd, const char *reply)
    214{
    215	int err;
    216
    217	init_completion(&rain->cmd_done);
    218
    219	mutex_lock(&rain->write_lock);
    220	err = rain_send(rain, cmd);
    221	if (err)
    222		goto err;
    223
    224	if (!wait_for_completion_timeout(&rain->cmd_done, HZ)) {
    225		err = -ETIMEDOUT;
    226		goto err;
    227	}
    228	if (reply && strncmp(rain->cmd_reply, reply, strlen(reply))) {
    229		dev_dbg(rain->dev,
    230			 "transmit of '%s': received '%s' instead of '%s'\n",
    231			 cmd, rain->cmd_reply, reply);
    232		err = -EIO;
    233	}
    234err:
    235	mutex_unlock(&rain->write_lock);
    236	return err;
    237}
    238
    239static int rain_setup(struct rain *rain, struct serio *serio,
    240			struct cec_log_addrs *log_addrs, u16 *pa)
    241{
    242	int err;
    243
    244	err = rain_send_and_wait(rain, "R", "REV");
    245	if (err)
    246		return err;
    247	dev_info(rain->dev, "Firmware version %s\n", rain->cmd_reply + 4);
    248
    249	err = rain_send_and_wait(rain, "Q 1", "QTY");
    250	if (err)
    251		return err;
    252	err = rain_send_and_wait(rain, "c0000", "CFG");
    253	if (err)
    254		return err;
    255	return rain_send_and_wait(rain, "A F 0000", "ADR");
    256}
    257
    258static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable)
    259{
    260	return 0;
    261}
    262
    263static int rain_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
    264{
    265	struct rain *rain = cec_get_drvdata(adap);
    266	u8 cmd[16];
    267
    268	if (log_addr == CEC_LOG_ADDR_INVALID)
    269		log_addr = CEC_LOG_ADDR_UNREGISTERED;
    270	snprintf(cmd, sizeof(cmd), "A %x", log_addr);
    271	return rain_send_and_wait(rain, cmd, "ADR");
    272}
    273
    274static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
    275				    u32 signal_free_time, struct cec_msg *msg)
    276{
    277	struct rain *rain = cec_get_drvdata(adap);
    278	char cmd[2 * CEC_MAX_MSG_SIZE + 16];
    279	unsigned int i;
    280	int err;
    281
    282	if (msg->len == 1) {
    283		snprintf(cmd, sizeof(cmd), "x%x", cec_msg_destination(msg));
    284	} else {
    285		char hex[3];
    286
    287		snprintf(cmd, sizeof(cmd), "x%x %02x ",
    288			 cec_msg_destination(msg), msg->msg[1]);
    289		for (i = 2; i < msg->len; i++) {
    290			snprintf(hex, sizeof(hex), "%02x", msg->msg[i]);
    291			strlcat(cmd, hex, sizeof(cmd));
    292		}
    293	}
    294	mutex_lock(&rain->write_lock);
    295	err = rain_send(rain, cmd);
    296	mutex_unlock(&rain->write_lock);
    297	return err;
    298}
    299
    300static const struct cec_adap_ops rain_cec_adap_ops = {
    301	.adap_enable = rain_cec_adap_enable,
    302	.adap_log_addr = rain_cec_adap_log_addr,
    303	.adap_transmit = rain_cec_adap_transmit,
    304};
    305
    306static int rain_connect(struct serio *serio, struct serio_driver *drv)
    307{
    308	u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
    309	struct rain *rain;
    310	int err = -ENOMEM;
    311	struct cec_log_addrs log_addrs = {};
    312	u16 pa = CEC_PHYS_ADDR_INVALID;
    313
    314	rain = kzalloc(sizeof(*rain), GFP_KERNEL);
    315
    316	if (!rain)
    317		return -ENOMEM;
    318
    319	rain->serio = serio;
    320	rain->adap = cec_allocate_adapter(&rain_cec_adap_ops, rain,
    321					  dev_name(&serio->dev), caps, 1);
    322	err = PTR_ERR_OR_ZERO(rain->adap);
    323	if (err < 0)
    324		goto free_device;
    325
    326	rain->dev = &serio->dev;
    327	serio_set_drvdata(serio, rain);
    328	INIT_WORK(&rain->work, rain_irq_work_handler);
    329	mutex_init(&rain->write_lock);
    330	spin_lock_init(&rain->buf_lock);
    331
    332	err = serio_open(serio, drv);
    333	if (err)
    334		goto delete_adap;
    335
    336	err = rain_setup(rain, serio, &log_addrs, &pa);
    337	if (err)
    338		goto close_serio;
    339
    340	err = cec_register_adapter(rain->adap, &serio->dev);
    341	if (err < 0)
    342		goto close_serio;
    343
    344	rain->dev = &rain->adap->devnode.dev;
    345	return 0;
    346
    347close_serio:
    348	serio_close(serio);
    349delete_adap:
    350	cec_delete_adapter(rain->adap);
    351	serio_set_drvdata(serio, NULL);
    352free_device:
    353	kfree(rain);
    354	return err;
    355}
    356
    357static const struct serio_device_id rain_serio_ids[] = {
    358	{
    359		.type	= SERIO_RS232,
    360		.proto	= SERIO_RAINSHADOW_CEC,
    361		.id	= SERIO_ANY,
    362		.extra	= SERIO_ANY,
    363	},
    364	{ 0 }
    365};
    366
    367MODULE_DEVICE_TABLE(serio, rain_serio_ids);
    368
    369static struct serio_driver rain_drv = {
    370	.driver		= {
    371		.name	= "rainshadow-cec",
    372	},
    373	.description	= "RainShadow Tech HDMI CEC driver",
    374	.id_table	= rain_serio_ids,
    375	.interrupt	= rain_interrupt,
    376	.connect	= rain_connect,
    377	.disconnect	= rain_disconnect,
    378};
    379
    380module_serio_driver(rain_drv);