code modified ( I have applied your comments...)

Signed-off-by: Alain Degreffe <eczema@ecze.com>


diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 3efe670..1afcd0d 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -554,6 +554,15 @@ config USB_SERIAL_OMNINET
 	  To compile this driver as a module, choose M here: the
 	  module will be called omninet.
 
+config USB_SERIAL_IUU
+	tristate "Infinity Usb  Unlimited SM Reader (EXPERIMENTAL)"
+	depends on USB_SERIAL && EXPERIMENTAL
+	help
+	  Say Y here if you want to use a IUU in phoenix mode.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called iuu_phoenix.
+
 config USB_SERIAL_DEBUG
 	tristate "USB Debugging Device"
 	depends on USB_SERIAL
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 61166ad..96b8ddc 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -47,4 +47,5 @@ obj-$(CONFIG_USB_SERIAL_TI)			+= ti_usb_3410_5052.o
 obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
 obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
 obj-$(CONFIG_USB_SERIAL_XIRCOM)			+= keyspan_pda.o
+obj-$(CONFIG USB_SERIAL_IUU)			+= iuu_phoenix.o
 
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
new file mode 100644
index 0000000..82d8485
--- /dev/null
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -0,0 +1,1110 @@
+/*
+ * Infinity Unlimited USB Phoenix driver
+ *
+ * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
+ *
+ *
+ * Original code taken from iuutool ( Copyright (C) 2006 Juan Carlos Borrás )
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *  And tested with help of WB Electronics
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include "iuu_phoenix.h"
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v0.2"
+#define DRIVER_DESC "Infinity USB Unlimited Phoenix driver"
+
+static struct usb_device_id id_table[] = {
+	{USB_DEVICE(IUU_USB_VENDOR_ID, IUU_USB_PRODUCT_ID)},
+	{}			/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_driver iuu_driver = {
+	.name = "iuu_phoenix",
+	.probe = usb_serial_probe,
+	.disconnect = usb_serial_disconnect,
+	.id_table = id_table,
+	.no_dynamic_id = 1,
+};
+
+static int iuu_startup(struct usb_serial *serial);
+
+static void iuu_shutdown(struct usb_serial *serial);
+
+static int iuu_open(struct usb_serial_port *port, struct file *filp);
+static void iuu_close(struct usb_serial_port *port, struct file *filp);
+
+static int iuu_uart_write(struct usb_serial_port *port,
+			  const u8 *buf, int count);
+
+static int read_immediate(struct usb_serial_port *port, u8 *buf,
+			  u8 count);
+static int iuu_uart_flush(struct usb_serial_port *port);
+static int iuu_uart_read(struct usb_serial_port *port, u8 *data,
+			 u8 *len);
+static int iuu_uart_baud(struct usb_serial_port *port, u_int32_t baud,
+			 u_int32_t *actual, u_int8_t parity);
+static int iuu_reset(struct usb_serial_port *port, u_int8_t wt);
+static int bulk_immediate(struct usb_serial_port *port, u8 *buf,
+			  u8 count);
+
+static int iuu_ioctl(struct usb_serial_port *port, struct file *file,
+		     unsigned int cmd, unsigned long arg);
+static int iuu_tiocmset(struct usb_serial_port *port, struct file *file,
+			unsigned int set, unsigned int clear);
+static int iuu_tiocmget(struct usb_serial_port *port, struct file *file);
+
+static int iuu_uart_read_and_store(struct usb_serial_port *port);
+static int iuu_uart_on(struct usb_serial_port *port);
+static int iuu_uart_off(struct usb_serial_port *port);
+
+static struct usb_serial_driver iuu_device = {
+	.driver = {
+		   .owner = THIS_MODULE,
+		   .name = "IUU Phoenix",
+		   },
+	.id_table = id_table,
+	.num_interrupt_in = NUM_DONT_CARE,
+	.num_bulk_in = 1,
+	.num_bulk_out = 1,
+	.num_ports = 1,
+	.open = iuu_open,
+	.close = iuu_close,
+	.write = iuu_uart_write,
+	.ioctl = iuu_ioctl,
+	.tiocmget = iuu_tiocmget,
+	.tiocmset = iuu_tiocmset,
+	.attach = iuu_startup,
+	.shutdown = iuu_shutdown,
+};
+
+struct iuu_private {
+	spinlock_t lock; /* store irq state */
+	wait_queue_head_t delta_msr_wait;
+	u8 line_control;
+	u8 line_status;
+	u8 termios_initialized;
+	int write_busy;
+	int TIOSTATUS;
+};
+
+/* Structure used for write/read operation */
+
+struct iuu_buffers {
+	u8 buf[256];
+	u8 finalbuf[256];
+	u8 dbgbuf[512];
+	u8 len;
+};
+
+/* turbo parameter */
+
+int multiplicator = 100;
+int clockmode = 1;
+int smart = 1;
+
+static int iuu_startup(struct usb_serial *serial)
+{
+	struct iuu_private *priv;
+	priv = kmalloc(sizeof(struct iuu_private), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	memset(priv, 0x00, sizeof(struct iuu_private));
+	spin_lock_init(&priv->lock);
+	init_waitqueue_head(&priv->delta_msr_wait);
+	usb_set_serial_port_data(serial->port[0], priv);
+	return 0;
+}
+
+/* Shutdown function */
+
+static void iuu_shutdown(struct usb_serial *serial)
+{
+
+	dbg("%s", __FUNCTION__);
+	kfree(usb_get_serial_port_data(serial->port[0]));
+	usb_set_serial_port_data(serial->port[0], NULL);
+}
+
+/* Status Function
+ * Return value is
+ * 0x00 = no card
+ * 0x01 = smartcard
+ * 0x02 = sim card
+ */
+
+int iuu_status(struct usb_serial_port *port, char *st)
+{
+	int status;
+	st[0] = IUU_GET_STATE_REGISTER;
+
+	status = bulk_immediate(port, st, 1);
+	if (status != IUU_OPERATION_OK) {
+		return status;
+	}
+
+	status = read_immediate(port, st, 1);
+	return status;
+}
+
+/* Fake settings
+ * The IUU need to be reset to deliver the ATR value
+ * ioctl is used ( tiocmget/set in new kernels) to pass the RST order
+ * In real life, a period of 500 ms is required to reset a card
+ * Here we just care that RST followed by a NONE is received to call iuu_reset
+ */
+
+static int
+iuu_tiocmset(struct usb_serial_port *port, struct file *file, unsigned int set,
+	     unsigned int clear)
+{
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	tty = port->tty;
+
+	dbg("%s (%d) msg : SET = 0x%04x, CLEAR = 0x%04x ", __FUNCTION__,
+	    port->number, set, clear);
+	if (set == TIOCM_RTS)
+		priv->TIOSTATUS = TIOCM_RTS;
+
+	if ((set == 0) && priv->TIOSTATUS == TIOCM_RTS) {
+		dbg("%s TIOCMSET RESET called !!!", __FUNCTION__);
+		priv->TIOSTATUS = 0;
+		if (iuu_reset(port, 0x0C))
+			return -EIO;
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1 + 500 * HZ / 1000);
+		iuu_uart_read_and_store(port);
+	}
+
+	return 0;
+}
+
+/* This is used to provide a carrier detect mechanism
+ * When a card is present, the response is 0x00
+ * When no card , the reader respond with TIOCM_CD
+ * This is known as CD autodetect mechanism
+ */
+
+static int iuu_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+	u8 *st;
+	int status ;
+	st = kmalloc(sizeof(u8), GFP_KERNEL);
+	if (!st)
+		return -ENOMEM ;
+	iuu_status(port, st);
+
+	dbg("%s (%d) msg ", __FUNCTION__, port->number);
+	if (st[0] & IUU_FULLCARD_IN) {
+		dbg("%s  card present ! value returned %i ", __FUNCTION__,
+		    TIOCM_CD);
+		status = 0 ;
+	} else {
+		status TIOCM_CD;
+	}
+	kfree(st);
+	return status ;
+}
+
+
+/* 
+ *
+ * I don't know if I will use this function
+ * but it could be usefull to support non classical
+ * ioctl command for special tuning with program
+ * in userland like iptable for netfilter....
+ *
+ *
+ */
+
+static int
+iuu_ioctl(struct usb_serial_port *port, struct file *file, unsigned int cmd,
+	  unsigned long arg)
+{
+
+	int mask;
+
+	dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
+
+	if ( get_user(mask, (unsigned long *)arg) )
+		return -EFAULT ;
+
+	dbg("%s (%d) msg = %04x", __FUNCTION__, port->number, mask);
+	switch (cmd) {
+	case TCGETS:
+		return (0);
+
+	/* could be usefull later... */
+	case TCFLSH: 
+		return -ENOIOCTLCMD;
+
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TCSETS:
+	case TIOCSSERIAL:
+		if (mask & CS8)
+			dbg("%s  TIOCMSET CS8", __FUNCTION__);
+		if (mask & CS7)
+			dbg("%s  TIOCMSET CS7", __FUNCTION__);
+		if (mask & CSTOPB)
+			dbg("%s  TIOCMSET CSTOPB", __FUNCTION__);
+		if (mask & PARENB)
+			dbg("%s  TIOCMSET PARENB", __FUNCTION__);
+		if (mask & PARODD)
+			dbg("%s  TIOCMSET PARODD", __FUNCTION__);
+
+		dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__,
+		    port->number);
+		return (0);
+
+	case TIOCMIWAIT:
+		dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number);
+		return 0;
+
+	default:
+		dbg("%s not supported = 0x%04x", __FUNCTION__, cmd);
+
+	}
+	return -ENOIOCTLCMD;
+}
+
+static int iuu_reset(struct usb_serial_port *port, u_int8_t wt)
+{
+	int status;
+	u8 *buf;
+
+	status = iuu_uart_flush(port);
+	if (status != IUU_OPERATION_OK) {
+		dbg("%s - reset (flush) error ", __FUNCTION__);
+		return status;
+	}
+
+	buf = kmalloc(sizeof(u8) * 4, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = IUU_RST_SET;
+	buf[1] = IUU_DELAY_MS;
+	buf[2] = wt;		/* miliseconds */
+	buf[3] = IUU_RST_CLEAR;
+
+	status = bulk_immediate(port, buf, 4);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - reset error ", __FUNCTION__);
+	else
+		dbg("%s - reset OK ", __FUNCTION__);
+	kfree(buf);
+	return status;
+}
+
+static int
+bulk_immediate(struct usb_serial_port *port, u8 *buf,
+	       u8 count)
+{
+	int status;
+	struct usb_serial *serial = port->serial;
+	int actual = 0;
+
+	/* send the data out the bulk port */
+
+	status =
+	    usb_bulk_msg(serial->dev,
+			 usb_sndbulkpipe(serial->dev,
+					 port->bulk_out_endpointAddress), buf,
+			 count, &actual, HZ * 1);
+
+	if (status != IUU_OPERATION_OK) {
+		dbg("%s - error = %2x", __FUNCTION__, status);
+	} else {
+		dbg("%s - write OK !", __FUNCTION__);
+	}
+	return status;
+}
+
+static int
+read_immediate(struct usb_serial_port *port, u8 *buf,
+	       u8 count)
+{
+	int status;
+	struct usb_serial *serial = port->serial;
+	int actual = 0;
+
+	/* send the data out the bulk port */
+
+	status =
+	    usb_bulk_msg(serial->dev,
+			 usb_rcvbulkpipe(serial->dev,
+					 port->bulk_in_endpointAddress), buf,
+			 count, &actual, HZ * 1);
+
+	if (status != IUU_OPERATION_OK) {
+		dbg("%s - error = %2x", __FUNCTION__, status);
+	} else {
+		dbg("%s - read OK !", __FUNCTION__);
+	}
+
+	return status;
+}
+
+int
+iuu_led(struct usb_serial_port *port, unsigned int R, unsigned int G,
+	unsigned int B, u8 f)
+{
+	int status;
+	u8 *buf;
+	buf = kmalloc(sizeof(u_int8_t) * 8, GFP_KERNEL);
+
+	buf[0] = IUU_SET_LED;
+	buf[1] = R & 0xFF;
+	buf[2] = (R >> 8) & 0xFF;
+	buf[3] = G & 0xFF;
+	buf[4] = (G >> 8) & 0xFF;
+	buf[5] = B & 0xFF;
+	buf[6] = (B >> 8) & 0xFF;
+	buf[7] = f;
+	status = bulk_immediate(port, buf, 8);
+	kfree(buf);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - led error status = %2x", __FUNCTION__, status);
+	else
+		dbg("%s - led OK !", __FUNCTION__);
+	return IUU_OPERATION_OK;
+}
+
+static int
+iuu_uart_write(struct usb_serial_port *port, const u8 *buf,
+	       int count)
+{
+	int status;
+	int curpos = 0;
+	int i;
+	int security = 0;
+
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+	struct tty_struct *tty;
+	struct iuu_buffers *tmp;
+
+	if (count == 0)
+		return (0);
+
+	/* prevent buffer overflow */
+	if (count > 256)
+		return -ENOMEM;
+
+	if (priv->write_busy)
+		return -1;
+
+	tmp = kmalloc(sizeof(struct iuu_buffers), GFP_KERNEL);
+	if (!tmp)
+		return -EIO;
+
+	priv->write_busy = 1;
+	dbg("%s - try to write %i chars : %02X ", __FUNCTION__, count, buf[0]);
+	tmp->buf[0] = IUU_UART_ESC;
+	tmp->buf[1] = IUU_UART_TX;
+	tmp->buf[2] = count;
+	memcpy(&tmp->buf[3], buf, count);
+	dbg("%s - try to write %i chars ", __FUNCTION__, tmp->buf[2]);
+	status = bulk_immediate(port, tmp->buf, count + 3);
+	if (status != IUU_OPERATION_OK) {
+		dbg("%s - uart_write error", __FUNCTION__);
+		kfree(tmp);
+		priv->write_busy = 0;
+		return status;
+	}
+
+	tty = port->tty;
+
+	while (security < 200) {
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1 + 1 * HZ / 1000);
+		if (iuu_uart_read(port, tmp->buf, &tmp->len) < 0) {
+			kfree(tmp);
+			dbg("%s - read error", __FUNCTION__);
+			return -EIO;
+		}
+		dbg("%s - port = %i , round = %i" , __FUNCTION__,
+			port->number, security );
+		if (tmp->len > 0) {
+			/* test buffer overflow */
+			if (curpos + tmp->len > 256) {
+				kfree(tmp);
+				return -1;
+			}
+			memcpy(&tmp->finalbuf[curpos], tmp->buf, tmp->len);
+			curpos += tmp->len;
+			for (i = 0; i < tmp->len; ++i) {
+				if (tty_buffer_request_room(tty, 1) == 0)
+					break;
+				tty_insert_flip_char(tty, tmp->buf[i],
+						     TTY_NORMAL);
+				sprintf(tmp->dbgbuf +
+					((curpos - tmp->len + i) * 2), "%02X",
+					tmp->buf[i]);
+			}
+			tmp->dbgbuf[curpos * 2] = '\0';
+			dbg("%s - read reader %i round %i = %s", __FUNCTION__,
+				port->number, security, tmp->dbgbuf);
+			tty_schedule_flip(tty);
+		}
+		if ( smart == 1 ) {
+
+			if ( (tmp->finalbuf[curpos - 2] == 0x90)
+				&& (curpos > count+1 )
+				&& (tmp->finalbuf[curpos - 1] == 0x00))
+					security = 200;
+			if (count == 5
+				&& curpos == count + 1
+				&& buf[2] != 0
+				&& buf[3] != 0 )
+					security = 200;
+			if ((tmp->finalbuf[curpos - 2] == 0x90)
+				&& (curpos == count + 2 )
+				&& (count > 5 ))
+					security = 200;
+		}
+		security++;
+	}
+	if ( security == 200 )
+		iuu_uart_flush(port);
+	kfree(tmp);
+	priv->write_busy = 0;
+	return count;
+
+}
+
+static int iuu_uart_read_and_store(struct usb_serial_port *port)
+{
+	struct iuu_buffers *tmp;
+	struct tty_struct *tty;
+	int i;
+
+	tmp = kmalloc(sizeof(struct iuu_buffers), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	if (iuu_uart_read(port, tmp->buf, &tmp->len) < 0) {
+		kfree(tmp);
+		return -EIO;
+	}
+
+	tty = port->tty;
+	for (i = 0; i < tmp->len; ++i) {
+		if (tty_buffer_request_room(tty, 1) == 0)
+			break;
+		tty_insert_flip_char(tty, tmp->buf[i], TTY_NORMAL);
+		sprintf(&tmp->dbgbuf[i * 2], "%02X", tmp->buf[i]);
+	}
+	tmp->dbgbuf[i * 2] = '\0';
+	dbg("%s - read = %s", __FUNCTION__, tmp->dbgbuf);
+	tty_flip_buffer_push(tty);
+	kfree(tmp);
+	return i;
+}
+
+static int iuu_clk(struct usb_serial_port *port, int dwFrq)
+{
+	int status;
+	struct iuu_buffers *tmp;
+	int Count = 0;
+	u8 FrqGenAdr = 0x69;
+	u8 DIV = 0;	/* 8bit */
+	u8 XDRV = 0;	/* 8bit */
+	u8 PUMP = 0;	/* 3bit */
+	u8 PBmsb = 0;	/* 2bit */
+	u8 PBlsb = 0;	/* 8bit */
+	u8 PO = 0;	/* 1bit */
+	u8 Q = 0;	/* 7bit */
+	/* 24bit = 3bytes */
+	unsigned int P = 0;
+	unsigned int P2 = 0;
+	int frq = (int)dwFrq;
+
+	tmp = kmalloc(sizeof(struct iuu_buffers), GFP_KERNEL);
+
+	if (!tmp)
+		return -ENOMEM;
+
+	if (frq == 0) {
+		tmp->buf[Count++] = IUU_UART_WRITE_I2C;
+		tmp->buf[Count++] = FrqGenAdr << 1;
+		tmp->buf[Count++] = 0x09;
+		tmp->buf[Count++] = 0x00;
+
+		status = bulk_immediate(port, (u_int8_t *) tmp->buf, Count);
+		if (status != 0) {
+			dbg("%s - write error ", __FUNCTION__);
+			kfree(tmp);
+			return status;
+		}
+	} else if (frq == 3579000) {
+		DIV = 100;
+		P = 1193;
+		Q = 40;
+		XDRV = 0;
+	} else if (frq == 3680000) {
+		DIV = 105;
+		P = 161;
+		Q = 5;
+		XDRV = 0;
+	} else if (frq == 6000000) {
+		DIV = 66;
+		P = 66;
+		Q = 2;
+		XDRV = 0x28;
+	} else {
+		unsigned int result = 0;
+		unsigned int tmp = 0;
+		unsigned int check;
+		unsigned int check2;
+		char found = 0x00;
+
+		unsigned int lQ = 2;
+		unsigned int lP = 2055;
+		unsigned int lDiv = 4;
+		for (lQ = 2; lQ <= 47 && !found; lQ++)
+			for (lP = 2055; lP >= 8 && !found; lP--)
+				for (lDiv = 4; lDiv <= 127 && !found; lDiv++) {
+					tmp = (12000000 / lDiv) * (lP / lQ);
+					if (abs((int)(tmp - frq)) <
+					    abs((int)(frq - result))) {
+						check2 = (12000000 / lQ);
+						if (check2 < 250000)
+							continue;
+						check = (12000000 / lQ) * lP;
+						if (check > 400000000)
+							continue;
+						if (check < 100000000)
+							continue;
+						if (lDiv < 4 || lDiv > 127)
+							continue;
+						result = tmp;
+						P = lP;
+						DIV = lDiv;
+						Q = lQ;
+						if (result == frq)
+							found = 0x01;
+					}
+				}
+	}
+	P2 = ((P - PO) / 2) - 4;
+	DIV = DIV;
+	PUMP = 0x04;
+	PBmsb = (P2 >> 8 & 0x03);
+	PBlsb = P2 & 0xFF;
+	PO = (P >> 10) & 0x01;
+	Q = Q - 2;
+
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/* 0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x09;
+	tmp->buf[Count++] = 0x20;	/* Adr = 0x09 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/* 0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x0C;
+	tmp->buf[Count++] = DIV;	/* Adr = 0x0C */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/* 0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x12;
+	tmp->buf[Count++] = XDRV;	/* Adr = 0x12 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x13;
+	tmp->buf[Count++] = 0x6B;	/* Adr = 0x13 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x40;
+	tmp->buf[Count++] = (0xC0 | ((PUMP & 0x07) << 2))
+				| (PBmsb & 0x03); /* Adr = 0x40 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x41;
+	tmp->buf[Count++] = PBlsb;	/* Adr = 0x41 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x42;
+	tmp->buf[Count++] = Q | (((PO & 0x01) << 7));	/* Adr = 0x42 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x44;
+	tmp->buf[Count++] = (char)0xFF;	/* Adr = 0x44 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x45;
+	tmp->buf[Count++] = (char)0xFE;	/* Adr = 0x45 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x46;
+	tmp->buf[Count++] = 0x7F;	/* Adr = 0x46 */
+	tmp->buf[Count++] = IUU_UART_WRITE_I2C;	/*  0x4C */
+	tmp->buf[Count++] = FrqGenAdr << 1;
+	tmp->buf[Count++] = 0x47;
+	tmp->buf[Count++] = (char)0x84;	/* Adr = 0x47 */
+
+	status = bulk_immediate(port, (u8 *)tmp->buf,
+							Count);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - write error ", __FUNCTION__);
+
+	kfree(tmp);
+	return status;
+}
+
+static int iuu_uart_flush(struct usb_serial_port *port)
+{
+	int i;
+	int status;
+	u8 rxcmd = IUU_UART_RX;
+	struct iuu_buffers *tmp;
+
+	tmp = kmalloc(sizeof(struct iuu_buffers), GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	/* u_int8_t datain[256]; */
+
+	if (iuu_led(port, 0xF000, 0, 0, 0xFF) < 0) {
+		kfree(tmp);
+		return -EIO;
+	}
+
+	for (i = 0; i < 2; i++) {
+		status = bulk_immediate(port, &rxcmd, 1);
+		if (status != IUU_OPERATION_OK) {
+			dbg("%s - uart_flush_write error", __FUNCTION__);
+			return status;
+		}
+
+		status = read_immediate(port, &tmp->len, 1);
+		if (status != IUU_OPERATION_OK) {
+			dbg("%s - uart_flush_read error", __FUNCTION__);
+			kfree(tmp);
+			return status;
+		}
+
+		if (tmp->len > 0) {
+			dbg("%s - uart_flush datalen is : %i ", __FUNCTION__,
+			    tmp->len);
+			status = read_immediate(port, tmp->buf, tmp->len);
+			if (status != IUU_OPERATION_OK) {
+				dbg("%s - uart_flush_read error", __FUNCTION__);
+				kfree(tmp);
+				return status;
+			}
+		}
+	}
+	dbg("%s - uart_flush_read OK!", __FUNCTION__);
+	iuu_led(port, 0, 0xF000, 0, 0xFF);
+	kfree(tmp);
+	return status;
+}
+
+static int
+iuu_uart_read(struct usb_serial_port *port, u8 *data,
+	      u8 *len)
+{
+	int status;
+	u8 rxcmd = IUU_UART_RX;
+	status = bulk_immediate(port, &rxcmd, 1);
+	if (status != IUU_OPERATION_OK) {
+		dbg("%s - uart_flush_write error", __FUNCTION__);
+		len = 0;
+		return status;
+	}
+
+	status = read_immediate(port, len, 1);
+	if (status != IUU_OPERATION_OK) {
+		dbg("%s - uart_flush_read error", __FUNCTION__);
+		len[0] = 0;
+		return status;
+	}
+
+	if (len[0] > 0) {
+		dbg("%s - uart_read datalen is : %i ", __FUNCTION__, len[0]);
+		status = read_immediate(port, data, len[0]);
+		if (status != IUU_OPERATION_OK) {
+			dbg("%s - uart_flush_read error", __FUNCTION__);
+			len[0] = 0;
+			return status;
+		}
+	}
+	dbg("%s - uart_read OK!", __FUNCTION__);
+	return len[0];
+}
+
+static int iuu_uart_on(struct usb_serial_port *port)
+{
+	int status;
+	u8 *buf;
+
+	buf = kmalloc(sizeof(u8) * 4, GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = IUU_UART_ENABLE;
+	buf[1] = (u8)((IUU_BAUD_9600 >> 8) & 0x00FF);
+	buf[2] = (u8)(0x00FF & IUU_BAUD_9600);
+	buf[3] =
+	    (u8)(0x0F0 & IUU_TWO_STOP_BITS) | (0x07 &
+						 IUU_PARITY_EVEN);
+
+	status = bulk_immediate(port, buf, 4);
+	if (status != IUU_OPERATION_OK) {
+		dbg("%s - uart_on error", __FUNCTION__);
+		kfree(buf);
+		return status;
+	}
+	/*  iuu_reset() the card after iuu_uart_on() */
+	status = iuu_uart_flush(port);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - uart_flush error", __FUNCTION__);
+
+	kfree(buf);
+	return status;
+}
+
+/*  Diables the IUU UART (a.k.a. the Phoenix voiderface) */
+static int iuu_uart_off(struct usb_serial_port *port)
+{
+	int status;
+	u8 buf = IUU_UART_DISABLE;
+
+	status = bulk_immediate(port, &buf, 1);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - uart_off error", __FUNCTION__);
+
+	return status;
+}
+
+/*
+static int
+iuu_uart_set(struct usb_serial_port *port, u8 br,
+	     u8 parity, u8 sbits)
+{
+	int status;
+	u8 *buf;
+
+	buf = kmalloc(sizeof(u8) * 5, GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+
+	buf[0] = IUU_UART_ESC;
+	buf[1] = IUU_UART_CHANGE;
+	buf[2] = (u_int8_t) ((br >> 8) & 0x00FF);
+	buf[3] = (u_int8_t) (0x00FF & br);
+	buf[4] = (u_int8_t) (parity | sbits);
+
+	status = bulk_immediate(port, buf, 5);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - uart_off error", __FUNCTION__);
+
+	kfree(buf);
+	return status;
+}
+*/
+
+static int
+iuu_uart_baud(struct usb_serial_port *port, u_int32_t baud, u_int32_t *actual,
+	      u_int8_t parity)
+{
+
+	/* SDK_STATUS sdk_status = SDK_SUCCESS; */
+	/* u8 dataout[10]; */
+	/* DWORD dwWritten = 0; */
+	/* unsigned void DataCount = 0; */
+
+	int status;
+	u8 *dataout;
+	u_int8_t DataCount = 0;
+	u8 T1Frekvens = 0;
+	u8 T1reload = 0;
+	unsigned int T1FrekvensHZ = 0;
+
+	dataout = kmalloc(sizeof(u8) * 5, GFP_KERNEL);
+
+	if (!dataout)
+		return -ENOMEM;
+
+	if (baud < 1200 || baud > 230400)
+		return IUU_INVALID_PARAMETER;
+
+	if (baud > 977) {
+		T1Frekvens = 3;
+		T1FrekvensHZ = 500000;
+	}
+
+	if (baud > 3906) {
+		T1Frekvens = 2;
+		T1FrekvensHZ = 2000000;
+	}
+
+	if (baud > 11718) {
+		T1Frekvens = 1;
+		T1FrekvensHZ = 6000000;
+	}
+
+	if (baud > 46875) {
+		T1Frekvens = 0;
+		T1FrekvensHZ = 24000000;
+	}
+
+	T1reload = 256 - (u8)(T1FrekvensHZ / (baud * 2));
+
+	/*  magic number here:  ENTER_FIRMWARE_UPDATE; */
+	dataout[DataCount++] = IUU_UART_ESC;
+	/*  magic number here:  CHANGE_BAUD; */
+	dataout[DataCount++] = IUU_UART_CHANGE;
+	dataout[DataCount++] = T1Frekvens;
+	dataout[DataCount++] = T1reload;
+
+	*actual = (T1FrekvensHZ / (256 - T1reload)) / 2;
+
+	switch (parity & 0x0F) {
+	case IUU_PARITY_NONE:
+		dataout[DataCount++] = 0x00;
+		break;
+	case IUU_PARITY_EVEN:
+		dataout[DataCount++] = 0x01;
+		break;
+	case IUU_PARITY_ODD:
+		dataout[DataCount++] = 0x02;
+		break;
+	case IUU_PARITY_MARK:
+		dataout[DataCount++] = 0x03;
+		break;
+	case IUU_PARITY_SPACE:
+		dataout[DataCount++] = 0x04;
+		break;
+	default:
+		return IUU_INVALID_PARAMETER;
+		break;
+	}
+
+	switch (parity & 0xF0) {
+	case IUU_ONE_STOP_BIT:
+		dataout[DataCount - 1] |= IUU_ONE_STOP_BIT;
+		break;
+
+	case IUU_TWO_STOP_BITS:
+		dataout[DataCount - 1] |= IUU_TWO_STOP_BITS;
+		break;
+	default:
+		return IUU_INVALID_PARAMETER;
+		break;
+	}
+
+	status = bulk_immediate(port, dataout, DataCount);
+	if (status != IUU_OPERATION_OK)
+		dbg("%s - uart_off error", __FUNCTION__);
+
+	return status;
+}
+
+static int iuu_open(struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	u8 *buf;
+	int result;
+	u_int32_t actual;
+	unsigned long flags;
+	struct iuu_private *priv = usb_get_serial_port_data(port);
+
+	dbg("%s -  port %d", __FUNCTION__, port->number);
+	usb_clear_halt(serial->dev, port->write_urb->pipe);
+	usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+	buf = kmalloc(10, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+/* set the termios structure */
+
+	spin_lock_irqsave(&priv->lock, flags);
+	/* if (!priv->termios_initialized) { */
+	*(port->tty->termios) = tty_std_termios;
+	port->tty->termios->c_cflag =
+	    CLOCAL | CREAD | CS8 | B9600 | TIOCM_CTS | CSTOPB | PARENB;
+	port->tty->termios->c_lflag = 0;
+	port->tty->termios->c_oflag = ONLCR;
+	port->tty->termios->c_iflag = 0;
+	priv->termios_initialized = 1;
+	port->tty->low_latency = 1;
+	/* } */
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+#define FISH(a, b, c, d) do { \
+	result = usb_control_msg(port->serial->dev,	\
+				usb_rcvctrlpipe(port->serial->dev, 0),	\
+				b, a, c, d, buf, 1, 1000); \
+	dbg("0x%x:0x%x:0x%x:0x%x  %d - %x", a, b, c, d, result,
+				buf[0]); } while (0);
+
+#define SOUP(a, b, c, d)  do { \
+	result = usb_control_msg(port->serial->dev,	\
+				usb_sndctrlpipe(port->serial->dev, 0),	\
+				b, a, c, d, NULL, 0, 1000); \
+	dbg("0x%x:0x%x:0x%x:0x%x  %d", a, b, c, d, result); } while (0)
+
+	/*  This is not UART related but IUU USB driver related or something */
+	/*  like that. Basically no IUU will accept any commands from the USB */
+	/*  host unless it has received the following message */
+	/* sprintf(buf ,"%c%c%c%c",0x03,0x02,0x02,0x0); */
+
+	SOUP(0x03, 0x02, 0x02, 0x0);
+	iuu_led(port, 0xF000, 0xF000, 0, 0xFF);
+	iuu_clk(port, IUU_CLK_6000000);
+	/* iuu_clk(serial->port, IUU_CLK_3579000); */
+	iuu_uart_on(port);
+	switch (clockmode) {
+	case 2:		/*  3.680 Mhz */
+		iuu_clk(port, IUU_CLK_3680000 * multiplicator / 100);
+		result =
+		    iuu_uart_baud(port, 9600 * multiplicator / 100, &actual,
+				  IUU_PARITY_EVEN);
+		break;
+	case 3:		/*  6.00 Mhz */
+		iuu_clk(port, IUU_CLK_6000000 * multiplicator / 100);
+		result =
+		    iuu_uart_baud(port, 16457 * multiplicator / 100, &actual,
+				  IUU_PARITY_EVEN);
+		break;
+	default:		/*  3.579 Mhz */
+		iuu_clk(port, IUU_CLK_3579000 * multiplicator / 100);
+		result =
+		    iuu_uart_baud(port, 9600 * multiplicator / 100, &actual,
+				  IUU_PARITY_EVEN);
+	}
+
+	iuu_uart_flush(port);
+
+	return 0;
+}
+
+static int set_control_lines(struct usb_device *dev, u8 value)
+{
+	return 0;
+}
+
+static void iuu_close(struct usb_serial_port *port, struct file *filp)
+{
+	/* iuu_led (port,255,0,0,0); */
+	struct usb_serial *serial;
+	struct iuu_private *priv;
+	unsigned long flags;
+	unsigned int c_cflag;
+	int result;
+
+	serial = port->serial;
+	if (!serial)
+		return;
+	iuu_led(port, 0, 0, 0xF000, 0xFF);
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	iuu_uart_off(port);
+	if (serial->dev) {
+		if (port->tty) {
+			c_cflag = port->tty->termios->c_cflag;
+			if (c_cflag & HUPCL) {
+				/* drop DTR and RTS */
+				priv = usb_get_serial_port_data(port);
+				spin_lock_irqsave(&priv->lock, flags);
+				priv->line_control = 0;
+				spin_unlock_irqrestore(&priv->lock, flags);
+				set_control_lines(port->serial->dev, 0);
+			}
+		}
+
+		/* shutdown our urbs */
+		dbg("%s - shutting down urbs", __FUNCTION__);
+		result = usb_unlink_urb(port->write_urb);
+		if (result)
+			dbg("%s - usb_unlink_urb (write_urb)"
+			    " failed with reason: %d", __FUNCTION__, result);
+
+		result = usb_unlink_urb(port->read_urb);
+		if (result)
+			dbg("%s - usb_unlink_urb (read_urb) "
+			    "failed with reason: %d", __FUNCTION__, result);
+
+		result = usb_unlink_urb(port->interrupt_in_urb);
+		if (result)
+			dbg("%s - usb_unlink_urb (voiderrupt_in_urb)"
+			    " failed with reason: %d", __FUNCTION__, result);
+	}
+}
+
+static int __init iuu_init(void)
+{
+	int retval;
+	retval = usb_serial_register(&iuu_device);
+	if (retval)
+		goto failed_usb_serial_register;
+	retval = usb_register(&iuu_driver);
+	if (retval)
+		goto failed_usb_register;
+	info(DRIVER_DESC " " DRIVER_VERSION);
+	return 0;
+failed_usb_register:
+	usb_serial_deregister(&iuu_device);
+failed_usb_serial_register:
+	return retval;
+}
+
+static void __exit iuu_exit(void)
+{
+	usb_deregister(&iuu_driver);
+	usb_serial_deregister(&iuu_device);
+}
+
+module_init(iuu_init);
+module_exit(iuu_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+MODULE_VERSION(DRIVER_VERSION);
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+module_param(smart, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(smart, "stop processing if card answer is 90 00  !");
+
+module_param(multiplicator, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(multiplicator, "overclock multiplicator percent 100 to 500");
+
+module_param(clockmode, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(clockmode, "1=3Mhz579,2=3Mhz680,3=6Mhz");
diff --git a/drivers/usb/serial/iuu_phoenix.h b/drivers/usb/serial/iuu_phoenix.h
new file mode 100644
index 0000000..3c9a766
--- /dev/null
+++ b/drivers/usb/serial/iuu_phoenix.h
@@ -0,0 +1,122 @@
+/*
+ * Infinity Unlimited USB Phoenix driver
+ *
+ * Copyright (C) 2007 Alain Degreffe (eczema@ecze.com)
+ *
+ *
+ * Original code taken from iuutool ( Copyright (C) 2006 Juan Carlos Borrás )
+ *
+ *	This program is free software; you can redistribute it and/or modify
+ *	it under the terms of the GNU General Public License as published by
+ *	the Free Software Foundation; either version 2 of the License, or
+ *	(at your option) any later version.
+ *
+ *  And tested with help of WB Electronics
+ *
+ */
+
+#define   IUU_USB_VENDOR_ID  0x104f
+#define   IUU_USB_PRODUCT_ID  0x0004
+#define   IUU_USB_OP_TIMEOUT  0x0200
+
+/* Programmer commands */
+
+#define IUU_NO_OPERATION   0x00
+#define IUU_GET_FIRMWARE_VERSION   0x01
+#define IUU_GET_PRODUCT_NAME   0x02
+#define IUU_GET_STATE_REGISTER   0x03
+#define IUU_SET_LED   0x04
+#define IUU_WAIT_MUS   0x05
+#define IUU_WAIT_MS   0x06
+#define IUU_GET_LOADER_VERSION   0x50
+#define IUU_RST_SET   0x52
+#define IUU_RST_CLEAR   0x53
+#define IUU_SET_VCC   0x59
+#define IUU_UART_ENABLE   0x49
+#define IUU_UART_DISABLE   0x4A
+#define IUU_UART_WRITE_I2C   0x4C
+#define IUU_UART_ESC   0x5E
+#define IUU_UART_TRAP   0x54
+#define IUU_UART_TRAP_BREAK   0x5B
+#define IUU_UART_RX   0x56
+#define IUU_AVR_ON   0x21
+#define IUU_AVR_OFF   0x22
+#define IUU_AVR_1CLK   0x23
+#define IUU_AVR_RESET   0x24
+#define IUU_AVR_RESET_PC   0x25
+#define IUU_AVR_INC_PC   0x26
+#define IUU_AVR_INCN_PC   0x27
+#define IUU_AVR_PREAD   0x29
+#define IUU_AVR_PREADN   0x2A
+#define IUU_AVR_PWRITE   0x28
+#define IUU_AVR_DREAD   0x2C
+#define IUU_AVR_DREADN   0x2D
+#define IUU_AVR_DWRITE   0x2B
+#define IUU_AVR_PWRITEN   0x2E
+#define IUU_EEPROM_ON   0x37
+#define IUU_EEPROM_OFF   0x38
+#define IUU_EEPROM_WRITE   0x39
+#define IUU_EEPROM_WRITEX   0x3A
+#define IUU_EEPROM_WRITE8   0x3B
+#define IUU_EEPROM_WRITE16   0x3C
+#define IUU_EEPROM_WRITEX32   0x3D
+#define IUU_EEPROM_WRITEX64   0x3E
+#define IUU_EEPROM_READ   0x3F
+#define IUU_EEPROM_READX   0x40
+#define IUU_EEPROM_BREAD   0x41
+#define IUU_EEPROM_BREADX   0x42
+#define IUU_PIC_CMD   0x0A
+#define IUU_PIC_CMD_LOAD   0x0B
+#define IUU_PIC_CMD_READ   0x0C
+#define IUU_PIC_ON   0x0D
+#define IUU_PIC_OFF   0x0E
+#define IUU_PIC_RESET   0x16
+#define IUU_PIC_INC_PC   0x0F
+#define IUU_PIC_INCN_PC   0x10
+#define IUU_PIC_PWRITE   0x11
+#define IUU_PIC_PREAD   0x12
+#define IUU_PIC_PREADN   0x13
+#define IUU_PIC_DWRITE   0x14
+#define IUU_PIC_DREAD   0x15
+#define IUU_UART_NOP   0x00
+#define IUU_UART_CHANGE   0x02
+#define IUU_UART_TX   0x04
+#define IUU_DELAY_MS   0x06
+
+#define IUU_OPERATION_OK   0x00
+#define IUU_DEVICE_NOT_FOUND   0x01
+#define IUU_INVALID_HANDLE   0x02
+#define IUU_INVALID_PARAMETER   0x03
+#define IUU_INVALID_voidERFACE   0x04
+#define IUU_INVALID_REQUEST_LENGTH   0x05
+#define IUU_UART_NOT_ENABLED   0x06
+#define IUU_WRITE_ERROR   0x07
+#define IUU_READ_ERROR   0x08
+#define IUU_TX_ERROR   0x09
+#define IUU_RX_ERROR   0x0A
+
+#define IUU_PARITY_NONE   0x00
+#define IUU_PARITY_EVEN   0x01
+#define IUU_PARITY_ODD   0x02
+#define IUU_PARITY_MARK   0x03
+#define IUU_PARITY_SPACE   0x04
+#define IUU_SC_INSERTED   0x01
+#define IUU_VERIFY_ERROR   0x02
+#define IUU_SIM_INSERTED   0x04
+#define IUU_TWO_STOP_BITS   0x00
+#define IUU_ONE_STOP_BIT   0x20
+#define IUU_BAUD_2400   0x0398
+#define IUU_BAUD_9600   0x0298
+#define IUU_BAUD_19200   0x0164
+#define IUU_BAUD_28800   0x0198
+#define IUU_BAUD_38400   0x01B2
+#define IUU_BAUD_57600   0x0030
+#define IUU_BAUD_115200   0x0098
+#define IUU_CLK_3579000   3579000
+#define IUU_CLK_3680000   3680000
+#define IUU_CLK_6000000   6000000
+#define IUU_FULLCARD_IN   0x01
+#define IUU_DEV_ERROR   0x02
+#define IUU_MINICARD_IN   0x04
+#define IUU_VCC_5V   0x00
+#define IUU_VCC_3V   0x01
