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