linux/drivers/media/cec/usb/rainshadow/rainshadow-cec.c
<<
>>
Prefs
   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);
 381