Smart card reader support for Anysee DVB devices

Message ID 4E832FE6.7020103@iki.fi (mailing list archive)
State RFC, archived
Headers

Commit Message

Antti Palosaari Sept. 28, 2011, 2:32 p.m. UTC
On 09/02/2011 04:32 PM, Antti Palosaari wrote:
> On 09/02/2011 02:04 PM, Bjørn Mork wrote:
>> Antti Palosaari<crope@iki.fi> writes:
>>
>>> Since Anysee device itself does not have CCID interface it is needed
>>> to make virtual USB device in order to get CCID support. I have never
>>> seen virtual USB devices like that, but there is VHCI in current
>>> kernel staging that actually does something like that over IP.
>>
>> Don't know if you have seen this already, but there's a virtual CCID
>> device implementation in QEMU. See
>> http://wiki.qemu.org/Features/Smartcard
>> Should be a good starting point. Combine it withe the VHCI driver from
>> USBIP and you have your CCID device.
>
> It is first time I hear about QEMU virtual CCID. Now we have all parts
> needed for USBIP VHCI and QEMU virtual CCID, just glue those together.
>
> I wonder if it is wise to even create virtual CCID "core" to Kernel.
> There is few other readers that can use that too, actually I think all
> USB readers that have unique USB ID (blocking out those which uses
> USB-serial converters with common IDs).
>
> As I see that CCID still more complex as serial device I will still look
> implementing it as serial as now.

Here it is, patch attached. Implemented as serial device. Anysee uses 
two different smart card interfaces, CST56I01 and TDA8024. That one is 
old CST56I01, I will try to add TDA8024 later, maybe even tonight.

Anyhow, it is something like proof-of-concept currently, missing locks 
and abusing ttyUSB. Have you any idea if I should reserve own major 
device numbers for Anysee or should I reserve one like DVB common?

Any other ideas?

Antti
  

Comments

Antti Palosaari Sept. 30, 2011, 3:36 p.m. UTC | #1
On 09/28/2011 05:32 PM, Antti Palosaari wrote:
> On 09/02/2011 04:32 PM, Antti Palosaari wrote:

>> As I see that CCID still more complex as serial device I will still look
>> implementing it as serial as now.
>
> Here it is, patch attached. Implemented as serial device. Anysee uses
> two different smart card interfaces, CST56I01 and TDA8024. That one is
> old CST56I01, I will try to add TDA8024 later, maybe even tonight.
>
> Anyhow, it is something like proof-of-concept currently, missing locks
> and abusing ttyUSB. Have you any idea if I should reserve own major
> device numbers for Anysee or should I reserve one like DVB common?
>
> Any other ideas?

http://git.linuxtv.org/anttip/media_tree.git/shortlog/refs/heads/anysee

Now it works for TDA8024 based readers too (addition to CST56I01). Main 
difference was that TDA8024 designs reads card presence from GPIO whilst 
for CST56I01 it was got from anysee firmware (I think CST56I01 outputs 
that using I2C whilst TDA8024 have external IO line).

I tested it;
* E30 Combo Plus (TDA8024)
* E7 T2C (TDA8024)
* E30 C (CST56I01)

If you would like to help me then you can find out correct device name 
and whats needed for that. I mainly see following possibilities;
* /dev/ttyAnyseeN
* /dev/ttyDVBN
* /dev/adapterN/serial

regards
Antti
  
HoP Oct. 2, 2011, 9:06 p.m. UTC | #2
> If you would like to help me then you can find out correct device name and
> whats needed for that. I mainly see following possibilities;
> * /dev/ttyAnyseeN
> * /dev/ttyDVBN
> * /dev/adapterN/serial
>

/dev/ttyscXN

or

/dev/dvb/adapterX/scN

where X = ordered adapter number
and N = ordered smartcard number in one device

/Honza
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  
Bjørn Mork Oct. 3, 2011, 5:56 p.m. UTC | #3
Antti Palosaari <crope@iki.fi> writes:

> If you would like to help me then you can find out correct device name
> and whats needed for that. I mainly see following possibilities;
> * /dev/ttyAnyseeN
> * /dev/ttyDVBN
> * /dev/adapterN/serial

You should probably include the TTY maintainer in that discussion.  I
assume this device won't really be a TTY device?  Then it probably
shouldn't be named like one.


Bjørn
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  

Patch

diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index 0bc1372..79497f3 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -44,7 +44,7 @@ 
 #include "cxd2820r.h"
 
 /* debug */
-static int dvb_usb_anysee_debug;
+static int dvb_usb_anysee_debug = -1;
 module_param_named(debug, dvb_usb_anysee_debug, int, 0644);
 MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
 static int dvb_usb_anysee_delsys;
@@ -67,6 +67,11 @@  static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
 	if (mutex_lock_interruptible(&anysee_usb_mutex) < 0)
 		return -EAGAIN;
 
+	if (sbuf[0] == CMD_SMARTCARD) {
+		deb_xfer(">>> ");
+		debug_dump(buf, slen, deb_xfer);
+	}
+
 	/* We need receive one message more after dvb_usb_generic_rw due
 	   to weird transaction flow, which is 1 x send + 2 x receive. */
 	ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0);
@@ -79,8 +84,15 @@  static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
 		if (ret)
 			err("%s: recv bulk message failed: %d", __func__, ret);
 		else {
-			deb_xfer("<<< ");
-			debug_dump(buf, act_len, deb_xfer);
+//			deb_xfer("<<< ");
+//			debug_dump(buf, act_len, deb_xfer);
+			if (sbuf[0] == CMD_SMARTCARD) {
+				if (buf[63] != 0x4f)
+					deb_info("%s: packet NOK: %02x\n", __func__, buf[63]);
+
+				deb_xfer("<<< ");
+				debug_dump(buf, 40, deb_xfer);
+			}
 		}
 	}
 
@@ -701,6 +713,8 @@  static int anysee_frontend_attach(struct dvb_usb_adapter *adap)
 		adap->fe_adap[0].fe = dvb_attach(tda10023_attach,
 			&anysee_tda10023_config, &adap->dev->i2c_adap, 0x48);
 
+		state->has_sc = true;
+
 		break;
 	case ANYSEE_HW_507SI: /* 11 */
 		/* E30 S2 Plus */
@@ -1014,6 +1028,7 @@  static int anysee_tuner_attach(struct dvb_usb_adapter *adap)
 	return ret;
 }
 
+#if 0
 static int anysee_rc_query(struct dvb_usb_device *d)
 {
 	u8 buf[] = {CMD_GET_IR_CODE};
@@ -1039,6 +1054,7 @@  static int anysee_rc_query(struct dvb_usb_device *d)
 
 	return 0;
 }
+#endif
 
 static int anysee_ci_read_attribute_mem(struct dvb_ca_en50221 *ci, int slot,
 	int addr)
@@ -1208,6 +1224,292 @@  static void anysee_ci_release(struct dvb_usb_device *d)
 	return;
 }
 
+// stty -aF /dev/ttyUSB0
+// setserial /dev/ttyUSB0
+// statserial /dev/ttyUSB0
+// more /proc/tty/drivers
+// watch head /proc/tty/driver/serial
+// http://www.ibiblio.org/pub/Linux/docs/HOWTO/other-formats/html_single/Serial-HOWTO.html
+
+
+static int anysee_rc_query(struct dvb_usb_device *d)
+{
+	u8 sbuf[] = {CMD_SMARTCARD, 0x06, 0x20};
+	u8 rbuf[60];
+	int ret;
+	u8 *ptr;
+	struct anysee_state *state = d->priv;
+	struct tty_struct *tty = state->sc_tty_driver->ttys[0];
+
+	if (state->sc_poll_count-- <= 0)
+		return 0;
+
+	deb_info("%s:\n", __func__);
+
+	ret = anysee_ctrl_msg(d, sbuf, sizeof(sbuf), rbuf, sizeof(rbuf));
+	if (ret)
+		return -EIO;
+
+	if (rbuf[0] != 0) {
+		ptr = &rbuf[2];
+		deb_info("rece data: ");
+		debug_dump(ptr, rbuf[0], deb_info);
+
+		tty_insert_flip_string(tty, &rbuf[2], rbuf[0]);
+		tty_flip_buffer_push(tty);
+	}
+
+	return 0;
+}
+
+static int anysee_sc_open(struct tty_struct *tty, struct file *filp)
+{
+	struct dvb_usb_device *d = tty->driver_data;
+	struct anysee_state *state = d->priv;
+	int ret, i;
+	u8 sbuf[5] = {CMD_SMARTCARD, 0x03, 1};
+	u8 tab[][2] = {
+		{0x00, 0xaf},
+		{0x10, 0x08},
+		{0x11, 0x4c},
+	};
+
+	deb_info("%s:\n", __func__);
+
+	for (i = 0; i < ARRAY_SIZE(tab); i++) {
+		sbuf[3] = tab[i][0];
+		sbuf[4] = tab[i][1];
+		ret = anysee_ctrl_msg(d, sbuf, sizeof(sbuf), NULL, 0);
+		if (ret)
+			goto err;
+	}
+
+	state->sc_poll_count = 0;
+
+	return 0;
+err:
+	return ret;
+}
+
+static void anysee_sc_close(struct tty_struct *tty, struct file *filp)
+{
+	struct dvb_usb_device *d = tty->driver_data;
+	struct anysee_state *state = d->priv;
+
+	deb_info("%s:\n", __func__);
+
+	state->sc_poll_count = 0;
+
+	msleep(1000);
+
+	return;
+}
+
+static int anysee_sc_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear)
+{
+	int ret;
+	struct dvb_usb_device *d = tty->driver_data;
+	struct anysee_state *state = d->priv;
+	u8 sbuf[] = {CMD_SMARTCARD, 0x08, 0x01, 0x02};
+
+	deb_info("%s: set=%x clear=%x set_RTS=%d clear_RTS=%d sc_card_present=%d\n", __func__, set, clear, (set & TIOCM_RTS), (clear & TIOCM_RTS),  state->sc_card_present);
+
+	if ((set & TIOCM_RTS) && state->sc_card_present) {
+		deb_info("%s: RESET CARD\n", __func__);
+		ret = anysee_ctrl_msg(d, sbuf, sizeof(sbuf), NULL, 0);
+		if (ret)
+			goto err;
+
+		state->sc_poll_count = 50;
+	}
+
+	return 0;
+err:
+	return ret;
+}
+
+static int anysee_sc_tiocmget(struct tty_struct *tty)
+{
+	struct dvb_usb_device *d = tty->driver_data;
+	struct anysee_state *state = d->priv;
+	int ret;
+	u8 sbuf[] = {CMD_SMARTCARD, 0x02, 0x01, 0x10};
+	u8 rbuf[1];
+	int tio = 0;
+
+	deb_info("%s:\n", __func__);
+	ret = anysee_ctrl_msg(d, sbuf, sizeof(sbuf), rbuf, sizeof(rbuf));
+	if (ret)
+		goto err;
+
+	if (rbuf[0] & (1 << 7)) {
+		tio |= TIOCM_CD;
+		state->sc_card_present = false;
+	} else {
+		tio &= ~TIOCM_CD;
+		state->sc_card_present = true;
+	}
+
+	deb_info("%s: TIO=%x rd:", __func__, tio);
+	debug_dump(rbuf, 1, deb_info);
+
+	return tio;
+err:
+	return ret;
+}
+
+static int anysee_sc_write(struct tty_struct *tty, const u8 *buf, int count)
+{
+	struct dvb_usb_device *d = tty->driver_data;
+	struct anysee_state *state = d->priv;
+	int ret;
+	u8 rbuf[60];
+	u8 sbuf[60] = {CMD_SMARTCARD, 0x08, 0x01, 0x01, 0x00, count};
+
+	deb_info("send data: ");
+	debug_dump(buf, count, deb_info);
+
+	ret = anysee_ctrl_msg(d, sbuf, 7, NULL, 0);
+	if (ret)
+		goto err;
+
+	sbuf[1] = 0x07;
+	sbuf[2] = count;
+	memcpy(&sbuf[3], buf, count);
+
+	ret = anysee_ctrl_msg(d, sbuf, count+3, rbuf, sizeof(rbuf));
+	if (ret)
+		goto err;
+
+	if (rbuf[0]) {
+		/* TODO: can that echo removed ? */
+		tty_insert_flip_string(tty, &sbuf[3], rbuf[0]);
+		tty_flip_buffer_push(tty);
+	}
+
+	state->sc_poll_count = 50;
+
+	return count;
+
+err:
+	return ret;
+}
+
+static int anysee_sc_write_room(struct tty_struct *tty)
+{
+	deb_info("%s:\n", __func__);
+	return 32;
+}
+
+static void anysee_sc_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+	tty_termios_copy_hw(tty->termios, old);
+	deb_info("%s:\n", __func__);
+	return;
+}
+
+static int anysee_sc_install(struct tty_driver *driver, struct tty_struct *tty)
+{
+	int idx = tty->index;
+
+	deb_info("%s:\n", __func__);
+
+	if (tty_init_termios(tty) == 0) {
+		tty_driver_kref_get(driver);
+		tty->count++;
+		driver->ttys[idx] = tty;
+		tty->driver_data = driver->driver_state;
+		return 0;
+	}
+	return -ENOMEM;
+}
+
+static const struct tty_operations serial_ops = {
+	.open =			anysee_sc_open,
+	.close =		anysee_sc_close,
+	.write =		anysee_sc_write,
+//	.hangup = 		anysee_sc_hangup,
+	.write_room =		anysee_sc_write_room,
+//	.ioctl =		anysee_sc_ioctl,
+	.set_termios =		anysee_sc_set_termios,
+//	.throttle =		anysee_sc_throttle,
+//	.unthrottle =		anysee_sc_unthrottle,
+//	.break_ctl =		anysee_sc_break,
+//	.chars_in_buffer =	anysee_sc_chars_in_buffer,
+	.tiocmget =		anysee_sc_tiocmget,
+	.tiocmset =		anysee_sc_tiocmset,
+//	.get_icount = 		anysee_sc_get_icount,
+//	.cleanup = 		anysee_sc_cleanup,
+	.install = 		anysee_sc_install,
+//	.proc_fops =		&anysee_sc_proc_fops,
+};
+
+#define SERIAL_TTY_MAJOR 188  /* abuse usb-serial... */
+static int anysee_sc_init(struct dvb_usb_device *d)
+{
+	struct anysee_state *state = d->priv;
+	int ret;
+
+	deb_info("%s:\n", __func__);
+
+	state->sc_tty_driver = alloc_tty_driver(1);
+	if (!state->sc_tty_driver) {
+		ret = -ENOMEM;
+		goto err_alloc_tty_driver;
+	}
+
+	state->sc_tty_driver->owner = THIS_MODULE;
+	state->sc_tty_driver->driver_name = "anysee";
+	state->sc_tty_driver->name = "ttyUSB";
+	state->sc_tty_driver->major = SERIAL_TTY_MAJOR;
+	state->sc_tty_driver->minor_start = 0;
+	state->sc_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+	state->sc_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+	state->sc_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+	state->sc_tty_driver->init_termios = tty_std_termios;
+	state->sc_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	state->sc_tty_driver->init_termios.c_ispeed = 9600;
+	state->sc_tty_driver->init_termios.c_ospeed = 9600;
+	state->sc_tty_driver->driver_state = d;
+	tty_set_operations(state->sc_tty_driver, &serial_ops);
+	ret = tty_register_driver(state->sc_tty_driver);
+	if (ret)
+		goto err_tty_register_driver;
+
+	if (!tty_register_device(state->sc_tty_driver, 0, NULL))
+		goto err_tty_register_device;
+
+	info("serial device registered");
+
+	return 0;
+
+err_tty_register_device:
+	tty_unregister_driver(state->sc_tty_driver);
+
+err_tty_register_driver:
+	put_tty_driver(state->sc_tty_driver);
+
+err_alloc_tty_driver:
+	state->has_sc = false;
+	err("%s: failed=%d", __func__, ret);
+
+	return ret;
+}
+
+
+static void anysee_sc_release(struct dvb_usb_device *d)
+{
+	struct anysee_state *state = d->priv;
+
+	/* detach SmartCard */
+	if (state->has_sc) {
+		tty_unregister_device(state->sc_tty_driver, 0);
+		tty_unregister_driver(state->sc_tty_driver);
+	}
+
+	return;
+}
+
 static int anysee_init(struct dvb_usb_device *d)
 {
 	struct anysee_state *state = d->priv;
@@ -1232,6 +1534,15 @@  static int anysee_init(struct dvb_usb_device *d)
 		}
 	}
 
+	/* attach SmartCard */
+	if (state->has_sc) {
+		ret = anysee_sc_init(d);
+		if (ret) {
+			state->has_sc = false;
+			return ret;
+		}
+	}
+
 	return 0;
 }
 
@@ -1280,6 +1591,7 @@  static void anysee_disconnect(struct usb_interface *intf)
 {
 	struct dvb_usb_device *d = usb_get_intfdata(intf);
 
+	anysee_sc_release(d);
 	anysee_ci_release(d);
 	dvb_usb_device_exit(intf);
 
@@ -1342,7 +1654,7 @@  static struct dvb_usb_device_properties anysee_properties = {
 		.protocol         = RC_TYPE_OTHER,
 		.module_name      = "anysee",
 		.rc_query         = anysee_rc_query,
-		.rc_interval      = 250,  /* windows driver uses 500ms */
+		.rc_interval      = 150,  /* windows driver uses 500ms */
 	},
 
 	.i2c_algo         = &anysee_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/anysee.h b/drivers/media/dvb/dvb-usb/anysee.h
index 8ac8794..1026717 100644
--- a/drivers/media/dvb/dvb-usb/anysee.h
+++ b/drivers/media/dvb/dvb-usb/anysee.h
@@ -38,6 +38,9 @@ 
 #include "dvb-usb.h"
 #include "dvb_ca_en50221.h"
 
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
 #define deb_info(args...) dprintk(dvb_usb_anysee_debug, 0x01, args)
 #define deb_xfer(args...) dprintk(dvb_usb_anysee_debug, 0x02, args)
 #define deb_rc(args...)   dprintk(dvb_usb_anysee_debug, 0x04, args)
@@ -63,8 +66,14 @@  struct anysee_state {
 	u8 seq;
 	u8 fe_id:1; /* frondend ID */
 	u8 has_ci:1;
+	u8 has_sc:1;
+
 	struct dvb_ca_en50221 ci;
 	unsigned long ci_cam_ready; /* jiffies */
+
+	struct tty_driver *sc_tty_driver;
+	u8 sc_card_present:1;
+	int sc_poll_count;
 };
 
 #define ANYSEE_HW_507T    2 /* E30 */