Commit 60a8fc01 authored by Alain Degreffe's avatar Alain Degreffe Committed by Greg Kroah-Hartman

USB: add iuu_phoenix driver

Signed-off-by: default avatarAlain Degreffe <eczema@ecze.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Oliver Neukum <oliver@neukum.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent c74e8095
Infinity Usb Unlimited Readme
-----------------------------
Hi all,
This module provide a serial interface to use your
IUU unit in phoenix mode. Loading this module will
bring a ttyUSB[0-x] interface. This driver must be
used by your favorite application to pilot the IUU
This driver is still in beta stage, so bugs can
occur and your system may freeze. As far I now,
I never had any problem with it, but I'm not a real
guru, so don't blame me if your system is unstable
You can plug more than one IUU. Every unit will
have his own device file(/dev/ttyUSB0,/dev/ttyUSB1,...)
How to tune the reader speed ?
A few parameters can be used at load time
To use parameters, just unload the module if it is
already loaded and use modprobe iuu_phoenix param=value.
In case of prebuilt module, use the command
insmod iuu_phoenix param=value.
Example:
modprobe iuu_phoenix clockmode=3
The parameters are:
parm: clockmode:1=3Mhz579,2=3Mhz680,3=6Mhz (int)
parm: boost:overclock boost percent 100 to 500 (int)
parm: cdmode:Card detect mode 0=none, 1=CD, 2=!CD, 3=DSR, 4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING (int)
parm: xmas:xmas color enabled or not (bool)
parm: debug:Debug enabled or not (bool)
- clockmode will provide 3 different base settings commonly adopted by
different software:
1. 3Mhz579
2. 3Mhz680
3. 6Mhz
- boost provide a way to overclock the reader ( my favorite :-) )
For example to have best performance than a simple clockmode=3, try this:
modprobe boost=195
This will put the reader in a base of 3Mhz579 but boosted a 195 % !
the real clock will be now : 6979050 Hz ( 6Mhz979 ) and will increase
the speed to a score 10 to 20% better than the simple clockmode=3 !!!
- cdmode permit to setup the signal used to inform the userland ( ioctl answer )
if the card is present or not. Eight signals are possible.
- xmas is completely useless except for your eyes. This is one of my friend who was
so sad to have a nice device like the iuu without seeing all color range available.
So I have added this option to permit him to see a lot of color ( each activity change the color
and the frequency randomly )
- debug will produce a lot of debugging messages...
Last notes:
Don't worry about the serial settings, the serial emulation
is an abstraction, so use any speed or parity setting will
work. ( This will not change anything ).Later I will perhaps
use this settings to deduce de boost but is that feature
really necessary ?
The autodetect feature used is the serial CD. If that doesn't
work for your software, disable detection mechanism in it.
Have fun !
Alain Degreffe
eczema(at)ecze.com
...@@ -282,6 +282,17 @@ config USB_SERIAL_IPW ...@@ -282,6 +282,17 @@ config USB_SERIAL_IPW
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called ipw. module will be called ipw.
config USB_SERIAL_IUU
tristate "USB Infinity USB Unlimited Phoenix Driver (Experimental)"
depends on USB_SERIAL && EXPERIMENTAL
help
Say Y here if you want to use a IUU in phoenix mode and get
an extra ttyUSBx device. More information available on
http://eczema.ecze.com/iuu_phoenix.html
To compile this driver as a module, choose M here: the
module will be called iuu_phoenix.o
config USB_SERIAL_KEYSPAN_PDA config USB_SERIAL_KEYSPAN_PDA
tristate "USB Keyspan PDA Single Port Serial Driver" tristate "USB Keyspan PDA Single Port Serial Driver"
depends on USB_SERIAL depends on USB_SERIAL
......
...@@ -30,6 +30,7 @@ obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o ...@@ -30,6 +30,7 @@ obj-$(CONFIG_USB_SERIAL_GARMIN) += garmin_gps.o
obj-$(CONFIG_USB_SERIAL_HP4X) += hp4x.o obj-$(CONFIG_USB_SERIAL_HP4X) += hp4x.o
obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o obj-$(CONFIG_USB_SERIAL_IPAQ) += ipaq.o
obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o obj-$(CONFIG_USB_SERIAL_IPW) += ipw.o
obj-$(CONFIG_USB_SERIAL_IUU) += iuu_phoenix.o
obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o obj-$(CONFIG_USB_SERIAL_IR) += ir-usb.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o obj-$(CONFIG_USB_SERIAL_KEYSPAN) += keyspan.o
obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
......
/*
* 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"
#include <linux/random.h>
#ifdef CONFIG_USB_SERIAL_DEBUG
static int debug = 1;
#else
static int debug;
#endif
/*
* Version Information
*/
#define DRIVER_VERSION "v0.5"
#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,
};
/* turbo parameter */
static int boost = 100;
static int clockmode = 1;
static int cdmode = 1;
static int iuu_cardin;
static int iuu_cardout;
static int xmas;
static void read_rxcmd_callback(struct urb *urb);
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 tiostatus; /* store IUART SIGNAL for tiocmget call */
u8 reset; /* if 1 reset is needed */
int poll; /* number of poll */
u8 *writebuf; /* buffer for writing to device */
int writelen; /* num of byte to write to device */
u8 *buf; /* used for initialize speed */
u8 *dbgbuf; /* debug buffer */
u8 len;
};
static void iuu_free_buf(struct iuu_private *priv)
{
kfree(priv->buf);
kfree(priv->dbgbuf);
kfree(priv->writebuf);
}
static int iuu_alloc_buf(struct iuu_private *priv)
{
priv->buf = kzalloc(256, GFP_KERNEL);
priv->dbgbuf = kzalloc(256, GFP_KERNEL);
priv->writebuf = kzalloc(256, GFP_KERNEL);
if (!priv->buf || !priv->dbgbuf || !priv->writebuf) {
iuu_free_buf(priv);
dbg("%s problem allocation buffer", __FUNCTION__);
return -ENOMEM;
}
dbg("%s - Privates buffers allocation success", __FUNCTION__);
return 0;
}
static int iuu_startup(struct usb_serial *serial)
{
struct iuu_private *priv;
priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL);
dbg("%s- priv allocation success", __FUNCTION__);
if (!priv)
return -ENOMEM;
if (iuu_alloc_buf(priv)) {
kfree(priv);
return -ENOMEM;
}
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)
{
struct usb_serial_port *port = serial->port[0];
struct iuu_private *priv = usb_get_serial_port_data(port);
if (!port)
return;
dbg("%s", __FUNCTION__);
if (priv) {
iuu_free_buf(priv);
dbg("%s - I will free all", __FUNCTION__);
usb_set_serial_port_data(port, NULL);
dbg("%s - priv is not anymore in port structure", __FUNCTION__);
kfree(priv);
dbg("%s priv is now kfree", __FUNCTION__);
}
}
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 & TIOCM_RTS) && priv->tiostatus == TIOCM_RTS) {
dbg("%s TIOCMSET RESET called !!!", __FUNCTION__);
priv->reset = 1;
return 0;
}
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)
{
struct iuu_private *priv = usb_get_serial_port_data(port);
return priv->tiostatus;
}
static void iuu_rxcmd(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
int result;
dbg("%s - enter", __FUNCTION__);
if (urb->status) {
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
/* error stop all */
return;
}
memset(port->write_urb->transfer_buffer, IUU_UART_RX, 1);
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 1,
read_rxcmd_callback, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
static int iuu_reset(struct usb_serial_port *port, u8 wt)
{
struct iuu_private *priv = usb_get_serial_port_data(port);
int result;
char *buf_ptr = port->write_urb->transfer_buffer;
dbg("%s - enter", __FUNCTION__);
/* Prepare the reset sequence */
*buf_ptr++ = IUU_RST_SET;
*buf_ptr++ = IUU_DELAY_MS;
*buf_ptr++ = wt;
*buf_ptr = IUU_RST_CLEAR;
/* send the sequence */
usb_fill_bulk_urb(port->write_urb,
port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 4, iuu_rxcmd, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
priv->reset = 0;
return result;
}
/* Status Function
* Return value is
* 0x00 = no card
* 0x01 = smartcard
* 0x02 = sim card
*/
static void iuu_update_status_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct iuu_private *priv = usb_get_serial_port_data(port);
u8 *st;
dbg("%s - enter", __FUNCTION__);
if (urb->status) {
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
/* error stop all */
return;
}
st = urb->transfer_buffer;
dbg("%s - enter", __FUNCTION__);
if (urb->actual_length == 1) {
switch (st[0]) {
case 0x1:
priv->tiostatus = iuu_cardout;
break;
case 0x0:
priv->tiostatus = iuu_cardin;
break;
default:
priv->tiostatus = iuu_cardin;
}
}
iuu_rxcmd(urb);
}
static void iuu_status_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
int result;
dbg("%s - enter", __FUNCTION__);
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
usb_rcvbulkpipe(port->serial->dev,
port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, 256,
iuu_update_status_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
}
static int iuu_status(struct usb_serial_port *port)
{
int result;
dbg("%s - enter", __FUNCTION__);
memset(port->write_urb->transfer_buffer, IUU_GET_STATE_REGISTER, 1);
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 1,
iuu_status_callback, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
return result;
}
static int bulk_immediate(struct usb_serial_port *port, u8 *buf, u8 count)
{
int status;
struct usb_serial *serial = port->serial;
int actual = 0;
dbg("%s - enter", __FUNCTION__);
/* 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;
dbg("%s - enter", __FUNCTION__);
/* 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;
}
static 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(8, GFP_KERNEL);
if (!buf)
return -ENOMEM;
dbg("%s - enter", __FUNCTION__);
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 void iuu_rgbf_fill_buffer(u8 *buf, u8 r1, u8 r2, u8 g1, u8 g2, u8 b1,
u8 b2, u8 freq)
{
*buf++ = IUU_SET_LED;
*buf++ = r1;
*buf++ = r2;
*buf++ = g1;
*buf++ = g2;
*buf++ = b1;
*buf++ = b2;
*buf = freq;
}
static void iuu_led_activity_on(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
int result;
char *buf_ptr = port->write_urb->transfer_buffer;
*buf_ptr++ = IUU_SET_LED;
if (xmas == 1) {
get_random_bytes(buf_ptr, 6);
*(buf_ptr+7) = 1;
} else {
iuu_rgbf_fill_buffer(buf_ptr, 255, 255, 0, 0, 0, 0, 255);
}
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 8 ,
iuu_rxcmd, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
static void iuu_led_activity_off(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
int result;
char *buf_ptr = port->write_urb->transfer_buffer;
if (xmas == 1) {
iuu_rxcmd(urb);
return;
} else {
*buf_ptr++ = IUU_SET_LED;
iuu_rgbf_fill_buffer(buf_ptr, 0, 0, 255, 255, 0, 0, 255);
}
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 8 ,
iuu_rxcmd, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
}
static int iuu_clk(struct usb_serial_port *port, int dwFrq)
{
int status;
struct iuu_private *priv = usb_get_serial_port_data(port);
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;
dbg("%s - enter", __FUNCTION__);
if (frq == 0) {
priv->buf[Count++] = IUU_UART_WRITE_I2C;
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x09;
priv->buf[Count++] = 0x00;
status = bulk_immediate(port, (u8 *) priv->buf, Count);
if (status != 0) {
dbg("%s - write error ", __FUNCTION__);
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;
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x09;
priv->buf[Count++] = 0x20; /* Adr = 0x09 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x0C;
priv->buf[Count++] = DIV; /* Adr = 0x0C */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x12;
priv->buf[Count++] = XDRV; /* Adr = 0x12 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x13;
priv->buf[Count++] = 0x6B; /* Adr = 0x13 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x40;
priv->buf[Count++] = (0xC0 | ((PUMP & 0x07) << 2)) |
(PBmsb & 0x03); /* Adr = 0x40 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x41;
priv->buf[Count++] = PBlsb; /* Adr = 0x41 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x42;
priv->buf[Count++] = Q | (((PO & 0x01) << 7)); /* Adr = 0x42 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x44;
priv->buf[Count++] = (char)0xFF; /* Adr = 0x44 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x45;
priv->buf[Count++] = (char)0xFE; /* Adr = 0x45 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x46;
priv->buf[Count++] = 0x7F; /* Adr = 0x46 */
priv->buf[Count++] = IUU_UART_WRITE_I2C; /* 0x4C */
priv->buf[Count++] = FrqGenAdr << 1;
priv->buf[Count++] = 0x47;
priv->buf[Count++] = (char)0x84; /* Adr = 0x47 */
status = bulk_immediate(port, (u8 *) priv->buf, Count);
if (status != IUU_OPERATION_OK)
dbg("%s - write error ", __FUNCTION__);
return status;
}
static int iuu_uart_flush(struct usb_serial_port *port)
{
int i;
int status;
u8 rxcmd = IUU_UART_RX;
struct iuu_private *priv = usb_get_serial_port_data(port);
dbg("%s - enter", __FUNCTION__);
if (iuu_led(port, 0xF000, 0, 0, 0xFF) < 0)
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, &priv->len, 1);
if (status != IUU_OPERATION_OK) {
dbg("%s - uart_flush_read error", __FUNCTION__);
return status;
}
if (priv->len > 0) {
dbg("%s - uart_flush datalen is : %i ", __FUNCTION__,
priv->len);
status = read_immediate(port, priv->buf, priv->len);
if (status != IUU_OPERATION_OK) {
dbg("%s - uart_flush_read error", __FUNCTION__);
return status;
}
}
}
dbg("%s - uart_flush_read OK!", __FUNCTION__);
iuu_led(port, 0, 0xF000, 0, 0xFF);
return status;
}
static void read_buf_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
unsigned char *data = urb->transfer_buffer;
struct tty_struct *tty;
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
if (urb->status) {
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
if (urb->status == -EPROTO) {
/* reschedule needed */
}
return;
}
dbg("%s - %i chars to write", __FUNCTION__, urb->actual_length);
tty = port->tty;
if (data == NULL)
dbg("%s - data is NULL !!!", __FUNCTION__);
if (tty && urb->actual_length && data) {
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
iuu_led_activity_on(urb);
}
static int iuu_bulk_write(struct usb_serial_port *port)
{
struct iuu_private *priv = usb_get_serial_port_data(port);
unsigned int flags;
int result;
int i;
char *buf_ptr = port->write_urb->transfer_buffer;
dbg("%s - enter", __FUNCTION__);
*buf_ptr++ = IUU_UART_ESC;
*buf_ptr++ = IUU_UART_TX;
*buf_ptr++ = priv->writelen;
memcpy(buf_ptr, priv->writebuf,
priv->writelen);
if (debug == 1) {
for (i = 0; i < priv->writelen; i++)
sprintf(priv->dbgbuf + i*2 ,
"%02X", priv->writebuf[i]);
priv->dbgbuf[priv->writelen+i*2] = 0;
dbg("%s - writing %i chars : %s", __FUNCTION__,
priv->writelen, priv->dbgbuf);
}
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, priv->writelen + 3,
iuu_rxcmd, port);
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
spin_lock_irqsave(&priv->lock, flags);
priv->writelen = 0;
spin_unlock_irqrestore(&priv->lock, flags);
usb_serial_port_softint(port);
return result;
}
static int iuu_read_buf(struct usb_serial_port *port, int len)
{
int result;
dbg("%s - enter", __FUNCTION__);
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
usb_rcvbulkpipe(port->serial->dev,
port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, len,
read_buf_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
return result;
}
static void iuu_uart_read_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct iuu_private *priv = usb_get_serial_port_data(port);
unsigned int flags;
int status;
int error = 0;
int len = 0;
unsigned char *data = urb->transfer_buffer;
priv->poll++;
dbg("%s - enter", __FUNCTION__);
if (urb->status) {
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
/* error stop all */
return;
}
if (data == NULL)
dbg("%s - data is NULL !!!", __FUNCTION__);
if (urb->actual_length == 1 && data != NULL)
len = (int) data[0];
if (urb->actual_length > 1) {
dbg("%s - urb->actual_length = %i", __FUNCTION__,
urb->actual_length);
error = 1;
return;
}
/* if len > 0 call readbuf */
if (len > 0 && error == 0) {
dbg("%s - call read buf - len to read is %i ",
__FUNCTION__, len);
status = iuu_read_buf(port, len);
return;
}
/* need to update status ? */
if (priv->poll > 99) {
status = iuu_status(port);
priv->poll = 0;
return;
}
/* reset waiting ? */
if (priv->reset == 1) {
status = iuu_reset(port, 0xC);
return;
}
/* Writebuf is waiting */
spin_lock_irqsave(&priv->lock, flags);
if (priv->writelen > 0) {
spin_unlock_irqrestore(&priv->lock, flags);
status = iuu_bulk_write(port);
return;
}
spin_unlock_irqrestore(&priv->lock, flags);
/* if nothing to write call again rxcmd */
dbg("%s - rxcmd recall", __FUNCTION__);
iuu_led_activity_off(urb);
return;
}
static int iuu_uart_write(struct usb_serial_port *port, const u8 *buf,
int count)
{
struct iuu_private *priv = usb_get_serial_port_data(port);
unsigned int flags;
dbg("%s - enter", __FUNCTION__);
if (count > 256)
return -ENOMEM;
spin_lock_irqsave(&priv->lock, flags);
if (priv->writelen > 0) {
/* buffer already filled but not commited */
spin_unlock_irqrestore(&priv->lock, flags);
return (0);
}
/* fill the buffer */
memcpy(priv->writebuf, buf, count);
priv->writelen = count;
spin_unlock_irqrestore(&priv->lock, flags);
return (count);
}
static void read_rxcmd_callback(struct urb *urb)
{
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
int result;
dbg("%s - enter", __FUNCTION__);
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
if (urb->status) {
dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
/* error stop all */
return;
}
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
usb_rcvbulkpipe(port->serial->dev,
port->bulk_in_endpointAddress),
port->read_urb->transfer_buffer, 256,
iuu_uart_read_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
dbg("%s - submit result = %d", __FUNCTION__, result);
return;
}
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__);
goto uart_enable_failed;
}
/* iuu_reset() the card after iuu_uart_on() */
status = iuu_uart_flush(port);
if (status != IUU_OPERATION_OK)
dbg("%s - uart_flush error", __FUNCTION__);
uart_enable_failed:
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;
buf = kmalloc(1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = IUU_UART_DISABLE;
status = bulk_immediate(port, buf, 1);
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, u32 baud,
u32 *actual, u8 parity)
{
int status;
u8 *dataout;
u8 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) {
kfree(dataout);
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:
kfree(dataout);
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:
kfree(dataout);
return IUU_INVALID_PARAMETER;
break;
}
status = bulk_immediate(port, dataout, DataCount);
if (status != IUU_OPERATION_OK)
dbg("%s - uart_off error", __FUNCTION__);
kfree(dataout);
return status;
}
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 = usb_get_serial_port_data(port);
unsigned long flags;
unsigned int c_cflag;
serial = port->serial;
if (!serial)
return;
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);
}
}
/* free writebuf */
/* shutdown our urbs */
dbg("%s - shutting down urbs", __FUNCTION__);
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->interrupt_in_urb);
msleep(1000);
/* wait one second to free all buffers */
iuu_led(port, 0, 0, 0xF000, 0xFF);
msleep(1000);
usb_reset_device(port->serial->dev);
}
}
static int iuu_open(struct usb_serial_port *port, struct file *filp)
{
struct usb_serial *serial = port->serial;
u8 *buf;
int result;
u32 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;
/* fixup the endpoint buffer size */
kfree(port->bulk_out_buffer);
port->bulk_out_buffer = kmalloc(512, GFP_KERNEL);
port->bulk_out_size = 512;
kfree(port->bulk_in_buffer);
port->bulk_in_buffer = kmalloc(512, GFP_KERNEL);
port->bulk_in_size = 512;
if (!port->bulk_out_buffer || !port->bulk_in_buffer) {
kfree(port->bulk_out_buffer);
kfree(port->bulk_in_buffer);
kfree(buf);
return -ENOMEM;
}
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->bulk_out_buffer, 512,
NULL, NULL);
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
usb_rcvbulkpipe(port->serial->dev,
port->bulk_in_endpointAddress),
port->bulk_in_buffer, 512,
NULL, NULL);
/* 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 = 0;
port->tty->termios->c_iflag = 0;
priv->termios_initialized = 1;
port->tty->low_latency = 1;
priv->poll = 0;
}
spin_unlock_irqrestore(&priv->lock, flags);
/* initialize writebuf */
#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);
kfree(buf);
iuu_led(port, 0xF000, 0xF000, 0, 0xFF);
iuu_uart_on(port);
if (boost < 100)
boost = 100;
switch (clockmode) {
case 2: /* 3.680 Mhz */
iuu_clk(port, IUU_CLK_3680000 * boost / 100);
result =
iuu_uart_baud(port, 9600 * boost / 100, &actual,
IUU_PARITY_EVEN);
break;
case 3: /* 6.00 Mhz */
iuu_clk(port, IUU_CLK_6000000 * boost / 100);
result =
iuu_uart_baud(port, 16457 * boost / 100, &actual,
IUU_PARITY_EVEN);
break;
default: /* 3.579 Mhz */
iuu_clk(port, IUU_CLK_3579000 * boost / 100);
result =
iuu_uart_baud(port, 9600 * boost / 100, &actual,
IUU_PARITY_EVEN);
}
/* set the cardin cardout signals */
switch (cdmode) {
case 0:
iuu_cardin = 0;
iuu_cardout = 0;
break;
case 1:
iuu_cardin = TIOCM_CD;
iuu_cardout = 0;
break;
case 2:
iuu_cardin = 0;
iuu_cardout = TIOCM_CD;
break;
case 3:
iuu_cardin = TIOCM_DSR;
iuu_cardout = 0;
break;
case 4:
iuu_cardin = 0;
iuu_cardout = TIOCM_DSR;
break;
case 5:
iuu_cardin = TIOCM_CTS;
iuu_cardout = 0;
break;
case 6:
iuu_cardin = 0;
iuu_cardout = TIOCM_CTS;
break;
case 7:
iuu_cardin = TIOCM_RNG;
iuu_cardout = 0;
break;
case 8:
iuu_cardin = 0;
iuu_cardout = TIOCM_RNG;
}
iuu_uart_flush(port);
dbg("%s - initialization done", __FUNCTION__);
memset(port->write_urb->transfer_buffer, IUU_UART_RX, 1);
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
port->write_urb->transfer_buffer, 1,
read_rxcmd_callback, port);
result = usb_submit_urb(port->write_urb, GFP_KERNEL);
if (result) {
dev_err(&port->dev, "%s - failed submitting read urb,"
" error %d\n", __FUNCTION__, result);
iuu_close(port, NULL);
return -EPROTO;
} else {
dbg("%s - rxcmd OK", __FUNCTION__);
}
return result;
}
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,
.read_bulk_callback = iuu_uart_read_callback,
.tiocmget = iuu_tiocmget,
.tiocmset = iuu_tiocmset,
.attach = iuu_startup,
.shutdown = iuu_shutdown,
};
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_AUTHOR("Alain Degreffe eczema@ecze.com");
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(xmas, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(xmas, "xmas color enabled or not");
module_param(boost, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(boost, "overclock boost percent 100 to 500");
module_param(clockmode, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(clockmode, "1=3Mhz579,2=3Mhz680,3=6Mhz");
module_param(cdmode, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(cdmode, "Card detect mode 0=none, 1=CD, 2=!CD, 3=DSR, "
"4=!DSR, 5=CTS, 6=!CTS, 7=RING, 8=!RING");
/*
* 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment