diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/char/ts0710_mux.c | |
Diffstat (limited to 'drivers/char/ts0710_mux.c')
| -rw-r--r-- | drivers/char/ts0710_mux.c | 4477 |
1 files changed, 4477 insertions, 0 deletions
diff --git a/drivers/char/ts0710_mux.c b/drivers/char/ts0710_mux.c new file mode 100644 index 00000000..9b2a9653 --- /dev/null +++ b/drivers/char/ts0710_mux.c @@ -0,0 +1,4477 @@ +/* File: mux_driver.c + * + * Portions derived from rfcomm.c, original header as follows: + * + * Copyright (C) 2000, 2001 Axis Communications AB + * + * Author: Mats Friden <mats.friden@axis.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Exceptionally, Axis Communications AB grants discretionary and + * conditional permissions for additional use of the text contained + * in the company's release of the AXIS OpenBT Stack under the + * provisions set forth hereunder. + * + * Provided that, if you use the AXIS OpenBT Stack with other files, + * that do not implement functionality as specified in the Bluetooth + * System specification, to produce an executable, this does not by + * itself cause the resulting executable to be covered by the GNU + * General Public License. Your use of that executable is in no way + * restricted on account of using the AXIS OpenBT Stack code with it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the provisions of the GNU + * General Public License. + * + */ + +/* + * Copyright (C) 2002-2004 Motorola + * Copyright (C) 2006 Harald Welte <laforge@openezx.org> + * 07/28/2002 Initial version + * 11/18/2002 Second version + * 04/21/2004 Add GPRS PROC + * 09/28/2008 Porting to kernel 2.6.21 by Spreadtrum + * 11/08/2013 Add Multi-mux support +*/ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/fcntl.h> +#include <linux/string.h> +#include <linux/major.h> +#include <linux/init.h> +#include <linux/proc_fs.h> +#include <linux/delay.h> +#include <linux/kthread.h> +#include <linux/completion.h> +#include <linux/sprdmux.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/sched/rt.h> +#include <linux/version.h> +#include <linux/seq_file.h> +#include <linux/vmalloc.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/timer.h> +#include "ts0710.h" +#include "ts0710_mux.h" + + +#define TS0710MUX_GPRS_SESSION_MAX 3 +#define TS0710MUX_MAJOR 250 +#define TS0710MUX_MINOR_START 0 +#define NR_MUXS 32 + +#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 (64*1024) +#define TS0710MUX_MAX_TOTAL_SIZE (1024*1024) + +#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 0 /* 6 * 11(ACK frame size) */ +#else +#define ACK_SPACE 0 /* 6 * 7(ACK frame size) */ +#endif +/*For BP UART problem End*/ + +#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 0 /*offset from start flag */ +#define SECOND_BP_SEQ_OFFSET 0 /*offset from start flag */ +#define FIRST_AP_SEQ_OFFSET 0 /*offset from start flag */ +#define SECOND_AP_SEQ_OFFSET 0 /*offset from start flag */ +#define SLIDE_BP_SEQ_OFFSET 0 /*offset from start flag */ +#define SEQ_FIELD_SIZE 0 +#else +#define SLIDE_BP_SEQ_OFFSET 1 /*offset from start flag */ +#define SEQ_FIELD_SIZE 0 +#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 3 +#define TS0710MUX_GPRS2_DLCI 4 +#define TS0710MUX_VT_DLCI 2 + +#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_VT_RECV_COUNT_IDX 4 +#define TS0710MUX_VT_SEND_COUNT_IDX 5 + +#define TS0710MUX_COUNT_MAX_IDX 5 +#define TS0710MUX_COUNT_IDX_NUM (TS0710MUX_COUNT_MAX_IDX + 1) + +#define SPRDMUX_MAX_NUM SPRDMUX_ID_MAX + +/* Bit number in flags of mux_send_struct */ +#define RECV_RUNNING 0 +#define MUX_MODE_MAX_LEN 11 + +#define MUX_STATE_NOT_READY 0 +#define MUX_STATE_READY 1 +#define MUX_STATE_CRASHED 3 +#define MUX_STATE_RECOVERING 4 + +#define MUX_RIL_LINE_END 11 + +#define MUX_VETH_LINE_BEGIN 13 +#define MUX_VETH_LINE_END 17 +#define MUX_VETH_RINGBUFER_NUM 24 + +#define MUX_WATCH_INTERVAL 3 * HZ + +/* Debug */ +//#define TS0710DEBUG +//#define TS0710LOG + +static __u8 line2dlci[NR_MUXS] = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ,14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}; +typedef struct { + __u8 cmdline; + __u8 dataline; +} dlci_line; + +static dlci_line dlci2line[] = { + {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, 5}, /* DLCI 6 */ + {6, 6}, /* DLCI 7 */ + {7, 7}, /* DLCI 8 */ + {8, 8}, /* DLCI 9 */ + {9, 9}, /* DLCI 10 */ + {10, 10}, /* DLCI 11 */ + {11, 11}, /* DLCI 12 */ + {12, 12}, /* DLCI 13 */ + {13, 13}, /* DLCI 14 */ + {14, 14}, /* DLCI 15 */ + {15, 15}, /* DLCI 16 */ + {16, 16}, /* DLCI 17 */ + {17, 17}, /* DLCI 18 */ + {18, 18}, /* DLCI 19 */ + {19, 19}, /* DLCI 20 */ + {20, 20}, /* DLCI 21 */ + {21, 21}, /* DLCI 22 */ + {22, 22}, /* DLCI 23 */ + {23, 23}, /* DLCI 24 */ + {24, 24}, /* DLCI 25 */ + {25, 25}, /* DLCI 26 */ + {26, 26}, /* DLCI 27 */ + {27, 27}, /* DLCI 28 */ + {28, 28}, /* DLCI 29 */ + {29, 29}, /* DLCI 30 */ + {30, 30}, /* DLCI 31 */ + {31, 31}, /* DLCI 32 */ +}; + +/* set line ring buffer num*/ +static __u8 ringbuf_num[SPRDMUX_ID_MAX][NR_MUXS]; + +typedef struct { + __u8 *buf; + __u8 **frame; + unsigned long flags; + __u16 *length; + __u8 need_notify; + + volatile __u8 dummy; /* Allignment to 4*n bytes */ + struct mutex send_lock; +// struct mutex send_data_lock; /*Todo*/ + __u32 read_index; + __u32 write_index; + + wait_queue_head_t tx_wait; +} mux_send_struct; + +struct mux_recv_packet_tag { + __u8 *data; + __u32 length; + __u32 pos; + 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; + __u32 pos; + mux_recv_packet *mux_packet; + struct mutex recv_lock; + struct mutex recv_data_lock; + wait_queue_head_t rx_wait; +}; +typedef struct mux_recv_struct_tag mux_recv_struct; + +struct notify { + notify_func func; + void *user_data; +}; + +struct sprd_mux { + SPRDMUX_ID_E mux_id; + int mux_exiting; + mux_send_struct *mux_send_info[NR_MUXS]; + volatile __u8 mux_send_info_flags[NR_MUXS]; + mux_recv_struct *mux_recv_info[NR_MUXS]; + volatile __u8 mux_recv_info_flags[NR_MUXS]; + struct completion send_completion; + + struct task_struct *mux_recv_kthread; + struct task_struct *mux_send_kthread; + + struct sprdmux *io_hal; + ts0710_con *connection; + volatile int cmux_mode; + + struct notify callback[NR_MUXS]; + struct mutex handshake_mutex; + struct mutex open_mutex[NR_MUXS]; + volatile short int open_count[NR_MUXS]; + + __u32 check_read_sum[NR_MUXS]; + __u32 check_write_sum[NR_MUXS]; + + wait_queue_head_t handshake_ready; + wait_queue_head_t modem_ready; + struct task_struct *mux_recover_kthread; + int mux_status; + +/* receive_worker */ + unsigned char tbuf[TS0710MUX_MAX_BUF_SIZE]; + unsigned char *tbuf_ptr; + unsigned char *start_flag; + int framelen; + /*For BP UART problem Begin */ + __u8 expect_seq; + /*For BP UART problem End */ + struct timer_list watch_timer; + +#ifdef TS0710DEBUG + unsigned char debug_hex_buf[TS0710MUX_MAX_BUF_SIZE]; + unsigned char debug_str_buf[TS0710MUX_MAX_BUF_SIZE]; +#endif + +#ifdef TS0710LOG + unsigned char debug_frame_buf[TS0710MUX_MAX_BUF_SIZE]; +#endif + +} ; + +typedef struct mux_info_{ + SPRDMUX_ID_E mux_id; + char mux_name[SPRDMUX_MAX_NAME_LEN]; + struct sprd_mux *handle; + struct sprdmux mux; + +} mux_info; + +static mux_info sprd_mux_mgr[SPRDMUX_MAX_NUM] = +{ + [0] = { + .mux_id = SPRDMUX_ID_SPI, + .mux_name = "spimux", + .handle = NULL, + }, + [1] = { + .mux_id = SPRDMUX_ID_SDIO, + .mux_name = "sdiomux", + .handle = NULL, + }, +}; + +static void mux_print_mux_mgr(void); +static void mux_mgr_init(mux_info *mux_mgr); +static int mux_receive_thread(void *data); +static void mux_display_recv_info(const char * tag, int mux_id, int line); +static void receive_worker(struct sprd_mux *self, int start); +static int mux_mode = 0; + +#ifdef min +#undef min +#define min(a,b) ( (a)<(b) ? (a):(b) ) +#endif + +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, __u32 ring_index, __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 void mux_sched_send(struct sprd_mux *self); +static int ts0710_ctrl_channel_status(ts0710_con * ts0710); +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 __u8 crctable[256]; + +/* mux recover */ +static int mux_handshake(struct sprd_mux *self); +static void mux_stop(struct sprd_mux *self); +static int mux_restore_channel(ts0710_con * ts0710); +static void mux_wakeup_all_read(struct sprd_mux *self); +static void mux_wakeup_all_write(struct sprd_mux *self); +static void mux_wakeup_all_opening(struct sprd_mux *self); +static void mux_wakeup_all_closing(struct sprd_mux *self); +static void mux_wakeup_all_wait(struct sprd_mux *self); + +static void mux_tidy_buff(struct sprd_mux *self); +static void mux_recover(struct sprd_mux *self); +static int mux_recover_thread(void *data); +static int mux_restore_channel(ts0710_con * ts0710); +static void mux_display_connection(ts0710_con * ts0710, const char *tag); + +static void mux_set_ringbuf_num(void); +static int mux_line_state(SPRDMUX_ID_E mux_id, int line); +static mux_send_struct * mux_alloc_send_info(SPRDMUX_ID_E mux_id, int line); +int sprdmux_line_busy(SPRDMUX_ID_E mux_id, int line); +void sprdmux_set_line_notify(SPRDMUX_ID_E mux_id, int line, __u8 notify); +static void mux_free_send_info(mux_send_struct * send_info); +static void display_send_info(char * tag, SPRDMUX_ID_E mux_id, int line); + +static void mux_init_timer(struct sprd_mux *self); +static void mux_start_timer(struct sprd_mux *self); +static void mux_wathch_check(unsigned long priv); +static void mux_stop_timer(struct sprd_mux *self); + +#ifdef TS0710DEBUG + +#define TS0710_DEBUG(fmt, arg...) printk(KERN_INFO "MUX:%s "fmt, __FUNCTION__ , ## arg) +#define MUX_TS0710_DEBUG(mux_id, fmt, arg...) \ +do \ +{if (mux_id >= 0 && mux_id < SPRDMUX_ID_MAX)\ +printk(KERN_INFO "[%s]: %s "fmt,sprd_mux_mgr[mux_id].mux_name, __FUNCTION__ , ## arg); \ +} while (0) +#else +#define TS0710_DEBUG(fmt...) +#define MUX_TS0710_DEBUG(mux_id, fmt, arg...) +#endif /* End #ifdef TS0710DEBUG */ + +#ifdef TS0710LOG +#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg) +#else +#define TS0710_PRINTK(fmt, arg...) printk(fmt, ## arg) +#endif /* End #ifdef TS0710LOG */ + +#ifdef TS0710DEBUG +static void MUX_TS0710_DEBUGHEX(SPRDMUX_ID_E mux_id, __u8 * buf, int len) +{ + unsigned char *tbuf = NULL; + int i = 0; + int c = 0; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX || !buf || len <= 0) { + printk(KERN_ERR "MUX: Error %s Invalid Param\n", __FUNCTION__); + return; + } + + if (!sprd_mux_mgr[mux_id].handle) { + printk(KERN_ERR "MUX[%d]: Error %s mux_id Handle is 0\n", mux_id, __FUNCTION__); + mux_print_mux_mgr(); + return; + } + + tbuf = sprd_mux_mgr[mux_id].handle->debug_hex_buf; + + if (!tbuf) { + printk(KERN_ERR "MUX[%d]: Error %s mux_id tbuf is NULL\n", mux_id, __FUNCTION__); + return; + } + + for (i = 0; (i < len) && (c < (TS0710MUX_MAX_BUF_SIZE - 3)); i++) { + sprintf(&tbuf[c], "%02x ", buf[i]); + c += 3; + } + tbuf[c] = 0; + + MUX_TS0710_DEBUG(mux_id, "%s", tbuf); +} + +static void MUX_TS0710_DEBUGSTR(SPRDMUX_ID_E mux_id, __u8 * buf, int len) +{ + unsigned char *tbuf = NULL; + struct sprd_mux *self = NULL; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX || !buf || len <= 0) { + printk(KERN_ERR "MUX: Error %s Invalid Param\n", __FUNCTION__); + return; + } + + self = sprd_mux_mgr[mux_id].handle; + if (!self) { + printk(KERN_ERR "MUX[%d]: Error %s self is NULL\n", mux_id, __FUNCTION__); + mux_print_mux_mgr(); + return; + } + + tbuf = self->debug_str_buf; + + if (!tbuf) { + printk(KERN_ERR "MUX[%d]: Error %s mux_id tbuf is NULL\n", mux_id, __FUNCTION__); + return; + } + + if (len > (TS0710MUX_MAX_BUF_SIZE - 1)) { + len = (TS0710MUX_MAX_BUF_SIZE - 1); + } + + memcpy(tbuf, buf, len); + tbuf[len] = 0; + + /* 0x00 byte in the string pointed by tbuf may truncate the print result */ + MUX_TS0710_DEBUG(mux_id, "%s", tbuf); +} +#else +#define MUX_TS0710_DEBUGHEX(mux_id, buf, len) +#define MUX_TS0710_DEBUGSTR(mux_id, buf, len) +#endif /* End #ifdef TS0710DEBUG */ + +#ifdef TS0710LOG +static void MUX_TS0710_LOGSTR_FRAME(SPRDMUX_ID_E mux_id, __u8 send, __u8 * data, int len) +{ + unsigned char *tbuf = NULL; + short_frame *short_pkt; + long_frame *long_pkt; + __u8 *uih_data_start; + __u32 uih_len; + __u8 dlci; + int pos; + struct sprd_mux *self = NULL; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX || !data || len <= 0) { + printk(KERN_ERR "MUX: Error %s Invalid Param\n", __FUNCTION__); + return; + } + + self = sprd_mux_mgr[mux_id].handle; + if (!self) { + printk(KERN_ERR "MUX[%d]: Error %s self is NULL\n", mux_id, __FUNCTION__); + return; + } + + tbuf = self->debug_frame_buf; + + if (!tbuf) { + printk(KERN_ERR "MUX[%d]: Error %s tbuf is NULL\n", mux_id, __FUNCTION__); + return; + } + + if (len <= 0) { + return; + } + + pos = 0; + if (send) { + pos += sprintf(&tbuf[pos], "<"); + short_pkt = (short_frame *) (data + 1); + } else { + pos += + sprintf(&tbuf[pos], ">%d ", + *(data + SLIDE_BP_SEQ_OFFSET)); + +#ifdef TS0710SEQ2 + pos += + sprintf(&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)); +#endif + + short_pkt = (short_frame *) (data + ADDRESS_FIELD_OFFSET); + } + + dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d; + switch (CLR_PF(short_pkt->h.control)) { + case SABM: + pos += sprintf(&tbuf[pos], "C SABM %d ::", dlci); + break; + case UA: + pos += sprintf(&tbuf[pos], "C UA %d ::", dlci); + break; + case DM: + pos += sprintf(&tbuf[pos], "C DM %d ::", dlci); + break; + case DISC: + pos += sprintf(&tbuf[pos], "C DISC %d ::", dlci); + break; + + case ACK: + pos += sprintf(&tbuf[pos], "C ACK %d ", short_pkt->data[0]); + +#ifdef TS0710SEQ2 + pos += + sprintf(&tbuf[pos], "%02x %02x %02x %02x ", + short_pkt->data[1], short_pkt->data[2], + short_pkt->data[3], short_pkt->data[4]); +#endif + + pos += sprintf(&tbuf[pos], "::"); + break; + + case UIH: + if (!dlci) { + pos += sprintf(&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 (0) { + case CMDTAG: + pos += + sprintf(&tbuf[pos], "I %d A %d ::", dlci, + uih_len); + break; + case DATATAG: + default: + pos += + sprintf(&tbuf[pos], "I %d D %d ::", dlci, + uih_len); + break; + } + + } + break; + default: + pos += sprintf(&tbuf[pos], "N!!! %d ::", dlci); + break; + } + + if (len > (TS0710MUX_MAX_BUF_SIZE - pos - 1)) { + len = (TS0710MUX_MAX_BUF_SIZE - pos - 1); + } + + memcpy(&tbuf[pos], data, len); + pos += len; + tbuf[pos] = 0; + + /* 0x00 byte in the string pointed by g_tbuf may truncate the print result */ + TS0710_PRINTK("[%s]: %s\n", sprd_mux_mgr[mux_id].mux_name, tbuf); +} +#else +#define MUX_TS0710_LOGSTR_FRAME(mux_id, send, data, len) +#endif + +#ifdef TS0710SIG +#define my_for_each_task(p) \ + for ((p) = current; ((p) = (p)->next_task) != current; ) + +/* To be fixed */ +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 valid_dlci(__u8 dlci) +{ + if ((dlci < TS0710_MAX_CHN) && (dlci > 0)) + return 1; + else + return 0; +} + +static int basic_write(ts0710_con * ts0710, __u8 * buf, int len) +{ + int res, send; + struct sprd_mux *self = NULL; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + self = (struct sprd_mux *)ts0710->user_data; + + if (!self || !self->io_hal || !self->io_hal->io_write) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + buf[0] = TS0710_BASIC_FLAG; + buf[len + 1] = TS0710_BASIC_FLAG; + + MUX_TS0710_LOGSTR_FRAME(self->mux_id, 1, buf, len + 2); + MUX_TS0710_DEBUGHEX(self->mux_id, buf, len + 2); + send = 0; + + while (send < len + 2) { + res = self->io_hal->io_write(buf + send, len + 2 - send); + if (res < 0) + return -EIO; + else if (res == 0) + msleep(2); + else + send = send + res; + } + + 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; +#ifdef TS0710DEBUG + __u32 len = length; +#endif + length = length - 1; + while (length--) { + fcs = crctable[fcs ^ *data++]; + } + fcs = crctable[fcs ^ check_sum]; + TS0710_DEBUG("fcs : %d\n", fcs); + TS0710_DEBUG(" crc_check :len:%d check_sum:%d fcs : %d\n", len, + check_sum, 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(ts0710_con *ts0710, __u8 j) +{ + if (j >= TS0710_MAX_CHN) + return; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + ts0710->dlci[j].state = DISCONNECTED; + ts0710->dlci[j].flow_control = 0; + ts0710->dlci[j].mtu = DEF_TS0710_MTU; + ts0710->dlci[j].initiated = 0; + ts0710->dlci[j].initiator = 0; +} + +static void ts0710_reset_con(ts0710_con *ts0710) +{ + __u8 j; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + ts0710->initiator = 0; + ts0710->mtu = DEF_TS0710_MTU + TS0710_MAX_HDR_SIZE; + ts0710->be_testing = 0; + ts0710->test_errs = 0; + + for (j = 0; j < TS0710_MAX_CHN; j++) { + ts0710_reset_dlci(ts0710, j); + } +} + +static void ts0710_init_waitqueue(ts0710_con *ts0710) +{ + __u8 j; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + init_waitqueue_head(&ts0710->test_wait); + + for (j = 0; j < TS0710_MAX_CHN; j++) { + init_waitqueue_head(&ts0710->dlci[j].open_wait); + init_waitqueue_head(&ts0710->dlci[j].close_wait); + } +} + +static void ts0710_init(ts0710_con *ts0710) +{ + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + ts0710_reset_con(ts0710); +} + +static void ts0710_upon_disconnect(ts0710_con *ts0710) +{ + __u8 j; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + for (j = 0; j < TS0710_MAX_CHN; j++) { + ts0710->dlci[j].state = DISCONNECTED; + wake_up_all(&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(ts0710); +} + +/* 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; + + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + if (!ts0710->user_data) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, "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; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + if (!ts0710->user_data) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, "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; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + if (!ts0710->user_data) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, "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; + + if (!ts0710) + { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + if (!ts0710->user_data) + { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, "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, __u32 ring_index, __u16 len, + ts0710_con * ts0710, __u8 dlci) +{ + __u32 size; + + if (!ts0710 || !send_info) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + if (!ts0710->user_data) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + if (!send_info->frame) { + printk(KERN_ERR "MUX: Error %s send_info->frame is NULL\n", __FUNCTION__); + return; + } + + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id,"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[ring_index] - 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[ring_index] = ((__u8 *) l_pkt) - 1; + } else { + short_frame *s_pkt; + + size = sizeof(short_frame) + len + FCS_SIZE; + s_pkt = (short_frame *) (send_info->frame[ring_index] - 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[ring_index] = ((__u8 *) s_pkt) - 1; + } + + if (!send_info->length) { + printk(KERN_ERR "MUX: %s pid[%d] send_info->length NULL\n", __FUNCTION__, current->pid); + return; + } + + send_info->length[ring_index] = 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; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + 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; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + 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); +} + +/* 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; + + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + if (!ts0710->user_data) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, + "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); + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + 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); + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + 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) +{ + __u32 size; + + if (!ts0710 || !test_pattern || !f_buf) + { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + 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 */ + +static void process_mcc(__u8 * data, __u32 len, ts0710_con * ts0710, int longpkt) +{ + __u8 *tbuf = NULL; + mcc_short_frame *mcc_short_pkt; + struct sprd_mux *self = NULL; + int j; + + TS0710_DEBUG("process_mcc\n"); + + if (!ts0710 || !data) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + self = (struct sprd_mux *)ts0710->user_data; + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + 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) { + MUX_TS0710_DEBUG(self->mux_id, "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; + MUX_TS0710_DEBUG(self->mux_id, "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; + MUX_TS0710_DEBUG(self->mux_id, "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 { + MUX_TS0710_DEBUG(self->mux_id, "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 */ + MUX_TS0710_DEBUG(self->mux_id, "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(self); + } + break; + + case FCOFF: /*Flow control off command */ + MUX_TS0710_DEBUG(self->mux_id, "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)) { + MUX_TS0710_DEBUG(self->mux_id, "Received Modem status dlci = %d\n",dlci); + send_dm(ts0710, dlci); + break; + } + if (mcc_short_pkt->h.type.cr == MCC_CMD) { + MUX_TS0710_DEBUG(self->mux_id, "Received Modem status command\n"); + if (v24_sigs & 2) { + if (ts0710->dlci[dlci].state == + CONNECTED) { + MUX_TS0710_DEBUG(self->mux_id, "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; + MUX_TS0710_DEBUG(self->mux_id, "MUX Received Flow on on dlci %d\n", + dlci); + mux_sched_send(self); + } + } + + ts0710_msc_msg(ts0710, v24_sigs, MCC_RSP, dlci); + + } else { + MUX_TS0710_DEBUG(self->mux_id, "Received Modem status response\n"); + + if (v24_sigs & 2) { + MUX_TS0710_DEBUG(self->mux_id, "Flow stop accepted\n"); + } + } + 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); + MUX_TS0710_DEBUG(self->mux_id, "Received DLC parameter negotiation, PN\n"); + if (pn_pkt->mcc_s_head.type.cr == MCC_CMD) { + MUX_TS0710_DEBUG(self->mux_id, "received PN command with:\n"); + MUX_TS0710_DEBUG(self->mux_id, "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; + MUX_TS0710_DEBUG(self->mux_id, "process_mcc : mtu set to %d\n", + ts0710->dlci[dlci].mtu); + } else { + MUX_TS0710_DEBUG(self->mux_id, "received PN response with:\n"); + MUX_TS0710_DEBUG(self->mux_id, "Frame size:%d\n", frame_size); + + frame_size = + min(frame_size, ts0710->dlci[dlci].mtu); + ts0710->dlci[dlci].mtu = frame_size; + + MUX_TS0710_DEBUG(self->mux_id, + "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_all(&ts0710->dlci[dlci].open_wait); + } + } + break; + } + + case NSC: /*Non supported command resonse */ + MUX_TS0710_DEBUG(self->mux_id, "MUX Received Non supported command response\n"); + break; + + default: /*Non supported command received */ + MUX_TS0710_DEBUG(self->mux_id, "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; + + 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; + recv_packet->pos = 0; + + return recv_packet; +} + +static void free_mux_recv_packet(mux_recv_packet * 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 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; + struct sprd_mux *self = NULL; + + if (!ts0710 || !data) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + self = (struct sprd_mux *)ts0710->user_data; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + short_pkt = (short_frame *) data; + + dlci = short_pkt->h.addr.server_chn << 1 | short_pkt->h.addr.d; + MUX_TS0710_DEBUG(self->mux_id, "MUX ts0710_recv_data dlci = %d\n",dlci); + switch (CLR_PF(short_pkt->h.control)) { + case SABM: + MUX_TS0710_DEBUG(self->mux_id, "SABM-packet received\n"); + + if (!dlci) { + MUX_TS0710_DEBUG(self->mux_id, "server channel == 0\n"); + ts0710->dlci[0].state = CONNECTED; + + MUX_TS0710_DEBUG(self->mux_id, "sending back UA - control channel\n"); + send_ua(ts0710, dlci); + wake_up_all(&ts0710->dlci[0].open_wait); + + } else if (valid_dlci(dlci)) { + + MUX_TS0710_DEBUG(self->mux_id, "Incomming connect on channel %d\n", dlci); + + MUX_TS0710_DEBUG(self->mux_id, "sending UA, dlci %d\n", dlci); + send_ua(ts0710, dlci); + + ts0710->dlci[dlci].state = CONNECTED; + wake_up_all(&ts0710->dlci[dlci].open_wait); + + } else { + MUX_TS0710_DEBUG(self->mux_id, "invalid dlci %d, sending DM\n", dlci); + send_dm(ts0710, dlci); + } + + break; + + case UA: + printk(KERN_INFO "MUX: id[%d] dlci = %d UA packet received\n", self->mux_id, dlci); + + if (!dlci) { + MUX_TS0710_DEBUG(self->mux_id, "server channel == 0|dlci[0].state=%d\n", + ts0710->dlci[0].state); + + if (ts0710->dlci[0].state == CONNECTING) { + ts0710->dlci[0].state = CONNECTED; + wake_up_all(&ts0710->dlci[0].open_wait); + } else if (ts0710->dlci[0].state == DISCONNECTING) { + ts0710_upon_disconnect(ts0710); + } else { + MUX_TS0710_DEBUG(self->mux_id, " Something wrong receiving UA packet\n"); + } + } else if (valid_dlci(dlci)) { + MUX_TS0710_DEBUG(self->mux_id, "Incomming UA on channel %d|dlci[%d].state=%d\n", + dlci, dlci, ts0710->dlci[dlci].state); + + if (ts0710->dlci[dlci].state == CONNECTING) { + ts0710->dlci[dlci].state = CONNECTED; + wake_up_all(&ts0710->dlci[dlci].open_wait); + } else if (ts0710->dlci[dlci].state == DISCONNECTING) { + ts0710->dlci[dlci].state = DISCONNECTED; + wake_up_all(&ts0710->dlci[dlci].open_wait); + wake_up_interruptible(&ts0710->dlci[dlci].close_wait); + ts0710_reset_dlci(ts0710, dlci); + } else { + /* Should NOT be here */ + ts0710->dlci[dlci].state = DISCONNECTED; + wake_up_all(&ts0710->dlci[dlci].open_wait); + wake_up_interruptible(&ts0710->dlci[dlci].close_wait); + ts0710_reset_dlci(ts0710, dlci); + + printk(KERN_ERR "MUX: id[%d] dlci[%d] wrong receiving UA packet state = %d\n", self->mux_id, dlci, ts0710->dlci[dlci].state); + } + } else { + MUX_TS0710_DEBUG(self->mux_id, "invalid dlci %d\n", dlci); + } + + break; + + case DM: + MUX_TS0710_DEBUG(self->mux_id, "DM packet received\n"); + + if (!dlci) { + MUX_TS0710_DEBUG(self->mux_id, "server channel == 0\n"); + + if (ts0710->dlci[0].state == CONNECTING) { + be_connecting = 1; + } else { + be_connecting = 0; + } + ts0710_upon_disconnect(ts0710); + if (be_connecting) { + ts0710->dlci[0].state = REJECTED; + } + } else if (valid_dlci(dlci)) { + MUX_TS0710_DEBUG(self->mux_id, "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_all(&ts0710->dlci[dlci].open_wait); + wake_up_interruptible(&ts0710->dlci[dlci].close_wait); + ts0710_reset_dlci(ts0710, dlci); + } else { + MUX_TS0710_DEBUG(self->mux_id, "invalid dlci %d\n", dlci); + } + + break; + case DISC: + MUX_TS0710_DEBUG(self->mux_id, "DISC packet received\n"); + + if (!dlci) { + MUX_TS0710_DEBUG(self->mux_id, "server channel == 0\n"); + + send_ua(ts0710, dlci); + MUX_TS0710_DEBUG(self->mux_id, "DISC, sending back UA\n"); + + ts0710_upon_disconnect(ts0710); + } else if (valid_dlci(dlci)) { + MUX_TS0710_DEBUG(self->mux_id, "Incomming DISC on channel %d\n", dlci); + + send_ua(ts0710, dlci); + MUX_TS0710_DEBUG(self->mux_id, "DISC, sending back UA\n"); + + ts0710->dlci[dlci].state = DISCONNECTED; + wake_up_all(&ts0710->dlci[dlci].open_wait); + wake_up_interruptible(&ts0710->dlci[dlci].close_wait); + ts0710_reset_dlci(ts0710, dlci); + } else { + MUX_TS0710_DEBUG(self->mux_id, "invalid dlci %d\n", dlci); + } + + break; + + case UIH: + MUX_TS0710_DEBUG(self->mux_id, "UIH packet received\n"); + + if ((dlci >= TS0710_MAX_CHN)) { + MUX_TS0710_DEBUG(self->mux_id, "invalid dlci %d\n", dlci); + send_dm(ts0710, dlci); + break; + } + + if (GET_PF(short_pkt->h.control)) { + MUX_TS0710_DEBUG(self->mux_id, "Error UIH packet with P/F set, discard it!\n"); + break; + } + + if ((ts0710->dlci[dlci].state != CONNECTED) + && (ts0710->dlci[dlci].state != FLOW_STOPPED)) { + MUX_TS0710_DEBUG(self->mux_id, "Error DLCI %d not connected, discard it!\n", dlci); + send_dm(ts0710, dlci); + break; + } + + if ((short_pkt->h.length.ea) == 0) { + MUX_TS0710_DEBUG(self->mux_id, "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; + MUX_TS0710_DEBUG(self->mux_id, "long packet length %d\n", uih_len); + + } else { + MUX_TS0710_DEBUG(self->mux_id, "Short UIH pkt received\n"); + uih_len = short_pkt->h.length.len; + uih_data_start = short_pkt->data; + + } + + if (dlci == 0) { + MUX_TS0710_DEBUG(self->mux_id, "UIH on serv_channel 0\n"); + process_mcc(data, len, ts0710, + !(short_pkt->h.length.ea)); + } else if (valid_dlci(dlci)) { + /* do line dispatch */ + __u8 line; + mux_recv_struct *recv_info; + + mux_recv_packet *recv_packet, *recv_packet2; + MUX_TS0710_DEBUG(self->mux_id, "UIH on channel %d\n", dlci); + + if (uih_len > ts0710->dlci[dlci].mtu) { + MUX_TS0710_DEBUG(self->mux_id, "MUX Error: DLCI:%d, uih_len:%d is bigger than mtu:%d, discard data!\n", + dlci, uih_len, ts0710->dlci[dlci].mtu); + break; + } + + line = dlci2line[dlci].dataline; /* we see data and commmand as same */ + + if (self->callback[line].func) { + MUX_TS0710_DEBUG(self->mux_id, "MUX: callback on mux%d", line); + if (!(*self->callback[line].func)(line, SPRDMUX_EVENT_COMPLETE_READ, uih_data_start, uih_len, self->callback[line].user_data)) { + break; + } + } + + if ((!self->open_count[line])) { + MUX_TS0710_DEBUG(self->mux_id, "MUX: No application waiting for, discard it! /dev/mux%d\n", line); + } else { /* Begin processing received data */ + if ((!self->mux_recv_info_flags[line]) + || (!self->mux_recv_info[line])) { + MUX_TS0710_DEBUG(self->mux_id, "MUX Error: No mux_recv_info, discard it! /dev/mux%d\n", line); + break; + } + + recv_info = self->mux_recv_info[line]; + if(!recv_info) { + MUX_TS0710_DEBUG(self->mux_id, "recv_data : recv_info is null\n"); + break; + } + + if (recv_info->total > TS0710MUX_MAX_TOTAL_SIZE) { + MUX_TS0710_DEBUG(self->mux_id, KERN_INFO "MUX : discard data for line:%d, recv_info->total = %d!\n", line, recv_info->total); + break; + } + + if (line <= MUX_RIL_LINE_END) { + /* Just display AT line */ + printk(KERN_ERR "MUX: id[%d] line[%d] received %d data\n", self->mux_id, line, uih_len); + } + + mutex_lock(&recv_info->recv_data_lock); + + if (recv_info->total != 0 || recv_info->length != 0) { /* add new data to tail */ + /* recv_info is already linked into mux_recv_queue */ + recv_packet = get_mux_recv_packet(uih_len); + if (!recv_packet) { + MUX_TS0710_DEBUG(self->mux_id, "MUX: no memory\n"); + mutex_unlock(&recv_info->recv_data_lock); + break; + } + + memcpy(recv_packet->data, uih_data_start, uih_len); + recv_packet->length = uih_len; + recv_info->total += uih_len; + self->check_read_sum[line] += 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 { + if (uih_len > TS0710MUX_RECV_BUF_SIZE) { + MUX_TS0710_DEBUG(self->mux_id, "MUX Error: line:%d, uih_len == %d is too big\n", line, 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; + recv_info->pos = 0; + self->check_read_sum[line] += uih_len; + wake_up_interruptible(&recv_info->rx_wait); + } + + mutex_unlock(&recv_info->recv_data_lock); + } /* End processing received data */ + } else { + MUX_TS0710_DEBUG(self->mux_id, "invalid dlci %d\n", dlci); + } + + break; + + default: + MUX_TS0710_DEBUG(self->mux_id, "illegal packet\n"); + break; + } + return 0; +} + +/* Close ts0710 channel */ +static void ts0710_close_channel(ts0710_con *ts0710, __u8 dlci) +{ + int try; + unsigned long t; + struct sprd_mux *self = NULL; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + self = (struct sprd_mux *)ts0710->user_data; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, "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; + if (self->mux_status != MUX_STATE_READY) { + printk(KERN_ERR "MUX: Error %s cp is Not OK\n", __FUNCTION__); + ts0710->dlci[dlci].state = DISCONNECTED; + return; + } + + try = 1; + while (try) { + --try; + t = jiffies; + send_disc(ts0710, dlci); + interruptible_sleep_on_timeout(&ts0710->dlci[dlci].close_wait, 10 * TS0710MUX_TIME_OUT); + + if (ts0710->dlci[dlci].state == DISCONNECTED) { + break; + } else if (ts0710->dlci[dlci].state == CONNECTED || ts0710->dlci[dlci].state == CONNECTING) { + /* Todo:Should NOT be here */ + printk(KERN_ERR "MUX id[%d] DLCI:%d opening occured!\n", self->mux_id, dlci); + break; + } + else if (signal_pending(current)) { + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, "MUX DLCI %d Send DISC got signal!\n", dlci); + break; + } else if ((jiffies - t) >= TS0710MUX_TIME_OUT) { + MUX_TS0710_DEBUG(((struct sprd_mux *)ts0710->user_data)->mux_id, "MUX DLCI %d Send DISC timeout!\n", dlci); + break; + } + } + if (ts0710->dlci[dlci].state != DISCONNECTED) { + if (dlci == 0) { /* Control Channel */ + ts0710_upon_disconnect(ts0710); + } else { /* Other Channel */ + ts0710->dlci[dlci].state = DISCONNECTED; + wake_up_interruptible(&ts0710->dlci[dlci].close_wait); + ts0710_reset_dlci(ts0710, dlci); + } + } + } +} + +static int ts0710_open_channel(ts0710_con *ts0710, __u8 dlci) +{ + int try; + int retval; + struct sprd_mux *self = NULL; + + retval = -ENODEV; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return retval; + } + + self = (struct sprd_mux *)ts0710->user_data; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return retval; + } + + TS0710_PRINTK("MUX %s id[%d] DLCI:%d state = %d dlci[0]'state = %d\n", __FUNCTION__, self->mux_id, dlci, ts0710->dlci[dlci].state, ts0710->dlci[0].state); + + 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 */ + printk(KERN_INFO "MUX: id[%d] DLCI: 0, reentry to open DLCI 0, pid: %d, %s !\n",self->mux_id, current->pid, current->comm); + try = 1;/* No try*/ + while (try) { + --try; + wait_event_interruptible(ts0710->dlci[0].open_wait, ts0710->dlci[0].state != CONNECTING || self->mux_status == MUX_STATE_CRASHED); + TS0710_PRINTK("MUX: id[%d] DLCI:%d after open wait state = %d try = %d", self->mux_id, dlci, ts0710->dlci[dlci].state, try); + + if (self->mux_status == MUX_STATE_CRASHED) { + printk(KERN_INFO "MUX: id[%d] Error DLCI[%d] cp is crashed!\n", self->mux_id, dlci); + //Need check + return -ENODEV; + } + + if ((ts0710->dlci[0].state == CONNECTED) + || (ts0710->dlci[0].state == FLOW_STOPPED)) { + retval = 0; + break; + } else if (ts0710->dlci[0].state == REJECTED) { + printk(KERN_ERR "MUX: id[%d] Error DLCI[%d] REJECTED\n", self->mux_id, dlci); + retval = -EREJECTED; + break; + } else if (ts0710->dlci[0].state == DISCONNECTED) { + printk(KERN_ERR "MUX: id[%d] Error DLCI[%d] DISCONNECTED\n", self->mux_id, dlci); + retval = -EAGAIN; + break; + } else if (signal_pending(current)) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d Wait for connecting got signal!\n", self->mux_id, dlci); + retval = -EAGAIN; + break; + + } else if (ts0710->dlci[0].state == CONNECTING) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d error out 6\n", self->mux_id, dlci); + break; + } + } + + if (ts0710->dlci[0].state == CONNECTING) { + ts0710->dlci[0].state = DISCONNECTED; + retval = -EAGAIN; + } + } else if ((ts0710->dlci[0].state != DISCONNECTED) + && (ts0710->dlci[0].state != REJECTED)) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d state is invalid!\n", self->mux_id, dlci); + return retval; + } else { + ts0710->initiator = 1; + ts0710->dlci[0].state = CONNECTING; + ts0710->dlci[0].initiator = 1; + try = 1;/* No try*/ + while (try) { + + --try; + send_sabm(ts0710, 0); + wait_event_interruptible(ts0710->dlci[0].open_wait, ts0710->dlci[0].state != CONNECTING || self->mux_status == MUX_STATE_CRASHED); + + TS0710_PRINTK("MUX: id[%d] DLCI:%d after open wait state = %d try = %d", self->mux_id, dlci, ts0710->dlci[dlci].state, try); + + if (self->mux_status == MUX_STATE_CRASHED) { + printk(KERN_INFO "MUX: id[%d] Error DLCI[%d] cp is crashed!\n", self->mux_id, dlci); + //Need check + return -ENODEV; + } + + if ((ts0710->dlci[0].state == CONNECTED) + || (ts0710->dlci[0].state == FLOW_STOPPED)) { + retval = 0; + break; + } else if (ts0710->dlci[0].state == REJECTED) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d Send SABM got rejected!\n",self->mux_id, dlci); + retval = -EREJECTED; + break; + } else if (signal_pending(current)) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d Send SABM got signal!\n", self->mux_id, dlci); + retval = -EAGAIN; + break; + } + } + + if (ts0710->dlci[0].state == CONNECTING) { + ts0710->dlci[0].state = DISCONNECTED; + retval = -EAGAIN; + } + wake_up_all(&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 = 1;/* No try*/ + while (try) { + + --try; + wait_event_interruptible(ts0710->dlci[dlci].open_wait, (ts0710->dlci[dlci].state != CONNECTING || ts0710->dlci[dlci].state!= NEGOTIATING || self->mux_status == MUX_STATE_CRASHED)); + TS0710_PRINTK("MUX: id[%d] DLCI:%d after open wait state = %d try = %d", self->mux_id, dlci, ts0710->dlci[dlci].state, try); + + if (self->mux_status == MUX_STATE_CRASHED) { + printk(KERN_INFO "MUX: id[%d] Error DLCI[%d] cp is crashed!\n", self->mux_id, dlci); + //Need check + return -ENODEV; + } + + if ((ts0710->dlci[dlci].state == CONNECTED) + || (ts0710->dlci[dlci].state == FLOW_STOPPED)) { + retval = 0; + break; + } else if (ts0710->dlci[dlci].state == REJECTED) { + retval = -EREJECTED; + printk(KERN_ERR "MUX: id[%d] DLCI:%d Send SABM got rejected!\n",self->mux_id, dlci); + break; + } else if (ts0710->dlci[dlci].state == DISCONNECTED) { + printk(KERN_ERR "MUX: id[%d] Error DLCI[%d] DISCONNECTED\n", self->mux_id, dlci); + break; + } else if (signal_pending(current)) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d Wait for connecting got signal!\n", self->mux_id, dlci); + retval = -EAGAIN; + break; + } else if ((ts0710->dlci[dlci].state == NEGOTIATING) + || (ts0710->dlci[dlci].state == CONNECTING)) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d CONNECTING or NEGOTIATING\n", self->mux_id, dlci); + break; + } + } + + if ((ts0710->dlci[dlci].state == NEGOTIATING) + || (ts0710->dlci[dlci].state == CONNECTING)) { + ts0710->dlci[dlci].state = DISCONNECTED; + retval = -EAGAIN; + } + } else if ((ts0710->dlci[dlci].state != DISCONNECTED) + && (ts0710->dlci[dlci].state != REJECTED)) { + return retval; + } else { + ts0710->dlci[dlci].state = CONNECTING; + ts0710->dlci[dlci].initiator = 1; + if (ts0710->dlci[dlci].state == CONNECTING) { + try = 1; + while (try) { + + --try; + send_sabm(ts0710, dlci); + wait_event_interruptible(ts0710->dlci[dlci].open_wait,ts0710->dlci[dlci].state != CONNECTING || self->mux_status == MUX_STATE_CRASHED); + + TS0710_PRINTK("MUX: id[%d] DLCI:%d after open wait state = %d try = %d", self->mux_id, dlci, ts0710->dlci[dlci].state, try); + + if (self->mux_status == MUX_STATE_CRASHED) { + printk(KERN_INFO "MUX: id[%d] Error DLCI[%d] cp is crashed!\n", self->mux_id, dlci); + //Need check + return -ENODEV; + } + + if ((ts0710->dlci[dlci].state == CONNECTED) + || (ts0710->dlci[dlci].state ==FLOW_STOPPED)) { + retval = 0; + break; + } else if (ts0710->dlci[dlci].state == DISCONNECTED || ts0710->dlci[dlci].state == DISCONNECTING) { + /* Todo:Should NOT be here */ + printk(KERN_ERR "MUX id[%d] DLCI:%d opening occured!\n", self->mux_id, dlci); + retval = -EAGAIN; + break; + } else if (ts0710->dlci[dlci].state == REJECTED) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d Send SABM got rejected!\n", self->mux_id, dlci); + retval = -EREJECTED; + break; + } else if (signal_pending(current)) { + printk(KERN_ERR "MUX: id[%d] DLCI:%d Send SABM got signal!\n", self->mux_id, dlci); + retval = -EAGAIN; + break; + } + } + } + + if ((ts0710->dlci[dlci].state == NEGOTIATING) + || (ts0710->dlci[dlci].state == CONNECTING)) { + ts0710->dlci[dlci].state = DISCONNECTED; + retval = -EAGAIN; + } + wake_up_all(&ts0710->dlci[dlci].open_wait); + } + } + return retval; +} + +static int ts0710_exec_test_cmd(ts0710_con *ts0710) +{ + __u8 *f_buf; /* Frame buffer */ + __u8 *d_buf; /* Data buffer */ + int retval = -EFAULT; + int j; + unsigned long t; + struct sprd_mux *self = NULL; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return retval; + } + + self = (struct sprd_mux *)ts0710->user_data; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return retval; + } + + MUX_TS0710_DEBUG(self->mux_id, "ts0710_exec_test_cmd\n"); + + 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)) { + MUX_TS0710_DEBUG(self->mux_id, "Wait for Test_cmd response got signal!\n"); + retval = -EAGAIN; + } else if ((jiffies - t) >= 3 * TS0710MUX_TIME_OUT) { + MUX_TS0710_DEBUG(self->mux_id, "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)) { + MUX_TS0710_DEBUG(self->mux_id, "Send Test_cmd got signal!\n"); + retval = -EAGAIN; + } else if ((jiffies - t) >= 2 * TS0710MUX_TIME_OUT) { + MUX_TS0710_DEBUG(self->mux_id, "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(struct sprd_mux *self) +{ + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + complete(&self->send_completion); +} + +/* + * Returns 1 if found, 0 otherwise. needle must be null-terminated. + * strstr might not work because WebBox sends garbage before the first OKread + * + */ +int findInBuf(unsigned char *buf, int len, char *needle) +{ + int i; + int needleMatchedPos = 0; + + if (needle[0] == '\0') { + return 1; + } + + for (i = 0; i < len; i++) { + if (needle[needleMatchedPos] == buf[i]) { + needleMatchedPos++; + if (needle[needleMatchedPos] == '\0') { + /* Entire needle was found */ + return 1; + } + } else { + needleMatchedPos = 0; + } + } + return 0; +} + + +/**************************** + * TTY driver routines +*****************************/ +void ts0710_mux_close(int mux_id, int line) +{ + __u8 dlci; + __u8 cmdline; + __u8 dataline; + ts0710_con *ts0710 = NULL; + struct sprd_mux *self = NULL; + + if ((line < 0) || (line >= NR_MUXS)) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] is invalid\n", __FUNCTION__, mux_id, line); + return; + } + + self = sprd_mux_mgr[mux_id].handle; + + if (!self) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] self is NULL\n", __FUNCTION__, mux_id, line); + return; + } + + ts0710 = self ->connection; + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] ts0710 is NULL\n", __FUNCTION__, mux_id, line); + return; + } + + if (self->open_count[line] > 0) { + self->open_count[line]--; + } + + // The close here and the open in mux_recover_thread may conflict, so wait + if (self->mux_status == MUX_STATE_RECOVERING) { + int rval = 0; + + printk(KERN_INFO "MUX: mux[%d] line[%d] pid[%d] wait close \n", mux_id, line, current->pid); + + rval = wait_event_interruptible_timeout(self->modem_ready, self->mux_status == MUX_STATE_READY, TS0710MUX_TIME_OUT); + + if (rval < 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] close wait interrupted!\n", __FUNCTION__, mux_id, line, current->pid); + + } else if (rval == 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] timeout!\n", __FUNCTION__, mux_id, line, current->pid); + + } + } + + printk(KERN_INFO "MUX: mux[%d] line[%d] pid[%d] closed!\n", mux_id, line, current->pid); + + dlci = line2dlci[line]; + cmdline = dlci2line[dlci].cmdline; + dataline = dlci2line[dlci].dataline; + + if ((self->open_count[cmdline] == 0) && (self->open_count[dataline] == 0)) { + //close current channel first + ts0710_close_channel(ts0710, dlci); + } + + if (self->open_count[line] == 0) { + if ((self->mux_send_info_flags[line]) + && (self->mux_send_info[line]) + ) { + self->mux_send_info_flags[line] = 0; + mux_free_send_info(self->mux_send_info[line]); + self->mux_send_info[line] = 0; + MUX_TS0710_DEBUG(self->mux_id, "Free mux_send_info for /dev/mux%d\n", line); + } + + if ((self->mux_recv_info_flags[line]) + && (self->mux_recv_info[line]) + && (self->mux_recv_info[line]->total == 0)) { + self->mux_recv_info_flags[line] = 0; + free_mux_recv_struct(self->mux_recv_info[line]); + self->mux_recv_info[line] = 0; + MUX_TS0710_DEBUG(self->mux_id, "Free mux_recv_info for /dev/mux%d\n", line); + } + } +} + +int ts0710_mux_read(int mux_id, int line, const unsigned char *buf, int count, int timeout) +{ + __u8 dlci; + mux_recv_struct *recv_info; + struct sprd_mux *self = NULL; + ts0710_con *ts0710 = NULL; + int rval = 0; + mux_recv_packet *recv_packet; + unsigned char *buf_pos; + int cp_cnt = 0; + + MUX_TS0710_DEBUG(mux_id, "line[%d] pid[%d] count = %d, timeout = %d\n", line, current->pid, count, timeout); + + if (count <= 0) { + return 0; + } + + if ((line < 0) || (line >= NR_MUXS)) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] is invalid\n", __FUNCTION__, mux_id, line); + return -EINVAL; + } + + self = sprd_mux_mgr[mux_id].handle; + + if (!self) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] self is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + ts0710 = self ->connection; + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] ts0710 is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + dlci = line2dlci[line]; + if (ts0710->dlci[0].state == FLOW_STOPPED) { + MUX_TS0710_DEBUG(self->mux_id, "Flow stopped on all channels, returning zero /dev/mux%d\n", line); + return 0; + } else if (ts0710->dlci[dlci].state == FLOW_STOPPED) { + MUX_TS0710_DEBUG(self->mux_id, "Flow stopped, returning zero /dev/mux%d\n", line); + return 0; + } else if (ts0710->dlci[dlci].state == CONNECTED) { + if (!(self->mux_send_info_flags[line])) { + TS0710_PRINTK("MUX Error: mux_write: mux_send_info_flags[%d] == 0\n", line); + return -ENODEV; + } + recv_info = self->mux_recv_info[line]; + if (!recv_info) { + TS0710_PRINTK("MUX Error: mux_write: mux_send_info[%d] == 0\n", line); + return -ENODEV; + } + + if (timeout == 0) { + if (!mutex_trylock(&recv_info->recv_lock)) { + printk(KERN_INFO "MUX: %s mux[%d] line[%d] pid[%d] is busy!\n", __FUNCTION__, mux_id, line, current->pid); + return -EBUSY; + } + + if (recv_info->total == 0) { + printk(KERN_INFO "MUX: %s mux[%d] line[%d] pid[%d] is empty!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&recv_info->recv_lock); + return -EBUSY; + } + } else if (timeout < 0){ + mutex_lock(&recv_info->recv_lock); + MUX_TS0710_DEBUG(mux_id, "line[%d] pid[%d] read wait\n", line, current->pid); + rval = wait_event_interruptible(recv_info->rx_wait, recv_info->total != 0 && self->mux_status == MUX_STATE_READY); + if (rval < 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] read wait interrupted!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&recv_info->recv_lock); + return -EINTR; + } + }else { + mutex_lock(&recv_info->recv_lock); + rval = wait_event_interruptible_timeout(recv_info->rx_wait, recv_info->total != 0 && self->mux_status == MUX_STATE_READY, timeout); + if (rval < 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] write wait interrupted!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&recv_info->recv_lock); + return -EINTR; + + } else if (rval == 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] timeout!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&recv_info->recv_lock); + return -ETIME; + + } + } + + mutex_lock(&recv_info->recv_data_lock); + + buf_pos = (unsigned char *)buf; + + MUX_TS0710_DEBUG(mux_id, "line[%d] pid[%d] length = %d total = %d\n", line, current->pid, recv_info->length, recv_info->total); + + //copy data of recv_info before one of the packet + if (recv_info->length != 0) { + + cp_cnt = min(count, recv_info->length); + + if ((uint32_t)buf > TASK_SIZE) { + memcpy(buf_pos, recv_info->data + recv_info->pos, cp_cnt); + } else { + if(copy_to_user(buf_pos, recv_info->data + recv_info->pos, cp_cnt)) { + mutex_unlock(&recv_info->recv_data_lock); + mutex_unlock(&recv_info->recv_lock); + return -EFAULT; + } + } + + buf_pos += cp_cnt; + count -= cp_cnt; + recv_info->length -= cp_cnt; + recv_info->total -= cp_cnt; + + self->check_read_sum[line] -= cp_cnt; + + if (recv_info->length == 0) { + recv_info->pos = 0; + } else { + recv_info->pos += cp_cnt; + } + } + + while(count > 0 && recv_info->total != 0) { + if ((recv_packet = recv_info->mux_packet)) { + cp_cnt = min(count, recv_packet->length); + + if ((uint32_t)buf > TASK_SIZE) { + memcpy(buf_pos, recv_packet->data + recv_packet->pos, cp_cnt); + } else { + if(copy_to_user(buf_pos, recv_packet->data + recv_packet->pos, cp_cnt)) { + mutex_unlock(&recv_info->recv_data_lock); + mutex_unlock(&recv_info->recv_lock); + printk(KERN_ERR "MUX: %s mux[%d] line[%d] pid[%d] total is error!\n", __FUNCTION__, mux_id, line, current->pid); + return -EFAULT; + } + } + + buf_pos += cp_cnt; + count -= cp_cnt; + recv_packet->length -= cp_cnt; + recv_info->total -= cp_cnt; + self->check_read_sum[line] -= cp_cnt; + + if (recv_packet->length == 0) { + recv_packet->pos = 0; + recv_info->mux_packet = recv_packet->next; + free_mux_recv_packet(recv_packet); + } else { + recv_packet->pos += cp_cnt; + } + } else { + if (recv_info->total != 0) { + printk(KERN_ERR "MUX: %s mux[%d] line[%d] pid[%d] total is incorrect!\n", __FUNCTION__, mux_id, line, current->pid); + break; + } + } + } + mutex_unlock(&recv_info->recv_data_lock); + mutex_unlock(&recv_info->recv_lock); + + MUX_TS0710_DEBUG(mux_id, "line[%d] pid[%d] read = %d\n", line, current->pid, buf_pos - buf); + + return buf_pos - buf; + } + return 0; +} + +int ts0710_mux_poll_wait(int mux_id, int line, struct file *filp, poll_table *wait) +{ + __u8 dlci; + struct sprd_mux *self = NULL; + ts0710_con *ts0710 = NULL; + mux_send_struct *send_info; + mux_recv_struct *recv_info; + unsigned int mask = 0; + + if ((line < 0) || (line >= NR_MUXS)) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] is invalid\n", __FUNCTION__, mux_id, line); + return -EINVAL; + } + + self = sprd_mux_mgr[mux_id].handle; + if (!self) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] self is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + ts0710 = self ->connection; + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] ts0710 is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + dlci = line2dlci[line]; + if (ts0710->dlci[dlci].state != CONNECTED) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] state is %d\n", __FUNCTION__, mux_id, line, ts0710->dlci[dlci].state); + return -ENODEV; + } + + send_info = self->mux_send_info[line]; + recv_info = self->mux_recv_info[line]; + + poll_wait(filp, &send_info->tx_wait, wait); + poll_wait(filp, &recv_info->rx_wait, wait); + + if (recv_info->total != 0 && self->mux_status == MUX_STATE_READY) { + mask |= POLLIN | POLLRDNORM; + } + + if (mux_line_state(mux_id, line) == 0 && self->mux_status == MUX_STATE_READY) { + mask |= POLLOUT | POLLWRNORM; + } + + return mask; + +} + +int ts0710_mux_write(int mux_id, int line, const unsigned char *buf, int count, int timeout) +{ + __u8 dlci; + mux_send_struct *send_info; + __u8 *d_buf; + __u16 c; + struct sprd_mux *self = NULL; + ts0710_con *ts0710 = NULL; + int rval = 0; + __u32 ring_index; + + MUX_TS0710_DEBUG(mux_id, "line[%d] pid[%d] count = %d, timeout = %d\n", line, current->pid, count, timeout); + + if (count <= 0) { + return 0; + } + + if ((line < 0) || (line >= NR_MUXS)) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] is invalid\n", __FUNCTION__, mux_id, line); + return -EINVAL; + } + + self = sprd_mux_mgr[mux_id].handle; + + if (!self) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] self is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + ts0710 = self ->connection; + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] ts0710 is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + dlci = line2dlci[line]; + if (ts0710->dlci[0].state == FLOW_STOPPED) { + MUX_TS0710_DEBUG(self->mux_id, "Flow stopped on all channels, returning zero /dev/mux%d\n", line); + return 0; + } else if (ts0710->dlci[dlci].state == FLOW_STOPPED) { + MUX_TS0710_DEBUG(self->mux_id, "Flow stopped, returning zero /dev/mux%d\n", line); + return 0; + } else if (ts0710->dlci[dlci].state == CONNECTED) { + if (!(self->mux_send_info_flags[line])) { + TS0710_PRINTK("MUX Error: mux_write: mux_send_info_flags[%d] == 0\n", line); + return -ENODEV; + } + send_info = self->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 (timeout == 0) { + if (!mutex_trylock(&send_info->send_lock)) { + printk(KERN_INFO "MUX: %s mux[%d] line[%d] pid[%d] is busy!\n", __FUNCTION__, mux_id, line, current->pid); + return -EBUSY; + } + + if (mux_line_state(mux_id, line)) { + printk(KERN_INFO "MUX: %s mux[%d] line[%d] pid[%d] is filled!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&send_info->send_lock); + return -EBUSY; + } + + if (self->mux_status != MUX_STATE_READY) + { + printk(KERN_INFO "MUX: %s mux[%d] line[%d] pid[%d] mux is Not ready!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&send_info->send_lock); + return -EBUSY; + } + } else if (timeout < 0){ + mutex_lock(&send_info->send_lock); + + MUX_TS0710_DEBUG(mux_id, "line[%d] pid[%d] wait write\n", line, current->pid); + + rval = wait_event_interruptible(send_info->tx_wait, mux_line_state(mux_id, line) == 0 && self->mux_status == MUX_STATE_READY); + if (rval < 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] write wait interrupted!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&send_info->send_lock); + return -EINTR; + } + }else { + mutex_lock(&send_info->send_lock); + rval = wait_event_interruptible_timeout(send_info->tx_wait, mux_line_state(mux_id, line) == 0 && self->mux_status == MUX_STATE_READY, timeout); + if (rval < 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] write wait interrupted!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&send_info->send_lock); + rval = -EINTR; + } else if (rval == 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] timeout!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&send_info->send_lock); + return -ETIME; + } + } + + ring_index = send_info->write_index % ringbuf_num[mux_id][line]; + + d_buf = send_info->buf + (ring_index * TS0710MUX_SEND_BUF_SIZE) + TS0710MUX_SEND_BUF_OFFSET; + +// printk(KERN_ERR "MUX: %s mux[%d] line[%d] pid[%d] ring_index = %d, num = %d\n", __FUNCTION__, mux_id, line, current->pid, ring_index, ringbuf_num[mux_id][line]); + + if ((uint32_t)buf > TASK_SIZE) { + memcpy(&d_buf[0], buf, c); + } else { + if(copy_from_user(&d_buf[0], buf, c)) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] pid[%d] failed to copy from user!\n", __FUNCTION__, mux_id, line, current->pid); + mutex_unlock(&send_info->send_lock); + return -EFAULT; + } + } + + MUX_TS0710_DEBUG(self->mux_id, "Prepare to send %d bytes from /dev/mux%d", c, line); + MUX_TS0710_DEBUGHEX(self->mux_id, d_buf, c); + + if (!send_info->frame) { + printk(KERN_ERR "MUX: %s mux[%d] line[%d] pid[%d] send_info->frame NULL\n", __FUNCTION__, mux_id, line, current->pid); + return 0; + } + + send_info->frame[ring_index] = d_buf; + + queue_uih(send_info, ring_index, c, ts0710, dlci); + + if (line <= MUX_RIL_LINE_END) { + printk(KERN_ERR "MUX: %s mux[%d] line[%d] pid[%d] write = %d\n", __FUNCTION__, mux_id, line, current->pid, c); + } + + send_info->write_index++; + + mutex_unlock(&send_info->send_lock); + + mux_sched_send(self); + +// printk(KERN_ERR "MUX: %s mux[%d] line[%d] pid[%d] write = %d\n", __FUNCTION__, mux_id, line, current->pid, c); + + MUX_TS0710_DEBUG(mux_id, "line[%d] pid[%d] wait write\n", line, current->pid); + + return c; + } else { + printk(KERN_ERR "MUX: %s mux[%d] line[%d] pid[%d] not connected\n", __FUNCTION__, mux_id, line, current->pid); + return -EDISCONNECTED; + } +} + +int ts0710_mux_mux_ioctl(int mux_id, int line, unsigned int cmd, unsigned long arg) +{ + __u8 dlci; + struct sprd_mux *self = NULL; + ts0710_con *ts0710 = NULL; + + UNUSED_PARAM(arg); + + if ((line < 0) || (line >= NR_MUXS)) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] is invalid\n", __FUNCTION__, mux_id, line); + return -EINVAL; + } + + self = sprd_mux_mgr[mux_id].handle; + + if (!self) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] self is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + ts0710 = self ->connection; + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s mux[%d] line[%d] ts0710 is NULL\n", __FUNCTION__, mux_id, line); + return -ENODEV; + } + + dlci = line2dlci[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(ts0710); + + default: + break; + } + return -ENOIOCTLCMD; +} + +int ts0710_mux_open(int mux_id, int line) +{ + int retval; + __u8 dlci; + __u8 cmdline; + __u8 dataline; + mux_send_struct *send_info; + mux_recv_struct *recv_info; + struct sprd_mux *self = NULL; + ts0710_con *ts0710 = NULL; + + retval = -ENODEV; + + if ((line < 0) || (line >= NR_MUXS)) { + printk(KERN_ERR "MUX: Error %s line[%d] is wrong\n", __FUNCTION__, line); + goto out; + } + + self = sprd_mux_mgr[mux_id].handle; + + mutex_lock(&self->open_mutex[line]); + + ts0710 = self->connection; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + goto out; + } + + self->open_count[line]++; + dlci = line2dlci[line]; + + printk(KERN_INFO "MUX: %s id = %d, dlci = %d, pid = %d\n", __FUNCTION__, self->mux_id, dlci, current->pid); + + retval = wait_event_interruptible(self->modem_ready, self->mux_status == MUX_STATE_READY); + if (retval < 0) { + printk(KERN_WARNING "MUX: %s mux[%d] line[%d] pid[%d] ready wait interrupted!\n", __FUNCTION__, self->mux_id, line, current->pid); + self->open_count[line]--; + mutex_unlock(&self->open_mutex[line]); + retval = -EINTR; + goto out; + } + + if (self->cmux_mode == 0) { + mutex_lock(&self->handshake_mutex); + printk(KERN_INFO "MUX: id = %d, dlci = %d mode = %d\n", self->mux_id, dlci, self->cmux_mode); + if (self->cmux_mode == 0) { + if (mux_handshake(self) != 0) { + mutex_unlock(&self->handshake_mutex); + self->open_count[line]--; + mutex_unlock(&self->open_mutex[line]); + printk(KERN_WARNING "\n wrong modem state !!!!\n"); + retval = -ENODEV; + goto out; + } + self->cmux_mode = 1; + wake_up(&self->handshake_ready); + + /* Open server channel 0 first */ + if ((retval = ts0710_open_channel(ts0710, 0)) != 0) { + printk(KERN_ERR "MUX: Error Can't connect server channel 0!\n"); + ts0710_init(ts0710); + self->open_count[line]--; + mutex_unlock(&self->handshake_mutex); + mutex_unlock(&self->open_mutex[line]); + goto out; + } + } + mutex_unlock(&self->handshake_mutex); + } + + if ((retval = ts0710_ctrl_channel_status(self->connection)) != 0) { + ts0710_init(ts0710); + self->open_count[line]--; + mutex_unlock(&self->open_mutex[line]); + goto out; + } + + /* Allocate memory first. As soon as connection has been established, MUX may receive */ + if (self->mux_send_info_flags[line] == 0) { + send_info = mux_alloc_send_info(mux_id, line); + if (!send_info) { + retval = -ENOMEM; + self->open_count[line]--; + mutex_unlock(&self->open_mutex[line]); + goto out; + } + + send_info->read_index = 0; + send_info->write_index = 0; + send_info->flags = 0; + mutex_init(&send_info->send_lock); + init_waitqueue_head(&send_info->tx_wait); + + self->mux_send_info[line] = send_info; + self->mux_send_info_flags[line] = 1; + + MUX_TS0710_DEBUG(self->mux_id, "Allocate mux_send_info for /dev/mux%d\n", line); + } + + if (self->mux_recv_info_flags[line] == 0) { + recv_info = (mux_recv_struct *) kmalloc(sizeof(mux_recv_struct), GFP_KERNEL); + if (!recv_info) { + self->mux_send_info_flags[line] = 0; + mux_free_send_info(self->mux_send_info[line]); + self->mux_send_info[line] = 0; + + MUX_TS0710_DEBUG(self->mux_id, "Free mux_send_info for /dev/mux%d\n", line); + retval = -ENOMEM; + + self->open_count[line]--; + mutex_unlock(&self->open_mutex[line]); + goto out; + } + recv_info->length = 0; + recv_info->total = 0; + recv_info->mux_packet = 0; + recv_info->pos = 0; + mutex_init(&recv_info->recv_lock); + mutex_init(&recv_info->recv_data_lock); + init_waitqueue_head(&recv_info->rx_wait); + + self->mux_recv_info[line] = recv_info; + self->mux_recv_info_flags[line] = 1; + + MUX_TS0710_DEBUG(self->mux_id, "Allocate mux_recv_info for /dev/mux%d\n", line); + } + + /* Now establish DLCI connection */ + cmdline = dlci2line[dlci].cmdline; + dataline = dlci2line[dlci].dataline; + + MUX_TS0710_DEBUG(self->mux_id, "dlci = %d cmdline = %d dataline = %d open_count[cmdline] = %d open_count[dataline] = %d\n", dlci, cmdline, dataline, self->open_count[cmdline], self->open_count[dataline]); + + if ((self->open_count[cmdline] > 0) || (self->open_count[dataline] > 0)) { + if ((retval = ts0710_open_channel(ts0710, dlci)) != 0) { + TS0710_PRINTK("MUX: Can't connected channel %d!\n", dlci); + ts0710_reset_dlci(ts0710, dlci); + + self->mux_send_info_flags[line] = 0; + mux_free_send_info(self->mux_send_info[line]); + self->mux_send_info[line] = 0; + + MUX_TS0710_DEBUG(self->mux_id, "Free mux_send_info for /dev/mux%d\n", line); + + self->mux_recv_info_flags[line] = 0; + free_mux_recv_struct(self->mux_recv_info[line]); + self->mux_recv_info[line] = 0; + MUX_TS0710_DEBUG(self->mux_id, "Free mux_recv_info for /dev/mux%d\n", line); + + self->open_count[line]--; + mutex_unlock(&self->open_mutex[line]); + goto out; + } + } + + mutex_unlock(&self->open_mutex[line]); + retval = 0; + + out: + printk(KERN_INFO "MUX: id = %d, dlci = %d, retval = %d\n", self->mux_id, dlci, retval); + + return retval; +} + +static inline int rt_policy(int policy) +{ + if (unlikely(policy == SCHED_FIFO) || unlikely(policy == SCHED_RR)) + return 1; + return 0; +} + +static inline int task_has_rt_policy(struct task_struct *p) +{ + return rt_policy(p->policy); +} + +/*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 +{ + return 0; +} + +static int mux_set_thread_pro(int pro) +{ + int ret; + struct sched_param s; + + /* just for this write, set us real-time */ + if (!task_has_rt_policy(current)) { + struct cred *new = prepare_creds(); + if(!new) + return -ENOMEM; + cap_raise(new->cap_effective, CAP_SYS_NICE); + commit_creds(new); + s.sched_priority = MAX_RT_PRIO - pro; + ret = sched_setscheduler(current, SCHED_RR, &s); + if (ret != 0) + printk(KERN_WARNING "MUX: set priority failed!\n"); + } + return 0; +} + +/*For BP UART problem End*/ + +static void receive_worker(struct sprd_mux *self, int start) +{ + int count; + unsigned char *search, *to, *from; + short_frame *short_pkt; + long_frame *long_pkt; + /*For BP UART problem Begin */ + __u32 crc_error; + __u8 *uih_data_start; + __u32 uih_len; + /*For BP UART problem End */ + int i; + + if (!self || !self->io_hal || !self->io_hal->io_read) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + if (start) { + memset(self->tbuf, 0, TS0710MUX_MAX_BUF_SIZE); + self->tbuf_ptr = self->tbuf; + self->start_flag = 0; + } + + for (i = MUX_VETH_LINE_BEGIN; i <= MUX_VETH_LINE_END; i++) { + if (self->callback[i].func) { + (*self->callback[i].func)(i, SPRDMUX_EVENT_READ_IND, NULL, 0, self->callback[i].user_data); + } + } + + count = self->io_hal->io_read(self->tbuf_ptr, TS0710MUX_MAX_BUF_SIZE - (self->tbuf_ptr - self->tbuf)); + if (count <= 0 || self->cmux_mode == 0) { + return; + } + + self->tbuf_ptr += count; + + if ((self->start_flag != 0) && (self->framelen != -1)) { + if ((self->tbuf_ptr - self->start_flag) < self->framelen) { + return; + } + } + + search = &self->tbuf[0]; + while (1) { + if (self->start_flag == 0) { /* Frame Start Flag not found */ + self->framelen = -1; + while (search < self->tbuf_ptr) { + if (*search == TS0710_BASIC_FLAG) { + + self->start_flag = search; + break; + } +#ifdef TS0710LOG + else { + TS0710_PRINTK(">S %02x %c\n", *search, *search); + } +#endif + search++; + } + + if (self->start_flag == 0) { + self->tbuf_ptr = &self->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 ((self->framelen == -1) && ((self->tbuf_ptr - self->start_flag) > (TS0710_MAX_HDR_SIZE + SEQ_FIELD_SIZE))) { /*For BP UART problem */ + /*short_pkt = (short_frame *) (start_flag + 1); */ + short_pkt = (short_frame *) (self->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; */ + self->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 *) (self->start_flag + ADDRESS_FIELD_OFFSET); /*For BP UART problem */ + /*framelen = TS0710_MAX_HDR_SIZE + GET_LONG_LENGTH( long_pkt->h.length ) + 2; */ + self->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 (self->framelen > (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)) { /*For BP UART problem */ + MUX_TS0710_LOGSTR_FRAME(self->mux_id, 0, self->start_flag, (self->tbuf_ptr - self->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__, self->framelen, (TS0710MUX_MAX_TOTAL_FRAME_SIZE + SEQ_FIELD_SIZE)); /*For BP UART problem */ + search = self->start_flag + 1; + self->start_flag = 0; + self->framelen = -1; + continue; + } + } + + if ((self->framelen != -1) + && ((self->tbuf_ptr - self->start_flag) >= self->framelen)) { + if (*(self->start_flag + self->framelen - 1) == TS0710_BASIC_FLAG) { /* OK, We got one frame */ + + /*For BP UART problem Begin */ + + MUX_TS0710_LOGSTR_FRAME(self->mux_id, 0, self->start_flag, self->framelen); + MUX_TS0710_DEBUGHEX(self->mux_id, self->start_flag, self->framelen); + short_pkt = (short_frame *) (self->start_flag + ADDRESS_FIELD_OFFSET); + if ((short_pkt->h.length.ea) == 0) { + long_pkt = (long_frame *) (self->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*) (self->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*) (self->start_flag + SLIDE_BP_SEQ_OFFSET), SHORT_CRC_CHECK + 1, *(uih_data_start + uih_len)); + } + + if (!crc_error) { + if (1) /*expect_seq == *(start_flag + SLIDE_BP_SEQ_OFFSET)*/ + { + self->expect_seq = *(self->start_flag + SLIDE_BP_SEQ_OFFSET); + self->expect_seq++; + if (self->expect_seq >= 4){ + self->expect_seq = 0; + } +#ifdef TS0710SEQ2 + send_ack(self->connection, self->expect_seq, *(self->start_flag + FIRST_BP_SEQ_OFFSET), *(self->start_flag + SECOND_BP_SEQ_OFFSET)); +#else + send_ack(self->connection, self->expect_seq); +#endif + + ts0710_recv_data(self->connection, self->start_flag + ADDRESS_FIELD_OFFSET, self->framelen - 2 - SEQ_FIELD_SIZE); + } else { + +#ifdef TS0710DEBUG + if (*(self->start_flag + SLIDE_BP_SEQ_OFFSET) != 0x9F) { +#endif + + TS0710_PRINTK("MUX sequence number %d is not expected %d, discard data!\n", *(self->start_flag + SLIDE_BP_SEQ_OFFSET), self->expect_seq); + +#ifdef TS0710SEQ2 + send_ack(self->connection, self->expect_seq, *(self->start_flag + FIRST_BP_SEQ_OFFSET), *(self->start_flag + SECOND_BP_SEQ_OFFSET)); +#else + send_ack(self->connection, self->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 = self->start_flag + 1; + self->start_flag = 0; + self->framelen = -1; + continue; + } /*End if(!crc_error) */ + + search = self->start_flag + self->framelen; + } else { + MUX_TS0710_LOGSTR_FRAME(self->mux_id, 0, self->start_flag, self->framelen); + MUX_TS0710_DEBUGHEX(self->mux_id, self->start_flag, self->framelen); + TS0710_PRINTK("MUX: Lost synchronization!\n"); + search = self->start_flag + 1; + } + + self->start_flag = 0; + self->framelen = -1; + continue; + } + + if (self->start_flag != &self->tbuf[0]) { + + to = self->tbuf; + from = self->start_flag; + count = self->tbuf_ptr - self->start_flag; + while (count--) { + *to++ = *from++; + } + + self->tbuf_ptr -= (self->start_flag - self->tbuf); + self->start_flag = self->tbuf; + + } + break; + } /* End Frame Start Flag found */ + } /* End while(1) */ +} + +static int mux_receive_thread(void *data) +{ + int start = 1; + struct sprd_mux *self = data; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return 0; + } + + printk(KERN_ERR "MUX: id = %d %s entered\n", self->mux_id, __FUNCTION__); + + mux_set_thread_pro(95); + while (!kthread_should_stop()) { + if (self->mux_exiting == 1) { + self->mux_exiting = 2; + return 0; + } + + wait_event(self->handshake_ready, self->cmux_mode == 1); + + receive_worker(self, start); + start = 0; + } + return 0; +} + +static int mux_send_thread(void *private_) +{ + ts0710_con *ts0710; + __u8 j; + mux_send_struct *send_info; + __u8 dlci; + struct sprd_mux *self; + __u32 ring_index; + __u32 num; + + self = (struct sprd_mux *)(private_); + + if (!self) { + printk(KERN_ERR "MUX: Error %s Self is NULL\n", __FUNCTION__); + return 0; + } + + printk(KERN_ERR "MUX: id = %d %s entered\n", self->mux_id, __FUNCTION__); + + ts0710 = self->connection; + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return 0; + } + + if (ts0710->dlci[0].state == FLOW_STOPPED) { + MUX_TS0710_DEBUG(self->mux_id, "Flow stopped on all channels\n"); + return 0; + } + + mux_set_thread_pro(80); + + while (!kthread_should_stop()) { + + wait_for_completion_interruptible(&self->send_completion); + for (j = 0; j < NR_MUXS; j++) { + wait_event(self->modem_ready, self->mux_status == MUX_STATE_READY); + + if (!(self->mux_send_info_flags[j])) { + continue; + } + + send_info = self->mux_send_info[j]; + if (!send_info) { + continue; + } + + if (send_info->write_index == send_info->read_index) { + continue; + } + + dlci = line2dlci[j]; + if (ts0710->dlci[dlci].state == FLOW_STOPPED) { + MUX_TS0710_DEBUG(self->mux_id, "Flow stopped on channel DLCI: %d\n", dlci); + continue; + } else if (ts0710->dlci[dlci].state != CONNECTED) { + MUX_TS0710_DEBUG(self->mux_id, "DLCI %d not connected\n", dlci); + send_info->read_index = send_info->write_index; + wake_up_interruptible(&send_info->tx_wait); + continue; + } + + while(send_info->write_index != send_info->read_index) { + ring_index = send_info->read_index % ringbuf_num[self->mux_id][j]; + MUX_TS0710_DEBUG(self->mux_id, "Send queued UIH for /dev/mux%d ring_index = %d, len = %d", j,ring_index, send_info->length[ring_index]); + + if (send_info->length[ring_index] <= TS0710MUX_SERIAL_BUF_SIZE) { + if (basic_write(ts0710, (__u8 *) send_info->frame[ring_index], send_info->length[ring_index]) <= 0 ) { + printk("[%s]: %s /dev/mux%d index = %d basic_write fail!\n", sprd_mux_mgr[self->mux_id].mux_name, __FUNCTION__, j, ring_index); + break; + } + } else { + printk("[%s]: %s /dev/mux%d send length is exceed %d\n", sprd_mux_mgr[self->mux_id].mux_name, __FUNCTION__, j, send_info->length[ring_index]); + } + send_info->length[ring_index] = 0; + send_info->read_index++; + num = send_info->write_index - send_info->read_index; + if (send_info->need_notify || 0 == num) { + if (num <= ringbuf_num[self->mux_id][j]/2) { + send_info->need_notify = false; +// printk("[%s]: %s /dev/mux%d num = %d, cnt = %u\n", sprd_mux_mgr[self->mux_id].mux_name, __FUNCTION__, j, num, cnt++); + if (self->callback[j].func) { + (*self->callback[j].func)(j, SPRDMUX_EVENT_COMPLETE_WRITE, (__u8 *)send_info->frame, 0, self->callback[j].user_data); + } + } + } + } + + wake_up_interruptible(&send_info->tx_wait); + } /* End for() loop */ + + } + return 0; +} + + +ssize_t mux_proc_read(struct file *file, char __user *buf, size_t size, loff_t *ppos) +{ + return seq_read(file, buf, size, ppos); +} + +static int mux_proc_show(struct seq_file *seq, void *v) +{ + char mux_buf[MUX_MODE_MAX_LEN + 1]; + + memset(mux_buf, 0, sizeof(mux_buf)); + snprintf(mux_buf, MUX_MODE_MAX_LEN, "%d\n", mux_mode); + seq_puts(seq, mux_buf); + + return 0; +} +static int mux_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, mux_proc_show, inode->i_private); +} + +static ssize_t mux_proc_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) +{ + char mux_buf[len + 1]; + int val; + + memset(mux_buf, 0, len + 1); + if (len > 0) { + if (copy_from_user(mux_buf, buf, len)) { + printk(KERN_ERR "MUX: Error %s failed to copy from user!\n", __FUNCTION__); + return -EFAULT; + + } + + val = simple_strtoul(mux_buf, NULL, 10); + mux_mode = val; + } + + return len; +} + +static int mux_create_proc(void) +{ + struct proc_dir_entry *mux_entry; + static const struct file_operations mux_fops = { + .owner = THIS_MODULE, + .open = mux_proc_open, + .read = mux_proc_read, + .write = mux_proc_write, + .llseek = seq_lseek, + .release = single_release, + }; + + mux_entry = proc_create("mux_mode", 0666, NULL, &mux_fops); + if (!mux_entry) { + printk(KERN_ERR "MUX: Error Can not create mux proc entry\n"); + return -ENOMEM; + } + + return 0; +} + +static void mux_set_ringbuf_num(void) +{ + int mux_id; + int line; + + for(mux_id = SPRDMUX_ID_SPI; mux_id < SPRDMUX_ID_MAX; mux_id++) { + for(line = 0; line < NR_MUXS; line++) { + if (mux_id == SPRDMUX_ID_SDIO && (line >= MUX_VETH_LINE_BEGIN && line <=MUX_VETH_LINE_END)) { + ringbuf_num[mux_id][line] = MUX_VETH_RINGBUFER_NUM; + } else { + /* Default */ + ringbuf_num[mux_id][line] = 1; + } + } + } +} + +static int mux_line_state(SPRDMUX_ID_E mux_id, int line) +{ + struct sprd_mux *self = NULL; + mux_send_struct *send_info; + + self = sprd_mux_mgr[mux_id].handle; + + send_info = self->mux_send_info[line]; + + if (!send_info) { + printk(KERN_ERR "MUX Error: %s: mux_send_info[%d][%d] == 0\n", __FUNCTION__, mux_id, line); + return 1; + } + + if (send_info->write_index - send_info->read_index >= ringbuf_num[mux_id][line]) { + /* No idle ring buffer */ + return 1; + } + + return 0; +} + +int sprdmux_line_busy(SPRDMUX_ID_E mux_id, int line) +{ + return mux_line_state(mux_id, line); +} + +void sprdmux_set_line_notify(SPRDMUX_ID_E mux_id, int line, __u8 notify) + +{ + struct sprd_mux *self = NULL; + mux_send_struct *send_info; + + self = sprd_mux_mgr[mux_id].handle; + + send_info = self->mux_send_info[line]; + + if (!send_info) { + printk(KERN_ERR "MUX Error: %s: mux_send_info[%d][%d] == 0\n", __FUNCTION__, mux_id, line); + return; + } + + send_info->need_notify = notify; +} + +static mux_send_struct * mux_alloc_send_info(SPRDMUX_ID_E mux_id, int line) +{ + mux_send_struct *send_info_ptr = NULL; + + send_info_ptr = (mux_send_struct *) kzalloc(sizeof(mux_send_struct), GFP_KERNEL); + if (!send_info_ptr) { + return NULL; + } + + send_info_ptr->buf = (__u8 *)vmalloc(sizeof(__u8) * TS0710MUX_SEND_BUF_SIZE * ringbuf_num[mux_id][line]); + if (!send_info_ptr->buf) { + kfree(send_info_ptr); + return NULL; + } + + send_info_ptr->length = (__u16 *)kzalloc(sizeof(__u16) * ringbuf_num[mux_id][line], GFP_KERNEL); + if (!send_info_ptr->length) { + vfree(send_info_ptr->buf); + kfree(send_info_ptr); + return NULL; + } + + send_info_ptr->frame = (__u8 **)kzalloc(sizeof(__u8 *) * ringbuf_num[mux_id][line], GFP_KERNEL); + + if (!send_info_ptr->frame) { + kfree(send_info_ptr->length); + vfree(send_info_ptr->buf); + kfree(send_info_ptr); + return NULL; + } + + return send_info_ptr; +} + +static void mux_free_send_info(mux_send_struct * send_info) +{ + if (!send_info) { + return; + } + + if (send_info->frame) { + kfree(send_info->frame); + send_info->frame = NULL; + } + + if (send_info->length) { + kfree(send_info->length); + send_info->length = NULL; + } + + if (send_info->buf) { + vfree(send_info->buf); + send_info->buf = NULL; + + } + + kfree(send_info); + send_info = NULL; +} + +static void mux_remove_proc(void) +{ + remove_proc_entry("mux_mode", NULL); /* remove /proc/mux_mode */ +} + +int ts0710_mux_init(void) +{ + create_crctable(crctable); + +// mux_mgr_init(sprd_mux_mgr); + + if (mux_create_proc()) { + printk(KERN_ERR "MUX: Error %s create mux proc interface failed!\n", __FUNCTION__); + } + + mux_set_ringbuf_num(); + return 0; +} + +void ts0710_mux_exit(void) +{ + mux_remove_proc(); + mux_mgr_init(sprd_mux_mgr); +} + +int ts0710_mux_create(int mux_id) +{ + struct sprd_mux *self = NULL; + int j, rval = 0; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX) { + printk(KERN_ERR "MUX: Error %sInvalid Param\n",__FUNCTION__); + rval = -EINVAL; + goto err_iomux; + } + + self = (struct sprd_mux *)kzalloc(sizeof(struct sprd_mux), GFP_KERNEL); + + if (!self) { + printk(KERN_ERR "MUX: Error %s no memory for self\n",__FUNCTION__); + rval = -ENOMEM; + goto err_sprd_mux; + } + + self->mux_id = mux_id; + + self->connection = (ts0710_con *)kzalloc(sizeof(ts0710_con), GFP_KERNEL); + + if (!self->connection) { + printk(KERN_ERR "MUX: Error %s no memory for connection\n",__FUNCTION__); + rval = -ENOMEM; + goto err_conn; + } + + self->connection->user_data = (void *)self; + + ts0710_init(self->connection); + + ts0710_init_waitqueue(self->connection); + + mutex_init(&self->handshake_mutex); + + for (j = 0; j < NR_MUXS; j++) { + self->mux_send_info_flags[j] = 0; + self->mux_send_info[j] = 0; + self->mux_recv_info_flags[j] = 0; + self->mux_recv_info[j] = 0; + self->open_count[j] = 0; + + mutex_init(&self->open_mutex[j]); + } + + self->framelen = -1; + self->expect_seq = 0; + self->mux_status = MUX_STATE_NOT_READY; + + init_completion(&self->send_completion); + init_waitqueue_head(&self->handshake_ready); + init_waitqueue_head(&self->modem_ready); + + /*create mux_send thread*/ + self->mux_send_kthread = kthread_create(mux_send_thread, self, "mux_send"); + if(IS_ERR(self->mux_send_kthread)) { + printk(KERN_ERR "MUX: Error %s Unable to create mux_send thread err = %ld\n", __FUNCTION__, PTR_ERR(self->mux_send_kthread)); + rval = PTR_ERR(self->mux_send_kthread); + goto err_send_thread; + } + + wake_up_process(self->mux_send_kthread); + + /*create receive thread*/ + self->mux_recv_kthread = kthread_create(mux_receive_thread, self, "mux_receive"); + if(IS_ERR(self->mux_recv_kthread)) { + printk(KERN_ERR "MUX: Error %s Unable to create mux_receive thread err = %ld\n", __FUNCTION__, PTR_ERR(self->mux_recv_kthread)); + rval = PTR_ERR(self->mux_recv_kthread); + goto err_recv_thread; + } + wake_up_process(self->mux_recv_kthread); + + self->io_hal = &sprd_mux_mgr[mux_id].mux; + sprd_mux_mgr[mux_id].handle = self; + + return rval; + +err_recv_thread: + kthread_stop(self->mux_send_kthread); + self->mux_send_kthread = NULL; +err_send_thread: + kfree(self->connection); +err_conn: + kfree(self); +err_sprd_mux: +err_iomux: + printk(KERN_ERR "MUX: Error %s Create Failed \n",__FUNCTION__); + + return rval; +} + + + + +void ts0710_mux_destory(int mux_id) +{ + int j; + struct sprd_mux *self = NULL; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX) { + printk(KERN_ERR "MUX: Error %sInvalid Param\n",__FUNCTION__); + return; + } + + self = sprd_mux_mgr[mux_id].handle; + + if (!self) { + printk(KERN_ERR "MUX: Error %s Invalid Param self = %x\n", __FUNCTION__, (int)self); + return; + } + + for (j = 0; j < NR_MUXS; j++) { + if (self->mux_send_info_flags[j]) { + mux_free_send_info(self->mux_send_info[j]); + self->mux_send_info_flags[j] = 0; + } + + if ((self->mux_recv_info_flags[j]) && (self->mux_recv_info[j])) { + free_mux_recv_struct(self->mux_recv_info[j]); + } + self->mux_recv_info_flags[j] = 0; + self->mux_recv_info[j] = 0; + } + self->mux_exiting = 1; + + ts0710_close_channel(self->connection, 0); + TS0710_SIG2APLOGD(); + self->cmux_mode = 0; + + if (!self->io_hal || !self->io_hal->io_stop) { + self->io_hal->io_stop(SPRDMUX_READ); + } + + while (self->mux_exiting == 1) { + msleep(10); + } + + if(self->mux_recv_kthread) { + kthread_stop(self->mux_recv_kthread); + self->mux_recv_kthread = NULL; + } + + if(self->mux_send_kthread) { + kthread_stop(self->mux_send_kthread); + self->mux_send_kthread = NULL; + } + + if (self->connection) { + kfree(self->connection); + } + + if (self->mux_recover_kthread) { + kthread_stop(self->mux_recover_kthread); + self->mux_recover_kthread = NULL; + } + kfree(self); +} + +static void mux_print_mux_mgr(void) +{ + int j = 0; + + printk(KERN_INFO "No MUX_ID Name Handle\n"); + + for (j = 0; j < SPRDMUX_MAX_NUM; j++) { + printk(KERN_INFO "%d %d %s %d\n", j, sprd_mux_mgr[j].mux_id, + sprd_mux_mgr[j].mux_name, (uint32_t)sprd_mux_mgr[j].handle); + } +} +static void mux_mgr_init(mux_info *mux_mgr) +{ + int j = 0; + mux_print_mux_mgr(); + + if (mux_mgr) { + for (j = 0; j < SPRDMUX_MAX_NUM; j++) { + mux_mgr[j].handle = NULL; + } + } +} + +int sprdmux_register(struct sprdmux *mux) +{ + if (!mux || mux->id < 0 || mux->id >= SPRDMUX_ID_MAX ) { + printk(KERN_ERR "MUX: %s Invalid Param\n", __FUNCTION__); + return -EINVAL; + } + + memcpy(&sprd_mux_mgr[mux->id].mux, mux, sizeof(struct sprdmux)); + + printk(KERN_INFO "ts0710MUX[%d] 0x%x, 0x%x 0x%x\n", mux->id, (unsigned int)mux->io_write, (unsigned int)mux->io_read, (unsigned int)mux->io_stop); + + ts0710_mux_create(mux->id); + + return 0; +} + +void sprdmux_unregister(SPRDMUX_ID_E mux_id) +{ + if (mux_id >= SPRDMUX_ID_MAX || mux_id < 0) { + printk(KERN_ERR "MUX: Error %s Invalid Param mux_id = %d\n", __FUNCTION__, mux_id); + return; + } + + memset(&sprd_mux_mgr[mux_id].mux, 0, sizeof(struct sprdmux)); + ts0710_mux_destory(mux_id); +} + +int sprdmux_open(SPRDMUX_ID_E mux_id, int index) +{ + int rval = 0; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX || index < 0 || index >= NR_MUXS) { + printk(KERN_ERR "MUX: Error %s Invalid Param mux_id = %d, index = %d\n", __FUNCTION__, mux_id, index); + return -EINVAL; + } + + rval = ts0710_mux_status(mux_id); + + if (rval != 0) { + printk(KERN_ERR "MUX: Error %s [%d][%d] mux_status is Not OK\n", __FUNCTION__, mux_id, index); + return rval; + } + + return ts0710_mux_open(mux_id, index); +} + +void sprdmux_close(SPRDMUX_ID_E mux_id, int index) +{ + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX || index < 0 || index >= NR_MUXS) { + printk(KERN_ERR "MUX: Error %s Invalid Param mux_id = %d, index = %d\n", __FUNCTION__, mux_id, index); + return; + } + + if (ts0710_mux_status(mux_id) != 0) { + printk(KERN_ERR "MUX: Error %s [%d][%d] mux_status is Not OK\n", __FUNCTION__, mux_id, index); + return; + } + + ts0710_mux_close(mux_id, index); +} + +int sprdmux_write(SPRDMUX_ID_E mux_id, int index, const unsigned char *buf, int count) +{ + int rval = 0; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX || index < 0 || index >= NR_MUXS) { + printk(KERN_ERR "MUX: Error %s Invalid Param mux_id = %d, index = %d\n", __FUNCTION__, mux_id, index); + return -EINVAL; + } + + rval = ts0710_mux_status(mux_id); + + if (rval != 0) { + printk(KERN_ERR "MUX: Error %s [%d][%d] mux_status is Not OK\n", __FUNCTION__, mux_id, index); + return rval; + } + + return ts0710_mux_write(mux_id, index, buf, count, 0); +} + +int sprdmux_register_notify_callback(SPRDMUX_ID_E mux_id, struct sprdmux_notify *notify) +{ + struct sprd_mux *self = NULL; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX || !notify || notify->index >= NR_MUXS) { + printk(KERN_ERR "MUX: Error %s Invalid Param\n", __FUNCTION__); + return -EINVAL; + } + + self = sprd_mux_mgr[mux_id].handle; + + if (self) { + if (!self->callback[notify->index].func) { + self->callback[notify->index].func = notify->func; + self->callback[notify->index].user_data = notify->user_data; + } + + return 0; + } + + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return -ENODEV; +} + +static void mux_display_recv_info(const char * tag, int mux_id, int line) +{ + struct sprd_mux *self = NULL; + mux_recv_struct *recv_info; + int sum = 0; + mux_recv_packet *recv_packet; + + if (mux_id < 0 || mux_id >= SPRDMUX_ID_MAX) { + printk(KERN_ERR "MUX: Error %s %sInvalid Param mux_id = %d\n", __FUNCTION__, tag, mux_id); + return; + } + + if ((line < 0) || (line >= NR_MUXS)) { + printk(KERN_ERR "MUX: Error %s %sline[%d] is wrong\n", __FUNCTION__, tag, line); + return; + + } + + self = sprd_mux_mgr[mux_id].handle; + + if (!self) { + printk(KERN_ERR "MUX[%d]: Error %s %s self is NULL\n", mux_id, __FUNCTION__, tag); + mux_print_mux_mgr(); + return; + } + + recv_info = self->mux_recv_info[line]; + + if (recv_info == NULL){ + printk(KERN_ERR "MUX: Error %s %s mux_recv_info is NULL\n", __FUNCTION__, tag); + return; + } + + sum += recv_info->length; + + while((recv_packet = recv_info->mux_packet) != NULL) { + sum += recv_packet->length; + recv_packet = recv_packet->next; + } + + if (sum != recv_info->total || sum != self->check_read_sum[line]) { + printk(KERN_ERR "MUX: Error %s %s mux_id = %d, line = %d, total = %d, sum = %d check = %d len = %d\n", __FUNCTION__, tag, mux_id, line, recv_info->total, sum, self->check_read_sum[line], recv_info->length); + while((recv_packet = recv_info->mux_packet) != NULL) { + sum += recv_packet->length; + recv_packet = recv_packet->next; + printk(KERN_ERR "MUX: Error %s %s recv_packet->length = %d\n", __FUNCTION__, tag, recv_packet->length); + } + } +} + +int ts0710_mux_status(int mux_id) +{ + if (mux_id >= SPRDMUX_ID_MAX || mux_id < 0) { + printk(KERN_ERR "MUX: Error %s [%d] Invalid Param\n", __FUNCTION__, mux_id); + return -EINVAL; + } + + if (sprd_mux_mgr[mux_id].handle && sprd_mux_mgr[mux_id].mux_id == mux_id + && sprd_mux_mgr[mux_id].mux.io_read != NULL && sprd_mux_mgr[mux_id].mux.io_write!= NULL && sprd_mux_mgr[mux_id].mux.io_stop!= NULL) { + + return 0; + } + + printk(KERN_ERR "MUX: Error %s [%d] status is Not OK\n", __FUNCTION__, mux_id); + + return -ENODEV; +} + +static int mux_handshake(struct sprd_mux *self) +{ + char buffer[256]; + char *buff = buffer; + int count, i = 0; + + MUX_TS0710_DEBUG(self->mux_id, "cmux_mode = %d\n", self->cmux_mode); + + if (self->cmux_mode == 1) { + printk(KERN_ERR "MUX: %s mux_mode is incorrect!\n", __FUNCTION__); + return 0; + } + + memset(buffer, 0, 256); + printk(KERN_INFO "\n cmux say hello >, id %d \n", self->mux_id); + + if (mux_mode == 1) { + count = self->io_hal->io_write("AT+SMMSWAP=0\r", strlen("AT+SMMSWAP=0\r")); + } else { + count = self->io_hal->io_write("AT\r", strlen("AT\r")); + } + + if (count < 0) { + printk(KERN_INFO "\n MUX: id = %d,cmux write stoped for crash\n", self->mux_id); + return -1; + } + + /*wait for response "OK \r" */ + printk(KERN_INFO "\n MUX: id = %d,cmux receiving\n", self->mux_id); + mux_init_timer(self); + while (1) { + mux_start_timer(self); + count = self->io_hal->io_read(buff, sizeof(buffer) - (buff - buffer)); + mux_stop_timer(self); + printk(KERN_INFO "MUX: id = %d,ts mux received %d chars\n", self->mux_id, count); + if(count > 0) { + buff += count; + if (findInBuf(buffer, 256, "OK")) { + break; + } else if (findInBuf(buffer, 256, "ERROR")) { + printk(KERN_INFO "\n MUX: id = %d,wrong modem state !!!!\n", self->mux_id); + break; + } + } else if (count == 0){ + msleep(200); + continue; + } else { + if (self->mux_status == MUX_STATE_CRASHED) { + printk(KERN_INFO "\n MUX: id = %d,cmux read stoped for crash\n", self->mux_id); + return -1; + } + msleep(2000); + if (mux_mode == 1) { + count = self->io_hal->io_write("AT+SMMSWAP=0\r", strlen("AT+SMMSWAP=0\r")); + } else { + count = self->io_hal->io_write("AT\r", strlen("AT\r")); + } + if (count < 0 && self->mux_status == MUX_STATE_CRASHED) { + printk(KERN_INFO "\n MUX: id = %d,cmux read stoped for crash\n", self->mux_id); + return -1; + } + i++; + } + if (i > 5) { + printk(KERN_WARNING "\n MUX: id = %d,wrong modem state !!!!\n", self->mux_id); + return -1; + } + } + + count = self->io_hal->io_write("at+cmux=0\r", strlen("at+cmux=0\r")); + if (count < 0) { + printk(KERN_INFO "\n MUX: id = %d,cmux write stoped\n", self->mux_id); + return -1; + } + + count = self->io_hal->io_read(buffer, sizeof(buffer)); + if (count < 0) { + printk(KERN_INFO "\n MUX: id = %d,cmux read stoped\n", self->mux_id); + return -1; + } + + printk(KERN_INFO "MUX: id = %d, handshake OK\n", self->mux_id); + + return 0; +} + +void mux_ipc_enable(__u8 is_enable) +{ + int j; + struct sprd_mux *self; + + printk(KERN_INFO "MUX: %s entered enable = %d\n", __FUNCTION__, is_enable); + + for (j = 0; j <sizeof(sprd_mux_mgr)/sizeof(sprd_mux_mgr[0]); j++) { + if (sprd_mux_mgr[j].handle) { + self = sprd_mux_mgr[j].handle; + + MUX_TS0710_DEBUG(self->mux_id, "enable = %d old state = %d\n", is_enable, self->mux_status); + + if (is_enable) { + if (self->mux_status == MUX_STATE_NOT_READY) { + self->mux_status = MUX_STATE_READY; + wake_up_all(&self->modem_ready); + } else if (self->mux_status == MUX_STATE_CRASHED) { + mux_recover(self); + } else { + printk(KERN_ERR "MUX: %s wrong enable state \n", __FUNCTION__); + } + } else { + if (self->mux_status == MUX_STATE_NOT_READY) { + ;//TODO: + } else { + mux_stop(self); + } + } + } + } +} + +static void mux_wakeup_all_read(struct sprd_mux *self) +{ + int j; + mux_recv_struct *recv_info; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return ; + } + + for (j = 0; j < NR_MUXS; j++) { + if (!(self->mux_recv_info_flags[j])) { + continue; + } + + recv_info = self->mux_recv_info[j]; + if (!recv_info) { + continue; + } + + wake_up_interruptible(&recv_info->rx_wait); + } +} + +static void mux_wakeup_all_write(struct sprd_mux *self) +{ + int j; + mux_send_struct *send_info; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return ; + } + + for (j = 0; j < NR_MUXS; j++) { + if (!(self->mux_send_info_flags[j])) { + continue; + } + + send_info = self->mux_send_info[j]; + if (!send_info) { + continue; + } + + wake_up_interruptible(&send_info->tx_wait); + } + +} + +static void mux_wakeup_all_opening(struct sprd_mux *self) +{ + int j; + ts0710_con *ts0710; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return ; + } + + ts0710 = self->connection; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + for (j = 0; j < TS0710_MAX_CHN; j++) { + if (ts0710->dlci[j].state == CONNECTING) { + wake_up_interruptible(&ts0710->dlci[j].open_wait); + } + } +} + +static void mux_wakeup_all_closing(struct sprd_mux *self) +{ + int j; + ts0710_con *ts0710; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return ; + } + + ts0710 = self->connection; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + for (j = 0; j < TS0710_MAX_CHN; j++) { + if (ts0710->dlci[j].state == DISCONNECTING) { + wake_up_interruptible(&ts0710->dlci[j].close_wait); + } + } +} + +static void mux_wakeup_all_wait(struct sprd_mux *self) +{ + mux_wakeup_all_read(self); + mux_wakeup_all_write(self); + mux_wakeup_all_opening(self); + mux_wakeup_all_closing(self); + + if (self && self->connection) { + wake_up_interruptible(&self->connection->test_wait); + } +} + +static void mux_tidy_buff(struct sprd_mux *self) +{ + int j; + int k; + mux_send_struct *send_info; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return ; + } + + for (j = 0; j < NR_MUXS; j++) { + if (!(self->mux_send_info_flags[j])) { + continue; + } + + send_info = self->mux_send_info[j]; + if (!send_info) { + continue; + } + + for (k = 0; k < ringbuf_num[self->mux_id][j]; k++) { + send_info->length[k] = 0; + } + + send_info->read_index = 0; + send_info->write_index = 0; + } + + memset(self->tbuf, 0, TS0710MUX_MAX_BUF_SIZE); + self->tbuf_ptr = self->tbuf; + self->start_flag = 0; + self->framelen = -1; + + return; +} + +static void mux_recover(struct sprd_mux *self) +{ + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + MUX_TS0710_DEBUG(self->mux_id, "entered\n"); + + self->mux_status = MUX_STATE_RECOVERING; + + if (self->mux_recover_kthread) { + kthread_stop(self->mux_recover_kthread); + schedule(); + self->mux_recover_kthread = NULL; + } + + self->mux_recover_kthread = kthread_create(mux_recover_thread, self, "mux_recover"); + + if(IS_ERR(self->mux_recover_kthread)) { + printk(KERN_ERR "MUX: Error %s Unable to create mux_recover_kthread thread err = %ld\n", __FUNCTION__, PTR_ERR(self->mux_recover_kthread)); + + } + + mux_tidy_buff(self); + + wake_up_process(self->mux_recover_kthread); +} + +static int mux_recover_thread(void *data) +{ + struct sprd_mux *self = data; + + if (!self) { + printk(KERN_ERR "MUX: %s self is NULL\n", __FUNCTION__); + return 0; + } + + printk(KERN_ERR "MUX: id[%d] %s entered\n", self->mux_id, __FUNCTION__); + + if (self->cmux_mode == 0) { + mutex_lock(&self->handshake_mutex); + if (mux_handshake(self) != 0) { + printk(KERN_ERR "MUX: id[%d] %s handshake fail\n", self->mux_id, __FUNCTION__); + mutex_unlock(&self->handshake_mutex); + self->mux_recover_kthread = NULL; + return 0; + } + + self->cmux_mode = 1; + wake_up(&self->handshake_ready); + mutex_unlock(&self->handshake_mutex); + while(!kthread_should_stop()) { + if (mux_restore_channel(self->connection) == 0) { + self->mux_status = MUX_STATE_READY; + + wake_up_all(&self->modem_ready); + mux_wakeup_all_read(self); + mux_wakeup_all_write(self); + self->mux_recover_kthread = NULL; + printk(KERN_ERR "MUX: id[%d] %s recover successed\n", self->mux_id, __FUNCTION__); + break; + } else { + if (self->mux_status == MUX_STATE_CRASHED) { + //another CP disable occured + printk(KERN_ERR "MUX: id[%d] %s out anoter disable occured\n", self->mux_id, __FUNCTION__); + break; + } + printk(KERN_ERR "MUX: id[%d] %s recover failed retry\n", self->mux_id, __FUNCTION__); + msleep(500); + } + } + } + + printk(KERN_ERR "MUX: id[%d] %s out\n", self->mux_id, __FUNCTION__); + + return 0; +} + +static void mux_stop(struct sprd_mux *self) +{ + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + MUX_TS0710_DEBUG(self->mux_id, "entered\n"); + + self->mux_status = MUX_STATE_CRASHED; + self->cmux_mode = 0; + self->io_hal->io_stop(SPRDMUX_ALL); + +// mux_wakeup_all_read(self); +// mux_wakeup_all_write(self); + mux_wakeup_all_opening(self); + mux_wakeup_all_closing(self); + + if (self->connection) { + wake_up_interruptible(&self->connection->test_wait); + } + + if (self->mux_recover_kthread) { + kthread_stop(self->mux_recover_kthread); + schedule(); + self->mux_recover_kthread = NULL; + } + + return; +} + +static void mux_wakeup_channel(ts0710_con * ts0710) +{ + __u8 j; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + for (j = 0; j < TS0710_MAX_CHN; j++) { + if (ts0710->dlci[j].state == CONNECTING) { + wake_up_interruptible(&ts0710->dlci[j].open_wait); + } + } + + for (j = 0; j < TS0710_MAX_CHN; j++) { + if (ts0710->dlci[j].state == DISCONNECTING) { + wake_up_interruptible(&ts0710->dlci[j].close_wait); + } + } +} + +static int mux_restore_channel(ts0710_con * ts0710) +{ + __u8 j; + __u8 line; + struct sprd_mux *self = NULL; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return -1; + } + + self = (struct sprd_mux *)ts0710->user_data; + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return -1; + } + + mux_display_connection(ts0710, "before restore"); + + if (ts0710_ctrl_channel_status(self->connection) != 0) { + printk(KERN_ERR "MUX: Error %s ctrl channel status is Not OK\n", __FUNCTION__); + return -1; + } + + for (j = 0; j < TS0710_MAX_CHN; j++) { + line = dlci2line[j].dataline; + if (ts0710->dlci[j].state == CONNECTING || + ts0710->dlci[j].state == NEGOTIATING|| + ts0710->dlci[j].state == CONNECTED || + self->open_count[line] != 0) { + ts0710->dlci[j].state = DISCONNECTED; + if (ts0710_open_channel(ts0710, j) != 0) { + printk(KERN_ERR "MUX: Error %s failed\n", __FUNCTION__); + return -1; + } + } + } + + mux_display_connection(ts0710, "after restore"); + + return 0; +} + +static void mux_display_connection(ts0710_con * ts0710, const char *tag) +{ +#ifdef TS0710DEBUG + __u8 j; + struct sprd_mux *self = NULL; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return; + } + + self = (struct sprd_mux *)ts0710->user_data; + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + for (j = 0; j < TS0710_MAX_CHN; j++) { + printk(KERN_ERR "MUX: %s id[%d] dlci[%d]'s state = %d\n", tag, self->mux_id, j, ts0710->dlci[j].state); + } +#endif + return; +} + +static void mux_init_timer(struct sprd_mux *self) +{ + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + init_timer(&self->watch_timer); +} + +static void mux_start_timer(struct sprd_mux *self) +{ + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + self->watch_timer.expires = jiffies + MUX_WATCH_INTERVAL; + self->watch_timer.data = (unsigned long)self; + self->watch_timer.function = mux_wathch_check; + + add_timer(&self->watch_timer); +} + +static void mux_wathch_check(unsigned long priv) +{ + struct sprd_mux *self = (struct sprd_mux *) priv; + + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + printk(KERN_ERR "MUX: %s called\n", __FUNCTION__); + + if (self->cmux_mode == 0) { + self->io_hal->io_stop(self->mux_id); + } +} + +static void mux_stop_timer(struct sprd_mux *self) +{ + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return; + } + + del_timer(&self->watch_timer); +} + +static int ts0710_ctrl_channel_status(ts0710_con * ts0710) +{ + int retval = -ENODEV; + struct sprd_mux *self = NULL; + + if (!ts0710) { + printk(KERN_ERR "MUX: Error %s ts0710 is NULL\n", __FUNCTION__); + return retval; + } + + self = (struct sprd_mux *)ts0710->user_data; + if (!self) { + printk(KERN_ERR "MUX: Error %s self is NULL\n", __FUNCTION__); + return retval; + } + + if (ts0710->dlci[0].state != CONNECTED) { + mutex_lock(&self->handshake_mutex); + if (ts0710->dlci[0].state != CONNECTED) { + if ((retval = ts0710_open_channel(ts0710, 0)) != 0) { + printk(KERN_ERR "MUX: Error Can't connect server channel 0!\n"); + ts0710_init(ts0710); + mutex_unlock(&self->handshake_mutex); + return retval; + } + } + mutex_unlock(&self->handshake_mutex); + } + + return 0; +} + +static void display_send_info(char * tag, SPRDMUX_ID_E mux_id, int line) +{ + struct sprd_mux *self = NULL; + mux_send_struct *send_info; + int j; + self = sprd_mux_mgr[mux_id].handle; + + if (!self) { + printk(KERN_ERR "MUX: %s, %s mux[%d] line[%d] pid[%d] self NULL\n", tag, __FUNCTION__, mux_id, line, current->pid); + return; + } + + send_info = self->mux_send_info[line]; + + if (!send_info) { + printk(KERN_ERR "MUX: %s, %s mux[%d] line[%d] pid[%d] send_info NULL\n", tag, __FUNCTION__, mux_id, line, current->pid); + return; + } + + if (!send_info->frame) { + printk(KERN_ERR "MUX: %s, %s mux[%d] line[%d] pid[%d] send_info->frame NULL\n", tag, __FUNCTION__, mux_id, line, current->pid); + return; + } + + if (!send_info->buf) { + printk(KERN_ERR "MUX: %s, %s mux[%d] line[%d] pid[%d] send_info->buf NULL\n", tag, __FUNCTION__, mux_id, line, current->pid); + return; + } + + if (!send_info->length) { + printk(KERN_ERR "MUX: %s, %s mux[%d] line[%d] pid[%d] send_info->length NULL\n", tag, __FUNCTION__, mux_id, line, current->pid); + return; + } + +// printk(KERN_ERR "MUX: %s, %s mux[%d] line[%d] buf = 0x%x, frame = 0x%x, length = 0x%x\n", tag, __FUNCTION__, mux_id, line, send_info->buf, send_info->frame, send_info->length); + + for (j = 0; j < ringbuf_num[mux_id][line]; j++) { +// printk(KERN_ERR "MUX: %s, %s mux[%d] line[%d] index[%d] send_info->length[%d] = %u send_info->frame[%d] = 0x%u\n", tag, __FUNCTION__, mux_id, line, j, send_info->length[j], j,send_info->frame[j]); + } +} + + + +EXPORT_SYMBOL(sprdmux_register); +EXPORT_SYMBOL(sprdmux_unregister); +EXPORT_SYMBOL(sprdmux_open); +EXPORT_SYMBOL(sprdmux_close); +EXPORT_SYMBOL(sprdmux_write); +EXPORT_SYMBOL(sprdmux_register_notify_callback); +EXPORT_SYMBOL(sprdmux_line_busy); +EXPORT_SYMBOL(sprdmux_set_line_notify); + +EXPORT_SYMBOL(ts0710_mux_create); +EXPORT_SYMBOL(ts0710_mux_destory); +EXPORT_SYMBOL(ts0710_mux_init); +EXPORT_SYMBOL(ts0710_mux_exit); +EXPORT_SYMBOL(ts0710_mux_status); +EXPORT_SYMBOL(ts0710_mux_open); +EXPORT_SYMBOL(ts0710_mux_close); +EXPORT_SYMBOL(ts0710_mux_write); +EXPORT_SYMBOL(ts0710_mux_read); +EXPORT_SYMBOL(ts0710_mux_poll_wait); +EXPORT_SYMBOL(ts0710_mux_mux_ioctl); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Harald Welte <laforge@openezx.org>"); +MODULE_DESCRIPTION("GSM TS 07.10 Multiplexer"); + |
