256 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 0x9f802019#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f803020#define EN50221_TAG_CA_PMT 0x9f803221#define EN50221_TAG_ENTER_MENU 0x9f802222 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