Index: linux-2.6.21/drivers/char/Kconfig
===================================================================
--- linux-2.6.21.orig/drivers/char/Kconfig	2007-05-06 17:07:33.000000000 -0300
+++ linux-2.6.21/drivers/char/Kconfig	2007-05-06 17:10:53.000000000 -0300
@@ -1071,5 +1071,18 @@
 	  /sys/devices/platform/telco_clock, with a number of files for
 	  controlling the behavior of this hardware.
 
+config TS0710_MUX
+	tristate "GSM TS 07.10 Multiplex driver"
+	depends on EZX_BP
+	help
+	  This implements the GSM 07.10 multiplex.
+
+config TS0710_MUX_USB
+	tristate "Motorola USB support for TS 07.10 Multiplex driver"
+	depends on TS0710_MUX
+	help
+	  This ads support for TS 07.10 over USB, as found in motorola
+	  Smartphones.
+
 endmenu
 
Index: linux-2.6.21/drivers/char/Makefile
===================================================================
--- linux-2.6.21.orig/drivers/char/Makefile	2007-05-06 17:07:33.000000000 -0300
+++ linux-2.6.21/drivers/char/Makefile	2007-05-06 17:10:21.000000000 -0300
@@ -104,6 +104,9 @@
 obj-$(CONFIG_HANGCHECK_TIMER)	+= hangcheck-timer.o
 obj-$(CONFIG_TCG_TPM)		+= tpm/
 
+obj-$(CONFIG_TS0710_MUX) += ts0710_mux.o ts0710_mux_usb.o
+
+
 # Files generated that shall be removed upon make clean
 clean-files := consolemap_deftbl.c defkeymap.c
 
Index: linux-2.6.21/drivers/char/ts0710.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/drivers/char/ts0710.h	2007-05-06 17:10:21.000000000 -0300
@@ -0,0 +1,368 @@
+/*
+ * File: ts0710.h
+ *
+ * Portions derived from rfcomm.c, original header as follows:
+ *
+ * Copyright (C) 2000, 2001  Axis Communications AB
+ *
+ * Author: Mats Friden <mats.friden@axis.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * Exceptionally, Axis Communications AB grants discretionary and
+ * conditional permissions for additional use of the text contained
+ * in the company's release of the AXIS OpenBT Stack under the
+ * provisions set forth hereunder.
+ *
+ * Provided that, if you use the AXIS OpenBT Stack with other files,
+ * that do not implement functionality as specified in the Bluetooth
+ * System specification, to produce an executable, this does not by
+ * itself cause the resulting executable to be covered by the GNU
+ * General Public License. Your use of that executable is in no way
+ * restricted on account of using the AXIS OpenBT Stack code with it.
+ *
+ * This exception does not however invalidate any other reasons why
+ * the executable file might be covered by the provisions of the GNU
+ * General Public License.
+ *
+ */
+/*
+ * Copyright (C) 2002  Motorola
+ *
+ *  07/28/2002  Initial version based on rfcomm.c
+ *  11/18/2002  Modified
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <asm/byteorder.h>
+#include <asm/types.h>
+
+#define TS0710_MAX_CHN 14
+
+#define SET_PF(ctr) ((ctr) | (1 << 4))
+#define CLR_PF(ctr) ((ctr) & 0xef)
+#define GET_PF(ctr) (((ctr) >> 4) & 0x1)
+
+#define GET_PN_MSG_FRAME_SIZE(pn) ( ((pn)->frame_sizeh << 8) | ((pn)->frame_sizel))
+#define SET_PN_MSG_FRAME_SIZE(pn, size) ({ (pn)->frame_sizel = (size) & 0xff; \
+                                           (pn)->frame_sizeh = (size) >> 8; })
+
+#define GET_LONG_LENGTH(a) ( ((a).h_len << 7) | ((a).l_len) )
+#define SET_LONG_LENGTH(a, length) ({ (a).ea = 0; \
+                                      (a).l_len = length & 0x7F; \
+                                      (a).h_len = (length >> 7) & 0xFF; })
+
+#define SHORT_CRC_CHECK 3
+#define LONG_CRC_CHECK 4
+
+/* FIXME: Should thsi one be define here? */
+#define SHORT_PAYLOAD_SIZE 127
+
+#define EA 1
+#define FCS_SIZE 1
+#define FLAG_SIZE 2
+
+#define TS0710_MAX_HDR_SIZE 5
+#define DEF_TS0710_MTU 256
+
+#define TS0710_BASIC_FLAG 0xF9
+/* the control field */
+#define SABM 0x2f
+#define SABM_SIZE 4
+#define UA 0x63
+#define UA_SIZE 4
+#define DM 0x0f
+#define DISC 0x43
+#define UIH 0xef
+
+/* the type field in a multiplexer command packet */
+#define TEST 0x8
+#define FCON 0x28
+#define FCOFF 0x18
+#define MSC 0x38
+#define RPN 0x24
+#define RLS 0x14
+#define PN 0x20
+#define NSC 0x4
+
+/* V.24 modem control signals */
+#define FC 0x2
+#define RTC 0x4
+#define RTR 0x8
+#define IC 0x40
+#define DV 0x80
+
+#define CTRL_CHAN 0		/* The control channel is defined as DLCI 0 */
+#define MCC_CMD 1		/* Multiplexer command cr */
+#define MCC_RSP 0		/* Multiplexer response cr */
+
+#ifdef __LITTLE_ENDIAN_BITFIELD
+
+typedef struct {
+	__u8 ea:1;
+	__u8 cr:1;
+	__u8 d:1;
+	__u8 server_chn:5;
+} __attribute__ ((packed)) address_field;
+
+typedef struct {
+	__u8 ea:1;
+	__u8 len:7;
+} __attribute__ ((packed)) short_length;
+
+typedef struct {
+	__u8 ea:1;
+	__u8 l_len:7;
+	__u8 h_len;
+} __attribute__ ((packed)) long_length;
+
+typedef struct {
+	address_field addr;
+	__u8 control;
+	short_length length;
+} __attribute__ ((packed)) short_frame_head;
+
+typedef struct {
+	short_frame_head h;
+	__u8 data[0];
+} __attribute__ ((packed)) short_frame;
+
+typedef struct {
+	address_field addr;
+	__u8 control;
+	long_length length;
+	__u8 data[0];
+} __attribute__ ((packed)) long_frame_head;
+
+typedef struct {
+	long_frame_head h;
+	__u8 data[0];
+} __attribute__ ((packed)) long_frame;
+
+/* Typedefinitions for structures used for the multiplexer commands */
+typedef struct {
+	__u8 ea:1;
+	__u8 cr:1;
+	__u8 type:6;
+} __attribute__ ((packed)) mcc_type;
+
+typedef struct {
+	mcc_type type;
+	short_length length;
+	__u8 value[0];
+} __attribute__ ((packed)) mcc_short_frame_head;
+
+typedef struct {
+	mcc_short_frame_head h;
+	__u8 value[0];
+} __attribute__ ((packed)) mcc_short_frame;
+
+typedef struct {
+	mcc_type type;
+	long_length length;
+	__u8 value[0];
+} __attribute__ ((packed)) mcc_long_frame_head;
+
+typedef struct {
+	mcc_long_frame_head h;
+	__u8 value[0];
+} __attribute__ ((packed)) mcc_long_frame;
+
+/* MSC-command */
+typedef struct {
+	__u8 ea:1;
+	__u8 fc:1;
+	__u8 rtc:1;
+	__u8 rtr:1;
+	__u8 reserved:2;
+	__u8 ic:1;
+	__u8 dv:1;
+} __attribute__ ((packed)) v24_sigs;
+
+typedef struct {
+	__u8 ea:1;
+	__u8 b1:1;
+	__u8 b2:1;
+	__u8 b3:1;
+	__u8 len:4;
+} __attribute__ ((packed)) brk_sigs;
+
+typedef struct {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	address_field dlci;
+	__u8 v24_sigs;
+	//brk_sigs break_signals;
+	__u8 fcs;
+} __attribute__ ((packed)) msc_msg;
+
+#if 0
+/* conflict with termios.h */
+/* RPN command */
+#define B2400 0
+#define B4800 1
+#define B7200 2
+#define B9600 3
+#define B19200 4
+#define B38400 5
+#define B57600 6
+#define B115200 7
+#define D230400 8
+#endif
+
+/*
+typedef struct{
+  __u8 bit_rate:1;
+  __u8 data_bits:1;
+  __u8 stop_bit:1;
+  __u8 parity:1;
+  __u8 parity_type:1;
+  __u8 xon_u8:1;
+  __u8 xoff_u8:1;
+  __u8 res1:1;
+  __u8 xon_input:1;
+  __u8 xon_output:1;
+  __u8 rtr_input:1;
+  __u8 rtr_output:1;
+  __u8 rtc_input:1;
+  __u8 rtc_output:1;
+  __u8 res2:2;
+} __attribute__((packed)) parameter_mask;
+
+typedef struct{
+  __u8 bit_rate;
+  __u8 data_bits:2;
+  __u8 stop_bit:1;
+  __u8 parity:1;
+  __u8 parity_type:2;
+  __u8 res1:2;
+  __u8 xon_input:1;
+  __u8 xon_output:1;
+  __u8 rtr_input:1;
+  __u8 rtr_output:1;
+  __u8 rtc_input:1;
+  __u8 rtc_output:1;
+  __u8 res2:2;
+  __u8 xon_u8;
+  __u8 xoff_u8;
+  parameter_mask pm;
+} __attribute__((packed)) rpn_values;
+
+typedef struct{
+  short_frame_head s_head;
+  mcc_short_frame_head mcc_s_head;
+  address_field dlci;
+  rpn_values rpn_val;
+  __u8 fcs;
+} __attribute__((packed)) rpn_msg;
+*/
+
+/* RLS-command */
+/*
+typedef struct{
+  short_frame_head s_head;
+  mcc_short_frame_head mcc_s_head;
+  address_field dlci;
+  __u8 error:4;
+  __u8 res:4;
+  __u8 fcs;
+} __attribute__((packed)) rls_msg;
+*/
+
+/* PN-command */
+typedef struct {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	__u8 dlci:6;
+	__u8 res1:2;
+	__u8 frame_type:4;
+	__u8 credit_flow:4;
+	__u8 prior:6;
+	__u8 res2:2;
+	__u8 ack_timer;
+	__u8 frame_sizel;
+	__u8 frame_sizeh;
+	__u8 max_nbrof_retrans;
+	__u8 credits;
+	__u8 fcs;
+} __attribute__ ((packed)) pn_msg;
+
+/* NSC-command */
+typedef struct {
+	short_frame_head s_head;
+	mcc_short_frame_head mcc_s_head;
+	mcc_type command_type;
+	__u8 fcs;
+} __attribute__ ((packed)) nsc_msg;
+
+#else
+#error Only littel-endianess supported now!
+#endif
+
+enum {
+	REJECTED = 0,
+	DISCONNECTED,
+	CONNECTING,
+	NEGOTIATING,
+	CONNECTED,
+	DISCONNECTING,
+	FLOW_STOPPED
+};
+
+enum ts0710_events {
+	CONNECT_IND,
+	CONNECT_CFM,
+	DISCONN_CFM
+};
+
+typedef struct {
+	volatile __u8 state;
+	volatile __u8 flow_control;
+	volatile __u8 initiated;
+	volatile __u8 initiator;
+	volatile __u16 mtu;
+	wait_queue_head_t open_wait;
+	wait_queue_head_t close_wait;
+} dlci_struct;
+
+/* user space interfaces */
+typedef struct {
+	volatile __u8 initiator;
+	volatile __u8 c_dlci;
+	volatile __u16 mtu;
+	volatile __u8 be_testing;
+	volatile __u32 test_errs;
+	wait_queue_head_t test_wait;
+
+	dlci_struct dlci[TS0710_MAX_CHN];
+} ts0710_con;
Index: linux-2.6.21/drivers/char/ts0710_mux.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/drivers/char/ts0710_mux.c	2007-05-06 17:10:21.000000000 -0300
@@ -0,0 +1,3966 @@
+/*
+ * File: mux_driver.c
+ *
+ * Portions derived from rfcomm.c, original header as follows:
+ *
+ * Copyright (C) 2000, 2001  Axis Communications AB
+ *
+ * Author: Mats Friden <mats.friden@axis.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ * Exceptionally, Axis Communications AB grants discretionary and
+ * conditional permissions for additional use of the text contained
+ * in the company's release of the AXIS OpenBT Stack under the
+ * provisions set forth hereunder.
+ *
+ * Provided that, if you use the AXIS OpenBT Stack with other files,
+ * that do not implement functionality as specified in the Bluetooth
+ * System specification, to produce an executable, this does not by
+ * itself cause the resulting executable to be covered by the GNU
+ * General Public License. Your use of that executable is in no way
+ * restricted on account of using the AXIS OpenBT Stack code with it.
+ *
+ * This exception does not however invalidate any other reasons why
+ * the executable file might be covered by the provisions of the GNU
+ * General Public License.
+ *
+ */
+/*
+ * Copyright (C) 2002-2004  Motorola
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ *
+ *  07/28/2002  Initial version
+ *  11/18/2002  Second version
+ *  04/21/2004  Add GPRS PROC
+ */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+
+#define USB_FOR_MUX
+
+#ifndef USB_FOR_MUX
+#include <linux/serial.h>
+#endif
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/major.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+//#include <syslog.h>
+
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#ifdef USB_FOR_MUX
+//#include <linux/usb.h>
+#include "ts0710_mux_usb.h"
+#endif
+
+#include "ts0710.h"
+#include "ts0710_mux.h"
+
+#define TS0710MUX_GPRS_SESSION_MAX 2
+#define TS0710MUX_MAJOR 250
+#define TS0710MUX_MINOR_START 0
+#define NR_MUXS 16
+
+				  /*#define TS0710MUX_TIME_OUT 30 *//* 300ms  */
+#define TS0710MUX_TIME_OUT 250	/* 2500ms, for BP UART hardware flow control AP UART  */
+
+#define TS0710MUX_IO_DLCI_FC_ON 0x54F2
+#define TS0710MUX_IO_DLCI_FC_OFF 0x54F3
+#define TS0710MUX_IO_FC_ON 0x54F4
+#define TS0710MUX_IO_FC_OFF 0x54F5
+
+#define TS0710MUX_MAX_BUF_SIZE 2048
+
+#define TS0710MUX_SEND_BUF_OFFSET 10
+#define TS0710MUX_SEND_BUF_SIZE (DEF_TS0710_MTU + TS0710MUX_SEND_BUF_OFFSET + 34)
+#define TS0710MUX_RECV_BUF_SIZE TS0710MUX_SEND_BUF_SIZE
+
+/*For BP UART problem Begin*/
+#ifdef TS0710SEQ2
+#define ACK_SPACE 66		/* 6 * 11(ACK frame size)  */
+#else
+#define ACK_SPACE 42		/* 6 * 7(ACK frame size)  */
+#endif
+/*For BP UART problem End*/
+
+									     /*#define TS0710MUX_SERIAL_BUF_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE)*//* For BP UART problem  */
+#define TS0710MUX_SERIAL_BUF_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE + ACK_SPACE)	/* For BP UART problem: ACK_SPACE  */
+
+#define TS0710MUX_MAX_TOTAL_FRAME_SIZE (DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE + FLAG_SIZE)
+#define TS0710MUX_MAX_CHARS_IN_BUF 65535
+#define TS0710MUX_THROTTLE_THRESHOLD DEF_TS0710_MTU
+
+#define TEST_PATTERN_SIZE 250
+
+#define CMDTAG 0x55
+#define DATATAG 0xAA
+
+#define ACK 0x4F		/*For BP UART problem */
+
+/*For BP UART problem Begin*/
+#ifdef TS0710SEQ2
+#define FIRST_BP_SEQ_OFFSET 1	/*offset from start flag */
+#define SECOND_BP_SEQ_OFFSET 2	/*offset from start flag */
+#define FIRST_AP_SEQ_OFFSET 3	/*offset from start flag */
+#define SECOND_AP_SEQ_OFFSET 4	/*offset from start flag */
+#define SLIDE_BP_SEQ_OFFSET 5	/*offset from start flag */
+#define SEQ_FIELD_SIZE 5
+#else
+#define SLIDE_BP_SEQ_OFFSET 1	/*offset from start flag */
+#define SEQ_FIELD_SIZE 1
+#endif
+
+#define ADDRESS_FIELD_OFFSET (1 + SEQ_FIELD_SIZE)	/*offset from start flag */
+/*For BP UART problem End*/
+
+#ifndef UNUSED_PARAM
+#define UNUSED_PARAM(v) (void)(v)
+#endif
+
+#define TS0710MUX_GPRS1_DLCI 7
+#define TS0710MUX_GPRS2_DLCI 8
+
+#define TS0710MUX_GPRS1_RECV_COUNT_IDX 0
+#define TS0710MUX_GPRS1_SEND_COUNT_IDX 1
+#define TS0710MUX_GPRS2_RECV_COUNT_IDX 2
+#define TS0710MUX_GPRS2_SEND_COUNT_IDX 3
+#define TS0710MUX_COUNT_MAX_IDX        3
+#define TS0710MUX_COUNT_IDX_NUM (TS0710MUX_COUNT_MAX_IDX + 1)
+
+static volatile int mux_data_count[TS0710MUX_COUNT_IDX_NUM] = { 0, 0, 0, 0 };
+static volatile int mux_data_count2[TS0710MUX_COUNT_IDX_NUM] = { 0, 0, 0, 0 };
+static struct semaphore mux_data_count_mutex[TS0710MUX_COUNT_IDX_NUM];
+static volatile __u8 post_recv_count_flag = 0;
+
+/*PROC file*/
+struct proc_dir_entry *gprs_proc_file = NULL;
+ssize_t file_proc_read(struct file *file, char *buf, size_t size,
+		       loff_t * ppos);
+ssize_t file_proc_write(struct file *file, const char *buf, size_t count,
+			loff_t * ppos);
+struct file_operations file_proc_operations = {
+      read:file_proc_read,
+      write:file_proc_write,
+};
+typedef struct {
+	int recvBytes;
+	int sentBytes;
+} gprs_bytes;
+
+static __u8 tty2dlci[NR_MUXS] =
+    { 1, 2, 3, 4, 5, 6, 7, 8, 6, 7, 8, 9, 10, 11, 12, 13 };
+static __u8 iscmdtty[NR_MUXS] =
+    { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
+typedef struct {
+	__u8 cmdtty;
+	__u8 datatty;
+} dlci_tty;
+static dlci_tty dlci2tty[] = { {0, 0},	/* DLCI 0 */
+{0, 0},				/* DLCI 1 */
+{1, 1},				/* DLCI 2 */
+{2, 2},				/* DLCI 3 */
+{3, 3},				/* DLCI 4 */
+{4, 4},				/* DLCI 5 */
+{5, 8},				/* DLCI 6 */
+{6, 9},				/* DLCI 7 */
+{7, 10},			/* DLCI 8 */
+{11, 11},			/* DLCI 9 */
+{12, 12},			/* DLCI 10 */
+{13, 13},			/* DLCI 11 */
+{14, 14},			/* DLCI 12 */
+{15, 15}
+};				/* DLCI 13 */
+
+typedef struct {
+	volatile __u8 buf[TS0710MUX_SEND_BUF_SIZE];
+	volatile __u8 *frame;
+	unsigned long flags;
+	volatile __u16 length;
+	volatile __u8 filled;
+	volatile __u8 dummy;	/* Allignment to 4*n bytes */
+} mux_send_struct;
+
+/* Bit number in flags of mux_send_struct */
+#define BUF_BUSY 0
+
+struct mux_recv_packet_tag {
+	__u8 *data;
+	__u32 length;
+	struct mux_recv_packet_tag *next;
+};
+typedef struct mux_recv_packet_tag mux_recv_packet;
+
+struct mux_recv_struct_tag {
+	__u8 data[TS0710MUX_RECV_BUF_SIZE];
+	__u32 length;
+	__u32 total;
+	mux_recv_packet *mux_packet;
+	struct mux_recv_struct_tag *next;
+	int no_tty;
+	volatile __u8 post_unthrottle;
+};
+typedef struct mux_recv_struct_tag mux_recv_struct;
+
+#define RECV_RUNNING 0
+static unsigned long mux_recv_flags = 0;
+
+static mux_send_struct *mux_send_info[NR_MUXS];
+static volatile __u8 mux_send_info_flags[NR_MUXS];
+static volatile __u8 mux_send_info_idx = NR_MUXS;
+
+static mux_recv_struct *mux_recv_info[NR_MUXS];
+static volatile __u8 mux_recv_info_flags[NR_MUXS];
+static mux_recv_struct *mux_recv_queue = NULL;
+
+static struct tty_driver mux_driver;
+
+#ifdef USB_FOR_MUX
+#define COMM_FOR_MUX_DRIVER usb_for_mux_driver
+#define COMM_FOR_MUX_TTY usb_for_mux_tty
+#define COMM_MUX_DISPATCHER usb_mux_dispatcher
+#define COMM_MUX_SENDER usb_mux_sender
+#else
+#define COMM_FOR_MUX_DRIVER serial_for_mux_driver
+#define COMM_FOR_MUX_TTY serial_for_mux_tty
+#define COMM_MUX_DISPATCHER serial_mux_dispatcher
+#define COMM_MUX_SENDER serial_mux_sender
+
+extern struct list_head *tq_serial_for_mux;
+#endif
+
+extern struct tty_driver *COMM_FOR_MUX_DRIVER;
+extern struct tty_struct *COMM_FOR_MUX_TTY;
+extern void (*COMM_MUX_DISPATCHER) (struct tty_struct * tty);
+extern void (*COMM_MUX_SENDER) (void);
+
+static struct work_struct send_tqueue;
+static struct work_struct receive_tqueue;
+static struct work_struct post_recv_tqueue;
+
+static struct tty_struct *mux_table[NR_MUXS];
+static struct termios *mux_termios[NR_MUXS];
+static struct termios *mux_termios_locked[NR_MUXS];
+static volatile short int mux_tty[NR_MUXS];
+
+#ifdef min
+#undef min
+#define min(a,b)    ( (a)<(b) ? (a):(b) )
+#endif
+
+static int get_count(__u8 idx);
+static int set_count(__u8 idx, int count);
+static int add_count(__u8 idx, int count);
+
+static int send_ua(ts0710_con * ts0710, __u8 dlci);
+static int send_dm(ts0710_con * ts0710, __u8 dlci);
+static int send_sabm(ts0710_con * ts0710, __u8 dlci);
+static int send_disc(ts0710_con * ts0710, __u8 dlci);
+static void queue_uih(mux_send_struct * send_info, __u16 len,
+		      ts0710_con * ts0710, __u8 dlci);
+static int send_pn_msg(ts0710_con * ts0710, __u8 prior, __u32 frame_size,
+		       __u8 credit_flow, __u8 credits, __u8 dlci, __u8 cr);
+static int send_nsc_msg(ts0710_con * ts0710, mcc_type cmd, __u8 cr);
+static void set_uih_hdr(short_frame * uih_pkt, __u8 dlci, __u32 len, __u8 cr);
+
+static __u32 crc_check(__u8 * data, __u32 length, __u8 check_sum);
+static __u8 crc_calc(__u8 * data, __u32 length);
+static void create_crctable(__u8 table[]);
+
+static void mux_sched_send(void);
+
+static __u8 crctable[256];
+
+static ts0710_con ts0710_connection;
+/*
+static rpn_values rpn_val;
+*/
+
+static int valid_dlci(__u8 dlci)
+{
+	if ((dlci < TS0710_MAX_CHN) && (dlci > 0))
+		return 1;
+	else
+		return 0;
+}
+
+#ifdef TS0710DEBUG
+
+#ifdef PRINT_OUTPUT_PRINTK
+#define TS0710_DEBUG(fmt, arg...) printk(KERN_INFO "MUX " __FUNCTION__ ": " fmt "\n" , ## arg)
+#else
+#include "ezxlog.h"
+static __u8 strDebug[256];
+#define TS0710_DEBUG(fmt, arg...) ({ snprintf(strDebug, sizeof(strDebug), "MUX " __FUNCTION__ ": " fmt "\n" , ## arg); \
+                                     /*printk("%s", strDebug)*/ezxlogk("MX", strDebug, strlen(strDebug)); })
+#endif				/* End #ifdef PRINT_OUTPUT_PRINTK */
+
+#else
+#define TS0710_DEBUG(fmt...)
+#endif				/* End #ifdef TS0710DEBUG */
+
+#ifdef TS0710LOG
+static unsigned char g_tbuf[TS0710MUX_MAX_BUF_SIZE];
+#ifdef PRINT_OUTPUT_PRINTK
+#define TS0710_LOG(fmt, arg...) printk(fmt, ## arg)
+#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg)
+#else
+#include "ezxlog.h"
+static __u8 strLog[256];
+#define TS0710_LOG(fmt, arg...) ({ snprintf(strLog, sizeof(strLog), fmt, ## arg); \
+                                     /*printk("%s", strLog)*/ezxlogk("MX", strLog, strlen(strLog)); })
+#define TS0710_PRINTK(fmt, arg...) ({ printk(fmt, ## arg); \
+                                      TS0710_LOG(fmt, ## arg); })
+#endif				/* End #ifdef PRINT_OUTPUT_PRINTK */
+
+#else
+#define TS0710_LOG(fmt...)
+#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg)
+#endif				/* End #ifdef TS0710LOG */
+
+#ifdef TS0710DEBUG
+static void TS0710_DEBUGHEX(__u8 * buf, int len)
+{
+	static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
+
+	int i;
+	int c;
+
+	if (len <= 0) {
+		return;
+	}
+
+	c = 0;
+	for (i = 0; (i < len) && (c < (TS0710MUX_MAX_BUF_SIZE - 3)); i++) {
+		sprintf(&tbuf[c], "%02x ", buf[i]);
+		c += 3;
+	}
+	tbuf[c] = 0;
+
+#ifdef PRINT_OUTPUT_PRINTK
+	TS0710_DEBUG("%s", tbuf);
+#else
+	/*printk("%s\n", tbuf) */ ezxlogk("MX", tbuf, c);
+#endif
+}
+static void TS0710_DEBUGSTR(__u8 * buf, int len)
+{
+	static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
+
+	if (len <= 0) {
+		return;
+	}
+
+	if (len > (TS0710MUX_MAX_BUF_SIZE - 1)) {
+		len = (TS0710MUX_MAX_BUF_SIZE - 1);
+	}
+
+	memcpy(tbuf, buf, len);
+	tbuf[len] = 0;
+
+#ifdef PRINT_OUTPUT_PRINTK
+	/* 0x00 byte in the string pointed by tbuf may truncate the print result */
+	TS0710_DEBUG("%s", tbuf);
+#else
+	/*printk("%s\n", tbuf) */ ezxlogk("MX", tbuf, len);
+#endif
+}
+#else
+#define TS0710_DEBUGHEX(buf, len)
+#define TS0710_DEBUGSTR(buf, len)
+#endif				/* End #ifdef TS0710DEBUG */
+
+#ifdef TS0710LOG
+static void TS0710_LOGSTR_FRAME(__u8 send, __u8 * data, int len)
+{
+	short_frame *short_pkt;
+	long_frame *long_pkt;
+	__u8 *uih_data_start;
+	__u32 uih_len;
+	__u8 dlci;
+	int pos;
+
+	if (len <= 0) {
+		return;
+	}
+
+	pos = 0;
+	if (send) {
+		pos += sprintf(&g_tbuf[pos], "<");
+		short_pkt = (short_frame *) (data + 1);	/*For BP UART problem */
+	} else {
+		/*For BP UART problem */
+		/*pos += sprintf(&g_tbuf[pos], ">"); */
+		pos += sprintf(&g_tbuf[pos], ">%d ", *(data + SLIDE_BP_SEQ_OFFSET));	/*For BP UART problem */
+
+#ifdef TS0710SEQ2
+		pos += sprintf(&g_tbuf[pos], "%02x %02x %02x %02x ", *(data + FIRST_BP_SEQ_OFFSET), *(data + SECOND_BP_SEQ_OFFSET), *(data + FIRST_AP_SEQ_OFFSET), *(data + SECOND_AP_SEQ_OFFSET));	/*For BP UART problem */
+#endif
+
+		short_pkt = (short_frame *) (data + ADDRESS_FIELD_OFFSET);	/*For BP UART problem */
+	}
+
+	/*For BP UART problem */
+	/*short_pkt = (short_frame *)(data + 1); */
+
+	dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d;
+	switch (CLR_PF(short_pkt->h.control)) {
+	case SABM:
+		pos += sprintf(&g_tbuf[pos], "C SABM %d ::", dlci);
+		break;
+	case UA:
+		pos += sprintf(&g_tbuf[pos], "C UA %d ::", dlci);
+		break;
+	case DM:
+		pos += sprintf(&g_tbuf[pos], "C DM %d ::", dlci);
+		break;
+	case DISC:
+		pos += sprintf(&g_tbuf[pos], "C DISC %d ::", dlci);
+		break;
+
+		/*For BP UART problem Begin */
+	case ACK:
+		pos += sprintf(&g_tbuf[pos], "C ACK %d ", short_pkt->data[0]);
+
+#ifdef TS0710SEQ2
+		pos += sprintf(&g_tbuf[pos], "%02x %02x %02x %02x ", short_pkt->data[1], short_pkt->data[2], short_pkt->data[3], short_pkt->data[4]);	/*For BP UART problem */
+#endif
+
+		pos += sprintf(&g_tbuf[pos], "::");
+		break;
+		/*For BP UART problem End */
+
+	case UIH:
+		if (!dlci) {
+			pos += sprintf(&g_tbuf[pos], "C MCC %d ::", dlci);
+		} else {
+
+			if ((short_pkt->h.length.ea) == 0) {
+				long_pkt = (long_frame *) short_pkt;
+				uih_len = GET_LONG_LENGTH(long_pkt->h.length);
+				uih_data_start = long_pkt->h.data;
+			} else {
+				uih_len = short_pkt->h.length.len;
+				uih_data_start = short_pkt->data;
+			}
+			switch (*uih_data_start) {
+			case CMDTAG:
+				pos +=
+				    sprintf(&g_tbuf[pos], "I %d A %d ::", dlci,
+					    uih_len);
+				break;
+			case DATATAG:
+			default:
+				pos +=
+				    sprintf(&g_tbuf[pos], "I %d D %d ::", dlci,
+					    uih_len);
+				break;
+			}
+
+		}
+		break;
+	default:
+		pos += sprintf(&g_tbuf[pos], "N!!! %d ::", dlci);
+		break;
+	}
+
+	if (len > (sizeof(g_tbuf) - pos - 1)) {
+		len = (sizeof(g_tbuf) - pos - 1);
+	}
+
+	memcpy(&g_tbuf[pos], data, len);
+	pos += len;
+	g_tbuf[pos] = 0;
+
+#ifdef PRINT_OUTPUT_PRINTK
+	/* 0x00 byte in the string pointed by g_tbuf may truncate the print result */
+	TS0710_LOG("%s\n", g_tbuf);
+#else
+	/*printk("%s\n", g_tbuf) */ ezxlogk("MX", g_tbuf, pos);
+#endif
+}
+#else
+#define TS0710_LOGSTR_FRAME(send, data, len)
+#endif
+
+#ifdef TS0710SIG
+#define my_for_each_task(p) \
+        for ((p) = current; ((p) = (p)->next_task) != current; )
+
+static void TS0710_SIG2APLOGD(void)
+{
+	struct task_struct *p;
+	static __u8 sig = 0;
+
+	if (sig) {
+		return;
+	}
+
+	read_lock(&tasklist_lock);
+	my_for_each_task(p) {
+		if (strncmp(p->comm, "aplogd", 6) == 0) {
+			sig = 1;
+			if (send_sig(SIGUSR2, p, 1) == 0) {
+				TS0710_PRINTK
+				    ("MUX: success to send SIGUSR2 to aplogd!\n");
+			} else {
+				TS0710_PRINTK
+				    ("MUX: failure to send SIGUSR2 to aplogd!\n");
+			}
+			break;
+		}
+	}
+	read_unlock(&tasklist_lock);
+
+	if (!sig) {
+		TS0710_PRINTK("MUX: not found aplogd!\n");
+	}
+}
+#else
+#define TS0710_SIG2APLOGD()
+#endif
+
+static int basic_write(ts0710_con * ts0710, __u8 * buf, int len)
+{
+	int res;
+
+	UNUSED_PARAM(ts0710);
+
+	buf[0] = TS0710_BASIC_FLAG;
+	buf[len + 1] = TS0710_BASIC_FLAG;
+
+	if ((COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)) {
+		TS0710_PRINTK
+		    ("MUX basic_write: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n");
+
+#ifndef USB_FOR_MUX
+		TS0710_PRINTK
+		    ("MUX basic_write: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n");
+		TS0710_SIG2APLOGD();
+#endif
+
+		return -1;
+	}
+
+	TS0710_LOGSTR_FRAME(1, buf, len + 2);
+	TS0710_DEBUGHEX(buf, len + 2);
+
+	res = COMM_FOR_MUX_DRIVER->write(COMM_FOR_MUX_TTY, buf, len + 2);
+
+	if (res != len + 2) {
+		TS0710_PRINTK("MUX basic_write: Write Error!\n");
+		return -1;
+	}
+
+	return len + 2;
+}
+
+/* Functions for the crc-check and calculation */
+
+#define CRC_VALID 0xcf
+
+static __u32 crc_check(__u8 * data, __u32 length, __u8 check_sum)
+{
+	__u8 fcs = 0xff;
+
+	while (length--) {
+		fcs = crctable[fcs ^ *data++];
+	}
+	fcs = crctable[fcs ^ check_sum];
+	TS0710_DEBUG("fcs : %d\n", fcs);
+	if (fcs == (uint) 0xcf) {	/*CRC_VALID) */
+		TS0710_DEBUG("crc_check: CRC check OK\n");
+		return 0;
+	} else {
+		TS0710_PRINTK("MUX crc_check: CRC check failed\n");
+		return 1;
+	}
+}
+
+/* Calculates the checksum according to the ts0710 specification */
+
+static __u8 crc_calc(__u8 * data, __u32 length)
+{
+	__u8 fcs = 0xff;
+
+	while (length--) {
+		fcs = crctable[fcs ^ *data++];
+	}
+
+	return 0xff - fcs;
+}
+
+/* Calulates a reversed CRC table for the FCS check */
+
+static void create_crctable(__u8 table[])
+{
+	int i, j;
+
+	__u8 data;
+	__u8 code_word = (__u8) 0xe0;
+	__u8 sr = (__u8) 0;
+
+	for (j = 0; j < 256; j++) {
+		data = (__u8) j;
+
+		for (i = 0; i < 8; i++) {
+			if ((data & 0x1) ^ (sr & 0x1)) {
+				sr >>= 1;
+				sr ^= code_word;
+			} else {
+				sr >>= 1;
+			}
+
+			data >>= 1;
+			sr &= 0xff;
+		}
+
+		table[j] = sr;
+		sr = 0;
+	}
+}
+
+static void ts0710_reset_dlci(__u8 j)
+{
+	if (j >= TS0710_MAX_CHN)
+		return;
+
+	ts0710_connection.dlci[j].state = DISCONNECTED;
+	ts0710_connection.dlci[j].flow_control = 0;
+	ts0710_connection.dlci[j].mtu = DEF_TS0710_MTU;
+	ts0710_connection.dlci[j].initiated = 0;
+	ts0710_connection.dlci[j].initiator = 0;
+	init_waitqueue_head(&ts0710_connection.dlci[j].open_wait);
+	init_waitqueue_head(&ts0710_connection.dlci[j].close_wait);
+}
+
+static void ts0710_reset_con(void)
+{
+	__u8 j;
+
+	ts0710_connection.initiator = 0;
+	ts0710_connection.mtu = DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE;
+	ts0710_connection.be_testing = 0;
+	ts0710_connection.test_errs = 0;
+	init_waitqueue_head(&ts0710_connection.test_wait);
+
+	for (j = 0; j < TS0710_MAX_CHN; j++) {
+		ts0710_reset_dlci(j);
+	}
+}
+
+static void ts0710_init(void)
+{
+	create_crctable(crctable);
+
+	ts0710_reset_con();
+
+	/* Set the values in the rpn octets */
+/*
+  rpn_val.bit_rate  = 7;
+  rpn_val.data_bits = 3;
+  rpn_val.stop_bit  = 0;
+  rpn_val.parity    = 0;
+  rpn_val.parity_type = 0;
+  rpn_val.res1    = 0;
+  rpn_val.xon_input = 0;
+  rpn_val.xon_output  = 0;
+  rpn_val.rtr_input = 0;
+  rpn_val.rtr_output  = 0;
+  rpn_val.rtc_input = 0;
+  rpn_val.rtc_output  = 0;
+  rpn_val.res2    = 0;
+  rpn_val.xon_u8  = 0x11;
+  rpn_val.xoff_u8 = 0x13;
+	  memset(&rpn_val.pm, 0 , 2); *//* Set the mask to zero */
+}
+
+static void ts0710_upon_disconnect(void)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	__u8 j;
+
+	for (j = 0; j < TS0710_MAX_CHN; j++) {
+		ts0710->dlci[j].state = DISCONNECTED;
+		wake_up_interruptible(&ts0710->dlci[j].open_wait);
+		wake_up_interruptible(&ts0710->dlci[j].close_wait);
+	}
+	ts0710->be_testing = 0;
+	wake_up_interruptible(&ts0710->test_wait);
+	ts0710_reset_con();
+}
+
+/* Sending packet functions */
+
+/* Creates a UA packet and puts it at the beginning of the pkt pointer */
+
+static int send_ua(ts0710_con * ts0710, __u8 dlci)
+{
+	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
+	short_frame *ua;
+
+	TS0710_DEBUG("send_ua: Creating UA packet to DLCI %d\n", dlci);
+
+	ua = (short_frame *) (buf + 1);
+	ua->h.addr.ea = 1;
+	ua->h.addr.cr = ((~(ts0710->initiator)) & 0x1);
+	ua->h.addr.d = (dlci) & 0x1;
+	ua->h.addr.server_chn = (dlci) >> 0x1;
+	ua->h.control = SET_PF(UA);
+	ua->h.length.ea = 1;
+	ua->h.length.len = 0;
+	ua->data[0] = crc_calc((__u8 *) ua, SHORT_CRC_CHECK);
+
+	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
+}
+
+/* Creates a DM packet and puts it at the beginning of the pkt pointer */
+
+static int send_dm(ts0710_con * ts0710, __u8 dlci)
+{
+	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
+	short_frame *dm;
+
+	TS0710_DEBUG("send_dm: Creating DM packet to DLCI %d\n", dlci);
+
+	dm = (short_frame *) (buf + 1);
+	dm->h.addr.ea = 1;
+	dm->h.addr.cr = ((~(ts0710->initiator)) & 0x1);
+	dm->h.addr.d = dlci & 0x1;
+	dm->h.addr.server_chn = dlci >> 0x1;
+	dm->h.control = SET_PF(DM);
+	dm->h.length.ea = 1;
+	dm->h.length.len = 0;
+	dm->data[0] = crc_calc((__u8 *) dm, SHORT_CRC_CHECK);
+
+	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
+}
+
+static int send_sabm(ts0710_con * ts0710, __u8 dlci)
+{
+	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
+	short_frame *sabm;
+
+	TS0710_DEBUG("send_sabm: Creating SABM packet to DLCI %d\n", dlci);
+
+	sabm = (short_frame *) (buf + 1);
+	sabm->h.addr.ea = 1;
+	sabm->h.addr.cr = ((ts0710->initiator) & 0x1);
+	sabm->h.addr.d = dlci & 0x1;
+	sabm->h.addr.server_chn = dlci >> 0x1;
+	sabm->h.control = SET_PF(SABM);
+	sabm->h.length.ea = 1;
+	sabm->h.length.len = 0;
+	sabm->data[0] = crc_calc((__u8 *) sabm, SHORT_CRC_CHECK);
+
+	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
+}
+
+static int send_disc(ts0710_con * ts0710, __u8 dlci)
+{
+	__u8 buf[sizeof(short_frame) + FCS_SIZE + FLAG_SIZE];
+	short_frame *disc;
+
+	TS0710_DEBUG("send_disc: Creating DISC packet to DLCI %d\n", dlci);
+
+	disc = (short_frame *) (buf + 1);
+	disc->h.addr.ea = 1;
+	disc->h.addr.cr = ((ts0710->initiator) & 0x1);
+	disc->h.addr.d = dlci & 0x1;
+	disc->h.addr.server_chn = dlci >> 0x1;
+	disc->h.control = SET_PF(DISC);
+	disc->h.length.ea = 1;
+	disc->h.length.len = 0;
+	disc->data[0] = crc_calc((__u8 *) disc, SHORT_CRC_CHECK);
+
+	return basic_write(ts0710, buf, sizeof(short_frame) + FCS_SIZE);
+}
+
+static void queue_uih(mux_send_struct * send_info, __u16 len,
+		      ts0710_con * ts0710, __u8 dlci)
+{
+	__u32 size;
+
+	TS0710_DEBUG
+	    ("queue_uih: Creating UIH packet with %d bytes data to DLCI %d\n",
+	     len, dlci);
+
+	if (len > SHORT_PAYLOAD_SIZE) {
+		long_frame *l_pkt;
+
+		size = sizeof(long_frame) + len + FCS_SIZE;
+		l_pkt = (long_frame *) (send_info->frame - sizeof(long_frame));
+		set_uih_hdr((void *)l_pkt, dlci, len, ts0710->initiator);
+		l_pkt->data[len] = crc_calc((__u8 *) l_pkt, LONG_CRC_CHECK);
+		send_info->frame = ((__u8 *) l_pkt) - 1;
+	} else {
+		short_frame *s_pkt;
+
+		size = sizeof(short_frame) + len + FCS_SIZE;
+		s_pkt =
+		    (short_frame *) (send_info->frame - sizeof(short_frame));
+		set_uih_hdr((void *)s_pkt, dlci, len, ts0710->initiator);
+		s_pkt->data[len] = crc_calc((__u8 *) s_pkt, SHORT_CRC_CHECK);
+		send_info->frame = ((__u8 *) s_pkt) - 1;
+	}
+	send_info->length = size;
+}
+
+/* Multiplexer command packets functions */
+
+/* Turns on the ts0710 flow control */
+
+static int ts0710_fcon_msg(ts0710_con * ts0710, __u8 cr)
+{
+	__u8 buf[30];
+	mcc_short_frame *mcc_pkt;
+	short_frame *uih_pkt;
+	__u32 size;
+
+	size = sizeof(short_frame) + sizeof(mcc_short_frame) + FCS_SIZE;
+	uih_pkt = (short_frame *) (buf + 1);
+	set_uih_hdr(uih_pkt, CTRL_CHAN, sizeof(mcc_short_frame),
+		    ts0710->initiator);
+	uih_pkt->data[sizeof(mcc_short_frame)] =
+	    crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
+	mcc_pkt = (mcc_short_frame *) (uih_pkt->data);
+
+	mcc_pkt->h.type.ea = EA;
+	mcc_pkt->h.type.cr = cr;
+	mcc_pkt->h.type.type = FCON;
+	mcc_pkt->h.length.ea = EA;
+	mcc_pkt->h.length.len = 0;
+
+	return basic_write(ts0710, buf, size);
+}
+
+/* Turns off the ts0710 flow control */
+
+static int ts0710_fcoff_msg(ts0710_con * ts0710, __u8 cr)
+{
+	__u8 buf[30];
+	mcc_short_frame *mcc_pkt;
+	short_frame *uih_pkt;
+	__u32 size;
+
+	size = (sizeof(short_frame) + sizeof(mcc_short_frame) + FCS_SIZE);
+	uih_pkt = (short_frame *) (buf + 1);
+	set_uih_hdr(uih_pkt, CTRL_CHAN, sizeof(mcc_short_frame),
+		    ts0710->initiator);
+	uih_pkt->data[sizeof(mcc_short_frame)] =
+	    crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
+	mcc_pkt = (mcc_short_frame *) (uih_pkt->data);
+
+	mcc_pkt->h.type.ea = 1;
+	mcc_pkt->h.type.cr = cr;
+	mcc_pkt->h.type.type = FCOFF;
+	mcc_pkt->h.length.ea = 1;
+	mcc_pkt->h.length.len = 0;
+
+	return basic_write(ts0710, buf, size);
+}
+
+/*
+static int ts0710_rpn_msg(ts0710_con *ts0710, __u8 cr, __u8 dlci, __u8 req)
+{
+  char buf[100];
+  rpn_msg* rpn_pkt;
+  __u32 fsize;
+  __u32 psize;
+
+  fsize = sizeof(rpn_msg);
+
+  if (req) {
+    fsize -= sizeof(rpn_values);
+  }
+
+  psize = (fsize - sizeof(short_frame) - FCS_SIZE);
+
+  rpn_pkt = (rpn_msg *) buf;
+
+  set_uih_hdr((short_frame *) rpn_pkt, CTRL_CHAN, psize, ts0710->initiator);
+
+  rpn_pkt->fcs = crc_calc((__u8*) rpn_pkt, SHORT_CRC_CHECK);
+
+  rpn_pkt->mcc_s_head.type.ea = EA;
+  rpn_pkt->mcc_s_head.type.cr = cr;
+  rpn_pkt->mcc_s_head.type.type = RPN;
+  rpn_pkt->mcc_s_head.length.ea = EA;
+
+  rpn_pkt->dlci.ea = EA;
+  rpn_pkt->dlci.cr = 1;
+  rpn_pkt->dlci.d = dlci & 1;
+  rpn_pkt->dlci.server_chn = (dlci >> 1);
+
+  if (req) {
+    rpn_pkt->mcc_s_head.length.len = 1;
+    rpn_pkt->rpn_val.bit_rate = rpn_pkt->fcs;
+  } else {
+    rpn_pkt->mcc_s_head.length.len = 8;
+    memcpy(&(rpn_pkt->rpn_val), &rpn_val, sizeof(rpn_values));
+  }
+  return basic_write(ts0710, buf, fsize);
+}
+*/
+/*
+static int ts0710_rls_msg(ts0710_con *ts0710, __u8 cr, __u8 dlci, __u8 err_code)
+{
+  char buf[100];
+  rls_msg *rls_pkt;
+  __u32 fsize;
+  __u32 psize;
+
+  fsize = sizeof(rls_msg);
+  psize = fsize - sizeof(short_frame) - FCS_SIZE;
+  rls_pkt = (rls_msg *) buf;
+
+  set_uih_hdr((short_frame *) rls_pkt, CTRL_CHAN, psize, ts0710->initiator);
+  rls_pkt->fcs = crc_calc((__u8*) rls_pkt, SHORT_CRC_CHECK);
+
+  rls_pkt->mcc_s_head.type.ea = EA;
+  rls_pkt->mcc_s_head.type.cr = cr;
+  rls_pkt->mcc_s_head.type.type = RLS;
+  rls_pkt->mcc_s_head.length.ea = EA;
+  rls_pkt->mcc_s_head.length.len = 2;
+
+  rls_pkt->dlci.ea = EA;
+  rls_pkt->dlci.cr = 1;
+  rls_pkt->dlci.d = dlci & 1;
+  rls_pkt->dlci.server_chn = dlci >> 1;
+  rls_pkt->error = err_code;
+  rls_pkt->res = 0;
+
+  return basic_write(ts0710, buf, fsize);
+}
+*/
+
+/* Sends an PN-messages and sets the not negotiable parameters to their
+   default values in ts0710 */
+
+static int send_pn_msg(ts0710_con * ts0710, __u8 prior, __u32 frame_size,
+		       __u8 credit_flow, __u8 credits, __u8 dlci, __u8 cr)
+{
+	__u8 buf[30];
+	pn_msg *pn_pkt;
+	__u32 size;
+	TS0710_DEBUG
+	    ("send_pn_msg: DLCI 0x%02x, prior:0x%02x, frame_size:%d, credit_flow:%x, credits:%d, cr:%x\n",
+	     dlci, prior, frame_size, credit_flow, credits, cr);
+
+	size = sizeof(pn_msg);
+	pn_pkt = (pn_msg *) (buf + 1);
+
+	set_uih_hdr((void *)pn_pkt, CTRL_CHAN,
+		    size - (sizeof(short_frame) + FCS_SIZE), ts0710->initiator);
+	pn_pkt->fcs = crc_calc((__u8 *) pn_pkt, SHORT_CRC_CHECK);
+
+	pn_pkt->mcc_s_head.type.ea = 1;
+	pn_pkt->mcc_s_head.type.cr = cr;
+	pn_pkt->mcc_s_head.type.type = PN;
+	pn_pkt->mcc_s_head.length.ea = 1;
+	pn_pkt->mcc_s_head.length.len = 8;
+
+	pn_pkt->res1 = 0;
+	pn_pkt->res2 = 0;
+	pn_pkt->dlci = dlci;
+	pn_pkt->frame_type = 0;
+	pn_pkt->credit_flow = credit_flow;
+	pn_pkt->prior = prior;
+	pn_pkt->ack_timer = 0;
+	SET_PN_MSG_FRAME_SIZE(pn_pkt, frame_size);
+	pn_pkt->credits = credits;
+	pn_pkt->max_nbrof_retrans = 0;
+
+	return basic_write(ts0710, buf, size);
+}
+
+/* Send a Not supported command - command, which needs 3 bytes */
+
+static int send_nsc_msg(ts0710_con * ts0710, mcc_type cmd, __u8 cr)
+{
+	__u8 buf[30];
+	nsc_msg *nsc_pkt;
+	__u32 size;
+
+	size = sizeof(nsc_msg);
+	nsc_pkt = (nsc_msg *) (buf + 1);
+
+	set_uih_hdr((void *)nsc_pkt, CTRL_CHAN,
+		    sizeof(nsc_msg) - sizeof(short_frame) - FCS_SIZE,
+		    ts0710->initiator);
+
+	nsc_pkt->fcs = crc_calc((__u8 *) nsc_pkt, SHORT_CRC_CHECK);
+
+	nsc_pkt->mcc_s_head.type.ea = 1;
+	nsc_pkt->mcc_s_head.type.cr = cr;
+	nsc_pkt->mcc_s_head.type.type = NSC;
+	nsc_pkt->mcc_s_head.length.ea = 1;
+	nsc_pkt->mcc_s_head.length.len = 1;
+
+	nsc_pkt->command_type.ea = 1;
+	nsc_pkt->command_type.cr = cmd.cr;
+	nsc_pkt->command_type.type = cmd.type;
+
+	return basic_write(ts0710, buf, size);
+}
+
+static int ts0710_msc_msg(ts0710_con * ts0710, __u8 value, __u8 cr, __u8 dlci)
+{
+	__u8 buf[30];
+	msc_msg *msc_pkt;
+	__u32 size;
+
+	size = sizeof(msc_msg);
+	msc_pkt = (msc_msg *) (buf + 1);
+
+	set_uih_hdr((void *)msc_pkt, CTRL_CHAN,
+		    sizeof(msc_msg) - sizeof(short_frame) - FCS_SIZE,
+		    ts0710->initiator);
+
+	msc_pkt->fcs = crc_calc((__u8 *) msc_pkt, SHORT_CRC_CHECK);
+
+	msc_pkt->mcc_s_head.type.ea = 1;
+	msc_pkt->mcc_s_head.type.cr = cr;
+	msc_pkt->mcc_s_head.type.type = MSC;
+	msc_pkt->mcc_s_head.length.ea = 1;
+	msc_pkt->mcc_s_head.length.len = 2;
+
+	msc_pkt->dlci.ea = 1;
+	msc_pkt->dlci.cr = 1;
+	msc_pkt->dlci.d = dlci & 1;
+	msc_pkt->dlci.server_chn = (dlci >> 1) & 0x1f;
+
+	msc_pkt->v24_sigs = value;
+
+	return basic_write(ts0710, buf, size);
+}
+
+static int ts0710_test_msg(ts0710_con * ts0710, __u8 * test_pattern, __u32 len,
+			   __u8 cr, __u8 * f_buf /*Frame buf */ )
+{
+	__u32 size;
+
+	if (len > SHORT_PAYLOAD_SIZE) {
+		long_frame *uih_pkt;
+		mcc_long_frame *mcc_pkt;
+
+		size =
+		    (sizeof(long_frame) + sizeof(mcc_long_frame) + len +
+		     FCS_SIZE);
+		uih_pkt = (long_frame *) (f_buf + 1);
+
+		set_uih_hdr((short_frame *) uih_pkt, CTRL_CHAN, len +
+			    sizeof(mcc_long_frame), ts0710->initiator);
+		uih_pkt->data[GET_LONG_LENGTH(uih_pkt->h.length)] =
+		    crc_calc((__u8 *) uih_pkt, LONG_CRC_CHECK);
+		mcc_pkt = (mcc_long_frame *) uih_pkt->data;
+
+		mcc_pkt->h.type.ea = EA;
+		/* cr tells whether it is a commmand (1) or a response (0) */
+		mcc_pkt->h.type.cr = cr;
+		mcc_pkt->h.type.type = TEST;
+		SET_LONG_LENGTH(mcc_pkt->h.length, len);
+		memcpy(mcc_pkt->value, test_pattern, len);
+	} else if (len > (SHORT_PAYLOAD_SIZE - sizeof(mcc_short_frame))) {
+		long_frame *uih_pkt;
+		mcc_short_frame *mcc_pkt;
+
+		/* Create long uih packet and short mcc packet */
+		size =
+		    (sizeof(long_frame) + sizeof(mcc_short_frame) + len +
+		     FCS_SIZE);
+		uih_pkt = (long_frame *) (f_buf + 1);
+
+		set_uih_hdr((short_frame *) uih_pkt, CTRL_CHAN,
+			    len + sizeof(mcc_short_frame), ts0710->initiator);
+		uih_pkt->data[GET_LONG_LENGTH(uih_pkt->h.length)] =
+		    crc_calc((__u8 *) uih_pkt, LONG_CRC_CHECK);
+		mcc_pkt = (mcc_short_frame *) uih_pkt->data;
+
+		mcc_pkt->h.type.ea = EA;
+		mcc_pkt->h.type.cr = cr;
+		mcc_pkt->h.type.type = TEST;
+		mcc_pkt->h.length.ea = EA;
+		mcc_pkt->h.length.len = len;
+		memcpy(mcc_pkt->value, test_pattern, len);
+	} else {
+		short_frame *uih_pkt;
+		mcc_short_frame *mcc_pkt;
+
+		size =
+		    (sizeof(short_frame) + sizeof(mcc_short_frame) + len +
+		     FCS_SIZE);
+		uih_pkt = (short_frame *) (f_buf + 1);
+
+		set_uih_hdr((void *)uih_pkt, CTRL_CHAN, len
+			    + sizeof(mcc_short_frame), ts0710->initiator);
+		uih_pkt->data[uih_pkt->h.length.len] =
+		    crc_calc((__u8 *) uih_pkt, SHORT_CRC_CHECK);
+		mcc_pkt = (mcc_short_frame *) uih_pkt->data;
+
+		mcc_pkt->h.type.ea = EA;
+		mcc_pkt->h.type.cr = cr;
+		mcc_pkt->h.type.type = TEST;
+		mcc_pkt->h.length.ea = EA;
+		mcc_pkt->h.length.len = len;
+		memcpy(mcc_pkt->value, test_pattern, len);
+
+	}
+	return basic_write(ts0710, f_buf, size);
+}
+
+static void set_uih_hdr(short_frame * uih_pkt, __u8 dlci, __u32 len, __u8 cr)
+{
+	uih_pkt->h.addr.ea = 1;
+	uih_pkt->h.addr.cr = cr;
+	uih_pkt->h.addr.d = dlci & 0x1;
+	uih_pkt->h.addr.server_chn = dlci >> 1;
+	uih_pkt->h.control = CLR_PF(UIH);
+
+	if (len > SHORT_PAYLOAD_SIZE) {
+		SET_LONG_LENGTH(((long_frame *) uih_pkt)->h.length, len);
+	} else {
+		uih_pkt->h.length.ea = 1;
+		uih_pkt->h.length.len = len;
+	}
+}
+
+/* Parses a multiplexer control channel packet */
+
+void process_mcc(__u8 * data, __u32 len, ts0710_con * ts0710, int longpkt)
+{
+	__u8 *tbuf = NULL;
+	mcc_short_frame *mcc_short_pkt;
+	int j;
+
+	if (longpkt) {
+		mcc_short_pkt =
+		    (mcc_short_frame *) (((long_frame *) data)->data);
+	} else {
+		mcc_short_pkt =
+		    (mcc_short_frame *) (((short_frame *) data)->data);
+	}
+
+	switch (mcc_short_pkt->h.type.type) {
+	case TEST:
+		if (mcc_short_pkt->h.type.cr == MCC_RSP) {
+			TS0710_DEBUG("Received test command response\n");
+
+			if (ts0710->be_testing) {
+				if ((mcc_short_pkt->h.length.ea) == 0) {
+					mcc_long_frame *mcc_long_pkt;
+					mcc_long_pkt =
+					    (mcc_long_frame *) mcc_short_pkt;
+					if (GET_LONG_LENGTH
+					    (mcc_long_pkt->h.length) !=
+					    TEST_PATTERN_SIZE) {
+						ts0710->test_errs =
+						    TEST_PATTERN_SIZE;
+						TS0710_DEBUG
+						    ("Err: received test pattern is %d bytes long, not expected %d\n",
+						     GET_LONG_LENGTH
+						     (mcc_long_pkt->h.length),
+						     TEST_PATTERN_SIZE);
+					} else {
+						ts0710->test_errs = 0;
+						for (j = 0;
+						     j < TEST_PATTERN_SIZE;
+						     j++) {
+							if (mcc_long_pkt->
+							    value[j] !=
+							    (j & 0xFF)) {
+								(ts0710->
+								 test_errs)++;
+							}
+						}
+					}
+
+				} else {
+
+#if TEST_PATTERN_SIZE < 128
+					if (mcc_short_pkt->h.length.len !=
+					    TEST_PATTERN_SIZE) {
+#endif
+
+						ts0710->test_errs =
+						    TEST_PATTERN_SIZE;
+						TS0710_DEBUG
+						    ("Err: received test pattern is %d bytes long, not expected %d\n",
+						     mcc_short_pkt->h.length.
+						     len, TEST_PATTERN_SIZE);
+
+#if TEST_PATTERN_SIZE < 128
+					} else {
+						ts0710->test_errs = 0;
+						for (j = 0;
+						     j < TEST_PATTERN_SIZE;
+						     j++) {
+							if (mcc_short_pkt->
+							    value[j] !=
+							    (j & 0xFF)) {
+								(ts0710->
+								 test_errs)++;
+							}
+						}
+					}
+#endif
+
+				}
+
+				ts0710->be_testing = 0;	/* Clear the flag */
+				wake_up_interruptible(&ts0710->test_wait);
+			} else {
+				TS0710_DEBUG
+				    ("Err: shouldn't or late to get test cmd response\n");
+			}
+		} else {
+			tbuf = (__u8 *) kmalloc(len + 32, GFP_ATOMIC);
+			if (!tbuf) {
+				break;
+			}
+
+			if ((mcc_short_pkt->h.length.ea) == 0) {
+				mcc_long_frame *mcc_long_pkt;
+				mcc_long_pkt = (mcc_long_frame *) mcc_short_pkt;
+				ts0710_test_msg(ts0710, mcc_long_pkt->value,
+						GET_LONG_LENGTH(mcc_long_pkt->h.
+								length),
+						MCC_RSP, tbuf);
+			} else {
+				ts0710_test_msg(ts0710, mcc_short_pkt->value,
+						mcc_short_pkt->h.length.len,
+						MCC_RSP, tbuf);
+			}
+
+			kfree(tbuf);
+		}
+		break;
+
+	case FCON:		/*Flow control on command */
+		TS0710_PRINTK
+		    ("MUX Received Flow control(all channels) on command\n");
+		if (mcc_short_pkt->h.type.cr == MCC_CMD) {
+			ts0710->dlci[0].state = CONNECTED;
+			ts0710_fcon_msg(ts0710, MCC_RSP);
+			mux_sched_send();
+		}
+		break;
+
+	case FCOFF:		/*Flow control off command */
+		TS0710_PRINTK
+		    ("MUX Received Flow control(all channels) off command\n");
+		if (mcc_short_pkt->h.type.cr == MCC_CMD) {
+			for (j = 0; j < TS0710_MAX_CHN; j++) {
+				ts0710->dlci[j].state = FLOW_STOPPED;
+			}
+			ts0710_fcoff_msg(ts0710, MCC_RSP);
+		}
+		break;
+
+	case MSC:		/*Modem status command */
+		{
+			__u8 dlci;
+			__u8 v24_sigs;
+
+			dlci = (mcc_short_pkt->value[0]) >> 2;
+			v24_sigs = mcc_short_pkt->value[1];
+
+			if ((ts0710->dlci[dlci].state != CONNECTED)
+			    && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
+				send_dm(ts0710, dlci);
+				break;
+			}
+			if (mcc_short_pkt->h.type.cr == MCC_CMD) {
+				TS0710_DEBUG("Received Modem status command\n");
+				if (v24_sigs & 2) {
+					if (ts0710->dlci[dlci].state ==
+					    CONNECTED) {
+						TS0710_LOG
+						    ("MUX Received Flow off on dlci %d\n",
+						     dlci);
+						ts0710->dlci[dlci].state =
+						    FLOW_STOPPED;
+					}
+				} else {
+					if (ts0710->dlci[dlci].state ==
+					    FLOW_STOPPED) {
+						ts0710->dlci[dlci].state =
+						    CONNECTED;
+						TS0710_LOG
+						    ("MUX Received Flow on on dlci %d\n",
+						     dlci);
+						mux_sched_send();
+					}
+				}
+
+				ts0710_msc_msg(ts0710, v24_sigs, MCC_RSP, dlci);
+/*
+          if (!(ts0710->dlci[dlci].initiated) && !(ts0710->dlci[dlci].initiator)) {
+            ts0710_msc_msg(ts0710, EA | RTR | RTC | DV, MCC_CMD, dlci);
+            ts0710->dlci[dlci].initiated = 1;
+          }
+*/
+			} else {
+				TS0710_DEBUG
+				    ("Received Modem status response\n");
+
+				if (v24_sigs & 2) {
+					TS0710_DEBUG("Flow stop accepted\n");
+				}
+			}
+			break;
+		}
+
+		/*    case RPN:  *//*Remote port negotiation command */
+
+/*      {
+        __u8 dlci;
+
+        dlci = (mcc_short_pkt->value[0]) >> 2;
+
+        if (mcc_short_pkt->h.type.cr == MCC_CMD) {
+          if (mcc_short_pkt->h.length.len == 1) {
+            TS0710_DEBUG("Received Remote port negotiation command\n");
+            ts0710_rpn_msg(ts0710, MCC_RSP, dlci, 0);
+          } else {
+*/
+		/* Accept the other sides settings (accept all for now) */
+/*            TS0710_DEBUG("Received Remote port negotiation respons\n");
+            memcpy(&rpn_val, &mcc_short_pkt->value[1], 8);
+            ts0710_rpn_msg(ts0710, MCC_RSP, dlci, 0);
+*/
+		/* Zero the parametermask after response */
+/*            memset(&rpn_val.pm, 0, 2);
+          }
+        }
+        break;
+      }
+*/
+/*
+		    case RLS: *//*Remote line status */
+/*      {
+        __u8 dlci;
+        __u8 err_code;
+
+        TS0710_DEBUG("Received Remote line status\n");
+        if (mcc_short_pkt->h.type.cr == MCC_CMD) {
+          dlci = mcc_short_pkt->value[0] >> 2;
+          err_code = mcc_short_pkt->value[1];
+
+          ts0710_rls_msg(ts0710, MCC_RSP, dlci, err_code);
+        }
+        break;
+      }
+*/
+	case PN:		/*DLC parameter negotiation */
+		{
+			__u8 dlci;
+			__u16 frame_size;
+			pn_msg *pn_pkt;
+
+			pn_pkt = (pn_msg *) data;
+			dlci = pn_pkt->dlci;
+			frame_size = GET_PN_MSG_FRAME_SIZE(pn_pkt);
+			TS0710_DEBUG
+			    ("Received DLC parameter negotiation, PN\n");
+			if (pn_pkt->mcc_s_head.type.cr == MCC_CMD) {
+				TS0710_DEBUG("received PN command with:\n");
+				TS0710_DEBUG("Frame size:%d\n", frame_size);
+
+				frame_size =
+				    min(frame_size, ts0710->dlci[dlci].mtu);
+				send_pn_msg(ts0710, pn_pkt->prior, frame_size,
+					    0, 0, dlci, MCC_RSP);
+				ts0710->dlci[dlci].mtu = frame_size;
+				TS0710_DEBUG("process_mcc : mtu set to %d\n",
+					     ts0710->dlci[dlci].mtu);
+			} else {
+				TS0710_DEBUG("received PN response with:\n");
+				TS0710_DEBUG("Frame size:%d\n", frame_size);
+
+				frame_size =
+				    min(frame_size, ts0710->dlci[dlci].mtu);
+				ts0710->dlci[dlci].mtu = frame_size;
+
+				TS0710_DEBUG
+				    ("process_mcc : mtu set on dlci:%d to %d\n",
+				     dlci, ts0710->dlci[dlci].mtu);
+
+				if (ts0710->dlci[dlci].state == NEGOTIATING) {
+					ts0710->dlci[dlci].state = CONNECTING;
+					wake_up_interruptible(&ts0710->
+							      dlci[dlci].
+							      open_wait);
+				}
+			}
+			break;
+		}
+
+	case NSC:		/*Non supported command resonse */
+		TS0710_LOG("MUX Received Non supported command response\n");
+		break;
+
+	default:		/*Non supported command received */
+		TS0710_LOG("MUX Received a non supported command\n");
+		send_nsc_msg(ts0710, mcc_short_pkt->h.type, MCC_RSP);
+		break;
+	}
+}
+
+static mux_recv_packet *get_mux_recv_packet(__u32 size)
+{
+	mux_recv_packet *recv_packet;
+
+	TS0710_DEBUG("Enter into get_mux_recv_packet");
+
+	recv_packet =
+	    (mux_recv_packet *) kmalloc(sizeof(mux_recv_packet), GFP_ATOMIC);
+	if (!recv_packet) {
+		return 0;
+	}
+
+	recv_packet->data = (__u8 *) kmalloc(size, GFP_ATOMIC);
+	if (!(recv_packet->data)) {
+		kfree(recv_packet);
+		return 0;
+	}
+	recv_packet->length = 0;
+	recv_packet->next = 0;
+	return recv_packet;
+}
+
+static void free_mux_recv_packet(mux_recv_packet * recv_packet)
+{
+	TS0710_DEBUG("Enter into free_mux_recv_packet");
+
+	if (!recv_packet) {
+		return;
+	}
+
+	if (recv_packet->data) {
+		kfree(recv_packet->data);
+	}
+	kfree(recv_packet);
+}
+
+static void free_mux_recv_struct(mux_recv_struct * recv_info)
+{
+	mux_recv_packet *recv_packet1, *recv_packet2;
+
+	if (!recv_info) {
+		return;
+	}
+
+	recv_packet1 = recv_info->mux_packet;
+	while (recv_packet1) {
+		recv_packet2 = recv_packet1->next;
+		free_mux_recv_packet(recv_packet1);
+		recv_packet1 = recv_packet2;
+	}
+
+	kfree(recv_info);
+}
+
+static inline void add_post_recv_queue(mux_recv_struct ** head,
+				       mux_recv_struct * new_item)
+{
+	new_item->next = *head;
+	*head = new_item;
+}
+
+static void ts0710_flow_on(__u8 dlci, ts0710_con * ts0710)
+{
+	int i;
+	__u8 cmdtty;
+	__u8 datatty;
+	struct tty_struct *tty;
+	mux_recv_struct *recv_info;
+
+	if ((ts0710->dlci[0].state != CONNECTED)
+	    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
+		return;
+	} else if ((ts0710->dlci[dlci].state != CONNECTED)
+		   && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
+		return;
+	}
+
+	if (!(ts0710->dlci[dlci].flow_control)) {
+		return;
+	}
+
+	cmdtty = dlci2tty[dlci].cmdtty;
+	datatty = dlci2tty[dlci].datatty;
+
+	if (cmdtty != datatty) {
+		/* Check AT cmd tty */
+		tty = mux_table[cmdtty];
+		if (mux_tty[cmdtty] && tty) {
+			if (test_bit(TTY_THROTTLED, &tty->flags)) {
+				return;
+			}
+		}
+		recv_info = mux_recv_info[cmdtty];
+		if (mux_recv_info_flags[cmdtty] && recv_info) {
+			if (recv_info->total) {
+				return;
+			}
+		}
+
+		/* Check data tty */
+		tty = mux_table[datatty];
+		if (mux_tty[datatty] && tty) {
+			if (test_bit(TTY_THROTTLED, &tty->flags)) {
+				return;
+			}
+		}
+		recv_info = mux_recv_info[datatty];
+		if (mux_recv_info_flags[datatty] && recv_info) {
+			if (recv_info->total) {
+				return;
+			}
+		}
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (ts0710_msc_msg(ts0710, EA | RTC | RTR | DV, MCC_CMD, dlci) <
+		    0) {
+			continue;
+		} else {
+			TS0710_LOG("MUX send Flow on on dlci %d\n", dlci);
+			ts0710->dlci[dlci].flow_control = 0;
+			break;
+		}
+	}
+}
+
+static void ts0710_flow_off(struct tty_struct *tty, __u8 dlci,
+			    ts0710_con * ts0710)
+{
+	int i;
+
+	if (test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+		return;
+	}
+
+	if ((ts0710->dlci[0].state != CONNECTED)
+	    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
+		return;
+	} else if ((ts0710->dlci[dlci].state != CONNECTED)
+		   && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
+		return;
+	}
+
+	if (ts0710->dlci[dlci].flow_control) {
+		return;
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (ts0710_msc_msg
+		    (ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, dlci) < 0) {
+			continue;
+		} else {
+			TS0710_LOG("MUX send Flow off on dlci %d\n", dlci);
+			ts0710->dlci[dlci].flow_control = 1;
+			break;
+		}
+	}
+}
+
+int ts0710_recv_data(ts0710_con * ts0710, char *data, int len)
+{
+	short_frame *short_pkt;
+	long_frame *long_pkt;
+	__u8 *uih_data_start;
+	__u32 uih_len;
+	__u8 dlci;
+	__u8 be_connecting;
+#ifdef TS0710DEBUG
+	unsigned long t;
+#endif
+
+	short_pkt = (short_frame *) data;
+
+	dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d;
+	switch (CLR_PF(short_pkt->h.control)) {
+	case SABM:
+		TS0710_DEBUG("SABM-packet received\n");
+
+/*For BP UART problem
+      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
+        break;
+*/
+
+		if (!dlci) {
+			TS0710_DEBUG("server channel == 0\n");
+			ts0710->dlci[0].state = CONNECTED;
+
+			TS0710_DEBUG("sending back UA - control channel\n");
+			send_ua(ts0710, dlci);
+			wake_up_interruptible(&ts0710->dlci[0].open_wait);
+
+		} else if (valid_dlci(dlci)) {
+
+			TS0710_DEBUG("Incomming connect on channel %d\n", dlci);
+
+			TS0710_DEBUG("sending UA, dlci %d\n", dlci);
+			send_ua(ts0710, dlci);
+
+			ts0710->dlci[dlci].state = CONNECTED;
+			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
+
+		} else {
+			TS0710_DEBUG("invalid dlci %d, sending DM\n", dlci);
+			send_dm(ts0710, dlci);
+		}
+
+		break;
+
+	case UA:
+		TS0710_DEBUG("UA packet received\n");
+
+/*For BP UART problem
+      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
+        break;
+*/
+
+		if (!dlci) {
+			TS0710_DEBUG("server channel == 0\n");
+
+			if (ts0710->dlci[0].state == CONNECTING) {
+				ts0710->dlci[0].state = CONNECTED;
+				wake_up_interruptible(&ts0710->dlci[0].
+						      open_wait);
+			} else if (ts0710->dlci[0].state == DISCONNECTING) {
+				ts0710_upon_disconnect();
+			} else {
+				TS0710_DEBUG
+				    (" Something wrong receiving UA packet\n");
+			}
+		} else if (valid_dlci(dlci)) {
+			TS0710_DEBUG("Incomming UA on channel %d\n", dlci);
+
+			if (ts0710->dlci[dlci].state == CONNECTING) {
+				ts0710->dlci[dlci].state = CONNECTED;
+				wake_up_interruptible(&ts0710->dlci[dlci].
+						      open_wait);
+			} else if (ts0710->dlci[dlci].state == DISCONNECTING) {
+				ts0710->dlci[dlci].state = DISCONNECTED;
+				wake_up_interruptible(&ts0710->dlci[dlci].
+						      open_wait);
+				wake_up_interruptible(&ts0710->dlci[dlci].
+						      close_wait);
+				ts0710_reset_dlci(dlci);
+			} else {
+				TS0710_DEBUG
+				    (" Something wrong receiving UA packet\n");
+			}
+		} else {
+			TS0710_DEBUG("invalid dlci %d\n", dlci);
+		}
+
+		break;
+
+	case DM:
+		TS0710_DEBUG("DM packet received\n");
+
+/*For BP UART problem
+      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
+        break;
+*/
+
+		if (!dlci) {
+			TS0710_DEBUG("server channel == 0\n");
+
+			if (ts0710->dlci[0].state == CONNECTING) {
+				be_connecting = 1;
+			} else {
+				be_connecting = 0;
+			}
+			ts0710_upon_disconnect();
+			if (be_connecting) {
+				ts0710->dlci[0].state = REJECTED;
+			}
+		} else if (valid_dlci(dlci)) {
+			TS0710_DEBUG("Incomming DM on channel %d\n", dlci);
+
+			if (ts0710->dlci[dlci].state == CONNECTING) {
+				ts0710->dlci[dlci].state = REJECTED;
+			} else {
+				ts0710->dlci[dlci].state = DISCONNECTED;
+			}
+			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
+			wake_up_interruptible(&ts0710->dlci[dlci].close_wait);
+			ts0710_reset_dlci(dlci);
+		} else {
+			TS0710_DEBUG("invalid dlci %d\n", dlci);
+		}
+
+		break;
+
+	case DISC:
+		TS0710_DEBUG("DISC packet received\n");
+
+/*For BP UART problem
+      if( crc_check((__u8*) short_pkt, SHORT_CRC_CHECK, short_pkt->data[0]) )
+        break;
+*/
+
+		if (!dlci) {
+			TS0710_DEBUG("server channel == 0\n");
+
+			send_ua(ts0710, dlci);
+			TS0710_DEBUG("DISC, sending back UA\n");
+
+			ts0710_upon_disconnect();
+		} else if (valid_dlci(dlci)) {
+			TS0710_DEBUG("Incomming DISC on channel %d\n", dlci);
+
+			send_ua(ts0710, dlci);
+			TS0710_DEBUG("DISC, sending back UA\n");
+
+			ts0710->dlci[dlci].state = DISCONNECTED;
+			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
+			wake_up_interruptible(&ts0710->dlci[dlci].close_wait);
+			ts0710_reset_dlci(dlci);
+		} else {
+			TS0710_DEBUG("invalid dlci %d\n", dlci);
+		}
+
+		break;
+
+	case UIH:
+		TS0710_DEBUG("UIH packet received\n");
+
+		if ((dlci >= TS0710_MAX_CHN)) {
+			TS0710_DEBUG("invalid dlci %d\n", dlci);
+			send_dm(ts0710, dlci);
+			break;
+		}
+
+		if (GET_PF(short_pkt->h.control)) {
+			TS0710_LOG
+			    ("MUX Error %s: UIH packet with P/F set, discard it!\n",
+			     __FUNCTION__);
+			break;
+		}
+
+		if ((ts0710->dlci[dlci].state != CONNECTED)
+		    && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
+			TS0710_LOG
+			    ("MUX Error %s: DLCI %d not connected, discard it!\n",
+			     __FUNCTION__, dlci);
+			send_dm(ts0710, dlci);
+			break;
+		}
+
+		if ((short_pkt->h.length.ea) == 0) {
+			TS0710_DEBUG("Long UIH packet received\n");
+			long_pkt = (long_frame *) data;
+			uih_len = GET_LONG_LENGTH(long_pkt->h.length);
+			uih_data_start = long_pkt->h.data;
+			TS0710_DEBUG("long packet length %d\n", uih_len);
+
+/*For BP UART problem
+        if (crc_check(data, LONG_CRC_CHECK, *(uih_data_start + uih_len)))
+          break;
+*/
+		} else {
+			TS0710_DEBUG("Short UIH pkt received\n");
+			uih_len = short_pkt->h.length.len;
+			uih_data_start = short_pkt->data;
+
+/*For BP UART problem
+        if (crc_check(data, SHORT_CRC_CHECK, *(uih_data_start + uih_len)))
+          break;
+*/
+		}
+
+		if (dlci == 0) {
+			TS0710_DEBUG("UIH on serv_channel 0\n");
+			process_mcc(data, len, ts0710,
+				    !(short_pkt->h.length.ea));
+		} else if (valid_dlci(dlci)) {
+			/* do tty dispatch */
+			__u8 tag;
+			__u8 tty_idx;
+			struct tty_struct *tty;
+			__u8 queue_data;
+			__u8 post_recv;
+			__u8 flow_control;
+			mux_recv_struct *recv_info;
+			int recv_room;
+			mux_recv_packet *recv_packet, *recv_packet2;
+
+			TS0710_DEBUG("UIH on channel %d\n", dlci);
+
+			if (uih_len > ts0710->dlci[dlci].mtu) {
+				TS0710_PRINTK
+				    ("MUX Error:  DLCI:%d, uih_len:%d is bigger than mtu:%d, discard data!\n",
+				     dlci, uih_len, ts0710->dlci[dlci].mtu);
+				break;
+			}
+
+			tag = *uih_data_start;
+			uih_data_start++;
+			uih_len--;
+
+			if (!uih_len) {
+				break;
+			}
+
+			switch (tag) {
+			case CMDTAG:
+				tty_idx = dlci2tty[dlci].cmdtty;
+				TS0710_DEBUG("CMDTAG on DLCI:%d, /dev/mux%d\n",
+					     dlci, tty_idx);
+				TS0710_DEBUGSTR(uih_data_start, uih_len);
+				if (!(iscmdtty[tty_idx])) {
+					TS0710_PRINTK
+					    ("MUX Error: %s: Wrong CMDTAG on DLCI:%d, /dev/mux%d\n",
+					     __FUNCTION__, dlci, tty_idx);
+				}
+				break;
+			case DATATAG:
+			default:
+				tty_idx = dlci2tty[dlci].datatty;
+				TS0710_DEBUG
+				    ("NON-CMDTAG on DLCI:%d, /dev/mux%d\n",
+				     dlci, tty_idx);
+				if (iscmdtty[tty_idx]) {
+					TS0710_PRINTK
+					    ("MUX Error: %s: Wrong NON-CMDTAG on DLCI:%d, /dev/mux%d\n",
+					     __FUNCTION__, dlci, tty_idx);
+				}
+				break;
+			}
+			tty = mux_table[tty_idx];
+			if ((!mux_tty[tty_idx]) || (!tty)) {
+				TS0710_PRINTK
+				    ("MUX: No application waiting for, discard it! /dev/mux%d\n",
+				     tty_idx);
+			} else {	/* Begin processing received data */
+				if ((!mux_recv_info_flags[tty_idx])
+				    || (!mux_recv_info[tty_idx])) {
+					TS0710_PRINTK
+					    ("MUX Error: No mux_recv_info, discard it! /dev/mux%d\n",
+					     tty_idx);
+					break;
+				}
+
+				recv_info = mux_recv_info[tty_idx];
+				if (recv_info->total > 8192) {
+					TS0710_PRINTK
+					    ("MUX : discard data for tty_idx:%d, recv_info->total > 8192 \n",
+					     tty_idx);
+					break;
+				}
+
+				queue_data = 0;
+				post_recv = 0;
+				flow_control = 0;
+				recv_room = 65535;
+				if (tty->receive_room)
+					recv_room = tty->receive_room;
+
+				if (test_bit(TTY_THROTTLED, &tty->flags)) {
+					queue_data = 1;
+				} else {
+					if (test_bit
+					    (TTY_DONT_FLIP, &tty->flags)) {
+						queue_data = 1;
+						post_recv = 1;
+					} else if (recv_info->total) {
+						queue_data = 1;
+						post_recv = 1;
+					} else if (recv_room < uih_len) {
+						queue_data = 1;
+						flow_control = 1;
+					}
+
+					if ((recv_room -
+					     (uih_len + recv_info->total)) <
+					    ts0710->dlci[dlci].mtu) {
+						flow_control = 1;
+					}
+				}
+
+				if (!queue_data) {
+					/* Put received data into read buffer of tty */
+					TS0710_DEBUG
+					    ("Put received data into read buffer of /dev/mux%d",
+					     tty_idx);
+
+#ifdef TS0710DEBUG
+					t = jiffies;
+#endif
+
+					(tty->ldisc.receive_buf) (tty,
+								  uih_data_start,
+								  NULL,
+								  uih_len);
+
+#ifdef TS0710DEBUG
+					TS0710_DEBUG
+					    ("tty->ldisc.receive_buf take ticks: %lu",
+					     (jiffies - t));
+#endif
+
+				} else {	/* Queue data */
+
+					TS0710_DEBUG
+					    ("Put received data into recv queue of /dev/mux%d",
+					     tty_idx);
+					if (recv_info->total) {
+						/* recv_info is already linked into mux_recv_queue */
+
+						recv_packet =
+						    get_mux_recv_packet
+						    (uih_len);
+						if (!recv_packet) {
+							TS0710_PRINTK
+							    ("MUX %s: no memory\n",
+							     __FUNCTION__);
+							break;
+						}
+
+						memcpy(recv_packet->data,
+						       uih_data_start, uih_len);
+						recv_packet->length = uih_len;
+						recv_info->total += uih_len;
+						recv_packet->next = NULL;
+
+						if (!(recv_info->mux_packet)) {
+							recv_info->mux_packet =
+							    recv_packet;
+						} else {
+							recv_packet2 =
+							    recv_info->
+							    mux_packet;
+							while (recv_packet2->
+							       next) {
+								recv_packet2 =
+								    recv_packet2->
+								    next;
+							}
+							recv_packet2->next =
+							    recv_packet;
+						}	/* End if( !(recv_info->mux_packet) ) */
+					} else {	/* recv_info->total == 0 */
+						if (uih_len >
+						    TS0710MUX_RECV_BUF_SIZE) {
+							TS0710_PRINTK
+							    ("MUX Error:  tty_idx:%d, uih_len == %d is too big\n",
+							     tty_idx, uih_len);
+							uih_len =
+							    TS0710MUX_RECV_BUF_SIZE;
+						}
+						memcpy(recv_info->data,
+						       uih_data_start, uih_len);
+						recv_info->length = uih_len;
+						recv_info->total = uih_len;
+
+						add_post_recv_queue
+						    (&mux_recv_queue,
+						     recv_info);
+					}	/* End recv_info->total == 0 */
+				}	/* End Queue data */
+
+				if (flow_control) {
+					/* Do something for flow control */
+					ts0710_flow_off(tty, dlci, ts0710);
+				}
+
+				if (tty_idx ==
+				    dlci2tty[TS0710MUX_GPRS1_DLCI].datatty) {
+					if (add_count
+					    (TS0710MUX_GPRS1_RECV_COUNT_IDX,
+					     uih_len) < 0) {
+						post_recv_count_flag = 1;
+						post_recv = 1;
+						mux_data_count2
+						    [TS0710MUX_GPRS1_RECV_COUNT_IDX]
+						    += uih_len;
+					}
+				} else if (tty_idx ==
+					   dlci2tty[TS0710MUX_GPRS2_DLCI].
+					   datatty) {
+					if (add_count
+					    (TS0710MUX_GPRS2_RECV_COUNT_IDX,
+					     uih_len) < 0) {
+						post_recv_count_flag = 1;
+						post_recv = 1;
+						mux_data_count2
+						    [TS0710MUX_GPRS2_RECV_COUNT_IDX]
+						    += uih_len;
+					}
+				}
+
+				if (post_recv)
+					schedule_work(&post_recv_tqueue);
+			}	/* End processing received data */
+		} else {
+			TS0710_DEBUG("invalid dlci %d\n", dlci);
+		}
+
+		break;
+
+	default:
+		TS0710_DEBUG("illegal packet\n");
+		break;
+	}
+	return 0;
+}
+
+/*
+int ts0710_send_data(ts0710_con *ts0710, __u8 dlci, __u8 *data, __u32 count)
+{
+  __u32 c, total = 0;
+  __u8 tag, first;
+
+  if( ts0710->dlci[0].state == FLOW_STOPPED ){
+    TS0710_DEBUG("Flow stopped on all channels, returning zero\n");
+*/
+/*
+    return -EFLOWSTOPPED;
+  } else if( ts0710->dlci[dlci].state == FLOW_STOPPED ){
+    TS0710_DEBUG("Flow stopped, returning zero\n");
+*/
+/*
+    return -EFLOWSTOPPED;
+  } else if( ts0710->dlci[dlci].state == CONNECTED ){
+
+    TS0710_DEBUG("trying to send %d bytes\n", count);
+    tag = *data;
+    first = 1;
+*/
+    /* The first byte is always a Cmd/Data tag */
+/*
+    while( count > 1 ){
+
+      c = min(count, ts0710->dlci[dlci].mtu);
+      if( queue_uih(data, c, ts0710, dlci) <= 0 ) {
+        break;
+      }
+
+      total += (c - 1);
+      data += (c - 1);
+      *data = tag;
+      count -= (c - 1);
+
+      if( first ) {
+        first = 0;
+	total++;
+      }
+    }
+    TS0710_DEBUG("sent %d bytes\n", total);
+    return total;
+  } else {
+    TS0710_DEBUG("DLCI %d not connected\n", dlci);
+    return -EDISCONNECTED;
+  }
+}
+*/
+
+/* Close ts0710 channel */
+static void ts0710_close_channel(__u8 dlci)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int try;
+	unsigned long t;
+
+	TS0710_DEBUG("ts0710_disc_command on channel %d\n", dlci);
+
+	if ((ts0710->dlci[dlci].state == DISCONNECTED)
+	    || (ts0710->dlci[dlci].state == REJECTED)) {
+		return;
+	} else if (ts0710->dlci[dlci].state == DISCONNECTING) {
+		/* Reentry */
+		return;
+	} else {
+		ts0710->dlci[dlci].state = DISCONNECTING;
+		try = 3;
+		while (try--) {
+			t = jiffies;
+			send_disc(ts0710, dlci);
+			interruptible_sleep_on_timeout(&ts0710->dlci[dlci].
+						       close_wait,
+						       TS0710MUX_TIME_OUT);
+			if (ts0710->dlci[dlci].state == DISCONNECTED) {
+				break;
+			} else if (signal_pending(current)) {
+				TS0710_PRINTK
+				    ("MUX DLCI %d Send DISC got signal!\n",
+				     dlci);
+				break;
+			} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
+				TS0710_PRINTK
+				    ("MUX DLCI %d Send DISC timeout!\n", dlci);
+				continue;
+			}
+		}
+
+		if (ts0710->dlci[dlci].state != DISCONNECTED) {
+			if (dlci == 0) {	/* Control Channel */
+				ts0710_upon_disconnect();
+			} else {	/* Other Channel */
+				ts0710->dlci[dlci].state = DISCONNECTED;
+				wake_up_interruptible(&ts0710->dlci[dlci].
+						      close_wait);
+				ts0710_reset_dlci(dlci);
+			}
+		}
+	}
+}
+
+int ts0710_open_channel(__u8 dlci)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int try;
+	int retval;
+	unsigned long t;
+
+	retval = -ENODEV;
+	if (dlci == 0) {	// control channel
+		if ((ts0710->dlci[0].state == CONNECTED)
+		    || (ts0710->dlci[0].state == FLOW_STOPPED)) {
+			return 0;
+		} else if (ts0710->dlci[0].state == CONNECTING) {
+			/* Reentry */
+			TS0710_PRINTK
+			    ("MUX DLCI: 0, reentry to open DLCI 0, pid: %d, %s !\n",
+			     current->pid, current->comm);
+			try = 11;
+			while (try--) {
+				t = jiffies;
+				interruptible_sleep_on_timeout(&ts0710->dlci[0].
+							       open_wait,
+							       TS0710MUX_TIME_OUT);
+				if ((ts0710->dlci[0].state == CONNECTED)
+				    || (ts0710->dlci[0].state ==
+					FLOW_STOPPED)) {
+					retval = 0;
+					break;
+				} else if (ts0710->dlci[0].state == REJECTED) {
+					retval = -EREJECTED;
+					break;
+				} else if (ts0710->dlci[0].state ==
+					   DISCONNECTED) {
+					break;
+				} else if (signal_pending(current)) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Wait for connecting got signal!\n",
+					     dlci);
+					retval = -EAGAIN;
+					break;
+				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Wait for connecting timeout!\n",
+					     dlci);
+					continue;
+				} else if (ts0710->dlci[0].state == CONNECTING) {
+					continue;
+				}
+			}
+
+			if (ts0710->dlci[0].state == CONNECTING) {
+				ts0710->dlci[0].state = DISCONNECTED;
+			}
+		} else if ((ts0710->dlci[0].state != DISCONNECTED)
+			   && (ts0710->dlci[0].state != REJECTED)) {
+			TS0710_PRINTK("MUX DLCI:%d state is invalid!\n", dlci);
+			return retval;
+		} else {
+			ts0710->initiator = 1;
+			ts0710->dlci[0].state = CONNECTING;
+			ts0710->dlci[0].initiator = 1;
+			try = 10;
+			while (try--) {
+				t = jiffies;
+				send_sabm(ts0710, 0);
+				interruptible_sleep_on_timeout(&ts0710->dlci[0].
+							       open_wait,
+							       TS0710MUX_TIME_OUT);
+				if ((ts0710->dlci[0].state == CONNECTED)
+				    || (ts0710->dlci[0].state ==
+					FLOW_STOPPED)) {
+					retval = 0;
+					break;
+				} else if (ts0710->dlci[0].state == REJECTED) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Send SABM got rejected!\n",
+					     dlci);
+					retval = -EREJECTED;
+					break;
+				} else if (signal_pending(current)) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Send SABM got signal!\n",
+					     dlci);
+					retval = -EAGAIN;
+					break;
+				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Send SABM timeout!\n",
+					     dlci);
+					continue;
+				}
+			}
+
+			if (ts0710->dlci[0].state == CONNECTING) {
+				ts0710->dlci[0].state = DISCONNECTED;
+			}
+			wake_up_interruptible(&ts0710->dlci[0].open_wait);
+		}
+	} else {		// other channel
+		if ((ts0710->dlci[0].state != CONNECTED)
+		    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
+			return retval;
+		} else if ((ts0710->dlci[dlci].state == CONNECTED)
+			   || (ts0710->dlci[dlci].state == FLOW_STOPPED)) {
+			return 0;
+		} else if ((ts0710->dlci[dlci].state == NEGOTIATING)
+			   || (ts0710->dlci[dlci].state == CONNECTING)) {
+			/* Reentry */
+			try = 8;
+			while (try--) {
+				t = jiffies;
+				interruptible_sleep_on_timeout(&ts0710->
+							       dlci[dlci].
+							       open_wait,
+							       TS0710MUX_TIME_OUT);
+				if ((ts0710->dlci[dlci].state == CONNECTED)
+				    || (ts0710->dlci[dlci].state ==
+					FLOW_STOPPED)) {
+					retval = 0;
+					break;
+				} else if (ts0710->dlci[dlci].state == REJECTED) {
+					retval = -EREJECTED;
+					break;
+				} else if (ts0710->dlci[dlci].state ==
+					   DISCONNECTED) {
+					break;
+				} else if (signal_pending(current)) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Wait for connecting got signal!\n",
+					     dlci);
+					retval = -EAGAIN;
+					break;
+				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Wait for connecting timeout!\n",
+					     dlci);
+					continue;
+				} else
+				    if ((ts0710->dlci[dlci].state ==
+					 NEGOTIATING)
+					|| (ts0710->dlci[dlci].state ==
+					    CONNECTING)) {
+					continue;
+				}
+			}
+
+			if ((ts0710->dlci[dlci].state == NEGOTIATING)
+			    || (ts0710->dlci[dlci].state == CONNECTING)) {
+				ts0710->dlci[dlci].state = DISCONNECTED;
+			}
+		} else if ((ts0710->dlci[dlci].state != DISCONNECTED)
+			   && (ts0710->dlci[dlci].state != REJECTED)) {
+			TS0710_PRINTK("MUX DLCI:%d state is invalid!\n", dlci);
+			return retval;
+		} else {
+			ts0710->dlci[dlci].state = NEGOTIATING;
+			ts0710->dlci[dlci].initiator = 1;
+			try = 3;
+			while (try--) {
+				t = jiffies;
+				send_pn_msg(ts0710, 7, ts0710->dlci[dlci].mtu,
+					    0, 0, dlci, 1);
+				interruptible_sleep_on_timeout(&ts0710->
+							       dlci[dlci].
+							       open_wait,
+							       TS0710MUX_TIME_OUT);
+				if (ts0710->dlci[dlci].state == CONNECTING) {
+					break;
+				} else if (signal_pending(current)) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Send pn_msg got signal!\n",
+					     dlci);
+					retval = -EAGAIN;
+					break;
+				} else if ((jiffies - t) >= TS0710MUX_TIME_OUT) {
+					TS0710_PRINTK
+					    ("MUX DLCI:%d Send pn_msg timeout!\n",
+					     dlci);
+					continue;
+				}
+			}
+
+			if (ts0710->dlci[dlci].state == CONNECTING) {
+				try = 3;
+				while (try--) {
+					t = jiffies;
+					send_sabm(ts0710, dlci);
+					interruptible_sleep_on_timeout(&ts0710->
+								       dlci
+								       [dlci].
+								       open_wait,
+								       TS0710MUX_TIME_OUT);
+					if ((ts0710->dlci[dlci].state ==
+					     CONNECTED)
+					    || (ts0710->dlci[dlci].state ==
+						FLOW_STOPPED)) {
+						retval = 0;
+						break;
+					} else if (ts0710->dlci[dlci].state ==
+						   REJECTED) {
+						TS0710_PRINTK
+						    ("MUX DLCI:%d Send SABM got rejected!\n",
+						     dlci);
+						retval = -EREJECTED;
+						break;
+					} else if (signal_pending(current)) {
+						TS0710_PRINTK
+						    ("MUX DLCI:%d Send SABM got signal!\n",
+						     dlci);
+						retval = -EAGAIN;
+						break;
+					} else if ((jiffies - t) >=
+						   TS0710MUX_TIME_OUT) {
+						TS0710_PRINTK
+						    ("MUX DLCI:%d Send SABM timeout!\n",
+						     dlci);
+						continue;
+					}
+				}
+			}
+
+			if ((ts0710->dlci[dlci].state == NEGOTIATING)
+			    || (ts0710->dlci[dlci].state == CONNECTING)) {
+				ts0710->dlci[dlci].state = DISCONNECTED;
+			}
+			wake_up_interruptible(&ts0710->dlci[dlci].open_wait);
+		}
+	}
+	return retval;
+}
+
+static int ts0710_exec_test_cmd(void)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	__u8 *f_buf;		/* Frame buffer */
+	__u8 *d_buf;		/* Data buffer */
+	int retval = -EFAULT;
+	int j;
+	unsigned long t;
+
+	if (ts0710->be_testing) {
+		/* Reentry */
+		t = jiffies;
+		interruptible_sleep_on_timeout(&ts0710->test_wait,
+					       3 * TS0710MUX_TIME_OUT);
+		if (ts0710->be_testing == 0) {
+			if (ts0710->test_errs == 0) {
+				retval = 0;
+			} else {
+				retval = -EFAULT;
+			}
+		} else if (signal_pending(current)) {
+			TS0710_DEBUG
+			    ("Wait for Test_cmd response got signal!\n");
+			retval = -EAGAIN;
+		} else if ((jiffies - t) >= 3 * TS0710MUX_TIME_OUT) {
+			TS0710_DEBUG("Wait for Test_cmd response timeout!\n");
+			retval = -EFAULT;
+		}
+	} else {
+		ts0710->be_testing = 1;	/* Set the flag */
+
+		f_buf = (__u8 *) kmalloc(TEST_PATTERN_SIZE + 32, GFP_KERNEL);
+		d_buf = (__u8 *) kmalloc(TEST_PATTERN_SIZE + 32, GFP_KERNEL);
+		if ((!f_buf) || (!d_buf)) {
+			if (f_buf) {
+				kfree(f_buf);
+			}
+			if (d_buf) {
+				kfree(d_buf);
+			}
+
+			ts0710->be_testing = 0;	/* Clear the flag */
+			ts0710->test_errs = TEST_PATTERN_SIZE;
+			wake_up_interruptible(&ts0710->test_wait);
+			return -ENOMEM;
+		}
+
+		for (j = 0; j < TEST_PATTERN_SIZE; j++) {
+			d_buf[j] = j & 0xFF;
+		}
+
+		t = jiffies;
+		ts0710_test_msg(ts0710, d_buf, TEST_PATTERN_SIZE, MCC_CMD,
+				f_buf);
+		interruptible_sleep_on_timeout(&ts0710->test_wait,
+					       2 * TS0710MUX_TIME_OUT);
+		if (ts0710->be_testing == 0) {
+			if (ts0710->test_errs == 0) {
+				retval = 0;
+			} else {
+				retval = -EFAULT;
+			}
+		} else if (signal_pending(current)) {
+			TS0710_DEBUG("Send Test_cmd got signal!\n");
+			retval = -EAGAIN;
+		} else if ((jiffies - t) >= 2 * TS0710MUX_TIME_OUT) {
+			TS0710_DEBUG("Send Test_cmd timeout!\n");
+			ts0710->test_errs = TEST_PATTERN_SIZE;
+			retval = -EFAULT;
+		}
+
+		ts0710->be_testing = 0;	/* Clear the flag */
+		wake_up_interruptible(&ts0710->test_wait);
+
+		/* Release buffer */
+		if (f_buf) {
+			kfree(f_buf);
+		}
+		if (d_buf) {
+			kfree(d_buf);
+		}
+	}
+
+	return retval;
+}
+
+static void mux_sched_send(void)
+{
+
+#ifdef USB_FOR_MUX
+	schedule_work(&send_tqueue);
+#else
+	if (!tq_serial_for_mux) {
+		TS0710_PRINTK("MUX Error: %s: tq_serial_for_mux == 0\n",
+			      __FUNCTION__);
+		return;
+	}
+	schedule_work(&send_tqueue);
+	mark_bh(SERIAL_BH);
+#endif
+
+}
+
+/****************************
+ * TTY driver routines
+*****************************/
+
+static void mux_close(struct tty_struct *tty, struct file *filp)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int line;
+	__u8 dlci;
+	__u8 cmdtty;
+	__u8 datatty;
+
+	UNUSED_PARAM(filp);
+
+	if (!tty) {
+		return;
+	}
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		return;
+	}
+	if (mux_tty[line] > 0)
+		mux_tty[line]--;
+
+	dlci = tty2dlci[line];
+	cmdtty = dlci2tty[dlci].cmdtty;
+	datatty = dlci2tty[dlci].datatty;
+	if ((mux_tty[cmdtty] == 0) && (mux_tty[datatty] == 0)) {
+		if (dlci == 1) {
+			ts0710_close_channel(0);
+			TS0710_PRINTK
+			    ("MUX mux_close: tapisrv might be down!!! Close DLCI 1\n");
+			TS0710_SIG2APLOGD();
+		}
+		ts0710_close_channel(dlci);
+	}
+
+	if (mux_tty[line] == 0) {
+		if ((mux_send_info_flags[line])
+		    && (mux_send_info[line])
+		    /*&& (mux_send_info[line]->filled == 0) */
+		    ) {
+			mux_send_info_flags[line] = 0;
+			kfree(mux_send_info[line]);
+			mux_send_info[line] = 0;
+			TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
+		}
+
+		if ((mux_recv_info_flags[line])
+		    && (mux_recv_info[line])
+		    && (mux_recv_info[line]->total == 0)) {
+			mux_recv_info_flags[line] = 0;
+			free_mux_recv_struct(mux_recv_info[line]);
+			mux_recv_info[line] = 0;
+			TS0710_DEBUG("Free mux_recv_info for /dev/mux%d", line);
+		}
+
+		ts0710_flow_on(dlci, ts0710);
+		schedule_work(&post_recv_tqueue);
+
+		wake_up_interruptible(&tty->read_wait);
+		wake_up_interruptible(&tty->write_wait);
+		tty->packet = 0;
+	}
+}
+
+static void mux_throttle(struct tty_struct *tty)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int line;
+	int i;
+	__u8 dlci;
+
+	if (!tty) {
+		return;
+	}
+
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		return;
+	}
+
+	TS0710_DEBUG("Enter into %s, minor number is: %d\n", __FUNCTION__,
+		     line);
+
+	dlci = tty2dlci[line];
+	if ((ts0710->dlci[0].state != CONNECTED)
+	    && (ts0710->dlci[0].state != FLOW_STOPPED)) {
+		return;
+	} else if ((ts0710->dlci[dlci].state != CONNECTED)
+		   && (ts0710->dlci[dlci].state != FLOW_STOPPED)) {
+		return;
+	}
+
+	if (ts0710->dlci[dlci].flow_control) {
+		return;
+	}
+
+	for (i = 0; i < 3; i++) {
+		if (ts0710_msc_msg
+		    (ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, dlci) < 0) {
+			continue;
+		} else {
+			TS0710_LOG("MUX Send Flow off on dlci %d\n", dlci);
+			ts0710->dlci[dlci].flow_control = 1;
+			break;
+		}
+	}
+}
+
+static void mux_unthrottle(struct tty_struct *tty)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int line;
+	__u8 dlci;
+	mux_recv_struct *recv_info;
+
+	if (!tty) {
+		return;
+	}
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		return;
+	}
+
+	if ((!mux_recv_info_flags[line]) || (!mux_recv_info[line])) {
+		return;
+	}
+
+	TS0710_DEBUG("Enter into %s, minor number is: %d\n", __FUNCTION__,
+		     line);
+
+	recv_info = mux_recv_info[line];
+	dlci = tty2dlci[line];
+
+	if (recv_info->total) {
+		recv_info->post_unthrottle = 1;
+		schedule_work(&post_recv_tqueue);
+	} else {
+		ts0710_flow_on(dlci, ts0710);
+	}
+}
+
+static int mux_chars_in_buffer(struct tty_struct *tty)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int retval;
+	int line;
+	__u8 dlci;
+	mux_send_struct *send_info;
+
+	retval = TS0710MUX_MAX_CHARS_IN_BUF;
+	if (!tty) {
+		goto out;
+	}
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		goto out;
+	}
+
+	dlci = tty2dlci[line];
+	if (ts0710->dlci[0].state == FLOW_STOPPED) {
+		TS0710_DEBUG
+		    ("Flow stopped on all channels, returning MAX chars in buffer\n");
+		goto out;
+	} else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
+		TS0710_DEBUG("Flow stopped, returning MAX chars in buffer\n");
+		goto out;
+	} else if (ts0710->dlci[dlci].state != CONNECTED) {
+		TS0710_DEBUG("DLCI %d not connected\n", dlci);
+		goto out;
+	}
+
+	if (!(mux_send_info_flags[line])) {
+		goto out;
+	}
+	send_info = mux_send_info[line];
+	if (!send_info) {
+		goto out;
+	}
+	if (send_info->filled) {
+		goto out;
+	}
+
+	retval = 0;
+
+      out:
+	return retval;
+}
+
+static int mux_chars_in_serial_buffer(struct tty_struct *tty)
+{
+	UNUSED_PARAM(tty);
+
+	if ((COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)) {
+		TS0710_PRINTK
+		    ("MUX %s: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n",
+		     __FUNCTION__);
+
+#ifndef USB_FOR_MUX
+		TS0710_PRINTK
+		    ("MUX %s: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n",
+		     __FUNCTION__);
+		TS0710_SIG2APLOGD();
+#endif
+
+		return 0;
+	}
+	return COMM_FOR_MUX_DRIVER->chars_in_buffer(COMM_FOR_MUX_TTY);
+}
+
+static int mux_write(struct tty_struct *tty,
+		     const unsigned char *buf, int count)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int line;
+	__u8 dlci;
+	mux_send_struct *send_info;
+	__u8 *d_buf;
+	__u16 c;
+	__u8 post_recv;
+
+	if (count <= 0) {
+		return 0;
+	}
+
+	if (!tty) {
+		return 0;
+	}
+
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS))
+		return -ENODEV;
+
+	dlci = tty2dlci[line];
+	if (ts0710->dlci[0].state == FLOW_STOPPED) {
+		TS0710_DEBUG
+		    ("Flow stopped on all channels, returning zero /dev/mux%d\n",
+		     line);
+		return 0;
+	} else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
+		TS0710_DEBUG("Flow stopped, returning zero /dev/mux%d\n", line);
+		return 0;
+	} else if (ts0710->dlci[dlci].state == CONNECTED) {
+
+		if (!(mux_send_info_flags[line])) {
+			TS0710_PRINTK
+			    ("MUX Error: mux_write: mux_send_info_flags[%d] == 0\n",
+			     line);
+			return -ENODEV;
+		}
+		send_info = mux_send_info[line];
+		if (!send_info) {
+			TS0710_PRINTK
+			    ("MUX Error: mux_write: mux_send_info[%d] == 0\n",
+			     line);
+			return -ENODEV;
+		}
+
+		c = min(count, (ts0710->dlci[dlci].mtu - 1));
+		if (c <= 0) {
+			return 0;
+		}
+
+		if (test_and_set_bit(BUF_BUSY, &send_info->flags))
+			return 0;
+
+		if (send_info->filled) {
+			clear_bit(BUF_BUSY, &send_info->flags);
+			return 0;
+		}
+
+		d_buf = ((__u8 *) send_info->buf) + TS0710MUX_SEND_BUF_OFFSET;
+		memcpy(&d_buf[1], buf, c);
+
+		TS0710_DEBUG("Prepare to send %d bytes from /dev/mux%d", c,
+			     line);
+		if (iscmdtty[line]) {
+			TS0710_DEBUGSTR(&d_buf[1], c);
+			TS0710_DEBUG("CMDTAG");
+			d_buf[0] = CMDTAG;
+		} else {
+			TS0710_DEBUG("DATATAG");
+			d_buf[0] = DATATAG;
+		}
+
+		TS0710_DEBUGHEX(d_buf, c + 1);
+
+		send_info->frame = d_buf;
+		queue_uih(send_info, c + 1, ts0710, dlci);
+		send_info->filled = 1;
+		clear_bit(BUF_BUSY, &send_info->flags);
+
+		post_recv = 0;
+		if (dlci == TS0710MUX_GPRS1_DLCI) {
+			if (add_count
+			    (TS0710MUX_GPRS1_SEND_COUNT_IDX, c) < 0) {
+				post_recv_count_flag = 1;
+				post_recv = 1;
+				mux_data_count2[TS0710MUX_GPRS1_SEND_COUNT_IDX]
+				    += c;
+			}
+		} else if (dlci == TS0710MUX_GPRS2_DLCI) {
+			if (add_count
+			    (TS0710MUX_GPRS2_SEND_COUNT_IDX, c) < 0) {
+				post_recv_count_flag = 1;
+				post_recv = 1;
+				mux_data_count2[TS0710MUX_GPRS2_SEND_COUNT_IDX]
+				    += c;
+			}
+		}
+
+		if (post_recv)
+			schedule_work(&post_recv_tqueue);
+
+		if (mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY) == 0) {
+			/* Sending bottom half should be
+			   run after return from this function */
+			mux_sched_send();
+		}
+		return c;
+	} else {
+		TS0710_PRINTK("MUX mux_write: DLCI %d not connected\n", dlci);
+		return -EDISCONNECTED;
+	}
+}
+
+static int mux_write_room(struct tty_struct *tty)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int retval;
+	int line;
+	__u8 dlci;
+	mux_send_struct *send_info;
+
+	retval = 0;
+	if (!tty) {
+		goto out;
+	}
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		goto out;
+	}
+
+	dlci = tty2dlci[line];
+	if (ts0710->dlci[0].state == FLOW_STOPPED) {
+		TS0710_DEBUG("Flow stopped on all channels, returning ZERO\n");
+		goto out;
+	} else if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
+		TS0710_DEBUG("Flow stopped, returning ZERO\n");
+		goto out;
+	} else if (ts0710->dlci[dlci].state != CONNECTED) {
+		TS0710_DEBUG("DLCI %d not connected\n", dlci);
+		goto out;
+	}
+
+	if (!(mux_send_info_flags[line])) {
+		goto out;
+	}
+	send_info = mux_send_info[line];
+	if (!send_info) {
+		goto out;
+	}
+	if (send_info->filled) {
+		goto out;
+	}
+
+	retval = ts0710->dlci[dlci].mtu - 1;
+
+      out:
+	return retval;
+}
+
+static int mux_ioctl(struct tty_struct *tty, struct file *file,
+		     unsigned int cmd, unsigned long arg)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int line;
+	__u8 dlci;
+
+	UNUSED_PARAM(file);
+	UNUSED_PARAM(arg);
+
+	if (!tty) {
+		return -EIO;
+	}
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		return -ENODEV;
+	}
+
+	dlci = tty2dlci[line];
+	switch (cmd) {
+	case TS0710MUX_IO_MSC_HANGUP:
+		if (ts0710_msc_msg(ts0710, EA | RTR | DV, MCC_CMD, dlci) < 0) {
+			return -EAGAIN;
+		} else {
+			return 0;
+		}
+
+	case TS0710MUX_IO_TEST_CMD:
+		return ts0710_exec_test_cmd();
+/*
+    case TS0710MUX_IO_DLCI_FC_ON:
+      if( line == 0 ) {
+        break;
+      }
+      if( ts0710_msc_msg(ts0710, EA | RTC | RTR | DV, MCC_CMD, (__u8)line) < 0) {
+        return -EAGAIN;
+      } else {
+        return 0;
+      }
+
+    case TS0710MUX_IO_DLCI_FC_OFF:
+      if( line == 0 ) {
+        break;
+      }
+      if( ts0710_msc_msg(ts0710, EA | FC | RTC | RTR | DV, MCC_CMD, (__u8)line) < 0) {
+        return -EAGAIN;
+      } else {
+        return 0;
+      }
+
+    case TS0710MUX_IO_FC_ON:
+      if( line != 0 ) {
+        break;
+      }
+      if( ts0710_fcon_msg(ts0710, MCC_CMD) < 0) {
+        return -EAGAIN;
+      } else {
+        return 0;
+      }
+
+    case TS0710MUX_IO_FC_OFF:
+      if( line != 0 ) {
+        break;
+      }
+      if( ts0710_fcoff_msg(ts0710, MCC_CMD) < 0) {
+        return -EAGAIN;
+      } else {
+        return 0;
+      }
+*/
+	default:
+		break;
+	}
+	return -ENOIOCTLCMD;
+}
+
+static void mux_flush_buffer(struct tty_struct *tty)
+{
+	int line;
+
+	if (!tty) {
+		return;
+	}
+
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		return;
+	}
+
+	TS0710_PRINTK("MUX %s: line is:%d\n", __FUNCTION__, line);
+
+	if ((mux_send_info_flags[line])
+	    && (mux_send_info[line])
+	    && (mux_send_info[line]->filled)) {
+
+		mux_send_info[line]->filled = 0;
+	}
+
+	wake_up_interruptible(&tty->write_wait);
+#ifdef SERIAL_HAVE_POLL_WAIT
+	wake_up_interruptible(&tty->poll_wait);
+#endif
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+	    tty->ldisc.write_wakeup) {
+		(tty->ldisc.write_wakeup) (tty);
+	}
+
+/*
+  if( (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0) ) {
+    TS0710_PRINTK("MUX %s: (COMM_FOR_MUX_DRIVER == 0) || (COMM_FOR_MUX_TTY == 0)\n", __FUNCTION__);
+
+#ifndef USB_FOR_MUX
+    TS0710_PRINTK("MUX %s: tapisrv might be down!!! (serial_for_mux_driver == 0) || (serial_for_mux_tty == 0)\n", __FUNCTION__);
+    TS0710_SIG2APLOGD();
+#endif
+
+    return;
+  }
+  return COMM_FOR_MUX_DRIVER->flush_buffer(COMM_FOR_MUX_TTY);
+*/
+}
+
+static int mux_open(struct tty_struct *tty, struct file *filp)
+{
+	int retval;
+	int line;
+	__u8 dlci;
+	__u8 cmdtty;
+	__u8 datatty;
+	mux_send_struct *send_info;
+	mux_recv_struct *recv_info;
+
+	UNUSED_PARAM(filp);
+
+	retval = -ENODEV;
+	if ((COMM_FOR_MUX_DRIVER == NULL) || (COMM_FOR_MUX_TTY == NULL)) {
+
+#ifdef USB_FOR_MUX
+		TS0710_PRINTK("MUX: please install and open IPC-USB first\n");
+#else
+		TS0710_PRINTK("MUX: please install and open ttyS0 first\n");
+#endif
+
+		goto out;
+	}
+
+	if (!tty) {
+		goto out;
+	}
+	line = tty->index;
+	if ((line < 0) || (line >= NR_MUXS)) {
+		goto out;
+	}
+#ifdef TS0710SERVER
+	/* do nothing as a server */
+	mux_tty[line]++;
+	retval = 0;
+#else
+	mux_tty[line]++;
+	dlci = tty2dlci[line];
+
+/*  if( dlci == 1 ) { */
+	/* Open server channel 0 first */
+	if ((retval = ts0710_open_channel(0)) != 0) {
+		TS0710_PRINTK("MUX: Can't connect server channel 0!\n");
+		ts0710_init();
+
+		mux_tty[line]--;
+		goto out;
+	}
+/*  } */
+
+	/* Allocate memory first. As soon as connection has been established, MUX may receive */
+	if (mux_send_info_flags[line] == 0) {
+		send_info =
+		    (mux_send_struct *) kmalloc(sizeof(mux_send_struct),
+						GFP_KERNEL);
+		if (!send_info) {
+			retval = -ENOMEM;
+
+			mux_tty[line]--;
+			goto out;
+		}
+		send_info->length = 0;
+		send_info->flags = 0;
+		send_info->filled = 0;
+		mux_send_info[line] = send_info;
+		mux_send_info_flags[line] = 1;
+		TS0710_DEBUG("Allocate mux_send_info for /dev/mux%d", line);
+	}
+
+	if (mux_recv_info_flags[line] == 0) {
+		recv_info =
+		    (mux_recv_struct *) kmalloc(sizeof(mux_recv_struct),
+						GFP_KERNEL);
+		if (!recv_info) {
+			mux_send_info_flags[line] = 0;
+			kfree(mux_send_info[line]);
+			mux_send_info[line] = 0;
+			TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
+			retval = -ENOMEM;
+
+			mux_tty[line]--;
+			goto out;
+		}
+		recv_info->length = 0;
+		recv_info->total = 0;
+		recv_info->mux_packet = 0;
+		recv_info->next = 0;
+		recv_info->no_tty = line;
+		recv_info->post_unthrottle = 0;
+		mux_recv_info[line] = recv_info;
+		mux_recv_info_flags[line] = 1;
+		TS0710_DEBUG("Allocate mux_recv_info for /dev/mux%d", line);
+	}
+
+	/* Now establish DLCI connection */
+	cmdtty = dlci2tty[dlci].cmdtty;
+	datatty = dlci2tty[dlci].datatty;
+	if ((mux_tty[cmdtty] > 0) || (mux_tty[datatty] > 0)) {
+		if ((retval = ts0710_open_channel(dlci)) != 0) {
+			TS0710_PRINTK("MUX: Can't connected channel %d!\n",
+				      dlci);
+			ts0710_reset_dlci(dlci);
+
+			mux_send_info_flags[line] = 0;
+			kfree(mux_send_info[line]);
+			mux_send_info[line] = 0;
+			TS0710_DEBUG("Free mux_send_info for /dev/mux%d", line);
+
+			mux_recv_info_flags[line] = 0;
+			free_mux_recv_struct(mux_recv_info[line]);
+			mux_recv_info[line] = 0;
+			TS0710_DEBUG("Free mux_recv_info for /dev/mux%d", line);
+
+			mux_tty[line]--;
+			goto out;
+		}
+	}
+
+	retval = 0;
+#endif
+      out:
+	return retval;
+}
+
+/* mux dispatcher, call from serial.c receiver_chars() */
+void mux_dispatcher(struct tty_struct *tty)
+{
+	UNUSED_PARAM(tty);
+
+	schedule_work(&receive_tqueue);
+}
+
+/*For BP UART problem Begin*/
+#ifdef TS0710SEQ2
+static int send_ack(ts0710_con * ts0710, __u8 seq_num, __u8 bp_seq1,
+		    __u8 bp_seq2)
+#else
+static int send_ack(ts0710_con * ts0710, __u8 seq_num)
+#endif
+{
+	__u8 buf[20];
+	short_frame *ack;
+
+#ifdef TS0710SEQ2
+	static __u16 ack_seq = 0;
+#endif
+
+	ack = (short_frame *) (buf + 1);
+	ack->h.addr.ea = 1;
+	ack->h.addr.cr = ((ts0710->initiator) & 0x1);
+	ack->h.addr.d = 0;
+	ack->h.addr.server_chn = 0;
+	ack->h.control = ACK;
+	ack->h.length.ea = 1;
+
+#ifdef TS0710SEQ2
+	ack->h.length.len = 5;
+	ack->data[0] = seq_num;
+	ack->data[1] = bp_seq1;
+	ack->data[2] = bp_seq2;
+	ack->data[3] = (ack_seq & 0xFF);
+	ack->data[4] = (ack_seq >> 8) & 0xFF;
+	ack_seq++;
+	ack->data[5] = crc_calc((__u8 *) ack, SHORT_CRC_CHECK);
+#else
+	ack->h.length.len = 1;
+	ack->data[0] = seq_num;
+	ack->data[1] = crc_calc((__u8 *) ack, SHORT_CRC_CHECK);
+#endif
+
+	return basic_write(ts0710, buf,
+			   (sizeof(short_frame) + FCS_SIZE +
+			    ack->h.length.len));
+}
+
+/*For BP UART problem End*/
+
+static void receive_worker(void *private_)
+{
+	struct tty_struct *tty = COMM_FOR_MUX_TTY;
+	int i, count;
+	static unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE];
+	static unsigned char *tbuf_ptr = &tbuf[0];
+	static unsigned char *start_flag = 0;
+	unsigned char *search, *to, *from;
+	short_frame *short_pkt;
+	long_frame *long_pkt;
+	static int framelen = -1;
+
+	/*For BP UART problem Begin */
+	static __u8 expect_seq = 0;
+	__u32 crc_error;
+	__u8 *uih_data_start;
+	__u32 uih_len;
+	/*For BP UART problem End */
+
+	UNUSED_PARAM(private_);
+
+	if (!tty)
+		return;
+
+#ifdef USB_FOR_MUX
+	TS0710_DEBUG("Receive following bytes from IPC-USB");
+#else
+	TS0710_DEBUG("Receive following bytes from UART");
+#endif
+
+	TS0710_DEBUGHEX(cp, count);
+
+	if (count > (TS0710MUX_MAX_BUF_SIZE - (tbuf_ptr - tbuf))) {
+		TS0710_PRINTK
+		    ("MUX receive_worker: !!!!! Exceed buffer boundary !!!!!\n");
+		count = (TS0710MUX_MAX_BUF_SIZE - (tbuf_ptr - tbuf));
+	}
+
+	count = tty_buffer_request_room(tty, count);
+
+	for (i = 0; i < count; i++)
+		tty_insert_flip_char(tty, tbuf_ptr[i], TTY_NORMAL);
+
+	tbuf_ptr += count;
+	search = &tbuf[0];
+
+	if (test_and_set_bit(RECV_RUNNING, &mux_recv_flags)) {
+		schedule_work(&receive_tqueue);
+		return;
+	}
+
+	if ((start_flag != 0) && (framelen != -1)) {
+		if ((tbuf_ptr - start_flag) < framelen) {
+			clear_bit(RECV_RUNNING, &mux_recv_flags);
+			return;
+		}
+	}
+
+	while (1) {
+		if (start_flag == 0) {	/* Frame Start Flag not found */
+			framelen = -1;
+			while (search < tbuf_ptr) {
+				if (*search == TS0710_BASIC_FLAG) {
+					start_flag = search;
+					break;
+				}
+#ifdef TS0710LOG
+				else {
+					TS0710_LOG(">S %02x %c\n", *search,
+						   *search);
+				}
+#endif
+
+				search++;
+			}
+
+			if (start_flag == 0) {
+				tbuf_ptr = &tbuf[0];
+				break;
+			}
+		} else {	/* Frame Start Flag found */
+			/* 1 start flag + 1 address + 1 control + 1 or 2 length + lengths data + 1 FCS + 1 end flag */
+			/* For BP UART problem 1 start flag + 1 seq_num + 1 address + ...... */
+			/*if( (framelen == -1) && ((tbuf_ptr - start_flag) > TS0710_MAX_HDR_SIZE) ) */
+			if ((framelen == -1) && ((tbuf_ptr - start_flag) > (TS0710_MAX_HDR_SIZE + SEQ_FIELD_SIZE))) {	/*For BP UART problem */
+				/*short_pkt = (short_frame *) (start_flag + 1); */
+				short_pkt = (short_frame *) (start_flag + ADDRESS_FIELD_OFFSET);	/*For BP UART problem */
+				if (short_pkt->h.length.ea == 1) {	/* short frame */
+					/*framelen = TS0710_MAX_HDR_SIZE + short_pkt->h.length.len + 1; */
+					framelen = TS0710_MAX_HDR_SIZE + short_pkt->h.length.len + 1 + SEQ_FIELD_SIZE;	/*For BP UART problem */
+				} else {	/* long frame */
+					/*long_pkt = (long_frame *) (start_flag + 1); */
+					long_pkt = (long_frame *) (start_flag + ADDRESS_FIELD_OFFSET);	/*For BP UART problem */
+					/*framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH( long_pkt->h.length ) + 2; */
+					framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH(long_pkt->h.length) + 2 + SEQ_FIELD_SIZE;	/*For BP UART problem */
+				}
+
+				/*if( framelen > TS0710MUX_MAX_TOTAL_FRAME_SIZE ) { */
+				if (framelen > (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)) {	/*For BP UART problem */
+					TS0710_LOGSTR_FRAME(0, start_flag,
+							    (tbuf_ptr -
+							     start_flag));
+					TS0710_PRINTK
+					    ("MUX Error: %s: frame length:%d is bigger than Max total frame size:%d\n",
+		 /*__FUNCTION__, framelen, TS0710MUX_MAX_TOTAL_FRAME_SIZE);*/
+					     __FUNCTION__, framelen, (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE));	/*For BP UART problem */
+					search = start_flag + 1;
+					start_flag = 0;
+					framelen = -1;
+					continue;
+				}
+			}
+
+			if ((framelen != -1)
+			    && ((tbuf_ptr - start_flag) >= framelen)) {
+				if (*(start_flag + framelen - 1) == TS0710_BASIC_FLAG) {	/* OK, We got one frame */
+
+					/*For BP UART problem Begin */
+					TS0710_LOGSTR_FRAME(0, start_flag,
+							    framelen);
+					TS0710_DEBUGHEX(start_flag, framelen);
+
+					short_pkt =
+					    (short_frame *) (start_flag +
+							     ADDRESS_FIELD_OFFSET);
+					if ((short_pkt->h.length.ea) == 0) {
+						long_pkt =
+						    (long_frame *) (start_flag +
+								    ADDRESS_FIELD_OFFSET);
+						uih_len =
+						    GET_LONG_LENGTH(long_pkt->h.
+								    length);
+						uih_data_start =
+						    long_pkt->h.data;
+
+						crc_error =
+						    crc_check((__u8
+							       *) (start_flag +
+								   SLIDE_BP_SEQ_OFFSET),
+							      LONG_CRC_CHECK +
+							      1,
+							      *(uih_data_start +
+								uih_len));
+					} else {
+						uih_len =
+						    short_pkt->h.length.len;
+						uih_data_start =
+						    short_pkt->data;
+
+						crc_error =
+						    crc_check((__u8
+							       *) (start_flag +
+								   SLIDE_BP_SEQ_OFFSET),
+							      SHORT_CRC_CHECK +
+							      1,
+							      *(uih_data_start +
+								uih_len));
+					}
+
+					if (!crc_error) {
+						if (expect_seq ==
+						    *(start_flag +
+						      SLIDE_BP_SEQ_OFFSET)) {
+							expect_seq++;
+							if (expect_seq >= 4) {
+								expect_seq = 0;
+							}
+#ifdef TS0710SEQ2
+							send_ack
+							    (&ts0710_connection,
+							     expect_seq,
+							     *(start_flag +
+							       FIRST_BP_SEQ_OFFSET),
+							     *(start_flag +
+							       SECOND_BP_SEQ_OFFSET));
+#else
+							send_ack
+							    (&ts0710_connection,
+							     expect_seq);
+#endif
+
+							ts0710_recv_data
+							    (&ts0710_connection,
+							     start_flag +
+							     ADDRESS_FIELD_OFFSET,
+							     framelen - 2 -
+							     SEQ_FIELD_SIZE);
+						} else {
+
+#ifdef TS0710DEBUG
+							if (*
+							    (start_flag +
+							     SLIDE_BP_SEQ_OFFSET)
+							    != 0x9F) {
+#endif
+
+								TS0710_LOG
+								    ("MUX sequence number %d is not expected %d, discard data!\n",
+								     *
+								     (start_flag
+								      +
+								      SLIDE_BP_SEQ_OFFSET),
+								     expect_seq);
+
+#ifdef TS0710SEQ2
+								send_ack
+								    (&ts0710_connection,
+								     expect_seq,
+								     *
+								     (start_flag
+								      +
+								      FIRST_BP_SEQ_OFFSET),
+								     *
+								     (start_flag
+								      +
+								      SECOND_BP_SEQ_OFFSET));
+#else
+								send_ack
+								    (&ts0710_connection,
+								     expect_seq);
+#endif
+
+#ifdef TS0710DEBUG
+							} else {
+								*(uih_data_start
+								  + uih_len) =
+						     0;
+								TS0710_PRINTK
+								    ("MUX bp log: %s\n",
+								     uih_data_start);
+							}
+#endif
+
+						}
+					} else {	/* crc_error */
+						search = start_flag + 1;
+						start_flag = 0;
+						framelen = -1;
+						continue;
+					}	/*End if(!crc_error) */
+
+					/*For BP UART problem End */
+
+/*For BP UART problem
+          TS0710_LOGSTR_FRAME(0, start_flag, framelen);
+          TS0710_DEBUGHEX(start_flag, framelen);
+	  ts0710_recv_data(&ts0710_connection, start_flag + 1, framelen - 2);
+*/
+					search = start_flag + framelen;
+				} else {
+					TS0710_LOGSTR_FRAME(0, start_flag,
+							    framelen);
+					TS0710_DEBUGHEX(start_flag, framelen);
+					TS0710_PRINTK
+					    ("MUX: Lost synchronization!\n");
+					search = start_flag + 1;
+				}
+
+				start_flag = 0;
+				framelen = -1;
+				continue;
+			}
+
+			if (start_flag != &tbuf[0]) {
+				to = tbuf;
+				from = start_flag;
+				count = tbuf_ptr - start_flag;
+				while (count--) {
+					*to++ = *from++;
+				}
+
+				tbuf_ptr -= (start_flag - tbuf);
+				start_flag = tbuf;
+			}
+			break;
+		}		/* End Frame Start Flag found */
+	}			/* End while(1) */
+
+	clear_bit(RECV_RUNNING, &mux_recv_flags);
+}
+
+static void post_recv_worker(void *private_)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	int tty_idx;
+	struct tty_struct *tty;
+	__u8 post_recv;
+	__u8 flow_control;
+	__u8 dlci;
+	mux_recv_struct *recv_info, *recv_info2, *post_recv_q;
+	int recv_room;
+	mux_recv_packet *recv_packet, *recv_packet2;
+	__u8 j;
+
+	UNUSED_PARAM(private_);
+
+	if (test_and_set_bit(RECV_RUNNING, &mux_recv_flags)) {
+		schedule_work(&post_recv_tqueue);
+		return;
+	}
+
+	TS0710_DEBUG("Enter into post_recv_worker");
+
+	post_recv = 0;
+	if (!mux_recv_queue) {
+		goto out;
+	}
+
+	post_recv_q = NULL;
+	recv_info2 = mux_recv_queue;
+	while ((recv_info = recv_info2)) {
+		recv_info2 = recv_info->next;
+
+		if (!(recv_info->total)) {
+			TS0710_PRINTK
+			    ("MUX Error: %s: Should not get here, recv_info->total == 0 \n",
+			     __FUNCTION__);
+			continue;
+		}
+
+		tty_idx = recv_info->no_tty;
+		dlci = tty2dlci[tty_idx];
+		tty = mux_table[tty_idx];
+		if ((!mux_tty[tty_idx]) || (!tty)) {
+			TS0710_PRINTK
+			    ("MUX: No application waiting for, free recv_info! tty_idx:%d\n",
+			     tty_idx);
+			mux_recv_info_flags[tty_idx] = 0;
+			free_mux_recv_struct(mux_recv_info[tty_idx]);
+			mux_recv_info[tty_idx] = 0;
+			ts0710_flow_on(dlci, ts0710);
+			continue;
+		}
+
+		TS0710_DEBUG("/dev/mux%d recv_info->total is: %d", tty_idx,
+			     recv_info->total);
+
+		if (test_bit(TTY_THROTTLED, &tty->flags)) {
+			add_post_recv_queue(&post_recv_q, recv_info);
+			continue;
+		} else if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+			post_recv = 1;
+			add_post_recv_queue(&post_recv_q, recv_info);
+			continue;
+		}
+
+		flow_control = 0;
+		recv_packet2 = recv_info->mux_packet;
+		while (recv_info->total) {
+			recv_room = 65535;
+			if (tty->receive_room)
+				recv_room = tty->receive_room;
+
+			if (recv_info->length) {
+				if (recv_room < recv_info->length) {
+					flow_control = 1;
+					break;
+				}
+
+				/* Put queued data into read buffer of tty */
+				TS0710_DEBUG
+				    ("Put queued recv data into read buffer of /dev/mux%d",
+				     tty_idx);
+				TS0710_DEBUGHEX(recv_info->data,
+						recv_info->length);
+				(tty->ldisc.receive_buf) (tty, recv_info->data,
+							  NULL,
+							  recv_info->length);
+				recv_info->total -= recv_info->length;
+				recv_info->length = 0;
+			} else {	/* recv_info->length == 0 */
+				if ((recv_packet = recv_packet2)) {
+					recv_packet2 = recv_packet->next;
+
+					if (recv_room < recv_packet->length) {
+						flow_control = 1;
+						recv_info->mux_packet =
+						    recv_packet;
+						break;
+					}
+
+					/* Put queued data into read buffer of tty */
+					TS0710_DEBUG
+					    ("Put queued recv data into read buffer of /dev/mux%d",
+					     tty_idx);
+					TS0710_DEBUGHEX(recv_packet->data,
+							recv_packet->length);
+					(tty->ldisc.receive_buf) (tty,
+								  recv_packet->
+								  data, NULL,
+								  recv_packet->
+								  length);
+					recv_info->total -= recv_packet->length;
+					free_mux_recv_packet(recv_packet);
+				} else {
+					TS0710_PRINTK
+					    ("MUX Error: %s: Should not get here, recv_info->total is:%u \n",
+					     __FUNCTION__, recv_info->total);
+				}
+			}	/* End recv_info->length == 0 */
+		}		/* End while( recv_info->total ) */
+
+		if (!(recv_info->total)) {
+			/* Important clear */
+			recv_info->mux_packet = 0;
+
+			if (recv_info->post_unthrottle) {
+				/* Do something for post_unthrottle */
+				ts0710_flow_on(dlci, ts0710);
+				recv_info->post_unthrottle = 0;
+			}
+		} else {
+			add_post_recv_queue(&post_recv_q, recv_info);
+
+			if (flow_control) {
+				/* Do something for flow control */
+				if (recv_info->post_unthrottle) {
+					set_bit(TTY_THROTTLED, &tty->flags);
+					recv_info->post_unthrottle = 0;
+				} else {
+					ts0710_flow_off(tty, dlci, ts0710);
+				}
+			}	/* End if( flow_control ) */
+		}
+	}			/* End while( (recv_info = recv_info2) ) */
+
+	mux_recv_queue = post_recv_q;
+
+      out:
+	if (post_recv_count_flag) {
+		post_recv_count_flag = 0;
+		for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) {
+			if (mux_data_count2[j] > 0) {
+				if (add_count(j, mux_data_count2[j]) == 0) {
+					mux_data_count2[j] = 0;
+				} else {
+					post_recv_count_flag = 1;
+					post_recv = 1;
+				}
+			}
+		}		/* End for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) */
+	}
+	/* End if( post_recv_count_flag ) */
+	if (post_recv)
+		schedule_work(&post_recv_tqueue);
+	clear_bit(RECV_RUNNING, &mux_recv_flags);
+}
+
+/* mux sender, call from serial.c transmit_chars() */
+void mux_sender(void)
+{
+	mux_send_struct *send_info;
+	int chars;
+	__u8 idx;
+
+	chars = mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY);
+	if (!chars) {
+		/* chars == 0 */
+		TS0710_LOG("<[]\n");
+		mux_sched_send();
+		return;
+	}
+
+	idx = mux_send_info_idx;
+	if ((idx < NR_MUXS) && (mux_send_info_flags[idx])) {
+		send_info = mux_send_info[idx];
+		if ((send_info)
+		    && (send_info->filled)
+		    && (send_info->length <=
+			(TS0710MUX_SERIAL_BUF_SIZE - chars))) {
+
+			mux_sched_send();
+		}
+	}
+}
+
+static void send_worker(void *private_)
+{
+	ts0710_con *ts0710 = &ts0710_connection;
+	__u8 j;
+	mux_send_struct *send_info;
+	int chars;
+	struct tty_struct *tty;
+	__u8 dlci;
+
+	UNUSED_PARAM(private_);
+
+	TS0710_DEBUG("Enter into send_worker");
+
+	mux_send_info_idx = NR_MUXS;
+
+	if (ts0710->dlci[0].state == FLOW_STOPPED) {
+		TS0710_DEBUG("Flow stopped on all channels\n");
+		return;
+	}
+
+	for (j = 0; j < NR_MUXS; j++) {
+
+		if (!(mux_send_info_flags[j])) {
+			continue;
+		}
+
+		send_info = mux_send_info[j];
+		if (!send_info) {
+			continue;
+		}
+
+		if (!(send_info->filled)) {
+			continue;
+		}
+
+		dlci = tty2dlci[j];
+		if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
+			TS0710_DEBUG("Flow stopped on channel DLCI: %d\n",
+				     dlci);
+			continue;
+		} else if (ts0710->dlci[dlci].state != CONNECTED) {
+			TS0710_DEBUG("DLCI %d not connected\n", dlci);
+			send_info->filled = 0;
+			continue;
+		}
+
+		chars = mux_chars_in_serial_buffer(COMM_FOR_MUX_TTY);
+		if (send_info->length <= (TS0710MUX_SERIAL_BUF_SIZE - chars)) {
+			TS0710_DEBUG("Send queued UIH for /dev/mux%d", j);
+			basic_write(ts0710, (__u8 *) send_info->frame,
+				    send_info->length);
+			send_info->length = 0;
+			send_info->filled = 0;
+		} else {
+			mux_send_info_idx = j;
+			break;
+		}
+	}			/* End for() loop */
+
+	/* Queue UIH data to be transmitted */
+	for (j = 0; j < NR_MUXS; j++) {
+
+		if (!(mux_send_info_flags[j])) {
+			continue;
+		}
+
+		send_info = mux_send_info[j];
+		if (!send_info) {
+			continue;
+		}
+
+		if (send_info->filled) {
+			continue;
+		}
+
+		/* Now queue UIH data to send_info->buf */
+
+		if (!mux_tty[j]) {
+			continue;
+		}
+
+		tty = mux_table[j];
+		if (!tty) {
+			continue;
+		}
+
+		dlci = tty2dlci[j];
+		if (ts0710->dlci[dlci].state == FLOW_STOPPED) {
+			TS0710_DEBUG("Flow stopped on channel DLCI: %d\n",
+				     dlci);
+			continue;
+		} else if (ts0710->dlci[dlci].state != CONNECTED) {
+			TS0710_DEBUG("DLCI %d not connected\n", dlci);
+			continue;
+		}
+
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+		    && tty->ldisc.write_wakeup) {
+			(tty->ldisc.write_wakeup) (tty);
+		}
+		wake_up_interruptible(&tty->write_wait);
+
+#ifdef SERIAL_HAVE_POLL_WAIT
+		wake_up_interruptible(&tty->poll_wait);
+#endif
+
+		if (send_info->filled) {
+			if (j < mux_send_info_idx) {
+				mux_send_info_idx = j;
+			}
+		}
+	}			/* End for() loop */
+}
+
+static int get_count(__u8 idx)
+{
+	int ret;
+
+	if (idx > TS0710MUX_COUNT_MAX_IDX) {
+		TS0710_PRINTK("MUX get_count: invalid idx: %d!\n", idx);
+		return -1;
+	}
+
+	down(&mux_data_count_mutex[idx]);
+	ret = mux_data_count[idx];
+	up(&mux_data_count_mutex[idx]);
+
+	return ret;
+}
+
+static int set_count(__u8 idx, int count)
+{
+	if (idx > TS0710MUX_COUNT_MAX_IDX) {
+		TS0710_PRINTK("MUX set_count: invalid idx: %d!\n", idx);
+		return -1;
+	}
+	if (count < 0) {
+		TS0710_PRINTK("MUX set_count: invalid count: %d!\n", count);
+		return -1;
+	}
+
+	down(&mux_data_count_mutex[idx]);
+	mux_data_count[idx] = count;
+	up(&mux_data_count_mutex[idx]);
+
+	return 0;
+}
+
+static int add_count(__u8 idx, int count)
+{
+	if (idx > TS0710MUX_COUNT_MAX_IDX) {
+		TS0710_PRINTK("MUX add_count: invalid idx: %d!\n", idx);
+		return -1;
+	}
+	if (count <= 0) {
+		TS0710_PRINTK("MUX add_count: invalid count: %d!\n", count);
+		return -1;
+	}
+
+	if (down_trylock(&mux_data_count_mutex[idx]))
+		return -1;
+	mux_data_count[idx] += count;
+	up(&mux_data_count_mutex[idx]);
+
+	return 0;
+}
+
+ssize_t file_proc_read(struct file * file, char *buf, size_t size,
+		       loff_t * ppos)
+{
+	gprs_bytes gprsData[TS0710MUX_GPRS_SESSION_MAX];
+	int bufLen = sizeof(gprs_bytes) * TS0710MUX_GPRS_SESSION_MAX;
+
+	UNUSED_PARAM(file);
+	UNUSED_PARAM(size);
+	UNUSED_PARAM(ppos);
+
+	gprsData[0].recvBytes = get_count(TS0710MUX_GPRS1_RECV_COUNT_IDX);
+	gprsData[0].sentBytes = get_count(TS0710MUX_GPRS1_SEND_COUNT_IDX);
+	gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].recvBytes =
+	    get_count(TS0710MUX_GPRS2_RECV_COUNT_IDX);
+	gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].sentBytes =
+	    get_count(TS0710MUX_GPRS2_SEND_COUNT_IDX);
+
+	copy_to_user(buf, gprsData, bufLen);
+
+	return bufLen;
+}
+
+ssize_t file_proc_write(struct file * file, const char *buf, size_t count,
+			loff_t * ppos)
+{
+	gprs_bytes gprsData[TS0710MUX_GPRS_SESSION_MAX];
+	int bufLen = sizeof(gprs_bytes) * TS0710MUX_GPRS_SESSION_MAX;
+
+	UNUSED_PARAM(file);
+	UNUSED_PARAM(count);
+	UNUSED_PARAM(ppos);
+
+	memset(gprsData, 0, bufLen);
+
+	copy_from_user(gprsData, buf, bufLen);
+
+	set_count(TS0710MUX_GPRS1_RECV_COUNT_IDX, gprsData[0].recvBytes);
+	set_count(TS0710MUX_GPRS1_SEND_COUNT_IDX, gprsData[0].sentBytes);
+	set_count(TS0710MUX_GPRS2_RECV_COUNT_IDX,
+		  gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].recvBytes);
+	set_count(TS0710MUX_GPRS2_SEND_COUNT_IDX,
+		  gprsData[TS0710MUX_GPRS_SESSION_MAX - 1].sentBytes);
+
+	return bufLen;
+}
+
+static void gprs_proc_init(void)
+{
+	gprs_proc_file =
+	    create_proc_entry("gprsbytes", S_IRUSR | S_IWUSR, NULL);
+	gprs_proc_file->proc_fops = &file_proc_operations;
+}
+
+static void gprs_proc_exit(void)
+{
+	remove_proc_entry("gprsbytes", gprs_proc_file);
+}
+
+static int __init mux_init(void)
+{
+	__u8 j;
+
+	if (COMM_FOR_MUX_DRIVER == NULL) {
+
+#ifdef USB_FOR_MUX
+		panic("please install IPC-USB first\n");
+#else
+		panic("please install ttyS0 first\n");
+#endif
+
+	}
+
+	ts0710_init();
+
+	for (j = 0; j < NR_MUXS; j++) {
+		mux_send_info_flags[j] = 0;
+		mux_send_info[j] = 0;
+		mux_recv_info_flags[j] = 0;
+		mux_recv_info[j] = 0;
+	}
+	mux_send_info_idx = NR_MUXS;
+	mux_recv_queue = NULL;
+	mux_recv_flags = 0;
+
+	for (j = 0; j < TS0710MUX_COUNT_IDX_NUM; j++) {
+		mux_data_count[j] = 0;
+		mux_data_count2[j] = 0;
+		init_MUTEX(&mux_data_count_mutex[j]);
+	}
+	post_recv_count_flag = 0;
+
+	INIT_WORK(&send_tqueue, send_worker, NULL);
+	INIT_WORK(&receive_tqueue, receive_worker, NULL);
+	INIT_WORK(&post_recv_tqueue, post_recv_worker, NULL);
+
+	memset(&mux_driver, 0, sizeof(struct tty_driver));
+	memset(&mux_tty, 0, sizeof(mux_tty));
+	mux_driver.magic = TTY_DRIVER_MAGIC;
+	mux_driver.driver_name = "ts0710mux";
+	mux_driver.name = "ts0710mux";
+	mux_driver.major = TS0710MUX_MAJOR;
+	mux_driver.minor_start = TS0710MUX_MINOR_START;
+	mux_driver.num = NR_MUXS;
+	mux_driver.type = TTY_DRIVER_TYPE_SERIAL;
+	mux_driver.subtype = SERIAL_TYPE_NORMAL;
+	mux_driver.init_termios = tty_std_termios;
+	mux_driver.init_termios.c_iflag = 0;
+	mux_driver.init_termios.c_oflag = 0;
+	mux_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
+	mux_driver.init_termios.c_lflag = 0;
+	mux_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
+
+	mux_driver.ttys = mux_table;
+	mux_driver.termios = mux_termios;
+	mux_driver.termios_locked = mux_termios_locked;
+//  mux_driver.driver_state = mux_state;
+	mux_driver.other = NULL;
+
+	mux_driver.open = mux_open;
+	mux_driver.close = mux_close;
+	mux_driver.write = mux_write;
+	mux_driver.write_room = mux_write_room;
+	mux_driver.flush_buffer = mux_flush_buffer;
+	mux_driver.chars_in_buffer = mux_chars_in_buffer;
+	mux_driver.throttle = mux_throttle;
+	mux_driver.unthrottle = mux_unthrottle;
+	mux_driver.ioctl = mux_ioctl;
+	mux_driver.owner = THIS_MODULE;
+
+	if (tty_register_driver(&mux_driver))
+		panic("Couldn't register mux driver");
+
+	COMM_MUX_DISPATCHER = mux_dispatcher;
+	COMM_MUX_SENDER = mux_sender;
+
+	gprs_proc_init();
+
+	return 0;
+}
+
+static void __exit mux_exit(void)
+{
+	__u8 j;
+
+	COMM_MUX_DISPATCHER = NULL;
+	COMM_MUX_SENDER = NULL;
+
+	gprs_proc_exit();
+
+	mux_send_info_idx = NR_MUXS;
+	mux_recv_queue = NULL;
+	for (j = 0; j < NR_MUXS; j++) {
+		if ((mux_send_info_flags[j]) && (mux_send_info[j])) {
+			kfree(mux_send_info[j]);
+		}
+		mux_send_info_flags[j] = 0;
+		mux_send_info[j] = 0;
+
+		if ((mux_recv_info_flags[j]) && (mux_recv_info[j])) {
+			free_mux_recv_struct(mux_recv_info[j]);
+		}
+		mux_recv_info_flags[j] = 0;
+		mux_recv_info[j] = 0;
+	}
+
+	if (tty_unregister_driver(&mux_driver))
+		panic("Couldn't unregister mux driver");
+}
+
+module_init(mux_init);
+module_exit(mux_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Harald Welte <laforge@openezx.org>");
+MODULE_DESCRIPTION("TS 07.10 Multiplexer");
Index: linux-2.6.21/drivers/char/ts0710_mux.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/drivers/char/ts0710_mux.h	2007-05-06 17:10:21.000000000 -0300
@@ -0,0 +1,103 @@
+/*
+ * mux_macro.h
+ *
+ * Copyright (C) 2002 2005 Motorola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ *  11/18/2002  (Motorola) - Initial version
+ *
+ */
+
+/*
+* This header file should be included by both MUX and other applications
+* which access MUX device files. It gives the additional macro definitions
+* shared between MUX and applications.
+*/
+
+/* MUX DLCI(Data Link Connection Identifier) Configuration */
+/*
+*  DLCI     Service
+*   0    Control Channel
+*   1    Voice Call & Network-related
+*   2    SMS MO
+*   3    SMS MT
+*   4    Phonebook & related
+*   5    MISC
+*   6    CSD/FAX
+*   7    GPRS1
+*   8    GPRS2
+*   9    Logger CMD
+*   10   Logger Data
+*   11   Test CMD
+*   12   AGPS
+*   13   Net Monitor
+*/
+
+/* Mapping between DLCI and MUX device files */
+/*
+*   File Name   Minor  DLCI  AT Command/Data
+*   /dev/mux0     0     1     AT Command
+*   /dev/mux1     1     2     AT Command
+*   /dev/mux2     2     3     AT Command
+*   /dev/mux3     3     4     AT Command
+*   /dev/mux4     4     5     AT Command
+*   /dev/mux5     5     6     AT Command
+*   /dev/mux6     6     7     AT Command
+*   /dev/mux7     7     8     AT Command
+*   /dev/mux8     8     6     Data
+*   /dev/mux9     9     7     Data
+*   /dev/mux10    10    8     Data
+*   /dev/mux11    11    9     Data
+*   /dev/mux12    12    10    Data
+*   /dev/mux13    13    11    Data
+*   /dev/mux14    14    12    Data
+*   /dev/mux15    15    13    Data
+*/
+
+#define MUX_CMD_FILE_VOICE_CALL   "/dev/mux0"
+#define MUX_CMD_FILE_SMS_MO       "/dev/mux1"
+#define MUX_CMD_FILE_SMS_MT       "/dev/mux2"
+#define MUX_CMD_FILE_PHONEBOOK    "/dev/mux3"
+#define MUX_CMD_FILE_MISC         "/dev/mux4"
+#define MUX_CMD_FILE_CSD          "/dev/mux5"
+#define MUX_CMD_FILE_GPRS1        "/dev/mux6"
+#define MUX_CMD_FILE_GPRS2        "/dev/mux7"
+
+#define MUX_DATA_FILE_CSD         "/dev/mux8"
+#define MUX_DATA_FILE_GPRS1       "/dev/mux9"
+#define MUX_DATA_FILE_GPRS2       "/dev/mux10"
+#define MUX_DATA_FILE_LOGGER_CMD  "/dev/mux11"
+#define MUX_DATA_FILE_LOGGER_DATA "/dev/mux12"
+#define MUX_DATA_FILE_TEST_CMD    "/dev/mux13"
+#define MUX_DATA_FILE_AGPS        "/dev/mux14"
+#define MUX_DATA_FILE_NET_MONITOR "/dev/mux15"
+
+#define NUM_MUX_CMD_FILES 8
+#define NUM_MUX_DATA_FILES 8
+#define NUM_MUX_FILES ( NUM_MUX_CMD_FILES  +  NUM_MUX_DATA_FILES )
+
+/* Special ioctl() upon a MUX device file for hanging up a call */
+#define TS0710MUX_IO_MSC_HANGUP 0x54F0
+
+/* Special ioctl() upon a MUX device file for MUX loopback test */
+#define TS0710MUX_IO_TEST_CMD 0x54F1
+
+/* Special Error code might be return from write() to a MUX device file  */
+#define EDISCONNECTED 900	/* Logical data link is disconnected */
+
+/* Special Error code might be return from open() to a MUX device file  */
+#define EREJECTED 901		/* Logical data link connection request is rejected */
Index: linux-2.6.21/drivers/char/ts0710_mux_usb.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/drivers/char/ts0710_mux_usb.c	2007-05-06 17:10:21.000000000 -0300
@@ -0,0 +1,868 @@
+/*
+ * linux/drivers/usb/ipcusb.c
+ *
+ * Implementation of a ipc driver based Intel's Bulverde USB Host
+ * Controller.
+ *
+ * Copyright (C) 2003-2005 Motorola
+ * Copyright (C) 2006 Harald Welte <laforge@openezx.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  2003-Nov-03 - (Motorola) created
+ *  2004-Feb-20 - (Motorola) Add Power Manager codes
+ *  2004-Apr-14 - (Motorola) Update Suspend/Resume codes
+ *  2004-May-10 - (Motorola) Add unlink_urbs codes and do some updates of send
+ *			     out urb sequence
+ *  2006-Jun-22 - (Harald Welte) port to Linux 2.6.x
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch-pxa/pxa-regs.h>
+#include <asm/arch-pxa/ezx.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/circ_buf.h>
+#include <linux/usb.h>
+
+#include "ts0710_mux_usb.h"
+
+/*Macro defined for this driver*/
+#define DRIVER_VERSION "1.0alpha1"
+#define DRIVER_AUTHOR "Motorola / Harald Welte <laforge@openezx.org>"
+#define DRIVER_DESC "USB IPC Driver (TS07.10 lowlevel)"
+#define MOTO_IPC_VID		0x22b8
+#define MOTO_IPC_PID		0x3006
+#define IBUF_SIZE 		32		/*urb size*/
+#define IPC_USB_XMIT_SIZE	1024
+#define IPC_URB_SIZE		32
+#define IPC_USB_WRITE_INIT 	0
+#define IPC_USB_WRITE_XMIT	1
+#define IPC_USB_PROBE_READY	3
+#define IPC_USB_PROBE_NOT_READY	4
+#define DBG_MAX_BUF_SIZE	1024
+#define ICL_EVENT_INTERVAL	(HZ)
+#undef BVD_DEBUG
+
+#define IS_EP_BULK(ep)  ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
+#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
+/*End defined macro*/
+
+/*global values defined*/
+static struct usb_driver 		usb_ipc_driver;
+static struct timer_list 		ipcusb_timer;
+static struct timer_list 		suspend_timer;
+static struct timer_list 		wakeup_timer;
+static struct tty_struct		ipcusb_tty;		/* the coresponding tty struct, we just use flip buffer here. */
+static struct tty_driver		ipcusb_tty_driver;	/* the coresponding tty driver, we just use write and chars in buff here*/
+struct tty_driver *usb_for_mux_driver = NULL;
+struct tty_struct *usb_for_mux_tty = NULL;
+void (*usb_mux_dispatcher)(struct tty_struct *tty) = NULL;
+void (*usb_mux_sender)(void) = NULL;
+void (*ipcusb_ap_to_bp)(unsigned char*, int) = NULL;
+void (*ipcusb_bp_to_ap)(unsigned char*, int) = NULL;
+EXPORT_SYMBOL(usb_for_mux_driver);
+EXPORT_SYMBOL(usb_for_mux_tty);
+EXPORT_SYMBOL(usb_mux_dispatcher);
+EXPORT_SYMBOL(usb_mux_sender);
+EXPORT_SYMBOL(ipcusb_ap_to_bp);
+EXPORT_SYMBOL(ipcusb_bp_to_ap);
+static int sumbit_times = 0;
+static int callback_times = 0;
+//static unsigned long last_jiff = 0;
+extern int usbh_finished_resume;
+/*end global values defined*/
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#ifdef BVD_DEBUG
+#define bvd_dbg(format, arg...) printk(__FILE__ ": " format "\n" , ## arg)
+#else
+#define bvd_dbg(format, arg...) do {} while (0)
+#endif
+
+/* USB device context */
+typedef struct {
+	struct list_head list;
+	int size;
+	char *body;
+} buf_list_t;
+
+struct ipc_usb_data {
+	u_int8_t 		write_finished_flag;
+	u_int8_t		write_flag,
+				ipc_flag,
+				suspend_flag;
+	struct usb_device 	*ipc_dev;
+	struct urb 		readurb_mux,
+				writeurb_mux,
+				writeurb_dsplog;
+	char 			*obuf, *ibuf;
+	int			writesize;	/* max packet size for the
+						   output bulk endpoint *
+						   transfer buffers */
+
+	struct circ_buf		xmit;		/* write cric bufffer */
+  	struct list_head 	in_buf_list;
+	char 			bulk_in_ep_mux,
+				bulk_out_ep_mux,
+				bulk_in_ep_dsplog;
+	unsigned int 		ifnum;
+
+	struct tasklet_struct	bh,
+				bh_bp;
+
+	spinlock_t		lock;
+};
+
+struct ipc_usb_data *bvd_ipc;
+
+#ifdef BVD_DEBUG
+static void bvd_dbg_hex(__u8 *buf, int len)
+{
+	static unsigned char tbuf[DBG_MAX_BUF_SIZE];
+	int i, c;
+
+	if (len <= 0)
+		return;
+
+	c = 0;
+	for (i=0; (i < len) && (c < (DBG_MAX_BUF_SIZE - 3)); i++) {
+		sprintf(&tbuf[c], "%02x ",buf[i]);
+		c += 3;
+	}
+	tbuf[c] = 0;
+
+	printk("%s: %s\n", __FUNCTION__, tbuf);
+}
+#else
+#define bvd_dbg_hex(buf, len)
+#endif
+
+static int unlink_urbs(struct urb *urb)
+{
+	unsigned long flags;
+	int retval;
+
+	spin_lock_irqsave(&bvd_ipc->lock, flags);
+
+	retval = usb_unlink_urb(urb);
+	if (retval != -EINPROGRESS && retval != 0)
+		printk("unlink urb err, %d", retval);
+
+	spin_unlock_irqrestore(&bvd_ipc->lock, flags);
+	return retval;
+}
+
+static void append_to_inbuf_list(struct urb *urb)
+{
+	buf_list_t *inbuf;
+	int count = urb->actual_length;
+
+	inbuf = kmalloc(sizeof(buf_list_t), GFP_KERNEL);
+	if (!inbuf) {
+		printk("append_to_inbuf_list: (%d) out of memory!\n",
+			sizeof(buf_list_t));
+		return;
+	}
+
+	inbuf->size = count;
+	inbuf->body = kmalloc(sizeof(char)*count, GFP_KERNEL);
+	if (!inbuf->body) {
+		kfree(inbuf);
+		printk("append_to_inbuf_list: (%d) out of memory!\n",
+			sizeof(char)*count);
+		return;
+	}
+	memcpy(inbuf->body, (unsigned char*)urb->transfer_buffer, count);
+	list_add_tail(&inbuf->list, &bvd_ipc->in_buf_list);
+}
+
+static void ipcusb_timeout(unsigned long data)
+{
+	struct tty_struct *tty = &ipcusb_tty;
+	struct urb *urb = (struct urb *)data;
+
+	bvd_dbg("ipcusb_timeout***");
+
+	while (!(list_empty(&bvd_ipc->in_buf_list))) {
+		int count;
+		buf_list_t *inbuf;
+		struct list_head *ptr = NULL;
+
+		ptr = bvd_ipc->in_buf_list.next;
+		inbuf = list_entry (ptr, buf_list_t, list);
+		count = inbuf->size;
+		if (tty_insert_flip_string(tty, inbuf->body, count) >= count) {
+			list_del(ptr);
+			kfree(inbuf->body);
+			inbuf->body = NULL;
+			kfree(inbuf);
+		} else {
+			bvd_dbg("ipcusb_timeout: bvd_ipc->in_buf_list empty!");
+			break;
+		}
+	}
+
+	if (usb_mux_dispatcher)
+		usb_mux_dispatcher(tty);	/**call Liu changhui's func.**/
+
+	if (list_empty(&bvd_ipc->in_buf_list)) {
+		urb->actual_length = 0;
+		urb->dev = bvd_ipc->ipc_dev;
+		if (usb_submit_urb(urb, GFP_ATOMIC))
+			bvd_dbg("ipcusb_timeout: failed resubmitting read urb");
+		bvd_dbg("ipcusb_timeout: resubmited read urb");
+	} else {
+		ipcusb_timer.data = (unsigned long)urb;
+		mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
+	}
+}
+
+static void usb_ipc_read_bulk(struct urb *urb, struct pt_regs *regs)
+{
+	buf_list_t *inbuf;
+	int count = urb->actual_length;
+	struct tty_struct *tty = &ipcusb_tty;
+
+ 	bvd_dbg("usb_ipc_read_bulk: begining!");
+	if (urb->status)
+		printk("nonzero read bulk status received: %d\n", urb->status);
+
+ 	bvd_dbg("usb_ipc_read_bulk: urb->actual_length=%d", urb->actual_length);
+ 	bvd_dbg("usb_ipc_read_bulk: urb->transfer_buffer:");
+
+	bvd_dbg_hex((unsigned char*)urb->transfer_buffer, urb->actual_length);
+
+	if (count > 0 && ((*ipcusb_bp_to_ap) != NULL))
+		(*ipcusb_bp_to_ap)(urb->transfer_buffer, urb->actual_length);
+
+ 	if (!(list_empty(&bvd_ipc->in_buf_list))) {
+		int need_mux = 0;
+
+ 		bvd_dbg("usb_ipc_read_bulk: some urbs in_buf_list");
+		if (count > 0) {
+			bvd_ipc->suspend_flag = 1;
+			append_to_inbuf_list(urb); /* append the current received urb */
+#if 0
+			if(jiffies - last_jiff > ICL_EVENT_INTERVAL)
+			{
+				last_jiff = jiffies;
+				queue_apm_event(KRNL_ICL, NULL);
+			}
+#endif
+		}
+
+		while (!(list_empty(&bvd_ipc->in_buf_list))) {
+			struct list_head* ptr = NULL;
+			ptr = bvd_ipc->in_buf_list.next;
+			inbuf = list_entry(ptr, buf_list_t, list);
+			count = inbuf->size;
+			need_mux = 1;
+
+			tty_insert_flip_string(tty, inbuf->body, count);
+
+			list_del(ptr);
+			kfree(inbuf->body);
+			inbuf->body = NULL;
+			kfree(inbuf);
+		}
+
+		if (usb_mux_dispatcher && need_mux)
+			usb_mux_dispatcher(tty); /* call Liu changhui's func. */
+
+		if (list_empty(&bvd_ipc->in_buf_list)) {
+			urb->actual_length = 0;
+			urb->dev = bvd_ipc->ipc_dev;
+			if (usb_submit_urb(urb, GFP_ATOMIC))
+				bvd_dbg("usb_ipc_read_bulk: "
+					"failed resubmitting read urb");
+			bvd_dbg("usb_ipc_read_bulk: resubmited read urb");
+		} else {
+			ipcusb_timer.data = (unsigned long)urb;
+			mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
+		}
+	} else if (count > 0) {
+ 		bvd_dbg("usb_ipc_read_bulk: no urbs in_buf_list");
+		bvd_ipc->suspend_flag = 1;
+
+		if (tty_insert_flip_string(tty, urb->transfer_buffer,
+					   count) < count) {
+			bvd_ipc->suspend_flag = 1;
+			append_to_inbuf_list(urb);
+			ipcusb_timer.data = (unsigned long)urb;
+			mod_timer(&ipcusb_timer, jiffies+(10*HZ/1000));
+#if 0
+			if(jiffies - last_jiff > ICL_EVENT_INTERVAL)
+			{
+				last_jiff = jiffies;
+				queue_apm_event(KRNL_ICL, NULL);
+			}
+#endif
+		}
+
+		if (usb_mux_dispatcher)
+			usb_mux_dispatcher(tty); /* call Liu changhui's func. */
+
+		urb->actual_length = 0;
+		urb->dev = bvd_ipc->ipc_dev;
+		if (usb_submit_urb(urb, GFP_ATOMIC))
+			bvd_dbg("failed resubmitting read urb");
+#if 0
+		if(jiffies - last_jiff > ICL_EVENT_INTERVAL)
+		{
+			last_jiff = jiffies;
+			queue_apm_event(KRNL_ICL, NULL);
+		}
+#endif
+		bvd_dbg("usb_ipc_read_bulk: resubmited read urb");
+	}
+
+	bvd_dbg("usb_ipc_read_bulk: completed!!!");
+}
+
+static void usb_ipc_write_bulk(struct urb *urb, struct pt_regs *regs)
+{
+	callback_times++;
+	bvd_ipc->write_finished_flag = 1;
+
+	bvd_dbg("usb_ipc_write_bulk: begining!");
+	//printk("%s: write_finished_flag=%d\n", __FUNCTION__, bvd_ipc->write_finished_flag);
+
+	if (urb->status)
+		printk("nonzero write bulk status received: %d\n", urb->status);
+
+	if (usb_mux_sender)
+		usb_mux_sender();		/**call Liu changhui's func**/
+
+	//printk("usb_ipc_write_bulk: mark ipcusb_softint!\n");
+	tasklet_schedule(&bvd_ipc->bh);
+
+	bvd_dbg("usb_ipc_write_bulk: finished!");
+}
+
+static void wakeup_timeout(unsigned long data)
+{
+	GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
+	bvd_dbg("wakup_timeout: send GPIO_MCU_INT_SW signal!");
+}
+
+static void suspend_timeout(unsigned long data)
+{
+	if (bvd_ipc->suspend_flag == 1) {
+		bvd_ipc->suspend_flag = 0;
+		mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
+		bvd_dbg("suspend_timeout: add the suspend timer again");
+	} else {
+		unlink_urbs(&bvd_ipc->readurb_mux);
+		UHCRHPS3 = 0x4;
+		mdelay(40);
+		bvd_dbg("suspend_timeout: send SUSPEND signal! UHCRHPS3=0x%x",
+			UHCRHPS3);
+	}
+}
+
+static void ipcusb_xmit_data(void)
+{
+	int c, count = IPC_URB_SIZE;
+	int result = 0;
+	int buf_flag = 0;
+	int buf_num = 0;
+
+	//printk("%s: sumbit_times=%d, callback_times=%d\n", __FUNCTION__, sumbit_times, callback_times);
+	if (bvd_ipc->write_finished_flag == 0)
+		return;
+
+	while (1) {
+		c = CIRC_CNT_TO_END(bvd_ipc->xmit.head, bvd_ipc->xmit.tail,
+				    IPC_USB_XMIT_SIZE);
+		if (count < c)
+			c = count;
+		if (c <= 0)
+			break;
+
+		memcpy(bvd_ipc->obuf+buf_num,
+		       bvd_ipc->xmit.buf + bvd_ipc->xmit.tail, c);
+		buf_flag = 1;
+		bvd_ipc->xmit.tail = ((bvd_ipc->xmit.tail + c)
+						& (IPC_USB_XMIT_SIZE-1));
+		count -= c;
+		buf_num += c;
+	}
+
+	if (buf_num == 0) {
+		bvd_dbg("ipcusb_xmit_data: buf_num=%d, add suspend_timer",
+			buf_num);
+		bvd_ipc->suspend_flag = 0;
+		mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
+	}
+
+	bvd_dbg("ipcusb_xmit_data: buf_num=%d", buf_num);
+	bvd_dbg("ipcusb_xmit_data: bvd_ipc->obuf: ");
+
+	bvd_dbg_hex((bvd_ipc->obuf)-buf_num, buf_num);
+
+	if (buf_flag) {
+		bvd_ipc->writeurb_mux.transfer_buffer_length = buf_num;
+		bvd_dbg("ipcusb_xmit_data: copy data to write urb finished! ");
+
+		if ((UHCRHPS3 & 0x4) == 0x4) {
+			static int ret;
+			int time = 0;
+
+			/* if BP sleep, wake up BP first */
+			pxa_gpio_mode(GPIO_IN | 41);
+			if (GPIO_is_high(41)) {
+				if (GPIO_is_high(GPIO_MCU_INT_SW))
+					GPCR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
+				else
+					GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
+
+				time = jiffies;
+				while (GPIO_is_high(41) && (jiffies < (time+HZ)));
+
+				if (GPIO_is_high(41)) {
+					printk("%s: Wakeup BP timeout! BP state is %d\n",
+						__FUNCTION__, GPIO_is_high(41));
+				}
+				if (GPIO_is_high(GPIO_MCU_INT_SW))
+					GPCR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
+				else
+					GPSR(GPIO_MCU_INT_SW) = GPIO_bit(GPIO_MCU_INT_SW);
+			}
+
+			/* Resume BP */
+			UHCRHPS3 = 0x8;
+			mdelay(40);
+			bvd_dbg("ipcusb_xmit_data: Send RESUME signal! UHCRHPS3=0x%x",
+				 UHCRHPS3);
+			/*send IN token*/
+			bvd_ipc->readurb_mux.actual_length = 0;
+			bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
+			if (ret = usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC))
+				printk("ipcusb_xmit_data: usb_submit_urb(read mux bulk)"
+					"failed! status=%d\n", ret);
+			bvd_dbg("ipcusb_xmit_data: Send a IN token successfully!");
+		}
+
+		sumbit_times++;
+		bvd_ipc->write_finished_flag = 0;
+		//printk("%s: clear write_finished_flag:%d\n", __FUNCTION__, bvd_ipc->write_finished_flag);
+		bvd_ipc->writeurb_mux.dev = bvd_ipc->ipc_dev;
+		if (result = usb_submit_urb(&bvd_ipc->writeurb_mux, GFP_ATOMIC))
+			warn("ipcusb_xmit_data: funky result! result=%d\n", result);
+
+		bvd_dbg("ipcusb_xmit_data: usb_submit_urb finished! result:%d", result);
+
+	}
+}
+
+static void usbipc_bh_func(unsigned long param)
+{
+	ipcusb_xmit_data();
+}
+
+extern void get_halted_bit(void);
+
+static void usbipc_bh_bp_func(unsigned long param)
+{
+	if ((UHCRHPS3 & 0x4) == 0x4) {
+		UHCRHPS3 = 0x8;
+		mdelay(40);
+		bvd_dbg("ipcusb_softint_send_readurb: Send RESUME signal! "
+			"UHCRHPS3=0x%x", UHCRHPS3);
+	}
+	if (bvd_ipc->ipc_flag == IPC_USB_PROBE_READY) {
+		get_halted_bit();
+
+		/*send a IN token*/
+		bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
+		if (usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC)) {
+			bvd_dbg("ipcusb_softint_send_readurb: "
+				"usb_submit_urb(read mux bulk) failed!");
+		}
+		bvd_dbg("ipcusb_softint_send_readurb: Send a IN token successfully!");
+		bvd_ipc->suspend_flag = 0;
+		bvd_dbg("ipcusb_softint_send_readurb: add suspend_timer");
+		mod_timer(&suspend_timer, jiffies+(5000*HZ/1000));
+	}
+}
+
+static int usb_ipc_write(struct tty_struct *tty,
+			 const unsigned char *buf, int count)
+{
+	int c, ret = 0;
+
+	bvd_dbg("usb_ipc_write: count=%d, buf: ", count);
+	bvd_dbg_hex(buf, count);
+
+	if (count <= 0)
+		return 0;
+
+	if (*ipcusb_ap_to_bp != NULL)
+		(*ipcusb_ap_to_bp)(buf, count);
+
+	bvd_ipc->suspend_flag = 1;
+
+	if ((bvd_ipc->ipc_flag == IPC_USB_PROBE_READY) &&
+	    (bvd_ipc->xmit.head == bvd_ipc->xmit.tail)) {
+		bvd_dbg("usb_ipc_write: set write_flag");
+		bvd_ipc->write_flag = IPC_USB_WRITE_XMIT;
+	}
+
+	while (1) {
+		c = CIRC_SPACE_TO_END(bvd_ipc->xmit.head,
+				      bvd_ipc->xmit.tail, IPC_USB_XMIT_SIZE);
+		if (count < c)
+			c = count;
+		if (c <= 0)
+			break;
+
+		memcpy(bvd_ipc->xmit.buf + bvd_ipc->xmit.head, buf, c);
+		bvd_ipc->xmit.head = ((bvd_ipc->xmit.head + c)
+						& (IPC_USB_XMIT_SIZE-1));
+		buf += c;
+		count -= c;
+		ret += c;
+	}
+	bvd_dbg("usb_ipc_write: ret=%d, bvd_ipc->xmit.buf: ", ret);
+
+	bvd_dbg_hex(bvd_ipc->xmit.buf, ret);
+
+	if (bvd_ipc->write_flag == IPC_USB_WRITE_XMIT) {
+		bvd_ipc->write_flag = IPC_USB_WRITE_INIT;
+		bvd_dbg("usb_ipc_write: mark ipcusb_softint");
+		tasklet_schedule(&bvd_ipc->bh);
+	}
+
+	bvd_dbg("usb_ipc_write: ret=%d\n", ret);
+	return ret;
+}
+
+static int usb_ipc_chars_in_buffer(struct tty_struct *tty)
+{
+	return CIRC_CNT(bvd_ipc->xmit.head, bvd_ipc->xmit.tail, IPC_USB_XMIT_SIZE);
+}
+
+void usb_send_readurb(void)
+{
+	//printk("usb_send_readurb: begining!UHCRHPS3=0x%x, usbh_finished_resume=%d\n", UHCRHPS3, usbh_finished_resume);
+
+	if (usbh_finished_resume == 0)
+		return;
+
+	tasklet_schedule(&bvd_ipc->bh_bp);
+}
+
+static int usb_ipc_probe(struct usb_interface *intf,
+			 const struct usb_device_id *id)
+{
+	struct usb_device *usbdev = interface_to_usbdev(intf);
+	struct usb_config_descriptor *ipccfg;
+	struct usb_interface_descriptor *interface;
+	struct usb_endpoint_descriptor *endpoint;
+	int ep_cnt, readsize, writesize;
+	char have_bulk_in_mux, have_bulk_out_mux;
+
+	bvd_dbg("usb_ipc_probe: vendor id 0x%x, device id 0x%x",
+		usbdev->descriptor.idVendor, usbdev->descriptor.idProduct);
+
+	if ((usbdev->descriptor.idVendor != MOTO_IPC_VID) ||
+	    (usbdev->descriptor.idProduct != MOTO_IPC_PID))
+ 		return -ENODEV;
+
+	/* a2590c : dsplog interface is not supported by this driver */
+	if (intf->minor == 2)	/* dsplog interface number is 2 */
+		return -1;
+
+	bvd_dbg("usb_ipc_probe: USB dev address:%p", usbdev);
+	bvd_dbg("usb_ipc_probe: ifnum:%u", intf->minor);
+
+	ipccfg = &usbdev->actconfig->desc;
+	bvd_dbg("usb_ipc_prob: config%d", ipccfg->bConfigurationValue);
+	bvd_dbg("usb_ipc_prob: bNumInterfaces = %d", ipccfg->bNumInterfaces);
+
+	/* After this point we can be a little noisy about what we are trying
+	 * to configure, hehe.  */
+	if (usbdev->descriptor.bNumConfigurations != 1) {
+		info("usb_ipc_probe: Only one device configuration "
+		     "is supported.");
+		return -1;
+	}
+
+	if (usbdev->config[0].desc.bNumInterfaces != 3) {
+		info("usb_ipc_probe: Only three device interfaces are "
+		     "supported.");
+		return -1;
+	}
+
+	interface = &intf->cur_altsetting->desc;
+	endpoint = &intf->cur_altsetting->endpoint[0].desc;
+	/* Start checking for two bulk endpoints or ... FIXME: This is a future
+	 * enhancement...*/
+	bvd_dbg("usb_ipc_probe: Number of Endpoints:%d",
+		(int) interface->bNumEndpoints);
+	if (interface->bNumEndpoints != 2) {
+		info("usb_ipc_probe: Only two endpoints supported.");
+		return -1;
+	}
+
+	ep_cnt = have_bulk_in_mux = have_bulk_out_mux = 0;
+
+	bvd_dbg("usb_ipc_probe: endpoint[0] is:%x",
+		(&endpoint[0])->bEndpointAddress);
+	bvd_dbg("usb_ipc_probe: endpoint[1] is:%x ",
+		(&endpoint[1])->bEndpointAddress);
+
+	while (ep_cnt < interface->bNumEndpoints) {
+
+		if (!have_bulk_in_mux && IS_EP_BULK_IN(endpoint[ep_cnt])) {
+			bvd_dbg("usb_ipc_probe: bEndpointAddress(IN) is:%x ",
+				(&endpoint[ep_cnt])->bEndpointAddress);
+			have_bulk_in_mux =
+					(&endpoint[ep_cnt])->bEndpointAddress;
+			readsize = (&endpoint[ep_cnt])->wMaxPacketSize;
+			bvd_dbg("usb_ipc_probe: readsize=%d", readsize);
+			ep_cnt++;
+			continue;
+		}
+
+		if (!have_bulk_out_mux && IS_EP_BULK_OUT(endpoint[ep_cnt])) {
+			bvd_dbg("usb_ipc_probe: bEndpointAddress(OUT) is:%x ",
+				(&endpoint[ep_cnt])->bEndpointAddress);
+			have_bulk_out_mux =
+				(&endpoint[ep_cnt])->bEndpointAddress;
+			writesize = (&endpoint[ep_cnt])->wMaxPacketSize;
+			bvd_dbg("usb_ipc_probe: writesize=%d", writesize);
+			ep_cnt++;
+			continue;
+		}
+
+		info("usb_ipc_probe: Undetected endpoint ^_^ ");
+		/* Shouldn't ever get here unless we have something weird */
+		return -1;
+	}
+
+	/* Perform a quick check to make sure that everything worked as it
+	 * should have.  */
+
+	switch (interface->bNumEndpoints) {
+	case 2:
+		if (!have_bulk_in_mux || !have_bulk_out_mux) {
+			info("usb_ipc_probe: Two bulk endpoints required.");
+			return -1;
+		}
+		break;
+	default:
+		info("usb_ipc_probe: Endpoint determination failed ^_^ ");
+		return -1;
+	}
+
+	/* Ok, now initialize all the relevant values */
+	if (!(bvd_ipc->obuf = (char *)kmalloc(writesize, GFP_KERNEL))) {
+		err("usb_ipc_probe: Not enough memory for the output buffer.");
+		kfree(bvd_ipc);
+		return -1;
+	}
+	bvd_dbg("usb_ipc_probe: obuf address:%p", bvd_ipc->obuf);
+
+	if (!(bvd_ipc->ibuf = (char *)kmalloc(readsize, GFP_KERNEL))) {
+		err("usb_ipc_probe: Not enough memory for the input buffer.");
+		kfree(bvd_ipc->obuf);
+		kfree(bvd_ipc);
+		return -1;
+	}
+	bvd_dbg("usb_ipc_probe: ibuf address:%p", bvd_ipc->ibuf);
+
+	bvd_ipc->ipc_flag = IPC_USB_PROBE_READY;
+	bvd_ipc->write_finished_flag = 1;
+	bvd_ipc->suspend_flag = 1;
+	bvd_ipc->bulk_in_ep_mux= have_bulk_in_mux;
+	bvd_ipc->bulk_out_ep_mux= have_bulk_out_mux;
+	bvd_ipc->ipc_dev = usbdev;
+	bvd_ipc->writesize = writesize;
+	INIT_LIST_HEAD (&bvd_ipc->in_buf_list);
+
+	bvd_ipc->bh.func = usbipc_bh_func;
+	bvd_ipc->bh.data = (unsigned long) bvd_ipc;
+
+	bvd_ipc->bh_bp.func = usbipc_bh_bp_func;
+	bvd_ipc->bh_bp.data = (unsigned long) bvd_ipc;
+
+	/*Build a write urb*/
+	usb_fill_bulk_urb(&bvd_ipc->writeurb_mux, usbdev,
+			  usb_sndbulkpipe(bvd_ipc->ipc_dev,
+			  		  bvd_ipc->bulk_out_ep_mux),
+			  bvd_ipc->obuf, writesize, usb_ipc_write_bulk,
+			  bvd_ipc);
+	//bvd_ipc->writeurb_mux.transfer_flags |= USB_ASYNC_UNLINK;
+
+	/*Build a read urb and send a IN token first time*/
+	usb_fill_bulk_urb(&bvd_ipc->readurb_mux, usbdev,
+			  usb_rcvbulkpipe(usbdev, bvd_ipc->bulk_in_ep_mux),
+			  bvd_ipc->ibuf, readsize, usb_ipc_read_bulk, bvd_ipc);
+	//bvd_ipc->readurb_mux.transfer_flags |= USB_ASYNC_UNLINK;
+
+	usb_driver_claim_interface(&usb_ipc_driver, intf, bvd_ipc);
+	//usb_driver_claim_interface(&usb_ipc_driver, &ipccfg->interface[1], bvd_ipc);
+
+        // a2590c: dsplog is not supported by this driver
+	//	usb_driver_claim_interface(&usb_ipc_driver,
+	//				   &ipccfg->interface[2], bvd_ipc);
+	/*send a IN token first time*/
+	bvd_ipc->readurb_mux.dev = bvd_ipc->ipc_dev;
+	if (usb_submit_urb(&bvd_ipc->readurb_mux, GFP_ATOMIC))
+		printk("usb_ipc_prob: usb_submit_urb(read mux bulk) failed!\n");
+
+	bvd_dbg("usb_ipc_prob: Send a IN token successfully!");
+
+	if (bvd_ipc->xmit.head != bvd_ipc->xmit.tail) {
+		printk("usb_ipc_probe: mark ipcusb_softint!\n");
+		tasklet_schedule(&bvd_ipc->bh);
+	}
+
+	printk("usb_ipc_probe: completed probe!");
+	usb_set_intfdata(intf, &bvd_ipc);
+	return 0;
+}
+
+static void usb_ipc_disconnect(struct usb_interface *intf)
+{
+	//struct usb_device *usbdev = interface_to_usbdev(intf);
+	struct ipc_usb_data *bvd_ipc_disconnect = usb_get_intfdata(intf);
+
+	printk("usb_ipc_disconnect:*** \n");
+
+	if ((UHCRHPS3 & 0x4) == 0)
+		usb_unlink_urb(&bvd_ipc_disconnect->readurb_mux);
+
+	usb_unlink_urb(&bvd_ipc_disconnect->writeurb_mux);
+
+	bvd_ipc_disconnect->ipc_flag = IPC_USB_PROBE_NOT_READY;
+	kfree(bvd_ipc_disconnect->ibuf);
+	kfree(bvd_ipc_disconnect->obuf);
+
+	usb_driver_release_interface(&usb_ipc_driver,
+			bvd_ipc_disconnect->ipc_dev->actconfig->interface[0]);
+        usb_driver_release_interface(&usb_ipc_driver,
+			bvd_ipc_disconnect->ipc_dev->actconfig->interface[1]);
+
+	//a2590c: dsplog interface is not supported by this driver
+	//usb_driver_release_interface(&usb_ipc_driver, &bvd_ipc_disconnect->ipc_dev->actconfig->interface[2]);
+
+	bvd_ipc_disconnect->ipc_dev = NULL;
+
+	usb_set_intfdata(intf, NULL);
+
+	printk("usb_ipc_disconnect completed!\n");
+}
+
+static struct usb_device_id usb_ipc_id_table[] = {
+	{ USB_DEVICE(MOTO_IPC_VID, MOTO_IPC_PID) },
+	{ }						/* Terminating entry */
+};
+
+static struct usb_driver usb_ipc_driver = {
+	.name		= "usb ipc",
+	.probe		= usb_ipc_probe,
+	.disconnect	= usb_ipc_disconnect,
+	.id_table	= usb_ipc_id_table,
+};
+
+static int __init usb_ipc_init(void)
+{
+	int result;
+
+	bvd_dbg("init usb_ipc");
+	/* register driver at the USB subsystem */
+	result = usb_register(&usb_ipc_driver);
+	if (result < 0) {
+		err ("usb ipc driver could not be registered");
+		return result;
+	}
+
+	/*init the related mux interface*/
+	if (!(bvd_ipc = kzalloc(sizeof(struct ipc_usb_data), GFP_KERNEL))) {
+		err("usb_ipc_init: Out of memory.");
+		usb_deregister(&usb_ipc_driver);
+		return -ENOMEM;
+	}
+	bvd_dbg("usb_ipc_init: Address of bvd_ipc:%p", bvd_ipc);
+
+	if (!(bvd_ipc->xmit.buf = kmalloc(IPC_USB_XMIT_SIZE, GFP_KERNEL))) {
+		err("usb_ipc_init: Not enough memory for the input buffer.");
+		kfree(bvd_ipc);
+		usb_deregister(&usb_ipc_driver);
+		return -ENOMEM;
+	}
+	bvd_dbg("usb_ipc_init: bvd_ipc->xmit.buf address:%p",
+		bvd_ipc->xmit.buf);
+	bvd_ipc->ipc_dev = NULL;
+	bvd_ipc->xmit.head = bvd_ipc->xmit.tail = 0;
+	bvd_ipc->write_flag = IPC_USB_WRITE_INIT;
+
+	ipcusb_tty_driver.write = usb_ipc_write;
+	ipcusb_tty_driver.chars_in_buffer = usb_ipc_chars_in_buffer;
+
+	usb_for_mux_driver = &ipcusb_tty_driver;
+	usb_for_mux_tty = &ipcusb_tty;
+
+	/* init timers for ipcusb read process and usb suspend */
+	init_timer(&ipcusb_timer);
+	ipcusb_timer.function = ipcusb_timeout;
+
+	init_timer(&suspend_timer);
+	suspend_timer.function = suspend_timeout;
+
+	init_timer(&wakeup_timer);
+	wakeup_timer.function = wakeup_timeout;
+
+	info("USB Host(Bulverde) IPC driver registered.");
+	info(DRIVER_VERSION ":" DRIVER_DESC);
+
+	return 0;
+}
+
+static void __exit usb_ipc_exit(void)
+{
+	bvd_dbg("cleanup bvd_ipc");
+
+	kfree(bvd_ipc->xmit.buf);
+	kfree(bvd_ipc);
+	usb_deregister(&usb_ipc_driver);
+
+	info("USB Host(Bulverde) IPC driver deregistered.");
+}
+
+module_init(usb_ipc_init);
+module_exit(usb_ipc_exit);
+EXPORT_SYMBOL(usb_send_readurb);
Index: linux-2.6.21/drivers/char/ts0710_mux_usb.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.21/drivers/char/ts0710_mux_usb.h	2007-05-06 17:10:21.000000000 -0300
@@ -0,0 +1,29 @@
+/*
+ * linux/drivers/usb/ipcusb.h
+ *
+ * Implementation of a ipc driver based Intel's Bulverde USB Host
+ * Controller.
+ *
+ * Copyright (C) 2003-2005 Motorola
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  2003-Nov-18 - (Motorola) created
+ *
+ */
+extern struct tty_driver *usb_for_mux_driver;
+extern struct tty_struct *usb_for_mux_tty;
+extern void (*usb_mux_dispatcher)(struct tty_struct *tty);
+extern void (*usb_mux_sender)(void);