Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * FireDTV driver (formerly known as FireSAT)
0004  *
0005  * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com>
0006  * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se>
0007  */
0008 
0009 #include <linux/device.h>
0010 #include <linux/dvb/ca.h>
0011 #include <linux/fs.h>
0012 #include <linux/module.h>
0013 
0014 #include <media/dvbdev.h>
0015 
0016 #include "firedtv.h"
0017 
0018 #define EN50221_TAG_APP_INFO_ENQUIRY    0x9f8020
0019 #define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030
0020 #define EN50221_TAG_CA_PMT      0x9f8032
0021 #define EN50221_TAG_ENTER_MENU      0x9f8022
0022 
0023 static int fdtv_ca_ready(struct firedtv_tuner_status *stat)
0024 {
0025     return stat->ca_initialization_status   == 1 &&
0026            stat->ca_error_flag      == 0 &&
0027            stat->ca_dvb_flag        == 1 &&
0028            stat->ca_module_present_status   == 1;
0029 }
0030 
0031 static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat)
0032 {
0033     int flags = 0;
0034 
0035     if (stat->ca_module_present_status == 1)
0036         flags |= CA_CI_MODULE_PRESENT;
0037     if (stat->ca_initialization_status == 1 &&
0038         stat->ca_error_flag            == 0 &&
0039         stat->ca_dvb_flag              == 1)
0040         flags |= CA_CI_MODULE_READY;
0041     return flags;
0042 }
0043 
0044 static int fdtv_ca_get_caps(void *arg)
0045 {
0046     struct ca_caps *cap = arg;
0047 
0048     cap->slot_num = 1;
0049     cap->slot_type = CA_CI;
0050     cap->descr_num = 1;
0051     cap->descr_type = CA_ECD;
0052     return 0;
0053 }
0054 
0055 static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg)
0056 {
0057     struct firedtv_tuner_status stat;
0058     struct ca_slot_info *slot = arg;
0059     int err;
0060 
0061     err = avc_tuner_status(fdtv, &stat);
0062     if (err)
0063         return err;
0064 
0065     if (slot->num != 0)
0066         return -EACCES;
0067 
0068     slot->type = CA_CI;
0069     slot->flags = fdtv_get_ca_flags(&stat);
0070     return 0;
0071 }
0072 
0073 static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg)
0074 {
0075     struct ca_msg *reply = arg;
0076 
0077     return avc_ca_app_info(fdtv, reply->msg, &reply->length);
0078 }
0079 
0080 static int fdtv_ca_info(struct firedtv *fdtv, void *arg)
0081 {
0082     struct ca_msg *reply = arg;
0083 
0084     return avc_ca_info(fdtv, reply->msg, &reply->length);
0085 }
0086 
0087 static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg)
0088 {
0089     struct ca_msg *reply = arg;
0090 
0091     return avc_ca_get_mmi(fdtv, reply->msg, &reply->length);
0092 }
0093 
0094 static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg)
0095 {
0096     struct firedtv_tuner_status stat;
0097     int err;
0098 
0099     switch (fdtv->ca_last_command) {
0100     case EN50221_TAG_APP_INFO_ENQUIRY:
0101         err = fdtv_ca_app_info(fdtv, arg);
0102         break;
0103     case EN50221_TAG_CA_INFO_ENQUIRY:
0104         err = fdtv_ca_info(fdtv, arg);
0105         break;
0106     default:
0107         err = avc_tuner_status(fdtv, &stat);
0108         if (err)
0109             break;
0110         if (stat.ca_mmi == 1)
0111             err = fdtv_ca_get_mmi(fdtv, arg);
0112         else {
0113             dev_info(fdtv->device, "unhandled CA message 0x%08x\n",
0114                  fdtv->ca_last_command);
0115             err = -EACCES;
0116         }
0117     }
0118     fdtv->ca_last_command = 0;
0119     return err;
0120 }
0121 
0122 static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)
0123 {
0124     struct ca_msg *msg = arg;
0125     int data_pos;
0126     int data_length;
0127     int i;
0128 
0129     data_pos = 4;
0130     if (msg->msg[3] & 0x80) {
0131         data_length = 0;
0132         for (i = 0; i < (msg->msg[3] & 0x7f); i++)
0133             data_length = (data_length << 8) + msg->msg[data_pos++];
0134     } else {
0135         data_length = msg->msg[3];
0136     }
0137     if (data_length > sizeof(msg->msg) - data_pos)
0138         return -EINVAL;
0139 
0140     return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length);
0141 }
0142 
0143 static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg)
0144 {
0145     struct ca_msg *msg = arg;
0146     int err;
0147 
0148     /* Do we need a semaphore for this? */
0149     fdtv->ca_last_command =
0150         (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2];
0151     switch (fdtv->ca_last_command) {
0152     case EN50221_TAG_CA_PMT:
0153         err = fdtv_ca_pmt(fdtv, arg);
0154         break;
0155     case EN50221_TAG_APP_INFO_ENQUIRY:
0156         /* handled in ca_get_msg */
0157         err = 0;
0158         break;
0159     case EN50221_TAG_CA_INFO_ENQUIRY:
0160         /* handled in ca_get_msg */
0161         err = 0;
0162         break;
0163     case EN50221_TAG_ENTER_MENU:
0164         err = avc_ca_enter_menu(fdtv);
0165         break;
0166     default:
0167         dev_err(fdtv->device, "unhandled CA message 0x%08x\n",
0168             fdtv->ca_last_command);
0169         err = -EACCES;
0170     }
0171     return err;
0172 }
0173 
0174 static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg)
0175 {
0176     struct dvb_device *dvbdev = file->private_data;
0177     struct firedtv *fdtv = dvbdev->priv;
0178     struct firedtv_tuner_status stat;
0179     int err;
0180 
0181     switch (cmd) {
0182     case CA_RESET:
0183         err = avc_ca_reset(fdtv);
0184         break;
0185     case CA_GET_CAP:
0186         err = fdtv_ca_get_caps(arg);
0187         break;
0188     case CA_GET_SLOT_INFO:
0189         err = fdtv_ca_get_slot_info(fdtv, arg);
0190         break;
0191     case CA_GET_MSG:
0192         err = fdtv_ca_get_msg(fdtv, arg);
0193         break;
0194     case CA_SEND_MSG:
0195         err = fdtv_ca_send_msg(fdtv, arg);
0196         break;
0197     default:
0198         dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd);
0199         err = -EOPNOTSUPP;
0200     }
0201 
0202     /* FIXME Is this necessary? */
0203     avc_tuner_status(fdtv, &stat);
0204 
0205     return err;
0206 }
0207 
0208 static __poll_t fdtv_ca_io_poll(struct file *file, poll_table *wait)
0209 {
0210     return EPOLLIN;
0211 }
0212 
0213 static const struct file_operations fdtv_ca_fops = {
0214     .owner      = THIS_MODULE,
0215     .unlocked_ioctl = dvb_generic_ioctl,
0216     .open       = dvb_generic_open,
0217     .release    = dvb_generic_release,
0218     .poll       = fdtv_ca_io_poll,
0219     .llseek     = noop_llseek,
0220 };
0221 
0222 static const struct dvb_device fdtv_ca = {
0223     .users      = 1,
0224     .readers    = 1,
0225     .writers    = 1,
0226     .fops       = &fdtv_ca_fops,
0227     .kernel_ioctl   = fdtv_ca_ioctl,
0228 };
0229 
0230 int fdtv_ca_register(struct firedtv *fdtv)
0231 {
0232     struct firedtv_tuner_status stat;
0233     int err;
0234 
0235     if (avc_tuner_status(fdtv, &stat))
0236         return -EINVAL;
0237 
0238     if (!fdtv_ca_ready(&stat))
0239         return -EFAULT;
0240 
0241     err = dvb_register_device(&fdtv->adapter, &fdtv->cadev,
0242                   &fdtv_ca, fdtv, DVB_DEVICE_CA, 0);
0243 
0244     if (stat.ca_application_info == 0)
0245         dev_err(fdtv->device, "CaApplicationInfo is not set\n");
0246     if (stat.ca_date_time_request == 1)
0247         avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval);
0248 
0249     return err;
0250 }
0251 
0252 void fdtv_ca_release(struct firedtv *fdtv)
0253 {
0254     dvb_unregister_device(fdtv->cadev);
0255 }