2017-11-03 13:28:30 +03:00
|
|
|
// SPDX-License-Identifier: GPL-2.0+
|
2016-05-12 11:48:33 +03:00
|
|
|
/*
|
2005-04-17 02:20:36 +04:00
|
|
|
* TI 3410/5052 USB Serial Driver
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Texas Instruments
|
|
|
|
*
|
|
|
|
* This driver is based on the Linux io_ti driver, which is
|
|
|
|
* Copyright (C) 2000-2002 Inside Out Networks
|
|
|
|
* Copyright (C) 2001-2002 Greg Kroah-Hartman
|
|
|
|
*
|
|
|
|
* For questions or problems with this driver, contact Texas Instruments
|
|
|
|
* technical support, or Al Borchers <alborchers@steinerpoint.com>, or
|
|
|
|
* Peter Berger <pberger@brimson.com>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/errno.h>
|
2008-07-22 14:09:07 +04:00
|
|
|
#include <linux/firmware.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/tty.h>
|
|
|
|
#include <linux/tty_driver.h>
|
|
|
|
#include <linux/tty_flip.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/ioctl.h>
|
|
|
|
#include <linux/serial.h>
|
2010-05-19 02:01:35 +04:00
|
|
|
#include <linux/kfifo.h>
|
2007-11-22 02:13:16 +03:00
|
|
|
#include <linux/mutex.h>
|
2008-07-22 14:15:17 +04:00
|
|
|
#include <linux/uaccess.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
#include <linux/usb.h>
|
2006-07-12 08:22:58 +04:00
|
|
|
#include <linux/usb/serial.h>
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2016-05-12 11:48:35 +03:00
|
|
|
/* Configuration ids */
|
|
|
|
#define TI_BOOT_CONFIG 1
|
|
|
|
#define TI_ACTIVE_CONFIG 2
|
|
|
|
|
|
|
|
/* Vendor and product ids */
|
|
|
|
#define TI_VENDOR_ID 0x0451
|
|
|
|
#define IBM_VENDOR_ID 0x04b3
|
|
|
|
#define TI_3410_PRODUCT_ID 0x3410
|
|
|
|
#define IBM_4543_PRODUCT_ID 0x4543
|
|
|
|
#define IBM_454B_PRODUCT_ID 0x454b
|
|
|
|
#define IBM_454C_PRODUCT_ID 0x454c
|
|
|
|
#define TI_3410_EZ430_ID 0xF430 /* TI ez430 development tool */
|
|
|
|
#define TI_5052_BOOT_PRODUCT_ID 0x5052 /* no EEPROM, no firmware */
|
|
|
|
#define TI_5152_BOOT_PRODUCT_ID 0x5152 /* no EEPROM, no firmware */
|
|
|
|
#define TI_5052_EEPROM_PRODUCT_ID 0x505A /* EEPROM, no firmware */
|
|
|
|
#define TI_5052_FIRMWARE_PRODUCT_ID 0x505F /* firmware is running */
|
|
|
|
#define FRI2_PRODUCT_ID 0x5053 /* Fish River Island II */
|
|
|
|
|
|
|
|
/* Multi-Tech vendor and product ids */
|
|
|
|
#define MTS_VENDOR_ID 0x06E0
|
|
|
|
#define MTS_GSM_NO_FW_PRODUCT_ID 0xF108
|
|
|
|
#define MTS_CDMA_NO_FW_PRODUCT_ID 0xF109
|
|
|
|
#define MTS_CDMA_PRODUCT_ID 0xF110
|
|
|
|
#define MTS_GSM_PRODUCT_ID 0xF111
|
|
|
|
#define MTS_EDGE_PRODUCT_ID 0xF112
|
|
|
|
#define MTS_MT9234MU_PRODUCT_ID 0xF114
|
|
|
|
#define MTS_MT9234ZBA_PRODUCT_ID 0xF115
|
|
|
|
#define MTS_MT9234ZBAOLD_PRODUCT_ID 0x0319
|
|
|
|
|
|
|
|
/* Abbott Diabetics vendor and product ids */
|
|
|
|
#define ABBOTT_VENDOR_ID 0x1a61
|
|
|
|
#define ABBOTT_STEREO_PLUG_ID 0x3410
|
|
|
|
#define ABBOTT_PRODUCT_ID ABBOTT_STEREO_PLUG_ID
|
|
|
|
#define ABBOTT_STRIP_PORT_ID 0x3420
|
|
|
|
|
|
|
|
/* Honeywell vendor and product IDs */
|
|
|
|
#define HONEYWELL_VENDOR_ID 0x10ac
|
|
|
|
#define HONEYWELL_HGI80_PRODUCT_ID 0x0102 /* Honeywell HGI80 */
|
|
|
|
|
|
|
|
/* Moxa UPORT 11x0 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 TI_GET_VERSION 0x01
|
|
|
|
#define TI_GET_PORT_STATUS 0x02
|
|
|
|
#define TI_GET_PORT_DEV_INFO 0x03
|
|
|
|
#define TI_GET_CONFIG 0x04
|
|
|
|
#define TI_SET_CONFIG 0x05
|
|
|
|
#define TI_OPEN_PORT 0x06
|
|
|
|
#define TI_CLOSE_PORT 0x07
|
|
|
|
#define TI_START_PORT 0x08
|
|
|
|
#define TI_STOP_PORT 0x09
|
|
|
|
#define TI_TEST_PORT 0x0A
|
|
|
|
#define TI_PURGE_PORT 0x0B
|
|
|
|
#define TI_RESET_EXT_DEVICE 0x0C
|
|
|
|
#define TI_WRITE_DATA 0x80
|
|
|
|
#define TI_READ_DATA 0x81
|
|
|
|
#define TI_REQ_TYPE_CLASS 0x82
|
|
|
|
|
|
|
|
/* Module identifiers */
|
|
|
|
#define TI_I2C_PORT 0x01
|
|
|
|
#define TI_IEEE1284_PORT 0x02
|
|
|
|
#define TI_UART1_PORT 0x03
|
|
|
|
#define TI_UART2_PORT 0x04
|
|
|
|
#define TI_RAM_PORT 0x05
|
|
|
|
|
|
|
|
/* Modem status */
|
|
|
|
#define TI_MSR_DELTA_CTS 0x01
|
|
|
|
#define TI_MSR_DELTA_DSR 0x02
|
|
|
|
#define TI_MSR_DELTA_RI 0x04
|
|
|
|
#define TI_MSR_DELTA_CD 0x08
|
|
|
|
#define TI_MSR_CTS 0x10
|
|
|
|
#define TI_MSR_DSR 0x20
|
|
|
|
#define TI_MSR_RI 0x40
|
|
|
|
#define TI_MSR_CD 0x80
|
|
|
|
#define TI_MSR_DELTA_MASK 0x0F
|
|
|
|
#define TI_MSR_MASK 0xF0
|
|
|
|
|
|
|
|
/* Line status */
|
|
|
|
#define TI_LSR_OVERRUN_ERROR 0x01
|
|
|
|
#define TI_LSR_PARITY_ERROR 0x02
|
|
|
|
#define TI_LSR_FRAMING_ERROR 0x04
|
|
|
|
#define TI_LSR_BREAK 0x08
|
|
|
|
#define TI_LSR_ERROR 0x0F
|
|
|
|
#define TI_LSR_RX_FULL 0x10
|
|
|
|
#define TI_LSR_TX_EMPTY 0x20
|
|
|
|
|
|
|
|
/* Line control */
|
|
|
|
#define TI_LCR_BREAK 0x40
|
|
|
|
|
|
|
|
/* Modem control */
|
|
|
|
#define TI_MCR_LOOP 0x04
|
|
|
|
#define TI_MCR_DTR 0x10
|
|
|
|
#define TI_MCR_RTS 0x20
|
|
|
|
|
|
|
|
/* Mask settings */
|
|
|
|
#define TI_UART_ENABLE_RTS_IN 0x0001
|
|
|
|
#define TI_UART_DISABLE_RTS 0x0002
|
|
|
|
#define TI_UART_ENABLE_PARITY_CHECKING 0x0008
|
|
|
|
#define TI_UART_ENABLE_DSR_OUT 0x0010
|
|
|
|
#define TI_UART_ENABLE_CTS_OUT 0x0020
|
|
|
|
#define TI_UART_ENABLE_X_OUT 0x0040
|
|
|
|
#define TI_UART_ENABLE_XA_OUT 0x0080
|
|
|
|
#define TI_UART_ENABLE_X_IN 0x0100
|
|
|
|
#define TI_UART_ENABLE_DTR_IN 0x0800
|
|
|
|
#define TI_UART_DISABLE_DTR 0x1000
|
|
|
|
#define TI_UART_ENABLE_MS_INTS 0x2000
|
|
|
|
#define TI_UART_ENABLE_AUTO_START_DMA 0x4000
|
|
|
|
|
|
|
|
/* Parity */
|
|
|
|
#define TI_UART_NO_PARITY 0x00
|
|
|
|
#define TI_UART_ODD_PARITY 0x01
|
|
|
|
#define TI_UART_EVEN_PARITY 0x02
|
|
|
|
#define TI_UART_MARK_PARITY 0x03
|
|
|
|
#define TI_UART_SPACE_PARITY 0x04
|
|
|
|
|
|
|
|
/* Stop bits */
|
|
|
|
#define TI_UART_1_STOP_BITS 0x00
|
|
|
|
#define TI_UART_1_5_STOP_BITS 0x01
|
|
|
|
#define TI_UART_2_STOP_BITS 0x02
|
|
|
|
|
|
|
|
/* Bits per character */
|
|
|
|
#define TI_UART_5_DATA_BITS 0x00
|
|
|
|
#define TI_UART_6_DATA_BITS 0x01
|
|
|
|
#define TI_UART_7_DATA_BITS 0x02
|
|
|
|
#define TI_UART_8_DATA_BITS 0x03
|
|
|
|
|
|
|
|
/* 232/485 modes */
|
|
|
|
#define TI_UART_232 0x00
|
|
|
|
#define TI_UART_485_RECEIVER_DISABLED 0x01
|
|
|
|
#define TI_UART_485_RECEIVER_ENABLED 0x02
|
|
|
|
|
|
|
|
/* Pipe transfer mode and timeout */
|
|
|
|
#define TI_PIPE_MODE_CONTINUOUS 0x01
|
|
|
|
#define TI_PIPE_MODE_MASK 0x03
|
|
|
|
#define TI_PIPE_TIMEOUT_MASK 0x7C
|
|
|
|
#define TI_PIPE_TIMEOUT_ENABLE 0x80
|
|
|
|
|
|
|
|
/* Config struct */
|
|
|
|
struct ti_uart_config {
|
2016-07-26 20:59:41 +03:00
|
|
|
__be16 wBaudRate;
|
|
|
|
__be16 wFlags;
|
|
|
|
u8 bDataBits;
|
|
|
|
u8 bParity;
|
|
|
|
u8 bStopBits;
|
2016-05-12 11:48:35 +03:00
|
|
|
char cXon;
|
|
|
|
char cXoff;
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 bUartMode;
|
2016-05-12 11:48:35 +03:00
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/* Get port status */
|
|
|
|
struct ti_port_status {
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 bCmdCode;
|
|
|
|
u8 bModuleId;
|
|
|
|
u8 bErrorCode;
|
|
|
|
u8 bMSR;
|
|
|
|
u8 bLSR;
|
2016-05-12 11:48:35 +03:00
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/* Purge modes */
|
|
|
|
#define TI_PURGE_OUTPUT 0x00
|
|
|
|
#define TI_PURGE_INPUT 0x80
|
|
|
|
|
|
|
|
/* Read/Write data */
|
|
|
|
#define TI_RW_DATA_ADDR_SFR 0x10
|
|
|
|
#define TI_RW_DATA_ADDR_IDATA 0x20
|
|
|
|
#define TI_RW_DATA_ADDR_XDATA 0x30
|
|
|
|
#define TI_RW_DATA_ADDR_CODE 0x40
|
|
|
|
#define TI_RW_DATA_ADDR_GPIO 0x50
|
|
|
|
#define TI_RW_DATA_ADDR_I2C 0x60
|
|
|
|
#define TI_RW_DATA_ADDR_FLASH 0x70
|
|
|
|
#define TI_RW_DATA_ADDR_DSP 0x80
|
|
|
|
|
|
|
|
#define TI_RW_DATA_UNSPECIFIED 0x00
|
|
|
|
#define TI_RW_DATA_BYTE 0x01
|
|
|
|
#define TI_RW_DATA_WORD 0x02
|
|
|
|
#define TI_RW_DATA_DOUBLE_WORD 0x04
|
|
|
|
|
|
|
|
struct ti_write_data_bytes {
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 bAddrType;
|
|
|
|
u8 bDataType;
|
|
|
|
u8 bDataCounter;
|
2016-05-12 11:48:35 +03:00
|
|
|
__be16 wBaseAddrHi;
|
|
|
|
__be16 wBaseAddrLo;
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 bData[0];
|
2016-05-12 11:48:35 +03:00
|
|
|
} __packed;
|
|
|
|
|
|
|
|
struct ti_read_data_request {
|
|
|
|
__u8 bAddrType;
|
|
|
|
__u8 bDataType;
|
|
|
|
__u8 bDataCounter;
|
|
|
|
__be16 wBaseAddrHi;
|
|
|
|
__be16 wBaseAddrLo;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
struct ti_read_data_bytes {
|
|
|
|
__u8 bCmdCode;
|
|
|
|
__u8 bModuleId;
|
|
|
|
__u8 bErrorCode;
|
|
|
|
__u8 bData[0];
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/* Interrupt struct */
|
|
|
|
struct ti_interrupt {
|
|
|
|
__u8 bICode;
|
|
|
|
__u8 bIInfo;
|
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/* Interrupt codes */
|
|
|
|
#define TI_CODE_HARDWARE_ERROR 0xFF
|
|
|
|
#define TI_CODE_DATA_ERROR 0x03
|
|
|
|
#define TI_CODE_MODEM_STATUS 0x04
|
|
|
|
|
|
|
|
/* Download firmware max packet size */
|
|
|
|
#define TI_DOWNLOAD_MAX_PACKET_SIZE 64
|
|
|
|
|
|
|
|
/* Firmware image header */
|
|
|
|
struct ti_firmware_header {
|
|
|
|
__le16 wLength;
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 bCheckSum;
|
2016-05-12 11:48:35 +03:00
|
|
|
} __packed;
|
|
|
|
|
|
|
|
/* UART addresses */
|
|
|
|
#define TI_UART1_BASE_ADDR 0xFFA0 /* UART 1 base address */
|
|
|
|
#define TI_UART2_BASE_ADDR 0xFFB0 /* UART 2 base address */
|
|
|
|
#define TI_UART_OFFSET_LCR 0x0002 /* UART MCR register offset */
|
|
|
|
#define TI_UART_OFFSET_MCR 0x0004 /* UART MCR register offset */
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
#define TI_DRIVER_AUTHOR "Al Borchers <alborchers@steinerpoint.com>"
|
|
|
|
#define TI_DRIVER_DESC "TI USB 3410/5052 Serial Driver"
|
|
|
|
|
|
|
|
#define TI_FIRMWARE_BUF_SIZE 16284
|
|
|
|
|
|
|
|
#define TI_TRANSFER_TIMEOUT 2
|
|
|
|
|
|
|
|
#define TI_DEFAULT_CLOSING_WAIT 4000 /* in .01 secs */
|
|
|
|
|
|
|
|
/* read urb states */
|
|
|
|
#define TI_READ_URB_RUNNING 0
|
|
|
|
#define TI_READ_URB_STOPPING 1
|
|
|
|
#define TI_READ_URB_STOPPED 2
|
|
|
|
|
|
|
|
#define TI_EXTRA_VID_PID_COUNT 5
|
|
|
|
|
|
|
|
struct ti_port {
|
|
|
|
int tp_is_open;
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 tp_msr;
|
|
|
|
u8 tp_shadow_mcr;
|
|
|
|
u8 tp_uart_mode; /* 232 or 485 modes */
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned int tp_uart_base_addr;
|
|
|
|
struct ti_device *tp_tdev;
|
|
|
|
struct usb_serial_port *tp_port;
|
|
|
|
spinlock_t tp_lock;
|
|
|
|
int tp_read_urb_state;
|
|
|
|
int tp_write_urb_in_use;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ti_device {
|
2007-11-22 02:13:16 +03:00
|
|
|
struct mutex td_open_close_lock;
|
2005-04-17 02:20:36 +04:00
|
|
|
int td_open_port_count;
|
|
|
|
struct usb_serial *td_serial;
|
|
|
|
int td_is_3410;
|
2016-05-10 10:08:48 +03:00
|
|
|
bool td_rs485_only;
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
static int ti_startup(struct usb_serial *serial);
|
2009-06-02 19:53:55 +04:00
|
|
|
static void ti_release(struct usb_serial *serial);
|
2012-10-17 18:31:35 +04:00
|
|
|
static int ti_port_probe(struct usb_serial_port *port);
|
|
|
|
static int ti_port_remove(struct usb_serial_port *port);
|
2009-09-20 00:13:26 +04:00
|
|
|
static int ti_open(struct tty_struct *tty, struct usb_serial_port *port);
|
2009-06-11 15:26:29 +04:00
|
|
|
static void ti_close(struct usb_serial_port *port);
|
2008-07-22 14:09:07 +04:00
|
|
|
static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
|
2008-07-22 14:15:17 +04:00
|
|
|
const unsigned char *data, int count);
|
2008-07-22 14:09:07 +04:00
|
|
|
static int ti_write_room(struct tty_struct *tty);
|
|
|
|
static int ti_chars_in_buffer(struct tty_struct *tty);
|
2013-05-05 22:32:32 +04:00
|
|
|
static bool ti_tx_empty(struct usb_serial_port *port);
|
2008-07-22 14:09:07 +04:00
|
|
|
static void ti_throttle(struct tty_struct *tty);
|
|
|
|
static void ti_unthrottle(struct tty_struct *tty);
|
2011-02-14 19:27:06 +03:00
|
|
|
static int ti_ioctl(struct tty_struct *tty,
|
2008-07-22 14:15:17 +04:00
|
|
|
unsigned int cmd, unsigned long arg);
|
|
|
|
static void ti_set_termios(struct tty_struct *tty,
|
|
|
|
struct usb_serial_port *port, struct ktermios *old_termios);
|
2011-02-14 19:26:14 +03:00
|
|
|
static int ti_tiocmget(struct tty_struct *tty);
|
2011-02-14 19:26:50 +03:00
|
|
|
static int ti_tiocmset(struct tty_struct *tty,
|
2008-07-22 14:15:17 +04:00
|
|
|
unsigned int set, unsigned int clear);
|
2008-07-22 14:09:07 +04:00
|
|
|
static void ti_break(struct tty_struct *tty, int break_state);
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 17:55:46 +04:00
|
|
|
static void ti_interrupt_callback(struct urb *urb);
|
|
|
|
static void ti_bulk_in_callback(struct urb *urb);
|
|
|
|
static void ti_bulk_out_callback(struct urb *urb);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-01-03 18:53:06 +04:00
|
|
|
static void ti_recv(struct usb_serial_port *port, unsigned char *data,
|
|
|
|
int length);
|
2005-04-17 02:20:36 +04:00
|
|
|
static void ti_send(struct ti_port *tport);
|
|
|
|
static int ti_set_mcr(struct ti_port *tport, unsigned int mcr);
|
2013-04-18 19:33:20 +04:00
|
|
|
static int ti_get_lsr(struct ti_port *tport, u8 *lsr);
|
2005-04-17 02:20:36 +04:00
|
|
|
static int ti_get_serial_info(struct ti_port *tport,
|
|
|
|
struct serial_struct __user *ret_arg);
|
2008-10-13 13:39:46 +04:00
|
|
|
static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
|
2005-04-17 02:20:36 +04:00
|
|
|
struct serial_struct __user *new_arg);
|
2016-07-26 20:59:41 +03:00
|
|
|
static void ti_handle_new_msr(struct ti_port *tport, u8 msr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty);
|
|
|
|
static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty);
|
|
|
|
|
|
|
|
static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
|
|
|
|
__u16 moduleid, __u16 value, __u8 *data, int size);
|
|
|
|
static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
|
|
|
|
__u16 moduleid, __u16 value, __u8 *data, int size);
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
static int ti_write_byte(struct usb_serial_port *port, struct ti_device *tdev,
|
2016-07-26 20:59:41 +03:00
|
|
|
unsigned long addr, u8 mask, u8 byte);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-01-11 22:48:53 +03:00
|
|
|
static int ti_download_firmware(struct ti_device *tdev);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
static int closing_wait = TI_DEFAULT_CLOSING_WAIT;
|
|
|
|
|
2013-12-29 22:22:54 +04:00
|
|
|
static const struct usb_device_id ti_id_table_3410[] = {
|
2005-04-17 02:20:36 +04:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
|
2006-11-17 11:21:27 +03:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) },
|
2009-01-11 22:49:00 +03:00
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_NO_FW_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) },
|
2010-04-23 02:18:20 +04:00
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234MU_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBA_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBAOLD_PRODUCT_ID) },
|
2009-01-12 15:31:16 +03:00
|
|
|
{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
|
2009-02-04 18:38:33 +03:00
|
|
|
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
|
2013-06-19 03:45:48 +04:00
|
|
|
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STEREO_PLUG_ID) },
|
|
|
|
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
|
2012-05-12 00:56:57 +04:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
|
2015-11-14 19:49:30 +03:00
|
|
|
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
|
2016-05-10 10:08:48 +03:00
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) },
|
2013-06-26 18:47:43 +04:00
|
|
|
{ } /* terminator */
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2013-12-29 22:22:54 +04:00
|
|
|
static const struct usb_device_id ti_id_table_5052[] = {
|
2005-04-17 02:20:36 +04:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
|
2016-05-12 11:48:33 +03:00
|
|
|
{ }
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2013-12-29 22:22:54 +04:00
|
|
|
static const struct usb_device_id ti_id_table_combined[] = {
|
2005-04-17 02:20:36 +04:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_PRODUCT_ID) },
|
2006-11-17 11:21:27 +03:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_3410_EZ430_ID) },
|
2009-01-11 22:49:00 +03:00
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_NO_FW_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_NO_FW_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_CDMA_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_GSM_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_EDGE_PRODUCT_ID) },
|
2010-04-23 02:18:20 +04:00
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234MU_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBA_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MTS_VENDOR_ID, MTS_MT9234ZBAOLD_PRODUCT_ID) },
|
2005-04-17 02:20:36 +04:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_BOOT_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5152_BOOT_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_EEPROM_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, TI_5052_FIRMWARE_PRODUCT_ID) },
|
2009-01-12 15:31:16 +03:00
|
|
|
{ USB_DEVICE(IBM_VENDOR_ID, IBM_4543_PRODUCT_ID) },
|
2009-02-04 18:38:33 +03:00
|
|
|
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454B_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(IBM_VENDOR_ID, IBM_454C_PRODUCT_ID) },
|
2012-02-20 12:31:57 +04:00
|
|
|
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_PRODUCT_ID) },
|
2013-10-08 23:03:37 +04:00
|
|
|
{ USB_DEVICE(ABBOTT_VENDOR_ID, ABBOTT_STRIP_PORT_ID) },
|
2012-05-12 00:56:57 +04:00
|
|
|
{ USB_DEVICE(TI_VENDOR_ID, FRI2_PRODUCT_ID) },
|
2015-11-14 19:49:30 +03:00
|
|
|
{ USB_DEVICE(HONEYWELL_VENDOR_ID, HONEYWELL_HGI80_PRODUCT_ID) },
|
2016-05-10 10:08:48 +03:00
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1110_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1130_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1131_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1150_PRODUCT_ID) },
|
|
|
|
{ USB_DEVICE(MXU1_VENDOR_ID, MXU1_1151_PRODUCT_ID) },
|
2013-06-26 18:47:43 +04:00
|
|
|
{ } /* terminator */
|
2005-04-17 02:20:36 +04:00
|
|
|
};
|
|
|
|
|
2005-06-21 08:15:16 +04:00
|
|
|
static struct usb_serial_driver ti_1port_device = {
|
2005-06-21 08:15:16 +04:00
|
|
|
.driver = {
|
2005-06-21 08:15:16 +04:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.name = "ti_usb_3410_5052_1",
|
2005-06-21 08:15:16 +04:00
|
|
|
},
|
2005-06-21 08:15:16 +04:00
|
|
|
.description = "TI USB 3410 1 port adapter",
|
2005-04-17 02:20:36 +04:00
|
|
|
.id_table = ti_id_table_3410,
|
|
|
|
.num_ports = 1,
|
2017-03-16 19:13:50 +03:00
|
|
|
.num_bulk_out = 1,
|
2005-04-17 02:20:36 +04:00
|
|
|
.attach = ti_startup,
|
2009-06-02 19:53:55 +04:00
|
|
|
.release = ti_release,
|
2012-10-17 18:31:35 +04:00
|
|
|
.port_probe = ti_port_probe,
|
|
|
|
.port_remove = ti_port_remove,
|
2005-04-17 02:20:36 +04:00
|
|
|
.open = ti_open,
|
|
|
|
.close = ti_close,
|
|
|
|
.write = ti_write,
|
|
|
|
.write_room = ti_write_room,
|
|
|
|
.chars_in_buffer = ti_chars_in_buffer,
|
2013-05-05 22:32:32 +04:00
|
|
|
.tx_empty = ti_tx_empty,
|
2005-04-17 02:20:36 +04:00
|
|
|
.throttle = ti_throttle,
|
|
|
|
.unthrottle = ti_unthrottle,
|
|
|
|
.ioctl = ti_ioctl,
|
|
|
|
.set_termios = ti_set_termios,
|
|
|
|
.tiocmget = ti_tiocmget,
|
|
|
|
.tiocmset = ti_tiocmset,
|
2013-03-21 15:37:34 +04:00
|
|
|
.tiocmiwait = usb_serial_generic_tiocmiwait,
|
2013-03-21 15:37:33 +04:00
|
|
|
.get_icount = usb_serial_generic_get_icount,
|
2005-04-17 02:20:36 +04:00
|
|
|
.break_ctl = ti_break,
|
|
|
|
.read_int_callback = ti_interrupt_callback,
|
|
|
|
.read_bulk_callback = ti_bulk_in_callback,
|
|
|
|
.write_bulk_callback = ti_bulk_out_callback,
|
|
|
|
};
|
|
|
|
|
2005-06-21 08:15:16 +04:00
|
|
|
static struct usb_serial_driver ti_2port_device = {
|
2005-06-21 08:15:16 +04:00
|
|
|
.driver = {
|
2005-06-21 08:15:16 +04:00
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.name = "ti_usb_3410_5052_2",
|
2005-06-21 08:15:16 +04:00
|
|
|
},
|
2005-06-21 08:15:16 +04:00
|
|
|
.description = "TI USB 5052 2 port adapter",
|
2005-04-17 02:20:36 +04:00
|
|
|
.id_table = ti_id_table_5052,
|
|
|
|
.num_ports = 2,
|
2017-03-16 19:13:50 +03:00
|
|
|
.num_bulk_out = 1,
|
2005-04-17 02:20:36 +04:00
|
|
|
.attach = ti_startup,
|
2009-06-02 19:53:55 +04:00
|
|
|
.release = ti_release,
|
2012-10-17 18:31:35 +04:00
|
|
|
.port_probe = ti_port_probe,
|
|
|
|
.port_remove = ti_port_remove,
|
2005-04-17 02:20:36 +04:00
|
|
|
.open = ti_open,
|
|
|
|
.close = ti_close,
|
|
|
|
.write = ti_write,
|
|
|
|
.write_room = ti_write_room,
|
|
|
|
.chars_in_buffer = ti_chars_in_buffer,
|
2013-05-05 22:32:32 +04:00
|
|
|
.tx_empty = ti_tx_empty,
|
2005-04-17 02:20:36 +04:00
|
|
|
.throttle = ti_throttle,
|
|
|
|
.unthrottle = ti_unthrottle,
|
|
|
|
.ioctl = ti_ioctl,
|
|
|
|
.set_termios = ti_set_termios,
|
|
|
|
.tiocmget = ti_tiocmget,
|
|
|
|
.tiocmset = ti_tiocmset,
|
2013-03-21 15:37:34 +04:00
|
|
|
.tiocmiwait = usb_serial_generic_tiocmiwait,
|
2013-03-21 15:37:33 +04:00
|
|
|
.get_icount = usb_serial_generic_get_icount,
|
2005-04-17 02:20:36 +04:00
|
|
|
.break_ctl = ti_break,
|
|
|
|
.read_int_callback = ti_interrupt_callback,
|
|
|
|
.read_bulk_callback = ti_bulk_in_callback,
|
|
|
|
.write_bulk_callback = ti_bulk_out_callback,
|
|
|
|
};
|
|
|
|
|
2012-02-23 23:57:32 +04:00
|
|
|
static struct usb_serial_driver * const serial_drivers[] = {
|
|
|
|
&ti_1port_device, &ti_2port_device, NULL
|
|
|
|
};
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
MODULE_AUTHOR(TI_DRIVER_AUTHOR);
|
|
|
|
MODULE_DESCRIPTION(TI_DRIVER_DESC);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
2008-05-30 19:49:51 +04:00
|
|
|
MODULE_FIRMWARE("ti_3410.fw");
|
|
|
|
MODULE_FIRMWARE("ti_5052.fw");
|
2009-01-11 22:49:11 +03:00
|
|
|
MODULE_FIRMWARE("mts_cdma.fw");
|
|
|
|
MODULE_FIRMWARE("mts_gsm.fw");
|
|
|
|
MODULE_FIRMWARE("mts_edge.fw");
|
2010-04-23 02:18:20 +04:00
|
|
|
MODULE_FIRMWARE("mts_mt9234mu.fw");
|
|
|
|
MODULE_FIRMWARE("mts_mt9234zba.fw");
|
2016-05-10 10:08:48 +03:00
|
|
|
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");
|
2008-05-30 19:49:51 +04:00
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
module_param(closing_wait, int, S_IRUGO | S_IWUSR);
|
2008-07-22 14:15:17 +04:00
|
|
|
MODULE_PARM_DESC(closing_wait,
|
|
|
|
"Maximum wait for data to drain in close, in .01 secs, default is 4000");
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(usb, ti_id_table_combined);
|
|
|
|
|
2013-06-26 18:47:43 +04:00
|
|
|
module_usb_serial_driver(serial_drivers, ti_id_table_combined);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
static int ti_startup(struct usb_serial *serial)
|
|
|
|
{
|
|
|
|
struct ti_device *tdev;
|
|
|
|
struct usb_device *dev = serial->dev;
|
2016-05-10 10:08:48 +03:00
|
|
|
struct usb_host_interface *cur_altsetting;
|
|
|
|
int num_endpoints;
|
|
|
|
u16 vid, pid;
|
2005-04-17 02:20:36 +04:00
|
|
|
int status;
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&dev->dev,
|
2014-03-12 22:09:42 +04:00
|
|
|
"%s - product 0x%4X, num configurations %d, configuration value %d\n",
|
2012-09-15 02:08:36 +04:00
|
|
|
__func__, le16_to_cpu(dev->descriptor.idProduct),
|
|
|
|
dev->descriptor.bNumConfigurations,
|
|
|
|
dev->actconfig->desc.bConfigurationValue);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2006-02-27 23:29:43 +03:00
|
|
|
tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL);
|
2013-12-29 22:22:56 +04:00
|
|
|
if (!tdev)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ENOMEM;
|
2013-12-29 22:22:56 +04:00
|
|
|
|
2007-11-22 02:13:16 +03:00
|
|
|
mutex_init(&tdev->td_open_close_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
tdev->td_serial = serial;
|
|
|
|
usb_set_serial_data(serial, tdev);
|
|
|
|
|
|
|
|
/* determine device type */
|
2013-06-28 14:24:26 +04:00
|
|
|
if (serial->type == &ti_1port_device)
|
2005-04-17 02:20:36 +04:00
|
|
|
tdev->td_is_3410 = 1;
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&dev->dev, "%s - device type is %s\n", __func__,
|
|
|
|
tdev->td_is_3410 ? "3410" : "5052");
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2016-05-10 10:08:48 +03:00
|
|
|
vid = le16_to_cpu(dev->descriptor.idVendor);
|
|
|
|
pid = le16_to_cpu(dev->descriptor.idProduct);
|
|
|
|
if (vid == MXU1_VENDOR_ID) {
|
|
|
|
switch (pid) {
|
|
|
|
case MXU1_1130_PRODUCT_ID:
|
|
|
|
case MXU1_1131_PRODUCT_ID:
|
|
|
|
tdev->td_rs485_only = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cur_altsetting = serial->interface->cur_altsetting;
|
|
|
|
num_endpoints = cur_altsetting->desc.bNumEndpoints;
|
|
|
|
|
|
|
|
/* if we have only 1 configuration and 1 endpoint, download firmware */
|
|
|
|
if (dev->descriptor.bNumConfigurations == 1 && num_endpoints == 1) {
|
2012-04-09 16:01:38 +04:00
|
|
|
status = ti_download_firmware(tdev);
|
|
|
|
|
|
|
|
if (status != 0)
|
2005-04-17 02:20:36 +04:00
|
|
|
goto free_tdev;
|
|
|
|
|
|
|
|
/* 3410 must be reset, 5052 resets itself */
|
|
|
|
if (tdev->td_is_3410) {
|
|
|
|
msleep_interruptible(100);
|
|
|
|
usb_reset_device(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
status = -ENODEV;
|
|
|
|
goto free_tdev;
|
2008-07-22 14:15:17 +04:00
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-12-16 14:25:55 +03:00
|
|
|
/* the second configuration must be set */
|
2005-04-17 02:20:36 +04:00
|
|
|
if (dev->actconfig->desc.bConfigurationValue == TI_BOOT_CONFIG) {
|
2008-12-16 14:25:55 +03:00
|
|
|
status = usb_driver_set_configuration(dev, TI_ACTIVE_CONFIG);
|
|
|
|
status = status ? status : -ENODEV;
|
2005-04-17 02:20:36 +04:00
|
|
|
goto free_tdev;
|
|
|
|
}
|
|
|
|
|
2017-01-03 18:40:03 +03:00
|
|
|
if (serial->num_bulk_in < serial->num_ports ||
|
|
|
|
serial->num_bulk_out < serial->num_ports) {
|
|
|
|
dev_err(&serial->interface->dev, "missing endpoints\n");
|
|
|
|
status = -ENODEV;
|
|
|
|
goto free_tdev;
|
|
|
|
}
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
free_tdev:
|
|
|
|
kfree(tdev);
|
|
|
|
usb_set_serial_data(serial, NULL);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-02 19:53:55 +04:00
|
|
|
static void ti_release(struct usb_serial *serial)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct ti_device *tdev = usb_get_serial_data(serial);
|
2012-10-17 18:31:35 +04:00
|
|
|
|
|
|
|
kfree(tdev);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ti_port_probe(struct usb_serial_port *port)
|
|
|
|
{
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport;
|
|
|
|
|
2012-10-17 18:31:35 +04:00
|
|
|
tport = kzalloc(sizeof(*tport), GFP_KERNEL);
|
|
|
|
if (!tport)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
spin_lock_init(&tport->tp_lock);
|
|
|
|
if (port == port->serial->port[0])
|
|
|
|
tport->tp_uart_base_addr = TI_UART1_BASE_ADDR;
|
|
|
|
else
|
|
|
|
tport->tp_uart_base_addr = TI_UART2_BASE_ADDR;
|
2013-04-18 19:33:23 +04:00
|
|
|
port->port.closing_wait = msecs_to_jiffies(10 * closing_wait);
|
2012-10-17 18:31:35 +04:00
|
|
|
tport->tp_port = port;
|
|
|
|
tport->tp_tdev = usb_get_serial_data(port->serial);
|
2016-05-10 10:08:48 +03:00
|
|
|
|
|
|
|
if (tport->tp_tdev->td_rs485_only)
|
|
|
|
tport->tp_uart_mode = TI_UART_485_RECEIVER_DISABLED;
|
|
|
|
else
|
|
|
|
tport->tp_uart_mode = TI_UART_232;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-10-17 18:31:35 +04:00
|
|
|
usb_set_serial_port_data(port, tport);
|
|
|
|
|
2013-06-26 18:47:23 +04:00
|
|
|
port->port.drain_delay = 3;
|
|
|
|
|
2012-10-17 18:31:35 +04:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2012-10-17 18:31:35 +04:00
|
|
|
static int ti_port_remove(struct usb_serial_port *port)
|
|
|
|
{
|
|
|
|
struct ti_port *tport;
|
|
|
|
|
|
|
|
tport = usb_get_serial_port_data(port);
|
|
|
|
kfree(tport);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-09-20 00:13:26 +04:00
|
|
|
static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
struct ti_device *tdev;
|
|
|
|
struct usb_device *dev;
|
|
|
|
struct urb *urb;
|
|
|
|
int port_number;
|
|
|
|
int status;
|
2016-07-26 20:59:41 +03:00
|
|
|
u16 open_settings;
|
|
|
|
|
|
|
|
open_settings = (TI_PIPE_MODE_CONTINUOUS |
|
|
|
|
TI_PIPE_TIMEOUT_ENABLE |
|
|
|
|
(TI_TRANSFER_TIMEOUT << 2));
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
dev = port->serial->dev;
|
|
|
|
tdev = tport->tp_tdev;
|
|
|
|
|
|
|
|
/* only one open on any port on a device at a time */
|
2007-11-22 02:13:16 +03:00
|
|
|
if (mutex_lock_interruptible(&tdev->td_open_close_lock))
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ERESTARTSYS;
|
|
|
|
|
2013-06-06 21:32:00 +04:00
|
|
|
port_number = port->port_number;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
tport->tp_msr = 0;
|
|
|
|
tport->tp_shadow_mcr |= (TI_MCR_RTS | TI_MCR_DTR);
|
|
|
|
|
|
|
|
/* start interrupt urb the first time a port is opened on this device */
|
|
|
|
if (tdev->td_open_port_count == 0) {
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - start interrupt in urb\n", __func__);
|
2005-04-17 02:20:36 +04:00
|
|
|
urb = tdev->td_serial->port[0]->interrupt_in_urb;
|
|
|
|
if (!urb) {
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_err(&port->dev, "%s - no interrupt urb\n", __func__);
|
2005-04-17 02:20:36 +04:00
|
|
|
status = -EINVAL;
|
2007-11-22 02:13:16 +03:00
|
|
|
goto release_lock;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
urb->context = tdev;
|
|
|
|
status = usb_submit_urb(urb, GFP_KERNEL);
|
|
|
|
if (status) {
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_err(&port->dev, "%s - submit interrupt urb failed, %d\n", __func__, status);
|
2007-11-22 02:13:16 +03:00
|
|
|
goto release_lock;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
if (tty)
|
2012-07-14 18:31:47 +04:00
|
|
|
ti_set_termios(tty, port, &tty->termios);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
status = ti_command_out_sync(tdev, TI_OPEN_PORT,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot send open command, %d\n",
|
2012-09-15 02:08:36 +04:00
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto unlink_int_urb;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ti_command_out_sync(tdev, TI_START_PORT,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot send start command, %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto unlink_int_urb;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ti_command_out_sync(tdev, TI_PURGE_PORT,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot clear input buffers, %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto unlink_int_urb;
|
|
|
|
}
|
|
|
|
status = ti_command_out_sync(tdev, TI_PURGE_PORT,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), TI_PURGE_OUTPUT, NULL, 0);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot clear output buffers, %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
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(dev, port->write_urb->pipe);
|
|
|
|
usb_clear_halt(dev, port->read_urb->pipe);
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
if (tty)
|
2012-07-14 18:31:47 +04:00
|
|
|
ti_set_termios(tty, port, &tty->termios);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
status = ti_command_out_sync(tdev, TI_OPEN_PORT,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot send open command (2), %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto unlink_int_urb;
|
|
|
|
}
|
|
|
|
|
|
|
|
status = ti_command_out_sync(tdev, TI_START_PORT,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot send start command (2), %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto unlink_int_urb;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* start read urb */
|
|
|
|
urb = port->read_urb;
|
|
|
|
if (!urb) {
|
2008-03-04 03:08:34 +03:00
|
|
|
dev_err(&port->dev, "%s - no read urb\n", __func__);
|
2005-04-17 02:20:36 +04:00
|
|
|
status = -EINVAL;
|
|
|
|
goto unlink_int_urb;
|
|
|
|
}
|
|
|
|
tport->tp_read_urb_state = TI_READ_URB_RUNNING;
|
|
|
|
urb->context = tport;
|
|
|
|
status = usb_submit_urb(urb, GFP_KERNEL);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - submit read urb failed, %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto unlink_int_urb;
|
|
|
|
}
|
|
|
|
|
|
|
|
tport->tp_is_open = 1;
|
|
|
|
++tdev->td_open_port_count;
|
|
|
|
|
2007-11-22 02:13:16 +03:00
|
|
|
goto release_lock;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
unlink_int_urb:
|
|
|
|
if (tdev->td_open_port_count == 0)
|
|
|
|
usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
|
2007-11-22 02:13:16 +03:00
|
|
|
release_lock:
|
|
|
|
mutex_unlock(&tdev->td_open_close_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-06-11 15:26:29 +04:00
|
|
|
static void ti_close(struct usb_serial_port *port)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct ti_device *tdev;
|
|
|
|
struct ti_port *tport;
|
|
|
|
int port_number;
|
|
|
|
int status;
|
2007-11-22 02:13:16 +03:00
|
|
|
int do_unlock;
|
2013-04-18 19:33:19 +04:00
|
|
|
unsigned long flags;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
tdev = usb_get_serial_data(port->serial);
|
|
|
|
tport = usb_get_serial_port_data(port);
|
|
|
|
|
|
|
|
tport->tp_is_open = 0;
|
|
|
|
|
|
|
|
usb_kill_urb(port->read_urb);
|
|
|
|
usb_kill_urb(port->write_urb);
|
|
|
|
tport->tp_write_urb_in_use = 0;
|
2013-04-18 19:33:19 +04:00
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2013-06-26 18:47:41 +04:00
|
|
|
kfifo_reset_out(&port->write_fifo);
|
2013-04-18 19:33:19 +04:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-06-06 21:32:00 +04:00
|
|
|
port_number = port->port_number;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
status = ti_command_out_sync(tdev, TI_CLOSE_PORT,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
|
|
|
|
if (status)
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev,
|
|
|
|
"%s - cannot send close port command, %d\n"
|
|
|
|
, __func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-11-22 02:13:16 +03:00
|
|
|
/* if mutex_lock is interrupted, continue anyway */
|
|
|
|
do_unlock = !mutex_lock_interruptible(&tdev->td_open_close_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
--tport->tp_tdev->td_open_port_count;
|
|
|
|
if (tport->tp_tdev->td_open_port_count <= 0) {
|
|
|
|
/* last port is closed, shut down interrupt urb */
|
|
|
|
usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
|
|
|
|
tport->tp_tdev->td_open_port_count = 0;
|
|
|
|
}
|
2007-11-22 02:13:16 +03:00
|
|
|
if (do_unlock)
|
|
|
|
mutex_unlock(&tdev->td_open_close_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
|
|
|
|
const unsigned char *data, int count)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
|
|
|
|
if (count == 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-07-26 20:59:44 +03:00
|
|
|
if (!tport->tp_is_open)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ENODEV;
|
|
|
|
|
2013-06-26 18:47:41 +04:00
|
|
|
count = kfifo_in_locked(&port->write_fifo, data, count,
|
2010-05-19 02:01:35 +04:00
|
|
|
&tport->tp_lock);
|
2005-04-17 02:20:36 +04:00
|
|
|
ti_send(tport);
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static int ti_write_room(struct tty_struct *tty)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
int room = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2013-06-26 18:47:41 +04:00
|
|
|
room = kfifo_avail(&port->write_fifo);
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
|
2005-04-17 02:20:36 +04:00
|
|
|
return room;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static int ti_chars_in_buffer(struct tty_struct *tty)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
int chars = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2013-06-26 18:47:41 +04:00
|
|
|
chars = kfifo_len(&port->write_fifo);
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
|
2005-04-17 02:20:36 +04:00
|
|
|
return chars;
|
|
|
|
}
|
|
|
|
|
2013-05-05 22:32:32 +04:00
|
|
|
static bool ti_tx_empty(struct usb_serial_port *port)
|
|
|
|
{
|
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
int ret;
|
|
|
|
u8 lsr;
|
|
|
|
|
|
|
|
ret = ti_get_lsr(tport, &lsr);
|
|
|
|
if (!ret && !(lsr & TI_LSR_TX_EMPTY))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static void ti_throttle(struct tty_struct *tty)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
|
|
|
|
if (I_IXOFF(tty) || C_CRTSCTS(tty))
|
|
|
|
ti_stop_read(tport, tty);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static void ti_unthrottle(struct tty_struct *tty)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
int status;
|
|
|
|
|
|
|
|
if (I_IXOFF(tty) || C_CRTSCTS(tty)) {
|
|
|
|
status = ti_restart_read(tport, tty);
|
|
|
|
if (status)
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot restart read, %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-14 19:27:06 +03:00
|
|
|
static int ti_ioctl(struct tty_struct *tty,
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned int cmd, unsigned long arg)
|
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
|
|
|
|
switch (cmd) {
|
2008-07-22 14:15:17 +04:00
|
|
|
case TIOCGSERIAL:
|
|
|
|
return ti_get_serial_info(tport,
|
|
|
|
(struct serial_struct __user *)arg);
|
|
|
|
case TIOCSSERIAL:
|
2008-10-13 13:39:46 +04:00
|
|
|
return ti_set_serial_info(tty, tport,
|
|
|
|
(struct serial_struct __user *)arg);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
return -ENOIOCTLCMD;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static void ti_set_termios(struct tty_struct *tty,
|
|
|
|
struct usb_serial_port *port, struct ktermios *old_termios)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
struct ti_uart_config *config;
|
|
|
|
int baud;
|
|
|
|
int status;
|
2013-06-06 21:32:00 +04:00
|
|
|
int port_number = port->port_number;
|
2005-04-17 02:20:36 +04:00
|
|
|
unsigned int mcr;
|
2016-07-26 20:59:41 +03:00
|
|
|
u16 wbaudrate;
|
|
|
|
u16 wflags = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
config = kmalloc(sizeof(*config), GFP_KERNEL);
|
2013-12-29 22:22:56 +04:00
|
|
|
if (!config)
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
|
|
|
|
/* these flags must be set */
|
2016-07-26 20:59:41 +03:00
|
|
|
wflags |= TI_UART_ENABLE_MS_INTS;
|
|
|
|
wflags |= TI_UART_ENABLE_AUTO_START_DMA;
|
|
|
|
config->bUartMode = tport->tp_uart_mode;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2016-07-26 20:59:45 +03:00
|
|
|
switch (C_CSIZE(tty)) {
|
2008-07-22 14:15:17 +04:00
|
|
|
case CS5:
|
|
|
|
config->bDataBits = TI_UART_5_DATA_BITS;
|
|
|
|
break;
|
|
|
|
case CS6:
|
|
|
|
config->bDataBits = TI_UART_6_DATA_BITS;
|
|
|
|
break;
|
|
|
|
case CS7:
|
|
|
|
config->bDataBits = TI_UART_7_DATA_BITS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
case CS8:
|
|
|
|
config->bDataBits = TI_UART_8_DATA_BITS;
|
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2008-01-03 20:03:11 +03:00
|
|
|
/* CMSPAR isn't supported by this driver */
|
2012-07-14 18:31:47 +04:00
|
|
|
tty->termios.c_cflag &= ~CMSPAR;
|
2008-01-03 20:03:11 +03:00
|
|
|
|
2016-07-26 20:59:45 +03:00
|
|
|
if (C_PARENB(tty)) {
|
|
|
|
if (C_PARODD(tty)) {
|
2016-07-26 20:59:41 +03:00
|
|
|
wflags |= TI_UART_ENABLE_PARITY_CHECKING;
|
2005-04-17 02:20:36 +04:00
|
|
|
config->bParity = TI_UART_ODD_PARITY;
|
|
|
|
} else {
|
2016-07-26 20:59:41 +03:00
|
|
|
wflags |= TI_UART_ENABLE_PARITY_CHECKING;
|
2005-04-17 02:20:36 +04:00
|
|
|
config->bParity = TI_UART_EVEN_PARITY;
|
|
|
|
}
|
|
|
|
} else {
|
2016-07-26 20:59:41 +03:00
|
|
|
wflags &= ~TI_UART_ENABLE_PARITY_CHECKING;
|
2008-07-22 14:15:17 +04:00
|
|
|
config->bParity = TI_UART_NO_PARITY;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2016-07-26 20:59:45 +03:00
|
|
|
if (C_CSTOPB(tty))
|
2005-04-17 02:20:36 +04:00
|
|
|
config->bStopBits = TI_UART_2_STOP_BITS;
|
|
|
|
else
|
|
|
|
config->bStopBits = TI_UART_1_STOP_BITS;
|
|
|
|
|
2016-07-26 20:59:45 +03:00
|
|
|
if (C_CRTSCTS(tty)) {
|
2005-04-17 02:20:36 +04:00
|
|
|
/* RTS flow control must be off to drop RTS for baud rate B0 */
|
2016-07-26 20:59:45 +03:00
|
|
|
if ((C_BAUD(tty)) != B0)
|
2016-07-26 20:59:41 +03:00
|
|
|
wflags |= TI_UART_ENABLE_RTS_IN;
|
|
|
|
wflags |= TI_UART_ENABLE_CTS_OUT;
|
2005-04-17 02:20:36 +04:00
|
|
|
} else {
|
|
|
|
ti_restart_read(tport, tty);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (I_IXOFF(tty) || I_IXON(tty)) {
|
|
|
|
config->cXon = START_CHAR(tty);
|
|
|
|
config->cXoff = STOP_CHAR(tty);
|
|
|
|
|
|
|
|
if (I_IXOFF(tty))
|
2016-07-26 20:59:41 +03:00
|
|
|
wflags |= TI_UART_ENABLE_X_IN;
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
|
|
|
ti_restart_read(tport, tty);
|
|
|
|
|
|
|
|
if (I_IXON(tty))
|
2016-07-26 20:59:41 +03:00
|
|
|
wflags |= TI_UART_ENABLE_X_OUT;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
baud = tty_get_baud_rate(tty);
|
2008-01-03 20:03:11 +03:00
|
|
|
if (!baud)
|
|
|
|
baud = 9600;
|
2005-04-17 02:20:36 +04:00
|
|
|
if (tport->tp_tdev->td_is_3410)
|
2016-07-26 20:59:41 +03:00
|
|
|
wbaudrate = (923077 + baud/2) / baud;
|
2005-04-17 02:20:36 +04:00
|
|
|
else
|
2016-07-26 20:59:41 +03:00
|
|
|
wbaudrate = (461538 + baud/2) / baud;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-01-03 20:03:11 +03:00
|
|
|
/* FIXME: Should calculate resulting baud here and report it back */
|
2016-07-26 20:59:45 +03:00
|
|
|
if ((C_BAUD(tty)) != B0)
|
2008-01-03 20:03:11 +03:00
|
|
|
tty_encode_baud_rate(tty, baud, baud);
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev,
|
2014-03-12 22:09:42 +04:00
|
|
|
"%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n",
|
2016-07-26 20:59:41 +03:00
|
|
|
__func__, baud, wbaudrate, wflags,
|
2012-09-15 02:08:36 +04:00
|
|
|
config->bDataBits, config->bParity, config->bStopBits,
|
|
|
|
config->cXon, config->cXoff, config->bUartMode);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2016-07-26 20:59:41 +03:00
|
|
|
config->wBaudRate = cpu_to_be16(wbaudrate);
|
|
|
|
config->wFlags = cpu_to_be16(wflags);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
status = ti_command_out_sync(tport->tp_tdev, TI_SET_CONFIG,
|
|
|
|
(__u8)(TI_UART1_PORT + port_number), 0, (__u8 *)config,
|
|
|
|
sizeof(*config));
|
|
|
|
if (status)
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev, "%s - cannot set config on port %d, %d\n",
|
|
|
|
__func__, port_number, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
/* SET_CONFIG asserts RTS and DTR, reset them correctly */
|
|
|
|
mcr = tport->tp_shadow_mcr;
|
|
|
|
/* if baud rate is B0, clear RTS and DTR */
|
2016-07-26 20:59:45 +03:00
|
|
|
if (C_BAUD(tty) == B0)
|
2005-04-17 02:20:36 +04:00
|
|
|
mcr &= ~(TI_MCR_DTR | TI_MCR_RTS);
|
|
|
|
status = ti_set_mcr(tport, mcr);
|
|
|
|
if (status)
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev,
|
|
|
|
"%s - cannot set modem control on port %d, %d\n",
|
|
|
|
__func__, port_number, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
kfree(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-14 19:26:14 +03:00
|
|
|
static int ti_tiocmget(struct tty_struct *tty)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
unsigned int result;
|
|
|
|
unsigned int msr;
|
|
|
|
unsigned int mcr;
|
2008-02-21 00:41:40 +03:00
|
|
|
unsigned long flags;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-02-21 00:41:40 +03:00
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
msr = tport->tp_msr;
|
|
|
|
mcr = tport->tp_shadow_mcr;
|
2008-02-21 00:41:40 +03:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
result = ((mcr & TI_MCR_DTR) ? TIOCM_DTR : 0)
|
|
|
|
| ((mcr & TI_MCR_RTS) ? TIOCM_RTS : 0)
|
|
|
|
| ((mcr & TI_MCR_LOOP) ? TIOCM_LOOP : 0)
|
|
|
|
| ((msr & TI_MSR_CTS) ? TIOCM_CTS : 0)
|
|
|
|
| ((msr & TI_MSR_CD) ? TIOCM_CAR : 0)
|
|
|
|
| ((msr & TI_MSR_RI) ? TIOCM_RI : 0)
|
|
|
|
| ((msr & TI_MSR_DSR) ? TIOCM_DSR : 0);
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-14 19:26:50 +03:00
|
|
|
static int ti_tiocmset(struct tty_struct *tty,
|
|
|
|
unsigned int set, unsigned int clear)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
unsigned int mcr;
|
2008-02-21 00:41:40 +03:00
|
|
|
unsigned long flags;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2008-02-21 00:41:40 +03:00
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
mcr = tport->tp_shadow_mcr;
|
|
|
|
|
|
|
|
if (set & TIOCM_RTS)
|
|
|
|
mcr |= TI_MCR_RTS;
|
|
|
|
if (set & TIOCM_DTR)
|
|
|
|
mcr |= TI_MCR_DTR;
|
|
|
|
if (set & TIOCM_LOOP)
|
|
|
|
mcr |= TI_MCR_LOOP;
|
|
|
|
|
|
|
|
if (clear & TIOCM_RTS)
|
|
|
|
mcr &= ~TI_MCR_RTS;
|
|
|
|
if (clear & TIOCM_DTR)
|
|
|
|
mcr &= ~TI_MCR_DTR;
|
|
|
|
if (clear & TIOCM_LOOP)
|
|
|
|
mcr &= ~TI_MCR_LOOP;
|
2008-02-21 00:41:40 +03:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return ti_set_mcr(tport, mcr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static void ti_break(struct tty_struct *tty, int break_state)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_serial_port *port = tty->driver_data;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port *tport = usb_get_serial_port_data(port);
|
|
|
|
int status;
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - state = %d\n", __func__, break_state);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
status = ti_write_byte(port, tport->tp_tdev,
|
2005-04-17 02:20:36 +04:00
|
|
|
tport->tp_uart_base_addr + TI_UART_OFFSET_LCR,
|
|
|
|
TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0);
|
|
|
|
|
|
|
|
if (status)
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - error setting break, %d\n", __func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2016-05-12 11:48:36 +03:00
|
|
|
static int ti_get_port_from_code(unsigned char code)
|
|
|
|
{
|
|
|
|
return (code >> 4) - 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ti_get_func_from_code(unsigned char code)
|
|
|
|
{
|
|
|
|
return code & 0x0f;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 17:55:46 +04:00
|
|
|
static void ti_interrupt_callback(struct urb *urb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-02-24 13:41:47 +03:00
|
|
|
struct ti_device *tdev = urb->context;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct usb_serial_port *port;
|
|
|
|
struct usb_serial *serial = tdev->td_serial;
|
|
|
|
struct ti_port *tport;
|
|
|
|
struct device *dev = &urb->dev->dev;
|
|
|
|
unsigned char *data = urb->transfer_buffer;
|
|
|
|
int length = urb->actual_length;
|
|
|
|
int port_number;
|
|
|
|
int function;
|
2007-06-16 02:44:13 +04:00
|
|
|
int status = urb->status;
|
|
|
|
int retval;
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 msr;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-06-16 02:44:13 +04:00
|
|
|
switch (status) {
|
2005-04-17 02:20:36 +04:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -ECONNRESET:
|
|
|
|
case -ENOENT:
|
|
|
|
case -ESHUTDOWN:
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
default:
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_err(dev, "%s - nonzero urb status, %d\n", __func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (length != 2) {
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(dev, "%s - bad packet size, %d\n", __func__, length);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data[0] == TI_CODE_HARDWARE_ERROR) {
|
2008-03-04 03:08:34 +03:00
|
|
|
dev_err(dev, "%s - hardware error, %d\n", __func__, data[1]);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
2016-05-12 11:48:36 +03:00
|
|
|
port_number = ti_get_port_from_code(data[0]);
|
|
|
|
function = ti_get_func_from_code(data[0]);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(dev, "%s - port_number %d, function %d, data 0x%02X\n",
|
|
|
|
__func__, port_number, function, data[1]);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (port_number >= serial->num_ports) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(dev, "%s - bad port number, %d\n",
|
|
|
|
__func__, port_number);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
port = serial->port[port_number];
|
|
|
|
|
|
|
|
tport = usb_get_serial_port_data(port);
|
|
|
|
if (!tport)
|
|
|
|
goto exit;
|
|
|
|
|
|
|
|
switch (function) {
|
|
|
|
case TI_CODE_DATA_ERROR:
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(dev, "%s - DATA ERROR, port %d, data 0x%02X\n",
|
2012-09-15 02:08:36 +04:00
|
|
|
__func__, port_number, data[1]);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case TI_CODE_MODEM_STATUS:
|
|
|
|
msr = data[1];
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(dev, "%s - port %d, msr 0x%02X\n", __func__, port_number, msr);
|
2005-04-17 02:20:36 +04:00
|
|
|
ti_handle_new_msr(tport, msr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(dev, "%s - unknown interrupt code, 0x%02X\n",
|
|
|
|
__func__, data[1]);
|
2005-04-17 02:20:36 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
2007-06-16 02:44:13 +04:00
|
|
|
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
|
|
|
if (retval)
|
|
|
|
dev_err(dev, "%s - resubmit interrupt urb failed, %d\n",
|
2008-03-04 03:08:34 +03:00
|
|
|
__func__, retval);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 17:55:46 +04:00
|
|
|
static void ti_bulk_in_callback(struct urb *urb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-02-24 13:41:47 +03:00
|
|
|
struct ti_port *tport = urb->context;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct usb_serial_port *port = tport->tp_port;
|
|
|
|
struct device *dev = &urb->dev->dev;
|
2007-06-16 02:44:13 +04:00
|
|
|
int status = urb->status;
|
2018-06-24 01:32:14 +03:00
|
|
|
unsigned long flags;
|
2007-06-16 02:44:13 +04:00
|
|
|
int retval = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2007-06-16 02:44:13 +04:00
|
|
|
switch (status) {
|
2005-04-17 02:20:36 +04:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -ECONNRESET:
|
|
|
|
case -ENOENT:
|
|
|
|
case -ESHUTDOWN:
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
default:
|
2007-06-16 02:44:13 +04:00
|
|
|
dev_err(dev, "%s - nonzero urb status, %d\n",
|
2008-07-22 14:15:17 +04:00
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
2007-06-16 02:44:13 +04:00
|
|
|
if (status == -EPIPE)
|
2005-04-17 02:20:36 +04:00
|
|
|
goto exit;
|
|
|
|
|
2007-06-16 02:44:13 +04:00
|
|
|
if (status) {
|
2008-03-04 03:08:34 +03:00
|
|
|
dev_err(dev, "%s - stopping read!\n", __func__);
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-01-03 18:53:06 +04:00
|
|
|
if (urb->actual_length) {
|
|
|
|
usb_serial_debug_data(dev, __func__, urb->actual_length,
|
|
|
|
urb->transfer_buffer);
|
|
|
|
|
|
|
|
if (!tport->tp_is_open)
|
|
|
|
dev_dbg(dev, "%s - port closed, dropping data\n",
|
|
|
|
__func__);
|
|
|
|
else
|
|
|
|
ti_recv(port, urb->transfer_buffer, urb->actual_length);
|
2018-06-24 01:32:14 +03:00
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2013-03-21 15:37:33 +04:00
|
|
|
port->icount.rx += urb->actual_length;
|
2018-06-24 01:32:14 +03:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
exit:
|
|
|
|
/* continue to read unless stopping */
|
2018-06-24 01:32:14 +03:00
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2011-11-06 22:06:28 +04:00
|
|
|
if (tport->tp_read_urb_state == TI_READ_URB_RUNNING)
|
2007-06-16 02:44:13 +04:00
|
|
|
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
2011-11-06 22:06:28 +04:00
|
|
|
else if (tport->tp_read_urb_state == TI_READ_URB_STOPPING)
|
2005-04-17 02:20:36 +04:00
|
|
|
tport->tp_read_urb_state = TI_READ_URB_STOPPED;
|
2011-11-06 22:06:28 +04:00
|
|
|
|
2018-06-24 01:32:14 +03:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2007-06-16 02:44:13 +04:00
|
|
|
if (retval)
|
|
|
|
dev_err(dev, "%s - resubmit read urb failed, %d\n",
|
2008-03-04 03:08:34 +03:00
|
|
|
__func__, retval);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 17:55:46 +04:00
|
|
|
static void ti_bulk_out_callback(struct urb *urb)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-02-24 13:41:47 +03:00
|
|
|
struct ti_port *tport = urb->context;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct usb_serial_port *port = tport->tp_port;
|
2007-06-16 02:44:13 +04:00
|
|
|
int status = urb->status;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
tport->tp_write_urb_in_use = 0;
|
|
|
|
|
2007-06-16 02:44:13 +04:00
|
|
|
switch (status) {
|
2005-04-17 02:20:36 +04:00
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case -ECONNRESET:
|
|
|
|
case -ENOENT:
|
|
|
|
case -ESHUTDOWN:
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - urb shutting down, %d\n", __func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
return;
|
|
|
|
default:
|
2012-02-10 16:20:51 +04:00
|
|
|
dev_err_console(port, "%s - nonzero urb status, %d\n",
|
2008-03-04 03:08:34 +03:00
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* send any buffered data */
|
|
|
|
ti_send(tport);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-01-03 18:53:06 +04:00
|
|
|
static void ti_recv(struct usb_serial_port *port, unsigned char *data,
|
|
|
|
int length)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int cnt;
|
|
|
|
|
|
|
|
do {
|
2013-01-03 18:53:04 +04:00
|
|
|
cnt = tty_insert_flip_string(&port->port, data, length);
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 07:54:13 +03:00
|
|
|
if (cnt < length) {
|
2013-01-03 18:53:04 +04:00
|
|
|
dev_err(&port->dev, "%s - dropping data, %d bytes lost\n",
|
2008-07-22 14:15:17 +04:00
|
|
|
__func__, length - cnt);
|
|
|
|
if (cnt == 0)
|
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by
serial driver people but they definitely need to live somewhere for a
while so the unconverted drivers can get knocked into shape, existing
drivers that have been updated can be better tuned and bugs whacked out.
This replaces the tty flip buffers with kmalloc objects in rings. In the
normal situation for an IRQ driven serial port at typical speeds the
behaviour is pretty much the same, two buffers end up allocated and the
kernel cycles between them as before.
When there are delays or at high speed we now behave far better as the
buffer pool can grow a bit rather than lose characters. This also means
that we can operate at higher speeds reliably.
For drivers that receive characters in blocks (DMA based, USB and
especially virtualisation) the layer allows a lot of driver specific
code that works around the tty layer with private secondary queues to be
removed. The IBM folks need this sort of layer, the smart serial port
people do, the virtualisers do (because a virtualised tty typically
operates at infinite speed rather than emulating 9600 baud).
Finally many drivers had invalid and unsafe attempts to avoid buffer
overflows by directly invoking tty methods extracted out of the innards
of work queue structs. These are no longer needed and all go away. That
fixes various random hangs with serial ports on overflow.
The other change in here is to optimise the receive_room path that is
used by some callers. It turns out that only one ldisc uses receive room
except asa constant and it updates it far far less than the value is
read. We thus make it a variable not a function call.
I expect the code to contain bugs due to the size alone but I'll be
watching and squashing them and feeding out new patches as it goes.
Because the buffers now dynamically expand you should only run out of
buffering when the kernel runs out of memory for real. That means a lot of
the horrible hacks high performance drivers used to do just aren't needed any
more.
Description:
tty_insert_flip_char is an old API and continues to work as before, as does
tty_flip_buffer_push() [this is why many drivers dont need modification]. It
does now also return the number of chars inserted
There are also
tty_buffer_request_room(tty, len)
which asks for a buffer block of the length requested and returns the space
found. This improves efficiency with hardware that knows how much to
transfer.
and tty_insert_flip_string_flags(tty, str, flags, len)
to insert a string of characters and flags
For a smart interface the usual code is
len = tty_request_buffer_room(tty, amount_hardware_says);
tty_insert_flip_string(tty, buffer_from_card, len);
More description!
At the moment tty buffers are attached directly to the tty. This is causing a
lot of the problems related to tty layer locking, also problems at high speed
and also with bursty data (such as occurs in virtualised environments)
I'm working on ripping out the flip buffers and replacing them with a pool of
dynamically allocated buffers. This allows both for old style "byte I/O"
devices and also helps virtualisation and smart devices where large blocks of
data suddenely materialise and need storing.
So far so good. Lots of drivers reference tty->flip.*. Several of them also
call directly and unsafely into function pointers it provides. This will all
break. Most drivers can use tty_insert_flip_char which can be kept as an API
but others need more.
At the moment I've added the following interfaces, if people think more will
be needed now is a good time to say
int tty_buffer_request_room(tty, size)
Try and ensure at least size bytes are available, returns actual room (may be
zero). At the moment it just uses the flipbuf space but that will change.
Repeated calls without characters being added are not cumulative. (ie if you
call it with 1, 1, 1, and then 4 you'll have four characters of space. The
other functions will also try and grow buffers in future but this will be a
more efficient way when you know block sizes.
int tty_insert_flip_char(tty, ch, flag)
As before insert a character if there is room. Now returns 1 for success, 0
for failure.
int tty_insert_flip_string(tty, str, len)
Insert a block of non error characters. Returns the number inserted.
int tty_prepare_flip_string(tty, strptr, len)
Adjust the buffer to allow len characters to be added. Returns a buffer
pointer in strptr and the length available. This allows for hardware that
needs to use functions like insl or mencpy_fromio.
Signed-off-by: Alan Cox <alan@redhat.com>
Cc: Paul Fulghum <paulkf@microgate.com>
Signed-off-by: Hirokazu Takata <takata@linux-m32r.org>
Signed-off-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Signed-off-by: John Hawkes <hawkes@sgi.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
2006-01-10 07:54:13 +03:00
|
|
|
break;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
2013-01-03 18:53:06 +04:00
|
|
|
tty_flip_buffer_push(&port->port);
|
2005-04-17 02:20:36 +04:00
|
|
|
data += cnt;
|
|
|
|
length -= cnt;
|
|
|
|
} while (length > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ti_send(struct ti_port *tport)
|
|
|
|
{
|
|
|
|
int count, result;
|
|
|
|
struct usb_serial_port *port = tport->tp_port;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
|
|
|
|
2008-10-13 13:39:46 +04:00
|
|
|
if (tport->tp_write_urb_in_use)
|
|
|
|
goto unlock;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-06-26 18:47:41 +04:00
|
|
|
count = kfifo_out(&port->write_fifo,
|
2005-04-17 02:20:36 +04:00
|
|
|
port->write_urb->transfer_buffer,
|
|
|
|
port->bulk_out_size);
|
|
|
|
|
2008-10-13 13:39:46 +04:00
|
|
|
if (count == 0)
|
|
|
|
goto unlock;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
tport->tp_write_urb_in_use = 1;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
|
|
|
|
2012-09-18 12:58:57 +04:00
|
|
|
usb_serial_debug_data(&port->dev, __func__, count,
|
|
|
|
port->write_urb->transfer_buffer);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
|
|
|
|
usb_sndbulkpipe(port->serial->dev,
|
|
|
|
port->bulk_out_endpointAddress),
|
|
|
|
port->write_urb->transfer_buffer, count,
|
|
|
|
ti_bulk_out_callback, tport);
|
|
|
|
|
|
|
|
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
|
|
|
|
if (result) {
|
2012-02-10 16:20:51 +04:00
|
|
|
dev_err_console(port, "%s - submit write urb failed, %d\n",
|
2008-07-22 14:15:17 +04:00
|
|
|
__func__, result);
|
|
|
|
tport->tp_write_urb_in_use = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
/* TODO: reschedule ti_send */
|
|
|
|
} else {
|
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2013-03-21 15:37:33 +04:00
|
|
|
port->icount.tx += count;
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* more room in the buffer for new writes, wakeup */
|
2013-03-07 16:12:29 +04:00
|
|
|
tty_port_tty_wakeup(&port->port);
|
|
|
|
|
2008-10-13 13:39:46 +04:00
|
|
|
return;
|
|
|
|
unlock:
|
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
|
|
|
return;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ti_set_mcr(struct ti_port *tport, unsigned int mcr)
|
|
|
|
{
|
2008-02-21 00:41:40 +03:00
|
|
|
unsigned long flags;
|
2005-04-17 02:20:36 +04:00
|
|
|
int status;
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
status = ti_write_byte(tport->tp_port, tport->tp_tdev,
|
2005-04-17 02:20:36 +04:00
|
|
|
tport->tp_uart_base_addr + TI_UART_OFFSET_MCR,
|
|
|
|
TI_MCR_RTS | TI_MCR_DTR | TI_MCR_LOOP, mcr);
|
|
|
|
|
2008-02-21 00:41:40 +03:00
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (!status)
|
|
|
|
tport->tp_shadow_mcr = mcr;
|
2008-02-21 00:41:40 +03:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-18 19:33:20 +04:00
|
|
|
static int ti_get_lsr(struct ti_port *tport, u8 *lsr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
2008-07-22 14:15:17 +04:00
|
|
|
int size, status;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_device *tdev = tport->tp_tdev;
|
|
|
|
struct usb_serial_port *port = tport->tp_port;
|
2013-06-06 21:32:00 +04:00
|
|
|
int port_number = port->port_number;
|
2005-04-17 02:20:36 +04:00
|
|
|
struct ti_port_status *data;
|
|
|
|
|
|
|
|
size = sizeof(struct ti_port_status);
|
|
|
|
data = kmalloc(size, GFP_KERNEL);
|
2013-12-29 22:22:56 +04:00
|
|
|
if (!data)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
status = ti_command_in_sync(tdev, TI_GET_PORT_STATUS,
|
|
|
|
(__u8)(TI_UART1_PORT+port_number), 0, (__u8 *)data, size);
|
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&port->dev,
|
|
|
|
"%s - get port status command failed, %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
goto free_data;
|
|
|
|
}
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - lsr 0x%02X\n", __func__, data->bLSR);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-04-18 19:33:20 +04:00
|
|
|
*lsr = data->bLSR;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
free_data:
|
|
|
|
kfree(data);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ti_get_serial_info(struct ti_port *tport,
|
|
|
|
struct serial_struct __user *ret_arg)
|
|
|
|
{
|
|
|
|
struct usb_serial_port *port = tport->tp_port;
|
|
|
|
struct serial_struct ret_serial;
|
2013-04-18 19:33:23 +04:00
|
|
|
unsigned cwait;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2013-04-18 19:33:23 +04:00
|
|
|
cwait = port->port.closing_wait;
|
|
|
|
if (cwait != ASYNC_CLOSING_WAIT_NONE)
|
|
|
|
cwait = jiffies_to_msecs(cwait) / 10;
|
|
|
|
|
2005-04-17 02:20:36 +04:00
|
|
|
memset(&ret_serial, 0, sizeof(ret_serial));
|
|
|
|
|
|
|
|
ret_serial.type = PORT_16550A;
|
2013-06-07 22:04:28 +04:00
|
|
|
ret_serial.line = port->minor;
|
2013-06-06 21:32:00 +04:00
|
|
|
ret_serial.port = port->port_number;
|
2013-06-26 18:47:41 +04:00
|
|
|
ret_serial.xmit_fifo_size = kfifo_size(&port->write_fifo);
|
2005-04-17 02:20:36 +04:00
|
|
|
ret_serial.baud_base = tport->tp_tdev->td_is_3410 ? 921600 : 460800;
|
2013-04-18 19:33:23 +04:00
|
|
|
ret_serial.closing_wait = cwait;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (copy_to_user(ret_arg, &ret_serial, sizeof(*ret_arg)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-10-13 13:39:46 +04:00
|
|
|
static int ti_set_serial_info(struct tty_struct *tty, struct ti_port *tport,
|
2005-04-17 02:20:36 +04:00
|
|
|
struct serial_struct __user *new_arg)
|
|
|
|
{
|
|
|
|
struct serial_struct new_serial;
|
2013-04-18 19:33:23 +04:00
|
|
|
unsigned cwait;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (copy_from_user(&new_serial, new_arg, sizeof(new_serial)))
|
|
|
|
return -EFAULT;
|
|
|
|
|
2013-04-18 19:33:23 +04:00
|
|
|
cwait = new_serial.closing_wait;
|
|
|
|
if (cwait != ASYNC_CLOSING_WAIT_NONE)
|
|
|
|
cwait = msecs_to_jiffies(10 * new_serial.closing_wait);
|
|
|
|
|
|
|
|
tport->tp_port->port.closing_wait = cwait;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-26 20:59:41 +03:00
|
|
|
static void ti_handle_new_msr(struct ti_port *tport, u8 msr)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
struct async_icount *icount;
|
|
|
|
struct tty_struct *tty;
|
|
|
|
unsigned long flags;
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&tport->tp_port->dev, "%s - msr 0x%02X\n", __func__, msr);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
if (msr & TI_MSR_DELTA_MASK) {
|
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
2013-03-21 15:37:33 +04:00
|
|
|
icount = &tport->tp_port->icount;
|
2005-04-17 02:20:36 +04:00
|
|
|
if (msr & TI_MSR_DELTA_CTS)
|
|
|
|
icount->cts++;
|
|
|
|
if (msr & TI_MSR_DELTA_DSR)
|
|
|
|
icount->dsr++;
|
|
|
|
if (msr & TI_MSR_DELTA_CD)
|
|
|
|
icount->dcd++;
|
|
|
|
if (msr & TI_MSR_DELTA_RI)
|
|
|
|
icount->rng++;
|
2013-03-21 15:37:34 +04:00
|
|
|
wake_up_interruptible(&tport->tp_port->port.delta_msr_wait);
|
2005-04-17 02:20:36 +04:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
tport->tp_msr = msr & TI_MSR_MASK;
|
|
|
|
|
|
|
|
/* handle CTS flow control */
|
2008-10-13 13:39:46 +04:00
|
|
|
tty = tty_port_tty_get(&tport->tp_port->port);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (tty && C_CRTSCTS(tty)) {
|
2014-09-10 23:06:27 +04:00
|
|
|
if (msr & TI_MSR_CTS)
|
2005-04-17 02:20:36 +04:00
|
|
|
tty_wakeup(tty);
|
|
|
|
}
|
2008-10-13 13:39:46 +04:00
|
|
|
tty_kref_put(tty);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ti_stop_read(struct ti_port *tport, struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
|
|
|
|
|
|
|
if (tport->tp_read_urb_state == TI_READ_URB_RUNNING)
|
|
|
|
tport->tp_read_urb_state = TI_READ_URB_STOPPING;
|
|
|
|
|
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ti_restart_read(struct ti_port *tport, struct tty_struct *tty)
|
|
|
|
{
|
|
|
|
struct urb *urb;
|
|
|
|
int status = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&tport->tp_lock, flags);
|
|
|
|
|
|
|
|
if (tport->tp_read_urb_state == TI_READ_URB_STOPPED) {
|
2007-05-07 10:33:18 +04:00
|
|
|
tport->tp_read_urb_state = TI_READ_URB_RUNNING;
|
2005-04-17 02:20:36 +04:00
|
|
|
urb = tport->tp_port->read_urb;
|
2007-05-07 10:33:18 +04:00
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
urb->context = tport;
|
|
|
|
status = usb_submit_urb(urb, GFP_KERNEL);
|
2007-05-07 10:33:18 +04:00
|
|
|
} else {
|
|
|
|
tport->tp_read_urb_state = TI_READ_URB_RUNNING;
|
|
|
|
spin_unlock_irqrestore(&tport->tp_lock, flags);
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
|
|
|
|
__u16 moduleid, __u16 value, __u8 *data, int size)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = usb_control_msg(tdev->td_serial->dev,
|
|
|
|
usb_sndctrlpipe(tdev->td_serial->dev, 0), command,
|
|
|
|
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT),
|
|
|
|
value, moduleid, data, size, 1000);
|
|
|
|
|
2017-01-12 16:56:23 +03:00
|
|
|
if (status < 0)
|
|
|
|
return status;
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2017-01-12 16:56:23 +03:00
|
|
|
return 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
|
|
|
|
__u16 moduleid, __u16 value, __u8 *data, int size)
|
|
|
|
{
|
|
|
|
int status;
|
|
|
|
|
|
|
|
status = usb_control_msg(tdev->td_serial->dev,
|
|
|
|
usb_rcvctrlpipe(tdev->td_serial->dev, 0), command,
|
|
|
|
(USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN),
|
|
|
|
value, moduleid, data, size, 1000);
|
|
|
|
|
|
|
|
if (status == size)
|
|
|
|
status = 0;
|
2017-01-12 16:56:23 +03:00
|
|
|
else if (status >= 0)
|
2005-04-17 02:20:36 +04:00
|
|
|
status = -ECOMM;
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
static int ti_write_byte(struct usb_serial_port *port,
|
2016-07-26 20:59:41 +03:00
|
|
|
struct ti_device *tdev, unsigned long addr,
|
|
|
|
u8 mask, u8 byte)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int status;
|
|
|
|
unsigned int size;
|
|
|
|
struct ti_write_data_bytes *data;
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&port->dev, "%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X\n", __func__,
|
|
|
|
addr, mask, byte);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
size = sizeof(struct ti_write_data_bytes) + 2;
|
|
|
|
data = kmalloc(size, GFP_KERNEL);
|
2013-12-29 22:22:56 +04:00
|
|
|
if (!data)
|
2005-04-17 02:20:36 +04:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
data->bAddrType = TI_RW_DATA_ADDR_XDATA;
|
|
|
|
data->bDataType = TI_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 = ti_command_out_sync(tdev, TI_WRITE_DATA, TI_RAM_PORT, 0,
|
|
|
|
(__u8 *)data, size);
|
|
|
|
|
|
|
|
if (status < 0)
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_err(&port->dev, "%s - failed, %d\n", __func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
kfree(data);
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2008-07-22 14:09:07 +04:00
|
|
|
static int ti_do_download(struct usb_device *dev, int pipe,
|
|
|
|
u8 *buffer, int size)
|
2005-04-17 02:20:36 +04:00
|
|
|
{
|
|
|
|
int pos;
|
2008-07-22 14:09:07 +04:00
|
|
|
u8 cs = 0;
|
2005-04-17 02:20:36 +04:00
|
|
|
int done;
|
|
|
|
struct ti_firmware_header *header;
|
2009-06-30 11:44:24 +04:00
|
|
|
int status = 0;
|
2008-07-22 14:09:07 +04:00
|
|
|
int len;
|
2008-07-22 14:15:17 +04:00
|
|
|
|
|
|
|
for (pos = sizeof(struct ti_firmware_header); pos < size; pos++)
|
2016-07-26 20:59:41 +03:00
|
|
|
cs = (u8)(cs + buffer[pos]);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
header = (struct ti_firmware_header *)buffer;
|
2016-07-26 20:59:41 +03:00
|
|
|
header->wLength = cpu_to_le16(size - sizeof(*header));
|
2005-04-17 02:20:36 +04:00
|
|
|
header->bCheckSum = cs;
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&dev->dev, "%s - downloading firmware\n", __func__);
|
2008-07-22 14:09:07 +04:00
|
|
|
for (pos = 0; pos < size; pos += done) {
|
|
|
|
len = min(size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE);
|
|
|
|
status = usb_bulk_msg(dev, pipe, buffer + pos, len,
|
|
|
|
&done, 1000);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (status)
|
|
|
|
break;
|
|
|
|
}
|
2008-07-22 14:09:07 +04:00
|
|
|
return status;
|
|
|
|
}
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2009-01-11 22:48:53 +03:00
|
|
|
static int ti_download_firmware(struct ti_device *tdev)
|
2008-07-22 14:09:07 +04:00
|
|
|
{
|
2009-01-11 22:48:53 +03:00
|
|
|
int status;
|
2008-07-22 14:09:07 +04:00
|
|
|
int buffer_size;
|
2016-07-26 20:59:41 +03:00
|
|
|
u8 *buffer;
|
2008-07-22 14:09:07 +04:00
|
|
|
struct usb_device *dev = tdev->td_serial->dev;
|
|
|
|
unsigned int pipe = usb_sndbulkpipe(dev,
|
|
|
|
tdev->td_serial->port[0]->bulk_out_endpointAddress);
|
|
|
|
const struct firmware *fw_p;
|
|
|
|
char buf[32];
|
2005-04-17 02:20:36 +04:00
|
|
|
|
2016-05-10 10:08:48 +03:00
|
|
|
if (le16_to_cpu(dev->descriptor.idVendor) == MXU1_VENDOR_ID) {
|
|
|
|
snprintf(buf,
|
|
|
|
sizeof(buf),
|
|
|
|
"moxa/moxa-%04x.fw",
|
|
|
|
le16_to_cpu(dev->descriptor.idProduct));
|
|
|
|
|
|
|
|
status = request_firmware(&fw_p, buf, &dev->dev);
|
|
|
|
goto check_firmware;
|
|
|
|
}
|
|
|
|
|
2009-01-11 22:48:53 +03:00
|
|
|
/* try ID specific firmware first, then try generic firmware */
|
2013-08-11 18:49:23 +04:00
|
|
|
sprintf(buf, "ti_usb-v%04x-p%04x.fw",
|
|
|
|
le16_to_cpu(dev->descriptor.idVendor),
|
|
|
|
le16_to_cpu(dev->descriptor.idProduct));
|
2012-04-09 16:01:38 +04:00
|
|
|
status = request_firmware(&fw_p, buf, &dev->dev);
|
|
|
|
|
|
|
|
if (status != 0) {
|
2009-01-11 22:49:00 +03:00
|
|
|
buf[0] = '\0';
|
2013-08-11 18:49:23 +04:00
|
|
|
if (le16_to_cpu(dev->descriptor.idVendor) == MTS_VENDOR_ID) {
|
|
|
|
switch (le16_to_cpu(dev->descriptor.idProduct)) {
|
2009-01-11 22:49:00 +03:00
|
|
|
case MTS_CDMA_PRODUCT_ID:
|
|
|
|
strcpy(buf, "mts_cdma.fw");
|
|
|
|
break;
|
|
|
|
case MTS_GSM_PRODUCT_ID:
|
|
|
|
strcpy(buf, "mts_gsm.fw");
|
|
|
|
break;
|
|
|
|
case MTS_EDGE_PRODUCT_ID:
|
|
|
|
strcpy(buf, "mts_edge.fw");
|
|
|
|
break;
|
2010-04-23 02:18:20 +04:00
|
|
|
case MTS_MT9234MU_PRODUCT_ID:
|
|
|
|
strcpy(buf, "mts_mt9234mu.fw");
|
|
|
|
break;
|
|
|
|
case MTS_MT9234ZBA_PRODUCT_ID:
|
|
|
|
strcpy(buf, "mts_mt9234zba.fw");
|
|
|
|
break;
|
|
|
|
case MTS_MT9234ZBAOLD_PRODUCT_ID:
|
|
|
|
strcpy(buf, "mts_mt9234zba.fw");
|
|
|
|
break; }
|
2009-01-11 22:49:00 +03:00
|
|
|
}
|
|
|
|
if (buf[0] == '\0') {
|
|
|
|
if (tdev->td_is_3410)
|
|
|
|
strcpy(buf, "ti_3410.fw");
|
|
|
|
else
|
|
|
|
strcpy(buf, "ti_5052.fw");
|
|
|
|
}
|
2009-01-11 22:48:53 +03:00
|
|
|
status = request_firmware(&fw_p, buf, &dev->dev);
|
|
|
|
}
|
2016-05-10 10:08:48 +03:00
|
|
|
|
|
|
|
check_firmware:
|
2009-01-11 22:48:53 +03:00
|
|
|
if (status) {
|
2008-07-22 14:09:07 +04:00
|
|
|
dev_err(&dev->dev, "%s - firmware not found\n", __func__);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
if (fw_p->size > TI_FIRMWARE_BUF_SIZE) {
|
2010-04-15 22:38:56 +04:00
|
|
|
dev_err(&dev->dev, "%s - firmware too large %zu\n", __func__, fw_p->size);
|
2011-06-14 00:50:41 +04:00
|
|
|
release_firmware(fw_p);
|
2008-07-22 14:09:07 +04:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer_size = TI_FIRMWARE_BUF_SIZE + sizeof(struct ti_firmware_header);
|
|
|
|
buffer = kmalloc(buffer_size, GFP_KERNEL);
|
|
|
|
if (buffer) {
|
|
|
|
memcpy(buffer, fw_p->data, fw_p->size);
|
|
|
|
memset(buffer + fw_p->size, 0xff, buffer_size - fw_p->size);
|
2008-09-11 01:11:38 +04:00
|
|
|
status = ti_do_download(dev, pipe, buffer, fw_p->size);
|
2008-07-22 14:09:07 +04:00
|
|
|
kfree(buffer);
|
2009-01-11 22:48:53 +03:00
|
|
|
} else {
|
|
|
|
status = -ENOMEM;
|
2008-07-22 14:09:07 +04:00
|
|
|
}
|
|
|
|
release_firmware(fw_p);
|
2005-04-17 02:20:36 +04:00
|
|
|
if (status) {
|
2008-07-22 14:15:17 +04:00
|
|
|
dev_err(&dev->dev, "%s - error downloading firmware, %d\n",
|
|
|
|
__func__, status);
|
2005-04-17 02:20:36 +04:00
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2012-09-15 02:08:36 +04:00
|
|
|
dev_dbg(&dev->dev, "%s - download successful\n", __func__);
|
2005-04-17 02:20:36 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|