Merge tag 'v3.10.95' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / usb / class / cdc-acm.c
index 9b1cbcf8fb7fcfbd0f3b576d207a90dd4421c62f..edfcbc9195a0d16057c52531fa61dd9a1e75d087 100644 (file)
 
 #include "cdc-acm.h"
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+
+/* adjust to 1 to avoid musb bug when lots write with clean urb */
+#undef ACM_NW 
+#define ACM_NW 1
+/* adjust SZ to hsmaxp*20 to prevent tty disc don't accept big size write */
+#define ACM_WB_SZ (512*20)
+
+#define DATA_DUMP_BYTES 15 // wx, how many bytes we'll print out for each packet
+#define DATA_DUMP_SIZE 64 // wx, should be large enough to hold DATA_DUMP_DIGITS
+static unsigned char data_out[DATA_DUMP_SIZE];
+static unsigned char data_in[DATA_DUMP_SIZE];
+/* Debug functions */
+static int enable_debug = 0;
+static int enable_dump = 0;
+//origninal CDC-ACM log, more detail
+#undef dev_dbg
+#define dev_dbg(dev, format, args...)  \
+       do{ \
+               if(enable_debug) { \
+                       dev_printk(KERN_WARNING, dev, "[CDC-ACM] " format, ##args); \
+               } \
+       }while(0)
+#undef dev_vdbg
+#define dev_vdbg  dev_dbg
+//MTK added CDC-ACM log, more critical
+#define dbg_mtk(dev, format, args...)  \
+       do{ \
+               dev_printk(KERN_WARNING, dev, "[CDC-ACM-MTK] " format "\n", ##args); \
+       }while(0)
+#else
+#define dbg_mtk(dev, format, args...) do{}while(0)
+#endif
+
 
 #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
@@ -60,6 +94,167 @@ static struct acm *acm_table[ACM_TTY_MINORS];
 
 static DEFINE_MUTEX(acm_table_lock);
 
+#define MYDBG(fmt, args...) do {printk(KERN_WARNING "MTK_ACM, <%s(), %d> " fmt, __func__, __LINE__, ## args); }while(0)
+
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+
+#define RECORD_SZ 100
+#define DUMP_DELAY 3
+struct record_entry
+{
+       struct timeval cur_time;
+       u32      transfer_buffer_length;
+       u32 actual_length;
+       int status;
+       unsigned char data;
+
+}w1[RECORD_SZ], w2[RECORD_SZ], r1[RECORD_SZ], r2[RECORD_SZ];
+
+static int w1_idx, w2_idx, r1_idx, r2_idx;
+static int record_enable = 0;
+static struct timeval tv_start; 
+void dump_record(void)
+{
+       int i, index_limit;
+       struct record_entry *ptr;
+
+       index_limit = w1_idx;
+       ptr = w1;
+       for(i = 0; i < index_limit; i++){
+               MYDBG("w1, time:(%d,%d), reqlen:%d, data:%x\n", 
+                               (unsigned int)ptr[i].cur_time.tv_sec, (unsigned int)ptr[i].cur_time.tv_usec, (unsigned int)ptr[i].transfer_buffer_length, ptr[i].data);
+               mdelay(DUMP_DELAY);
+       }
+       
+       index_limit = r1_idx;
+       ptr = r1;
+       for(i = 0; i < index_limit; i++){
+               MYDBG("r1, time:(%d,%d), reqlen:%d\n", 
+                               (unsigned int)ptr[i].cur_time.tv_sec,  (unsigned int)ptr[i].cur_time.tv_usec, ptr[i].transfer_buffer_length);
+               mdelay(DUMP_DELAY);
+       }
+       
+       index_limit = w2_idx;
+       ptr = w2;
+       for(i = 0; i < index_limit; i++){
+               MYDBG("w2, time:(%d,%d), reqlen:%d, actlen:%d, status:%d, data:%x\n",  
+                               (unsigned int)ptr[i].cur_time.tv_sec,  (unsigned int)ptr[i].cur_time.tv_usec, ptr[i].transfer_buffer_length, ptr[i].actual_length, ptr[i].status, ptr[i].data);
+               mdelay(DUMP_DELAY);
+       }
+       
+       index_limit = r2_idx;
+       ptr = r2;
+       for(i = 0; i < index_limit; i++){
+               MYDBG("r2, time:(%d,%d), reqlen:%d, actlen:%d, status:%d, data:%x\n",  
+                               (unsigned int)ptr[i].cur_time.tv_sec,  (unsigned int)ptr[i].cur_time.tv_usec, ptr[i].transfer_buffer_length, ptr[i].actual_length, ptr[i].status, ptr[i].data);
+               mdelay(DUMP_DELAY);
+       }
+
+}
+void record_activity(struct urb *urb, int is_in, int is_complete)
+{
+       struct timeval tv_time;
+
+       if(!record_enable)
+               return;
+
+       do_gettimeofday(&tv_time);
+       tv_time.tv_sec = tv_time.tv_sec - tv_start.tv_sec;
+       tv_time.tv_usec = tv_time.tv_usec - tv_start.tv_usec;
+       if(is_in){
+               if(is_complete){
+                       if(r2_idx >= RECORD_SZ)
+                               return;
+
+                       r2[r2_idx].cur_time = tv_time;
+                       r2[r2_idx].transfer_buffer_length = urb->transfer_buffer_length;
+                       r2[r2_idx].actual_length = urb->actual_length;
+                       r2[r2_idx].status = urb->status;
+                       r2[r2_idx].data = *((unsigned char*)urb->transfer_buffer);
+                       r2_idx++;
+                       return;
+               }
+               if(r1_idx >= RECORD_SZ)
+                       return;
+               r1[r1_idx].cur_time = tv_time;
+               r1[r1_idx].transfer_buffer_length = urb->transfer_buffer_length;
+               r1[r1_idx].actual_length = urb->actual_length;
+               r1[r1_idx].status = urb->status;
+               r1_idx++;
+       }else{
+               if(is_complete){
+                       if(w2_idx >= RECORD_SZ)
+                               return;
+                       w2[w2_idx].cur_time = tv_time;
+                       w2[w2_idx].transfer_buffer_length = urb->transfer_buffer_length;
+                       w2[w2_idx].actual_length = urb->actual_length;
+                       w2[w2_idx].status = urb->status;
+                       w2[w2_idx].data = *((unsigned char*)urb->transfer_buffer);
+                       w2_idx++;
+                       return;
+               }
+               if(w1_idx >= RECORD_SZ)
+                       return;
+               w1[w1_idx].cur_time = tv_time;
+               w1[w1_idx].transfer_buffer_length = urb->transfer_buffer_length;
+               w1[w1_idx].actual_length = urb->actual_length;
+               w1[w1_idx].status = urb->status;
+               w1[w1_idx].data = *((unsigned char*)urb->transfer_buffer);
+               w1_idx++;
+       }
+}      
+
+bool usb_h_acm_all_clear(void)
+{
+       int i;
+       int count = 0;
+       for (i = 0; i < ACM_TTY_MINORS; i++) {
+               if(acm_table[i] != NULL) {
+                       count++;
+               }
+       }
+       MYDBG("count<%d>\n", count); 
+       return !count;
+}
+EXPORT_SYMBOL_GPL(usb_h_acm_all_clear);
+
+#define CHECK_INTERVAL 2
+#define CB_NUM 3
+extern unsigned long volatile jiffies;
+static unsigned long callback_check_timeout[CB_NUM];
+static char *callback_name[CB_NUM] = {
+       "acm_read_bulk_callback",
+       "acm_write_bulk",
+       "acm_ctrl_irq",
+};
+void mark_callback_alive(char *func_name, struct urb *urb, struct acm *acm)
+{
+
+
+       int i;
+       for(i = 0; i < CB_NUM ; i++)
+       {
+               if(!strcmp(func_name, callback_name[i])){
+                       if(enable_debug || time_after(jiffies, callback_check_timeout[i]))
+                       {
+                               MYDBG("%s,ep(%d),len(%d,%d),data(%x),sts(%d), minor(%d)\n", 
+                                               func_name,
+                                               urb->ep->desc.bEndpointAddress,         
+                                               urb->actual_length, 
+                                               urb->transfer_buffer_length, 
+                                               *((unsigned char*)urb->transfer_buffer), 
+                                               urb->status,
+                                               acm->minor);
+                               callback_check_timeout[i] = jiffies + HZ * CHECK_INTERVAL;
+                       }
+                       break;
+               }
+       }
+}
+
+
+#endif
+
 /*
  * acm_table accessors
  */
@@ -122,13 +317,23 @@ static void acm_release_minor(struct acm *acm)
 static int acm_ctrl_msg(struct acm *acm, int request, int value,
                                                        void *buf, int len)
 {
-       int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
+       int retval;
+
+       retval = usb_autopm_get_interface(acm->control);
+       if (retval)
+               return retval;
+
+       retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0),
                request, USB_RT_ACM, value,
                acm->control->altsetting[0].desc.bInterfaceNumber,
                buf, len, 5000);
+
        dev_dbg(&acm->control->dev,
                        "%s - rq 0x%02x, val %#x, len %#x, result %d\n",
                        __func__, request, value, len, retval);
+
+       usb_autopm_put_interface(acm->control);
+
        return retval < 0 ? retval : 0;
 }
 
@@ -206,6 +411,9 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
        wb->urb->transfer_buffer_length = wb->len;
        wb->urb->dev = acm->dev;
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       record_activity(wb->urb, 0, 0);
+#endif
        rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
        if (rc < 0) {
                dev_err(&acm->data->dev,
@@ -233,12 +441,9 @@ static int acm_write_start(struct acm *acm, int wbn)
                                                        acm->susp_count);
        usb_autopm_get_interface_async(acm->control);
        if (acm->susp_count) {
-               if (!acm->delayed_wb)
-                       acm->delayed_wb = wb;
-               else
-                       usb_autopm_put_interface_async(acm->control);
+               usb_anchor_urb(wb->urb, &acm->delayed);
                spin_unlock_irqrestore(&acm->write_lock, flags);
-               return 0;       /* A white lie */
+               return 0;
        }
        usb_mark_last_busy(acm->dev);
 
@@ -305,18 +510,21 @@ static void acm_ctrl_irq(struct urb *urb)
        case -ENOENT:
        case -ESHUTDOWN:
                /* this urb is terminated, clean up */
-               dev_dbg(&acm->control->dev,
+               dev_err(&acm->control->dev,
                                "%s - urb shutting down with status: %d\n",
                                __func__, status);
                return;
        default:
-               dev_dbg(&acm->control->dev,
+               dev_err(&acm->control->dev,
                                "%s - nonzero urb status received: %d\n",
                                __func__, status);
                goto exit;
        }
 
        usb_mark_last_busy(acm->dev);
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       mark_callback_alive(__func__, urb, acm);
+#endif
 
        data = (unsigned char *)(dr + 1);
        switch (dr->bNotificationType) {
@@ -369,13 +577,19 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
 {
        int res;
 
-       if (!test_and_clear_bit(index, &acm->read_urbs_free))
+       if (!test_and_clear_bit(index, &acm->read_urbs_free)){
                return 0;
+       }
 
        dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       record_activity(acm->read_urbs[index], 1, 0);
+#endif
+
        res = usb_submit_urb(acm->read_urbs[index], mem_flags);
        if (res) {
+               MYDBG("urb fail(%d)\n", res);
                if (res != -EPERM) {
                        dev_err(&acm->data->dev,
                                        "%s - usb_submit_urb failed: %d\n",
@@ -395,8 +609,9 @@ static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
 
        for (i = 0; i < acm->rx_buflimit; ++i) {
                res = acm_submit_read_urb(acm, i, mem_flags);
-               if (res)
+               if (res){
                        return res;
+               }
        }
 
        return 0;
@@ -404,8 +619,23 @@ static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
 
 static void acm_process_read_urb(struct acm *acm, struct urb *urb)
 {
-       if (!urb->actual_length)
+
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       int i, len;
+#endif
+       if (!urb->actual_length){
                return;
+       }
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       if(enable_dump) {
+               len = sprintf(data_in, "DT-I: ");
+               for(i=0; i<urb->actual_length && i<DATA_DUMP_BYTES; i++) {
+                       len += sprintf(data_in+len, "%02X ", *(((unsigned char *)(urb->transfer_buffer))+i));
+               }
+               sprintf(data_in+len, "\n");
+               dbg_mtk(&acm->data->dev, "%s", data_in);
+       }
+#endif
 
        tty_insert_flip_string(&acm->port, urb->transfer_buffer,
                        urb->actual_length);
@@ -422,6 +652,10 @@ static void acm_read_bulk_callback(struct urb *urb)
                                        rb->index, urb->actual_length);
        set_bit(rb->index, &acm->read_urbs_free);
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       record_activity(urb, 1, 1);
+#endif
+
        if (!acm->dev) {
                dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
                return;
@@ -429,10 +663,13 @@ static void acm_read_bulk_callback(struct urb *urb)
        usb_mark_last_busy(acm->dev);
 
        if (urb->status) {
-               dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
+               dev_err(&acm->data->dev, "%s - non-zero urb status: %d\n",
                                                        __func__, urb->status);
                return;
        }
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       mark_callback_alive(__func__, urb, acm);
+#endif
        acm_process_read_urb(acm, urb);
 
        /* throttle device if requested by tty */
@@ -453,13 +690,22 @@ static void acm_write_bulk(struct urb *urb)
        struct acm *acm = wb->instance;
        unsigned long flags;
 
-       if (urb->status || (urb->actual_length != urb->transfer_buffer_length))
-               dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
+
+       if (urb->status || (urb->actual_length != urb->transfer_buffer_length)){
+               dev_err(&acm->data->dev, "%s - len %d/%d, status %d, data(%x)\n",
                        __func__,
                        urb->actual_length,
                        urb->transfer_buffer_length,
-                       urb->status);
+                       urb->status,
+                       *((char *)(urb->transfer_buffer)));
+       }
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       record_activity(urb, 0, 1);
+#endif
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       mark_callback_alive(__func__, urb, acm);
+#endif
        spin_lock_irqsave(&acm->write_lock, flags);
        acm_write_done(acm, wb);
        spin_unlock_irqrestore(&acm->write_lock, flags);
@@ -503,10 +749,46 @@ error_init_termios:
        return retval;
 }
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+extern struct usb_device *get_usb11_child_udev(void);
+extern int usb_autoresume_device(struct usb_device *udev);
+#endif
 static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 {
-       struct acm *acm = tty->driver_data;
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       struct usb_device *udev;
+       int result;
+#endif
+       struct acm *acm;
+       acm = tty->driver_data;
+
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       dbg_mtk(&acm->control->dev, "%s port_cnt=%d", __func__, acm->port.count);
+       MYDBG("ctrl:%x, read:%x, write:%x\n",
+                       (acm->control->cur_altsetting->endpoint[0].desc).bEndpointAddress,
+                       (acm->data->cur_altsetting->endpoint[0].desc).bEndpointAddress,
+                       (acm->data->cur_altsetting->endpoint[1].desc).bEndpointAddress);
+
+
+#define META_BIN_NAME "meta_tst"
+#define MDDOWNLOADER_BIN_NAME "downloader"
 
+       /* make sure usb1 is always alive in dl/meta mode */
+       if(!strcmp(META_BIN_NAME, current->comm) || !strcmp(MDDOWNLOADER_BIN_NAME, current->comm)){
+               udev = get_usb11_child_udev();
+               result = usb_autoresume_device(udev);
+               dbg_mtk(&acm->control->dev, "%s, auto result:%d", __func__, result);
+       }
+
+       if(!strcmp(MDDOWNLOADER_BIN_NAME, current->comm)){
+               record_enable = 1;      
+               w1_idx = w2_idx = r1_idx = r2_idx = 0;
+               do_gettimeofday(&tv_start);
+       }
+
+#else
+       dev_dbg(&acm->control->dev, "%s\n", __func__);
+#endif
        dev_dbg(tty->dev, "%s\n", __func__);
 
        return tty_port_open(&acm->port, tty, filp);
@@ -516,6 +798,7 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
 {
        struct acm *acm = container_of(port, struct acm, port);
        int retval = -ENODEV;
+       int i;
 
        dev_dbg(&acm->control->dev, "%s\n", __func__);
 
@@ -533,6 +816,11 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
         */
        set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
        acm->control->needs_remote_wakeup = 1;
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+#ifdef CONFIG_PM_RUNTIME
+       acm->control->needs_remote_wakeup = 0;          
+#endif
+#endif
 
        acm->ctrlurb->dev = acm->dev;
        if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
@@ -564,6 +852,8 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty)
        return 0;
 
 error_submit_read_urbs:
+       for (i = 0; i < acm->rx_buflimit; i++)
+               usb_kill_urb(acm->read_urbs[i]);
        acm->ctrlout = 0;
        acm_set_control(acm, acm->ctrlout);
 error_set_control:
@@ -591,21 +881,35 @@ static void acm_port_destruct(struct tty_port *port)
 static void acm_port_shutdown(struct tty_port *port)
 {
        struct acm *acm = container_of(port, struct acm, port);
+       struct urb *urb;
+       struct acm_wb *wb;
        int i;
+       int pm_err;
 
        dev_dbg(&acm->control->dev, "%s\n", __func__);
 
        mutex_lock(&acm->mutex);
        if (!acm->disconnected) {
-               usb_autopm_get_interface(acm->control);
+               pm_err = usb_autopm_get_interface(acm->control);
                acm_set_control(acm, acm->ctrlout = 0);
+
+               for (;;) {
+                       urb = usb_get_from_anchor(&acm->delayed);
+                       if (!urb)
+                               break;
+                       wb = urb->context;
+                       wb->use = 0;
+                       usb_autopm_put_interface_async(acm->control);
+               }
+
                usb_kill_urb(acm->ctrlurb);
                for (i = 0; i < ACM_NW; i++)
                        usb_kill_urb(acm->wb[i].urb);
                for (i = 0; i < acm->rx_buflimit; i++)
                        usb_kill_urb(acm->read_urbs[i]);
                acm->control->needs_remote_wakeup = 0;
-               usb_autopm_put_interface(acm->control);
+               if (!pm_err)
+                       usb_autopm_put_interface(acm->control);
        }
        mutex_unlock(&acm->mutex);
 }
@@ -628,6 +932,13 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
 {
        struct acm *acm = tty->driver_data;
        dev_dbg(&acm->control->dev, "%s\n", __func__);
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       dbg_mtk(&acm->control->dev, "%s port_cnt=%d", __func__, acm->port.count);
+       if(!strcmp(MDDOWNLOADER_BIN_NAME, current->comm)){
+               record_enable = 0;
+               dump_record();
+       }
+#endif
        tty_port_close(&acm->port, tty, filp);
 }
 
@@ -639,12 +950,25 @@ static int acm_tty_write(struct tty_struct *tty,
        unsigned long flags;
        int wbn;
        struct acm_wb *wb;
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       int i, len;
+#endif
 
        if (!count)
                return 0;
 
        dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       if(enable_dump) {
+               len = sprintf(data_out, "DT-O: ");
+               for(i=0; i<count && i<DATA_DUMP_BYTES; i++) {
+                       len += sprintf(data_out+len, "%02X ", *(buf+i));
+               }
+               sprintf(data_out+len, "\n");
+               dbg_mtk(&acm->data->dev, "%s", data_out);
+       }
+#endif
        spin_lock_irqsave(&acm->write_lock, flags);
        wbn = acm_wb_alloc(acm);
        if (wbn < 0) {
@@ -730,6 +1054,11 @@ static int acm_tty_tiocmget(struct tty_struct *tty)
 {
        struct acm *acm = tty->driver_data;
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       if(enable_dump) {
+               dbg_mtk(&acm->control->dev, "tiocmget ctrlin=%x\n", acm->ctrlin);
+       }
+#endif
        return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) |
               (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
               (acm->ctrlin  & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
@@ -744,6 +1073,11 @@ static int acm_tty_tiocmset(struct tty_struct *tty,
        struct acm *acm = tty->driver_data;
        unsigned int newctrl;
 
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       if(enable_dump) {
+               dbg_mtk(&acm->control->dev, "tiocmset ctrlout=%x\n", acm->ctrlout);
+       }
+#endif
        newctrl = acm->ctrlout;
        set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) |
                                        (set & TIOCM_RTS ? ACM_CTRL_RTS : 0);
@@ -859,11 +1193,12 @@ static void acm_tty_set_termios(struct tty_struct *tty,
        /* FIXME: Needs to clear unsupported bits in the termios */
        acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
 
-       if (!newline.dwDTERate) {
+       if (C_BAUD(tty) == B0) {
                newline.dwDTERate = acm->line.dwDTERate;
                newctrl &= ~ACM_CTRL_DTR;
-       } else
+       } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
                newctrl |=  ACM_CTRL_DTR;
+       }
 
        if (newctrl != acm->ctrlout)
                acm_set_control(acm, acm->ctrlout = newctrl);
@@ -1062,10 +1397,11 @@ next_desc:
        } else {
                control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
                data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
-               if (!control_interface || !data_interface) {
-                       dev_dbg(&intf->dev, "no interfaces\n");
-                       return -ENODEV;
-               }
+       }
+
+       if (!control_interface || !data_interface) {
+               dev_dbg(&intf->dev, "no interfaces\n");
+               return -ENODEV;
        }
 
        if (data_interface_num != call_interface_num)
@@ -1169,7 +1505,11 @@ made_compressed_probe:
        readsize = usb_endpoint_maxp(epread) *
                                (quirks == SINGLE_RX_URB ? 1 : 2);
        acm->combined_interfaces = combined_interfaces;
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       acm->writesize = ACM_WB_SZ;
+#else  
        acm->writesize = usb_endpoint_maxp(epwrite) * 20;
+#endif
        acm->control = control_interface;
        acm->data = data_interface;
        acm->minor = minor;
@@ -1190,6 +1530,7 @@ made_compressed_probe:
                acm->bInterval = epread->bInterval;
        tty_port_init(&acm->port);
        acm->port.ops = &acm_port_ops;
+       init_usb_anchor(&acm->delayed);
 
        buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
        if (!buf) {
@@ -1339,6 +1680,7 @@ alloc_fail8:
                                &dev_attr_wCountryCodes);
                device_remove_file(&acm->control->dev,
                                &dev_attr_iCountryCodeRelDate);
+               kfree(acm->country_codes);
        }
        device_remove_file(&acm->control->dev, &dev_attr_bmCapabilities);
 alloc_fail7:
@@ -1434,18 +1776,26 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
        struct acm *acm = usb_get_intfdata(intf);
        int cnt;
 
+       dbg_mtk(&acm->control->dev, "%s intf=%d", __func__, intf->cur_altsetting->desc.bInterfaceNumber);
+       spin_lock_irq(&acm->read_lock);
+       spin_lock(&acm->write_lock);
        if (PMSG_IS_AUTO(message)) {
-               int b;
-
-               spin_lock_irq(&acm->write_lock);
-               b = acm->transmitting;
-               spin_unlock_irq(&acm->write_lock);
-               if (b)
+               if (acm->transmitting) {
+                       spin_unlock(&acm->write_lock);
+                       spin_unlock_irq(&acm->read_lock);
                        return -EBUSY;
+               }
+       }else{
+               int i;
+               for (i = 0; i < ACM_NW; i++){
+                       if(acm->wb[i].use){
+                               spin_unlock(&acm->write_lock);
+                               spin_unlock_irq(&acm->read_lock);
+                               return -EBUSY;
+                       }
+               }
        }
 
-       spin_lock_irq(&acm->read_lock);
-       spin_lock(&acm->write_lock);
        cnt = acm->susp_count++;
        spin_unlock(&acm->write_lock);
        spin_unlock_irq(&acm->read_lock);
@@ -1453,8 +1803,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
        if (cnt)
                return 0;
 
-       if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags))
-               stop_data_traffic(acm);
+       stop_data_traffic(acm);
 
        return 0;
 }
@@ -1462,42 +1811,43 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
 static int acm_resume(struct usb_interface *intf)
 {
        struct acm *acm = usb_get_intfdata(intf);
-       struct acm_wb *wb;
+       struct urb *urb;
        int rv = 0;
-       int cnt;
+
+       dbg_mtk(&acm->control->dev, "%s intf=%d", __func__, intf->cur_altsetting->desc.bInterfaceNumber);
 
        spin_lock_irq(&acm->read_lock);
-       acm->susp_count -= 1;
-       cnt = acm->susp_count;
-       spin_unlock_irq(&acm->read_lock);
+       spin_lock(&acm->write_lock);
 
-       if (cnt)
-               return 0;
+       if (--acm->susp_count)
+               goto out;
 
        if (test_bit(ASYNCB_INITIALIZED, &acm->port.flags)) {
-               rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
-
-               spin_lock_irq(&acm->write_lock);
-               if (acm->delayed_wb) {
-                       wb = acm->delayed_wb;
-                       acm->delayed_wb = NULL;
-                       spin_unlock_irq(&acm->write_lock);
-                       acm_start_wb(acm, wb);
-               } else {
-                       spin_unlock_irq(&acm->write_lock);
+               rv = usb_submit_urb(acm->ctrlurb, GFP_ATOMIC);
+
+               for (;;) {
+                       urb = usb_get_from_anchor(&acm->delayed);
+                       if (!urb)
+                               break;
+
+                       acm_start_wb(acm, urb->context);
                }
 
                /*
                 * delayed error checking because we must
                 * do the write path at all cost
                 */
-               if (rv < 0)
-                       goto err_out;
+               if (rv < 0){
+                       MYDBG("urb fail:%d\n", rv);
+                       goto out;
+               }
 
-               rv = acm_submit_read_urbs(acm, GFP_NOIO);
+               rv = acm_submit_read_urbs(acm, GFP_ATOMIC);
        }
+out:
+       spin_unlock(&acm->write_lock);
+       spin_unlock_irq(&acm->read_lock);
 
-err_out:
        return rv;
 }
 
@@ -1529,6 +1879,8 @@ static int acm_reset_resume(struct usb_interface *intf)
 
 static const struct usb_device_id acm_ids[] = {
        /* quirky and broken devices */
+       { USB_DEVICE(0x17ef, 0x7000), /* Lenovo USB modem */
+       .driver_info = NO_UNION_NORMAL, },/* has no union descriptor */
        { USB_DEVICE(0x0870, 0x0001), /* Metricom GS Modem */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
@@ -1568,17 +1920,32 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
+       { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
        { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
        },
        /* Motorola H24 HSPA module: */
        { USB_DEVICE(0x22b8, 0x2d91) }, /* modem                                */
-       { USB_DEVICE(0x22b8, 0x2d92) }, /* modem           + diagnostics        */
-       { USB_DEVICE(0x22b8, 0x2d93) }, /* modem + AT port                      */
-       { USB_DEVICE(0x22b8, 0x2d95) }, /* modem + AT port + diagnostics        */
-       { USB_DEVICE(0x22b8, 0x2d96) }, /* modem                         + NMEA */
-       { USB_DEVICE(0x22b8, 0x2d97) }, /* modem           + diagnostics + NMEA */
-       { USB_DEVICE(0x22b8, 0x2d99) }, /* modem + AT port               + NMEA */
-       { USB_DEVICE(0x22b8, 0x2d9a) }, /* modem + AT port + diagnostics + NMEA */
+       { USB_DEVICE(0x22b8, 0x2d92),   /* modem           + diagnostics        */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d93),   /* modem + AT port                      */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d95),   /* modem + AT port + diagnostics        */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d96),   /* modem                         + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d97),   /* modem           + diagnostics + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d99),   /* modem + AT port               + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
+       { USB_DEVICE(0x22b8, 0x2d9a),   /* modem + AT port + diagnostics + NMEA */
+       .driver_info = NO_UNION_NORMAL, /* handle only modem interface          */
+       },
 
        { USB_DEVICE(0x0572, 0x1329), /* Hummingbird huc56s (Conexant) */
        .driver_info = NO_UNION_NORMAL, /* union descriptor misplaced on
@@ -1688,6 +2055,11 @@ static const struct usb_device_id acm_ids[] = {
        },
 #endif
 
+       /* Exclude Infineon Flash Loader utility */
+       { USB_DEVICE(0x058b, 0x0041),
+       .driver_info = IGNORE_DEVICE,
+       },
+
        /* control interfaces without any protocol set */
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
                USB_CDC_PROTO_NONE) },
@@ -1756,6 +2128,9 @@ static const struct tty_operations acm_ops = {
 static int __init acm_init(void)
 {
        int retval;
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       int i;
+#endif
        acm_tty_driver = alloc_tty_driver(ACM_TTY_MINORS);
        if (!acm_tty_driver)
                return -ENOMEM;
@@ -1769,6 +2144,19 @@ static int __init acm_init(void)
        acm_tty_driver->init_termios = tty_std_termios;
        acm_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD |
                                                                HUPCL | CLOCAL;
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+       /* wx, disable echo and other flags in the very beginning. 
+         * otherwise RILD will disable them via calling tcsetattr() after it opened tty port,
+         * so there may be a gap between port opening and calling tcsetattr(). If modem send data
+         * at that time, things goes ugly.
+       */
+       acm_tty_driver->init_termios.c_iflag = 0;
+       acm_tty_driver->init_termios.c_oflag = 0;
+       acm_tty_driver->init_termios.c_lflag = 0;
+       for(i= 0; i < CB_NUM ; i++){
+               callback_check_timeout[i] = jiffies;
+       }
+#endif
        tty_set_operations(acm_tty_driver, &acm_ops);
 
        retval = tty_register_driver(acm_tty_driver);
@@ -1798,6 +2186,10 @@ static void __exit acm_exit(void)
 
 module_init(acm_init);
 module_exit(acm_exit);
+#ifdef CONFIG_MTK_DT_USB_SUPPORT
+module_param(enable_debug, int, 0644);
+module_param(enable_dump, int, 0644);
+#endif
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);