]>
Commit | Line | Data |
---|---|---|
50b215a0 JS |
1 | /* |
2 | CA-driver for TwinHan DST Frontend/Card | |
3 | ||
4 | Copyright (C) 2004, 2005 Manu Abraham (manu@kromtek.com) | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | ||
50b215a0 JS |
21 | #include <linux/kernel.h> |
22 | #include <linux/module.h> | |
5a0e3ad6 | 23 | #include <linux/slab.h> |
50b215a0 | 24 | #include <linux/init.h> |
405f5571 | 25 | #include <linux/smp_lock.h> |
50b215a0 | 26 | #include <linux/string.h> |
50b215a0 JS |
27 | #include <linux/dvb/ca.h> |
28 | #include "dvbdev.h" | |
29 | #include "dvb_frontend.h" | |
50b215a0 JS |
30 | #include "dst_ca.h" |
31 | #include "dst_common.h" | |
32 | ||
a427de6f MA |
33 | #define DST_CA_ERROR 0 |
34 | #define DST_CA_NOTICE 1 | |
35 | #define DST_CA_INFO 2 | |
36 | #define DST_CA_DEBUG 3 | |
37 | ||
38 | #define dprintk(x, y, z, format, arg...) do { \ | |
39 | if (z) { \ | |
40 | if ((x > DST_CA_ERROR) && (x > y)) \ | |
b2e62e7c | 41 | printk(KERN_ERR "%s: " format "\n", __func__ , ##arg); \ |
a427de6f | 42 | else if ((x > DST_CA_NOTICE) && (x > y)) \ |
b2e62e7c | 43 | printk(KERN_NOTICE "%s: " format "\n", __func__ , ##arg); \ |
a427de6f | 44 | else if ((x > DST_CA_INFO) && (x > y)) \ |
b2e62e7c | 45 | printk(KERN_INFO "%s: " format "\n", __func__ , ##arg); \ |
a427de6f | 46 | else if ((x > DST_CA_DEBUG) && (x > y)) \ |
b2e62e7c | 47 | printk(KERN_DEBUG "%s: " format "\n", __func__ , ##arg); \ |
a427de6f MA |
48 | } else { \ |
49 | if (x > y) \ | |
50 | printk(format, ## arg); \ | |
51 | } \ | |
52 | } while(0) | |
53 | ||
54 | ||
7d53421c | 55 | static unsigned int verbose = 5; |
50b215a0 JS |
56 | module_param(verbose, int, 0644); |
57 | MODULE_PARM_DESC(verbose, "verbose startup messages, default is 1 (yes)"); | |
58 | ||
4a2cc126 | 59 | /* Need some more work */ |
50b215a0 JS |
60 | static int ca_set_slot_descr(void) |
61 | { | |
62 | /* We could make this more graceful ? */ | |
63 | return -EOPNOTSUPP; | |
64 | } | |
65 | ||
4a2cc126 | 66 | /* Need some more work */ |
50b215a0 JS |
67 | static int ca_set_pid(void) |
68 | { | |
69 | /* We could make this more graceful ? */ | |
70 | return -EOPNOTSUPP; | |
71 | } | |
72 | ||
1f19456d HS |
73 | static void put_command_and_length(u8 *data, int command, int length) |
74 | { | |
75 | data[0] = (command >> 16) & 0xff; | |
76 | data[1] = (command >> 8) & 0xff; | |
77 | data[2] = command & 0xff; | |
78 | data[3] = length; | |
79 | } | |
50b215a0 | 80 | |
4fbbc7ee | 81 | static void put_checksum(u8 *check_string, int length) |
50b215a0 | 82 | { |
4fbbc7ee PA |
83 | dprintk(verbose, DST_CA_DEBUG, 1, " Computing string checksum."); |
84 | dprintk(verbose, DST_CA_DEBUG, 1, " -> string length : 0x%02x", length); | |
85 | check_string[length] = dst_check_sum (check_string, length); | |
86 | dprintk(verbose, DST_CA_DEBUG, 1, " -> checksum : 0x%02x", check_string[length]); | |
50b215a0 JS |
87 | } |
88 | ||
89 | static int dst_ci_command(struct dst_state* state, u8 * data, u8 *ca_string, u8 len, int read) | |
90 | { | |
91 | u8 reply; | |
92 | ||
3593cab5 | 93 | mutex_lock(&state->dst_mutex); |
50b215a0 JS |
94 | dst_comm_init(state); |
95 | msleep(65); | |
96 | ||
97 | if (write_dst(state, data, len)) { | |
a427de6f | 98 | dprintk(verbose, DST_CA_INFO, 1, " Write not successful, trying to recover"); |
50b215a0 | 99 | dst_error_recovery(state); |
d28d5762 | 100 | goto error; |
50b215a0 | 101 | } |
50b215a0 | 102 | if ((dst_pio_disable(state)) < 0) { |
a427de6f | 103 | dprintk(verbose, DST_CA_ERROR, 1, " DST PIO disable failed."); |
d28d5762 | 104 | goto error; |
50b215a0 | 105 | } |
50b215a0 | 106 | if (read_dst(state, &reply, GET_ACK) < 0) { |
a427de6f | 107 | dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); |
50b215a0 | 108 | dst_error_recovery(state); |
d28d5762 | 109 | goto error; |
50b215a0 | 110 | } |
50b215a0 JS |
111 | if (read) { |
112 | if (! dst_wait_dst_ready(state, LONG_DELAY)) { | |
a427de6f | 113 | dprintk(verbose, DST_CA_NOTICE, 1, " 8820 not ready"); |
d28d5762 | 114 | goto error; |
50b215a0 | 115 | } |
50b215a0 | 116 | if (read_dst(state, ca_string, 128) < 0) { /* Try to make this dynamic */ |
a427de6f | 117 | dprintk(verbose, DST_CA_INFO, 1, " Read not successful, trying to recover"); |
50b215a0 | 118 | dst_error_recovery(state); |
d28d5762 | 119 | goto error; |
50b215a0 JS |
120 | } |
121 | } | |
3593cab5 | 122 | mutex_unlock(&state->dst_mutex); |
50b215a0 | 123 | return 0; |
d28d5762 MA |
124 | |
125 | error: | |
3593cab5 | 126 | mutex_unlock(&state->dst_mutex); |
d28d5762 | 127 | return -EIO; |
50b215a0 JS |
128 | } |
129 | ||
130 | ||
131 | static int dst_put_ci(struct dst_state *state, u8 *data, int len, u8 *ca_string, int read) | |
132 | { | |
133 | u8 dst_ca_comm_err = 0; | |
134 | ||
135 | while (dst_ca_comm_err < RETRIES) { | |
a427de6f | 136 | dprintk(verbose, DST_CA_NOTICE, 1, " Put Command"); |
50b215a0 JS |
137 | if (dst_ci_command(state, data, ca_string, len, read)) { // If error |
138 | dst_error_recovery(state); | |
139 | dst_ca_comm_err++; // work required here. | |
1f19456d HS |
140 | } else { |
141 | break; | |
50b215a0 | 142 | } |
50b215a0 JS |
143 | } |
144 | ||
1f19456d HS |
145 | if(dst_ca_comm_err == RETRIES) |
146 | return -1; | |
147 | ||
50b215a0 JS |
148 | return 0; |
149 | } | |
150 | ||
151 | ||
152 | ||
153 | static int ca_get_app_info(struct dst_state *state) | |
154 | { | |
1f19456d | 155 | int length, str_length; |
50b215a0 JS |
156 | static u8 command[8] = {0x07, 0x40, 0x01, 0x00, 0x01, 0x00, 0x00, 0xff}; |
157 | ||
158 | put_checksum(&command[0], command[0]); | |
159 | if ((dst_put_ci(state, command, sizeof(command), state->messages, GET_REPLY)) < 0) { | |
a427de6f | 160 | dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); |
50b215a0 JS |
161 | return -1; |
162 | } | |
a427de6f MA |
163 | dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); |
164 | dprintk(verbose, DST_CA_INFO, 1, " ================================ CI Module Application Info ======================================"); | |
165 | dprintk(verbose, DST_CA_INFO, 1, " Application Type=[%d], Application Vendor=[%d], Vendor Code=[%d]\n%s: Application info=[%s]", | |
166 | state->messages[7], (state->messages[8] << 8) | state->messages[9], | |
b2e62e7c | 167 | (state->messages[10] << 8) | state->messages[11], __func__, (char *)(&state->messages[12])); |
a427de6f | 168 | dprintk(verbose, DST_CA_INFO, 1, " =================================================================================================="); |
50b215a0 | 169 | |
1f19456d HS |
170 | // Transform dst message to correct application_info message |
171 | length = state->messages[5]; | |
172 | str_length = length - 6; | |
173 | if (str_length < 0) { | |
174 | str_length = 0; | |
175 | dprintk(verbose, DST_CA_ERROR, 1, "Invalid string length returned in ca_get_app_info(). Recovering."); | |
176 | } | |
177 | ||
178 | // First, the command and length fields | |
179 | put_command_and_length(&state->messages[0], CA_APP_INFO, length); | |
180 | ||
181 | // Copy application_type, application_manufacturer and manufacturer_code | |
182 | memcpy(&state->messages[4], &state->messages[7], 5); | |
183 | ||
184 | // Set string length and copy string | |
185 | state->messages[9] = str_length; | |
186 | memcpy(&state->messages[10], &state->messages[12], str_length); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static int ca_get_ca_info(struct dst_state *state) | |
192 | { | |
193 | int srcPtr, dstPtr, i, num_ids; | |
194 | static u8 slot_command[8] = {0x07, 0x40, 0x00, 0x00, 0x02, 0x00, 0x00, 0xff}; | |
195 | const int in_system_id_pos = 8, out_system_id_pos = 4, in_num_ids_pos = 7; | |
196 | ||
197 | put_checksum(&slot_command[0], slot_command[0]); | |
198 | if ((dst_put_ci(state, slot_command, sizeof (slot_command), state->messages, GET_REPLY)) < 0) { | |
199 | dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); | |
200 | return -1; | |
201 | } | |
202 | dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); | |
203 | ||
204 | // Print raw data | |
205 | dprintk(verbose, DST_CA_INFO, 0, " DST data = ["); | |
206 | for (i = 0; i < state->messages[0] + 1; i++) { | |
207 | dprintk(verbose, DST_CA_INFO, 0, " 0x%02x", state->messages[i]); | |
208 | } | |
209 | dprintk(verbose, DST_CA_INFO, 0, "]\n"); | |
210 | ||
211 | // Set the command and length of the output | |
212 | num_ids = state->messages[in_num_ids_pos]; | |
213 | if (num_ids >= 100) { | |
214 | num_ids = 100; | |
215 | dprintk(verbose, DST_CA_ERROR, 1, "Invalid number of ids (>100). Recovering."); | |
216 | } | |
217 | put_command_and_length(&state->messages[0], CA_INFO, num_ids * 2); | |
218 | ||
219 | dprintk(verbose, DST_CA_INFO, 0, " CA_INFO = ["); | |
220 | srcPtr = in_system_id_pos; | |
221 | dstPtr = out_system_id_pos; | |
222 | for(i = 0; i < num_ids; i++) { | |
223 | dprintk(verbose, DST_CA_INFO, 0, " 0x%02x%02x", state->messages[srcPtr + 0], state->messages[srcPtr + 1]); | |
224 | // Append to output | |
225 | state->messages[dstPtr + 0] = state->messages[srcPtr + 0]; | |
226 | state->messages[dstPtr + 1] = state->messages[srcPtr + 1]; | |
227 | srcPtr += 2; | |
228 | dstPtr += 2; | |
229 | } | |
230 | dprintk(verbose, DST_CA_INFO, 0, "]\n"); | |
231 | ||
50b215a0 JS |
232 | return 0; |
233 | } | |
234 | ||
174f80df | 235 | static int ca_get_slot_caps(struct dst_state *state, struct ca_caps *p_ca_caps, void __user *arg) |
50b215a0 JS |
236 | { |
237 | int i; | |
238 | u8 slot_cap[256]; | |
239 | static u8 slot_command[8] = {0x07, 0x40, 0x02, 0x00, 0x02, 0x00, 0x00, 0xff}; | |
240 | ||
241 | put_checksum(&slot_command[0], slot_command[0]); | |
242 | if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_cap, GET_REPLY)) < 0) { | |
a427de6f | 243 | dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); |
50b215a0 JS |
244 | return -1; |
245 | } | |
a427de6f | 246 | dprintk(verbose, DST_CA_NOTICE, 1, " -->dst_put_ci SUCCESS !"); |
50b215a0 JS |
247 | |
248 | /* Will implement the rest soon */ | |
249 | ||
a427de6f MA |
250 | dprintk(verbose, DST_CA_INFO, 1, " Slot cap = [%d]", slot_cap[7]); |
251 | dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); | |
1f19456d | 252 | for (i = 0; i < slot_cap[0] + 1; i++) |
a427de6f MA |
253 | dprintk(verbose, DST_CA_INFO, 0, " %d", slot_cap[i]); |
254 | dprintk(verbose, DST_CA_INFO, 0, "\n"); | |
50b215a0 JS |
255 | |
256 | p_ca_caps->slot_num = 1; | |
257 | p_ca_caps->slot_type = 1; | |
258 | p_ca_caps->descr_num = slot_cap[7]; | |
259 | p_ca_caps->descr_type = 1; | |
260 | ||
174f80df | 261 | if (copy_to_user(arg, p_ca_caps, sizeof (struct ca_caps))) |
50b215a0 | 262 | return -EFAULT; |
50b215a0 JS |
263 | |
264 | return 0; | |
265 | } | |
266 | ||
4a2cc126 | 267 | /* Need some more work */ |
174f80df | 268 | static int ca_get_slot_descr(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) |
50b215a0 JS |
269 | { |
270 | return -EOPNOTSUPP; | |
271 | } | |
272 | ||
273 | ||
174f80df | 274 | static int ca_get_slot_info(struct dst_state *state, struct ca_slot_info *p_ca_slot_info, void __user *arg) |
50b215a0 JS |
275 | { |
276 | int i; | |
277 | static u8 slot_command[8] = {0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff}; | |
278 | ||
5c15c0b4 | 279 | u8 *slot_info = state->messages; |
50b215a0 JS |
280 | |
281 | put_checksum(&slot_command[0], 7); | |
282 | if ((dst_put_ci(state, slot_command, sizeof (slot_command), slot_info, GET_REPLY)) < 0) { | |
a427de6f | 283 | dprintk(verbose, DST_CA_ERROR, 1, " -->dst_put_ci FAILED !"); |
50b215a0 JS |
284 | return -1; |
285 | } | |
a427de6f | 286 | dprintk(verbose, DST_CA_INFO, 1, " -->dst_put_ci SUCCESS !"); |
50b215a0 JS |
287 | |
288 | /* Will implement the rest soon */ | |
289 | ||
a427de6f MA |
290 | dprintk(verbose, DST_CA_INFO, 1, " Slot info = [%d]", slot_info[3]); |
291 | dprintk(verbose, DST_CA_INFO, 0, "===================================\n"); | |
292 | for (i = 0; i < 8; i++) | |
293 | dprintk(verbose, DST_CA_INFO, 0, " %d", slot_info[i]); | |
294 | dprintk(verbose, DST_CA_INFO, 0, "\n"); | |
50b215a0 JS |
295 | |
296 | if (slot_info[4] & 0x80) { | |
297 | p_ca_slot_info->flags = CA_CI_MODULE_PRESENT; | |
298 | p_ca_slot_info->num = 1; | |
299 | p_ca_slot_info->type = CA_CI; | |
a427de6f | 300 | } else if (slot_info[4] & 0x40) { |
50b215a0 JS |
301 | p_ca_slot_info->flags = CA_CI_MODULE_READY; |
302 | p_ca_slot_info->num = 1; | |
303 | p_ca_slot_info->type = CA_CI; | |
a427de6f | 304 | } else |
50b215a0 | 305 | p_ca_slot_info->flags = 0; |
50b215a0 | 306 | |
174f80df | 307 | if (copy_to_user(arg, p_ca_slot_info, sizeof (struct ca_slot_info))) |
50b215a0 | 308 | return -EFAULT; |
50b215a0 JS |
309 | |
310 | return 0; | |
311 | } | |
312 | ||
313 | ||
174f80df | 314 | static int ca_get_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) |
50b215a0 JS |
315 | { |
316 | u8 i = 0; | |
317 | u32 command = 0; | |
318 | ||
174f80df | 319 | if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) |
50b215a0 JS |
320 | return -EFAULT; |
321 | ||
50b215a0 | 322 | if (p_ca_message->msg) { |
a427de6f | 323 | dprintk(verbose, DST_CA_NOTICE, 1, " Message = [%02x %02x %02x]", p_ca_message->msg[0], p_ca_message->msg[1], p_ca_message->msg[2]); |
50b215a0 JS |
324 | |
325 | for (i = 0; i < 3; i++) { | |
326 | command = command | p_ca_message->msg[i]; | |
327 | if (i < 2) | |
328 | command = command << 8; | |
329 | } | |
a427de6f | 330 | dprintk(verbose, DST_CA_NOTICE, 1, " Command=[0x%x]", command); |
50b215a0 JS |
331 | |
332 | switch (command) { | |
a427de6f MA |
333 | case CA_APP_INFO: |
334 | memcpy(p_ca_message->msg, state->messages, 128); | |
174f80df | 335 | if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) |
a427de6f | 336 | return -EFAULT; |
50b215a0 | 337 | break; |
1f19456d HS |
338 | case CA_INFO: |
339 | memcpy(p_ca_message->msg, state->messages, 128); | |
340 | if (copy_to_user(arg, p_ca_message, sizeof (struct ca_msg)) ) | |
341 | return -EFAULT; | |
342 | break; | |
50b215a0 JS |
343 | } |
344 | } | |
345 | ||
346 | return 0; | |
347 | } | |
348 | ||
7d53421c | 349 | static int handle_dst_tag(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u32 length) |
50b215a0 | 350 | { |
4a2cc126 | 351 | if (state->dst_hw_cap & DST_TYPE_HAS_SESSION) { |
94b7410c MA |
352 | hw_buffer->msg[2] = p_ca_message->msg[1]; /* MSB */ |
353 | hw_buffer->msg[3] = p_ca_message->msg[2]; /* LSB */ | |
354 | } else { | |
355 | if (length > 247) { | |
a427de6f | 356 | dprintk(verbose, DST_CA_ERROR, 1, " Message too long ! *** Bailing Out *** !"); |
94b7410c MA |
357 | return -1; |
358 | } | |
7d53421c MA |
359 | hw_buffer->msg[0] = (length & 0xff) + 7; |
360 | hw_buffer->msg[1] = 0x40; | |
50b215a0 JS |
361 | hw_buffer->msg[2] = 0x03; |
362 | hw_buffer->msg[3] = 0x00; | |
7d53421c MA |
363 | hw_buffer->msg[4] = 0x03; |
364 | hw_buffer->msg[5] = length & 0xff; | |
365 | hw_buffer->msg[6] = 0x00; | |
50c25fff | 366 | |
94b7410c MA |
367 | /* |
368 | * Need to compute length for EN50221 section 8.3.2, for the time being | |
369 | * assuming 8.3.2 is not applicable | |
370 | */ | |
371 | memcpy(&hw_buffer->msg[7], &p_ca_message->msg[4], length); | |
50b215a0 | 372 | } |
50c25fff | 373 | |
50b215a0 JS |
374 | return 0; |
375 | } | |
376 | ||
7d53421c | 377 | static int write_to_8820(struct dst_state *state, struct ca_msg *hw_buffer, u8 length, u8 reply) |
50b215a0 | 378 | { |
7d53421c | 379 | if ((dst_put_ci(state, hw_buffer->msg, length, hw_buffer->msg, reply)) < 0) { |
a427de6f MA |
380 | dprintk(verbose, DST_CA_ERROR, 1, " DST-CI Command failed."); |
381 | dprintk(verbose, DST_CA_NOTICE, 1, " Resetting DST."); | |
50b215a0 JS |
382 | rdc_reset_state(state); |
383 | return -1; | |
384 | } | |
1f19456d | 385 | dprintk(verbose, DST_CA_NOTICE, 1, " DST-CI Command success."); |
50b215a0 JS |
386 | |
387 | return 0; | |
388 | } | |
389 | ||
174f80df | 390 | static u32 asn_1_decode(u8 *asn_1_array) |
50b215a0 | 391 | { |
7d53421c MA |
392 | u8 length_field = 0, word_count = 0, count = 0; |
393 | u32 length = 0; | |
394 | ||
395 | length_field = asn_1_array[0]; | |
a427de6f | 396 | dprintk(verbose, DST_CA_DEBUG, 1, " Length field=[%02x]", length_field); |
7d53421c MA |
397 | if (length_field < 0x80) { |
398 | length = length_field & 0x7f; | |
a427de6f | 399 | dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%02x]\n", length); |
7d53421c MA |
400 | } else { |
401 | word_count = length_field & 0x7f; | |
402 | for (count = 0; count < word_count; count++) { | |
93a14f15 RM |
403 | length = length << 8; |
404 | length += asn_1_array[count + 1]; | |
a427de6f | 405 | dprintk(verbose, DST_CA_DEBUG, 1, " Length=[%04x]", length); |
50b215a0 JS |
406 | } |
407 | } | |
7d53421c MA |
408 | return length; |
409 | } | |
50b215a0 | 410 | |
7d53421c MA |
411 | static int debug_string(u8 *msg, u32 length, u32 offset) |
412 | { | |
413 | u32 i; | |
50b215a0 | 414 | |
a427de6f | 415 | dprintk(verbose, DST_CA_DEBUG, 0, " String=[ "); |
7d53421c | 416 | for (i = offset; i < length; i++) |
a427de6f MA |
417 | dprintk(verbose, DST_CA_DEBUG, 0, "%02x ", msg[i]); |
418 | dprintk(verbose, DST_CA_DEBUG, 0, "]\n"); | |
50b215a0 | 419 | |
7d53421c MA |
420 | return 0; |
421 | } | |
50b215a0 | 422 | |
3e357fd8 | 423 | |
7d53421c MA |
424 | static int ca_set_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer, u8 reply, u8 query) |
425 | { | |
94b7410c MA |
426 | u32 length = 0; |
427 | u8 tag_length = 8; | |
50b215a0 | 428 | |
7d53421c | 429 | length = asn_1_decode(&p_ca_message->msg[3]); |
a427de6f | 430 | dprintk(verbose, DST_CA_DEBUG, 1, " CA Message length=[%d]", length); |
94b7410c | 431 | debug_string(&p_ca_message->msg[4], length, 0); /* length is excluding tag & length */ |
50b215a0 | 432 | |
94b7410c | 433 | memset(hw_buffer->msg, '\0', length); |
7d53421c | 434 | handle_dst_tag(state, p_ca_message, hw_buffer, length); |
94b7410c | 435 | put_checksum(hw_buffer->msg, hw_buffer->msg[0]); |
50b215a0 | 436 | |
94b7410c MA |
437 | debug_string(hw_buffer->msg, (length + tag_length), 0); /* tags too */ |
438 | write_to_8820(state, hw_buffer, (length + tag_length), reply); | |
50b215a0 JS |
439 | |
440 | return 0; | |
441 | } | |
442 | ||
7d53421c | 443 | |
50b215a0 JS |
444 | /* Board supports CA PMT reply ? */ |
445 | static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message, struct ca_msg *hw_buffer) | |
446 | { | |
447 | int ca_pmt_reply_test = 0; | |
448 | ||
449 | /* Do test board */ | |
450 | /* Not there yet but soon */ | |
451 | ||
50b215a0 JS |
452 | /* CA PMT Reply capable */ |
453 | if (ca_pmt_reply_test) { | |
454 | if ((ca_set_pmt(state, p_ca_message, hw_buffer, 1, GET_REPLY)) < 0) { | |
a427de6f | 455 | dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); |
50b215a0 JS |
456 | return -1; |
457 | } | |
458 | ||
459 | /* Process CA PMT Reply */ | |
460 | /* will implement soon */ | |
a427de6f | 461 | dprintk(verbose, DST_CA_ERROR, 1, " Not there yet"); |
50b215a0 JS |
462 | } |
463 | /* CA PMT Reply not capable */ | |
464 | if (!ca_pmt_reply_test) { | |
465 | if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, NO_REPLY)) < 0) { | |
a427de6f | 466 | dprintk(verbose, DST_CA_ERROR, 1, " ca_set_pmt.. failed !"); |
50b215a0 JS |
467 | return -1; |
468 | } | |
a427de6f | 469 | dprintk(verbose, DST_CA_NOTICE, 1, " ca_set_pmt.. success !"); |
50b215a0 JS |
470 | /* put a dummy message */ |
471 | ||
472 | } | |
473 | return 0; | |
474 | } | |
475 | ||
174f80df | 476 | static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) |
50b215a0 JS |
477 | { |
478 | int i = 0; | |
479 | unsigned int ca_message_header_len; | |
480 | ||
481 | u32 command = 0; | |
482 | struct ca_msg *hw_buffer; | |
f630558d | 483 | int result = 0; |
50b215a0 | 484 | |
5cbded58 | 485 | if ((hw_buffer = kmalloc(sizeof (struct ca_msg), GFP_KERNEL)) == NULL) { |
a427de6f | 486 | dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); |
50b215a0 JS |
487 | return -ENOMEM; |
488 | } | |
a427de6f | 489 | dprintk(verbose, DST_CA_DEBUG, 1, " "); |
50b215a0 | 490 | |
94299171 | 491 | if (copy_from_user(p_ca_message, arg, sizeof (struct ca_msg))) { |
f630558d PA |
492 | result = -EFAULT; |
493 | goto free_mem_and_exit; | |
494 | } | |
495 | ||
50b215a0 JS |
496 | |
497 | if (p_ca_message->msg) { | |
498 | ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */ | |
499 | /* EN50221 tag */ | |
500 | command = 0; | |
501 | ||
502 | for (i = 0; i < 3; i++) { | |
503 | command = command | p_ca_message->msg[i]; | |
504 | if (i < 2) | |
505 | command = command << 8; | |
506 | } | |
a427de6f | 507 | dprintk(verbose, DST_CA_DEBUG, 1, " Command=[0x%x]\n", command); |
50b215a0 JS |
508 | |
509 | switch (command) { | |
a427de6f MA |
510 | case CA_PMT: |
511 | dprintk(verbose, DST_CA_DEBUG, 1, "Command = SEND_CA_PMT"); | |
512 | if ((ca_set_pmt(state, p_ca_message, hw_buffer, 0, 0)) < 0) { // code simplification started | |
513 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT Failed !"); | |
f630558d PA |
514 | result = -1; |
515 | goto free_mem_and_exit; | |
a427de6f MA |
516 | } |
517 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT Success !"); | |
518 | break; | |
519 | case CA_PMT_REPLY: | |
520 | dprintk(verbose, DST_CA_INFO, 1, "Command = CA_PMT_REPLY"); | |
521 | /* Have to handle the 2 basic types of cards here */ | |
522 | if ((dst_check_ca_pmt(state, p_ca_message, hw_buffer)) < 0) { | |
523 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_PMT_REPLY Failed !"); | |
f630558d PA |
524 | result = -1; |
525 | goto free_mem_and_exit; | |
a427de6f MA |
526 | } |
527 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_PMT_REPLY Success !"); | |
528 | break; | |
529 | case CA_APP_INFO_ENQUIRY: // only for debugging | |
530 | dprintk(verbose, DST_CA_INFO, 1, " Getting Cam Application information"); | |
531 | ||
532 | if ((ca_get_app_info(state)) < 0) { | |
533 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_APP_INFO_ENQUIRY Failed !"); | |
f630558d PA |
534 | result = -1; |
535 | goto free_mem_and_exit; | |
a427de6f MA |
536 | } |
537 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_APP_INFO_ENQUIRY Success !"); | |
538 | break; | |
1f19456d HS |
539 | case CA_INFO_ENQUIRY: |
540 | dprintk(verbose, DST_CA_INFO, 1, " Getting CA Information"); | |
541 | ||
542 | if ((ca_get_ca_info(state)) < 0) { | |
543 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_INFO_ENQUIRY Failed !"); | |
544 | result = -1; | |
545 | goto free_mem_and_exit; | |
546 | } | |
547 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_INFO_ENQUIRY Success !"); | |
548 | break; | |
50b215a0 JS |
549 | } |
550 | } | |
f630558d PA |
551 | free_mem_and_exit: |
552 | kfree (hw_buffer); | |
553 | ||
554 | return result; | |
50b215a0 JS |
555 | } |
556 | ||
5ed2b641 | 557 | static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg) |
50b215a0 | 558 | { |
ce18c485 HV |
559 | struct dvb_device *dvbdev; |
560 | struct dst_state *state; | |
50b215a0 JS |
561 | struct ca_slot_info *p_ca_slot_info; |
562 | struct ca_caps *p_ca_caps; | |
563 | struct ca_msg *p_ca_message; | |
174f80df | 564 | void __user *arg = (void __user *)ioctl_arg; |
f630558d | 565 | int result = 0; |
50b215a0 | 566 | |
ce18c485 HV |
567 | lock_kernel(); |
568 | dvbdev = (struct dvb_device *)file->private_data; | |
569 | state = (struct dst_state *)dvbdev->priv; | |
b57e5578 MCC |
570 | p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL); |
571 | p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL); | |
572 | p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL); | |
573 | if (!p_ca_message || !p_ca_slot_info || !p_ca_caps) { | |
a427de6f | 574 | dprintk(verbose, DST_CA_ERROR, 1, " Memory allocation failure"); |
b57e5578 MCC |
575 | result = -ENOMEM; |
576 | goto free_mem_and_exit; | |
50b215a0 | 577 | } |
b57e5578 | 578 | |
50b215a0 JS |
579 | /* We have now only the standard ioctl's, the driver is upposed to handle internals. */ |
580 | switch (cmd) { | |
a427de6f MA |
581 | case CA_SEND_MSG: |
582 | dprintk(verbose, DST_CA_INFO, 1, " Sending message"); | |
583 | if ((ca_send_message(state, p_ca_message, arg)) < 0) { | |
584 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SEND_MSG Failed !"); | |
f630558d PA |
585 | result = -1; |
586 | goto free_mem_and_exit; | |
a427de6f MA |
587 | } |
588 | break; | |
589 | case CA_GET_MSG: | |
590 | dprintk(verbose, DST_CA_INFO, 1, " Getting message"); | |
591 | if ((ca_get_message(state, p_ca_message, arg)) < 0) { | |
592 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_MSG Failed !"); | |
f630558d PA |
593 | result = -1; |
594 | goto free_mem_and_exit; | |
a427de6f MA |
595 | } |
596 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_MSG Success !"); | |
597 | break; | |
598 | case CA_RESET: | |
599 | dprintk(verbose, DST_CA_ERROR, 1, " Resetting DST"); | |
600 | dst_error_bailout(state); | |
601 | msleep(4000); | |
602 | break; | |
603 | case CA_GET_SLOT_INFO: | |
604 | dprintk(verbose, DST_CA_INFO, 1, " Getting Slot info"); | |
605 | if ((ca_get_slot_info(state, p_ca_slot_info, arg)) < 0) { | |
606 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_SLOT_INFO Failed !"); | |
f630558d PA |
607 | result = -1; |
608 | goto free_mem_and_exit; | |
a427de6f MA |
609 | } |
610 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_SLOT_INFO Success !"); | |
611 | break; | |
612 | case CA_GET_CAP: | |
613 | dprintk(verbose, DST_CA_INFO, 1, " Getting Slot capabilities"); | |
614 | if ((ca_get_slot_caps(state, p_ca_caps, arg)) < 0) { | |
615 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_CAP Failed !"); | |
f630558d PA |
616 | result = -1; |
617 | goto free_mem_and_exit; | |
a427de6f MA |
618 | } |
619 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_CAP Success !"); | |
620 | break; | |
621 | case CA_GET_DESCR_INFO: | |
622 | dprintk(verbose, DST_CA_INFO, 1, " Getting descrambler description"); | |
623 | if ((ca_get_slot_descr(state, p_ca_message, arg)) < 0) { | |
624 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_GET_DESCR_INFO Failed !"); | |
f630558d PA |
625 | result = -1; |
626 | goto free_mem_and_exit; | |
a427de6f MA |
627 | } |
628 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_GET_DESCR_INFO Success !"); | |
629 | break; | |
630 | case CA_SET_DESCR: | |
631 | dprintk(verbose, DST_CA_INFO, 1, " Setting descrambler"); | |
632 | if ((ca_set_slot_descr()) < 0) { | |
633 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_DESCR Failed !"); | |
f630558d PA |
634 | result = -1; |
635 | goto free_mem_and_exit; | |
a427de6f MA |
636 | } |
637 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_DESCR Success !"); | |
638 | break; | |
639 | case CA_SET_PID: | |
640 | dprintk(verbose, DST_CA_INFO, 1, " Setting PID"); | |
641 | if ((ca_set_pid()) < 0) { | |
642 | dprintk(verbose, DST_CA_ERROR, 1, " -->CA_SET_PID Failed !"); | |
f630558d PA |
643 | result = -1; |
644 | goto free_mem_and_exit; | |
a427de6f MA |
645 | } |
646 | dprintk(verbose, DST_CA_INFO, 1, " -->CA_SET_PID Success !"); | |
647 | default: | |
f630558d | 648 | result = -EOPNOTSUPP; |
a427de6f | 649 | }; |
f630558d PA |
650 | free_mem_and_exit: |
651 | kfree (p_ca_message); | |
652 | kfree (p_ca_slot_info); | |
653 | kfree (p_ca_caps); | |
50b215a0 | 654 | |
5ed2b641 | 655 | unlock_kernel(); |
f630558d | 656 | return result; |
50b215a0 JS |
657 | } |
658 | ||
659 | static int dst_ca_open(struct inode *inode, struct file *file) | |
660 | { | |
a427de6f | 661 | dprintk(verbose, DST_CA_DEBUG, 1, " Device opened [%p] ", file); |
50b215a0 JS |
662 | try_module_get(THIS_MODULE); |
663 | ||
664 | return 0; | |
665 | } | |
666 | ||
667 | static int dst_ca_release(struct inode *inode, struct file *file) | |
668 | { | |
a427de6f | 669 | dprintk(verbose, DST_CA_DEBUG, 1, " Device closed."); |
50b215a0 JS |
670 | module_put(THIS_MODULE); |
671 | ||
672 | return 0; | |
673 | } | |
674 | ||
94299171 | 675 | static ssize_t dst_ca_read(struct file *file, char __user *buffer, size_t length, loff_t *offset) |
50b215a0 | 676 | { |
07f640f0 | 677 | ssize_t bytes_read = 0; |
50b215a0 | 678 | |
a427de6f | 679 | dprintk(verbose, DST_CA_DEBUG, 1, " Device read."); |
50b215a0 JS |
680 | |
681 | return bytes_read; | |
682 | } | |
683 | ||
94299171 | 684 | static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset) |
50b215a0 | 685 | { |
a427de6f | 686 | dprintk(verbose, DST_CA_DEBUG, 1, " Device write."); |
50b215a0 JS |
687 | |
688 | return 0; | |
689 | } | |
690 | ||
784e29d2 | 691 | static const struct file_operations dst_ca_fops = { |
50b215a0 | 692 | .owner = THIS_MODULE, |
5ed2b641 | 693 | .unlocked_ioctl = dst_ca_ioctl, |
50b215a0 JS |
694 | .open = dst_ca_open, |
695 | .release = dst_ca_release, | |
696 | .read = dst_ca_read, | |
697 | .write = dst_ca_write | |
698 | }; | |
699 | ||
700 | static struct dvb_device dvbdev_ca = { | |
701 | .priv = NULL, | |
702 | .users = 1, | |
703 | .readers = 1, | |
704 | .writers = 1, | |
705 | .fops = &dst_ca_fops | |
706 | }; | |
707 | ||
bbdd11fa | 708 | struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter) |
50b215a0 JS |
709 | { |
710 | struct dvb_device *dvbdev; | |
bbdd11fa | 711 | |
a427de6f | 712 | dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device"); |
bbdd11fa MA |
713 | if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) { |
714 | dst->dst_ca = dvbdev; | |
715 | return dst->dst_ca; | |
716 | } | |
717 | ||
718 | return NULL; | |
50b215a0 JS |
719 | } |
720 | ||
721 | EXPORT_SYMBOL(dst_ca_attach); | |
722 | ||
723 | MODULE_DESCRIPTION("DST DVB-S/T/C Combo CA driver"); | |
724 | MODULE_AUTHOR("Manu Abraham"); | |
725 | MODULE_LICENSE("GPL"); |