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