mirror of
https://github.com/torvalds/linux.git
synced 2026-05-28 17:13:52 +02:00
USB-serial updates for 6.1-rc1
Here are the USB-serial updates for 6.1-rc1, including: - a fix for a very long-standing FTDI SIO regression - a long-overdue cleanup of the FTDI type handling - support for new FTDI HP and HA devices Included are also various clean ups. All have been in linux-next with no reported issues. -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQQHbPq+cpGvN/peuzMLxc3C7H1lCAUCYyxl3wAKCRALxc3C7H1l CCxZAP0T7FH942QyTZZLiWOuS2eEOrwiskrZbNW7DA9CLqA4UwEAzDEWnQzfEpxJ sONPl80YQB+1uikSgBVnf2yIak+WJgM= =Bi2/ -----END PGP SIGNATURE----- Merge tag 'usb-serial-6.1-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial into usb-next Johan writes: "USB-serial updates for 6.1-rc1 Here are the USB-serial updates for 6.1-rc1, including: - a fix for a very long-standing FTDI SIO regression - a long-overdue cleanup of the FTDI type handling - support for new FTDI HP and HA devices Included are also various clean ups. All have been in linux-next with no reported issues." * tag 'usb-serial-6.1-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial: USB: serial: ftdi_sio: clean up driver prefix USB: serial: ftdi_sio: move driver structure USB: serial: ftdi_sio: clean up attribute visibility logic USB: serial: console: move mutex_unlock() before usb_serial_put() USB: serial: ftdi_sio: convert to use dev_groups USB: serial: ftdi_sio: add support for HP and HA devices USB: serial: ftdi_sio: simplify divisor handling USB: serial: ftdi_sio: assume hi-speed type USB: serial: ftdi_sio: clean up baudrate request USB: serial: ftdi_sio: clean up attribute handling USB: serial: ftdi_sio: clean up modem-status handling USB: serial: ftdi_sio: tighten device-type detection USB: serial: ftdi_sio: rename channel index USB: serial: ftdi_sio: include FT2232D in type string USB: serial: ftdi_sio: rename chip types USB: serial: ftdi_sio: drop redundant chip type comments USB: serial: ftdi_sio: clean up chip type enum USB: serial: ftdi_sio: fix 300 bps rate for SIO
This commit is contained in:
commit
715a654bc6
|
|
@ -189,8 +189,8 @@ static int usb_console_setup(struct console *co, char *options)
|
|||
info->port = NULL;
|
||||
usb_autopm_put_interface(serial->interface);
|
||||
error_get_interface:
|
||||
usb_serial_put(serial);
|
||||
mutex_unlock(&serial->disc_mutex);
|
||||
usb_serial_put(serial);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,10 +47,27 @@
|
|||
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr, Johan Hovold <jhovold@gmail.com>"
|
||||
#define DRIVER_DESC "USB FTDI Serial Converters Driver"
|
||||
|
||||
enum ftdi_chip_type {
|
||||
SIO,
|
||||
FT232A,
|
||||
FT232B,
|
||||
FT2232C,
|
||||
FT232R,
|
||||
FT232H,
|
||||
FT2232H,
|
||||
FT4232H,
|
||||
FT4232HA,
|
||||
FT232HP,
|
||||
FT233HP,
|
||||
FT2232HP,
|
||||
FT2233HP,
|
||||
FT4232HP,
|
||||
FT4233HP,
|
||||
FTX,
|
||||
};
|
||||
|
||||
struct ftdi_private {
|
||||
enum ftdi_chip_type chip_type;
|
||||
/* type of device, either SIO or FT8U232AM */
|
||||
int baud_base; /* baud base clock for divisor setting */
|
||||
int custom_divisor; /* custom_divisor kludge, this is for
|
||||
baud_base (different from what goes to the
|
||||
|
|
@ -62,8 +79,7 @@ struct ftdi_private {
|
|||
unsigned long last_dtr_rts; /* saved modem control outputs */
|
||||
char prev_status; /* Used for TIOCMIWAIT */
|
||||
char transmit_empty; /* If transmitter is empty or not */
|
||||
u16 interface; /* FT2232C, FT2232H or FT4232H port interface
|
||||
(0 for FT232/245) */
|
||||
u16 channel; /* channel index, or 0 for legacy types */
|
||||
|
||||
speed_t force_baud; /* if non-zero, force the baud rate to
|
||||
this value */
|
||||
|
|
@ -84,8 +100,7 @@ struct ftdi_private {
|
|||
#endif
|
||||
};
|
||||
|
||||
/* struct ftdi_sio_quirk is used by devices requiring special attention. */
|
||||
struct ftdi_sio_quirk {
|
||||
struct ftdi_quirk {
|
||||
int (*probe)(struct usb_serial *);
|
||||
/* Special settings for probed ports. */
|
||||
void (*port_probe)(struct ftdi_private *);
|
||||
|
|
@ -98,27 +113,27 @@ static int ftdi_8u2232c_probe(struct usb_serial *serial);
|
|||
static void ftdi_USB_UIRT_setup(struct ftdi_private *priv);
|
||||
static void ftdi_HE_TIRA1_setup(struct ftdi_private *priv);
|
||||
|
||||
static const struct ftdi_sio_quirk ftdi_jtag_quirk = {
|
||||
static const struct ftdi_quirk ftdi_jtag_quirk = {
|
||||
.probe = ftdi_jtag_probe,
|
||||
};
|
||||
|
||||
static const struct ftdi_sio_quirk ftdi_NDI_device_quirk = {
|
||||
static const struct ftdi_quirk ftdi_NDI_device_quirk = {
|
||||
.probe = ftdi_NDI_device_setup,
|
||||
};
|
||||
|
||||
static const struct ftdi_sio_quirk ftdi_USB_UIRT_quirk = {
|
||||
static const struct ftdi_quirk ftdi_USB_UIRT_quirk = {
|
||||
.port_probe = ftdi_USB_UIRT_setup,
|
||||
};
|
||||
|
||||
static const struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
|
||||
static const struct ftdi_quirk ftdi_HE_TIRA1_quirk = {
|
||||
.port_probe = ftdi_HE_TIRA1_setup,
|
||||
};
|
||||
|
||||
static const struct ftdi_sio_quirk ftdi_stmclite_quirk = {
|
||||
static const struct ftdi_quirk ftdi_stmclite_quirk = {
|
||||
.probe = ftdi_stmclite_probe,
|
||||
};
|
||||
|
||||
static const struct ftdi_sio_quirk ftdi_8u2232c_quirk = {
|
||||
static const struct ftdi_quirk ftdi_8u2232c_quirk = {
|
||||
.probe = ftdi_8u2232c_probe,
|
||||
};
|
||||
|
||||
|
|
@ -180,6 +195,13 @@ static const struct usb_device_id id_table_combined[] = {
|
|||
{ USB_DEVICE(FTDI_VID, FTDI_4232H_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_232H_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FTX_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FT2233HP_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FT4233HP_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FT2232HP_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FT4232HP_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FT233HP_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FT232HP_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_FT4232HA_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
|
||||
{ USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
|
||||
|
|
@ -1061,15 +1083,22 @@ static const struct usb_device_id id_table_combined[] = {
|
|||
MODULE_DEVICE_TABLE(usb, id_table_combined);
|
||||
|
||||
static const char *ftdi_chip_name[] = {
|
||||
[SIO] = "SIO", /* the serial part of FT8U100AX */
|
||||
[FT8U232AM] = "FT8U232AM",
|
||||
[FT232BM] = "FT232BM",
|
||||
[FT2232C] = "FT2232C",
|
||||
[FT232RL] = "FT232RL",
|
||||
[FT2232H] = "FT2232H",
|
||||
[FT4232H] = "FT4232H",
|
||||
[FT232H] = "FT232H",
|
||||
[FTX] = "FT-X"
|
||||
[SIO] = "SIO", /* the serial part of FT8U100AX */
|
||||
[FT232A] = "FT232A",
|
||||
[FT232B] = "FT232B",
|
||||
[FT2232C] = "FT2232C/D",
|
||||
[FT232R] = "FT232R",
|
||||
[FT232H] = "FT232H",
|
||||
[FT2232H] = "FT2232H",
|
||||
[FT4232H] = "FT4232H",
|
||||
[FT4232HA] = "FT4232HA",
|
||||
[FT232HP] = "FT232HP",
|
||||
[FT233HP] = "FT233HP",
|
||||
[FT2232HP] = "FT2232HP",
|
||||
[FT2233HP] = "FT2233HP",
|
||||
[FT4232HP] = "FT4232HP",
|
||||
[FT4233HP] = "FT4233HP",
|
||||
[FTX] = "FT-X",
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1078,74 +1107,11 @@ static const char *ftdi_chip_name[] = {
|
|||
#define FTDI_STATUS_B1_MASK (FTDI_RS_BI)
|
||||
/* End TIOCMIWAIT */
|
||||
|
||||
/* function prototypes for a FTDI serial converter */
|
||||
static int ftdi_sio_probe(struct usb_serial *serial,
|
||||
const struct usb_device_id *id);
|
||||
static int ftdi_sio_port_probe(struct usb_serial_port *port);
|
||||
static void ftdi_sio_port_remove(struct usb_serial_port *port);
|
||||
static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port);
|
||||
static void ftdi_dtr_rts(struct usb_serial_port *port, int on);
|
||||
static void ftdi_process_read_urb(struct urb *urb);
|
||||
static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
|
||||
void *dest, size_t size);
|
||||
static void ftdi_set_termios(struct tty_struct *tty,
|
||||
struct usb_serial_port *port, struct ktermios *old);
|
||||
static int ftdi_tiocmget(struct tty_struct *tty);
|
||||
static int ftdi_tiocmset(struct tty_struct *tty,
|
||||
unsigned int set, unsigned int clear);
|
||||
static int ftdi_ioctl(struct tty_struct *tty,
|
||||
unsigned int cmd, unsigned long arg);
|
||||
static void get_serial_info(struct tty_struct *tty, struct serial_struct *ss);
|
||||
static int set_serial_info(struct tty_struct *tty,
|
||||
struct serial_struct *ss);
|
||||
static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
|
||||
static bool ftdi_tx_empty(struct usb_serial_port *port);
|
||||
static int ftdi_get_modem_status(struct usb_serial_port *port,
|
||||
unsigned char status[2]);
|
||||
|
||||
static unsigned short int ftdi_232am_baud_base_to_divisor(int baud, int base);
|
||||
static unsigned short int ftdi_232am_baud_to_divisor(int baud);
|
||||
static u32 ftdi_232bm_baud_base_to_divisor(int baud, int base);
|
||||
static u32 ftdi_232bm_baud_to_divisor(int baud);
|
||||
static u32 ftdi_2232h_baud_base_to_divisor(int baud, int base);
|
||||
static u32 ftdi_2232h_baud_to_divisor(int baud);
|
||||
|
||||
static struct usb_serial_driver ftdi_sio_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ftdi_sio",
|
||||
},
|
||||
.description = "FTDI USB Serial Device",
|
||||
.id_table = id_table_combined,
|
||||
.num_ports = 1,
|
||||
.bulk_in_size = 512,
|
||||
.bulk_out_size = 256,
|
||||
.probe = ftdi_sio_probe,
|
||||
.port_probe = ftdi_sio_port_probe,
|
||||
.port_remove = ftdi_sio_port_remove,
|
||||
.open = ftdi_open,
|
||||
.dtr_rts = ftdi_dtr_rts,
|
||||
.throttle = usb_serial_generic_throttle,
|
||||
.unthrottle = usb_serial_generic_unthrottle,
|
||||
.process_read_urb = ftdi_process_read_urb,
|
||||
.prepare_write_buffer = ftdi_prepare_write_buffer,
|
||||
.tiocmget = ftdi_tiocmget,
|
||||
.tiocmset = ftdi_tiocmset,
|
||||
.tiocmiwait = usb_serial_generic_tiocmiwait,
|
||||
.get_icount = usb_serial_generic_get_icount,
|
||||
.ioctl = ftdi_ioctl,
|
||||
.get_serial = get_serial_info,
|
||||
.set_serial = set_serial_info,
|
||||
.set_termios = ftdi_set_termios,
|
||||
.break_ctl = ftdi_break_ctl,
|
||||
.tx_empty = ftdi_tx_empty,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver * const serial_drivers[] = {
|
||||
&ftdi_sio_device, NULL
|
||||
};
|
||||
|
||||
|
||||
#define WDR_TIMEOUT 5000 /* default urb timeout */
|
||||
#define WDR_SHORT_TIMEOUT 1000 /* shorter urb timeout */
|
||||
|
||||
|
|
@ -1261,7 +1227,7 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
|
|||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_MODEM_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE,
|
||||
value, priv->interface,
|
||||
value, priv->channel,
|
||||
NULL, 0, WDR_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_dbg(dev, "%s Error from MODEM_CTRL urb: DTR %s, RTS %s\n",
|
||||
|
|
@ -1307,7 +1273,7 @@ static u32 get_ftdi_divisor(struct tty_struct *tty,
|
|||
if (!baud)
|
||||
baud = 9600;
|
||||
switch (priv->chip_type) {
|
||||
case SIO: /* SIO chip */
|
||||
case SIO:
|
||||
switch (baud) {
|
||||
case 300: div_value = ftdi_sio_b300; break;
|
||||
case 600: div_value = ftdi_sio_b600; break;
|
||||
|
|
@ -1319,8 +1285,7 @@ static u32 get_ftdi_divisor(struct tty_struct *tty,
|
|||
case 38400: div_value = ftdi_sio_b38400; break;
|
||||
case 57600: div_value = ftdi_sio_b57600; break;
|
||||
case 115200: div_value = ftdi_sio_b115200; break;
|
||||
} /* baud */
|
||||
if (div_value == 0) {
|
||||
default:
|
||||
dev_dbg(dev, "%s - Baudrate (%d) requested is not supported\n",
|
||||
__func__, baud);
|
||||
div_value = ftdi_sio_b9600;
|
||||
|
|
@ -1328,7 +1293,7 @@ static u32 get_ftdi_divisor(struct tty_struct *tty,
|
|||
div_okay = 0;
|
||||
}
|
||||
break;
|
||||
case FT8U232AM: /* 8U232AM chip */
|
||||
case FT232A:
|
||||
if (baud <= 3000000) {
|
||||
div_value = ftdi_232am_baud_to_divisor(baud);
|
||||
} else {
|
||||
|
|
@ -1338,10 +1303,10 @@ static u32 get_ftdi_divisor(struct tty_struct *tty,
|
|||
div_okay = 0;
|
||||
}
|
||||
break;
|
||||
case FT232BM: /* FT232BM chip */
|
||||
case FT2232C: /* FT2232C chip */
|
||||
case FT232RL: /* FT232RL chip */
|
||||
case FTX: /* FT-X series */
|
||||
case FT232B:
|
||||
case FT2232C:
|
||||
case FT232R:
|
||||
case FTX:
|
||||
if (baud <= 3000000) {
|
||||
u16 product_id = le16_to_cpu(
|
||||
port->serial->dev->descriptor.idProduct);
|
||||
|
|
@ -1361,9 +1326,7 @@ static u32 get_ftdi_divisor(struct tty_struct *tty,
|
|||
baud = 9600;
|
||||
}
|
||||
break;
|
||||
case FT2232H: /* FT2232H chip */
|
||||
case FT4232H: /* FT4232H chip */
|
||||
case FT232H: /* FT232H chip */
|
||||
default:
|
||||
if ((baud <= 12000000) && (baud >= 1200)) {
|
||||
div_value = ftdi_2232h_baud_to_divisor(baud);
|
||||
} else if (baud < 1200) {
|
||||
|
|
@ -1375,7 +1338,7 @@ static u32 get_ftdi_divisor(struct tty_struct *tty,
|
|||
baud = 9600;
|
||||
}
|
||||
break;
|
||||
} /* priv->chip_type */
|
||||
}
|
||||
|
||||
if (div_okay) {
|
||||
dev_dbg(dev, "%s - Baud rate set to %d (divisor 0x%lX) on chip %s\n",
|
||||
|
|
@ -1398,13 +1361,8 @@ static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
|
|||
index_value = get_ftdi_divisor(tty, port);
|
||||
value = (u16)index_value;
|
||||
index = (u16)(index_value >> 16);
|
||||
if (priv->chip_type == FT2232C || priv->chip_type == FT2232H ||
|
||||
priv->chip_type == FT4232H || priv->chip_type == FT232H ||
|
||||
priv->chip_type == FTX) {
|
||||
/* Probably the BM type needs the MSB of the encoded fractional
|
||||
* divider also moved like for the chips above. Any infos? */
|
||||
index = (u16)((index << 8) | priv->interface);
|
||||
}
|
||||
if (priv->channel)
|
||||
index = (u16)((index << 8) | priv->channel);
|
||||
|
||||
rv = usb_control_msg(port->serial->dev,
|
||||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
|
|
@ -1422,7 +1380,7 @@ static int write_latency_timer(struct usb_serial_port *port)
|
|||
int rv;
|
||||
int l = priv->latency;
|
||||
|
||||
if (priv->chip_type == SIO || priv->chip_type == FT8U232AM)
|
||||
if (priv->chip_type == SIO || priv->chip_type == FT232A)
|
||||
return -EINVAL;
|
||||
|
||||
if (priv->flags & ASYNC_LOW_LATENCY)
|
||||
|
|
@ -1434,7 +1392,7 @@ static int write_latency_timer(struct usb_serial_port *port)
|
|||
usb_sndctrlpipe(udev, 0),
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_SET_LATENCY_TIMER_REQUEST_TYPE,
|
||||
l, priv->interface,
|
||||
l, priv->channel,
|
||||
NULL, 0, WDR_TIMEOUT);
|
||||
if (rv < 0)
|
||||
dev_err(&port->dev, "Unable to write latency timer: %i\n", rv);
|
||||
|
|
@ -1450,7 +1408,7 @@ static int _read_latency_timer(struct usb_serial_port *port)
|
|||
|
||||
rv = usb_control_msg_recv(udev, 0, FTDI_SIO_GET_LATENCY_TIMER_REQUEST,
|
||||
FTDI_SIO_GET_LATENCY_TIMER_REQUEST_TYPE, 0,
|
||||
priv->interface, &buf, 1, WDR_TIMEOUT,
|
||||
priv->channel, &buf, 1, WDR_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (rv == 0)
|
||||
rv = buf;
|
||||
|
|
@ -1463,7 +1421,7 @@ static int read_latency_timer(struct usb_serial_port *port)
|
|||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
int rv;
|
||||
|
||||
if (priv->chip_type == SIO || priv->chip_type == FT8U232AM)
|
||||
if (priv->chip_type == SIO || priv->chip_type == FT232A)
|
||||
return -EINVAL;
|
||||
|
||||
rv = _read_latency_timer(port);
|
||||
|
|
@ -1538,90 +1496,97 @@ static int get_lsr_info(struct usb_serial_port *port,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Determine type of FTDI chip based on USB config and descriptor. */
|
||||
static void ftdi_determine_type(struct usb_serial_port *port)
|
||||
static int ftdi_determine_type(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
struct usb_serial *serial = port->serial;
|
||||
struct usb_device *udev = serial->dev;
|
||||
unsigned version;
|
||||
unsigned interfaces;
|
||||
|
||||
/* Assume it is not the original SIO device for now. */
|
||||
priv->baud_base = 48000000 / 2;
|
||||
unsigned int version, ifnum;
|
||||
|
||||
version = le16_to_cpu(udev->descriptor.bcdDevice);
|
||||
interfaces = udev->actconfig->desc.bNumInterfaces;
|
||||
dev_dbg(&port->dev, "%s: bcdDevice = 0x%x, bNumInterfaces = %u\n", __func__,
|
||||
version, interfaces);
|
||||
if (interfaces > 1) {
|
||||
struct usb_interface *intf = serial->interface;
|
||||
int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
|
||||
ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
|
||||
|
||||
/* Multiple interfaces.*/
|
||||
if (version == 0x0800) {
|
||||
priv->chip_type = FT4232H;
|
||||
/* Hi-speed - baud clock runs at 120MHz */
|
||||
priv->baud_base = 120000000 / 2;
|
||||
} else if (version == 0x0700) {
|
||||
priv->chip_type = FT2232H;
|
||||
/* Hi-speed - baud clock runs at 120MHz */
|
||||
priv->baud_base = 120000000 / 2;
|
||||
} else
|
||||
priv->chip_type = FT2232C;
|
||||
/* Assume Hi-Speed type */
|
||||
priv->baud_base = 120000000 / 2;
|
||||
priv->channel = CHANNEL_A + ifnum;
|
||||
|
||||
/* Determine interface code. */
|
||||
if (ifnum == 0)
|
||||
priv->interface = INTERFACE_A;
|
||||
else if (ifnum == 1)
|
||||
priv->interface = INTERFACE_B;
|
||||
else if (ifnum == 2)
|
||||
priv->interface = INTERFACE_C;
|
||||
else if (ifnum == 3)
|
||||
priv->interface = INTERFACE_D;
|
||||
|
||||
/* BM-type devices have a bug where bcdDevice gets set
|
||||
* to 0x200 when iSerialNumber is 0. */
|
||||
if (version < 0x500) {
|
||||
dev_dbg(&port->dev,
|
||||
"%s: something fishy - bcdDevice too low for multi-interface device\n",
|
||||
__func__);
|
||||
}
|
||||
} else if (version < 0x200) {
|
||||
/* Old device. Assume it's the original SIO. */
|
||||
priv->chip_type = SIO;
|
||||
priv->baud_base = 12000000 / 16;
|
||||
} else if (version < 0x400) {
|
||||
/* Assume it's an FT8U232AM (or FT8U245AM) */
|
||||
priv->chip_type = FT8U232AM;
|
||||
switch (version) {
|
||||
case 0x200:
|
||||
priv->chip_type = FT232A;
|
||||
priv->baud_base = 48000000 / 2;
|
||||
priv->channel = 0;
|
||||
/*
|
||||
* It might be a BM type because of the iSerialNumber bug.
|
||||
* If iSerialNumber==0 and the latency timer is readable,
|
||||
* assume it is BM type.
|
||||
* FT232B devices have a bug where bcdDevice gets set to 0x200
|
||||
* when iSerialNumber is 0. Assume it is an FT232B in case the
|
||||
* latency timer is readable.
|
||||
*/
|
||||
if (udev->descriptor.iSerialNumber == 0 &&
|
||||
_read_latency_timer(port) >= 0) {
|
||||
dev_dbg(&port->dev,
|
||||
"%s: has latency timer so not an AM type\n",
|
||||
__func__);
|
||||
priv->chip_type = FT232BM;
|
||||
priv->chip_type = FT232B;
|
||||
}
|
||||
} else if (version < 0x600) {
|
||||
/* Assume it's an FT232BM (or FT245BM) */
|
||||
priv->chip_type = FT232BM;
|
||||
} else if (version < 0x900) {
|
||||
/* Assume it's an FT232RL */
|
||||
priv->chip_type = FT232RL;
|
||||
} else if (version < 0x1000) {
|
||||
/* Assume it's an FT232H */
|
||||
break;
|
||||
case 0x400:
|
||||
priv->chip_type = FT232B;
|
||||
priv->baud_base = 48000000 / 2;
|
||||
priv->channel = 0;
|
||||
break;
|
||||
case 0x500:
|
||||
priv->chip_type = FT2232C;
|
||||
priv->baud_base = 48000000 / 2;
|
||||
break;
|
||||
case 0x600:
|
||||
priv->chip_type = FT232R;
|
||||
priv->baud_base = 48000000 / 2;
|
||||
priv->channel = 0;
|
||||
break;
|
||||
case 0x700:
|
||||
priv->chip_type = FT2232H;
|
||||
break;
|
||||
case 0x800:
|
||||
priv->chip_type = FT4232H;
|
||||
break;
|
||||
case 0x900:
|
||||
priv->chip_type = FT232H;
|
||||
} else {
|
||||
/* Assume it's an FT-X series device */
|
||||
break;
|
||||
case 0x1000:
|
||||
priv->chip_type = FTX;
|
||||
priv->baud_base = 48000000 / 2;
|
||||
break;
|
||||
case 0x2800:
|
||||
priv->chip_type = FT2233HP;
|
||||
break;
|
||||
case 0x2900:
|
||||
priv->chip_type = FT4233HP;
|
||||
break;
|
||||
case 0x3000:
|
||||
priv->chip_type = FT2232HP;
|
||||
break;
|
||||
case 0x3100:
|
||||
priv->chip_type = FT4232HP;
|
||||
break;
|
||||
case 0x3200:
|
||||
priv->chip_type = FT233HP;
|
||||
break;
|
||||
case 0x3300:
|
||||
priv->chip_type = FT232HP;
|
||||
break;
|
||||
case 0x3600:
|
||||
priv->chip_type = FT4232HA;
|
||||
break;
|
||||
default:
|
||||
if (version < 0x200) {
|
||||
priv->chip_type = SIO;
|
||||
priv->baud_base = 12000000 / 16;
|
||||
priv->channel = 0;
|
||||
} else {
|
||||
dev_err(&port->dev, "unknown device type: 0x%02x\n", version);
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1720,7 +1685,7 @@ static ssize_t event_char_store(struct device *dev,
|
|||
usb_sndctrlpipe(udev, 0),
|
||||
FTDI_SIO_SET_EVENT_CHAR_REQUEST,
|
||||
FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE,
|
||||
v, priv->interface,
|
||||
v, priv->channel,
|
||||
NULL, 0, WDR_TIMEOUT);
|
||||
if (rv < 0) {
|
||||
dev_dbg(&port->dev, "Unable to write event character: %i\n", rv);
|
||||
|
|
@ -1731,51 +1696,42 @@ static ssize_t event_char_store(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR_WO(event_char);
|
||||
|
||||
static int create_sysfs_attrs(struct usb_serial_port *port)
|
||||
static struct attribute *ftdi_attrs[] = {
|
||||
&dev_attr_event_char.attr,
|
||||
&dev_attr_latency_timer.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t ftdi_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct usb_serial_port *port = to_usb_serial_port(dev);
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
int retval = 0;
|
||||
enum ftdi_chip_type type = priv->chip_type;
|
||||
|
||||
/* XXX I've no idea if the original SIO supports the event_char
|
||||
* sysfs parameter, so I'm playing it safe. */
|
||||
if (priv->chip_type != SIO) {
|
||||
dev_dbg(&port->dev, "sysfs attributes for %s\n", ftdi_chip_name[priv->chip_type]);
|
||||
retval = device_create_file(&port->dev, &dev_attr_event_char);
|
||||
if ((!retval) &&
|
||||
(priv->chip_type == FT232BM ||
|
||||
priv->chip_type == FT2232C ||
|
||||
priv->chip_type == FT232RL ||
|
||||
priv->chip_type == FT2232H ||
|
||||
priv->chip_type == FT4232H ||
|
||||
priv->chip_type == FT232H ||
|
||||
priv->chip_type == FTX)) {
|
||||
retval = device_create_file(&port->dev,
|
||||
&dev_attr_latency_timer);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void remove_sysfs_attrs(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
/* XXX see create_sysfs_attrs */
|
||||
if (priv->chip_type != SIO) {
|
||||
device_remove_file(&port->dev, &dev_attr_event_char);
|
||||
if (priv->chip_type == FT232BM ||
|
||||
priv->chip_type == FT2232C ||
|
||||
priv->chip_type == FT232RL ||
|
||||
priv->chip_type == FT2232H ||
|
||||
priv->chip_type == FT4232H ||
|
||||
priv->chip_type == FT232H ||
|
||||
priv->chip_type == FTX) {
|
||||
device_remove_file(&port->dev, &dev_attr_latency_timer);
|
||||
}
|
||||
if (attr == &dev_attr_event_char.attr) {
|
||||
if (type == SIO)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (attr == &dev_attr_latency_timer.attr) {
|
||||
if (type == SIO || type == FT232A)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group ftdi_group = {
|
||||
.attrs = ftdi_attrs,
|
||||
.is_visible = ftdi_is_visible,
|
||||
};
|
||||
|
||||
static const struct attribute_group *ftdi_groups[] = {
|
||||
&ftdi_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
|
||||
static int ftdi_set_bitmode(struct usb_serial_port *port, u8 mode)
|
||||
|
|
@ -1794,7 +1750,7 @@ static int ftdi_set_bitmode(struct usb_serial_port *port, u8 mode)
|
|||
usb_sndctrlpipe(serial->dev, 0),
|
||||
FTDI_SIO_SET_BITMODE_REQUEST,
|
||||
FTDI_SIO_SET_BITMODE_REQUEST_TYPE, val,
|
||||
priv->interface, NULL, 0, WDR_TIMEOUT);
|
||||
priv->channel, NULL, 0, WDR_TIMEOUT);
|
||||
if (result < 0) {
|
||||
dev_err(&serial->interface->dev,
|
||||
"bitmode request failed for value 0x%04x: %d\n",
|
||||
|
|
@ -1858,7 +1814,7 @@ static int ftdi_read_cbus_pins(struct usb_serial_port *port)
|
|||
result = usb_control_msg_recv(serial->dev, 0,
|
||||
FTDI_SIO_READ_PINS_REQUEST,
|
||||
FTDI_SIO_READ_PINS_REQUEST_TYPE, 0,
|
||||
priv->interface, &buf, 1, WDR_TIMEOUT,
|
||||
priv->channel, &buf, 1, WDR_TIMEOUT,
|
||||
GFP_KERNEL);
|
||||
if (result == 0)
|
||||
result = buf;
|
||||
|
|
@ -2143,7 +2099,7 @@ static int ftdi_gpio_init(struct usb_serial_port *port)
|
|||
case FT232H:
|
||||
result = ftdi_gpio_init_ft232h(port);
|
||||
break;
|
||||
case FT232RL:
|
||||
case FT232R:
|
||||
result = ftdi_gpio_init_ft232r(port);
|
||||
break;
|
||||
case FTX:
|
||||
|
|
@ -2213,12 +2169,9 @@ static void ftdi_gpio_remove(struct usb_serial_port *port) { }
|
|||
* ***************************************************************************
|
||||
*/
|
||||
|
||||
/* Probe function to check for special devices */
|
||||
static int ftdi_sio_probe(struct usb_serial *serial,
|
||||
const struct usb_device_id *id)
|
||||
static int ftdi_probe(struct usb_serial *serial, const struct usb_device_id *id)
|
||||
{
|
||||
const struct ftdi_sio_quirk *quirk =
|
||||
(struct ftdi_sio_quirk *)id->driver_info;
|
||||
const struct ftdi_quirk *quirk = (struct ftdi_quirk *)id->driver_info;
|
||||
|
||||
if (quirk && quirk->probe) {
|
||||
int ret = quirk->probe(serial);
|
||||
|
|
@ -2231,10 +2184,10 @@ static int ftdi_sio_probe(struct usb_serial *serial,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ftdi_sio_port_probe(struct usb_serial_port *port)
|
||||
static int ftdi_port_probe(struct usb_serial_port *port)
|
||||
{
|
||||
const struct ftdi_quirk *quirk = usb_get_serial_data(port->serial);
|
||||
struct ftdi_private *priv;
|
||||
const struct ftdi_sio_quirk *quirk = usb_get_serial_data(port->serial);
|
||||
int result;
|
||||
|
||||
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
|
||||
|
|
@ -2248,12 +2201,14 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
|
|||
|
||||
usb_set_serial_port_data(port, priv);
|
||||
|
||||
ftdi_determine_type(port);
|
||||
result = ftdi_determine_type(port);
|
||||
if (result)
|
||||
goto err_free;
|
||||
|
||||
ftdi_set_max_packet_size(port);
|
||||
if (read_latency_timer(port) < 0)
|
||||
priv->latency = 16;
|
||||
write_latency_timer(port);
|
||||
create_sysfs_attrs(port);
|
||||
|
||||
result = ftdi_gpio_init(port);
|
||||
if (result < 0) {
|
||||
|
|
@ -2263,6 +2218,11 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
|
|||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(priv);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Setup for the USB-UIRT device, which requires hardwired
|
||||
|
|
@ -2373,14 +2333,12 @@ static int ftdi_stmclite_probe(struct usb_serial *serial)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ftdi_sio_port_remove(struct usb_serial_port *port)
|
||||
static void ftdi_port_remove(struct usb_serial_port *port)
|
||||
{
|
||||
struct ftdi_private *priv = usb_get_serial_port_data(port);
|
||||
|
||||
ftdi_gpio_remove(port);
|
||||
|
||||
remove_sysfs_attrs(port);
|
||||
|
||||
kfree(priv);
|
||||
}
|
||||
|
||||
|
|
@ -2394,7 +2352,7 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
|
|||
usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE,
|
||||
FTDI_SIO_RESET_SIO,
|
||||
priv->interface, NULL, 0, WDR_TIMEOUT);
|
||||
priv->channel, NULL, 0, WDR_TIMEOUT);
|
||||
|
||||
/* Termios defaults are set by usb_serial_init. We don't change
|
||||
port->tty->termios - this would lose speed settings, etc.
|
||||
|
|
@ -2417,7 +2375,7 @@ static void ftdi_dtr_rts(struct usb_serial_port *port, int on)
|
|||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0, priv->interface, NULL, 0,
|
||||
0, priv->channel, NULL, 0,
|
||||
WDR_TIMEOUT) < 0) {
|
||||
dev_err(&port->dev, "error from flowcontrol urb\n");
|
||||
}
|
||||
|
|
@ -2610,7 +2568,7 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
|
|||
usb_sndctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_SET_DATA_REQUEST,
|
||||
FTDI_SIO_SET_DATA_REQUEST_TYPE,
|
||||
value , priv->interface,
|
||||
value, priv->channel,
|
||||
NULL, 0, WDR_TIMEOUT) < 0) {
|
||||
dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n",
|
||||
__func__, break_state);
|
||||
|
|
@ -2746,7 +2704,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
|
|||
if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_SET_DATA_REQUEST,
|
||||
FTDI_SIO_SET_DATA_REQUEST_TYPE,
|
||||
value , priv->interface,
|
||||
value, priv->channel,
|
||||
NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
|
||||
dev_err(ddev, "%s FAILED to set databits/stopbits/parity\n",
|
||||
__func__);
|
||||
|
|
@ -2759,7 +2717,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
|
|||
if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
0, priv->channel,
|
||||
NULL, 0, WDR_TIMEOUT) < 0) {
|
||||
dev_err(ddev, "%s error from disable flowcontrol urb\n",
|
||||
__func__);
|
||||
|
|
@ -2793,7 +2751,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
|
|||
index = FTDI_SIO_DISABLE_FLOW_CTRL;
|
||||
}
|
||||
|
||||
index |= priv->interface;
|
||||
index |= priv->channel;
|
||||
|
||||
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
|
||||
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
|
||||
|
|
@ -2821,33 +2779,19 @@ static int ftdi_get_modem_status(struct usb_serial_port *port,
|
|||
if (!buf)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* The 8U232AM returns a two byte value (the SIO a 1 byte value) in
|
||||
* the same format as the data returned from the in point.
|
||||
* The device returns a two byte value (the SIO a 1 byte value) in the
|
||||
* same format as the data returned from the IN endpoint.
|
||||
*/
|
||||
switch (priv->chip_type) {
|
||||
case SIO:
|
||||
if (priv->chip_type == SIO)
|
||||
len = 1;
|
||||
break;
|
||||
case FT8U232AM:
|
||||
case FT232BM:
|
||||
case FT2232C:
|
||||
case FT232RL:
|
||||
case FT2232H:
|
||||
case FT4232H:
|
||||
case FT232H:
|
||||
case FTX:
|
||||
else
|
||||
len = 2;
|
||||
break;
|
||||
default:
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(port->serial->dev,
|
||||
usb_rcvctrlpipe(port->serial->dev, 0),
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST,
|
||||
FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE,
|
||||
0, priv->interface,
|
||||
0, priv->channel,
|
||||
buf, len, WDR_TIMEOUT);
|
||||
|
||||
/* NOTE: We allow short responses and handle that below. */
|
||||
|
|
@ -2917,6 +2861,41 @@ static int ftdi_ioctl(struct tty_struct *tty,
|
|||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static struct usb_serial_driver ftdi_device = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ftdi_sio",
|
||||
.dev_groups = ftdi_groups,
|
||||
},
|
||||
.description = "FTDI USB Serial Device",
|
||||
.id_table = id_table_combined,
|
||||
.num_ports = 1,
|
||||
.bulk_in_size = 512,
|
||||
.bulk_out_size = 256,
|
||||
.probe = ftdi_probe,
|
||||
.port_probe = ftdi_port_probe,
|
||||
.port_remove = ftdi_port_remove,
|
||||
.open = ftdi_open,
|
||||
.dtr_rts = ftdi_dtr_rts,
|
||||
.throttle = usb_serial_generic_throttle,
|
||||
.unthrottle = usb_serial_generic_unthrottle,
|
||||
.process_read_urb = ftdi_process_read_urb,
|
||||
.prepare_write_buffer = ftdi_prepare_write_buffer,
|
||||
.tiocmget = ftdi_tiocmget,
|
||||
.tiocmset = ftdi_tiocmset,
|
||||
.tiocmiwait = usb_serial_generic_tiocmiwait,
|
||||
.get_icount = usb_serial_generic_get_icount,
|
||||
.ioctl = ftdi_ioctl,
|
||||
.get_serial = get_serial_info,
|
||||
.set_serial = set_serial_info,
|
||||
.set_termios = ftdi_set_termios,
|
||||
.break_ctl = ftdi_break_ctl,
|
||||
.tx_empty = ftdi_tx_empty,
|
||||
};
|
||||
|
||||
static struct usb_serial_driver * const serial_drivers[] = {
|
||||
&ftdi_device, NULL
|
||||
};
|
||||
module_usb_serial_driver(serial_drivers, id_table_combined);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
|
|
|
|||
|
|
@ -40,11 +40,11 @@
|
|||
#define FTDI_SIO_READ_PINS 0x0c /* Read immediate value of pins */
|
||||
#define FTDI_SIO_READ_EEPROM 0x90 /* Read EEPROM */
|
||||
|
||||
/* Interface indices for FT2232, FT2232H and FT4232H devices */
|
||||
#define INTERFACE_A 1
|
||||
#define INTERFACE_B 2
|
||||
#define INTERFACE_C 3
|
||||
#define INTERFACE_D 4
|
||||
/* Channel indices for FT2232, FT2232H and FT4232H devices */
|
||||
#define CHANNEL_A 1
|
||||
#define CHANNEL_B 2
|
||||
#define CHANNEL_C 3
|
||||
#define CHANNEL_D 4
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -153,18 +153,6 @@
|
|||
* not supported by the FT8U232AM).
|
||||
*/
|
||||
|
||||
enum ftdi_chip_type {
|
||||
SIO = 1,
|
||||
FT8U232AM = 2,
|
||||
FT232BM = 3,
|
||||
FT2232C = 4,
|
||||
FT232RL = 5,
|
||||
FT2232H = 6,
|
||||
FT4232H = 7,
|
||||
FT232H = 8,
|
||||
FTX = 9,
|
||||
};
|
||||
|
||||
enum ftdi_sio_baudrate {
|
||||
ftdi_sio_b300 = 0,
|
||||
ftdi_sio_b600 = 1,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@
|
|||
#define FTDI_4232H_PID 0x6011 /* Quad channel hi-speed device */
|
||||
#define FTDI_232H_PID 0x6014 /* Single channel hi-speed device */
|
||||
#define FTDI_FTX_PID 0x6015 /* FT-X series (FT201X, FT230X, FT231X, etc) */
|
||||
#define FTDI_FT2233HP_PID 0x6040 /* Dual channel hi-speed device with PD */
|
||||
#define FTDI_FT4233HP_PID 0x6041 /* Quad channel hi-speed device with PD */
|
||||
#define FTDI_FT2232HP_PID 0x6042 /* Dual channel hi-speed device with PD */
|
||||
#define FTDI_FT4232HP_PID 0x6043 /* Quad channel hi-speed device with PD */
|
||||
#define FTDI_FT233HP_PID 0x6044 /* Dual channel hi-speed device with PD */
|
||||
#define FTDI_FT232HP_PID 0x6045 /* Dual channel hi-speed device with PD */
|
||||
#define FTDI_FT4232HA_PID 0x6048 /* Quad channel automotive grade hi-speed device */
|
||||
#define FTDI_SIO_PID 0x8372 /* Product Id SIO application of 8U100AX */
|
||||
#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user