diff options
author | Jose Alberto Reguero <jareguero@telefonica.net> | 2012-03-05 02:22:05 +0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-03-19 21:15:16 +0400 |
commit | 962f8f67e48693d110c26d06089406dd9931474d (patch) | |
tree | 5628eb5a09797b861b300fbdfa17b7f9caaf7610 /drivers/media/dvb | |
parent | 266e8ae37daa04fb3d89759305659f4e0acbc126 (diff) | |
download | linux-962f8f67e48693d110c26d06089406dd9931474d.tar.xz |
[media] Add CI support to az6007 driver
This patch add CI support to az6007 driver.
Signed-off-by: Jose Alberto Reguero <jareguero@telefonica.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/dvb')
-rw-r--r-- | drivers/media/dvb/dvb-usb/az6007.c | 377 |
1 files changed, 376 insertions, 1 deletions
diff --git a/drivers/media/dvb/dvb-usb/az6007.c b/drivers/media/dvb/dvb-usb/az6007.c index df4cbc0ed932..4008b9c50fbd 100644 --- a/drivers/media/dvb/dvb-usb/az6007.c +++ b/drivers/media/dvb/dvb-usb/az6007.c @@ -54,6 +54,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); struct az6007_device_state { struct mutex mutex; + struct mutex ca_mutex; struct dvb_ca_en50221 ca; unsigned warm:1; int (*gate_ctrl) (struct dvb_frontend *, int); @@ -218,6 +219,371 @@ static int az6007_rc_query(struct dvb_usb_device *d) return 0; } +static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC1; + value = address; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + ret = b[0]; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + deb_info("%s %d", __func__, slot); + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC2; + value1 = address; + index = value; + blen = 0; + + ret = az6007_write(d, req, value1, index, NULL, blen); + if (ret != 0) + warn("usb out operation failed. (%d)", ret); + + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + if (slot != 0) + return -EINVAL; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + mutex_lock(&state->ca_mutex); + + req = 0xC3; + value = address; + index = 0; + blen = 2; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + if (b[0] == 0) + warn("Read CI IO error"); + + ret = b[1]; + deb_info("read cam data = %x from 0x%x", b[1], value); + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + +static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC4; + value1 = address; + index = value; + blen = 0; + + ret = az6007_write(d, req, value1, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + + req = 0xC8; + value = 0; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else{ + ret = b[0]; + } + kfree(b); + return ret; +} + +static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret, i; + u8 req; + u16 value; + u16 index; + int blen; + + mutex_lock(&state->ca_mutex); + + req = 0xC6; + value = 1; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + msleep(500); + req = 0xC6; + value = 0; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + for (i = 0; i < 15; i++) { + msleep(100); + + if (CI_CamReady(ca, slot)) { + deb_info("CAM Ready"); + break; + } + } + msleep(5000); + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return 0; +} + +static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + deb_info("%s", __func__); + mutex_lock(&state->ca_mutex); + req = 0xC7; + value = 1; + index = 0; + blen = 0; + + ret = az6007_write(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 *b; + + b = kmalloc(12, GFP_KERNEL); + if (!b) + return -ENOMEM; + mutex_lock(&state->ca_mutex); + + req = 0xC5; + value = 0; + index = 0; + blen = 1; + + ret = az6007_read(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else + ret = 0; + + if (!ret && b[0] == 1) { + ret = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } + + mutex_unlock(&state->ca_mutex); + kfree(b); + return ret; +} + + +static void az6007_ci_uninit(struct dvb_usb_device *d) +{ + struct az6007_device_state *state; + + deb_info("%s", __func__); + + if (NULL == d) + return; + + state = (struct az6007_device_state *)d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + + +static int az6007_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct az6007_device_state *state = (struct az6007_device_state *)d->priv; + int ret; + + deb_info("%s", __func__); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = az6007_ci_read_attribute_mem; + state->ca.write_attribute_mem = az6007_ci_write_attribute_mem; + state->ca.read_cam_control = az6007_ci_read_cam_control; + state->ca.write_cam_control = az6007_ci_write_cam_control; + state->ca.slot_reset = az6007_ci_slot_reset; + state->ca.slot_shutdown = az6007_ci_slot_shutdown; + state->ca.slot_ts_enable = az6007_ci_slot_ts_enable; + state->ca.poll_slot_status = az6007_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + 0, /* flags */ + 1);/* n_slots */ + if (ret != 0) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + deb_info("CI initialized."); + + return 0; +} + static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6]) { struct az6007_device_state *st = d->priv; @@ -249,6 +615,8 @@ static int az6007_frontend_attach(struct dvb_usb_adapter *adap) st->gate_ctrl = adap->fe_adap[0].fe->ops.i2c_gate_ctrl; adap->fe_adap[0].fe->ops.i2c_gate_ctrl = drxk_gate_ctrl; + az6007_ci_init(adap); + return 0; } @@ -471,6 +839,13 @@ int az6007_identify_state(struct usb_device *udev, static struct dvb_usb_device_properties az6007_properties; +static void az6007_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + az6007_ci_uninit(d); + dvb_usb_device_exit(intf); +} + static int az6007_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -546,7 +921,7 @@ static struct dvb_usb_device_properties az6007_properties = { static struct usb_driver az6007_usb_driver = { .name = "dvb_usb_az6007", .probe = az6007_usb_probe, - .disconnect = dvb_usb_device_exit, + .disconnect = az6007_usb_disconnect, .id_table = az6007_usb_table, }; |