summaryrefslogtreecommitdiffstats
path: root/drivers/media/usb/rainshadow-cec/rainshadow-cec.c
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab+huawei@kernel.org>2020-04-15 11:14:25 +0200
committerMauro Carvalho Chehab <mchehab+huawei@kernel.org>2020-04-15 12:06:40 +0200
commita81068181aad78a5f6980408a4b6115d22bb8aed (patch)
treeb5aa2c123bf45cd31419e8a0b1297c2155b30b5a /drivers/media/usb/rainshadow-cec/rainshadow-cec.c
parent4be5e8648b0c287aefc6ac3f3a0b12c696054f43 (diff)
downloadlinux-stable-a81068181aad78a5f6980408a4b6115d22bb8aed.tar.gz
linux-stable-a81068181aad78a5f6980408a4b6115d22bb8aed.tar.bz2
linux-stable-a81068181aad78a5f6980408a4b6115d22bb8aed.zip
media: move CEC USB drivers to a separate directory
As CEC support doesn't depend on MEDIA_SUPPORT, let's place the platform drivers outside the media menu. As a side effect, instead of depends on USB, drivers just select it. Acked-by: Hans Verkuil <hverkuil-cisco@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
Diffstat (limited to 'drivers/media/usb/rainshadow-cec/rainshadow-cec.c')
-rw-r--r--drivers/media/usb/rainshadow-cec/rainshadow-cec.c380
1 files changed, 0 insertions, 380 deletions
diff --git a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c b/drivers/media/usb/rainshadow-cec/rainshadow-cec.c
deleted file mode 100644
index ee870ea1a886..000000000000
--- a/drivers/media/usb/rainshadow-cec/rainshadow-cec.c
+++ /dev/null
@@ -1,380 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * RainShadow Tech HDMI CEC driver
- *
- * Copyright 2016 Hans Verkuil <hverkuil@xs4all.nl
- */
-
-/*
- * Notes:
- *
- * The higher level protocols are currently disabled. This can be added
- * later, similar to how this is done for the Pulse Eight CEC driver.
- *
- * Documentation of the protocol is available here:
- *
- * http://rainshadowtech.com/doc/HDMICECtoUSBandRS232v2.0.pdf
- */
-
-#include <linux/completion.h>
-#include <linux/ctype.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/serio.h>
-#include <linux/slab.h>
-#include <linux/spinlock.h>
-#include <linux/time.h>
-#include <linux/workqueue.h>
-
-#include <media/cec.h>
-
-MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
-MODULE_DESCRIPTION("RainShadow Tech HDMI CEC driver");
-MODULE_LICENSE("GPL");
-
-#define DATA_SIZE 256
-
-struct rain {
- struct device *dev;
- struct serio *serio;
- struct cec_adapter *adap;
- struct completion cmd_done;
- struct work_struct work;
-
- /* Low-level ringbuffer, collecting incoming characters */
- char buf[DATA_SIZE];
- unsigned int buf_rd_idx;
- unsigned int buf_wr_idx;
- unsigned int buf_len;
- spinlock_t buf_lock;
-
- /* command buffer */
- char cmd[DATA_SIZE];
- unsigned int cmd_idx;
- bool cmd_started;
-
- /* reply to a command, only used to store the firmware version */
- char cmd_reply[DATA_SIZE];
-
- struct mutex write_lock;
-};
-
-static void rain_process_msg(struct rain *rain)
-{
- struct cec_msg msg = {};
- const char *cmd = rain->cmd + 3;
- int stat = -1;
-
- for (; *cmd; cmd++) {
- if (!isxdigit(*cmd))
- continue;
- if (isxdigit(cmd[0]) && isxdigit(cmd[1])) {
- if (msg.len == CEC_MAX_MSG_SIZE)
- break;
- if (hex2bin(msg.msg + msg.len, cmd, 1))
- continue;
- msg.len++;
- cmd++;
- continue;
- }
- if (!cmd[1])
- stat = hex_to_bin(cmd[0]);
- break;
- }
-
- if (rain->cmd[0] == 'R') {
- if (stat == 1 || stat == 2)
- cec_received_msg(rain->adap, &msg);
- return;
- }
-
- switch (stat) {
- case 1:
- cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_OK);
- break;
- case 2:
- cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_NACK);
- break;
- default:
- cec_transmit_attempt_done(rain->adap, CEC_TX_STATUS_LOW_DRIVE);
- break;
- }
-}
-
-static void rain_irq_work_handler(struct work_struct *work)
-{
- struct rain *rain =
- container_of(work, struct rain, work);
-
- while (true) {
- unsigned long flags;
- char data;
-
- spin_lock_irqsave(&rain->buf_lock, flags);
- if (!rain->buf_len) {
- spin_unlock_irqrestore(&rain->buf_lock, flags);
- break;
- }
-
- data = rain->buf[rain->buf_rd_idx];
- rain->buf_len--;
- rain->buf_rd_idx = (rain->buf_rd_idx + 1) & 0xff;
-
- spin_unlock_irqrestore(&rain->buf_lock, flags);
-
- if (!rain->cmd_started && data != '?')
- continue;
-
- switch (data) {
- case '\r':
- rain->cmd[rain->cmd_idx] = '\0';
- dev_dbg(rain->dev, "received: %s\n", rain->cmd);
- if (!memcmp(rain->cmd, "REC", 3) ||
- !memcmp(rain->cmd, "STA", 3)) {
- rain_process_msg(rain);
- } else {
- strscpy(rain->cmd_reply, rain->cmd,
- sizeof(rain->cmd_reply));
- complete(&rain->cmd_done);
- }
- rain->cmd_idx = 0;
- rain->cmd_started = false;
- break;
-
- case '\n':
- rain->cmd_idx = 0;
- rain->cmd_started = false;
- break;
-
- case '?':
- rain->cmd_idx = 0;
- rain->cmd_started = true;
- break;
-
- default:
- if (rain->cmd_idx >= DATA_SIZE - 1) {
- dev_dbg(rain->dev,
- "throwing away %d bytes of garbage\n", rain->cmd_idx);
- rain->cmd_idx = 0;
- }
- rain->cmd[rain->cmd_idx++] = data;
- break;
- }
- }
-}
-
-static irqreturn_t rain_interrupt(struct serio *serio, unsigned char data,
- unsigned int flags)
-{
- struct rain *rain = serio_get_drvdata(serio);
-
- if (rain->buf_len == DATA_SIZE) {
- dev_warn_once(rain->dev, "buffer overflow\n");
- return IRQ_HANDLED;
- }
- spin_lock(&rain->buf_lock);
- rain->buf_len++;
- rain->buf[rain->buf_wr_idx] = data;
- rain->buf_wr_idx = (rain->buf_wr_idx + 1) & 0xff;
- spin_unlock(&rain->buf_lock);
- schedule_work(&rain->work);
- return IRQ_HANDLED;
-}
-
-static void rain_disconnect(struct serio *serio)
-{
- struct rain *rain = serio_get_drvdata(serio);
-
- cancel_work_sync(&rain->work);
- cec_unregister_adapter(rain->adap);
- dev_info(&serio->dev, "disconnected\n");
- serio_close(serio);
- serio_set_drvdata(serio, NULL);
- kfree(rain);
-}
-
-static int rain_send(struct rain *rain, const char *command)
-{
- int err = serio_write(rain->serio, '!');
-
- dev_dbg(rain->dev, "send: %s\n", command);
- while (!err && *command)
- err = serio_write(rain->serio, *command++);
- if (!err)
- err = serio_write(rain->serio, '~');
-
- return err;
-}
-
-static int rain_send_and_wait(struct rain *rain,
- const char *cmd, const char *reply)
-{
- int err;
-
- init_completion(&rain->cmd_done);
-
- mutex_lock(&rain->write_lock);
- err = rain_send(rain, cmd);
- if (err)
- goto err;
-
- if (!wait_for_completion_timeout(&rain->cmd_done, HZ)) {
- err = -ETIMEDOUT;
- goto err;
- }
- if (reply && strncmp(rain->cmd_reply, reply, strlen(reply))) {
- dev_dbg(rain->dev,
- "transmit of '%s': received '%s' instead of '%s'\n",
- cmd, rain->cmd_reply, reply);
- err = -EIO;
- }
-err:
- mutex_unlock(&rain->write_lock);
- return err;
-}
-
-static int rain_setup(struct rain *rain, struct serio *serio,
- struct cec_log_addrs *log_addrs, u16 *pa)
-{
- int err;
-
- err = rain_send_and_wait(rain, "R", "REV");
- if (err)
- return err;
- dev_info(rain->dev, "Firmware version %s\n", rain->cmd_reply + 4);
-
- err = rain_send_and_wait(rain, "Q 1", "QTY");
- if (err)
- return err;
- err = rain_send_and_wait(rain, "c0000", "CFG");
- if (err)
- return err;
- return rain_send_and_wait(rain, "A F 0000", "ADR");
-}
-
-static int rain_cec_adap_enable(struct cec_adapter *adap, bool enable)
-{
- return 0;
-}
-
-static int rain_cec_adap_log_addr(struct cec_adapter *adap, u8 log_addr)
-{
- struct rain *rain = cec_get_drvdata(adap);
- u8 cmd[16];
-
- if (log_addr == CEC_LOG_ADDR_INVALID)
- log_addr = CEC_LOG_ADDR_UNREGISTERED;
- snprintf(cmd, sizeof(cmd), "A %x", log_addr);
- return rain_send_and_wait(rain, cmd, "ADR");
-}
-
-static int rain_cec_adap_transmit(struct cec_adapter *adap, u8 attempts,
- u32 signal_free_time, struct cec_msg *msg)
-{
- struct rain *rain = cec_get_drvdata(adap);
- char cmd[2 * CEC_MAX_MSG_SIZE + 16];
- unsigned int i;
- int err;
-
- if (msg->len == 1) {
- snprintf(cmd, sizeof(cmd), "x%x", cec_msg_destination(msg));
- } else {
- char hex[3];
-
- snprintf(cmd, sizeof(cmd), "x%x %02x ",
- cec_msg_destination(msg), msg->msg[1]);
- for (i = 2; i < msg->len; i++) {
- snprintf(hex, sizeof(hex), "%02x", msg->msg[i]);
- strlcat(cmd, hex, sizeof(cmd));
- }
- }
- mutex_lock(&rain->write_lock);
- err = rain_send(rain, cmd);
- mutex_unlock(&rain->write_lock);
- return err;
-}
-
-static const struct cec_adap_ops rain_cec_adap_ops = {
- .adap_enable = rain_cec_adap_enable,
- .adap_log_addr = rain_cec_adap_log_addr,
- .adap_transmit = rain_cec_adap_transmit,
-};
-
-static int rain_connect(struct serio *serio, struct serio_driver *drv)
-{
- u32 caps = CEC_CAP_DEFAULTS | CEC_CAP_PHYS_ADDR | CEC_CAP_MONITOR_ALL;
- struct rain *rain;
- int err = -ENOMEM;
- struct cec_log_addrs log_addrs = {};
- u16 pa = CEC_PHYS_ADDR_INVALID;
-
- rain = kzalloc(sizeof(*rain), GFP_KERNEL);
-
- if (!rain)
- return -ENOMEM;
-
- rain->serio = serio;
- rain->adap = cec_allocate_adapter(&rain_cec_adap_ops, rain,
- dev_name(&serio->dev), caps, 1);
- err = PTR_ERR_OR_ZERO(rain->adap);
- if (err < 0)
- goto free_device;
-
- rain->dev = &serio->dev;
- serio_set_drvdata(serio, rain);
- INIT_WORK(&rain->work, rain_irq_work_handler);
- mutex_init(&rain->write_lock);
- spin_lock_init(&rain->buf_lock);
-
- err = serio_open(serio, drv);
- if (err)
- goto delete_adap;
-
- err = rain_setup(rain, serio, &log_addrs, &pa);
- if (err)
- goto close_serio;
-
- err = cec_register_adapter(rain->adap, &serio->dev);
- if (err < 0)
- goto close_serio;
-
- rain->dev = &rain->adap->devnode.dev;
- return 0;
-
-close_serio:
- serio_close(serio);
-delete_adap:
- cec_delete_adapter(rain->adap);
- serio_set_drvdata(serio, NULL);
-free_device:
- kfree(rain);
- return err;
-}
-
-static const struct serio_device_id rain_serio_ids[] = {
- {
- .type = SERIO_RS232,
- .proto = SERIO_RAINSHADOW_CEC,
- .id = SERIO_ANY,
- .extra = SERIO_ANY,
- },
- { 0 }
-};
-
-MODULE_DEVICE_TABLE(serio, rain_serio_ids);
-
-static struct serio_driver rain_drv = {
- .driver = {
- .name = "rainshadow-cec",
- },
- .description = "RainShadow Tech HDMI CEC driver",
- .id_table = rain_serio_ids,
- .interrupt = rain_interrupt,
- .connect = rain_connect,
- .disconnect = rain_disconnect,
-};
-
-module_serio_driver(rain_drv);