Index: linux-2.6.24/drivers/char/Kconfig =================================================================== --- linux-2.6.24.orig/drivers/char/Kconfig +++ linux-2.6.24/drivers/char/Kconfig @@ -1040,5 +1040,18 @@ source "drivers/s390/char/Kconfig" +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.24/drivers/char/Makefile =================================================================== --- linux-2.6.24.orig/drivers/char/Makefile +++ linux-2.6.24/drivers/char/Makefile @@ -111,6 +111,9 @@ obj-$(CONFIG_JS_RTC) += js-rtc.o js-rtc-y = rtc.o +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.24/drivers/char/ts0710.h =================================================================== --- /dev/null +++ linux-2.6.24/drivers/char/ts0710.h @@ -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.24/drivers/char/ts0710_mux.c =================================================================== --- /dev/null +++ linux-2.6.24/drivers/char/ts0710_mux.c @@ -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_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 "); +MODULE_DESCRIPTION("TS 07.10 Multiplexer"); Index: linux-2.6.24/drivers/char/ts0710_mux.h =================================================================== --- /dev/null +++ linux-2.6.24/drivers/char/ts0710_mux.h @@ -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.24/drivers/char/ts0710_mux_usb.c =================================================================== --- /dev/null +++ linux-2.6.24/drivers/char/ts0710_mux_usb.c @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ts0710_mux_usb.h" + +/*Macro defined for this driver*/ +#define DRIVER_VERSION "1.0alpha1" +#define DRIVER_AUTHOR "Motorola / Harald Welte " +#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.24/drivers/char/ts0710_mux_usb.h =================================================================== --- /dev/null +++ linux-2.6.24/drivers/char/ts0710_mux_usb.h @@ -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);