USB: ftdi_sio: fix status line change handling for TIOCMIWAIT and TIOCGICOUNT
[linux-flexiantxendom0.git] / drivers / usb / serial / ftdi_sio.c
index d3e58b5..cfc8e59 100644 (file)
@@ -17,7 +17,7 @@
  * See Documentation/usb/usb-serial.txt for more information on using this
  * driver
  *
- * See http://ftdi-usb-sio.sourceforge.net for upto date testing info
+ * See http://ftdi-usb-sio.sourceforge.net for up to date testing info
  *     and extra documentation
  *
  * Change entries from 2004 and earlier can be found in versions of this
@@ -73,8 +73,10 @@ struct ftdi_private {
                                 */
        int flags;              /* some ASYNC_xxxx flags are supported */
        unsigned long last_dtr_rts;     /* saved modem control outputs */
+       struct async_icount     icount;
        wait_queue_head_t delta_msr_wait; /* Used for TIOCMIWAIT */
-       char prev_status, diff_status;        /* Used for TIOCMIWAIT */
+       char prev_status;        /* Used for TIOCMIWAIT */
+       char transmit_empty;    /* If transmitter is empty or not */
        struct usb_serial_port *port;
        __u16 interface;        /* FT2232C, FT2232H or FT4232H port interface
                                   (0 for FT232/245) */
@@ -99,6 +101,8 @@ struct ftdi_sio_quirk {
 static int   ftdi_jtag_probe(struct usb_serial *serial);
 static int   ftdi_mtxorb_hack_setup(struct usb_serial *serial);
 static int   ftdi_NDI_device_setup(struct usb_serial *serial);
+static int   ftdi_stmclite_probe(struct usb_serial *serial);
+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);
 
@@ -122,6 +126,14 @@ static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
        .port_probe = ftdi_HE_TIRA1_setup,
 };
 
+static struct ftdi_sio_quirk ftdi_stmclite_quirk = {
+       .probe  = ftdi_stmclite_probe,
+};
+
+static struct ftdi_sio_quirk ftdi_8u2232c_quirk = {
+       .probe  = ftdi_8u2232c_probe,
+};
+
 /*
  * The 8U232AM has the same API as the sio except for:
  * - it can support MUCH higher baudrates; up to:
@@ -145,6 +157,9 @@ static struct ftdi_sio_quirk ftdi_HE_TIRA1_quirk = {
  * /sys/bus/usb/ftdi_sio/new_id, then send patch/report!
  */
 static struct usb_device_id id_table_combined [] = {
+       { USB_DEVICE(FTDI_VID, FTDI_ZEITCONTROL_TAGTRACE_MIFARE_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_CTI_MINI_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_CTI_NANO_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_AMC232_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_CANUSB_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_CANDAPTER_PID) },
@@ -169,8 +184,11 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) },
-       { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) ,
+               .driver_info = (kernel_ulong_t)&ftdi_8u2232c_quirk },
        { 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_MICRO_CHAMELEON_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_OPENDCC_PID) },
@@ -191,6 +209,8 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_XF_640_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_XF_642_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_DSS20_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_URBAN_0_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_URBAN_1_PID) },
        { USB_DEVICE(FTDI_NF_RIC_VID, FTDI_NF_RIC_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_VNHCPCUSB_D_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MTXORB_0_PID) },
@@ -201,6 +221,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_MTXORB_5_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MTXORB_6_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_R2000KU_TRUE_RNG) },
+       { USB_DEVICE(FTDI_VID, FTDI_VARDAAN_PID) },
        { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0100_PID) },
        { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0101_PID) },
        { USB_DEVICE(MTXORB_VID, MTXORB_FTDI_RANGE_0102_PID) },
@@ -516,8 +537,13 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_6_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_7_PID) },
        { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803_8_PID) },
+       { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_1_PID) },
+       { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_2_PID) },
+       { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_3_PID) },
+       { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2803R_4_PID) },
        { USB_DEVICE(IDTECH_VID, IDTECH_IDT1221U_PID) },
        { USB_DEVICE(OCT_VID, OCT_US101_PID) },
+       { USB_DEVICE(OCT_VID, OCT_DK201_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_HE_TIRA1_PID),
                .driver_info = (kernel_ulong_t)&ftdi_HE_TIRA1_quirk },
        { USB_DEVICE(FTDI_VID, FTDI_USB_UIRT_PID),
@@ -556,6 +582,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) },
        /*
         * ELV devices:
         */
@@ -614,6 +641,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_OCEANIC_PID) },
        { USB_DEVICE(TTI_VID, TTI_QL355P_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) },
+       { USB_DEVICE(ACTON_VID, ACTON_SPECTRAPRO_PID) },
        { USB_DEVICE(CONTEC_VID, CONTEC_COM1USBH_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) },
        { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) },
@@ -635,6 +663,7 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, EVER_ECO_PRO_CDS) },
        { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_1_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_2_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_4N_GALAXY_DE_3_PID) },
        { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_0_PID) },
        { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_1_PID) },
        { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_2_PID) },
@@ -674,7 +703,17 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_PCDJ_DAC2_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_RRCIRKITS_LOCOBUFFER_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ASK_RDR400_PID) },
-       { USB_DEVICE(ICOM_ID1_VID, ICOM_ID1_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_1_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_OPC_U_UC_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C1_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP2C2_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP2D_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VT_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP2VR_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVT_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP4KVR_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVT_PID) },
+       { USB_DEVICE(ICOM_VID, ICOM_ID_RP2KVR_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ACG_HFDUAL_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_YEI_SERVOCENTER31_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) },
@@ -696,13 +735,17 @@ static struct usb_device_id id_table_combined [] = {
                .driver_info = (kernel_ulong_t)&ftdi_NDI_device_quirk },
        { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) },
        { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_SERIAL_VX7_PID) },
+       { USB_DEVICE(RTSYSTEMS_VID, RTSYSTEMS_CT29B_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_MAXSTREAM_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_PHI_FISCO_PID) },
        { USB_DEVICE(TML_VID, TML_USB_SERIAL_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_ELSTER_UNICOM_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_PROPOX_JTAGCABLEII_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_PROPOX_ISPCABLEIII_PID) },
        { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+       { USB_DEVICE(OLIMEX_VID, OLIMEX_ARM_USB_OCD_H_PID),
+               .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(FIC_VID, FIC_NEO1973_DEBUG_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(FTDI_VID, FTDI_OOCDLINK_PID),
@@ -711,6 +754,8 @@ static struct usb_device_id id_table_combined [] = {
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(FTDI_VID, LMI_LM3S_EVAL_BOARD_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+       { USB_DEVICE(FTDI_VID, LMI_LM3S_ICDI_BOARD_PID),
+               .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(FTDI_VID, FTDI_TURTELIZER_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(RATOC_VENDOR_ID, RATOC_PRODUCT_ID_USB60F) },
@@ -757,6 +802,7 @@ static struct usb_device_id id_table_combined [] = {
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+       { USB_DEVICE(MICROCHIP_VID, MICROCHIP_USB_BOARD_PID) },
        { USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
        { USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
@@ -765,7 +811,11 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
        { USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
                .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+       { USB_DEVICE(FTDI_VID, TI_XDS100V2_PID),
+               .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
        { USB_DEVICE(FTDI_VID, HAMEG_HO820_PID) },
+       { USB_DEVICE(FTDI_VID, HAMEG_HO720_PID) },
+       { USB_DEVICE(FTDI_VID, HAMEG_HO730_PID) },
        { USB_DEVICE(FTDI_VID, HAMEG_HO870_PID) },
        { USB_DEVICE(FTDI_VID, MJSG_GENERIC_PID) },
        { USB_DEVICE(FTDI_VID, MJSG_SR_RADIO_PID) },
@@ -791,6 +841,19 @@ static struct usb_device_id id_table_combined [] = {
        { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MAXI_WING_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_MEDIA_WING_PID) },
        { USB_DEVICE(FTDI_VID, FTDI_CHAMSYS_WING_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LOGBOOKML_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_LS_LOGBOOK_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_SCIENCESCOPE_HS_LOGBOOK_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_CINTERION_MC55I_PID) },
+       { USB_DEVICE(FTDI_VID, FTDI_DOTEC_PID) },
+       { USB_DEVICE(QIHARDWARE_VID, MILKYMISTONE_JTAGSERIAL_PID),
+               .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+       { USB_DEVICE(ST_VID, ST_STMCLT1030_PID),
+               .driver_info = (kernel_ulong_t)&ftdi_stmclite_quirk },
+       { USB_DEVICE(FTDI_VID, FTDI_RF_R106) },
+       { USB_DEVICE(FTDI_VID, FTDI_DISTORTEC_JTAG_LOCK_PICK_PID),
+               .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+       { USB_DEVICE(FTDI_VID, FTDI_LUMEL_PD12_PID) },
        { },                                    /* Optional parameter entry */
        { }                                     /* Terminating entry */
 };
@@ -812,7 +875,9 @@ static const char *ftdi_chip_name[] = {
        [FT2232C] = "FT2232C",
        [FT232RL] = "FT232RL",
        [FT2232H] = "FT2232H",
-       [FT4232H] = "FT4232H"
+       [FT4232H] = "FT4232H",
+       [FT232H]  = "FT232H",
+       [FTX]     = "FT-X"
 };
 
 
@@ -837,10 +902,12 @@ 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, struct file *file);
-static int  ftdi_tiocmset(struct tty_struct *tty, struct file *file,
+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, struct file *file,
+static int ftdi_get_icount(struct tty_struct *tty,
+                          struct serial_icounter_struct *icount);
+static int  ftdi_ioctl(struct tty_struct *tty,
                        unsigned int cmd, unsigned long arg);
 static void ftdi_break_ctl(struct tty_struct *tty, int break_state);
 
@@ -874,6 +941,7 @@ static struct usb_serial_driver ftdi_sio_device = {
        .prepare_write_buffer = ftdi_prepare_write_buffer,
        .tiocmget =             ftdi_tiocmget,
        .tiocmset =             ftdi_tiocmset,
+       .get_icount =           ftdi_get_icount,
        .ioctl =                ftdi_ioctl,
        .set_termios =          ftdi_set_termios,
        .break_ctl =            ftdi_break_ctl,
@@ -946,7 +1014,7 @@ static __u32 ftdi_2232h_baud_base_to_divisor(int baud, int base)
        int divisor3;
 
        /* hi-speed baud rate is 10-bit sampling instead of 16-bit */
-       divisor3 = (base / 10 / baud) * 8;
+       divisor3 = base * 8 / (baud * 10);
 
        divisor = divisor3 >> 3;
        divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
@@ -1110,7 +1178,8 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
                break;
        case FT232BM: /* FT232BM chip */
        case FT2232C: /* FT2232C chip */
-       case FT232RL:
+       case FT232RL: /* FT232RL chip */
+       case FTX:     /* FT-X series */
                if (baud <= 3000000) {
                        __u16 product_id = le16_to_cpu(
                                port->serial->dev->descriptor.idProduct);
@@ -1132,7 +1201,8 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
                break;
        case FT2232H: /* FT2232H chip */
        case FT4232H: /* FT4232H chip */
-               if ((baud <= 12000000) & (baud >= 1200)) {
+       case FT232H:  /* FT232H chip */
+               if ((baud <= 12000000) && (baud >= 1200)) {
                        div_value = ftdi_2232h_baud_to_divisor(baud);
                } else if (baud < 1200) {
                        div_value = ftdi_232bm_baud_to_divisor(baud);
@@ -1166,7 +1236,10 @@ static int change_speed(struct tty_struct *tty, struct usb_serial_port *port)
        urb_index_value = get_ftdi_divisor(tty, port);
        urb_value = (__u16)urb_index_value;
        urb_index = (__u16)(urb_index_value >> 16);
-       if (priv->interface) {  /* FT2232C */
+       if ((priv->chip_type == FT2232C) || (priv->chip_type == FT2232H) ||
+               (priv->chip_type == FT4232H) || (priv->chip_type == FT232H)) {
+               /* Probably the BM type needs the MSB of the encoded fractional
+                * divider also moved like for the chips above. Any infos? */
                urb_index = (__u16)((urb_index << 8) | priv->interface);
        }
 
@@ -1275,8 +1348,7 @@ static int set_serial_info(struct tty_struct *tty,
                goto check_and_exit;
        }
 
-       if ((new_serial.baud_base != priv->baud_base) &&
-           (new_serial.baud_base < 9600)) {
+       if (new_serial.baud_base != priv->baud_base) {
                mutex_unlock(&priv->cfg_lock);
                return -EINVAL;
        }
@@ -1315,6 +1387,23 @@ check_and_exit:
        return 0;
 }
 
+static int get_lsr_info(struct usb_serial_port *port,
+                       struct serial_struct __user *retinfo)
+{
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       unsigned int result = 0;
+
+       if (!retinfo)
+               return -EFAULT;
+
+       if (priv->transmit_empty)
+               result = TIOCSER_TEMT;
+
+       if (copy_to_user(retinfo, &result, sizeof(unsigned int)))
+               return -EFAULT;
+       return 0;
+}
+
 
 /* Determine type of FTDI chip based on USB config and descriptor. */
 static void ftdi_determine_type(struct usb_serial_port *port)
@@ -1376,10 +1465,17 @@ static void ftdi_determine_type(struct usb_serial_port *port)
        } else if (version < 0x600) {
                /* Assume it's an FT232BM (or FT245BM) */
                priv->chip_type = FT232BM;
-       } else {
-               /* Assume it's an FT232R */
+       } else if (version < 0x900) {
+               /* Assume it's an FT232RL */
                priv->chip_type = FT232RL;
+       } else if (version < 0x1000) {
+               /* Assume it's an FT232H */
+               priv->chip_type = FT232H;
+       } else {
+               /* Assume it's an FT-X series device */
+               priv->chip_type = FTX;
        }
+
        dev_info(&udev->dev, "Detected %s\n", ftdi_chip_name[priv->chip_type]);
 }
 
@@ -1417,7 +1513,7 @@ static void ftdi_set_max_packet_size(struct usb_serial_port *port)
        }
 
        /* set max packet size based on descriptor */
-       priv->max_packet_size = le16_to_cpu(ep_desc->wMaxPacketSize);
+       priv->max_packet_size = usb_endpoint_maxp(ep_desc);
 
        dev_info(&udev->dev, "Setting MaxPacketSize %d\n", priv->max_packet_size);
 }
@@ -1506,7 +1602,9 @@ static int create_sysfs_attrs(struct usb_serial_port *port)
                     priv->chip_type == FT2232C ||
                     priv->chip_type == FT232RL ||
                     priv->chip_type == FT2232H ||
-                    priv->chip_type == FT4232H)) {
+                    priv->chip_type == FT4232H ||
+                    priv->chip_type == FT232H ||
+                    priv->chip_type == FTX)) {
                        retval = device_create_file(&port->dev,
                                                    &dev_attr_latency_timer);
                }
@@ -1527,7 +1625,9 @@ static void remove_sysfs_attrs(struct usb_serial_port *port)
                    priv->chip_type == FT2232C ||
                    priv->chip_type == FT232RL ||
                    priv->chip_type == FT2232H ||
-                   priv->chip_type == FT4232H) {
+                   priv->chip_type == FT4232H ||
+                   priv->chip_type == FT232H ||
+                   priv->chip_type == FTX) {
                        device_remove_file(&port->dev, &dev_attr_latency_timer);
                }
        }
@@ -1575,6 +1675,7 @@ static int ftdi_sio_port_probe(struct usb_serial_port *port)
 
        kref_init(&priv->kref);
        mutex_init(&priv->cfg_lock);
+       memset(&priv->icount, 0x00, sizeof(priv->icount));
        init_waitqueue_head(&priv->delta_msr_wait);
 
        priv->flags = ASYNC_LOW_LATENCY;
@@ -1672,6 +1773,38 @@ static int ftdi_jtag_probe(struct usb_serial *serial)
        return 0;
 }
 
+static int ftdi_8u2232c_probe(struct usb_serial *serial)
+{
+       struct usb_device *udev = serial->dev;
+
+       dbg("%s", __func__);
+
+       if ((udev->manufacturer && !strcmp(udev->manufacturer, "CALAO Systems")) ||
+           (udev->product && !strcmp(udev->product, "BeagleBone/XDS100")))
+               return ftdi_jtag_probe(serial);
+
+       return 0;
+}
+
+/*
+ * First and second port on STMCLiteadaptors is reserved for JTAG interface
+ * and the forth port for pio
+ */
+static int ftdi_stmclite_probe(struct usb_serial *serial)
+{
+       struct usb_device *udev = serial->dev;
+       struct usb_interface *interface = serial->interface;
+
+       dbg("%s", __func__);
+
+       if (interface == udev->actconfig->interface[2])
+               return 0;
+
+       dev_info(&udev->dev, "Ignoring serial port reserved for JTAG\n");
+
+       return -ENODEV;
+}
+
 /*
  * The Matrix Orbital VK204-25-USB has an invalid IN endpoint.
  * We have to correct it if we want to read from it.
@@ -1712,6 +1845,7 @@ static int ftdi_sio_port_remove(struct usb_serial_port *port)
 
 static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
 {
+       struct ktermios dummy;
        struct usb_device *dev = port->serial->dev;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
        int result;
@@ -1730,8 +1864,10 @@ static int ftdi_open(struct tty_struct *tty, struct usb_serial_port *port)
           This is same behaviour as serial.c/rs_open() - Kuba */
 
        /* ftdi_set_termios  will send usb control messages */
-       if (tty)
-               ftdi_set_termios(tty, port, tty->termios);
+       if (tty) {
+               memset(&dummy, 0, sizeof(dummy));
+               ftdi_set_termios(tty, port, &dummy);
+       }
 
        /* Start reading from the device */
        result = usb_serial_generic_open(tty, port);
@@ -1807,6 +1943,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
                        c = kfifo_out(&port->write_fifo, &buffer[i + 1], len);
                        if (!c)
                                break;
+                       priv->icount.tx += c;
                        buffer[i] = (c << 2) + 1;
                        count += c + 1;
                }
@@ -1814,6 +1951,7 @@ static int ftdi_prepare_write_buffer(struct usb_serial_port *port,
        } else {
                count = kfifo_out_locked(&port->write_fifo, dest, size,
                                                                &port->lock);
+               priv->icount.tx += count;
        }
 
        return count;
@@ -1842,8 +1980,18 @@ static int ftdi_process_packet(struct tty_struct *tty,
           are only processed once.  */
        status = packet[0] & FTDI_STATUS_B0_MASK;
        if (status != priv->prev_status) {
-               priv->diff_status |= status ^ priv->prev_status;
-               wake_up_interruptible(&priv->delta_msr_wait);
+               char diff_status = status ^ priv->prev_status;
+
+               if (diff_status & FTDI_RS0_CTS)
+                       priv->icount.cts++;
+               if (diff_status & FTDI_RS0_DSR)
+                       priv->icount.dsr++;
+               if (diff_status & FTDI_RS0_RI)
+                       priv->icount.rng++;
+               if (diff_status & FTDI_RS0_RLSD)
+                       priv->icount.dcd++;
+
+               wake_up_interruptible_all(&priv->delta_msr_wait);
                priv->prev_status = status;
        }
 
@@ -1853,20 +2001,32 @@ static int ftdi_process_packet(struct tty_struct *tty,
                 * over framing errors */
                if (packet[1] & FTDI_RS_BI) {
                        flag = TTY_BREAK;
+                       priv->icount.brk++;
                        usb_serial_handle_break(port);
                } else if (packet[1] & FTDI_RS_PE) {
                        flag = TTY_PARITY;
+                       priv->icount.parity++;
                } else if (packet[1] & FTDI_RS_FE) {
                        flag = TTY_FRAME;
+                       priv->icount.frame++;
                }
                /* Overrun is special, not associated with a char */
-               if (packet[1] & FTDI_RS_OE)
+               if (packet[1] & FTDI_RS_OE) {
+                       priv->icount.overrun++;
                        tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+               }
        }
 
+       /* save if the transmitter is empty or not */
+       if (packet[1] & FTDI_RS_TEMT)
+               priv->transmit_empty = 1;
+       else
+               priv->transmit_empty = 0;
+
        len -= 2;
        if (!len)
                return 0;       /* status only */
+       priv->icount.rx += len;
        ch = packet + 2;
 
        if (port->port.console && port->sysrq) {
@@ -1971,13 +2131,19 @@ static void ftdi_set_termios(struct tty_struct *tty,
 
        cflag = termios->c_cflag;
 
-       /* FIXME -For this cut I don't care if the line is really changing or
-          not  - so just do the change regardless  - should be able to
-          compare old_termios and tty->termios */
+       if (old_termios->c_cflag == termios->c_cflag
+           && old_termios->c_ispeed == termios->c_ispeed
+           && old_termios->c_ospeed == termios->c_ospeed)
+               goto no_c_cflag_changes;
+
        /* NOTE These routines can get interrupted by
           ftdi_sio_read_bulk_callback  - need to examine what this means -
           don't see any problems yet */
 
+       if ((old_termios->c_cflag & (CSIZE|PARODD|PARENB|CMSPAR|CSTOPB)) ==
+           (termios->c_cflag & (CSIZE|PARODD|PARENB|CMSPAR|CSTOPB)))
+               goto no_data_parity_stop_changes;
+
        /* Set number of data bits, parity, stop bits */
 
        urb_value = 0;
@@ -2018,6 +2184,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
        }
 
        /* Now do the baudrate */
+no_data_parity_stop_changes:
        if ((cflag & CBAUD) == B0) {
                /* Disable flow control */
                if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
@@ -2045,6 +2212,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
 
        /* Set flow control */
        /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
+no_c_cflag_changes:
        if (cflag & CRTSCTS) {
                dbg("%s Setting to CRTSCTS flow control", __func__);
                if (usb_control_msg(dev,
@@ -2105,10 +2273,9 @@ static void ftdi_set_termios(struct tty_struct *tty,
                }
 
        }
-       return;
 }
 
-static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)
+static int ftdi_tiocmget(struct tty_struct *tty)
 {
        struct usb_serial_port *port = tty->driver_data;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
@@ -2135,6 +2302,8 @@ static int ftdi_tiocmget(struct tty_struct *tty, struct file *file)
        case FT232RL:
        case FT2232H:
        case FT4232H:
+       case FT232H:
+       case FTX:
                len = 2;
                break;
        default:
@@ -2161,7 +2330,7 @@ out:
        return ret;
 }
 
-static int ftdi_tiocmset(struct tty_struct *tty, struct file *file,
+static int ftdi_tiocmset(struct tty_struct *tty,
                        unsigned int set, unsigned int clear)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -2169,11 +2338,34 @@ static int ftdi_tiocmset(struct tty_struct *tty, struct file *file,
        return update_mctrl(port, set, clear);
 }
 
-static int ftdi_ioctl(struct tty_struct *tty, struct file *file,
+static int ftdi_get_icount(struct tty_struct *tty,
+                               struct serial_icounter_struct *icount)
+{
+       struct usb_serial_port *port = tty->driver_data;
+       struct ftdi_private *priv = usb_get_serial_port_data(port);
+       struct async_icount *ic = &priv->icount;
+
+       icount->cts = ic->cts;
+       icount->dsr = ic->dsr;
+       icount->rng = ic->rng;
+       icount->dcd = ic->dcd;
+       icount->tx = ic->tx;
+       icount->rx = ic->rx;
+       icount->frame = ic->frame;
+       icount->parity = ic->parity;
+       icount->overrun = ic->overrun;
+       icount->brk = ic->brk;
+       icount->buf_overrun = ic->buf_overrun;
+       return 0;
+}
+
+static int ftdi_ioctl(struct tty_struct *tty,
                                        unsigned int cmd, unsigned long arg)
 {
        struct usb_serial_port *port = tty->driver_data;
        struct ftdi_private *priv = usb_get_serial_port_data(port);
+       struct async_icount cnow;
+       struct async_icount cprev;
 
        dbg("%s cmd 0x%04x", __func__, cmd);
 
@@ -2197,36 +2389,29 @@ static int ftdi_ioctl(struct tty_struct *tty, struct file *file,
         * This code is borrowed from linux/drivers/char/serial.c
         */
        case TIOCMIWAIT:
-               while (priv != NULL) {
+               cprev = priv->icount;
+               while (1) {
                        interruptible_sleep_on(&priv->delta_msr_wait);
                        /* see if a signal did it */
                        if (signal_pending(current))
                                return -ERESTARTSYS;
-                       else {
-                               char diff = priv->diff_status;
-
-                               if (diff == 0)
-                                       return -EIO; /* no change => error */
-
-                               /* Consume all events */
-                               priv->diff_status = 0;
-
-                               /* Return 0 if caller wanted to know about
-                                  these bits */
-                               if (((arg & TIOCM_RNG) && (diff & FTDI_RS0_RI)) ||
-                                   ((arg & TIOCM_DSR) && (diff & FTDI_RS0_DSR)) ||
-                                   ((arg & TIOCM_CD)  && (diff & FTDI_RS0_RLSD)) ||
-                                   ((arg & TIOCM_CTS) && (diff & FTDI_RS0_CTS))) {
-                                       return 0;
-                               }
-                               /*
-                                * Otherwise caller can't care less about what
-                                * happened,and so we continue to wait for more
-                                * events.
-                                */
+                       cnow = priv->icount;
+                       if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+                           cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+                               return -EIO; /* no change => error */
+                       if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+                           ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+                           ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
+                           ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+                               return 0;
                        }
+                       cprev = cnow;
                }
-               return 0;
+               /* not reached */
+               break;
+       case TIOCSERGETLSR:
+               return get_lsr_info(port, (struct serial_struct __user *)arg);
+               break;
        default:
                break;
        }