Re: Smart card reader support for Anysee DVB devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 09/02/2011 04:32 PM, Antti Palosaari wrote:
On 09/02/2011 02:04 PM, Bjørn Mork wrote:
Antti Palosaari<crope@xxxxxx> 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
--
http://palosaari.fi/
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 */

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux