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