[PATCH v3 1/5] usb: serial: add register map for F81232

From: Peter Hung
Date: Wed Jan 28 2015 - 00:58:09 EST


Add register map for F81232. and add some function to operating this device.
etc. f81232_get_register()/f81232_set_register() to work with USB control
point. and worker f81232_int_work_wq() to read MSR when IIR acquired.

Signed-off-by: Peter Hung <hpeter+linux_kernel@xxxxxxxxx>
---
drivers/usb/serial/f81232.c | 229 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 214 insertions(+), 15 deletions(-)

diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index c5dc233..efd45a7 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -23,6 +23,8 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
+#include <linux/serial_reg.h>
+#include <linux/version.h>

static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1934, 0x0706) },
@@ -37,19 +39,197 @@ MODULE_DEVICE_TABLE(usb, id_table);
#define UART_STATE_TRANSIENT_MASK 0x74
#define UART_DCD 0x01
#define UART_DSR 0x02
-#define UART_BREAK_ERROR 0x04
#define UART_RING 0x08
-#define UART_FRAME_ERROR 0x10
-#define UART_PARITY_ERROR 0x20
-#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80

+#define UART_BREAK_ERROR 0x10
+#define UART_FRAME_ERROR 0x08
+#define UART_PARITY_ERROR 0x04
+#define UART_OVERRUN_ERROR 0x02
+#define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR)
+
+#define REGISTER_REQUEST 0xA0
+#define GET_REGISTER 0xc0
+#define SET_REGISTER 0x40
+#define F81232_USB_TIMEOUT 1000
+#define F81232_USB_RETRY 20
+
+#define SERIAL_BASE_ADDRESS (0x0120)
+#define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS)
+#define TRANSMIT_HOLDING_REGISTER (0x00 + SERIAL_BASE_ADDRESS)
+#define INTERRUPT_ENABLE_REGISTER (0x01 + SERIAL_BASE_ADDRESS)
+#define INTERRUPT_IDENT_REGISTER (0x02 + SERIAL_BASE_ADDRESS)
+#define FIFO_CONTROL_REGISTER (0x02 + SERIAL_BASE_ADDRESS)
+#define LINE_CONTROL_REGISTER (0x03 + SERIAL_BASE_ADDRESS)
+#define MODEM_CONTROL_REGISTER (0x04 + SERIAL_BASE_ADDRESS)
+#define LINE_STATUS_REGISTER (0x05 + SERIAL_BASE_ADDRESS)
+#define MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS)
+
struct f81232_private {
spinlock_t lock;
u8 line_control;
u8 line_status;
+
+ struct work_struct int_worker;
+ struct usb_serial_port *port;
};

+static inline int calc_baud_divisor(u32 baudrate)
+{
+ u32 divisor, rem;
+
+ divisor = 115200L / baudrate;
+ rem = 115200L % baudrate;
+
+ /* Round to nearest divisor */
+ if (((rem * 2) >= baudrate) && (baudrate != 110))
+ divisor++;
+
+ return divisor;
+}
+
+
+static inline int f81232_get_register(struct usb_device *dev,
+ u16 reg, u8 *data)
+{
+ int status;
+ int i = F81232_USB_RETRY;
+
+ while (i--) {
+ status = usb_control_msg(dev,
+ usb_rcvctrlpipe(dev, 0),
+ REGISTER_REQUEST,
+ GET_REGISTER,
+ reg,
+ 0,
+ data,
+ sizeof(*data),
+ F81232_USB_TIMEOUT);
+
+ if (status < 0) {
+ dev_dbg(&dev->dev,
+ "f81232_get_register status: %d, fail:%d\n",
+ status, i);
+ } else
+ break;
+ }
+
+ return status;
+}
+
+
+static inline int f81232_set_register(struct usb_device *dev,
+ u16 reg, u8 data)
+{
+ int status;
+ int i = F81232_USB_RETRY;
+
+ while (i--) {
+ status = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ REGISTER_REQUEST,
+ SET_REGISTER,
+ reg,
+ 0,
+ &data,
+ 1,
+ F81232_USB_TIMEOUT);
+
+ if (status < 0)
+ dev_dbg(&dev->dev,
+ "f81232_set_register status: %d, fail:%d\n",
+ status, i);
+ else
+ break;
+ }
+
+ return status;
+}
+
+static void f81232_read_msr(struct f81232_private *priv)
+{
+ unsigned long flags;
+ u8 current_msr, old_msr;
+ struct usb_device *dev = priv->port->serial->dev;
+
+ f81232_get_register(dev, MODEM_STATUS_REGISTER, &current_msr);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ old_msr = priv->line_status;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+
+ if ((current_msr & 0xf0) ^ (old_msr & 0xf0)) {
+ if (priv->port->port.tty)
+ usb_serial_handle_dcd_change(priv->port,
+ priv->port->port.tty,
+ current_msr & UART_MSR_DCD);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_status = current_msr;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+
+ dev_dbg(&dev->dev, "f81232_read_msr: %x\n", priv->line_status);
+}
+
+
+static inline int update_mctrl(struct f81232_private *port_priv,
+ unsigned int set, unsigned int clear)
+{
+ struct usb_device *dev = port_priv->port->serial->dev;
+ u8 urb_value;
+ int status;
+ unsigned long flags;
+
+ if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
+ dev_dbg(&dev->dev, "update_mctrl fail - DTR|RTS %d\n",
+ __LINE__);
+ return 0; /* no change */
+ }
+
+
+ clear &= ~set; /* 'set' takes precedence over 'clear' */
+ urb_value = 8 | port_priv->line_control;
+
+
+ if (clear & TIOCM_DTR) {
+ urb_value &= ~UART_MCR_DTR;
+ dev_dbg(&dev->dev, "clear DTR\n");
+ }
+
+ if (clear & TIOCM_RTS) {
+ urb_value &= ~UART_MCR_RTS;
+ dev_dbg(&dev->dev, "clear RTS\n");
+ }
+
+ if (set & TIOCM_DTR) {
+ urb_value |= UART_MCR_DTR;
+ dev_dbg(&dev->dev, "set DTR\n");
+ }
+
+ if (set & TIOCM_RTS) {
+ urb_value |= UART_MCR_RTS;
+ dev_dbg(&dev->dev, "set RTS\n");
+ }
+
+ dev_dbg(&dev->dev, "update_mctrl n:%x o:%x\n", urb_value,
+ port_priv->line_control);
+
+ status = f81232_set_register(dev, MODEM_CONTROL_REGISTER, urb_value);
+
+ if (status < 0) {
+ dev_dbg(&dev->dev, "MODEM_CONTROL_REGISTER < 0\n");
+ } else {
+ spin_lock_irqsave(&port_priv->lock, flags);
+ port_priv->line_control = urb_value;
+ spin_unlock_irqrestore(&port_priv->lock, flags);
+ }
+
+ f81232_read_msr(port_priv);
+
+ return status;
+}
static void f81232_update_line_status(struct usb_serial_port *port,
unsigned char *data,
unsigned int actual_length)
@@ -201,8 +381,8 @@ static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)

result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
- dev_err(&port->dev, "%s - failed submitting interrupt urb,"
- " error %d\n", __func__, result);
+ dev_err(&port->dev, "failed submitting interrupt urb, error %d\n",
+ result);
return result;
}

@@ -241,6 +421,7 @@ static void f81232_dtr_rts(struct usb_serial_port *port, int on)
static int f81232_carrier_raised(struct usb_serial_port *port)
{
struct f81232_private *priv = usb_get_serial_port_data(port);
+
if (priv->line_status & UART_DCD)
return 1;
return 0;
@@ -254,13 +435,18 @@ static int f81232_ioctl(struct tty_struct *tty,

switch (cmd) {
case TIOCGSERIAL:
- memset(&ser, 0, sizeof ser);
- ser.type = PORT_16654;
+ memset(&ser, 0, sizeof(ser));
+ ser.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+ ser.xmit_fifo_size = port->bulk_out_size;
+ ser.close_delay = 5*HZ;
+ ser.closing_wait = 30*HZ;
+
+ ser.type = PORT_16550A;
ser.line = port->minor;
ser.port = port->port_number;
- ser.baud_base = 460800;
+ ser.baud_base = 115200;

- if (copy_to_user((void __user *)arg, &ser, sizeof ser))
+ if (copy_to_user((void __user *)arg, &ser, sizeof(ser)))
return -EFAULT;

return 0;
@@ -270,6 +456,17 @@ static int f81232_ioctl(struct tty_struct *tty,
return -ENOIOCTLCMD;
}

+
+
+
+static void f81232_int_work_wq(struct work_struct *work)
+{
+ struct f81232_private *priv =
+ container_of(work, struct f81232_private, int_worker);
+
+ f81232_read_msr(priv);
+}
+
static int f81232_port_probe(struct usb_serial_port *port)
{
struct f81232_private *priv;
@@ -279,10 +476,11 @@ static int f81232_port_probe(struct usb_serial_port *port)
return -ENOMEM;

spin_lock_init(&priv->lock);
+ INIT_WORK(&priv->int_worker, f81232_int_work_wq);

usb_set_serial_port_data(port, priv);

- port->port.drain_delay = 256;
+ priv->port = port;

return 0;
}
@@ -304,11 +502,11 @@ static struct usb_serial_driver f81232_device = {
},
.id_table = id_table,
.num_ports = 1,
- .bulk_in_size = 256,
- .bulk_out_size = 256,
+ .bulk_in_size = 64,
+ .bulk_out_size = 64,
.open = f81232_open,
.close = f81232_close,
- .dtr_rts = f81232_dtr_rts,
+ .dtr_rts = f81232_dtr_rts,
.carrier_raised = f81232_carrier_raised,
.ioctl = f81232_ioctl,
.break_ctl = f81232_break_ctl,
@@ -330,5 +528,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);

MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
-MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx");
+MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>");
+MODULE_AUTHOR("Peter Hong <peter_hong@xxxxxxxxxxxxx>");
MODULE_LICENSE("GPL v2");
--
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/