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, ¤t_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-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html