Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
df4846c3 | 2 | /* |
612262a5 | 3 | * FireDTV driver (formerly known as FireSAT) |
df4846c3 | 4 | * |
612262a5 SR |
5 | * Copyright (C) 2004 Andreas Monitzer <andy@monitzer.com> |
6 | * Copyright (C) 2008 Henrik Kurelid <henrik@kurelid.se> | |
df4846c3 HK |
7 | */ |
8 | ||
15490795 | 9 | #include <linux/device.h> |
c81c8b68 | 10 | #include <linux/dvb/ca.h> |
612262a5 SR |
11 | #include <linux/fs.h> |
12 | #include <linux/module.h> | |
13 | ||
fada1935 | 14 | #include <media/dvbdev.h> |
c81c8b68 | 15 | |
a70f81c1 | 16 | #include "firedtv.h" |
612262a5 | 17 | |
15490795 SR |
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) | |
df4846c3 | 24 | { |
15490795 SR |
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; | |
df4846c3 HK |
29 | } |
30 | ||
15490795 | 31 | static int fdtv_get_ca_flags(struct firedtv_tuner_status *stat) |
df4846c3 HK |
32 | { |
33 | int flags = 0; | |
8ae83cdf | 34 | |
15490795 | 35 | if (stat->ca_module_present_status == 1) |
df4846c3 | 36 | flags |= CA_CI_MODULE_PRESENT; |
15490795 SR |
37 | if (stat->ca_initialization_status == 1 && |
38 | stat->ca_error_flag == 0 && | |
39 | stat->ca_dvb_flag == 1) | |
df4846c3 HK |
40 | flags |= CA_CI_MODULE_READY; |
41 | return flags; | |
42 | } | |
43 | ||
a70f81c1 | 44 | static int fdtv_ca_get_caps(void *arg) |
df4846c3 | 45 | { |
8ae83cdf SR |
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; | |
df4846c3 HK |
53 | } |
54 | ||
a70f81c1 | 55 | static int fdtv_ca_get_slot_info(struct firedtv *fdtv, void *arg) |
df4846c3 | 56 | { |
15490795 | 57 | struct firedtv_tuner_status stat; |
8ae83cdf | 58 | struct ca_slot_info *slot = arg; |
686a9488 | 59 | int err; |
df4846c3 | 60 | |
686a9488 SR |
61 | err = avc_tuner_status(fdtv, &stat); |
62 | if (err) | |
63 | return err; | |
df4846c3 | 64 | |
8ae83cdf | 65 | if (slot->num != 0) |
686a9488 | 66 | return -EACCES; |
8ae83cdf SR |
67 | |
68 | slot->type = CA_CI; | |
15490795 | 69 | slot->flags = fdtv_get_ca_flags(&stat); |
df4846c3 HK |
70 | return 0; |
71 | } | |
72 | ||
a70f81c1 | 73 | static int fdtv_ca_app_info(struct firedtv *fdtv, void *arg) |
df4846c3 | 74 | { |
8ae83cdf | 75 | struct ca_msg *reply = arg; |
df4846c3 | 76 | |
686a9488 | 77 | return avc_ca_app_info(fdtv, reply->msg, &reply->length); |
df4846c3 HK |
78 | } |
79 | ||
a70f81c1 | 80 | static int fdtv_ca_info(struct firedtv *fdtv, void *arg) |
df4846c3 | 81 | { |
8ae83cdf | 82 | struct ca_msg *reply = arg; |
df4846c3 | 83 | |
686a9488 | 84 | return avc_ca_info(fdtv, reply->msg, &reply->length); |
df4846c3 HK |
85 | } |
86 | ||
a70f81c1 | 87 | static int fdtv_ca_get_mmi(struct firedtv *fdtv, void *arg) |
df4846c3 | 88 | { |
8ae83cdf | 89 | struct ca_msg *reply = arg; |
df4846c3 | 90 | |
686a9488 | 91 | return avc_ca_get_mmi(fdtv, reply->msg, &reply->length); |
df4846c3 HK |
92 | } |
93 | ||
a70f81c1 | 94 | static int fdtv_ca_get_msg(struct firedtv *fdtv, void *arg) |
df4846c3 | 95 | { |
15490795 | 96 | struct firedtv_tuner_status stat; |
8ae83cdf | 97 | int err; |
df4846c3 | 98 | |
a70f81c1 | 99 | switch (fdtv->ca_last_command) { |
15490795 | 100 | case EN50221_TAG_APP_INFO_ENQUIRY: |
a70f81c1 | 101 | err = fdtv_ca_app_info(fdtv, arg); |
df4846c3 | 102 | break; |
15490795 | 103 | case EN50221_TAG_CA_INFO_ENQUIRY: |
a70f81c1 | 104 | err = fdtv_ca_info(fdtv, arg); |
c81c8b68 | 105 | break; |
df4846c3 | 106 | default: |
686a9488 SR |
107 | err = avc_tuner_status(fdtv, &stat); |
108 | if (err) | |
109 | break; | |
110 | if (stat.ca_mmi == 1) | |
a70f81c1 | 111 | err = fdtv_ca_get_mmi(fdtv, arg); |
df4846c3 | 112 | else { |
15490795 SR |
113 | dev_info(fdtv->device, "unhandled CA message 0x%08x\n", |
114 | fdtv->ca_last_command); | |
686a9488 | 115 | err = -EACCES; |
df4846c3 HK |
116 | } |
117 | } | |
a70f81c1 | 118 | fdtv->ca_last_command = 0; |
df4846c3 HK |
119 | return err; |
120 | } | |
c81c8b68 | 121 | |
a70f81c1 | 122 | static int fdtv_ca_pmt(struct firedtv *fdtv, void *arg) |
df4846c3 | 123 | { |
8ae83cdf | 124 | struct ca_msg *msg = arg; |
df4846c3 | 125 | int data_pos; |
7199e523 HK |
126 | int data_length; |
127 | int i; | |
128 | ||
129 | data_pos = 4; | |
130 | if (msg->msg[3] & 0x80) { | |
131 | data_length = 0; | |
15490795 | 132 | for (i = 0; i < (msg->msg[3] & 0x7f); i++) |
7199e523 HK |
133 | data_length = (data_length << 8) + msg->msg[data_pos++]; |
134 | } else { | |
135 | data_length = msg->msg[3]; | |
136 | } | |
df4846c3 | 137 | |
686a9488 | 138 | return avc_ca_pmt(fdtv, &msg->msg[data_pos], data_length); |
df4846c3 HK |
139 | } |
140 | ||
a70f81c1 | 141 | static int fdtv_ca_send_msg(struct firedtv *fdtv, void *arg) |
df4846c3 | 142 | { |
8ae83cdf | 143 | struct ca_msg *msg = arg; |
df4846c3 | 144 | int err; |
df4846c3 | 145 | |
8ae83cdf | 146 | /* Do we need a semaphore for this? */ |
a70f81c1 | 147 | fdtv->ca_last_command = |
8ae83cdf | 148 | (msg->msg[0] << 16) + (msg->msg[1] << 8) + msg->msg[2]; |
a70f81c1 | 149 | switch (fdtv->ca_last_command) { |
15490795 | 150 | case EN50221_TAG_CA_PMT: |
a70f81c1 | 151 | err = fdtv_ca_pmt(fdtv, arg); |
df4846c3 | 152 | break; |
15490795 | 153 | case EN50221_TAG_APP_INFO_ENQUIRY: |
8ae83cdf | 154 | /* handled in ca_get_msg */ |
c81c8b68 GKH |
155 | err = 0; |
156 | break; | |
15490795 | 157 | case EN50221_TAG_CA_INFO_ENQUIRY: |
8ae83cdf | 158 | /* handled in ca_get_msg */ |
c81c8b68 GKH |
159 | err = 0; |
160 | break; | |
15490795 | 161 | case EN50221_TAG_ENTER_MENU: |
a70f81c1 | 162 | err = avc_ca_enter_menu(fdtv); |
df4846c3 HK |
163 | break; |
164 | default: | |
15490795 SR |
165 | dev_err(fdtv->device, "unhandled CA message 0x%08x\n", |
166 | fdtv->ca_last_command); | |
686a9488 | 167 | err = -EACCES; |
c81c8b68 | 168 | } |
df4846c3 HK |
169 | return err; |
170 | } | |
171 | ||
16ef8def | 172 | static int fdtv_ca_ioctl(struct file *file, unsigned int cmd, void *arg) |
df4846c3 | 173 | { |
8ae83cdf | 174 | struct dvb_device *dvbdev = file->private_data; |
a70f81c1 | 175 | struct firedtv *fdtv = dvbdev->priv; |
15490795 | 176 | struct firedtv_tuner_status stat; |
8ae83cdf | 177 | int err; |
df4846c3 | 178 | |
15490795 | 179 | switch (cmd) { |
df4846c3 | 180 | case CA_RESET: |
686a9488 | 181 | err = avc_ca_reset(fdtv); |
df4846c3 HK |
182 | break; |
183 | case CA_GET_CAP: | |
a70f81c1 | 184 | err = fdtv_ca_get_caps(arg); |
df4846c3 HK |
185 | break; |
186 | case CA_GET_SLOT_INFO: | |
a70f81c1 | 187 | err = fdtv_ca_get_slot_info(fdtv, arg); |
df4846c3 HK |
188 | break; |
189 | case CA_GET_MSG: | |
a70f81c1 | 190 | err = fdtv_ca_get_msg(fdtv, arg); |
df4846c3 HK |
191 | break; |
192 | case CA_SEND_MSG: | |
a70f81c1 | 193 | err = fdtv_ca_send_msg(fdtv, arg); |
df4846c3 | 194 | break; |
c81c8b68 | 195 | default: |
15490795 | 196 | dev_info(fdtv->device, "unhandled CA ioctl %u\n", cmd); |
df4846c3 | 197 | err = -EOPNOTSUPP; |
c81c8b68 | 198 | } |
df4846c3 | 199 | |
8ae83cdf | 200 | /* FIXME Is this necessary? */ |
15490795 | 201 | avc_tuner_status(fdtv, &stat); |
df4846c3 | 202 | |
c81c8b68 GKH |
203 | return err; |
204 | } | |
c81c8b68 | 205 | |
c23e0cb8 | 206 | static __poll_t fdtv_ca_io_poll(struct file *file, poll_table *wait) |
df4846c3 | 207 | { |
a9a08845 | 208 | return EPOLLIN; |
c81c8b68 GKH |
209 | } |
210 | ||
828c0950 | 211 | static const struct file_operations fdtv_ca_fops = { |
8ae83cdf | 212 | .owner = THIS_MODULE, |
16ef8def | 213 | .unlocked_ioctl = dvb_generic_ioctl, |
8ae83cdf SR |
214 | .open = dvb_generic_open, |
215 | .release = dvb_generic_release, | |
a70f81c1 | 216 | .poll = fdtv_ca_io_poll, |
6038f373 | 217 | .llseek = noop_llseek, |
c81c8b68 GKH |
218 | }; |
219 | ||
a70f81c1 | 220 | static struct dvb_device fdtv_ca = { |
8ae83cdf SR |
221 | .users = 1, |
222 | .readers = 1, | |
223 | .writers = 1, | |
a70f81c1 R |
224 | .fops = &fdtv_ca_fops, |
225 | .kernel_ioctl = fdtv_ca_ioctl, | |
c81c8b68 GKH |
226 | }; |
227 | ||
a70f81c1 | 228 | int fdtv_ca_register(struct firedtv *fdtv) |
df4846c3 | 229 | { |
15490795 | 230 | struct firedtv_tuner_status stat; |
8ae83cdf | 231 | int err; |
c81c8b68 | 232 | |
15490795 | 233 | if (avc_tuner_status(fdtv, &stat)) |
df4846c3 HK |
234 | return -EINVAL; |
235 | ||
15490795 | 236 | if (!fdtv_ca_ready(&stat)) |
8ae83cdf SR |
237 | return -EFAULT; |
238 | ||
a70f81c1 | 239 | err = dvb_register_device(&fdtv->adapter, &fdtv->cadev, |
df2f94e5 | 240 | &fdtv_ca, fdtv, DVB_DEVICE_CA, 0); |
8ae83cdf | 241 | |
15490795 SR |
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) | |
a70f81c1 | 245 | avc_ca_get_time_date(fdtv, &fdtv->ca_time_interval); |
df4846c3 HK |
246 | |
247 | return err; | |
c81c8b68 GKH |
248 | } |
249 | ||
a70f81c1 | 250 | void fdtv_ca_release(struct firedtv *fdtv) |
df4846c3 | 251 | { |
d3fe22fa | 252 | dvb_unregister_device(fdtv->cadev); |
c81c8b68 | 253 | } |