0001
0002
0003
0004
0005
0006
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
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
0157 err = 0;
0158 break;
0159 case EN50221_TAG_CA_INFO_ENQUIRY:
0160
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
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 }