All of lore.kernel.org
 help / color / mirror / Atom feed
* Smart card reader support for Anysee DVB devices
@ 2011-07-17 14:18 István Váradi
  2011-08-14 23:51 ` Antti Palosaari
  0 siblings, 1 reply; 16+ messages in thread
From: István Váradi @ 2011-07-17 14:18 UTC (permalink / raw)
  To: linux-media

[-- Attachment #1: Type: text/plain, Size: 1727 bytes --]

Hi,

I have developed smart card reader support for the Anysee devices by
extending Antti Palosaari's driver. I attached the patches for it. It
registers a character device named /dev/anysee_scN for each Anysee
device.

The character device supports two ioctl's (see anysee_sc), one for
detecting the presence of a card, the other one for resetting the card
and querying the ATR. The write() operation writes to the card by
packaging the bytes into USB commands. The read() operation issues an
appropriate command over USB and returns the reply. I have also
written a simple OpenCT driver (attached) which shows the usage.

I would like to have the kernel driver included in the official
sources. For this reason I corresponded with Antti, and he suggested
the perhaps the kernel driver should have a lower-level interface. I
had the following proposal:

We would continue having the two ioctls, ANYSEE_SC_ACTIVATE and
ANYSEE_SC_PRESENT, however, ANYSEE_SC_ACTIVATE would do only the
register reading and writing.

Besides these two we need access to the anysee_ctrl_msg() function
somehow. I think the cleanest way would be via another ioctl() call in
which we would pass the return buffer as well, with the length so that
we know how many bytes to copy. Another possibility would be that a
call to write() calls anysee_ctrl_msg() and stores the return data in
a 64 byte buffer that we allocate for each device. The read()
following a write() would read this buffer, then discard it. Further
read() attempts would fail with EAGAIN, or we could maintain an offset
into the 64 byte buffer, and read as long as there is data, and fail
only then. A write() would cause losing any unread data.

What do you think?

Thanks,

Istvan

[-- Attachment #2: patch --]
[-- Type: application/octet-stream, Size: 11432 bytes --]

--- anysee.c.orig	2010-08-02 00:11:14.000000000 +0200
+++ anysee.c	2010-12-28 09:04:45.000000000 +0100
@@ -32,11 +32,14 @@
  */
 
 #include "anysee.h"
+#include "anysee_sc.h"
 #include "tda1002x.h"
 #include "mt352.h"
 #include "mt352_priv.h"
 #include "zl10353.h"
 
+#include <linux/device.h>
+
 /* debug */
 static int dvb_usb_anysee_debug;
 module_param_named(debug, dvb_usb_anysee_debug, int, 0644);
@@ -132,6 +135,273 @@
 	return anysee_ctrl_msg(d, buf, sizeof(buf), NULL, 0);
 }
 
+static dev_t sc_dev;
+static const unsigned sc_count = 16;
+static struct class* sc_class = 0;
+
+static int anysee_sc_open(struct inode *inode, struct file *filp)
+{
+        struct anysee_sc_state* sc_state = container_of(inode->i_cdev, struct anysee_sc_state, cdev);
+        filp->private_data = sc_state;
+        return 0;
+}
+
+static int anysee_sc_check_presence(struct dvb_usb_device* d)
+{
+    int ret = 0;
+    u8 x;
+
+    ret = anysee_read_reg(d, 0x0080, &x);
+    if (ret!=0) return ret;
+
+    if ((x&0x02)!=0) return 0;
+
+    ret = anysee_write_reg(d, 0xb1, 0xa7);
+    if (ret!=0) return ret;
+
+    ret = anysee_read_reg(d, 0x0080, &x);
+    
+    if (ret!=0) return ret;
+    else return ((x&0x02)==0) ? -EPIPE : 0;
+}
+
+static int anysee_sc_read_byte(struct dvb_usb_device* d, u8* dest, unsigned int timeout,
+                               u8 last)
+{
+    static const unsigned read_interval = 20;
+    
+    u8 cmd[] = { 0x34, 0x06, 0x01, 0x00 };
+    
+    u8 buf[4];
+    unsigned num_cycles = timeout / read_interval, i;
+
+    int ret = 0;
+    cmd[3] = last;
+    for(i = 0; i<num_cycles && ret==0; ++i) {
+        ret = anysee_ctrl_msg(d, cmd, sizeof(cmd), buf, sizeof(buf));
+        if (ret==0) {
+            if (buf[0]==0x01) {
+                *dest = buf[2];
+                return 0;
+            }
+            if (msleep_interruptible(read_interval)>0) {
+                return -EAGAIN;
+            }
+        }
+    }
+    
+    return -ETIMEDOUT;
+}
+
+static ssize_t anysee_sc_read(struct file *filp, char __user *buf, size_t count, loff_t *offp)
+{
+        struct anysee_sc_state* sc_state = (struct anysee_sc_state*)filp->private_data;
+        struct dvb_usb_device* d = sc_state->dvb_usb_device;
+        
+        int ret = 0;
+        unsigned index = 0;
+        u8 read_buf[count];
+
+
+	if (mutex_lock_interruptible(&sc_state->mutex) < 0)
+		return -EAGAIN;
+        
+        if (ret==0) {
+            ret = anysee_sc_check_presence(d);
+        }
+
+        while(ret==0 && index<count) {
+            ret = anysee_sc_read_byte(d, read_buf+index, (index==0) ? 1000 : 100, 0x01);
+            if (ret==0) ++index;
+        }
+
+        if (ret==0) {
+            if (copy_to_user(buf, read_buf, index)) {
+                ret = -EFAULT;
+            }
+        }
+
+        mutex_unlock(&sc_state->mutex);
+    
+        return (ret==0) ? index : ret;
+}
+
+static ssize_t anysee_sc_write(struct file *filp, const char __user *buf, size_t count, loff_t *offp)
+{
+        struct anysee_sc_state* sc_state = (struct anysee_sc_state*)filp->private_data;
+        struct dvb_usb_device* d = sc_state->dvb_usb_device;
+
+        u8 cmd[] = { 0x34, 0x08, 0x01, 0x01, 0x00, 0x05 };
+        ssize_t ret = 0;
+        unsigned written = 0;
+
+        if (count>256) {
+                return -EINVAL;
+        }
+        
+	if (mutex_lock_interruptible(&sc_state->mutex) < 0)
+		return -EAGAIN;
+
+        if (ret==0) {
+            ret = anysee_sc_check_presence(d);
+        }
+
+        if (ret==0) {
+                cmd[5] = count&0xff;
+                ret =  anysee_ctrl_msg(d, cmd, sizeof(cmd), 0, 0);
+        }
+        
+        while (ret==0 && written<count) {
+                u8 write_buf[52];
+
+                unsigned to_write = count - written;
+                if (to_write>48) to_write = 48;
+
+                write_buf[0] = 0x34;
+                write_buf[1] = 0x07;
+                write_buf[2] = to_write;
+                if (copy_from_user(write_buf+3, buf + written, to_write)) {
+                    ret = -EFAULT;
+                }
+
+                if (ret==0) {
+                    write_buf[to_write+3] = 0x01;
+                    
+                    ret = anysee_ctrl_msg(d, write_buf, to_write + 4,
+                                          write_buf, 1);
+                    if (ret==0) {
+                        if (write_buf[0]==to_write) {
+                            written += to_write;
+                        } else {
+                            ret = -EIO;
+                        }
+                    }
+                }
+        }
+        
+        mutex_unlock(&sc_state->mutex);
+
+        return (ret==0) ? count : ret;
+}
+
+static int anysee_sc_activate(struct anysee_sc_state* sc_state, unsigned long arg)
+{    
+        struct dvb_usb_device* d = sc_state->dvb_usb_device;
+        struct anysee_sc_activate __user* params = (struct anysee_sc_activate __user*)arg;
+        int ret = 0;
+        u8 x;
+
+        if (!access_ok(VERIFY_WRITE, params, sizeof(anysee_sc_activate))) {
+            ret = -EFAULT;
+        }
+
+        if (ret==0) {
+            ret = anysee_sc_check_presence(d);
+        }
+        
+        if (ret==0) {
+            ret = anysee_read_reg(d, 0x00a0, &x);
+        }
+
+        if (ret==0) {
+            ret = anysee_write_reg(d, 0x00a0, x|0x01);
+        }
+
+        if (ret==0) {
+            static u8 cmd[] = { 0x34, 0x09, 0x01 };
+            ret = anysee_ctrl_msg(d, cmd, sizeof(cmd), 0, 0);
+        }
+
+        if (ret==0) {
+            static u8 cmd[] = { 0x34, 0x08, 0x01, 0x02 };
+            ret = anysee_ctrl_msg(d, cmd, sizeof(cmd), 0, 0);
+        }
+
+        if (ret==0) {
+            unsigned index = 0;
+            unsigned atr_length = 2;
+            unsigned fmt_index = 1;
+            unsigned k = 0;
+            
+            while(index<sizeof(params->atr) && index<atr_length && ret==0) {
+                ret = anysee_sc_read_byte(d, &x, (index==0) ? 1000 : 100, 0x00);
+                if (ret==0) {
+                    if (index==fmt_index) {
+                        if (index==1) k = x&0x0f;
+                        if (x&0x10) ++atr_length;
+                        if (x&0x20) ++atr_length;
+                        if (x&0x40) ++atr_length;
+                        if (x&0x80) {
+                            fmt_index = atr_length;
+                            ++atr_length;
+                        } else {
+                            atr_length += k;
+                        }
+                    }
+                    params->atr[index++] = x;
+                }
+            }
+            params->atr_length = index;
+        }
+
+        return ret;
+}
+
+static int anysee_sc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+        struct anysee_sc_state* sc_state = (struct anysee_sc_state*)filp->private_data;
+        int ret = -ENODEV;
+
+	if (mutex_lock_interruptible(&sc_state->mutex) < 0)
+		return -EAGAIN;
+
+        switch(cmd) {
+            case ANYSEE_SC_ACTIVATE:
+              ret = anysee_sc_activate(sc_state, arg);
+              break;
+            case ANYSEE_SC_PRESENT:
+              ret = anysee_sc_check_presence(sc_state->dvb_usb_device);
+              if (ret==-EPIPE) ret = 0;
+              else if (ret==0) ret = 1;
+        }
+
+        mutex_unlock(&sc_state->mutex);
+
+        return ret;
+}
+
+static struct file_operations anysee_sc_fops = {
+        .owner = THIS_MODULE,
+        .open = anysee_sc_open,
+        .read = anysee_sc_read,
+        .write = anysee_sc_write,
+        .ioctl = anysee_sc_ioctl,
+};
+
+static int anysee_sc_init(struct dvb_usb_adapter* adap)
+{
+        struct dvb_usb_device* d = adap->dev;
+        int ret = 0;
+        struct anysee_state *state = d->priv;
+        state->sc_state.dvb_usb_device = d;
+        
+        state->sc_state.devnum = MKDEV(MAJOR(sc_dev), d->adapter[0].id);
+        
+        cdev_init(&state->sc_state.cdev, &anysee_sc_fops);
+        state->sc_state.cdev.owner = THIS_MODULE;
+
+        mutex_init(&state->sc_state.mutex);
+        
+        ret = cdev_add(&state->sc_state.cdev, state->sc_state.devnum, 1);
+        if (ret) {
+		err("%s: cdev_add failed. Error number %d", __func__, ret);
+        } else {
+                device_create(sc_class, adap->dvb_adap.device, state->sc_state.devnum, 
+                              0, "anysee_sc%u", d->adapter[0].id);
+        }
+        return ret;
+}
+
 static int anysee_init(struct dvb_usb_device *d)
 {
 	int ret;
@@ -148,6 +418,17 @@
 	return 0;
 }
 
+static void anysee_exit(struct usb_interface *intf)
+{
+	struct dvb_usb_device *d = usb_get_intfdata(intf);
+        struct anysee_state *state = d->priv;
+
+        device_destroy(sc_class, state->sc_state.devnum);
+        cdev_del(&state->sc_state.cdev);
+
+        dvb_usb_device_exit(intf);
+}
+
 /* I2C */
 static int anysee_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msg,
 	int num)
@@ -269,6 +550,10 @@
 		return ret;
 	deb_info("%s: IO port D:%02x\n", __func__, io_d);
 
+        ret = anysee_sc_init(adap);
+	if (ret)
+		return ret;
+
 	/* Select demod using trial and error method. */
 
 	/* Try to attach demodulator in following order:
@@ -541,7 +826,7 @@
 static struct usb_driver anysee_driver = {
 	.name       = "dvb_usb_anysee",
 	.probe      = anysee_probe,
-	.disconnect = dvb_usb_device_exit,
+	.disconnect = anysee_exit,
 	.id_table   = anysee_table,
 };
 
@@ -550,10 +835,28 @@
 {
 	int ret;
 
-	ret = usb_register(&anysee_driver);
-	if (ret)
-		err("%s: usb_register failed. Error number %d", __func__, ret);
-
+        ret = alloc_chrdev_region(&sc_dev, 0, sc_count, "anysee_sc");
+        if (ret) 
+		err("%s: alloc_chrdev_region failed. Error number %d", __func__, ret);
+        
+        if (!ret) {
+                sc_class = class_create(THIS_MODULE, "anysee_sc");
+                if (IS_ERR(sc_class)) {
+                        err("%s: class_simple_create failed.", __func__);
+                        unregister_chrdev_region(sc_dev, sc_count);
+                        ret = -ENODEV;
+                }
+        }
+
+        if (!ret) {
+                ret = usb_register(&anysee_driver);
+                if (ret) {
+                        err("%s: usb_register failed. Error number %d", __func__, ret);
+                        class_destroy(sc_class);
+                        unregister_chrdev_region(sc_dev, sc_count);
+                }
+        }
+                
 	return ret;
 }
 
@@ -561,6 +864,11 @@
 {
 	/* deregister this driver from the USB subsystem */
 	usb_deregister(&anysee_driver);
+
+        class_destroy(sc_class);
+        
+        unregister_chrdev_region(sc_dev, sc_count);
+
 }
 
 module_init(anysee_module_init);
--- anysee.h.orig	2010-08-02 00:11:14.000000000 +0200
+++ anysee.h	2010-12-05 19:07:55.000000000 +0100
@@ -34,6 +34,8 @@
 #ifndef _DVB_USB_ANYSEE_H_
 #define _DVB_USB_ANYSEE_H_
 
+#include <linux/cdev.h>
+
 #define DVB_USB_LOG_PREFIX "anysee"
 #include "dvb-usb.h"
 
@@ -56,9 +58,17 @@
 	CMD_SMARTCARD           = 0x34,
 };
 
+struct anysee_sc_state {
+        struct dvb_usb_device* dvb_usb_device;
+        dev_t devnum;
+        struct cdev cdev;
+        struct mutex mutex;
+};
+
 struct anysee_state {
 	u8 tuner;
 	u8 seq;
+        struct anysee_sc_state sc_state;
 };
 
 #endif

[-- Attachment #3: anysee_sc.h --]
[-- Type: text/x-chdr, Size: 338 bytes --]

#ifndef ANYSEE_SC_H
#define ANYSEE_SC_H

#include <linux/ioctl.h>

#define ANYSEE_SC_IOC_MAGIC 's'

struct anysee_sc_activate {
        unsigned atr_length;
        unsigned char atr[33];
};

#define ANYSEE_SC_ACTIVATE _IOR(ANYSEE_SC_IOC_MAGIC, 1, struct anysee_sc_activate)

#define ANYSEE_SC_PRESENT _IO(ANYSEE_SC_IOC_MAGIC, 2)

#endif

[-- Attachment #4: ifd-anysee.c --]
[-- Type: text/x-csrc, Size: 2581 bytes --]

#include "internal.h"

#include "anysee_sc.h"

#include <string.h>
#include <fcntl.h>

static int anysee_open(ifd_reader_t* reader, const char* name)
{
    ifd_device_t* dev = 0;
    int fd = -1;
    const char* device_name = 0;

    ifd_debug(1, "anysee_open: name='%s'\n", name);

    device_name = strchr(name, ':');
    if (device_name==0) return -1;
    ++device_name;

    fd = open(device_name, O_RDWR);
    ifd_debug(2, "anysee_open: fd=%d\n", fd);
    if (fd<0) return -1;
    
    reader->name = "Anysee DVB USB card reader";
    reader->nslots = 1;

    dev = ifd_device_new(name, 0, sizeof(*dev));
    reader->device = dev;
    dev->timeout = 1000;
    dev->fd = fd;
    dev->type = IFD_DEVICE_TYPE_OTHER;

    return 0;
}

static int anysee_close(ifd_reader_t * reader)
{
    return close(reader->device->fd);
}

static int anysee_activate(ifd_reader_t *reader)
{
    return 0;
}

static int anysee_deactivate(ifd_reader_t *reader)
{
    return 0;
}

static int anysee_card_status(ifd_reader_t *reader, int slot, int *status)
{
    int rv = ioctl(reader->device->fd, ANYSEE_SC_PRESENT);
    ifd_debug(2, "anysee_card_status: rv=%d\n", rv);
    if (rv<0) {
        return -1;
    } else {
        *status = (rv==0) ? 0 : IFD_CARD_PRESENT;
        return 0;
    }
}

static int anysee_card_reset(ifd_reader_t *reader, int slot, void *atr, size_t atr_len)
{
    struct anysee_sc_activate activate;

    if (ioctl(reader->device->fd, ANYSEE_SC_ACTIVATE, &activate)<0) {
        ifd_debug(2, "anysee_card_reset: failed\n");
        return -1;
    } else {
        size_t length = (atr_len<activate.atr_length) ? atr_len : activate.atr_length;
        memcpy(atr, activate.atr, length);
        return length;
    }
}


static int anysee_send(ifd_reader_t *reader, unsigned int dad,
                       const unsigned char *buffer, size_t len)
{
    return write(reader->device->fd, buffer, len);
}

static int anysee_recv(ifd_reader_t *reader, unsigned int dad,
                       unsigned char *buffer, size_t len,
                       long timeout)
{
    return read(reader->device->fd, buffer, len);
}

static struct ifd_driver_ops anysee_ops;

void ifd_anysee_register()
{
    anysee_ops.open = &anysee_open;
    anysee_ops.close = &anysee_close;
    anysee_ops.activate = &anysee_activate;
    anysee_ops.deactivate = &anysee_deactivate;
    anysee_ops.card_status = &anysee_card_status;
    anysee_ops.card_reset = &anysee_card_reset;
    anysee_ops.send = &anysee_send;
    anysee_ops.recv = &anysee_recv;

    ifd_driver_register("anysee", &anysee_ops);
}

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2011-10-03 17:56 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-07-17 14:18 Smart card reader support for Anysee DVB devices István Váradi
2011-08-14 23:51 ` Antti Palosaari
2011-08-15 11:14   ` Antti Palosaari
2011-08-17 20:41     ` Antti Palosaari
2011-08-29 14:44       ` István Váradi
2011-08-29 14:50         ` Antti Palosaari
2011-08-29 15:13           ` István Váradi
2011-08-29 15:23             ` Antti Palosaari
2011-09-02 11:04               ` Bjørn Mork
2011-09-02 13:32                 ` Antti Palosaari
2011-09-28 14:32                   ` Antti Palosaari
2011-09-30 15:36                     ` Antti Palosaari
2011-10-02 21:06                       ` HoP
2011-10-03 17:56                       ` Bjørn Mork
2011-10-03 12:36           ` James Courtier-Dutton
2011-10-03 12:50             ` Antti Palosaari

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.