brintos

brintos / linux-shallow public Read only

0
0
Text · 8.3 KiB · ee870ea Raw
381 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * RainShadow Tech HDMI CEC driver4 *5 * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl6 */7 8/*9 * Notes:10 *11 * The higher level protocols are currently disabled. This can be added12 * 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.pdf17 */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 25639 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);381