1 /* 2 * FireDTV driver (formerly known as FireSAT) 3 * 4 * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> 5 * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU General Public License as 9 * published by the Free Software Foundation; either version 2 of 10 * the License, or (at your option) any later version. 11 */ 12 13 #include <linux/device.h> 14 #include <linux/dvb/ca.h> 15 #include <linux/fs.h> 16 #include <linux/module.h> 17 18 #include <dvbdev.h> 19 20 #include "firedtv.h" 21 22 #define EN50221_TAG_APP_INFO_ENQUIRY 0x9f8020 23 #define EN50221_TAG_CA_INFO_ENQUIRY 0x9f8030 24 #define EN50221_TAG_CA_PMT 0x9f8032 25 #define EN50221_TAG_ENTER_MENU 0x9f8022 26 27 static int fdtv_ca_ready(struct firedtv_tuner_status *stat) 28 { 29 return stat->ca_initialization_status == 1 && 30 stat->ca_error_flag == 0 && 31 stat->ca_dvb_flag == 1 && 32 stat->ca_module_present_status == 1; 33 } 34 35 static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat) 36 { 37 int flags = 0; 38 39 if (stat->ca_module_present_status == 1) 40 flags |= CA_CI_MODULE_PRESENT; 41 if (stat->ca_initialization_status == 1 && 42 stat->ca_error_flag == 0 && 43 stat->ca_dvb_flag == 1) 44 flags |= CA_CI_MODULE_READY; 45 return flags; 46 } 47 48 static int fdtv_ca_get_caps(void *arg) 49 { 50 struct ca_caps *cap = arg; 51 52 cap->slot_num = 1; 53 cap->slot_type = CA_CI; 54 cap->descr_num = 1; 55 cap->descr_type = CA_ECD; 56 return 0; 57 } 58 59 static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) 60 { 61 struct firedtv_tuner_status stat; 62 struct ca_slot_info *slot = arg; 63 int err; 64 65 err = avc_tuner_status(fdtv, &stat); 66 if (err) 67 return err; 68 69 if (slot->num != 0) 70 return -EACCES; 71 72 slot->type = CA_CI; 73 slot->flags = fdtv_get_ca_flags(&stat); 74 return 0; 75 } 76 77 static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) 78 { 79 struct ca_msg *reply = arg; 80 81 return avc_ca_app_info(fdtv, reply->msg, &reply->length); 82 } 83 84 static int fdtv_ca_info(struct firedtv *fdtv, void *arg) 85 { 86 struct ca_msg *reply = arg; 87 88 return avc_ca_info(fdtv, reply->msg, &reply->length); 89 } 90 91 static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) 92 { 93 struct ca_msg *reply = arg; 94 95 return avc_ca_get_mmi(fdtv, reply->msg, &reply->length); 96 } 97 98 static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) 99 { 100 struct firedtv_tuner_status stat; 101 int err; 102 103 switch (fdtv->ca_last_command) { 104 case EN50221_TAG_APP_INFO_ENQUIRY: 105 err = fdtv_ca_app_info(fdtv, arg); 106 break; 107 case EN50221_TAG_CA_INFO_ENQUIRY: 108 err = fdtv_ca_info(fdtv, arg); 109 break; 110 default: 111 err = avc_tuner_status(fdtv, &stat); 112 if (err) 113 break; 114 if (stat.ca_mmi == 1) 115 err = fdtv_ca_get_mmi(fdtv, arg); 116 else { 117 dev_info(fdtv->device, "unhandled CA message 0x%08x\n", 118 fdtv->ca_last_command); 119 err = -EACCES; 120 } 121 } 122 fdtv->ca_last_command = 0; 123 return err; 124 } 125 126 static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) 127 { 128 struct ca_msg *msg = arg; 129 int data_pos; 130 int data_length; 131 int i; 132 133 data_pos = 4; 134 if (msg->msg[3] & 0x80) { 135 data_length = 0; 136 for (i = 0; i < (msg->msg[3] & 0x7f); i++) 137 data_length = (data_length << 8) + msg->msg[data_pos++]; 138 } else { 139 data_length = msg->msg[3]; 140 } 141 142 return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length); 143 } 144 145 static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) 146 { 147 struct ca_msg *msg = arg; 148 int err; 149 150 /* Do we need a semaphore for this? */ 151 fdtv->ca_last_command = 152 (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; 153 switch (fdtv->ca_last_command) { 154 case EN50221_TAG_CA_PMT: 155 err = fdtv_ca_pmt(fdtv, arg); 156 break; 157 case EN50221_TAG_APP_INFO_ENQUIRY: 158 /* handled in ca_get_msg */ 159 err = 0; 160 break; 161 case EN50221_TAG_CA_INFO_ENQUIRY: 162 /* handled in ca_get_msg */ 163 err = 0; 164 break; 165 case EN50221_TAG_ENTER_MENU: 166 err = avc_ca_enter_menu(fdtv); 167 break; 168 default: 169 dev_err(fdtv->device, "unhandled CA message 0x%08x\n", 170 fdtv->ca_last_command); 171 err = -EACCES; 172 } 173 return err; 174 } 175 176 static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg) 177 { 178 struct dvb_device *dvbdev = file->private_data; 179 struct firedtv *fdtv = dvbdev->priv; 180 struct firedtv_tuner_status stat; 181 int err; 182 183 switch (cmd) { 184 case CA_RESET: 185 err = avc_ca_reset(fdtv); 186 break; 187 case CA_GET_CAP: 188 err = fdtv_ca_get_caps(arg); 189 break; 190 case CA_GET_SLOT_INFO: 191 err = fdtv_ca_get_slot_info(fdtv, arg); 192 break; 193 case CA_GET_MSG: 194 err = fdtv_ca_get_msg(fdtv, arg); 195 break; 196 case CA_SEND_MSG: 197 err = fdtv_ca_send_msg(fdtv, arg); 198 break; 199 default: 200 dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); 201 err = -EOPNOTSUPP; 202 } 203 204 /* FIXME Is this necessary? */ 205 avc_tuner_status(fdtv, &stat); 206 207 return err; 208 } 209 210 static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) 211 { 212 return POLLIN; 213 } 214 215 static const struct file_operations fdtv_ca_fops = { 216 .owner = THIS_MODULE, 217 .unlocked_ioctl = dvb_generic_ioctl, 218 .open = dvb_generic_open, 219 .release = dvb_generic_release, 220 .poll = fdtv_ca_io_poll, 221 .llseek = noop_llseek, 222 }; 223 224 static struct dvb_device fdtv_ca = { 225 .users = 1, 226 .readers = 1, 227 .writers = 1, 228 .fops = &fdtv_ca_fops, 229 .kernel_ioctl = fdtv_ca_ioctl, 230 }; 231 232 int fdtv_ca_register(struct firedtv *fdtv) 233 { 234 struct firedtv_tuner_status stat; 235 int err; 236 237 if (avc_tuner_status(fdtv, &stat)) 238 return -EINVAL; 239 240 if (!fdtv_ca_ready(&stat)) 241 return -EFAULT; 242 243 err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, 244 &fdtv_ca, fdtv, DVB_DEVICE_CA); 245 246 if (stat.ca_application_info == 0) 247 dev_err(fdtv->device, "CaApplicationInfo is not set\n"); 248 if (stat.ca_date_time_request == 1) 249 avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); 250 251 return err; 252 } 253 254 void fdtv_ca_release(struct firedtv *fdtv) 255 { 256 if (fdtv->cadev) 257 dvb_unregister_device(fdtv->cadev); 258 } 259