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