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 23 static 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 31 static 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 44 static 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 55 static 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 73 static 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 80 static 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 87 static 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 94 static 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 122 static 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 143 static 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 174 static 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 208 static __poll_t fdtv_ca_io_poll(struct file *file, poll_table *wait) 209 { 210 return EPOLLIN; 211 } 212 213 static 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 222 static 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 230 int 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 252 void fdtv_ca_release(struct firedtv *fdtv) 253 { 254 dvb_unregister_device(fdtv->cadev); 255 } 256