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 138 return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length); 139 } 140 141 static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) 142 { 143 struct ca_msg *msg = arg; 144 int err; 145 146 /* Do we need a semaphore for this? */ 147 fdtv->ca_last_command = 148 (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; 149 switch (fdtv->ca_last_command) { 150 case EN50221_TAG_CA_PMT: 151 err = fdtv_ca_pmt(fdtv, arg); 152 break; 153 case EN50221_TAG_APP_INFO_ENQUIRY: 154 /* handled in ca_get_msg */ 155 err = 0; 156 break; 157 case EN50221_TAG_CA_INFO_ENQUIRY: 158 /* handled in ca_get_msg */ 159 err = 0; 160 break; 161 case EN50221_TAG_ENTER_MENU: 162 err = avc_ca_enter_menu(fdtv); 163 break; 164 default: 165 dev_err(fdtv->device, "unhandled CA message 0x%08x\n", 166 fdtv->ca_last_command); 167 err = -EACCES; 168 } 169 return err; 170 } 171 172 static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg) 173 { 174 struct dvb_device *dvbdev = file->private_data; 175 struct firedtv *fdtv = dvbdev->priv; 176 struct firedtv_tuner_status stat; 177 int err; 178 179 switch (cmd) { 180 case CA_RESET: 181 err = avc_ca_reset(fdtv); 182 break; 183 case CA_GET_CAP: 184 err = fdtv_ca_get_caps(arg); 185 break; 186 case CA_GET_SLOT_INFO: 187 err = fdtv_ca_get_slot_info(fdtv, arg); 188 break; 189 case CA_GET_MSG: 190 err = fdtv_ca_get_msg(fdtv, arg); 191 break; 192 case CA_SEND_MSG: 193 err = fdtv_ca_send_msg(fdtv, arg); 194 break; 195 default: 196 dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); 197 err = -EOPNOTSUPP; 198 } 199 200 /* FIXME Is this necessary? */ 201 avc_tuner_status(fdtv, &stat); 202 203 return err; 204 } 205 206 static __poll_t fdtv_ca_io_poll(struct file *file, poll_table *wait) 207 { 208 return EPOLLIN; 209 } 210 211 static const struct file_operations fdtv_ca_fops = { 212 .owner = THIS_MODULE, 213 .unlocked_ioctl = dvb_generic_ioctl, 214 .open = dvb_generic_open, 215 .release = dvb_generic_release, 216 .poll = fdtv_ca_io_poll, 217 .llseek = noop_llseek, 218 }; 219 220 static const struct dvb_device fdtv_ca = { 221 .users = 1, 222 .readers = 1, 223 .writers = 1, 224 .fops = &fdtv_ca_fops, 225 .kernel_ioctl = fdtv_ca_ioctl, 226 }; 227 228 int fdtv_ca_register(struct firedtv *fdtv) 229 { 230 struct firedtv_tuner_status stat; 231 int err; 232 233 if (avc_tuner_status(fdtv, &stat)) 234 return -EINVAL; 235 236 if (!fdtv_ca_ready(&stat)) 237 return -EFAULT; 238 239 err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, 240 &fdtv_ca, fdtv, DVB_DEVICE_CA, 0); 241 242 if (stat.ca_application_info == 0) 243 dev_err(fdtv->device, "CaApplicationInfo is not set\n"); 244 if (stat.ca_date_time_request == 1) 245 avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); 246 247 return err; 248 } 249 250 void fdtv_ca_release(struct firedtv *fdtv) 251 { 252 dvb_unregister_device(fdtv->cadev); 253 } 254