/* * Fintek F81232 USB to serial adaptor driver * * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org) * Copyright (C) 2012 Linux Foundation * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1934, 0x0706) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, id_table); /* Maximum baudrate for F81232 */ #define F81232_MAX_BAUDRATE 115200 /* USB Control EP parameter */ #define F81232_REGISTER_REQUEST 0xa0 #define F81232_GET_REGISTER 0xc0 #define F81232_SET_REGISTER 0x40 #define SERIAL_BASE_ADDRESS 0x0120 #define RECEIVE_BUFFER_REGISTER (0x00 + SERIAL_BASE_ADDRESS) #define INTERRUPT_ENABLE_REGISTER (0x01 + 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 MODEM_STATUS_REGISTER (0x06 + SERIAL_BASE_ADDRESS) struct f81232_private { struct mutex lock; u8 modem_control; u8 modem_status; struct work_struct interrupt_work; struct usb_serial_port *port; }; static int calc_baud_divisor(speed_t baudrate) { return DIV_ROUND_CLOSEST(F81232_MAX_BAUDRATE, baudrate); } static int f81232_get_register(struct usb_serial_port *port, u16 reg, u8 *val) { int status; u8 *tmp; struct usb_device *dev = port->serial->dev; tmp = kmalloc(sizeof(*val), GFP_KERNEL); if (!tmp) return -ENOMEM; status = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), F81232_REGISTER_REQUEST, F81232_GET_REGISTER, reg, 0, tmp, sizeof(*val), USB_CTRL_GET_TIMEOUT); if (status != sizeof(*val)) { dev_err(&port->dev, "%s failed status: %d\n", __func__, status); if (status < 0) status = usb_translate_errors(status); else status = -EIO; } else { status = 0; *val = *tmp; } kfree(tmp); return status; } static int f81232_set_register(struct usb_serial_port *port, u16 reg, u8 val) { int status; u8 *tmp; struct usb_device *dev = port->serial->dev; tmp = kmalloc(sizeof(val), GFP_KERNEL); if (!tmp) return -ENOMEM; *tmp = val; status = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), F81232_REGISTER_REQUEST, F81232_SET_REGISTER, reg, 0, tmp, sizeof(val), USB_CTRL_SET_TIMEOUT); if (status != sizeof(val)) { dev_err(&port->dev, "%s failed status: %d\n", __func__, status); if (status < 0) status = usb_translate_errors(status); else status = -EIO; } else { status = 0; } kfree(tmp); return status; } static void f81232_read_msr(struct usb_serial_port *port) { int status; u8 current_msr; struct tty_struct *tty; struct f81232_private *priv = usb_get_serial_port_data(port); mutex_lock(&priv->lock); status = f81232_get_register(port, MODEM_STATUS_REGISTER, ¤t_msr); if (status) { dev_err(&port->dev, "%s fail, status: %d\n", __func__, status); mutex_unlock(&priv->lock); return; } if (!(current_msr & UART_MSR_ANY_DELTA)) { mutex_unlock(&priv->lock); return; } priv->modem_status = current_msr; if (current_msr & UART_MSR_DCTS) port->icount.cts++; if (current_msr & UART_MSR_DDSR
heat_template_version: 2015-04-30

description: A stack which creates no network(s).
switch (cmd) { case TIOCGSERIAL: return f81232_get_serial_info(port, arg); default: break; } return -ENOIOCTLCMD; } static void f81232_interrupt_work(struct work_struct *work) { struct f81232_private *priv = container_of(work, struct f81232_private, interrupt_work); f81232_read_msr(priv->port); } static int f81232_port_probe(struct usb_serial_port *port) { struct f81232_private *priv; priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; mutex_init(&priv->lock); INIT_WORK(&priv->interrupt_work, f81232_interrupt_work); usb_set_serial_port_data(port, priv); port->port.drain_delay = 256; priv->port = port; return 0; } static int f81232_port_remove(struct usb_serial_port *port) { struct f81232_private *priv; priv = usb_get_serial_port_data(port); kfree(priv); return 0; } static struct usb_serial_driver f81232_device = { .driver = { .owner = THIS_MODULE, .name = "f81232", }, .id_table = id_table, .num_ports = 1, .bulk_in_size = 256, .bulk_out_size = 256, .open = f81232_open, .close = f81232_close, .dtr_rts = f81232_dtr_rts, .carrier_raised = f81232_carrier_raised, .ioctl = f81232_ioctl, .break_ctl = f81232_break_ctl, .set_termios = f81232_set_termios, .tiocmget = f81232_tiocmget, .tiocmset = f81232_tiocmset, .tiocmiwait = usb_serial_generic_tiocmiwait, .process_read_urb = f81232_process_read_urb, .read_int_callback = f81232_read_int_callback, .port_probe = f81232_port_probe, .port_remove = f81232_port_remove, }; static struct usb_serial_driver * const serial_drivers[] = { &f81232_device, NULL, }; module_usb_serial_driver(serial_drivers, id_table); MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver"); MODULE_AUTHOR("Greg Kroah-Hartman "); MODULE_AUTHOR("Peter Hong "); MODULE_LICENSE("GPL v2");