162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mxuport.c - MOXA UPort series driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2006 Moxa Technologies Co., Ltd. 662306a36Sopenharmony_ci * Copyright (c) 2013 Andrew Lunn <andrew@lunn.ch> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Supports the following Moxa USB to serial converters: 962306a36Sopenharmony_ci * 2 ports : UPort 1250, UPort 1250I 1062306a36Sopenharmony_ci * 4 ports : UPort 1410, UPort 1450, UPort 1450I 1162306a36Sopenharmony_ci * 8 ports : UPort 1610-8, UPort 1650-8 1262306a36Sopenharmony_ci * 16 ports : UPort 1610-16, UPort 1650-16 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/firmware.h> 1862306a36Sopenharmony_ci#include <linux/jiffies.h> 1962306a36Sopenharmony_ci#include <linux/serial.h> 2062306a36Sopenharmony_ci#include <linux/serial_reg.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/tty.h> 2362306a36Sopenharmony_ci#include <linux/tty_driver.h> 2462306a36Sopenharmony_ci#include <linux/tty_flip.h> 2562306a36Sopenharmony_ci#include <linux/uaccess.h> 2662306a36Sopenharmony_ci#include <linux/usb.h> 2762306a36Sopenharmony_ci#include <linux/usb/serial.h> 2862306a36Sopenharmony_ci#include <asm/unaligned.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* Definitions for the vendor ID and device ID */ 3162306a36Sopenharmony_ci#define MX_USBSERIAL_VID 0x110A 3262306a36Sopenharmony_ci#define MX_UPORT1250_PID 0x1250 3362306a36Sopenharmony_ci#define MX_UPORT1251_PID 0x1251 3462306a36Sopenharmony_ci#define MX_UPORT1410_PID 0x1410 3562306a36Sopenharmony_ci#define MX_UPORT1450_PID 0x1450 3662306a36Sopenharmony_ci#define MX_UPORT1451_PID 0x1451 3762306a36Sopenharmony_ci#define MX_UPORT1618_PID 0x1618 3862306a36Sopenharmony_ci#define MX_UPORT1658_PID 0x1658 3962306a36Sopenharmony_ci#define MX_UPORT1613_PID 0x1613 4062306a36Sopenharmony_ci#define MX_UPORT1653_PID 0x1653 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* Definitions for USB info */ 4362306a36Sopenharmony_ci#define HEADER_SIZE 4 4462306a36Sopenharmony_ci#define EVENT_LENGTH 8 4562306a36Sopenharmony_ci#define DOWN_BLOCK_SIZE 64 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Definitions for firmware info */ 4862306a36Sopenharmony_ci#define VER_ADDR_1 0x20 4962306a36Sopenharmony_ci#define VER_ADDR_2 0x24 5062306a36Sopenharmony_ci#define VER_ADDR_3 0x28 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* Definitions for USB vendor request */ 5362306a36Sopenharmony_ci#define RQ_VENDOR_NONE 0x00 5462306a36Sopenharmony_ci#define RQ_VENDOR_SET_BAUD 0x01 /* Set baud rate */ 5562306a36Sopenharmony_ci#define RQ_VENDOR_SET_LINE 0x02 /* Set line status */ 5662306a36Sopenharmony_ci#define RQ_VENDOR_SET_CHARS 0x03 /* Set Xon/Xoff chars */ 5762306a36Sopenharmony_ci#define RQ_VENDOR_SET_RTS 0x04 /* Set RTS */ 5862306a36Sopenharmony_ci#define RQ_VENDOR_SET_DTR 0x05 /* Set DTR */ 5962306a36Sopenharmony_ci#define RQ_VENDOR_SET_XONXOFF 0x06 /* Set auto Xon/Xoff */ 6062306a36Sopenharmony_ci#define RQ_VENDOR_SET_RX_HOST_EN 0x07 /* Set RX host enable */ 6162306a36Sopenharmony_ci#define RQ_VENDOR_SET_OPEN 0x08 /* Set open/close port */ 6262306a36Sopenharmony_ci#define RQ_VENDOR_PURGE 0x09 /* Purge Rx/Tx buffer */ 6362306a36Sopenharmony_ci#define RQ_VENDOR_SET_MCR 0x0A /* Set MCR register */ 6462306a36Sopenharmony_ci#define RQ_VENDOR_SET_BREAK 0x0B /* Set Break signal */ 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define RQ_VENDOR_START_FW_DOWN 0x0C /* Start firmware download */ 6762306a36Sopenharmony_ci#define RQ_VENDOR_STOP_FW_DOWN 0x0D /* Stop firmware download */ 6862306a36Sopenharmony_ci#define RQ_VENDOR_QUERY_FW_READY 0x0E /* Query if new firmware ready */ 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define RQ_VENDOR_SET_FIFO_DISABLE 0x0F /* Set fifo disable */ 7162306a36Sopenharmony_ci#define RQ_VENDOR_SET_INTERFACE 0x10 /* Set interface */ 7262306a36Sopenharmony_ci#define RQ_VENDOR_SET_HIGH_PERFOR 0x11 /* Set hi-performance */ 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define RQ_VENDOR_ERASE_BLOCK 0x12 /* Erase flash block */ 7562306a36Sopenharmony_ci#define RQ_VENDOR_WRITE_PAGE 0x13 /* Write flash page */ 7662306a36Sopenharmony_ci#define RQ_VENDOR_PREPARE_WRITE 0x14 /* Prepare write flash */ 7762306a36Sopenharmony_ci#define RQ_VENDOR_CONFIRM_WRITE 0x15 /* Confirm write flash */ 7862306a36Sopenharmony_ci#define RQ_VENDOR_LOCATE 0x16 /* Locate the device */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci#define RQ_VENDOR_START_ROM_DOWN 0x17 /* Start firmware download */ 8162306a36Sopenharmony_ci#define RQ_VENDOR_ROM_DATA 0x18 /* Rom file data */ 8262306a36Sopenharmony_ci#define RQ_VENDOR_STOP_ROM_DOWN 0x19 /* Stop firmware download */ 8362306a36Sopenharmony_ci#define RQ_VENDOR_FW_DATA 0x20 /* Firmware data */ 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define RQ_VENDOR_RESET_DEVICE 0x23 /* Try to reset the device */ 8662306a36Sopenharmony_ci#define RQ_VENDOR_QUERY_FW_CONFIG 0x24 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define RQ_VENDOR_GET_VERSION 0x81 /* Get firmware version */ 8962306a36Sopenharmony_ci#define RQ_VENDOR_GET_PAGE 0x82 /* Read flash page */ 9062306a36Sopenharmony_ci#define RQ_VENDOR_GET_ROM_PROC 0x83 /* Get ROM process state */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci#define RQ_VENDOR_GET_INQUEUE 0x84 /* Data in input buffer */ 9362306a36Sopenharmony_ci#define RQ_VENDOR_GET_OUTQUEUE 0x85 /* Data in output buffer */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define RQ_VENDOR_GET_MSR 0x86 /* Get modem status register */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* Definitions for UPort event type */ 9862306a36Sopenharmony_ci#define UPORT_EVENT_NONE 0 /* None */ 9962306a36Sopenharmony_ci#define UPORT_EVENT_TXBUF_THRESHOLD 1 /* Tx buffer threshold */ 10062306a36Sopenharmony_ci#define UPORT_EVENT_SEND_NEXT 2 /* Send next */ 10162306a36Sopenharmony_ci#define UPORT_EVENT_MSR 3 /* Modem status */ 10262306a36Sopenharmony_ci#define UPORT_EVENT_LSR 4 /* Line status */ 10362306a36Sopenharmony_ci#define UPORT_EVENT_MCR 5 /* Modem control */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/* Definitions for serial event type */ 10662306a36Sopenharmony_ci#define SERIAL_EV_CTS 0x0008 /* CTS changed state */ 10762306a36Sopenharmony_ci#define SERIAL_EV_DSR 0x0010 /* DSR changed state */ 10862306a36Sopenharmony_ci#define SERIAL_EV_RLSD 0x0020 /* RLSD changed state */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* Definitions for modem control event type */ 11162306a36Sopenharmony_ci#define SERIAL_EV_XOFF 0x40 /* XOFF received */ 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci/* Definitions for line control of communication */ 11462306a36Sopenharmony_ci#define MX_WORDLENGTH_5 5 11562306a36Sopenharmony_ci#define MX_WORDLENGTH_6 6 11662306a36Sopenharmony_ci#define MX_WORDLENGTH_7 7 11762306a36Sopenharmony_ci#define MX_WORDLENGTH_8 8 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci#define MX_PARITY_NONE 0 12062306a36Sopenharmony_ci#define MX_PARITY_ODD 1 12162306a36Sopenharmony_ci#define MX_PARITY_EVEN 2 12262306a36Sopenharmony_ci#define MX_PARITY_MARK 3 12362306a36Sopenharmony_ci#define MX_PARITY_SPACE 4 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define MX_STOP_BITS_1 0 12662306a36Sopenharmony_ci#define MX_STOP_BITS_1_5 1 12762306a36Sopenharmony_ci#define MX_STOP_BITS_2 2 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define MX_RTS_DISABLE 0x0 13062306a36Sopenharmony_ci#define MX_RTS_ENABLE 0x1 13162306a36Sopenharmony_ci#define MX_RTS_HW 0x2 13262306a36Sopenharmony_ci#define MX_RTS_NO_CHANGE 0x3 /* Flag, not valid register value*/ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define MX_INT_RS232 0 13562306a36Sopenharmony_ci#define MX_INT_2W_RS485 1 13662306a36Sopenharmony_ci#define MX_INT_RS422 2 13762306a36Sopenharmony_ci#define MX_INT_4W_RS485 3 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* Definitions for holding reason */ 14062306a36Sopenharmony_ci#define MX_WAIT_FOR_CTS 0x0001 14162306a36Sopenharmony_ci#define MX_WAIT_FOR_DSR 0x0002 14262306a36Sopenharmony_ci#define MX_WAIT_FOR_DCD 0x0004 14362306a36Sopenharmony_ci#define MX_WAIT_FOR_XON 0x0008 14462306a36Sopenharmony_ci#define MX_WAIT_FOR_START_TX 0x0010 14562306a36Sopenharmony_ci#define MX_WAIT_FOR_UNTHROTTLE 0x0020 14662306a36Sopenharmony_ci#define MX_WAIT_FOR_LOW_WATER 0x0040 14762306a36Sopenharmony_ci#define MX_WAIT_FOR_SEND_NEXT 0x0080 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#define MX_UPORT_2_PORT BIT(0) 15062306a36Sopenharmony_ci#define MX_UPORT_4_PORT BIT(1) 15162306a36Sopenharmony_ci#define MX_UPORT_8_PORT BIT(2) 15262306a36Sopenharmony_ci#define MX_UPORT_16_PORT BIT(3) 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci/* This structure holds all of the local port information */ 15562306a36Sopenharmony_cistruct mxuport_port { 15662306a36Sopenharmony_ci u8 mcr_state; /* Last MCR state */ 15762306a36Sopenharmony_ci u8 msr_state; /* Last MSR state */ 15862306a36Sopenharmony_ci struct mutex mutex; /* Protects mcr_state */ 15962306a36Sopenharmony_ci spinlock_t spinlock; /* Protects msr_state */ 16062306a36Sopenharmony_ci}; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci/* Table of devices that work with this driver */ 16362306a36Sopenharmony_cistatic const struct usb_device_id mxuport_idtable[] = { 16462306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1250_PID), 16562306a36Sopenharmony_ci .driver_info = MX_UPORT_2_PORT }, 16662306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1251_PID), 16762306a36Sopenharmony_ci .driver_info = MX_UPORT_2_PORT }, 16862306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1410_PID), 16962306a36Sopenharmony_ci .driver_info = MX_UPORT_4_PORT }, 17062306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1450_PID), 17162306a36Sopenharmony_ci .driver_info = MX_UPORT_4_PORT }, 17262306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1451_PID), 17362306a36Sopenharmony_ci .driver_info = MX_UPORT_4_PORT }, 17462306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1618_PID), 17562306a36Sopenharmony_ci .driver_info = MX_UPORT_8_PORT }, 17662306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1658_PID), 17762306a36Sopenharmony_ci .driver_info = MX_UPORT_8_PORT }, 17862306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1613_PID), 17962306a36Sopenharmony_ci .driver_info = MX_UPORT_16_PORT }, 18062306a36Sopenharmony_ci { USB_DEVICE(MX_USBSERIAL_VID, MX_UPORT1653_PID), 18162306a36Sopenharmony_ci .driver_info = MX_UPORT_16_PORT }, 18262306a36Sopenharmony_ci {} /* Terminating entry */ 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, mxuport_idtable); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci/* 18862306a36Sopenharmony_ci * Add a four byte header containing the port number and the number of 18962306a36Sopenharmony_ci * bytes of data in the message. Return the number of bytes in the 19062306a36Sopenharmony_ci * buffer. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_cistatic int mxuport_prepare_write_buffer(struct usb_serial_port *port, 19362306a36Sopenharmony_ci void *dest, size_t size) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci u8 *buf = dest; 19662306a36Sopenharmony_ci int count; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci count = kfifo_out_locked(&port->write_fifo, buf + HEADER_SIZE, 19962306a36Sopenharmony_ci size - HEADER_SIZE, 20062306a36Sopenharmony_ci &port->lock); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci put_unaligned_be16(port->port_number, buf); 20362306a36Sopenharmony_ci put_unaligned_be16(count, buf + 2); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - size %zd count %d\n", __func__, 20662306a36Sopenharmony_ci size, count); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return count + HEADER_SIZE; 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci/* Read the given buffer in from the control pipe. */ 21262306a36Sopenharmony_cistatic int mxuport_recv_ctrl_urb(struct usb_serial *serial, 21362306a36Sopenharmony_ci u8 request, u16 value, u16 index, 21462306a36Sopenharmony_ci u8 *data, size_t size) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci int status; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci status = usb_control_msg(serial->dev, 21962306a36Sopenharmony_ci usb_rcvctrlpipe(serial->dev, 0), 22062306a36Sopenharmony_ci request, 22162306a36Sopenharmony_ci (USB_DIR_IN | USB_TYPE_VENDOR | 22262306a36Sopenharmony_ci USB_RECIP_DEVICE), value, index, 22362306a36Sopenharmony_ci data, size, 22462306a36Sopenharmony_ci USB_CTRL_GET_TIMEOUT); 22562306a36Sopenharmony_ci if (status < 0) { 22662306a36Sopenharmony_ci dev_err(&serial->interface->dev, 22762306a36Sopenharmony_ci "%s - usb_control_msg failed (%d)\n", 22862306a36Sopenharmony_ci __func__, status); 22962306a36Sopenharmony_ci return status; 23062306a36Sopenharmony_ci } 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (status != size) { 23362306a36Sopenharmony_ci dev_err(&serial->interface->dev, 23462306a36Sopenharmony_ci "%s - short read (%d / %zd)\n", 23562306a36Sopenharmony_ci __func__, status, size); 23662306a36Sopenharmony_ci return -EIO; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return status; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci/* Write the given buffer out to the control pipe. */ 24362306a36Sopenharmony_cistatic int mxuport_send_ctrl_data_urb(struct usb_serial *serial, 24462306a36Sopenharmony_ci u8 request, 24562306a36Sopenharmony_ci u16 value, u16 index, 24662306a36Sopenharmony_ci u8 *data, size_t size) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci int status; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci status = usb_control_msg(serial->dev, 25162306a36Sopenharmony_ci usb_sndctrlpipe(serial->dev, 0), 25262306a36Sopenharmony_ci request, 25362306a36Sopenharmony_ci (USB_DIR_OUT | USB_TYPE_VENDOR | 25462306a36Sopenharmony_ci USB_RECIP_DEVICE), value, index, 25562306a36Sopenharmony_ci data, size, 25662306a36Sopenharmony_ci USB_CTRL_SET_TIMEOUT); 25762306a36Sopenharmony_ci if (status < 0) { 25862306a36Sopenharmony_ci dev_err(&serial->interface->dev, 25962306a36Sopenharmony_ci "%s - usb_control_msg failed (%d)\n", 26062306a36Sopenharmony_ci __func__, status); 26162306a36Sopenharmony_ci return status; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* Send a vendor request without any data */ 26862306a36Sopenharmony_cistatic int mxuport_send_ctrl_urb(struct usb_serial *serial, 26962306a36Sopenharmony_ci u8 request, u16 value, u16 index) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci return mxuport_send_ctrl_data_urb(serial, request, value, index, 27262306a36Sopenharmony_ci NULL, 0); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci/* 27662306a36Sopenharmony_ci * mxuport_throttle - throttle function of driver 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * This function is called by the tty driver when it wants to stop the 27962306a36Sopenharmony_ci * data being read from the port. Since all the data comes over one 28062306a36Sopenharmony_ci * bulk in endpoint, we cannot stop submitting urbs by setting 28162306a36Sopenharmony_ci * port->throttle. Instead tell the device to stop sending us data for 28262306a36Sopenharmony_ci * the port. 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic void mxuport_throttle(struct tty_struct *tty) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 28762306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s\n", __func__); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, 29262306a36Sopenharmony_ci 0, port->port_number); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* 29662306a36Sopenharmony_ci * mxuport_unthrottle - unthrottle function of driver 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * This function is called by the tty driver when it wants to resume 29962306a36Sopenharmony_ci * the data being read from the port. Tell the device it can resume 30062306a36Sopenharmony_ci * sending us received data from the port. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic void mxuport_unthrottle(struct tty_struct *tty) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 30662306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s\n", __func__); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, 31162306a36Sopenharmony_ci 1, port->port_number); 31262306a36Sopenharmony_ci} 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci/* 31562306a36Sopenharmony_ci * Processes one chunk of data received for a port. Mostly a copy of 31662306a36Sopenharmony_ci * usb_serial_generic_process_read_urb(). 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_cistatic void mxuport_process_read_urb_data(struct usb_serial_port *port, 31962306a36Sopenharmony_ci char *data, int size) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int i; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (port->sysrq) { 32462306a36Sopenharmony_ci for (i = 0; i < size; i++, data++) { 32562306a36Sopenharmony_ci if (!usb_serial_handle_sysrq_char(port, *data)) 32662306a36Sopenharmony_ci tty_insert_flip_char(&port->port, *data, 32762306a36Sopenharmony_ci TTY_NORMAL); 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci tty_insert_flip_string(&port->port, data, size); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci tty_flip_buffer_push(&port->port); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic void mxuport_msr_event(struct usb_serial_port *port, u8 buf[4]) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct mxuport_port *mxport = usb_get_serial_port_data(port); 33862306a36Sopenharmony_ci u8 rcv_msr_hold = buf[2] & 0xF0; 33962306a36Sopenharmony_ci u16 rcv_msr_event = get_unaligned_be16(buf); 34062306a36Sopenharmony_ci unsigned long flags; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (rcv_msr_event == 0) 34362306a36Sopenharmony_ci return; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Update MSR status */ 34662306a36Sopenharmony_ci spin_lock_irqsave(&mxport->spinlock, flags); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - current MSR status = 0x%x\n", 34962306a36Sopenharmony_ci __func__, mxport->msr_state); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (rcv_msr_hold & UART_MSR_CTS) { 35262306a36Sopenharmony_ci mxport->msr_state |= UART_MSR_CTS; 35362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - CTS high\n", __func__); 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci mxport->msr_state &= ~UART_MSR_CTS; 35662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - CTS low\n", __func__); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (rcv_msr_hold & UART_MSR_DSR) { 36062306a36Sopenharmony_ci mxport->msr_state |= UART_MSR_DSR; 36162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - DSR high\n", __func__); 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci mxport->msr_state &= ~UART_MSR_DSR; 36462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - DSR low\n", __func__); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (rcv_msr_hold & UART_MSR_DCD) { 36862306a36Sopenharmony_ci mxport->msr_state |= UART_MSR_DCD; 36962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - DCD high\n", __func__); 37062306a36Sopenharmony_ci } else { 37162306a36Sopenharmony_ci mxport->msr_state &= ~UART_MSR_DCD; 37262306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - DCD low\n", __func__); 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci spin_unlock_irqrestore(&mxport->spinlock, flags); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (rcv_msr_event & 37762306a36Sopenharmony_ci (SERIAL_EV_CTS | SERIAL_EV_DSR | SERIAL_EV_RLSD)) { 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (rcv_msr_event & SERIAL_EV_CTS) { 38062306a36Sopenharmony_ci port->icount.cts++; 38162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - CTS change\n", __func__); 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (rcv_msr_event & SERIAL_EV_DSR) { 38562306a36Sopenharmony_ci port->icount.dsr++; 38662306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - DSR change\n", __func__); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (rcv_msr_event & SERIAL_EV_RLSD) { 39062306a36Sopenharmony_ci port->icount.dcd++; 39162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - DCD change\n", __func__); 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci wake_up_interruptible(&port->port.delta_msr_wait); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic void mxuport_lsr_event(struct usb_serial_port *port, u8 buf[4]) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci u8 lsr_event = buf[2]; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (lsr_event & UART_LSR_BI) { 40262306a36Sopenharmony_ci port->icount.brk++; 40362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - break error\n", __func__); 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (lsr_event & UART_LSR_FE) { 40762306a36Sopenharmony_ci port->icount.frame++; 40862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - frame error\n", __func__); 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (lsr_event & UART_LSR_PE) { 41262306a36Sopenharmony_ci port->icount.parity++; 41362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - parity error\n", __func__); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (lsr_event & UART_LSR_OE) { 41762306a36Sopenharmony_ci port->icount.overrun++; 41862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - overrun error\n", __func__); 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci/* 42362306a36Sopenharmony_ci * When something interesting happens, modem control lines XON/XOFF 42462306a36Sopenharmony_ci * etc, the device sends an event. Process these events. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_cistatic void mxuport_process_read_urb_event(struct usb_serial_port *port, 42762306a36Sopenharmony_ci u8 buf[4], u32 event) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - receive event : %04x\n", __func__, event); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci switch (event) { 43262306a36Sopenharmony_ci case UPORT_EVENT_SEND_NEXT: 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * Sent as part of the flow control on device buffers. 43562306a36Sopenharmony_ci * Not currently used. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci case UPORT_EVENT_MSR: 43962306a36Sopenharmony_ci mxuport_msr_event(port, buf); 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci case UPORT_EVENT_LSR: 44262306a36Sopenharmony_ci mxuport_lsr_event(port, buf); 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case UPORT_EVENT_MCR: 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * Event to indicate a change in XON/XOFF from the 44762306a36Sopenharmony_ci * peer. Currently not used. We just continue 44862306a36Sopenharmony_ci * sending the device data and it will buffer it if 44962306a36Sopenharmony_ci * needed. This event could be used for flow control 45062306a36Sopenharmony_ci * between the host and the device. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_ci break; 45362306a36Sopenharmony_ci default: 45462306a36Sopenharmony_ci dev_dbg(&port->dev, "Unexpected event\n"); 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci/* 46062306a36Sopenharmony_ci * One URB can contain data for multiple ports. Demultiplex the data, 46162306a36Sopenharmony_ci * checking the port exists, is opened and the message is valid. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_cistatic void mxuport_process_read_urb_demux_data(struct urb *urb) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 46662306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 46762306a36Sopenharmony_ci u8 *data = urb->transfer_buffer; 46862306a36Sopenharmony_ci u8 *end = data + urb->actual_length; 46962306a36Sopenharmony_ci struct usb_serial_port *demux_port; 47062306a36Sopenharmony_ci u8 *ch; 47162306a36Sopenharmony_ci u16 rcv_port; 47262306a36Sopenharmony_ci u16 rcv_len; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci while (data < end) { 47562306a36Sopenharmony_ci if (data + HEADER_SIZE > end) { 47662306a36Sopenharmony_ci dev_warn(&port->dev, "%s - message with short header\n", 47762306a36Sopenharmony_ci __func__); 47862306a36Sopenharmony_ci return; 47962306a36Sopenharmony_ci } 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci rcv_port = get_unaligned_be16(data); 48262306a36Sopenharmony_ci if (rcv_port >= serial->num_ports) { 48362306a36Sopenharmony_ci dev_warn(&port->dev, "%s - message for invalid port\n", 48462306a36Sopenharmony_ci __func__); 48562306a36Sopenharmony_ci return; 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci demux_port = serial->port[rcv_port]; 48962306a36Sopenharmony_ci rcv_len = get_unaligned_be16(data + 2); 49062306a36Sopenharmony_ci if (!rcv_len || data + HEADER_SIZE + rcv_len > end) { 49162306a36Sopenharmony_ci dev_warn(&port->dev, "%s - short data\n", __func__); 49262306a36Sopenharmony_ci return; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (tty_port_initialized(&demux_port->port)) { 49662306a36Sopenharmony_ci ch = data + HEADER_SIZE; 49762306a36Sopenharmony_ci mxuport_process_read_urb_data(demux_port, ch, rcv_len); 49862306a36Sopenharmony_ci } else { 49962306a36Sopenharmony_ci dev_dbg(&demux_port->dev, "%s - data for closed port\n", 50062306a36Sopenharmony_ci __func__); 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci data += HEADER_SIZE + rcv_len; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* 50762306a36Sopenharmony_ci * One URB can contain events for multiple ports. Demultiplex the event, 50862306a36Sopenharmony_ci * checking the port exists, and is opened. 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_cistatic void mxuport_process_read_urb_demux_event(struct urb *urb) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 51362306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 51462306a36Sopenharmony_ci u8 *data = urb->transfer_buffer; 51562306a36Sopenharmony_ci u8 *end = data + urb->actual_length; 51662306a36Sopenharmony_ci struct usb_serial_port *demux_port; 51762306a36Sopenharmony_ci u8 *ch; 51862306a36Sopenharmony_ci u16 rcv_port; 51962306a36Sopenharmony_ci u16 rcv_event; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci while (data < end) { 52262306a36Sopenharmony_ci if (data + EVENT_LENGTH > end) { 52362306a36Sopenharmony_ci dev_warn(&port->dev, "%s - message with short event\n", 52462306a36Sopenharmony_ci __func__); 52562306a36Sopenharmony_ci return; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci rcv_port = get_unaligned_be16(data); 52962306a36Sopenharmony_ci if (rcv_port >= serial->num_ports) { 53062306a36Sopenharmony_ci dev_warn(&port->dev, "%s - message for invalid port\n", 53162306a36Sopenharmony_ci __func__); 53262306a36Sopenharmony_ci return; 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci demux_port = serial->port[rcv_port]; 53662306a36Sopenharmony_ci if (tty_port_initialized(&demux_port->port)) { 53762306a36Sopenharmony_ci ch = data + HEADER_SIZE; 53862306a36Sopenharmony_ci rcv_event = get_unaligned_be16(data + 2); 53962306a36Sopenharmony_ci mxuport_process_read_urb_event(demux_port, ch, 54062306a36Sopenharmony_ci rcv_event); 54162306a36Sopenharmony_ci } else { 54262306a36Sopenharmony_ci dev_dbg(&demux_port->dev, 54362306a36Sopenharmony_ci "%s - event for closed port\n", __func__); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci data += EVENT_LENGTH; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci/* 55062306a36Sopenharmony_ci * This is called when we have received data on the bulk in 55162306a36Sopenharmony_ci * endpoint. Depending on which port it was received on, it can 55262306a36Sopenharmony_ci * contain serial data or events. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_cistatic void mxuport_process_read_urb(struct urb *urb) 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct usb_serial_port *port = urb->context; 55762306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (port == serial->port[0]) 56062306a36Sopenharmony_ci mxuport_process_read_urb_demux_data(urb); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (port == serial->port[1]) 56362306a36Sopenharmony_ci mxuport_process_read_urb_demux_event(urb); 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci/* 56762306a36Sopenharmony_ci * Ask the device how many bytes it has queued to be sent out. If 56862306a36Sopenharmony_ci * there are none, return true. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_cistatic bool mxuport_tx_empty(struct usb_serial_port *port) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 57362306a36Sopenharmony_ci bool is_empty = true; 57462306a36Sopenharmony_ci u32 txlen; 57562306a36Sopenharmony_ci u8 *len_buf; 57662306a36Sopenharmony_ci int err; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci len_buf = kzalloc(4, GFP_KERNEL); 57962306a36Sopenharmony_ci if (!len_buf) 58062306a36Sopenharmony_ci goto out; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_OUTQUEUE, 0, 58362306a36Sopenharmony_ci port->port_number, len_buf, 4); 58462306a36Sopenharmony_ci if (err < 0) 58562306a36Sopenharmony_ci goto out; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci txlen = get_unaligned_be32(len_buf); 58862306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - tx len = %u\n", __func__, txlen); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (txlen != 0) 59162306a36Sopenharmony_ci is_empty = false; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ciout: 59462306a36Sopenharmony_ci kfree(len_buf); 59562306a36Sopenharmony_ci return is_empty; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int mxuport_set_mcr(struct usb_serial_port *port, u8 mcr_state) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 60162306a36Sopenharmony_ci int err; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - %02x\n", __func__, mcr_state); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_MCR, 60662306a36Sopenharmony_ci mcr_state, port->port_number); 60762306a36Sopenharmony_ci if (err) 60862306a36Sopenharmony_ci dev_err(&port->dev, "%s - failed to change MCR\n", __func__); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return err; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic int mxuport_set_dtr(struct usb_serial_port *port, int on) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci struct mxuport_port *mxport = usb_get_serial_port_data(port); 61662306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 61762306a36Sopenharmony_ci int err; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci mutex_lock(&mxport->mutex); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_DTR, 62262306a36Sopenharmony_ci !!on, port->port_number); 62362306a36Sopenharmony_ci if (!err) { 62462306a36Sopenharmony_ci if (on) 62562306a36Sopenharmony_ci mxport->mcr_state |= UART_MCR_DTR; 62662306a36Sopenharmony_ci else 62762306a36Sopenharmony_ci mxport->mcr_state &= ~UART_MCR_DTR; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci mutex_unlock(&mxport->mutex); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci return err; 63362306a36Sopenharmony_ci} 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic int mxuport_set_rts(struct usb_serial_port *port, u8 state) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct mxuport_port *mxport = usb_get_serial_port_data(port); 63862306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 63962306a36Sopenharmony_ci int err; 64062306a36Sopenharmony_ci u8 mcr_state; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci mutex_lock(&mxport->mutex); 64362306a36Sopenharmony_ci mcr_state = mxport->mcr_state; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci switch (state) { 64662306a36Sopenharmony_ci case MX_RTS_DISABLE: 64762306a36Sopenharmony_ci mcr_state &= ~UART_MCR_RTS; 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci case MX_RTS_ENABLE: 65062306a36Sopenharmony_ci mcr_state |= UART_MCR_RTS; 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci case MX_RTS_HW: 65362306a36Sopenharmony_ci /* 65462306a36Sopenharmony_ci * Do not update mxport->mcr_state when doing hardware 65562306a36Sopenharmony_ci * flow control. 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_ci break; 65862306a36Sopenharmony_ci default: 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * Should not happen, but somebody might try passing 66162306a36Sopenharmony_ci * MX_RTS_NO_CHANGE, which is not valid. 66262306a36Sopenharmony_ci */ 66362306a36Sopenharmony_ci err = -EINVAL; 66462306a36Sopenharmony_ci goto out; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RTS, 66762306a36Sopenharmony_ci state, port->port_number); 66862306a36Sopenharmony_ci if (!err) 66962306a36Sopenharmony_ci mxport->mcr_state = mcr_state; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ciout: 67262306a36Sopenharmony_ci mutex_unlock(&mxport->mutex); 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci return err; 67562306a36Sopenharmony_ci} 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_cistatic void mxuport_dtr_rts(struct usb_serial_port *port, int on) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct mxuport_port *mxport = usb_get_serial_port_data(port); 68062306a36Sopenharmony_ci u8 mcr_state; 68162306a36Sopenharmony_ci int err; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci mutex_lock(&mxport->mutex); 68462306a36Sopenharmony_ci mcr_state = mxport->mcr_state; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci if (on) 68762306a36Sopenharmony_ci mcr_state |= (UART_MCR_RTS | UART_MCR_DTR); 68862306a36Sopenharmony_ci else 68962306a36Sopenharmony_ci mcr_state &= ~(UART_MCR_RTS | UART_MCR_DTR); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci err = mxuport_set_mcr(port, mcr_state); 69262306a36Sopenharmony_ci if (!err) 69362306a36Sopenharmony_ci mxport->mcr_state = mcr_state; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci mutex_unlock(&mxport->mutex); 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int mxuport_tiocmset(struct tty_struct *tty, unsigned int set, 69962306a36Sopenharmony_ci unsigned int clear) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 70262306a36Sopenharmony_ci struct mxuport_port *mxport = usb_get_serial_port_data(port); 70362306a36Sopenharmony_ci int err; 70462306a36Sopenharmony_ci u8 mcr_state; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci mutex_lock(&mxport->mutex); 70762306a36Sopenharmony_ci mcr_state = mxport->mcr_state; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (set & TIOCM_RTS) 71062306a36Sopenharmony_ci mcr_state |= UART_MCR_RTS; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (set & TIOCM_DTR) 71362306a36Sopenharmony_ci mcr_state |= UART_MCR_DTR; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (clear & TIOCM_RTS) 71662306a36Sopenharmony_ci mcr_state &= ~UART_MCR_RTS; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (clear & TIOCM_DTR) 71962306a36Sopenharmony_ci mcr_state &= ~UART_MCR_DTR; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci err = mxuport_set_mcr(port, mcr_state); 72262306a36Sopenharmony_ci if (!err) 72362306a36Sopenharmony_ci mxport->mcr_state = mcr_state; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci mutex_unlock(&mxport->mutex); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return err; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic int mxuport_tiocmget(struct tty_struct *tty) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct mxuport_port *mxport; 73362306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 73462306a36Sopenharmony_ci unsigned int result; 73562306a36Sopenharmony_ci unsigned long flags; 73662306a36Sopenharmony_ci unsigned int msr; 73762306a36Sopenharmony_ci unsigned int mcr; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci mxport = usb_get_serial_port_data(port); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci mutex_lock(&mxport->mutex); 74262306a36Sopenharmony_ci spin_lock_irqsave(&mxport->spinlock, flags); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci msr = mxport->msr_state; 74562306a36Sopenharmony_ci mcr = mxport->mcr_state; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci spin_unlock_irqrestore(&mxport->spinlock, flags); 74862306a36Sopenharmony_ci mutex_unlock(&mxport->mutex); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci result = (((mcr & UART_MCR_DTR) ? TIOCM_DTR : 0) | /* 0x002 */ 75162306a36Sopenharmony_ci ((mcr & UART_MCR_RTS) ? TIOCM_RTS : 0) | /* 0x004 */ 75262306a36Sopenharmony_ci ((msr & UART_MSR_CTS) ? TIOCM_CTS : 0) | /* 0x020 */ 75362306a36Sopenharmony_ci ((msr & UART_MSR_DCD) ? TIOCM_CAR : 0) | /* 0x040 */ 75462306a36Sopenharmony_ci ((msr & UART_MSR_RI) ? TIOCM_RI : 0) | /* 0x080 */ 75562306a36Sopenharmony_ci ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0)); /* 0x100 */ 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - 0x%04x\n", __func__, result); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return result; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int mxuport_set_termios_flow(struct tty_struct *tty, 76362306a36Sopenharmony_ci const struct ktermios *old_termios, 76462306a36Sopenharmony_ci struct usb_serial_port *port, 76562306a36Sopenharmony_ci struct usb_serial *serial) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci u8 xon = START_CHAR(tty); 76862306a36Sopenharmony_ci u8 xoff = STOP_CHAR(tty); 76962306a36Sopenharmony_ci int enable; 77062306a36Sopenharmony_ci int err; 77162306a36Sopenharmony_ci u8 *buf; 77262306a36Sopenharmony_ci u8 rts; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci buf = kmalloc(2, GFP_KERNEL); 77562306a36Sopenharmony_ci if (!buf) 77662306a36Sopenharmony_ci return -ENOMEM; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci /* S/W flow control settings */ 77962306a36Sopenharmony_ci if (I_IXOFF(tty) || I_IXON(tty)) { 78062306a36Sopenharmony_ci enable = 1; 78162306a36Sopenharmony_ci buf[0] = xon; 78262306a36Sopenharmony_ci buf[1] = xoff; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_CHARS, 78562306a36Sopenharmony_ci 0, port->port_number, 78662306a36Sopenharmony_ci buf, 2); 78762306a36Sopenharmony_ci if (err) 78862306a36Sopenharmony_ci goto out; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - XON = 0x%02x, XOFF = 0x%02x\n", 79162306a36Sopenharmony_ci __func__, xon, xoff); 79262306a36Sopenharmony_ci } else { 79362306a36Sopenharmony_ci enable = 0; 79462306a36Sopenharmony_ci } 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_XONXOFF, 79762306a36Sopenharmony_ci enable, port->port_number); 79862306a36Sopenharmony_ci if (err) 79962306a36Sopenharmony_ci goto out; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci rts = MX_RTS_NO_CHANGE; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* H/W flow control settings */ 80462306a36Sopenharmony_ci if (!old_termios || 80562306a36Sopenharmony_ci C_CRTSCTS(tty) != (old_termios->c_cflag & CRTSCTS)) { 80662306a36Sopenharmony_ci if (C_CRTSCTS(tty)) 80762306a36Sopenharmony_ci rts = MX_RTS_HW; 80862306a36Sopenharmony_ci else 80962306a36Sopenharmony_ci rts = MX_RTS_ENABLE; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (C_BAUD(tty)) { 81362306a36Sopenharmony_ci if (old_termios && (old_termios->c_cflag & CBAUD) == B0) { 81462306a36Sopenharmony_ci /* Raise DTR and RTS */ 81562306a36Sopenharmony_ci if (C_CRTSCTS(tty)) 81662306a36Sopenharmony_ci rts = MX_RTS_HW; 81762306a36Sopenharmony_ci else 81862306a36Sopenharmony_ci rts = MX_RTS_ENABLE; 81962306a36Sopenharmony_ci mxuport_set_dtr(port, 1); 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci } else { 82262306a36Sopenharmony_ci /* Drop DTR and RTS */ 82362306a36Sopenharmony_ci rts = MX_RTS_DISABLE; 82462306a36Sopenharmony_ci mxuport_set_dtr(port, 0); 82562306a36Sopenharmony_ci } 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (rts != MX_RTS_NO_CHANGE) 82862306a36Sopenharmony_ci err = mxuport_set_rts(port, rts); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ciout: 83162306a36Sopenharmony_ci kfree(buf); 83262306a36Sopenharmony_ci return err; 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic void mxuport_set_termios(struct tty_struct *tty, 83662306a36Sopenharmony_ci struct usb_serial_port *port, 83762306a36Sopenharmony_ci const struct ktermios *old_termios) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 84062306a36Sopenharmony_ci u8 *buf; 84162306a36Sopenharmony_ci u8 data_bits; 84262306a36Sopenharmony_ci u8 stop_bits; 84362306a36Sopenharmony_ci u8 parity; 84462306a36Sopenharmony_ci int baud; 84562306a36Sopenharmony_ci int err; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci if (old_termios && 84862306a36Sopenharmony_ci !tty_termios_hw_change(&tty->termios, old_termios) && 84962306a36Sopenharmony_ci tty->termios.c_iflag == old_termios->c_iflag) { 85062306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - nothing to change\n", __func__); 85162306a36Sopenharmony_ci return; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci buf = kmalloc(4, GFP_KERNEL); 85562306a36Sopenharmony_ci if (!buf) 85662306a36Sopenharmony_ci return; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* Set data bit of termios */ 85962306a36Sopenharmony_ci switch (C_CSIZE(tty)) { 86062306a36Sopenharmony_ci case CS5: 86162306a36Sopenharmony_ci data_bits = MX_WORDLENGTH_5; 86262306a36Sopenharmony_ci break; 86362306a36Sopenharmony_ci case CS6: 86462306a36Sopenharmony_ci data_bits = MX_WORDLENGTH_6; 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci case CS7: 86762306a36Sopenharmony_ci data_bits = MX_WORDLENGTH_7; 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci case CS8: 87062306a36Sopenharmony_ci default: 87162306a36Sopenharmony_ci data_bits = MX_WORDLENGTH_8; 87262306a36Sopenharmony_ci break; 87362306a36Sopenharmony_ci } 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci /* Set parity of termios */ 87662306a36Sopenharmony_ci if (C_PARENB(tty)) { 87762306a36Sopenharmony_ci if (C_CMSPAR(tty)) { 87862306a36Sopenharmony_ci if (C_PARODD(tty)) 87962306a36Sopenharmony_ci parity = MX_PARITY_MARK; 88062306a36Sopenharmony_ci else 88162306a36Sopenharmony_ci parity = MX_PARITY_SPACE; 88262306a36Sopenharmony_ci } else { 88362306a36Sopenharmony_ci if (C_PARODD(tty)) 88462306a36Sopenharmony_ci parity = MX_PARITY_ODD; 88562306a36Sopenharmony_ci else 88662306a36Sopenharmony_ci parity = MX_PARITY_EVEN; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci } else { 88962306a36Sopenharmony_ci parity = MX_PARITY_NONE; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci /* Set stop bit of termios */ 89362306a36Sopenharmony_ci if (C_CSTOPB(tty)) 89462306a36Sopenharmony_ci stop_bits = MX_STOP_BITS_2; 89562306a36Sopenharmony_ci else 89662306a36Sopenharmony_ci stop_bits = MX_STOP_BITS_1; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci buf[0] = data_bits; 89962306a36Sopenharmony_ci buf[1] = parity; 90062306a36Sopenharmony_ci buf[2] = stop_bits; 90162306a36Sopenharmony_ci buf[3] = 0; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_LINE, 90462306a36Sopenharmony_ci 0, port->port_number, buf, 4); 90562306a36Sopenharmony_ci if (err) 90662306a36Sopenharmony_ci goto out; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci err = mxuport_set_termios_flow(tty, old_termios, port, serial); 90962306a36Sopenharmony_ci if (err) 91062306a36Sopenharmony_ci goto out; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci baud = tty_get_baud_rate(tty); 91362306a36Sopenharmony_ci if (!baud) 91462306a36Sopenharmony_ci baud = 9600; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci /* Note: Little Endian */ 91762306a36Sopenharmony_ci put_unaligned_le32(baud, buf); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_SET_BAUD, 92062306a36Sopenharmony_ci 0, port->port_number, 92162306a36Sopenharmony_ci buf, 4); 92262306a36Sopenharmony_ci if (err) 92362306a36Sopenharmony_ci goto out; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci dev_dbg(&port->dev, "baud_rate : %d\n", baud); 92662306a36Sopenharmony_ci dev_dbg(&port->dev, "data_bits : %d\n", data_bits); 92762306a36Sopenharmony_ci dev_dbg(&port->dev, "parity : %d\n", parity); 92862306a36Sopenharmony_ci dev_dbg(&port->dev, "stop_bits : %d\n", stop_bits); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ciout: 93162306a36Sopenharmony_ci kfree(buf); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci/* 93562306a36Sopenharmony_ci * Determine how many ports this device has dynamically. It will be 93662306a36Sopenharmony_ci * called after the probe() callback is called, but before attach(). 93762306a36Sopenharmony_ci */ 93862306a36Sopenharmony_cistatic int mxuport_calc_num_ports(struct usb_serial *serial, 93962306a36Sopenharmony_ci struct usb_serial_endpoints *epds) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci unsigned long features = (unsigned long)usb_get_serial_data(serial); 94262306a36Sopenharmony_ci int num_ports; 94362306a36Sopenharmony_ci int i; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (features & MX_UPORT_2_PORT) { 94662306a36Sopenharmony_ci num_ports = 2; 94762306a36Sopenharmony_ci } else if (features & MX_UPORT_4_PORT) { 94862306a36Sopenharmony_ci num_ports = 4; 94962306a36Sopenharmony_ci } else if (features & MX_UPORT_8_PORT) { 95062306a36Sopenharmony_ci num_ports = 8; 95162306a36Sopenharmony_ci } else if (features & MX_UPORT_16_PORT) { 95262306a36Sopenharmony_ci num_ports = 16; 95362306a36Sopenharmony_ci } else { 95462306a36Sopenharmony_ci dev_warn(&serial->interface->dev, 95562306a36Sopenharmony_ci "unknown device, assuming two ports\n"); 95662306a36Sopenharmony_ci num_ports = 2; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci /* 96062306a36Sopenharmony_ci * Setup bulk-out endpoint multiplexing. All ports share the same 96162306a36Sopenharmony_ci * bulk-out endpoint. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci for (i = 1; i < num_ports; ++i) 96662306a36Sopenharmony_ci epds->bulk_out[i] = epds->bulk_out[0]; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci epds->num_bulk_out = num_ports; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return num_ports; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci/* Get the version of the firmware currently running. */ 97462306a36Sopenharmony_cistatic int mxuport_get_fw_version(struct usb_serial *serial, u32 *version) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci u8 *ver_buf; 97762306a36Sopenharmony_ci int err; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci ver_buf = kzalloc(4, GFP_KERNEL); 98062306a36Sopenharmony_ci if (!ver_buf) 98162306a36Sopenharmony_ci return -ENOMEM; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci /* Get firmware version from SDRAM */ 98462306a36Sopenharmony_ci err = mxuport_recv_ctrl_urb(serial, RQ_VENDOR_GET_VERSION, 0, 0, 98562306a36Sopenharmony_ci ver_buf, 4); 98662306a36Sopenharmony_ci if (err != 4) { 98762306a36Sopenharmony_ci err = -EIO; 98862306a36Sopenharmony_ci goto out; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci *version = (ver_buf[0] << 16) | (ver_buf[1] << 8) | ver_buf[2]; 99262306a36Sopenharmony_ci err = 0; 99362306a36Sopenharmony_ciout: 99462306a36Sopenharmony_ci kfree(ver_buf); 99562306a36Sopenharmony_ci return err; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/* Given a firmware blob, download it to the device. */ 99962306a36Sopenharmony_cistatic int mxuport_download_fw(struct usb_serial *serial, 100062306a36Sopenharmony_ci const struct firmware *fw_p) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci u8 *fw_buf; 100362306a36Sopenharmony_ci size_t txlen; 100462306a36Sopenharmony_ci size_t fwidx; 100562306a36Sopenharmony_ci int err; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci fw_buf = kmalloc(DOWN_BLOCK_SIZE, GFP_KERNEL); 100862306a36Sopenharmony_ci if (!fw_buf) 100962306a36Sopenharmony_ci return -ENOMEM; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci dev_dbg(&serial->interface->dev, "Starting firmware download...\n"); 101262306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_START_FW_DOWN, 0, 0); 101362306a36Sopenharmony_ci if (err) 101462306a36Sopenharmony_ci goto out; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci fwidx = 0; 101762306a36Sopenharmony_ci do { 101862306a36Sopenharmony_ci txlen = min_t(size_t, (fw_p->size - fwidx), DOWN_BLOCK_SIZE); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci memcpy(fw_buf, &fw_p->data[fwidx], txlen); 102162306a36Sopenharmony_ci err = mxuport_send_ctrl_data_urb(serial, RQ_VENDOR_FW_DATA, 102262306a36Sopenharmony_ci 0, 0, fw_buf, txlen); 102362306a36Sopenharmony_ci if (err) { 102462306a36Sopenharmony_ci mxuport_send_ctrl_urb(serial, RQ_VENDOR_STOP_FW_DOWN, 102562306a36Sopenharmony_ci 0, 0); 102662306a36Sopenharmony_ci goto out; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci fwidx += txlen; 103062306a36Sopenharmony_ci usleep_range(1000, 2000); 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci } while (fwidx < fw_p->size); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci msleep(1000); 103562306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_STOP_FW_DOWN, 0, 0); 103662306a36Sopenharmony_ci if (err) 103762306a36Sopenharmony_ci goto out; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci msleep(1000); 104062306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_QUERY_FW_READY, 0, 0); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ciout: 104362306a36Sopenharmony_ci kfree(fw_buf); 104462306a36Sopenharmony_ci return err; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic int mxuport_probe(struct usb_serial *serial, 104862306a36Sopenharmony_ci const struct usb_device_id *id) 104962306a36Sopenharmony_ci{ 105062306a36Sopenharmony_ci u16 productid = le16_to_cpu(serial->dev->descriptor.idProduct); 105162306a36Sopenharmony_ci const struct firmware *fw_p = NULL; 105262306a36Sopenharmony_ci u32 version; 105362306a36Sopenharmony_ci int local_ver; 105462306a36Sopenharmony_ci char buf[32]; 105562306a36Sopenharmony_ci int err; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci /* Load our firmware */ 105862306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_QUERY_FW_CONFIG, 0, 0); 105962306a36Sopenharmony_ci if (err) { 106062306a36Sopenharmony_ci mxuport_send_ctrl_urb(serial, RQ_VENDOR_RESET_DEVICE, 0, 0); 106162306a36Sopenharmony_ci return err; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci err = mxuport_get_fw_version(serial, &version); 106562306a36Sopenharmony_ci if (err < 0) 106662306a36Sopenharmony_ci return err; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci dev_dbg(&serial->interface->dev, "Device firmware version v%x.%x.%x\n", 106962306a36Sopenharmony_ci (version & 0xff0000) >> 16, 107062306a36Sopenharmony_ci (version & 0xff00) >> 8, 107162306a36Sopenharmony_ci (version & 0xff)); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci snprintf(buf, sizeof(buf) - 1, "moxa/moxa-%04x.fw", productid); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci err = request_firmware(&fw_p, buf, &serial->interface->dev); 107662306a36Sopenharmony_ci if (err) { 107762306a36Sopenharmony_ci dev_warn(&serial->interface->dev, "Firmware %s not found\n", 107862306a36Sopenharmony_ci buf); 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci /* Use the firmware already in the device */ 108162306a36Sopenharmony_ci err = 0; 108262306a36Sopenharmony_ci } else { 108362306a36Sopenharmony_ci local_ver = ((fw_p->data[VER_ADDR_1] << 16) | 108462306a36Sopenharmony_ci (fw_p->data[VER_ADDR_2] << 8) | 108562306a36Sopenharmony_ci fw_p->data[VER_ADDR_3]); 108662306a36Sopenharmony_ci dev_dbg(&serial->interface->dev, 108762306a36Sopenharmony_ci "Available firmware version v%x.%x.%x\n", 108862306a36Sopenharmony_ci fw_p->data[VER_ADDR_1], fw_p->data[VER_ADDR_2], 108962306a36Sopenharmony_ci fw_p->data[VER_ADDR_3]); 109062306a36Sopenharmony_ci if (local_ver > version) { 109162306a36Sopenharmony_ci err = mxuport_download_fw(serial, fw_p); 109262306a36Sopenharmony_ci if (err) 109362306a36Sopenharmony_ci goto out; 109462306a36Sopenharmony_ci err = mxuport_get_fw_version(serial, &version); 109562306a36Sopenharmony_ci if (err < 0) 109662306a36Sopenharmony_ci goto out; 109762306a36Sopenharmony_ci } 109862306a36Sopenharmony_ci } 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci dev_info(&serial->interface->dev, 110162306a36Sopenharmony_ci "Using device firmware version v%x.%x.%x\n", 110262306a36Sopenharmony_ci (version & 0xff0000) >> 16, 110362306a36Sopenharmony_ci (version & 0xff00) >> 8, 110462306a36Sopenharmony_ci (version & 0xff)); 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* 110762306a36Sopenharmony_ci * Contains the features of this hardware. Store away for 110862306a36Sopenharmony_ci * later use, eg, number of ports. 110962306a36Sopenharmony_ci */ 111062306a36Sopenharmony_ci usb_set_serial_data(serial, (void *)id->driver_info); 111162306a36Sopenharmony_ciout: 111262306a36Sopenharmony_ci if (fw_p) 111362306a36Sopenharmony_ci release_firmware(fw_p); 111462306a36Sopenharmony_ci return err; 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int mxuport_port_probe(struct usb_serial_port *port) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 112162306a36Sopenharmony_ci struct mxuport_port *mxport; 112262306a36Sopenharmony_ci int err; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci mxport = devm_kzalloc(&port->dev, sizeof(struct mxuport_port), 112562306a36Sopenharmony_ci GFP_KERNEL); 112662306a36Sopenharmony_ci if (!mxport) 112762306a36Sopenharmony_ci return -ENOMEM; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci mutex_init(&mxport->mutex); 113062306a36Sopenharmony_ci spin_lock_init(&mxport->spinlock); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* Set the port private data */ 113362306a36Sopenharmony_ci usb_set_serial_port_data(port, mxport); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci /* Set FIFO (Enable) */ 113662306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_FIFO_DISABLE, 113762306a36Sopenharmony_ci 0, port->port_number); 113862306a36Sopenharmony_ci if (err) 113962306a36Sopenharmony_ci return err; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci /* Set transmission mode (Hi-Performance) */ 114262306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_HIGH_PERFOR, 114362306a36Sopenharmony_ci 0, port->port_number); 114462306a36Sopenharmony_ci if (err) 114562306a36Sopenharmony_ci return err; 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci /* Set interface (RS-232) */ 114862306a36Sopenharmony_ci return mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_INTERFACE, 114962306a36Sopenharmony_ci MX_INT_RS232, 115062306a36Sopenharmony_ci port->port_number); 115162306a36Sopenharmony_ci} 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_cistatic int mxuport_attach(struct usb_serial *serial) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci struct usb_serial_port *port0 = serial->port[0]; 115662306a36Sopenharmony_ci struct usb_serial_port *port1 = serial->port[1]; 115762306a36Sopenharmony_ci int err; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci /* 116062306a36Sopenharmony_ci * All data from the ports is received on the first bulk in 116162306a36Sopenharmony_ci * endpoint, with a multiplex header. The second bulk in is 116262306a36Sopenharmony_ci * used for events. 116362306a36Sopenharmony_ci * 116462306a36Sopenharmony_ci * Start to read from the device. 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_ci err = usb_serial_generic_submit_read_urbs(port0, GFP_KERNEL); 116762306a36Sopenharmony_ci if (err) 116862306a36Sopenharmony_ci return err; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci err = usb_serial_generic_submit_read_urbs(port1, GFP_KERNEL); 117162306a36Sopenharmony_ci if (err) { 117262306a36Sopenharmony_ci usb_serial_generic_close(port0); 117362306a36Sopenharmony_ci return err; 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci return 0; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic void mxuport_release(struct usb_serial *serial) 118062306a36Sopenharmony_ci{ 118162306a36Sopenharmony_ci struct usb_serial_port *port0 = serial->port[0]; 118262306a36Sopenharmony_ci struct usb_serial_port *port1 = serial->port[1]; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci usb_serial_generic_close(port1); 118562306a36Sopenharmony_ci usb_serial_generic_close(port0); 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic int mxuport_open(struct tty_struct *tty, struct usb_serial_port *port) 118962306a36Sopenharmony_ci{ 119062306a36Sopenharmony_ci struct mxuport_port *mxport = usb_get_serial_port_data(port); 119162306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 119262306a36Sopenharmony_ci int err; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* Set receive host (enable) */ 119562306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, 119662306a36Sopenharmony_ci 1, port->port_number); 119762306a36Sopenharmony_ci if (err) 119862306a36Sopenharmony_ci return err; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci err = mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_OPEN, 120162306a36Sopenharmony_ci 1, port->port_number); 120262306a36Sopenharmony_ci if (err) { 120362306a36Sopenharmony_ci mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, 120462306a36Sopenharmony_ci 0, port->port_number); 120562306a36Sopenharmony_ci return err; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* Initial port termios */ 120962306a36Sopenharmony_ci if (tty) 121062306a36Sopenharmony_ci mxuport_set_termios(tty, port, NULL); 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* 121362306a36Sopenharmony_ci * TODO: use RQ_VENDOR_GET_MSR, once we know what it 121462306a36Sopenharmony_ci * returns. 121562306a36Sopenharmony_ci */ 121662306a36Sopenharmony_ci mxport->msr_state = 0; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci return err; 121962306a36Sopenharmony_ci} 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cistatic void mxuport_close(struct usb_serial_port *port) 122262306a36Sopenharmony_ci{ 122362306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_OPEN, 0, 122662306a36Sopenharmony_ci port->port_number); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_RX_HOST_EN, 0, 122962306a36Sopenharmony_ci port->port_number); 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci/* Send a break to the port. */ 123362306a36Sopenharmony_cistatic int mxuport_break_ctl(struct tty_struct *tty, int break_state) 123462306a36Sopenharmony_ci{ 123562306a36Sopenharmony_ci struct usb_serial_port *port = tty->driver_data; 123662306a36Sopenharmony_ci struct usb_serial *serial = port->serial; 123762306a36Sopenharmony_ci int enable; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci if (break_state == -1) { 124062306a36Sopenharmony_ci enable = 1; 124162306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - sending break\n", __func__); 124262306a36Sopenharmony_ci } else { 124362306a36Sopenharmony_ci enable = 0; 124462306a36Sopenharmony_ci dev_dbg(&port->dev, "%s - clearing break\n", __func__); 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci return mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_BREAK, 124862306a36Sopenharmony_ci enable, port->port_number); 124962306a36Sopenharmony_ci} 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_cistatic int mxuport_resume(struct usb_serial *serial) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci struct usb_serial_port *port; 125462306a36Sopenharmony_ci int c = 0; 125562306a36Sopenharmony_ci int i; 125662306a36Sopenharmony_ci int r; 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 125962306a36Sopenharmony_ci port = serial->port[i]; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci r = usb_serial_generic_submit_read_urbs(port, GFP_NOIO); 126262306a36Sopenharmony_ci if (r < 0) 126362306a36Sopenharmony_ci c++; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci for (i = 0; i < serial->num_ports; i++) { 126762306a36Sopenharmony_ci port = serial->port[i]; 126862306a36Sopenharmony_ci if (!tty_port_initialized(&port->port)) 126962306a36Sopenharmony_ci continue; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci r = usb_serial_generic_write_start(port, GFP_NOIO); 127262306a36Sopenharmony_ci if (r < 0) 127362306a36Sopenharmony_ci c++; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci return c ? -EIO : 0; 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic struct usb_serial_driver mxuport_device = { 128062306a36Sopenharmony_ci .driver = { 128162306a36Sopenharmony_ci .owner = THIS_MODULE, 128262306a36Sopenharmony_ci .name = "mxuport", 128362306a36Sopenharmony_ci }, 128462306a36Sopenharmony_ci .description = "MOXA UPort", 128562306a36Sopenharmony_ci .id_table = mxuport_idtable, 128662306a36Sopenharmony_ci .num_bulk_in = 2, 128762306a36Sopenharmony_ci .num_bulk_out = 1, 128862306a36Sopenharmony_ci .probe = mxuport_probe, 128962306a36Sopenharmony_ci .port_probe = mxuport_port_probe, 129062306a36Sopenharmony_ci .attach = mxuport_attach, 129162306a36Sopenharmony_ci .release = mxuport_release, 129262306a36Sopenharmony_ci .calc_num_ports = mxuport_calc_num_ports, 129362306a36Sopenharmony_ci .open = mxuport_open, 129462306a36Sopenharmony_ci .close = mxuport_close, 129562306a36Sopenharmony_ci .set_termios = mxuport_set_termios, 129662306a36Sopenharmony_ci .break_ctl = mxuport_break_ctl, 129762306a36Sopenharmony_ci .tx_empty = mxuport_tx_empty, 129862306a36Sopenharmony_ci .tiocmiwait = usb_serial_generic_tiocmiwait, 129962306a36Sopenharmony_ci .get_icount = usb_serial_generic_get_icount, 130062306a36Sopenharmony_ci .throttle = mxuport_throttle, 130162306a36Sopenharmony_ci .unthrottle = mxuport_unthrottle, 130262306a36Sopenharmony_ci .tiocmget = mxuport_tiocmget, 130362306a36Sopenharmony_ci .tiocmset = mxuport_tiocmset, 130462306a36Sopenharmony_ci .dtr_rts = mxuport_dtr_rts, 130562306a36Sopenharmony_ci .process_read_urb = mxuport_process_read_urb, 130662306a36Sopenharmony_ci .prepare_write_buffer = mxuport_prepare_write_buffer, 130762306a36Sopenharmony_ci .resume = mxuport_resume, 130862306a36Sopenharmony_ci}; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_cistatic struct usb_serial_driver *const serial_drivers[] = { 131162306a36Sopenharmony_ci &mxuport_device, NULL 131262306a36Sopenharmony_ci}; 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_cimodule_usb_serial_driver(serial_drivers, mxuport_idtable); 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ciMODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>"); 131762306a36Sopenharmony_ciMODULE_AUTHOR("<support@moxa.com>"); 131862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1319