1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * av7110_ca.c: CA and CI stuff |
4 | * |
5 | * Copyright (C) 1999-2002 Ralph Metzler |
6 | * & Marcus Metzler for convergence integrated media GmbH |
7 | * |
8 | * originally based on code by: |
9 | * Copyright (C) 1998,1999 Christian Theiss <mistert@rz.fh-augsburg.de> |
10 | * |
11 | * the project's page is at https://linuxtv.org |
12 | */ |
13 | |
14 | #include <linux/kernel.h> |
15 | #include <linux/types.h> |
16 | #include <linux/delay.h> |
17 | #include <linux/fs.h> |
18 | #include <linux/timer.h> |
19 | #include <linux/poll.h> |
20 | #include <linux/gfp.h> |
21 | |
22 | #include "av7110.h" |
23 | #include "av7110_hw.h" |
24 | #include "av7110_ca.h" |
25 | |
26 | |
27 | void CI_handle(struct av7110 *av7110, u8 *data, u16 len) |
28 | { |
29 | dprintk(8, "av7110:%p\n" ,av7110); |
30 | |
31 | if (len < 3) |
32 | return; |
33 | switch (data[0]) { |
34 | case CI_MSG_CI_INFO: |
35 | if (data[2] != 1 && data[2] != 2) |
36 | break; |
37 | switch (data[1]) { |
38 | case 0: |
39 | av7110->ci_slot[data[2] - 1].flags = 0; |
40 | break; |
41 | case 1: |
42 | av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT; |
43 | break; |
44 | case 2: |
45 | av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY; |
46 | break; |
47 | } |
48 | break; |
49 | case CI_SWITCH_PRG_REPLY: |
50 | //av7110->ci_stat=data[1]; |
51 | break; |
52 | default: |
53 | break; |
54 | } |
55 | } |
56 | |
57 | |
58 | void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len) |
59 | { |
60 | if (dvb_ringbuffer_free(rbuf: cibuf) < len + 2) |
61 | return; |
62 | |
63 | DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8); |
64 | DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff); |
65 | dvb_ringbuffer_write(rbuf: cibuf, buf: data, len); |
66 | wake_up_interruptible(&cibuf->queue); |
67 | } |
68 | |
69 | |
70 | /****************************************************************************** |
71 | * CI link layer file ops |
72 | ******************************************************************************/ |
73 | |
74 | static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size) |
75 | { |
76 | struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p; |
77 | void *data; |
78 | |
79 | for (p = tab; *p; p++) { |
80 | data = vmalloc(size); |
81 | if (!data) { |
82 | while (p-- != tab) { |
83 | vfree(addr: p[0]->data); |
84 | p[0]->data = NULL; |
85 | } |
86 | return -ENOMEM; |
87 | } |
88 | dvb_ringbuffer_init(rbuf: *p, data, len: size); |
89 | } |
90 | return 0; |
91 | } |
92 | |
93 | static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) |
94 | { |
95 | dvb_ringbuffer_flush_spinlock_wakeup(rbuf: cirbuf); |
96 | dvb_ringbuffer_flush_spinlock_wakeup(rbuf: ciwbuf); |
97 | } |
98 | |
99 | static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf) |
100 | { |
101 | vfree(addr: cirbuf->data); |
102 | cirbuf->data = NULL; |
103 | vfree(addr: ciwbuf->data); |
104 | ciwbuf->data = NULL; |
105 | } |
106 | |
107 | static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file, |
108 | int slots, struct ca_slot_info *slot) |
109 | { |
110 | int i; |
111 | int len = 0; |
112 | u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 }; |
113 | |
114 | for (i = 0; i < 2; i++) { |
115 | if (slots & (1 << i)) |
116 | len += 8; |
117 | } |
118 | |
119 | if (dvb_ringbuffer_free(rbuf: cibuf) < len) |
120 | return -EBUSY; |
121 | |
122 | for (i = 0; i < 2; i++) { |
123 | if (slots & (1 << i)) { |
124 | msg[2] = i; |
125 | dvb_ringbuffer_write(rbuf: cibuf, buf: msg, len: 8); |
126 | slot[i].flags = 0; |
127 | } |
128 | } |
129 | |
130 | return 0; |
131 | } |
132 | |
133 | static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file, |
134 | const char __user *buf, size_t count, loff_t *ppos) |
135 | { |
136 | int free; |
137 | int non_blocking = file->f_flags & O_NONBLOCK; |
138 | u8 *page = (u8 *)__get_free_page(GFP_USER); |
139 | int res; |
140 | |
141 | if (!page) |
142 | return -ENOMEM; |
143 | |
144 | res = -EINVAL; |
145 | if (count > 2048) |
146 | goto out; |
147 | |
148 | res = -EFAULT; |
149 | if (copy_from_user(to: page, from: buf, n: count)) |
150 | goto out; |
151 | |
152 | free = dvb_ringbuffer_free(rbuf: cibuf); |
153 | if (count + 2 > free) { |
154 | res = -EWOULDBLOCK; |
155 | if (non_blocking) |
156 | goto out; |
157 | res = -ERESTARTSYS; |
158 | if (wait_event_interruptible(cibuf->queue, |
159 | (dvb_ringbuffer_free(cibuf) >= count + 2))) |
160 | goto out; |
161 | } |
162 | |
163 | DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8); |
164 | DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff); |
165 | |
166 | res = dvb_ringbuffer_write(rbuf: cibuf, buf: page, len: count); |
167 | out: |
168 | free_page((unsigned long)page); |
169 | return res; |
170 | } |
171 | |
172 | static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, |
173 | char __user *buf, size_t count, loff_t *ppos) |
174 | { |
175 | int avail; |
176 | int non_blocking = file->f_flags & O_NONBLOCK; |
177 | ssize_t len; |
178 | |
179 | if (!cibuf->data || !count) |
180 | return 0; |
181 | if (non_blocking && (dvb_ringbuffer_empty(rbuf: cibuf))) |
182 | return -EWOULDBLOCK; |
183 | if (wait_event_interruptible(cibuf->queue, |
184 | !dvb_ringbuffer_empty(cibuf))) |
185 | return -ERESTARTSYS; |
186 | avail = dvb_ringbuffer_avail(rbuf: cibuf); |
187 | if (avail < 4) |
188 | return 0; |
189 | len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8; |
190 | len |= DVB_RINGBUFFER_PEEK(cibuf, 1); |
191 | if (avail < len + 2 || count < len) |
192 | return -EINVAL; |
193 | DVB_RINGBUFFER_SKIP(cibuf, 2); |
194 | |
195 | return dvb_ringbuffer_read_user(rbuf: cibuf, buf, len); |
196 | } |
197 | |
198 | static int dvb_ca_open(struct inode *inode, struct file *file) |
199 | { |
200 | struct dvb_device *dvbdev = file->private_data; |
201 | struct av7110 *av7110 = dvbdev->priv; |
202 | int err = dvb_generic_open(inode, file); |
203 | |
204 | dprintk(8, "av7110:%p\n" ,av7110); |
205 | |
206 | if (err < 0) |
207 | return err; |
208 | ci_ll_flush(cirbuf: &av7110->ci_rbuffer, ciwbuf: &av7110->ci_wbuffer); |
209 | return 0; |
210 | } |
211 | |
212 | static __poll_t dvb_ca_poll (struct file *file, poll_table *wait) |
213 | { |
214 | struct dvb_device *dvbdev = file->private_data; |
215 | struct av7110 *av7110 = dvbdev->priv; |
216 | struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; |
217 | struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; |
218 | __poll_t mask = 0; |
219 | |
220 | dprintk(8, "av7110:%p\n" ,av7110); |
221 | |
222 | poll_wait(filp: file, wait_address: &rbuf->queue, p: wait); |
223 | poll_wait(filp: file, wait_address: &wbuf->queue, p: wait); |
224 | |
225 | if (!dvb_ringbuffer_empty(rbuf)) |
226 | mask |= (EPOLLIN | EPOLLRDNORM); |
227 | |
228 | if (dvb_ringbuffer_free(rbuf: wbuf) > 1024) |
229 | mask |= (EPOLLOUT | EPOLLWRNORM); |
230 | |
231 | return mask; |
232 | } |
233 | |
234 | static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg) |
235 | { |
236 | struct dvb_device *dvbdev = file->private_data; |
237 | struct av7110 *av7110 = dvbdev->priv; |
238 | unsigned long arg = (unsigned long) parg; |
239 | int ret = 0; |
240 | |
241 | dprintk(8, "av7110:%p\n" ,av7110); |
242 | |
243 | if (mutex_lock_interruptible(&av7110->ioctl_mutex)) |
244 | return -ERESTARTSYS; |
245 | |
246 | switch (cmd) { |
247 | case CA_RESET: |
248 | ret = ci_ll_reset(cibuf: &av7110->ci_wbuffer, file, slots: arg, |
249 | slot: &av7110->ci_slot[0]); |
250 | break; |
251 | case CA_GET_CAP: |
252 | { |
253 | struct ca_caps cap; |
254 | |
255 | cap.slot_num = 2; |
256 | cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ? |
257 | CA_CI_LINK : CA_CI) | CA_DESCR; |
258 | cap.descr_num = 16; |
259 | cap.descr_type = CA_ECD; |
260 | memcpy(parg, &cap, sizeof(cap)); |
261 | break; |
262 | } |
263 | |
264 | case CA_GET_SLOT_INFO: |
265 | { |
266 | struct ca_slot_info *info=(struct ca_slot_info *)parg; |
267 | |
268 | if (info->num < 0 || info->num > 1) { |
269 | mutex_unlock(lock: &av7110->ioctl_mutex); |
270 | return -EINVAL; |
271 | } |
272 | av7110->ci_slot[info->num].num = info->num; |
273 | av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ? |
274 | CA_CI_LINK : CA_CI; |
275 | memcpy(info, &av7110->ci_slot[info->num], sizeof(struct ca_slot_info)); |
276 | break; |
277 | } |
278 | |
279 | case CA_GET_MSG: |
280 | break; |
281 | |
282 | case CA_SEND_MSG: |
283 | break; |
284 | |
285 | case CA_GET_DESCR_INFO: |
286 | { |
287 | struct ca_descr_info info; |
288 | |
289 | info.num = 16; |
290 | info.type = CA_ECD; |
291 | memcpy(parg, &info, sizeof (info)); |
292 | break; |
293 | } |
294 | |
295 | case CA_SET_DESCR: |
296 | { |
297 | struct ca_descr *descr = (struct ca_descr*) parg; |
298 | |
299 | if (descr->index >= 16 || descr->parity > 1) { |
300 | mutex_unlock(lock: &av7110->ioctl_mutex); |
301 | return -EINVAL; |
302 | } |
303 | av7110_fw_cmd(av7110, type: COMTYPE_PIDFILTER, com: SetDescr, num: 5, |
304 | (descr->index<<8)|descr->parity, |
305 | (descr->cw[0]<<8)|descr->cw[1], |
306 | (descr->cw[2]<<8)|descr->cw[3], |
307 | (descr->cw[4]<<8)|descr->cw[5], |
308 | (descr->cw[6]<<8)|descr->cw[7]); |
309 | break; |
310 | } |
311 | |
312 | default: |
313 | ret = -EINVAL; |
314 | break; |
315 | } |
316 | |
317 | mutex_unlock(lock: &av7110->ioctl_mutex); |
318 | return ret; |
319 | } |
320 | |
321 | static ssize_t dvb_ca_write(struct file *file, const char __user *buf, |
322 | size_t count, loff_t *ppos) |
323 | { |
324 | struct dvb_device *dvbdev = file->private_data; |
325 | struct av7110 *av7110 = dvbdev->priv; |
326 | |
327 | dprintk(8, "av7110:%p\n" ,av7110); |
328 | return ci_ll_write(cibuf: &av7110->ci_wbuffer, file, buf, count, ppos); |
329 | } |
330 | |
331 | static ssize_t dvb_ca_read(struct file *file, char __user *buf, |
332 | size_t count, loff_t *ppos) |
333 | { |
334 | struct dvb_device *dvbdev = file->private_data; |
335 | struct av7110 *av7110 = dvbdev->priv; |
336 | |
337 | dprintk(8, "av7110:%p\n" ,av7110); |
338 | return ci_ll_read(cibuf: &av7110->ci_rbuffer, file, buf, count, ppos); |
339 | } |
340 | |
341 | static const struct file_operations dvb_ca_fops = { |
342 | .owner = THIS_MODULE, |
343 | .read = dvb_ca_read, |
344 | .write = dvb_ca_write, |
345 | .unlocked_ioctl = dvb_generic_ioctl, |
346 | .open = dvb_ca_open, |
347 | .release = dvb_generic_release, |
348 | .poll = dvb_ca_poll, |
349 | .llseek = default_llseek, |
350 | }; |
351 | |
352 | static struct dvb_device dvbdev_ca = { |
353 | .priv = NULL, |
354 | .users = 1, |
355 | .writers = 1, |
356 | .fops = &dvb_ca_fops, |
357 | .kernel_ioctl = dvb_ca_ioctl, |
358 | }; |
359 | |
360 | |
361 | int av7110_ca_register(struct av7110 *av7110) |
362 | { |
363 | return dvb_register_device(adap: &av7110->dvb_adapter, pdvbdev: &av7110->ca_dev, |
364 | template: &dvbdev_ca, priv: av7110, type: DVB_DEVICE_CA, demux_sink_pads: 0); |
365 | } |
366 | |
367 | void av7110_ca_unregister(struct av7110 *av7110) |
368 | { |
369 | dvb_unregister_device(dvbdev: av7110->ca_dev); |
370 | } |
371 | |
372 | int av7110_ca_init(struct av7110* av7110) |
373 | { |
374 | return ci_ll_init(cirbuf: &av7110->ci_rbuffer, ciwbuf: &av7110->ci_wbuffer, size: 8192); |
375 | } |
376 | |
377 | void av7110_ca_exit(struct av7110* av7110) |
378 | { |
379 | ci_ll_release(cirbuf: &av7110->ci_rbuffer, ciwbuf: &av7110->ci_wbuffer); |
380 | } |
381 | |