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