Path: blob/master/drivers/media/dvb/firewire/firedtv-ci.c
15112 views
/*1* FireDTV driver (formerly known as FireSAT)2*3* Copyright (C) 2004 Andreas Monitzer <[email protected]>4* Copyright (C) 2008 Henrik Kurelid <[email protected]>5*6* This program is free software; you can redistribute it and/or7* modify it under the terms of the GNU General Public License as8* published by the Free Software Foundation; either version 2 of9* the License, or (at your option) any later version.10*/1112#include <linux/device.h>13#include <linux/dvb/ca.h>14#include <linux/fs.h>15#include <linux/module.h>1617#include <dvbdev.h>1819#include "firedtv.h"2021#define EN50221_TAG_APP_INFO_ENQUIRY 0x9f802022#define EN50221_TAG_CA_INFO_ENQUIRY 0x9f803023#define EN50221_TAG_CA_PMT 0x9f803224#define EN50221_TAG_ENTER_MENU 0x9f80222526static int fdtv_ca_ready(struct firedtv_tuner_status *stat)27{28return stat->ca_initialization_status == 1 &&29stat->ca_error_flag == 0 &&30stat->ca_dvb_flag == 1 &&31stat->ca_module_present_status == 1;32}3334static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat)35{36int flags = 0;3738if (stat->ca_module_present_status == 1)39flags |= CA_CI_MODULE_PRESENT;40if (stat->ca_initialization_status == 1 &&41stat->ca_error_flag == 0 &&42stat->ca_dvb_flag == 1)43flags |= CA_CI_MODULE_READY;44return flags;45}4647static int fdtv_ca_reset(struct firedtv *fdtv)48{49return avc_ca_reset(fdtv) ? -EFAULT : 0;50}5152static int fdtv_ca_get_caps(void *arg)53{54struct ca_caps *cap = arg;5556cap->slot_num = 1;57cap->slot_type = CA_CI;58cap->descr_num = 1;59cap->descr_type = CA_ECD;60return 0;61}6263static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg)64{65struct firedtv_tuner_status stat;66struct ca_slot_info *slot = arg;6768if (avc_tuner_status(fdtv, &stat))69return -EFAULT;7071if (slot->num != 0)72return -EFAULT;7374slot->type = CA_CI;75slot->flags = fdtv_get_ca_flags(&stat);76return 0;77}7879static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg)80{81struct ca_msg *reply = arg;8283return avc_ca_app_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0;84}8586static int fdtv_ca_info(struct firedtv *fdtv, void *arg)87{88struct ca_msg *reply = arg;8990return avc_ca_info(fdtv, reply->msg, &reply->length) ? -EFAULT : 0;91}9293static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg)94{95struct ca_msg *reply = arg;9697return avc_ca_get_mmi(fdtv, reply->msg, &reply->length) ? -EFAULT : 0;98}99100static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg)101{102struct firedtv_tuner_status stat;103int err;104105switch (fdtv->ca_last_command) {106case EN50221_TAG_APP_INFO_ENQUIRY:107err = fdtv_ca_app_info(fdtv, arg);108break;109case EN50221_TAG_CA_INFO_ENQUIRY:110err = fdtv_ca_info(fdtv, arg);111break;112default:113if (avc_tuner_status(fdtv, &stat))114err = -EFAULT;115else if (stat.ca_mmi == 1)116err = fdtv_ca_get_mmi(fdtv, arg);117else {118dev_info(fdtv->device, "unhandled CA message 0x%08x\n",119fdtv->ca_last_command);120err = -EFAULT;121}122}123fdtv->ca_last_command = 0;124return err;125}126127static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg)128{129struct ca_msg *msg = arg;130int data_pos;131int data_length;132int i;133134data_pos = 4;135if (msg->msg[3] & 0x80) {136data_length = 0;137for (i = 0; i < (msg->msg[3] & 0x7f); i++)138data_length = (data_length << 8) + msg->msg[data_pos++];139} else {140data_length = msg->msg[3];141}142143return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length) ? -EFAULT : 0;144}145146static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg)147{148struct ca_msg *msg = arg;149int err;150151/* Do we need a semaphore for this? */152fdtv->ca_last_command =153(msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2];154switch (fdtv->ca_last_command) {155case EN50221_TAG_CA_PMT:156err = fdtv_ca_pmt(fdtv, arg);157break;158case EN50221_TAG_APP_INFO_ENQUIRY:159/* handled in ca_get_msg */160err = 0;161break;162case EN50221_TAG_CA_INFO_ENQUIRY:163/* handled in ca_get_msg */164err = 0;165break;166case EN50221_TAG_ENTER_MENU:167err = avc_ca_enter_menu(fdtv);168break;169default:170dev_err(fdtv->device, "unhandled CA message 0x%08x\n",171fdtv->ca_last_command);172err = -EFAULT;173}174return err;175}176177static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg)178{179struct dvb_device *dvbdev = file->private_data;180struct firedtv *fdtv = dvbdev->priv;181struct firedtv_tuner_status stat;182int err;183184switch (cmd) {185case CA_RESET:186err = fdtv_ca_reset(fdtv);187break;188case CA_GET_CAP:189err = fdtv_ca_get_caps(arg);190break;191case CA_GET_SLOT_INFO:192err = fdtv_ca_get_slot_info(fdtv, arg);193break;194case CA_GET_MSG:195err = fdtv_ca_get_msg(fdtv, arg);196break;197case CA_SEND_MSG:198err = fdtv_ca_send_msg(fdtv, arg);199break;200default:201dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd);202err = -EOPNOTSUPP;203}204205/* FIXME Is this necessary? */206avc_tuner_status(fdtv, &stat);207208return err;209}210211static unsigned int fdtv_ca_io_poll(struct file *file, poll_table *wait)212{213return POLLIN;214}215216static 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.llseek = noop_llseek,223};224225static struct dvb_device fdtv_ca = {226.users = 1,227.readers = 1,228.writers = 1,229.fops = &fdtv_ca_fops,230.kernel_ioctl = fdtv_ca_ioctl,231};232233int fdtv_ca_register(struct firedtv *fdtv)234{235struct firedtv_tuner_status stat;236int err;237238if (avc_tuner_status(fdtv, &stat))239return -EINVAL;240241if (!fdtv_ca_ready(&stat))242return -EFAULT;243244err = dvb_register_device(&fdtv->adapter, &fdtv->cadev,245&fdtv_ca, fdtv, DVB_DEVICE_CA);246247if (stat.ca_application_info == 0)248dev_err(fdtv->device, "CaApplicationInfo is not set\n");249if (stat.ca_date_time_request == 1)250avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval);251252return err;253}254255void fdtv_ca_release(struct firedtv *fdtv)256{257if (fdtv->cadev)258dvb_unregister_device(fdtv->cadev);259}260261262