diff options
Diffstat (limited to 'drivers/usb/serial/mxu11x0.c')
-rw-r--r-- | drivers/usb/serial/mxu11x0.c | 1006 |
1 files changed, 0 insertions, 1006 deletions
diff --git a/drivers/usb/serial/mxu11x0.c b/drivers/usb/serial/mxu11x0.c deleted file mode 100644 index 619607323bfd..000000000000 --- a/drivers/usb/serial/mxu11x0.c +++ /dev/null @@ -1,1006 +0,0 @@ -/* - * USB Moxa UPORT 11x0 Serial Driver - * - * Copyright (C) 2007 MOXA Technologies Co., Ltd. - * Copyright (C) 2015 Mathieu Othacehe <m.othacehe@gmail.com> - * - * 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. - * - * - * Supports the following Moxa USB to serial converters: - * UPort 1110, 1 port RS-232 USB to Serial Hub. - * UPort 1130, 1 port RS-422/485 USB to Serial Hub. - * UPort 1130I, 1 port RS-422/485 USB to Serial Hub with isolation - * protection. - * UPort 1150, 1 port RS-232/422/485 USB to Serial Hub. - * UPort 1150I, 1 port RS-232/422/485 USB to Serial Hub with isolation - * protection. - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/firmware.h> -#include <linux/jiffies.h> -#include <linux/serial.h> -#include <linux/serial_reg.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/mutex.h> -#include <linux/tty.h> -#include <linux/tty_driver.h> -#include <linux/tty_flip.h> -#include <linux/uaccess.h> -#include <linux/usb.h> -#include <linux/usb/serial.h> - -/* Vendor and product ids */ -#define MXU1_VENDOR_ID 0x110a -#define MXU1_1110_PRODUCT_ID 0x1110 -#define MXU1_1130_PRODUCT_ID 0x1130 -#define MXU1_1150_PRODUCT_ID 0x1150 -#define MXU1_1151_PRODUCT_ID 0x1151 -#define MXU1_1131_PRODUCT_ID 0x1131 - -/* Commands */ -#define MXU1_GET_VERSION 0x01 -#define MXU1_GET_PORT_STATUS 0x02 -#define MXU1_GET_PORT_DEV_INFO 0x03 -#define MXU1_GET_CONFIG 0x04 -#define MXU1_SET_CONFIG 0x05 -#define MXU1_OPEN_PORT 0x06 -#define MXU1_CLOSE_PORT 0x07 -#define MXU1_START_PORT 0x08 -#define MXU1_STOP_PORT 0x09 -#define MXU1_TEST_PORT 0x0A -#define MXU1_PURGE_PORT 0x0B -#define MXU1_RESET_EXT_DEVICE 0x0C -#define MXU1_GET_OUTQUEUE 0x0D -#define MXU1_WRITE_DATA 0x80 -#define MXU1_READ_DATA 0x81 -#define MXU1_REQ_TYPE_CLASS 0x82 - -/* Module identifiers */ -#define MXU1_I2C_PORT 0x01 -#define MXU1_IEEE1284_PORT 0x02 -#define MXU1_UART1_PORT 0x03 -#define MXU1_UART2_PORT 0x04 -#define MXU1_RAM_PORT 0x05 - -/* Modem status */ -#define MXU1_MSR_DELTA_CTS 0x01 -#define MXU1_MSR_DELTA_DSR 0x02 -#define MXU1_MSR_DELTA_RI 0x04 -#define MXU1_MSR_DELTA_CD 0x08 -#define MXU1_MSR_CTS 0x10 -#define MXU1_MSR_DSR 0x20 -#define MXU1_MSR_RI 0x40 -#define MXU1_MSR_CD 0x80 -#define MXU1_MSR_DELTA_MASK 0x0F -#define MXU1_MSR_MASK 0xF0 - -/* Line status */ -#define MXU1_LSR_OVERRUN_ERROR 0x01 -#define MXU1_LSR_PARITY_ERROR 0x02 -#define MXU1_LSR_FRAMING_ERROR 0x04 -#define MXU1_LSR_BREAK 0x08 -#define MXU1_LSR_ERROR 0x0F -#define MXU1_LSR_RX_FULL 0x10 -#define MXU1_LSR_TX_EMPTY 0x20 - -/* Modem control */ -#define MXU1_MCR_LOOP 0x04 -#define MXU1_MCR_DTR 0x10 -#define MXU1_MCR_RTS 0x20 - -/* Mask settings */ -#define MXU1_UART_ENABLE_RTS_IN 0x0001 -#define MXU1_UART_DISABLE_RTS 0x0002 -#define MXU1_UART_ENABLE_PARITY_CHECKING 0x0008 -#define MXU1_UART_ENABLE_DSR_OUT 0x0010 -#define MXU1_UART_ENABLE_CTS_OUT 0x0020 -#define MXU1_UART_ENABLE_X_OUT 0x0040 -#define MXU1_UART_ENABLE_XA_OUT 0x0080 -#define MXU1_UART_ENABLE_X_IN 0x0100 -#define MXU1_UART_ENABLE_DTR_IN 0x0800 -#define MXU1_UART_DISABLE_DTR 0x1000 -#define MXU1_UART_ENABLE_MS_INTS 0x2000 -#define MXU1_UART_ENABLE_AUTO_START_DMA 0x4000 -#define MXU1_UART_SEND_BREAK_SIGNAL 0x8000 - -/* Parity */ -#define MXU1_UART_NO_PARITY 0x00 -#define MXU1_UART_ODD_PARITY 0x01 -#define MXU1_UART_EVEN_PARITY 0x02 -#define MXU1_UART_MARK_PARITY 0x03 -#define MXU1_UART_SPACE_PARITY 0x04 - -/* Stop bits */ -#define MXU1_UART_1_STOP_BITS 0x00 -#define MXU1_UART_1_5_STOP_BITS 0x01 -#define MXU1_UART_2_STOP_BITS 0x02 - -/* Bits per character */ -#define MXU1_UART_5_DATA_BITS 0x00 -#define MXU1_UART_6_DATA_BITS 0x01 -#define MXU1_UART_7_DATA_BITS 0x02 -#define MXU1_UART_8_DATA_BITS 0x03 - -/* Operation modes */ -#define MXU1_UART_232 0x00 -#define MXU1_UART_485_RECEIVER_DISABLED 0x01 -#define MXU1_UART_485_RECEIVER_ENABLED 0x02 - -/* Pipe transfer mode and timeout */ -#define MXU1_PIPE_MODE_CONTINUOUS 0x01 -#define MXU1_PIPE_MODE_MASK 0x03 -#define MXU1_PIPE_TIMEOUT_MASK 0x7C -#define MXU1_PIPE_TIMEOUT_ENABLE 0x80 - -/* Config struct */ -struct mxu1_uart_config { - __be16 wBaudRate; - __be16 wFlags; - u8 bDataBits; - u8 bParity; - u8 bStopBits; - char cXon; - char cXoff; - u8 bUartMode; -} __packed; - -/* Purge modes */ -#define MXU1_PURGE_OUTPUT 0x00 -#define MXU1_PURGE_INPUT 0x80 - -/* Read/Write data */ -#define MXU1_RW_DATA_ADDR_SFR 0x10 -#define MXU1_RW_DATA_ADDR_IDATA 0x20 -#define MXU1_RW_DATA_ADDR_XDATA 0x30 -#define MXU1_RW_DATA_ADDR_CODE 0x40 -#define MXU1_RW_DATA_ADDR_GPIO 0x50 -#define MXU1_RW_DATA_ADDR_I2C 0x60 -#define MXU1_RW_DATA_ADDR_FLASH 0x70 -#define MXU1_RW_DATA_ADDR_DSP 0x80 - -#define MXU1_RW_DATA_UNSPECIFIED 0x00 -#define MXU1_RW_DATA_BYTE 0x01 -#define MXU1_RW_DATA_WORD 0x02 -#define MXU1_RW_DATA_DOUBLE_WORD 0x04 - -struct mxu1_write_data_bytes { - u8 bAddrType; - u8 bDataType; - u8 bDataCounter; - __be16 wBaseAddrHi; - __be16 wBaseAddrLo; - u8 bData[0]; -} __packed; - -/* Interrupt codes */ -#define MXU1_CODE_HARDWARE_ERROR 0xFF -#define MXU1_CODE_DATA_ERROR 0x03 -#define MXU1_CODE_MODEM_STATUS 0x04 - -static inline int mxu1_get_func_from_code(unsigned char code) -{ - return code & 0x0f; -} - -/* Download firmware max packet size */ -#define MXU1_DOWNLOAD_MAX_PACKET_SIZE 64 - -/* Firmware image header */ -struct mxu1_firmware_header { - __le16 wLength; - u8 bCheckSum; -} __packed; - -#define MXU1_UART_BASE_ADDR 0xFFA0 -#define MXU1_UART_OFFSET_MCR 0x0004 - -#define MXU1_BAUD_BASE 923077 - -#define MXU1_TRANSFER_TIMEOUT 2 -#define MXU1_DOWNLOAD_TIMEOUT 1000 -#define MXU1_DEFAULT_CLOSING_WAIT 4000 /* in .01 secs */ - -struct mxu1_port { - u8 msr; - u8 mcr; - u8 uart_mode; - spinlock_t spinlock; /* Protects msr */ - struct mutex mutex; /* Protects mcr */ - bool send_break; -}; - -struct mxu1_device { - u16 mxd_model; -}; - -static const struct usb_device_id mxu1_idtable[] = { - { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) }, - { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) }, - { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) }, - { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) }, - { USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) }, - { } -}; - -MODULE_DEVICE_TABLE(usb, mxu1_idtable); - -/* Write the given buffer out to the control pipe. */ -static int mxu1_send_ctrl_data_urb(struct usb_serial *serial, - u8 request, - u16 value, u16 index, - void *data, size_t size) -{ - int status; - - status = usb_control_msg(serial->dev, - usb_sndctrlpipe(serial->dev, 0), - request, - (USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE), value, index, - data, size, - USB_CTRL_SET_TIMEOUT); - if (status < 0) { - dev_err(&serial->interface->dev, - "%s - usb_control_msg failed: %d\n", - __func__, status); - return status; - } - - if (status != size) { - dev_err(&serial->interface->dev, - "%s - short write (%d / %zd)\n", - __func__, status, size); - return -EIO; - } - - return 0; -} - -/* Send a vendor request without any data */ -static int mxu1_send_ctrl_urb(struct usb_serial *serial, - u8 request, u16 value, u16 index) -{ - return mxu1_send_ctrl_data_urb(serial, request, value, index, - NULL, 0); -} - -static int mxu1_download_firmware(struct usb_serial *serial, - const struct firmware *fw_p) -{ - int status = 0; - int buffer_size; - int pos; - int len; - int done; - u8 cs = 0; - u8 *buffer; - struct usb_device *dev = serial->dev; - struct mxu1_firmware_header *header; - unsigned int pipe; - - pipe = usb_sndbulkpipe(dev, serial->port[0]->bulk_out_endpointAddress); - - buffer_size = fw_p->size + sizeof(*header); - buffer = kmalloc(buffer_size, GFP_KERNEL); - if (!buffer) - return -ENOMEM; - - memcpy(buffer, fw_p->data, fw_p->size); - memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size); - - for (pos = sizeof(*header); pos < buffer_size; pos++) - cs = (u8)(cs + buffer[pos]); - - header = (struct mxu1_firmware_header *)buffer; - header->wLength = cpu_to_le16(buffer_size - sizeof(*header)); - header->bCheckSum = cs; - - dev_dbg(&dev->dev, "%s - downloading firmware\n", __func__); - - for (pos = 0; pos < buffer_size; pos += done) { - len = min(buffer_size - pos, MXU1_DOWNLOAD_MAX_PACKET_SIZE); - - status = usb_bulk_msg(dev, pipe, buffer + pos, len, &done, - MXU1_DOWNLOAD_TIMEOUT); - if (status) - break; - } - - kfree(buffer); - - if (status) { - dev_err(&dev->dev, "failed to download firmware: %d\n", status); - return status; - } - - msleep_interruptible(100); - usb_reset_device(dev); - - dev_dbg(&dev->dev, "%s - download successful\n", __func__); - - return 0; -} - -static int mxu1_port_probe(struct usb_serial_port *port) -{ - struct mxu1_port *mxport; - struct mxu1_device *mxdev; - - if (!port->interrupt_in_urb) { - dev_err(&port->dev, "no interrupt urb\n"); - return -ENODEV; - } - - mxport = kzalloc(sizeof(struct mxu1_port), GFP_KERNEL); - if (!mxport) - return -ENOMEM; - - spin_lock_init(&mxport->spinlock); - mutex_init(&mxport->mutex); - - mxdev = usb_get_serial_data(port->serial); - - switch (mxdev->mxd_model) { - case MXU1_1110_PRODUCT_ID: - case MXU1_1150_PRODUCT_ID: - case MXU1_1151_PRODUCT_ID: - mxport->uart_mode = MXU1_UART_232; - break; - case MXU1_1130_PRODUCT_ID: - case MXU1_1131_PRODUCT_ID: - mxport->uart_mode = MXU1_UART_485_RECEIVER_DISABLED; - break; - } - - usb_set_serial_port_data(port, mxport); - - port->port.closing_wait = - msecs_to_jiffies(MXU1_DEFAULT_CLOSING_WAIT * 10); - port->port.drain_delay = 1; - - return 0; -} - -static int mxu1_port_remove(struct usb_serial_port *port) -{ - struct mxu1_port *mxport; - - mxport = usb_get_serial_port_data(port); - kfree(mxport); - - return 0; -} - -static int mxu1_startup(struct usb_serial *serial) -{ - struct mxu1_device *mxdev; - struct usb_device *dev = serial->dev; - struct usb_host_interface *cur_altsetting; - char fw_name[32]; - const struct firmware *fw_p = NULL; - int err; - - dev_dbg(&serial->interface->dev, "%s - product 0x%04X, num configurations %d, configuration value %d\n", - __func__, le16_to_cpu(dev->descriptor.idProduct), - dev->descriptor.bNumConfigurations, - dev->actconfig->desc.bConfigurationValue); - - /* create device structure */ - mxdev = kzalloc(sizeof(struct mxu1_device), GFP_KERNEL); - if (!mxdev) - return -ENOMEM; - - usb_set_serial_data(serial, mxdev); - - mxdev->mxd_model = le16_to_cpu(dev->descriptor.idProduct); - - cur_altsetting = serial->interface->cur_altsetting; - - /* if we have only 1 configuration, download firmware */ - if (cur_altsetting->desc.bNumEndpoints == 1) { - - snprintf(fw_name, - sizeof(fw_name), - "moxa/moxa-%04x.fw", - mxdev->mxd_model); - - err = request_firmware(&fw_p, fw_name, &serial->interface->dev); - if (err) { - dev_err(&serial->interface->dev, "failed to request firmware: %d\n", - err); - goto err_free_mxdev; - } - - err = mxu1_download_firmware(serial, fw_p); - if (err) - goto err_release_firmware; - - /* device is being reset */ - err = -ENODEV; - goto err_release_firmware; - } - - return 0; - -err_release_firmware: - release_firmware(fw_p); -err_free_mxdev: - kfree(mxdev); - - return err; -} - -static void mxu1_release(struct usb_serial *serial) -{ - struct mxu1_device *mxdev; - - mxdev = usb_get_serial_data(serial); - kfree(mxdev); -} - -static int mxu1_write_byte(struct usb_serial_port *port, u32 addr, - u8 mask, u8 byte) -{ - int status; - size_t size; - struct mxu1_write_data_bytes *data; - - dev_dbg(&port->dev, "%s - addr 0x%08X, mask 0x%02X, byte 0x%02X\n", - __func__, addr, mask, byte); - - size = sizeof(struct mxu1_write_data_bytes) + 2; - data = kzalloc(size, GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->bAddrType = MXU1_RW_DATA_ADDR_XDATA; - data->bDataType = MXU1_RW_DATA_BYTE; - data->bDataCounter = 1; - data->wBaseAddrHi = cpu_to_be16(addr >> 16); - data->wBaseAddrLo = cpu_to_be16(addr); - data->bData[0] = mask; - data->bData[1] = byte; - - status = mxu1_send_ctrl_data_urb(port->serial, MXU1_WRITE_DATA, 0, - MXU1_RAM_PORT, data, size); - if (status < 0) - dev_err(&port->dev, "%s - failed: %d\n", __func__, status); - - kfree(data); - - return status; -} - -static int mxu1_set_mcr(struct usb_serial_port *port, unsigned int mcr) -{ - int status; - - status = mxu1_write_byte(port, - MXU1_UART_BASE_ADDR + MXU1_UART_OFFSET_MCR, - MXU1_MCR_RTS | MXU1_MCR_DTR | MXU1_MCR_LOOP, - mcr); - return status; -} - -static void mxu1_set_termios(struct tty_struct *tty, - struct usb_serial_port *port, - struct ktermios *old_termios) -{ - struct mxu1_port *mxport = usb_get_serial_port_data(port); - struct mxu1_uart_config *config; - tcflag_t cflag, iflag; - speed_t baud; - int status; - unsigned int mcr; - - cflag = tty->termios.c_cflag; - iflag = tty->termios.c_iflag; - - if (old_termios && - !tty_termios_hw_change(&tty->termios, old_termios) && - tty->termios.c_iflag == old_termios->c_iflag) { - dev_dbg(&port->dev, "%s - nothing to change\n", __func__); - return; - } - - dev_dbg(&port->dev, - "%s - cflag 0x%08x, iflag 0x%08x\n", __func__, cflag, iflag); - - if (old_termios) { - dev_dbg(&port->dev, "%s - old cflag 0x%08x, old iflag 0x%08x\n", - __func__, - old_termios->c_cflag, - old_termios->c_iflag); - } - - config = kzalloc(sizeof(*config), GFP_KERNEL); - if (!config) - return; - - /* these flags must be set */ - config->wFlags |= MXU1_UART_ENABLE_MS_INTS; - config->wFlags |= MXU1_UART_ENABLE_AUTO_START_DMA; - if (mxport->send_break) - config->wFlags |= MXU1_UART_SEND_BREAK_SIGNAL; - config->bUartMode = mxport->uart_mode; - - switch (C_CSIZE(tty)) { - case CS5: - config->bDataBits = MXU1_UART_5_DATA_BITS; - break; - case CS6: - config->bDataBits = MXU1_UART_6_DATA_BITS; - break; - case CS7: - config->bDataBits = MXU1_UART_7_DATA_BITS; - break; - default: - case CS8: - config->bDataBits = MXU1_UART_8_DATA_BITS; - break; - } - - if (C_PARENB(tty)) { - config->wFlags |= MXU1_UART_ENABLE_PARITY_CHECKING; - if (C_CMSPAR(tty)) { - if (C_PARODD(tty)) - config->bParity = MXU1_UART_MARK_PARITY; - else - config->bParity = MXU1_UART_SPACE_PARITY; - } else { - if (C_PARODD(tty)) - config->bParity = MXU1_UART_ODD_PARITY; - else - config->bParity = MXU1_UART_EVEN_PARITY; - } - } else { - config->bParity = MXU1_UART_NO_PARITY; - } - - if (C_CSTOPB(tty)) - config->bStopBits = MXU1_UART_2_STOP_BITS; - else - config->bStopBits = MXU1_UART_1_STOP_BITS; - - if (C_CRTSCTS(tty)) { - /* RTS flow control must be off to drop RTS for baud rate B0 */ - if (C_BAUD(tty) != B0) - config->wFlags |= MXU1_UART_ENABLE_RTS_IN; - config->wFlags |= MXU1_UART_ENABLE_CTS_OUT; - } - - if (I_IXOFF(tty) || I_IXON(tty)) { - config->cXon = START_CHAR(tty); - config->cXoff = STOP_CHAR(tty); - - if (I_IXOFF(tty)) - config->wFlags |= MXU1_UART_ENABLE_X_IN; - - if (I_IXON(tty)) - config->wFlags |= MXU1_UART_ENABLE_X_OUT; - } - - baud = tty_get_baud_rate(tty); - if (!baud) - baud = 9600; - config->wBaudRate = MXU1_BAUD_BASE / baud; - - dev_dbg(&port->dev, "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n", - __func__, baud, config->wBaudRate, config->wFlags, - config->bDataBits, config->bParity, config->bStopBits, - config->cXon, config->cXoff, config->bUartMode); - - cpu_to_be16s(&config->wBaudRate); - cpu_to_be16s(&config->wFlags); - - status = mxu1_send_ctrl_data_urb(port->serial, MXU1_SET_CONFIG, 0, - MXU1_UART1_PORT, config, - sizeof(*config)); - if (status) - dev_err(&port->dev, "cannot set config: %d\n", status); - - mutex_lock(&mxport->mutex); - mcr = mxport->mcr; - - if (C_BAUD(tty) == B0) - mcr &= ~(MXU1_MCR_DTR | MXU1_MCR_RTS); - else if (old_termios && (old_termios->c_cflag & CBAUD) == B0) - mcr |= MXU1_MCR_DTR | MXU1_MCR_RTS; - - status = mxu1_set_mcr(port, mcr); - if (status) - dev_err(&port->dev, "cannot set modem control: %d\n", status); - else - mxport->mcr = mcr; - - mutex_unlock(&mxport->mutex); - - kfree(config); -} - -static int mxu1_get_serial_info(struct usb_serial_port *port, - struct serial_struct __user *ret_arg) -{ - struct serial_struct ret_serial; - unsigned cwait; - - if (!ret_arg) - return -EFAULT; - - cwait = port->port.closing_wait; - if (cwait != ASYNC_CLOSING_WAIT_NONE) - cwait = jiffies_to_msecs(cwait) / 10; - - memset(&ret_serial, 0, sizeof(ret_serial)); - - ret_serial.type = PORT_16550A; - ret_serial.line = port->minor; - ret_serial.port = 0; - ret_serial.xmit_fifo_size = port->bulk_out_size; - ret_serial.baud_base = MXU1_BAUD_BASE; - ret_serial.close_delay = 5*HZ; - ret_serial.closing_wait = cwait; - - if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg))) - return -EFAULT; - - return 0; -} - - -static int mxu1_set_serial_info(struct usb_serial_port *port, - struct serial_struct __user *new_arg) -{ - struct serial_struct new_serial; - unsigned cwait; - - if (copy_from_user(&new_serial, new_arg, sizeof(new_serial))) - return -EFAULT; - - cwait = new_serial.closing_wait; - if (cwait != ASYNC_CLOSING_WAIT_NONE) - cwait = msecs_to_jiffies(10 * new_serial.closing_wait); - - port->port.closing_wait = cwait; - - return 0; -} - -static int mxu1_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct usb_serial_port *port = tty->driver_data; - - switch (cmd) { - case TIOCGSERIAL: - return mxu1_get_serial_info(port, - (struct serial_struct __user *)arg); - case TIOCSSERIAL: - return mxu1_set_serial_info(port, - (struct serial_struct __user *)arg); - } - - return -ENOIOCTLCMD; -} - -static int mxu1_tiocmget(struct tty_struct *tty) -{ - struct usb_serial_port *port = tty->driver_data; - struct mxu1_port *mxport = usb_get_serial_port_data(port); - unsigned int result; - unsigned int msr; - unsigned int mcr; - unsigned long flags; - - mutex_lock(&mxport->mutex); - spin_lock_irqsave(&mxport->spinlock, flags); - - msr = mxport->msr; - mcr = mxport->mcr; - - spin_unlock_irqrestore(&mxport->spinlock, flags); - mutex_unlock(&mxport->mutex); - - result = ((mcr & MXU1_MCR_DTR) ? TIOCM_DTR : 0) | - ((mcr & MXU1_MCR_RTS) ? TIOCM_RTS : 0) | - ((mcr & MXU1_MCR_LOOP) ? TIOCM_LOOP : 0) | - ((msr & MXU1_MSR_CTS) ? TIOCM_CTS : 0) | - ((msr & MXU1_MSR_CD) ? TIOCM_CAR : 0) | - ((msr & MXU1_MSR_RI) ? TIOCM_RI : 0) | - ((msr & MXU1_MSR_DSR) ? TIOCM_DSR : 0); - - dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result); - - return result; -} - -static int mxu1_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct usb_serial_port *port = tty->driver_data; - struct mxu1_port *mxport = usb_get_serial_port_data(port); - int err; - unsigned int mcr; - - mutex_lock(&mxport->mutex); - mcr = mxport->mcr; - - if (set & TIOCM_RTS) - mcr |= MXU1_MCR_RTS; - if (set & TIOCM_DTR) - mcr |= MXU1_MCR_DTR; - if (set & TIOCM_LOOP) - mcr |= MXU1_MCR_LOOP; - - if (clear & TIOCM_RTS) - mcr &= ~MXU1_MCR_RTS; - if (clear & TIOCM_DTR) - mcr &= ~MXU1_MCR_DTR; - if (clear & TIOCM_LOOP) - mcr &= ~MXU1_MCR_LOOP; - - err = mxu1_set_mcr(port, mcr); - if (!err) - mxport->mcr = mcr; - - mutex_unlock(&mxport->mutex); - - return err; -} - -static void mxu1_break(struct tty_struct *tty, int break_state) -{ - struct usb_serial_port *port = tty->driver_data; - struct mxu1_port *mxport = usb_get_serial_port_data(port); - - if (break_state == -1) - mxport->send_break = true; - else - mxport->send_break = false; - - mxu1_set_termios(tty, port, NULL); -} - -static int mxu1_open(struct tty_struct *tty, struct usb_serial_port *port) -{ - struct mxu1_port *mxport = usb_get_serial_port_data(port); - struct usb_serial *serial = port->serial; - int status; - u16 open_settings; - - open_settings = (MXU1_PIPE_MODE_CONTINUOUS | - MXU1_PIPE_TIMEOUT_ENABLE | - (MXU1_TRANSFER_TIMEOUT << 2)); - - mxport->msr = 0; - - status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (status) { - dev_err(&port->dev, "failed to submit interrupt urb: %d\n", - status); - return status; - } - - if (tty) - mxu1_set_termios(tty, port, NULL); - - status = mxu1_send_ctrl_urb(serial, MXU1_OPEN_PORT, - open_settings, MXU1_UART1_PORT); - if (status) { - dev_err(&port->dev, "cannot send open command: %d\n", status); - goto unlink_int_urb; - } - - status = mxu1_send_ctrl_urb(serial, MXU1_START_PORT, - 0, MXU1_UART1_PORT); - if (status) { - dev_err(&port->dev, "cannot send start command: %d\n", status); - goto unlink_int_urb; - } - - status = mxu1_send_ctrl_urb(serial, MXU1_PURGE_PORT, - MXU1_PURGE_INPUT, MXU1_UART1_PORT); - if (status) { - dev_err(&port->dev, "cannot clear input buffers: %d\n", - status); - - goto unlink_int_urb; - } - - status = mxu1_send_ctrl_urb(serial, MXU1_PURGE_PORT, - MXU1_PURGE_OUTPUT, MXU1_UART1_PORT); - if (status) { - dev_err(&port->dev, "cannot clear output buffers: %d\n", - status); - - goto unlink_int_urb; - } - - /* - * reset the data toggle on the bulk endpoints to work around bug in - * host controllers where things get out of sync some times - */ - usb_clear_halt(serial->dev, port->write_urb->pipe); - usb_clear_halt(serial->dev, port->read_urb->pipe); - - if (tty) - mxu1_set_termios(tty, port, NULL); - - status = mxu1_send_ctrl_urb(serial, MXU1_OPEN_PORT, - open_settings, MXU1_UART1_PORT); - if (status) { - dev_err(&port->dev, "cannot send open command: %d\n", status); - goto unlink_int_urb; - } - - status = mxu1_send_ctrl_urb(serial, MXU1_START_PORT, - 0, MXU1_UART1_PORT); - if (status) { - dev_err(&port->dev, "cannot send start command: %d\n", status); - goto unlink_int_urb; - } - - status = usb_serial_generic_open(tty, port); - if (status) - goto unlink_int_urb; - - return 0; - -unlink_int_urb: - usb_kill_urb(port->interrupt_in_urb); - - return status; -} - -static void mxu1_close(struct usb_serial_port *port) -{ - int status; - - usb_serial_generic_close(port); - usb_kill_urb(port->interrupt_in_urb); - - status = mxu1_send_ctrl_urb(port->serial, MXU1_CLOSE_PORT, - 0, MXU1_UART1_PORT); - if (status) { - dev_err(&port->dev, "failed to send close port command: %d\n", - status); - } -} - -static void mxu1_handle_new_msr(struct usb_serial_port *port, u8 msr) -{ - struct mxu1_port *mxport = usb_get_serial_port_data(port); - struct async_icount *icount; - unsigned long flags; - - dev_dbg(&port->dev, "%s - msr 0x%02X\n", __func__, msr); - - spin_lock_irqsave(&mxport->spinlock, flags); - mxport->msr = msr & MXU1_MSR_MASK; - spin_unlock_irqrestore(&mxport->spinlock, flags); - - if (msr & MXU1_MSR_DELTA_MASK) { - icount = &port->icount; - if (msr & MXU1_MSR_DELTA_CTS) - icount->cts++; - if (msr & MXU1_MSR_DELTA_DSR) - icount->dsr++; - if (msr & MXU1_MSR_DELTA_CD) - icount->dcd++; - if (msr & MXU1_MSR_DELTA_RI) - icount->rng++; - - wake_up_interruptible(&port->port.delta_msr_wait); - } -} - -static void mxu1_interrupt_callback(struct urb *urb) -{ - struct usb_serial_port *port = urb->context; - unsigned char *data = urb->transfer_buffer; - int length = urb->actual_length; - int function; - int status; - u8 msr; - - switch (urb->status) { - case 0: - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - dev_dbg(&port->dev, "%s - urb shutting down: %d\n", - __func__, urb->status); - return; - default: - dev_dbg(&port->dev, "%s - nonzero urb status: %d\n", - __func__, urb->status); - goto exit; - } - - if (length != 2) { - dev_dbg(&port->dev, "%s - bad packet size: %d\n", - __func__, length); - goto exit; - } - - if (data[0] == MXU1_CODE_HARDWARE_ERROR) { - dev_err(&port->dev, "hardware error: %d\n", data[1]); - goto exit; - } - - function = mxu1_get_func_from_code(data[0]); - - dev_dbg(&port->dev, "%s - function %d, data 0x%02X\n", - __func__, function, data[1]); - - switch (function) { - case MXU1_CODE_DATA_ERROR: - dev_dbg(&port->dev, "%s - DATA ERROR, data 0x%02X\n", - __func__, data[1]); - break; - - case MXU1_CODE_MODEM_STATUS: - msr = data[1]; - mxu1_handle_new_msr(port, msr); - break; - - default: - dev_err(&port->dev, "unknown interrupt code: 0x%02X\n", - data[1]); - break; - } - -exit: - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) { - dev_err(&port->dev, "resubmit interrupt urb failed: %d\n", - status); - } -} - -static struct usb_serial_driver mxu11x0_device = { - .driver = { - .owner = THIS_MODULE, - .name = "mxu11x0", - }, - .description = "MOXA UPort 11x0", - .id_table = mxu1_idtable, - .num_ports = 1, - .port_probe = mxu1_port_probe, - .port_remove = mxu1_port_remove, - .attach = mxu1_startup, - .release = mxu1_release, - .open = mxu1_open, - .close = mxu1_close, - .ioctl = mxu1_ioctl, - .set_termios = mxu1_set_termios, - .tiocmget = mxu1_tiocmget, - .tiocmset = mxu1_tiocmset, - .tiocmiwait = usb_serial_generic_tiocmiwait, - .get_icount = usb_serial_generic_get_icount, - .break_ctl = mxu1_break, - .read_int_callback = mxu1_interrupt_callback, -}; - -static struct usb_serial_driver *const serial_drivers[] = { - &mxu11x0_device, NULL -}; - -module_usb_serial_driver(serial_drivers, mxu1_idtable); - -MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); -MODULE_DESCRIPTION("MOXA UPort 11x0 USB to Serial Hub Driver"); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("moxa/moxa-1110.fw"); -MODULE_FIRMWARE("moxa/moxa-1130.fw"); -MODULE_FIRMWARE("moxa/moxa-1131.fw"); -MODULE_FIRMWARE("moxa/moxa-1150.fw"); -MODULE_FIRMWARE("moxa/moxa-1151.fw"); |