Path: blob/master/drivers/media/dvb/ttpci/av7110_ca.c
15112 views
/*1* av7110_ca.c: CA and CI stuff2*3* Copyright (C) 1999-2002 Ralph Metzler4* & Marcus Metzler for convergence integrated media GmbH5*6* originally based on code by:7* Copyright (C) 1998,1999 Christian Theiss <[email protected]>8*9* This program is free software; you can redistribute it and/or10* modify it under the terms of the GNU General Public License11* as published by the Free Software Foundation; either version 212* of the License, or (at your option) any later version.13*14*15* This program is distributed in the hope that it will be useful,16* but WITHOUT ANY WARRANTY; without even the implied warranty of17* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the18* GNU General Public License for more details.19*20*21* You should have received a copy of the GNU General Public License22* along with this program; if not, write to the Free Software23* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.24* Or, point your browser to http://www.gnu.org/copyleft/gpl.html25*26*27* the project's page is at http://www.linuxtv.org/28*/2930#include <linux/kernel.h>31#include <linux/types.h>32#include <linux/delay.h>33#include <linux/fs.h>34#include <linux/timer.h>35#include <linux/poll.h>36#include <linux/gfp.h>3738#include "av7110.h"39#include "av7110_hw.h"40#include "av7110_ca.h"414243void CI_handle(struct av7110 *av7110, u8 *data, u16 len)44{45dprintk(8, "av7110:%p\n",av7110);4647if (len < 3)48return;49switch (data[0]) {50case CI_MSG_CI_INFO:51if (data[2] != 1 && data[2] != 2)52break;53switch (data[1]) {54case 0:55av7110->ci_slot[data[2] - 1].flags = 0;56break;57case 1:58av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_PRESENT;59break;60case 2:61av7110->ci_slot[data[2] - 1].flags |= CA_CI_MODULE_READY;62break;63}64break;65case CI_SWITCH_PRG_REPLY:66//av7110->ci_stat=data[1];67break;68default:69break;70}71}727374void ci_get_data(struct dvb_ringbuffer *cibuf, u8 *data, int len)75{76if (dvb_ringbuffer_free(cibuf) < len + 2)77return;7879DVB_RINGBUFFER_WRITE_BYTE(cibuf, len >> 8);80DVB_RINGBUFFER_WRITE_BYTE(cibuf, len & 0xff);81dvb_ringbuffer_write(cibuf, data, len);82wake_up_interruptible(&cibuf->queue);83}848586/******************************************************************************87* CI link layer file ops88******************************************************************************/8990static int ci_ll_init(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf, int size)91{92struct dvb_ringbuffer *tab[] = { cirbuf, ciwbuf, NULL }, **p;93void *data;9495for (p = tab; *p; p++) {96data = vmalloc(size);97if (!data) {98while (p-- != tab) {99vfree(p[0]->data);100p[0]->data = NULL;101}102return -ENOMEM;103}104dvb_ringbuffer_init(*p, data, size);105}106return 0;107}108109static void ci_ll_flush(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)110{111dvb_ringbuffer_flush_spinlock_wakeup(cirbuf);112dvb_ringbuffer_flush_spinlock_wakeup(ciwbuf);113}114115static void ci_ll_release(struct dvb_ringbuffer *cirbuf, struct dvb_ringbuffer *ciwbuf)116{117vfree(cirbuf->data);118cirbuf->data = NULL;119vfree(ciwbuf->data);120ciwbuf->data = NULL;121}122123static int ci_ll_reset(struct dvb_ringbuffer *cibuf, struct file *file,124int slots, ca_slot_info_t *slot)125{126int i;127int len = 0;128u8 msg[8] = { 0x00, 0x06, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00 };129130for (i = 0; i < 2; i++) {131if (slots & (1 << i))132len += 8;133}134135if (dvb_ringbuffer_free(cibuf) < len)136return -EBUSY;137138for (i = 0; i < 2; i++) {139if (slots & (1 << i)) {140msg[2] = i;141dvb_ringbuffer_write(cibuf, msg, 8);142slot[i].flags = 0;143}144}145146return 0;147}148149static ssize_t ci_ll_write(struct dvb_ringbuffer *cibuf, struct file *file,150const char __user *buf, size_t count, loff_t *ppos)151{152int free;153int non_blocking = file->f_flags & O_NONBLOCK;154u8 *page = (u8 *)__get_free_page(GFP_USER);155int res;156157if (!page)158return -ENOMEM;159160res = -EINVAL;161if (count > 2048)162goto out;163164res = -EFAULT;165if (copy_from_user(page, buf, count))166goto out;167168free = dvb_ringbuffer_free(cibuf);169if (count + 2 > free) {170res = -EWOULDBLOCK;171if (non_blocking)172goto out;173res = -ERESTARTSYS;174if (wait_event_interruptible(cibuf->queue,175(dvb_ringbuffer_free(cibuf) >= count + 2)))176goto out;177}178179DVB_RINGBUFFER_WRITE_BYTE(cibuf, count >> 8);180DVB_RINGBUFFER_WRITE_BYTE(cibuf, count & 0xff);181182res = dvb_ringbuffer_write(cibuf, page, count);183out:184free_page((unsigned long)page);185return res;186}187188static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file,189char __user *buf, size_t count, loff_t *ppos)190{191int avail;192int non_blocking = file->f_flags & O_NONBLOCK;193ssize_t len;194195if (!cibuf->data || !count)196return 0;197if (non_blocking && (dvb_ringbuffer_empty(cibuf)))198return -EWOULDBLOCK;199if (wait_event_interruptible(cibuf->queue,200!dvb_ringbuffer_empty(cibuf)))201return -ERESTARTSYS;202avail = dvb_ringbuffer_avail(cibuf);203if (avail < 4)204return 0;205len = DVB_RINGBUFFER_PEEK(cibuf, 0) << 8;206len |= DVB_RINGBUFFER_PEEK(cibuf, 1);207if (avail < len + 2 || count < len)208return -EINVAL;209DVB_RINGBUFFER_SKIP(cibuf, 2);210211return dvb_ringbuffer_read_user(cibuf, buf, len);212}213214static int dvb_ca_open(struct inode *inode, struct file *file)215{216struct dvb_device *dvbdev = file->private_data;217struct av7110 *av7110 = dvbdev->priv;218int err = dvb_generic_open(inode, file);219220dprintk(8, "av7110:%p\n",av7110);221222if (err < 0)223return err;224ci_ll_flush(&av7110->ci_rbuffer, &av7110->ci_wbuffer);225return 0;226}227228static unsigned int dvb_ca_poll (struct file *file, poll_table *wait)229{230struct dvb_device *dvbdev = file->private_data;231struct av7110 *av7110 = dvbdev->priv;232struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer;233struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer;234unsigned int mask = 0;235236dprintk(8, "av7110:%p\n",av7110);237238poll_wait(file, &rbuf->queue, wait);239poll_wait(file, &wbuf->queue, wait);240241if (!dvb_ringbuffer_empty(rbuf))242mask |= (POLLIN | POLLRDNORM);243244if (dvb_ringbuffer_free(wbuf) > 1024)245mask |= (POLLOUT | POLLWRNORM);246247return mask;248}249250static int dvb_ca_ioctl(struct file *file, unsigned int cmd, void *parg)251{252struct dvb_device *dvbdev = file->private_data;253struct av7110 *av7110 = dvbdev->priv;254unsigned long arg = (unsigned long) parg;255256dprintk(8, "av7110:%p\n",av7110);257258switch (cmd) {259case CA_RESET:260return ci_ll_reset(&av7110->ci_wbuffer, file, arg, &av7110->ci_slot[0]);261break;262case CA_GET_CAP:263{264ca_caps_t cap;265266cap.slot_num = 2;267cap.slot_type = (FW_CI_LL_SUPPORT(av7110->arm_app) ?268CA_CI_LINK : CA_CI) | CA_DESCR;269cap.descr_num = 16;270cap.descr_type = CA_ECD;271memcpy(parg, &cap, sizeof(cap));272break;273}274275case CA_GET_SLOT_INFO:276{277ca_slot_info_t *info=(ca_slot_info_t *)parg;278279if (info->num < 0 || info->num > 1)280return -EINVAL;281av7110->ci_slot[info->num].num = info->num;282av7110->ci_slot[info->num].type = FW_CI_LL_SUPPORT(av7110->arm_app) ?283CA_CI_LINK : CA_CI;284memcpy(info, &av7110->ci_slot[info->num], sizeof(ca_slot_info_t));285break;286}287288case CA_GET_MSG:289break;290291case CA_SEND_MSG:292break;293294case CA_GET_DESCR_INFO:295{296ca_descr_info_t info;297298info.num = 16;299info.type = CA_ECD;300memcpy(parg, &info, sizeof (info));301break;302}303304case CA_SET_DESCR:305{306ca_descr_t *descr = (ca_descr_t*) parg;307308if (descr->index >= 16)309return -EINVAL;310if (descr->parity > 1)311return -EINVAL;312av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetDescr, 5,313(descr->index<<8)|descr->parity,314(descr->cw[0]<<8)|descr->cw[1],315(descr->cw[2]<<8)|descr->cw[3],316(descr->cw[4]<<8)|descr->cw[5],317(descr->cw[6]<<8)|descr->cw[7]);318break;319}320321default:322return -EINVAL;323}324return 0;325}326327static ssize_t dvb_ca_write(struct file *file, const char __user *buf,328size_t count, loff_t *ppos)329{330struct dvb_device *dvbdev = file->private_data;331struct av7110 *av7110 = dvbdev->priv;332333dprintk(8, "av7110:%p\n",av7110);334return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos);335}336337static ssize_t dvb_ca_read(struct file *file, char __user *buf,338size_t count, loff_t *ppos)339{340struct dvb_device *dvbdev = file->private_data;341struct av7110 *av7110 = dvbdev->priv;342343dprintk(8, "av7110:%p\n",av7110);344return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos);345}346347static const struct file_operations dvb_ca_fops = {348.owner = THIS_MODULE,349.read = dvb_ca_read,350.write = dvb_ca_write,351.unlocked_ioctl = dvb_generic_ioctl,352.open = dvb_ca_open,353.release = dvb_generic_release,354.poll = dvb_ca_poll,355.llseek = default_llseek,356};357358static struct dvb_device dvbdev_ca = {359.priv = NULL,360.users = 1,361.writers = 1,362.fops = &dvb_ca_fops,363.kernel_ioctl = dvb_ca_ioctl,364};365366367int av7110_ca_register(struct av7110 *av7110)368{369return dvb_register_device(&av7110->dvb_adapter, &av7110->ca_dev,370&dvbdev_ca, av7110, DVB_DEVICE_CA);371}372373void av7110_ca_unregister(struct av7110 *av7110)374{375dvb_unregister_device(av7110->ca_dev);376}377378int av7110_ca_init(struct av7110* av7110)379{380return ci_ll_init(&av7110->ci_rbuffer, &av7110->ci_wbuffer, 8192);381}382383void av7110_ca_exit(struct av7110* av7110)384{385ci_ll_release(&av7110->ci_rbuffer, &av7110->ci_wbuffer);386}387388389