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 27static 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 35static 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 48static int fdtv_ca_reset(struct firedtv *fdtv) 49{ 50 return avc_ca_reset(fdtv) ? -EFAULT : 0; 51} 52 53static int fdtv_ca_get_caps(void *arg) 54{ 55 struct ca_caps *cap = arg; 56 57 cap->slot_num = 1; 58 cap->slot_type = CA_CI; 59 cap->descr_num = 1; 60 cap->descr_type = CA_ECD; 61 return 0; 62} 63 64static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) 65{ 66 struct firedtv_tuner_status stat; 67 struct ca_slot_info *slot = arg; 68 69 if (avc_tuner_status(fdtv, &stat)) 70 return -EFAULT; 71 72 if (slot->num != 0) 73 return -EFAULT; 74 75 slot->type = CA_CI; 76 slot->flags = fdtv_get_ca_flags(&stat); 77 return 0; 78} 79 80static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) 81{ 82 struct ca_msg *reply = arg; 83 84 return avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; 85} 86 87static int fdtv_ca_info(struct firedtv *fdtv, void *arg) 88{ 89 struct ca_msg *reply = arg; 90 91 return avc_ca_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; 92} 93 94static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) 95{ 96 struct ca_msg *reply = arg; 97 98 return avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0; 99} 100 101static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) 102{ 103 struct firedtv_tuner_status stat; 104 int err; 105 106 switch (fdtv->ca_last_command) { 107 case EN50221_TAG_APP_INFO_ENQUIRY: 108 err = fdtv_ca_app_info(fdtv, arg); 109 break; 110 case EN50221_TAG_CA_INFO_ENQUIRY: 111 err = fdtv_ca_info(fdtv, arg); 112 break; 113 default: 114 if (avc_tuner_status(fdtv, &stat)) 115 err = -EFAULT; 116 else if (stat.ca_mmi == 1) 117 err = fdtv_ca_get_mmi(fdtv, arg); 118 else { 119 dev_info(fdtv->device, "unhandled CA message 0x%08x\n", 120 fdtv->ca_last_command); 121 err = -EFAULT; 122 } 123 } 124 fdtv->ca_last_command = 0; 125 return err; 126} 127 128static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) 129{ 130 struct ca_msg *msg = arg; 131 int data_pos; 132 int data_length; 133 int i; 134 135 data_pos = 4; 136 if (msg->msg[3] & 0x80) { 137 data_length = 0; 138 for (i = 0; i < (msg->msg[3] & 0x7f); i++) 139 data_length = (data_length << 8) + msg->msg[data_pos++]; 140 } else { 141 data_length = msg->msg[3]; 142 } 143 144 return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? -EFAULT : 0; 145} 146 147static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) 148{ 149 struct ca_msg *msg = arg; 150 int err; 151 152 /* Do we need a semaphore for this? */ 153 fdtv->ca_last_command = 154 (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; 155 switch (fdtv->ca_last_command) { 156 case EN50221_TAG_CA_PMT: 157 err = fdtv_ca_pmt(fdtv, arg); 158 break; 159 case EN50221_TAG_APP_INFO_ENQUIRY: 160 /* handled in ca_get_msg */ 161 err = 0; 162 break; 163 case EN50221_TAG_CA_INFO_ENQUIRY: 164 /* handled in ca_get_msg */ 165 err = 0; 166 break; 167 case EN50221_TAG_ENTER_MENU: 168 err = avc_ca_enter_menu(fdtv); 169 break; 170 default: 171 dev_err(fdtv->device, "unhandled CA message 0x%08x\n", 172 fdtv->ca_last_command); 173 err = -EFAULT; 174 } 175 return err; 176} 177 178static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg) 179{ 180 struct dvb_device *dvbdev = file->private_data; 181 struct firedtv *fdtv = dvbdev->priv; 182 struct firedtv_tuner_status stat; 183 int err; 184 185 switch (cmd) { 186 case CA_RESET: 187 err = fdtv_ca_reset(fdtv); 188 break; 189 case CA_GET_CAP: 190 err = fdtv_ca_get_caps(arg); 191 break; 192 case CA_GET_SLOT_INFO: 193 err = fdtv_ca_get_slot_info(fdtv, arg); 194 break; 195 case CA_GET_MSG: 196 err = fdtv_ca_get_msg(fdtv, arg); 197 break; 198 case CA_SEND_MSG: 199 err = fdtv_ca_send_msg(fdtv, arg); 200 break; 201 default: 202 dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); 203 err = -EOPNOTSUPP; 204 } 205 206 avc_tuner_status(fdtv, &stat); 207 208 return err; 209} 210 211static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait) 212{ 213 return POLLIN; 214} 215 216static const struct file_operations fdtv_ca_fops = { 217 .owner = THIS_MODULE, 218 .unlocked_ioctl = dvb_generic_ioctl, 219 .open = dvb_generic_open, 220 .release = dvb_generic_release, 221 .poll = fdtv_ca_io_poll, 222}; 223 224static 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 232int 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 254void fdtv_ca_release(struct firedtv *fdtv) 255{ 256 if (fdtv->cadev) 257 dvb_unregister_device(fdtv->cadev); 258} 259