File Index
Files and directories
Sign in
FILE

av7110_ca.c

File preview with syntax highlighting.

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