Index: linux-2.6.21/drivers/char/Kconfig =================================================================== --- linux-2.6.21.orig/drivers/char/Kconfig 2007-04-26 20:07:19.000000000 +0200 +++ linux-2.6.21/drivers/char/Kconfig 2007-04-26 20:09:29.000000000 +0200 @@ -1071,5 +1071,17 @@ /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" + 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-04-26 20:07:19.000000000 +0200 +++ linux-2.6.21/drivers/char/Makefile 2007-04-26 20:09:29.000000000 +0200 @@ -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-04-26 20:09:29.000000000 +0200 @@ -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 + * + * 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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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-04-26 20:09:29.000000000 +0200 @@ -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 + * + * 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 + * + * 07/28/2002 Initial version + * 11/18/2002 Second version + * 04/21/2004 Add GPRS PROC + */ +#include +#include +#include + +#include +#include + +#define USB_FOR_MUX + +#ifndef USB_FOR_MUX +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +#include +#include +#include + +#ifdef USB_FOR_MUX +//#include +#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_ST