linux/drivers/media/firewire/firedtv-ci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * FireDTV driver (formerly known as FireSAT)
   4 *
   5 * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
   6 * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
   7 */
   8
   9#include <linux/device.h>
  10#include <linux/dvb/ca.h>
  11#include <linux/fs.h>
  12#include <linux/module.h>
  13
  14#include <media/dvbdev.h>
  15
  16#include "firedtv.h"
  17
  18#define EN50221_TAG_APP_INFO_ENQUIRY    0x9f8020
  19#define EN50221_TAG_CA_INFO_ENQUIRY     0x9f8030
  20#define EN50221_TAG_CA_PMT              0x9f8032
  21#define EN50221_TAG_ENTER_MENU          0x9f8022
  22
  23static int fdtv_ca_ready(struct firedtv_tuner_status *stat)
  24{
  25        return stat->ca_initialization_status   == 1 &&
  26               stat->ca_error_flag              == 0 &&
  27               stat->ca_dvb_flag                == 1 &&
  28               stat->ca_module_present_status   == 1;
  29}
  30
  31static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat)
  32{
  33        int flags = 0;
  34
  35        if (stat->ca_module_present_status == 1)
  36                flags |= CA_CI_MODULE_PRESENT;
  37        if (stat->ca_initialization_status == 1 &&
  38            stat->ca_error_flag            == 0 &&
  39            stat->ca_dvb_flag              == 1)
  40                flags |= CA_CI_MODULE_READY;
  41        return flags;
  42}
  43
  44static int fdtv_ca_get_caps(void *arg)
  45{
  46        struct ca_caps *cap = arg;
  47
  48        cap->slot_num = 1;
  49        cap->slot_type = CA_CI;
  50        cap->descr_num = 1;
  51        cap->descr_type = CA_ECD;
  52        return 0;
  53}
  54
  55static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg)
  56{
  57        struct firedtv_tuner_status stat;
  58        struct ca_slot_info *slot = arg;
  59        int err;
  60
  61        err = avc_tuner_status(fdtv, &stat);
  62        if (err)
  63                return err;
  64
  65        if (slot->num != 0)
  66                return -EACCES;
  67
  68        slot->type = CA_CI;
  69        slot->flags = fdtv_get_ca_flags(&stat);
  70        return 0;
  71}
  72
  73static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg)
  74{
  75        struct ca_msg *reply = arg;
  76
  77        return avc_ca_app_info(fdtv, reply->msg, &reply->length);
  78}
  79
  80static int fdtv_ca_info(struct firedtv *fdtv, void *arg)
  81{
  82        struct ca_msg *reply = arg;
  83
  84        return avc_ca_info(fdtv, reply->msg, &reply->length);
  85}
  86
  87static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg)
  88{
  89        struct ca_msg *reply = arg;
  90
  91        return avc_ca_get_mmi(fdtv, reply->msg, &reply->length);
  92}
  93
  94static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg)
  95{
  96        struct firedtv_tuner_status stat;
  97        int err;
  98
  99        switch (fdtv->ca_last_command) {
 100        case EN50221_TAG_APP_INFO_ENQUIRY:
 101                err = fdtv_ca_app_info(fdtv, arg);
 102                break;
 103        case EN50221_TAG_CA_INFO_ENQUIRY:
 104                err = fdtv_ca_info(fdtv, arg);
 105                break;
 106        default:
 107                err = avc_tuner_status(fdtv, &stat);
 108                if (err)
 109                        break;
 110                if (stat.ca_mmi == 1)
 111                        err = fdtv_ca_get_mmi(fdtv, arg);
 112                else {
 113                        dev_info(fdtv->device, "unhandled CA message 0x%08x\n",
 114                                 fdtv->ca_last_command);
 115                        err = -EACCES;
 116                }
 117        }
 118        fdtv->ca_last_command = 0;
 119        return err;
 120}
 121
 122static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
 123{
 124        struct ca_msg *msg = arg;
 125        int data_pos;
 126        int data_length;
 127        int i;
 128
 129        data_pos = 4;
 130        if (msg->msg[3] & 0x80) {
 131                data_length = 0;
 132                for (i = 0; i < (msg->msg[3] & 0x7f); i++)
 133                        data_length = (data_length << 8) + msg->msg[data_pos++];
 134        } else {
 135                data_length = msg->msg[3];
 136        }
 137        if (data_length > sizeof(msg->msg) - data_pos)
 138                return -EINVAL;
 139
 140        return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
 141}
 142
 143static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg)
 144{
 145        struct ca_msg *msg = arg;
 146        int err;
 147
 148        /* Do we need a semaphore for this? */
 149        fdtv->ca_last_command =
 150                (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2];
 151        switch (fdtv->ca_last_command) {
 152        case EN50221_TAG_CA_PMT:
 153                err = fdtv_ca_pmt(fdtv, arg);
 154                break;
 155        case EN50221_TAG_APP_INFO_ENQUIRY:
 156                /* handled in ca_get_msg */
 157                err = 0;
 158                break;
 159        case EN50221_TAG_CA_INFO_ENQUIRY:
 160                /* handled in ca_get_msg */
 161                err = 0;
 162                break;
 163        case EN50221_TAG_ENTER_MENU:
 164                err = avc_ca_enter_menu(fdtv);
 165                break;
 166        default:
 167                dev_err(fdtv->device, "unhandled CA message 0x%08x\n",
 168                        fdtv->ca_last_command);
 169                err = -EACCES;
 170        }
 171        return err;
 172}
 173
 174static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg)
 175{
 176        struct dvb_device *dvbdev = file->private_data;
 177        struct firedtv *fdtv = dvbdev->priv;
 178        struct firedtv_tuner_status stat;
 179        int err;
 180
 181        switch (cmd) {
 182        case CA_RESET:
 183                err = avc_ca_reset(fdtv);
 184                break;
 185        case CA_GET_CAP:
 186                err = fdtv_ca_get_caps(arg);
 187                break;
 188        case CA_GET_SLOT_INFO:
 189                err = fdtv_ca_get_slot_info(fdtv, arg);
 190                break;
 191        case CA_GET_MSG:
 192                err = fdtv_ca_get_msg(fdtv, arg);
 193                break;
 194        case CA_SEND_MSG:
 195                err = fdtv_ca_send_msg(fdtv, arg);
 196                break;
 197        default:
 198                dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd);
 199                err = -EOPNOTSUPP;
 200        }
 201
 202        /* FIXME Is this necessary? */
 203        avc_tuner_status(fdtv, &stat);
 204
 205        return err;
 206}
 207
 208static __poll_t fdtv_ca_io_poll(struct file *file, poll_table *wait)
 209{
 210        return EPOLLIN;
 211}
 212
 213static const struct file_operations fdtv_ca_fops = {
 214        .owner          = THIS_MODULE,
 215        .unlocked_ioctl = dvb_generic_ioctl,
 216        .open           = dvb_generic_open,
 217        .release        = dvb_generic_release,
 218        .poll           = fdtv_ca_io_poll,
 219        .llseek         = noop_llseek,
 220};
 221
 222static const struct dvb_device fdtv_ca = {
 223        .users          = 1,
 224        .readers        = 1,
 225        .writers        = 1,
 226        .fops           = &fdtv_ca_fops,
 227        .kernel_ioctl   = fdtv_ca_ioctl,
 228};
 229
 230int fdtv_ca_register(struct firedtv *fdtv)
 231{
 232        struct firedtv_tuner_status stat;
 233        int err;
 234
 235        if (avc_tuner_status(fdtv, &stat))
 236                return -EINVAL;
 237
 238        if (!fdtv_ca_ready(&stat))
 239                return -EFAULT;
 240
 241        err = dvb_register_device(&fdtv->adapter, &fdtv->cadev,
 242                                  &fdtv_ca, fdtv, DVB_DEVICE_CA, 0);
 243
 244        if (stat.ca_application_info == 0)
 245                dev_err(fdtv->device, "CaApplicationInfo is not set\n");
 246        if (stat.ca_date_time_request == 1)
 247                avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval);
 248
 249        return err;
 250}
 251
 252void fdtv_ca_release(struct firedtv *fdtv)
 253{
 254        dvb_unregister_device(fdtv->cadev);
 255}
 256