/*
 * This is a reverse-engineered driver for mobile WiMAX (802.16e) devices
 * based on Samsung CMC-730 chip.
 * Copyright (C) 2008-2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>
 *
 * 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
 */

#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <poll.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <libusb.h>

#define CONFIG_USER_WIJET_DRV 1

#if CONFIG_USER_WIJET_DRV==1
	#include "usb_eap.h"
#else
	#include <stdarg.h>
#endif

#include "protocol.h"
#include "wimax.h"
#include "tap_dev.h"

/* variables for the command-line parameters */
static int daemonize = 0;
static int diode_on = 1;
static int detach_dvd = 0;
static char *ssid = "@yota.ru";

#define MATCH_BY_LIST		0
#define MATCH_BY_VID_PID	1
#define MATCH_BY_BUS_DEV	2

static int match_method = MATCH_BY_LIST;

/* for matching by list... */
typedef struct usb_device_id_t {
	unsigned short vendorID;
	unsigned short productID;
} usb_device_id_t;

/* list of all known devices */
static usb_device_id_t wimax_dev_ids[] = {
	{ 0x04e8, 0x6761 }, // Samsung dongle
	{ 0x04e9, 0x6761 },
	{ 0x04e8, 0x6731 },
	{ 0x04e8, 0x6780 },
#if WIJET_SUPPORT == 1
	{ 0x1076, 0x7F40}, // wijet storage mode and need to switch to modem mode
	{ 0x1076, 0x7F00},
#endif
};

/* for other methods of matching... */
static union {
	struct {
		unsigned short vid;
		unsigned short pid;
	};
	struct {
		unsigned int bus;
		unsigned int dev;
	};
} match_params;

/* USB-related parameters */
#define IF_MODEM		0
#define IF_DVD			1

void WIMAX_PRINT(int do_syslog, const char *format, ...) 
{
	char  wimax_print_buf[200];
	int                 len;
	va_list             args;

	// Build MSG
	va_start(args,format);    
	len = vsprintf(wimax_print_buf,format,args);
	va_end(args);

	printf("%s\r\n", wimax_print_buf);
	syslog(do_syslog, "%s", wimax_print_buf);
}

#define MAX_PACKET_LEN		0x4000

#if WIJET_SUPPORT==1
// Wijet : bulk IN pipe : 0x82, bulk OUT pipe : 0x01
// Samsung : bulk IN pipe : 0x82, bulk OUT pipe : 0x04
unsigned char EP_IN;
unsigned char EP_OUT;

typedef struct _data_buf{
	unsigned char buf[MAX_PACKET_LEN];
	int used;
	struct libusb_transfer *utransfer;
}data_buf;

#define	MAX_TX_BUFF_CNT			0x1f
#define	MAX_RX_BUFF_CNT			0x1f

data_buf dtx_buf[MAX_TX_BUFF_CNT] = {0};
data_buf drx_buf[MAX_RX_BUFF_CNT] = {0};

int tx_idx = 0, rx_idx = 0;
#else
#define EP_IN			(2 | LIBUSB_ENDPOINT_IN)
#define EP_OUT			(4 | LIBUSB_ENDPOINT_OUT)
#endif

/* debug */
#define WIMAX_DEBUG 0
#define WIMAX_RTX_DEBUG 0

/* information collector */
static struct wimax_dev_status wd_status;

char *wimax_states[] = {"INIT", "SYNC", "NEGO", "NORMAL", "SLEEP", "IDLE", "HHO", "FBSS", "RESET", "RESERVED", "UNDEFINED", "BE", "NRTPS", "RTPS", "ERTPS", "UGS", "INITIAL_RNG", "BASIC", "PRIMARY", "SECONDARY", "MULTICAST", "NORMAL_MULTICAST", "SLEEP_MULTICAST", "IDLE_MULTICAST", "FRAG_BROADCAST", "BROADCAST", "MANAGEMENT", "TRANSPORT"};

/* libusb stuff */
static struct libusb_context *ctx = NULL;
static struct libusb_device_handle *devh = NULL;
static struct libusb_transfer *req_transfer = NULL;
static int kernel_driver_active = 0;

static unsigned char read_buffer[MAX_PACKET_LEN];

static int tap_fd = -1;
static char tap_dev[20] = "wimax%d";
static int tap_if_up = 0;

static nfds_t nfds;
static struct pollfd* fds = NULL;

static int first_nego_flag = 0;
static int device_disconnected = 0;

#define CHECK_NEGATIVE(x) {if((r = (x)) < 0) return r;}
#define CHECK_DISCONNECTED(x) {if((r = (x)) == LIBUSB_ERROR_NO_DEVICE) exit_release_resources(0);}

static void exit_release_resources(int code);
void wimax_dump_buffer(unsigned char *data, int size, char *info);
static int if_down();
int process_events_by_mask(int timeout, unsigned int event_mask);

#if EAP_OVER_USB == 1

//-------------------------------------------------------
//	those stuffs should be in flash,  fix_me
//-------------------------------------------------------
//Global Mobile sometimes use mac as identity
static char eap_identity[60] = "draytek@g1.com.tw";
static char phase2_username[60] = "draytek@g1.com.tw";
static char phase2_password[60] = "bigdog5972727";
static int auto_mode = 1;
char cipher_id = 0;
#define USB_EAP_TIMEOUT 10    //in units of 3 seconds

struct wimax_provider
{
	unsigned int network_id;
	char * provider_name;
};

static struct wimax_provider wimax_provider_table[] =
{
	{0x00B612, "Global Mobile"},
	{0xF74844, "Tatung Info"},
	{0,        "UNKNOWN"}
};

static char * find_wimax_provider(int network_id)
{
	int i, table_size;
	table_size = sizeof(wimax_provider_table)/sizeof(struct wimax_provider);

	for(i = 0; i < table_size; i++)
	{
		if(network_id == wimax_provider_table[i].network_id) break;
	}

	if(i == table_size)
		return wimax_provider_table[table_size - 1].provider_name;
	else
		return wimax_provider_table[i].provider_name;	
}

#endif //EAP_OVER_USB == 1


#if WIJET_SUPPORT == 1
unsigned char wijet_get_mac[5] = {0x00, 0x02, 0x00, 0x01, 0x00}; 

#if WIJET_BLOCK_USELESS_CMD == 0
unsigned char wijet_get_info[13] = {0x00, 0x02, 0x00, 0x09, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xD1};
#endif //WIJET_BLOCK_USELESS_CMD == 0

unsigned char wijet_cmd1[4] = {0x00, 0x04, 0x00, 0x00};
unsigned char wijet_cmd2[5] = {0x00, 0x02, 0x00, 0x01, 0xAB};
unsigned char wijet_cmd3[7] = {0x00, 0x01, 0x00, 0x03, 0xB1, 0x01, 0x00};
unsigned char wijet_cmd4[7] = {0x00, 0x02, 0x00, 0x03, 0x1B, 0x1D, 0x1F};
unsigned char wijet_cmd5[10] = {0x03, 0x12, 0x00, 0x06, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00};
unsigned char wijet_cmd6[10] = {0x03, 0x12, 0x00, 0x06, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00};
unsigned char wijet_cmd7[10] = {0x03, 0x12, 0x00, 0x06, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00};
unsigned char wijet_cmd8[10] = {0x03, 0x12, 0x00, 0x06, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00};
unsigned char wijet_cmd9[6] = {0x00, 0x02, 0x00, 0x02, 0x1D, 0x19};
unsigned char wijet_cmd10[5] = {0x00, 0x02, 0x00, 0x01, 0x19};
unsigned char wijet_power_on[7] = {0x00, 0x01, 0x00, 0x03, 0xAF, 0x01, 0x01};
unsigned char wijet_cmd11[4] = {0x00, 0x06, 0x00, 0x00};
unsigned char wijet_cmd12[24] = {0x03, 0x0C, 0x00, 0x14, 0x63, 0x66, 0x67, 0x20, 0x69, 0x64,	
                                 0x6C, 0x65, 0x20, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5F, 0x54,	
                                 0x4F, 0x20, 0x30, 0x0A};
unsigned char wijet_cmd13[14] = {0x03, 0x0C, 0x00, 0x0A, 0x63, 0x66, 0x67, 0x20, 0x61, 0x72,	
                                 0x71, 0x20, 0x30, 0x0A};

unsigned char wijet_scan[17] = {0x01, 0x0D, 0x00, 0x0D, 0x02, 0xD2, 0x03, 0xF7, 0x48, 0x44,	
                                 0xD4, 0x05, 0x57, 0x69, 0x4A, 0x45, 0x54};
//unsigned char wijet_scan[17] = {0x01, 0x0D, 0x00, 0x0D, 0x02, 0xD2, 0x03, 0x00, 0xb6, 0x12,	
//                                 0xD4, 0x05, 0x57, 0x69, 0x4A, 0x45, 0x54};
unsigned char wijet_connect_1[7] = {0x00, 0x01, 0x00, 0x03, 0xAF, 0x01, 0x01};
unsigned char wijet_connect_2[14] = {0x01, 0x10, 0x00, 0x0A, 0xD2, 0x03, 0xF7, 0x48, 0x44, 0xD6,	
                                     0x03, 0x00, 0xB6, 0x12};	
unsigned char wijet_cmd14[8] = {0x00, 0x02, 0x00, 0x04, 0x60, 0x61, 0x6A, 0x7F};
unsigned char wijet_key[70] =
{
	0x00, 0x01, 0x00, 0x42, 0x02, 0x40	
};

unsigned char wijet_drop[4] =
{
	0x01, 0x02, 0x00, 0x00
};	

static void wijet_async_tx_cb(struct libusb_transfer *transfer)
{
	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
		WIMAX_PRINT(LOG_ERR, "Wimax : wijet_init_tx_cb type=%d, error %d", transfer->type, transfer->status);
		if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
			device_disconnected = 1;
			return;
		}
	}
	libusb_free_transfer(transfer);
}

int rw_wijet_async(unsigned char endpoint, unsigned char * data, int size)
{
	struct libusb_transfer *tx_transfer;
	int cnt = 0;

	tx_transfer = libusb_alloc_transfer(0);
	if (!tx_transfer)
		return -ENOMEM;
	libusb_fill_bulk_transfer(tx_transfer, devh, endpoint, data, size, wijet_async_tx_cb, NULL, 0);
	libusb_submit_transfer(tx_transfer);
	return 0;
}

int rw_wijet_sync(unsigned char endpoint, unsigned char * data, int size)
{
	int r;
	int transferred;
	
	wimax_dump_buffer(data, size, "rw_wijet_sync:");
	r = libusb_bulk_transfer(devh, endpoint, data, size, &transferred, 2000);
	if (r < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : rw_wijet_sync %s error %d transferred=%d",(endpoint==EP_OUT)?"write":"read", r, transferred);
		if (r == LIBUSB_ERROR_NO_DEVICE) {
			exit_release_resources(0);
		}
		return r;
	}

	/* tx and the size is not the same as wish. */
	if (endpoint==EP_OUT && transferred < size) {
		WIMAX_PRINT(LOG_ERR, "Wimax : rw_wijet_sync short write (%d)", r);
		return -1;
	}

	/* no rx. */
	if (endpoint==EP_IN && transferred < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : rw_wijet_sync short read (%d)", r);
		return -1;
	}
	return transferred;
}


#define WIJET_WRITE_THEN_READ  0
#define WIJET_ONLY_WRITE       1
#define WIJET_ONLY_READ        2
int wijet_transaction(unsigned char * out, int out_req_len, unsigned char *in, int in_req_len, char * desc, int mode)
{
    int act_count;
	int bulk_out_cnt = 0;

    if(mode == WIJET_ONLY_READ) goto read;

	/**
	 * After network found, rx packets are processed in callback function.
	 * We can't use synchronous bulk out function in callback fun, otherwise
	 * system would hang. -- Fanny
	 */
	if(wd_status.num_of_network_found && mode==WIJET_ONLY_WRITE){
		return rw_wijet_async(EP_OUT, out, out_req_len);
	}

    if(rw_wijet_sync(EP_OUT, out, out_req_len) < 0){
 	    WIMAX_PRINT(LOG_ERR, "%s failed (out direction)", desc);
        return -1;
    }

    if(mode == WIJET_ONLY_WRITE){
        return 0;
    }

read:
    if( (act_count = rw_wijet_sync(EP_IN, in, in_req_len) )< 0){
  	    WIMAX_PRINT(LOG_ERR, "WiJET %s failed (in direction)", desc);
        return -1;
    }

    if(act_count == 0){
        //zero length packet received occasionally, let's read it again
        if( (act_count = rw_wijet_sync(EP_IN, in, in_req_len) )< 0){
  	        WIMAX_PRINT(LOG_ERR, "WiJET %s failed (in direction) zero length", desc);
            return -1;
        }
    }
    return 0;
}

int wijet_read_n_times(unsigned char * buf, int req_count, int times)
{
    int i;
    for(i=0; i< times; i++) 
        if(wijet_transaction(0, 0, buf, req_count, "read", WIJET_ONLY_READ)  < 0 ) return -1;
    return 0;
}

static void wijet_tx_cb(struct libusb_transfer *transfer)
{
	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
		WIMAX_PRINT(LOG_ERR, "Wimax : wijet_tx_cb write error %d", transfer->status);
		if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
			device_disconnected = 1;
			return;
		}
	}

	
	if(transfer->user_data){
		// normal ethernet data transfer
		((data_buf *)transfer->user_data)->used = 0;
	}else{
		// need to free buffer --- eap packet
		if(transfer->buffer)
			free(transfer->buffer);
	}
		
	libusb_free_transfer(transfer);
}

#if 0
void dbg_mem_dump(unsigned char *buf, int len)
{
	int i;

	printf("*****************\r\n");
	for(i=0; i<len; i++){
		printf("%02x ", *(buf+i));
		if((i+1) % 8 == 0){
			printf("\r\n");
		}
	}
	printf("*****************\r\n");
}
#endif

/* Asynchronous bulk out transfer. */
int wijet_send_to_usb(unsigned char * buf, int len, int type)
{
	struct libusb_transfer *tx_transfer;
	int msg_type;

#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_INFO, "wijet_send_to_usb() : type=%s", 
		(type == WIMAX_TX_EAP_PKT)?"EAP":"ETH");
#endif

    if(len < 4){
		WIMAX_PRINT(LOG_ERR, "WIJET: buffer length too short in wijet_send_to_usb()");
        free(buf);
        return -1;
    }

	//fill USB header
	if(type == WIMAX_TX_EAP_PKT)
		msg_type = WIJET_TX_EAP_PKT;
	else
		msg_type = WIJET_TX_ETHERNET_PKT;

	buf[0] = (msg_type & 0xFF00) >> 8;
	buf[1] = msg_type & 0xFF;
	buf[2] = ((len -4) & 0xFF00) >> 8;
	buf[3] = (len -4) & 0xFF;

	tx_transfer = libusb_alloc_transfer(0);
	if (!tx_transfer){
		free(buf);
		return -ENOMEM;
	}


#if 0
	if(type != WIMAX_TX_EAP_PKT){
		printf("Send Ethernet Packet: \r\n");
		dbg_mem_dump(buf, len);
	}
#endif
	if(msg_type != WIJET_TX_ETHERNET_PKT){
		libusb_fill_bulk_transfer(tx_transfer, devh, EP_OUT, buf, len, wijet_tx_cb, NULL, 0);
	}else{
		libusb_fill_bulk_transfer(tx_transfer, devh, EP_OUT, buf, len, wijet_tx_cb, (void *)buf, 0);
	}
	libusb_submit_transfer(tx_transfer);
    return 0;
}

void wijet_parse_scan_result(unsigned char * buf, int len)
{
	unsigned char * p;
	int info_len, i ;
	
	p = buf;
	wd_status.num_of_network_found = 0;
	while(len > 0)
	{
		info_len = p[1];
		switch(p[0])
		{
		case 0xD2: //dongle provider network ID
//			WIMAX_PRINT(LOG_INFO, "Wijet: dongle provider network ID");
			break;

		case 0xD6: //wimax network ID found 
//			WIMAX_PRINT(LOG_INFO, "Wijet: wimax network ID found  ");
			wd_status.num_of_network_found++;
            wd_status.network[wd_status.num_of_network_found-1].id = (p[2] << 16) | (p[3] << 8) | p[4];
			break;

		case 0xD3: //wimax network name found 
//			WIMAX_PRINT(LOG_INFO, "Wijet: wimax network name found  ");
            if(info_len < 30){
				strncpy(wd_status.network[wd_status.num_of_network_found-1].name, (char*)&p[2], info_len);
				wd_status.network[wd_status.num_of_network_found-1].name[info_len] = 0;
//				strupr((char*)wd_status.network[wd_status.num_of_network_found-1].name);
				if(strcmp(wd_status.network[wd_status.num_of_network_found-1].name, "Unknown") == 0){
					char * p;
					//try resolve network name by id
					#if EAP_OVER_USB == 1
					p = find_wimax_provider(wd_status.network[wd_status.num_of_network_found-1].id);
					strcpy(wd_status.network[wd_status.num_of_network_found-1].name, p);
					#endif
				}
			}else{
				WIMAX_PRINT(LOG_INFO, "Wijet: network name too long");
			}
			break;

		case 0x61: //strength indicator 0 - 30
//			WIMAX_PRINT(LOG_INFO, "Wijet: signal strength %d  (0 ~ 30)  ", p[2]);
			wd_status.network[wd_status.num_of_network_found-1].ssi_level = p[2];
			break;

		case 0x60: //may be ssi in dbm ????
			wd_status.network[wd_status.num_of_network_found-1].ssi_dbm = p[2] - 120;
			wd_status.rssi = -(p[2] - 120); //fanny : Added for web display
//			WIMAX_PRINT(LOG_INFO, "Wijet: signal strength = %d dbm  ", -(p[2] - 120));
			break;

		case 0x01: //base station mac address
//			WIMAX_PRINT(LOG_INFO, "Wijet: base station mac address: ", p[2]);
			break;

		default:
			WIMAX_PRINT(LOG_INFO, "Wijet: unknown scan information");
			break;
		}
		p += info_len + 2;
		len -=  info_len + 2;
	}

	WIMAX_PRINT(LOG_INFO, "Wijet: %d networks found", wd_status.num_of_network_found);
	for(i = 0; i < wd_status.num_of_network_found; i++){
		WIMAX_PRINT(LOG_INFO, "id = %06X, name = %s, sig = -%d dbm, level = %d (0~30)", 
						wd_status.network[wd_status.num_of_network_found-1].id,
						wd_status.network[wd_status.num_of_network_found-1].name,
						wd_status.network[wd_status.num_of_network_found-1].ssi_dbm,
						wd_status.network[wd_status.num_of_network_found-1].ssi_level);
	}
}

int wijet_connect(void)
{
	WIMAX_PRINT(LOG_INFO, "Wijet: connecting to %s network", wd_status.network[wd_status.network_idx_selected].name);
	//wd_status.link_status = 1;
	wijet_connect_2[11] = (wd_status.network[wd_status.network_idx_selected].id & 0xFF0000) >> 16;
	wijet_connect_2[12] = (wd_status.network[wd_status.network_idx_selected].id & 0xFF00) >> 8;
	wijet_connect_2[13] = (wd_status.network[wd_status.network_idx_selected].id & 0xFF) ;
	if(wijet_transaction(wijet_connect_1, sizeof(wijet_connect_1), 0, 0, "wijet_connect_1", WIJET_ONLY_WRITE) <0 ) return -1;
	if(wijet_transaction(wijet_connect_1, sizeof(wijet_connect_1), 0, 0, "wijet_connect_1", WIJET_ONLY_WRITE) <0 ) return -1;
	if(wijet_transaction(wijet_connect_2, sizeof(wijet_connect_2), 0, 0, "wijet_connect_2", WIJET_ONLY_WRITE) <0 ) return -1;
	return 0;
}

int wijet_disconnect(void)
{
	WIMAX_PRINT(LOG_INFO, "Wijet: disconnecting");
	wijet_transaction(wijet_drop, sizeof(wijet_drop), 0, 0, "wijet_drop", WIJET_ONLY_WRITE);
}

int wijet_send_key(unsigned char * key)
{
	memcpy(&wijet_key[6], key, 64);
	WIMAX_PRINT(LOG_INFO, "send master session key to Wijet dongle");
	return wijet_transaction(wijet_key, sizeof(wijet_key), 0, 0, "wijet_key", WIJET_ONLY_WRITE);
}


/*--------------------------------------------------------------------------------------------------------------------------
purpose: initialize wijet
Note:
  initialization sequence using sync operation is put here for reference. it will failed if we doing a software reboot.
  the better way is use sync write and async read, I just put these here for reference.

    //get dongle's info, this is not really necessary,just put here for reference
    if(wijet_transaction(wijet_get_info, sizeof(wijet_get_info), p, 1500, "get info", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd1, sizeof(wijet_cmd1), p, 1500, "wijet_cmd1", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd2, sizeof(wijet_cmd2), p, 1500, "wijet_cmd2", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd3, sizeof(wijet_cmd3), 0, 0, "wijet_cmd3", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd4, sizeof(wijet_cmd4), 0, 0, "wijet_cmd4", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd5, sizeof(wijet_cmd5), 0, 0, "wijet_cmd5", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;
    if(wijet_read_n_times(p, 1500, 3) < 0) goto wijet_error;
    if(wijet_transaction(wijet_cmd6, sizeof(wijet_cmd6), p, 1500, "wijet_cmd6", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd7, sizeof(wijet_cmd7), p, 1500, "wijet_cmd7", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd8, sizeof(wijet_cmd8), p, 1500, "wijet_cmd8", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd9, sizeof(wijet_cmd9), p, 1500, "wijet_cmd9", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_read_n_times(p, 1500, 1) < 0) goto wijet_error;
    if(wijet_transaction(wijet_cmd10, sizeof(wijet_cmd10), p, 1500, "wijet_cmd10", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_power_on, sizeof(wijet_power_on), 0, 0, "wijet_power_on", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd11, sizeof(wijet_cmd11), p, 1500, "wijet_cmd11", WIJET_WRITE_THEN_READ)  < 0 ) goto wijet_error;
    if(wijet_read_n_times(p, 1500, 3) < 0) goto wijet_error;
    if(wijet_transaction(wijet_cmd12, sizeof(wijet_cmd12), 0, 0, "wijet_cmd_12", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;
    if(wijet_transaction(wijet_cmd13, sizeof(wijet_cmd13), 0, 0, "wijet_cmd_13", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;

    DrayDelay(Unit_ms(1000));
    if(wijet_transaction(wijet_cmd4, sizeof(wijet_cmd4), 0, 0, "wijet_cmd_4", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;
    if(wijet_read_n_times(p, 1500, 1) < 0) goto wijet_error;
    DrayDelay(Unit_ms(500));

    if(wijet_read_n_times(p, 1500, 4) < 0) goto wijet_error;
    if(wijet_transaction(wijet_cmd11, sizeof(wijet_cmd11), p, 1500, "wijet_cmd11", WIJET_ONLY_WRITE)  < 0 ) goto wijet_error;
--------------------------------------------------------------------------------------------------------------------------*/
int init_wijet(void)
{
#if WIJET_BLOCK_USELESS_CMD == 0
	if(wijet_transaction(wijet_get_info, sizeof(wijet_get_info), 0, 0, "get info", WIJET_ONLY_WRITE)  < 0 ) return -1;
#endif
	/* initialize wijet */
    if(wijet_transaction(wijet_cmd1, sizeof(wijet_cmd1), 0, 0, "wijet_cmd1", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd2, sizeof(wijet_cmd2), 0, 0, "wijet_cmd2", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd3, sizeof(wijet_cmd3), 0, 0, "wijet_cmd3", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd4, sizeof(wijet_cmd4), 0, 0, "wijet_cmd4", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd5, sizeof(wijet_cmd5), 0, 0, "wijet_cmd5", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd6, sizeof(wijet_cmd6), 0, 0, "wijet_cmd6", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd7, sizeof(wijet_cmd7), 0, 0, "wijet_cmd7", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd8, sizeof(wijet_cmd8), 0, 0, "wijet_cmd8", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd9, sizeof(wijet_cmd9), 0, 0, "wijet_cmd9", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd10, sizeof(wijet_cmd10), 0, 0, "wijet_cmd10", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_power_on, sizeof(wijet_power_on), 0, 0, "wijet_power_on", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd11, sizeof(wijet_cmd11), 0, 0, "wijet_cmd11", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd12, sizeof(wijet_cmd12), 0, 0, "wijet_cmd_12", WIJET_ONLY_WRITE)  < 0 ) return -1;
    if(wijet_transaction(wijet_cmd13, sizeof(wijet_cmd13), 0, 0, "wijet_cmd_13", WIJET_ONLY_WRITE)  < 0 ) return -1;

	process_events_by_mask(1000, 0);//DrayDelay(Unit_ms(1000));

    if(wijet_transaction(wijet_cmd4, sizeof(wijet_cmd4), 0, 0, "wijet_cmd_4", WIJET_ONLY_WRITE)  < 0 ) return -1;
	process_events_by_mask(500, 0);//DrayDelay(Unit_ms(500));

    if(wijet_transaction(wijet_cmd11, sizeof(wijet_cmd11), 0, 0, "wijet_cmd11", WIJET_ONLY_WRITE)  < 0 ) return -1;
	return 0;
}

#endif //WIJET_SUPPORT == 1

#if EAP_OVER_USB == 1

int usb_eap_init(struct wimax_dev_status * dev)
{

#if WIJET_SUPPORT == 1
	if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid == 0x7F00){
		dev->eap_interface.tx_usb_header_len = 4;                           //proprietary Tx USB header length
		dev->eap_interface.rx_usb_header_len = 4;                           //proprietary Rx USB header length
		dev->eap_interface.send_to_usb = wijet_send_to_usb;                 //wijet send method
		dev->eap_interface.send_key_to_dongle = wijet_send_key;             //wijet send key method
		dev->eap_interface.connect = wijet_connect;                   //wijet connect method
		dev->eap_interface.disconnect = wijet_disconnect;                   //wijet disconnect method
	}
#endif

    dev->eap_interface.identity = eap_identity;
	dev->eap_interface.mschap2_data.username = phase2_username;
	dev->eap_interface.mschap2_data.password = phase2_password;
	dev->eap_interface.eap_conf_data.phase2_type = TTLS_PHASE2_MSCHAPV2;
	dev->eap_interface.eap_conf_data.session_resume = RES_NO;
	dev->eap_interface.conf_phase2.phase2_type = TTLS_PHASE2_MSCHAPV2;
	dev->eap_interface.conf_phase2.next = 0;
	dev->eap_interface.conf_phase2.phase2_data = &dev->eap_interface.mschap2_data;
	dev->eap_interface.eap_conf_data.phase2 = &dev->eap_interface.conf_phase2;
    return 0;
}

#endif //EAP_OVER_USB

void wimax_dump_buffer(unsigned char *data, int size, char *info)
{
	int i;
	unsigned char str[50], *p;

#if WIMAX_RTX_DEBUG == 0
	return;
#endif
	if (!data || !info)
		return;
#if WIMAX_DEBUG == 1
	printf("Wimax : %s (length = %d)\n", info, size);
#endif
	WIMAX_PRINT(LOG_INFO, "Wimax : %s(length = %d)\n", info, size); 
    for (i = 0, p = str; i < size; ) {
		p += sprintf((char*)p, "0x%02X ", data[i]);
		i++;
		if ((i % 8) == 0) {
#if WIMAX_DEBUG == 1
			printf("%s\n", str);
#endif
			WIMAX_PRINT(LOG_INFO, "%s", str); 
			p = str;
		}
	}
	if((i % 8) != 0) {
#if WIMAX_DEBUG==1
		printf("%s\n", str);
#endif
		WIMAX_PRINT(LOG_INFO, "%s", str); 
	}
}

static struct libusb_device_handle* find_wimax_device(void)
{
	struct libusb_device **devs;
	struct libusb_device *found = NULL;
	struct libusb_device *dev;
	struct libusb_device_handle *handle = NULL;
	int i = 0;
	int r;

	if (libusb_get_device_list(ctx, &devs) < 0)
		return NULL;

	while (!found && (dev = devs[i++]) != NULL) {
		struct libusb_device_descriptor desc;
		unsigned int j = 0;
		unsigned short dev_vid, dev_pid;

		r = libusb_get_device_descriptor(dev, &desc);
		if (r < 0) {
			continue;
		}
		dev_vid = libusb_le16_to_cpu(desc.idVendor);
		dev_pid = libusb_le16_to_cpu(desc.idProduct);

	#if WIJET_SUPPORT
		wd_status.dev_vid = dev_vid;
		wd_status.dev_pid = dev_pid;
		WIMAX_PRINT(LOG_INFO, "Wimax : Bus %03d Device %03d: ID %04x:%04x", libusb_get_bus_number(dev), libusb_get_device_address(dev), dev_vid, dev_pid);
	#endif

	#if WIMAX_DEBUG == 1
		WIMAX_PRINT(LOG_INFO, "Wimax : Bus %03d Device %03d: ID %04x:%04x", libusb_get_bus_number(dev), libusb_get_device_address(dev), dev_vid, dev_pid);
	#endif
		switch (match_method) {
		case MATCH_BY_LIST: {
			for (j = 0; j < sizeof(wimax_dev_ids) / sizeof(usb_device_id_t); j++) {
				if (dev_vid == wimax_dev_ids[j].vendorID && dev_pid == wimax_dev_ids[j].productID) {
					found = dev;
					break;
				}
			}
			break;
		}
		case MATCH_BY_VID_PID: {
			if (dev_vid == match_params.vid && dev_pid == match_params.pid) {
				found = dev;
			}
			break;
		}
		case MATCH_BY_BUS_DEV: {
			if (libusb_get_bus_number(dev) == match_params.bus && libusb_get_device_address(dev) == match_params.dev) {
				found = dev;
			}
			break;
		}
		}
	}
	if (found) {
		r = libusb_open(found, &handle);
		if (r < 0)
			handle = NULL;
	}

	libusb_free_device_list(devs, 1);
	return handle;
}

// mapping to 2910 : WimaxSend() --> asynchronous. but set_data() is synchronous
static int set_data(unsigned char* data, int size)
{
	int r;
	int transferred;
	wimax_dump_buffer(data, size, "Bulk write:");

	r = libusb_bulk_transfer(devh, EP_OUT, data, size, &transferred, 0);
	if (r < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : bulk write error %d", r);
	#if WIMAX_DEBUG
		printf("Wimax : bulk write error %d\n", r);
	#endif
		if (r == LIBUSB_ERROR_NO_DEVICE) {
			exit_release_resources(0);
		}
		return r;
	}
	if (transferred < size) {
		WIMAX_PRINT(LOG_ERR, "Wimax : short write (%d)", r);
	#if WIMAX_DEBUG
		printf("Wimax : short write (%d)\n", r);
	#endif
		return -1;
	}
	return r;
}

// mapping to 2910 : wimax_read_comp()
static void cb_req(struct libusb_transfer *transfer)
{
	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
		WIMAX_PRINT(LOG_ERR, "Wimax : async bulk read error %d", transfer->status);
	#if WIMAX_DEBUG
		printf("Wimax : async bulk read error %d\n", transfer->status);
	#endif
		if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
			device_disconnected = 1;
	#if WIJET_SUPPORT==1
			if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid== 0x7F00){
				if(transfer->user_data){
					((data_buf *)transfer->user_data)->used = 0;
					((data_buf *)transfer->user_data)->utransfer = 0;
				}
				libusb_free_transfer(transfer);
			}
	#endif
			return;
		}
	} else {
		wimax_dump_buffer(transfer->buffer, transfer->actual_length, "Async read:");

#if WIJET_SUPPORT == 1
		if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid== 0x7F00){
			unsigned short msg_type;
			unsigned short msg_len;
			unsigned char * p, *eap_rsp_pkt;
			p = transfer->buffer;
			msg_type = (p[0] << 8) + p[1];
			msg_len = (p[2] << 8) + p[3];
			switch(msg_type){
				case WIJET_SCAN_COMPLETE:
					//scan completed
					break;
		
				case WIJET_SCAN_RESULT:
					//scan result
					if(msg_len == 0){
						WIMAX_PRINT(LOG_INFO, "WIJET: no signal found");
						wd_status.link_status = 0;
						if_down();
					}else{
						wijet_parse_scan_result(p+4, msg_len);
						//if(!wd_status.link_status) wd_status.link_status = 1;
						#if EAP_OVER_USB
						if(wd_status.link_status == 0 && (auto_mode||wd_status.trigger_connect) ){
							WIMAX_PRINT(LOG_INFO, "EAP start connecting....  id = %s, username = %s, password = %s",
							wd_status.eap_interface.identity,
							wd_status.eap_interface.mschap2_data.username,
							wd_status.eap_interface.mschap2_data.password);
					
							if(wd_status.eap_interface.connect() < 0) {
								wd_status.link_status = 0;
							}else{
								wd_status.link_status = 1;
								wd_status.HsTimer = USB_EAP_TIMEOUT; //start handshake timer
								wd_status.trigger_connect = 0;
							}
						}
						#endif
					}
					break;
		
				case WIJET_RX_EAP_PKT:
#if 0			
					WIMAX_PRINT(LOG_INFO, "<=======  WIJET EAP packet time = %d", TickTime);
					if(urb->ActCount > 100)
					{
						WIMAX_PRINT(LOG_INFO, "WIJET: dump fisrt 100 bytes");
						WIMAX_DUMP_BUFFER(0, urb->Buffer, 100); 
					}
					else
						WIMAX_DUMP_BUFFER(0, urb->Buffer, urb->ActCount); 
#endif
					//prepare a buffer for EAP response
					eap_rsp_pkt = (unsigned char *)calloc(3, 1024);			 
					if (!eap_rsp_pkt) { 
						WIMAX_PRINT(LOG_ERR, "WIJET: can not get a buffer for EAP response, can not process)");
						break;
					}
					#if EAP_OVER_USB
					//process the EAP response
					usb_eap_process(&wd_status.eap_interface, transfer->buffer, transfer->actual_length, eap_rsp_pkt);
					#endif
					break;
		
				case WIJET_RX_ETHERNET_PKT:
					//WIMAX_PRINT(LOG_INFO, "Receive WIJET Ethernet packet");
		//			WIMAX_DUMP_BUFFER(0, urb->Buffer, urb->ActCount); 
					//data frame, send to network stack
					write_netif(transfer->buffer + 4, transfer->actual_length - 4); 
					goto re_submit;
		
				case 0x8112:
					WIMAX_PRINT(LOG_INFO, "WIJET receive packet [0x%04X]", msg_type);
		//			WIMAX_DUMP_BUFFER(0, urb->Buffer, urb->ActCount); 
					break;
		
				case 0x8115:
				case 0x8003:
					WIMAX_PRINT(LOG_INFO, "WIJET carrier lost [0x%04X]", msg_type);
		//			WIMAX_DUMP_BUFFER(0, urb->Buffer, urb->ActCount); 
					wd_status.link_status = 0;
					wd_status.HsTimer = 0;
					if_down();
					break;
		
				default:
					WIMAX_PRINT(LOG_INFO, "WIJET receive message [0x%04X]", msg_type);
		//			WIMAX_DUMP_BUFFER(0, urb->Buffer, urb->ActCount); 
					break;
			}
			//RelBuffer((char *)urb->Buffer);  //fix_me
			goto re_submit;
		}
#endif //WIJET_SUPPORT == 1
		process_response(&wd_status, transfer->buffer, transfer->actual_length);
	}

re_submit:	
#if WIJET_SUPPORT == 1
	if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid== 0x7F00){
		libusb_fill_bulk_transfer(transfer, devh, EP_IN, transfer->buffer, MAX_PACKET_LEN, cb_req, NULL, 0);
	}
#endif
	if (libusb_submit_transfer(req_transfer) < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : async read transfer sumbit failed");
	#if WIMAX_DEBUG
		printf("Wimax : async read transfer sumbit failed\n");
	#endif
	}
}

/* get link_status */
int get_link_status()
{
	return wd_status.link_status;
}

/* set close-on-exec flag on the file descriptor */
int set_coe(int fd)
{
	int flags;

	flags = fcntl(fd, F_GETFD);
	if (flags == -1) {
		WIMAX_PRINT(LOG_ERR, "Wimax : failed to set close-on-exec flag on fd %d", fd);
	#if WIMAX_DEBUG
		printf("Wimax : failed to set close-on-exec flag on fd %d\n", fd);
	#endif
		return -1;
	}
	flags |= FD_CLOEXEC;
	if (fcntl(fd, F_SETFD, flags) == -1) {
		WIMAX_PRINT(LOG_ERR, "Wimax : failed to set close-on-exec flag on fd %d", fd);
	#if WIMAX_DEBUG
		printf("Wimax : failed to set close-on-exec flag on fd %d\n", fd);
	#endif
		return -1;
	}

	return 0;
}

/* brings interface up and runs a user-supplied script */
static int if_create()
{
	tap_fd = tap_open(tap_dev);
	if (tap_fd < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : failed to allocate tap interface");
	#if WIMAX_DEBUG
		printf("Wimax : failed to allocate tap interface\n");
		printf("You should have TUN/TAP driver compiled in the kernel or as a kernel module.\n");
		printf("If 'modprobe tun' doesn't help then recompile your kernel.\n");
	#endif
		exit_release_resources(1);
	}
	tap_set_hwaddr(tap_fd, tap_dev, wd_status.mac);
	tap_set_mtu(tap_fd, tap_dev, 1386);
	set_coe(tap_fd);
#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_NOTICE, "Wimax : Allocated tap interface: %s", tap_dev);
	WIMAX_PRINT(LOG_INFO, "Wimax : if create");
	printf("Wimax : if create\n");
#endif
	return 0;
}

#if WIJET_SUPPORT == 1

/* brings interface up and runs a user-supplied script */
int if_up() // mapping to 2910 link_up()
{
	char buf[70];
	int up = 0;
	FILE *f;

	if (tap_if_up)return 0;

	WIMAX_PRINT(LOG_INFO, "Wimax: send master session key to Wijet dongle");
	wijet_transaction(wijet_key, sizeof(wijet_key), 0, 0, "wijet_key", WIJET_ONLY_WRITE);
	wd_status.link_status = 2;
	wd_status.HsTimer = 0;
	WIMAX_PRINT(LOG_INFO, "Wimax : Prepare to bring interface up and start uhcpc...!!!!!!!");

#if 1 // yota firmware would start dhcp in wimax.sh
	system("echo 1 > /var/state/wimaxStatus2");
	system("/usr/sbin/run_wimax.sh up");
	tap_if_up = 1;
#else
	tap_bring_up(tap_fd, tap_dev);
	system("killall -q udhcpc");
	system("route del default");
	sprintf(buf, "udhcpc -i %s -s /sbin/udhcpc.sh -O staticroutes -p /var/run/udhcpc.pid &", tap_dev);
	system(buf);
	tap_if_up = 1;
#endif
	return 0;
}

/* brings interface down and runs a user-supplied script */
static int if_down()
{
	if (!tap_if_up) return 0;

	tap_if_up = 0;
#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_INFO, "Wimax : udhcpc stop, if down...");
	printf("Wimax : udhcpc stop, if down...\n");
#endif

#if 1 // yota firmware would do this in wimax.sh
	system("echo 0 > /var/state/wimaxStatus2");
	system("/usr/sbin/run_wimax.sh down");
#else
	tap_bring_down(tap_fd, tap_dev);
#endif
	return 0;
}

#endif

/* brings interface down and runs a user-supplied script */
static int if_release() // mapping to 2910 : 
{
#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_INFO, "Wimax : if release");
	printf("Wimax : if release\n");
#endif
	tap_close(tap_fd, tap_dev);
#if EAP_OVER_USB == 1
	//clean up previous active method anyway
	eap_cleanup(&wd_status.eap_interface.activemethod);
#endif
	return 0;
}

/* set link_status */
void set_link_status(int link_status)
{
	wd_status.info_updated |= WDS_LINK_STATUS;

	if (wd_status.link_status == link_status) return;

	if (wd_status.link_status < 2 && link_status == 2) {
		system("echo 1 > /var/state/wimaxStatus2");
		system("/usr/sbin/run_wimax.sh up");
	}
	if (wd_status.link_status == 2 && link_status < 2) {
		system("echo 0 > /var/state/wimaxStatus2");
		system("/usr/sbin/run_wimax.sh down");
	}
	if (link_status == 1) {
		first_nego_flag = 1;
	}
	wd_status.link_status = link_status;
}

/* get state */
int get_state()
{
	return wd_status.state;
}

/* set state */
void set_state(int state)
{
	wd_status.state = state;
	wd_status.info_updated |= WDS_STATE;
	if (state >= 1 && state <= 3 && wd_status.link_status != (state - 1)) {
		set_link_status(state - 1);
	}
}

static int alloc_transfers(void)
{
	req_transfer = libusb_alloc_transfer(0);
	if (!req_transfer)
		return -ENOMEM;
	libusb_fill_bulk_transfer(req_transfer, devh, EP_IN, read_buffer, sizeof(read_buffer), cb_req, NULL, 0);
	return 0;
}

#ifdef BIG_ENDIAN
#define ET_ARP			0x0806      /* address request message */
#else
#define ET_ARP			0x0608      /* address request message */
#endif

int write_netif(const void *buf, int count)
{	
#if WIJET_SUPPORT == 1
	// wimax driver rx function
	if(*(unsigned short *)(buf+12) == ET_ARP){
		if(wd_status.dev_vid == 0x1076 && wd_status.dev_pid == 0x7F00){
			//Wijet source mac address is not equal to the target mac address
			memcpy((char *)buf + 6, (char *)buf + 22, 6); 
		}
	}
#endif //WIJET_SUPPORT == 1

	return tap_write(tap_fd, buf, count);
}

static int read_tap()
{
	unsigned char buf[MAX_PACKET_LEN];
	int hlen = get_header_len();
	int r;
	int len;

#if WIJET_SUPPORT==1
	char *tx_pkt;
	if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid== 0x7F00){
		hlen = 4;
		if(dtx_buf[tx_idx].used == 0){
			/* wijet tx uses asynchronous transfer so we need to allocate memory */
			tx_pkt = dtx_buf[tx_idx].buf; // fixme
			r = tap_read(tap_fd, tx_pkt + hlen, MAX_PACKET_LEN - hlen);
			tx_idx = (tx_idx++)& MAX_TX_BUFF_CNT ;
		}else{
			// no buffer available
			syslog(LOG_INFO, "No tx buffer available....");
			return 0;
		}
	}else
#endif
	r = tap_read(tap_fd, buf + hlen, MAX_PACKET_LEN - hlen);

	if (r < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : Error while reading from TAP interface");
	#if WIMAX_DEBUG
		printf("Wimax : Error while reading from TAP interface\n");
	#endif
		return r;
	}
	if (r == 0) {
		return 0;
	}

#if WIJET_SUPPORT==1
	if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid== 0x7F00){
		return wijet_send_to_usb((unsigned char*)tx_pkt, r+hlen, WIJET_TX_ETHERNET_PKT);
	}
#endif
	len = fill_data_packet_header(buf, r);
	wimax_dump_buffer(buf, len, "Outgoing packet:");
	r = set_data(buf, len);

	return r;
}

static int process_events_once(int timeout)
{
	struct timeval tv = {0, 0};
	int r;
	int libusb_delay;
	int delay;
	unsigned int i;
	char process_libusb = 0;

	r = libusb_get_next_timeout(ctx, &tv);
	if (r == 1 && tv.tv_sec == 0 && tv.tv_usec == 0) {
		r = libusb_handle_events_timeout(ctx, &tv);
	}

	delay = libusb_delay = tv.tv_sec * 1000 + tv.tv_usec;
	if (delay <= 0 || delay > timeout) {
		delay = timeout;
	}

	CHECK_NEGATIVE(poll(fds, nfds, delay));

	process_libusb = (r == 0 && delay == libusb_delay);

	for (i = 0; i < nfds; ++i) {
		if (fds[i].fd == tap_fd) {
			if (fds[i].revents) {
				CHECK_NEGATIVE(read_tap());
			}
			continue;
		}
		process_libusb |= fds[i].revents;
	}

	if (process_libusb) {
		struct timeval tv = {.tv_sec = 0, .tv_usec = 0};
		CHECK_NEGATIVE(libusb_handle_events_timeout(ctx, &tv));
	}

	return 0;
}

/* handle events until timeout is reached or all of the events in event_mask happen */
//static int process_events_by_mask(int timeout, unsigned int event_mask)
int process_events_by_mask(int timeout, unsigned int event_mask)
{
	struct timeval start, curr;
	int r;
	int delay = timeout;

	CHECK_NEGATIVE(gettimeofday(&start, NULL));

	wd_status.info_updated &= ~event_mask;

	while ((event_mask == 0 || (wd_status.info_updated & event_mask) != event_mask) && delay >= 0) {
		long a;

		CHECK_NEGATIVE(process_events_once(delay));

		if (device_disconnected) {
			exit_release_resources(0);
		}

		CHECK_NEGATIVE(gettimeofday(&curr, NULL));

		a = (curr.tv_sec - start.tv_sec) * 1000 + (curr.tv_usec - start.tv_usec) / 1000;
		delay = timeout - a;
	}

	wd_status.info_updated &= ~event_mask;

	return (delay > 0) ? delay : 0;
}

// struct libusb_pollfd : monitor this file descriptor for becoming ready to read from, and POLLOUT indicates that you should monitor 
// this file descriptor for nonblocking write readiness.

int alloc_fds()
{
	int i;
	const struct libusb_pollfd **usb_fds = libusb_get_pollfds(ctx);

	if (!usb_fds)
		return -1;

	nfds = 0;
	while (usb_fds[nfds]) {
		nfds++;
	}
	if (tap_fd != -1)
		nfds++;

	if(fds != NULL)
		free(fds);

	fds = (struct pollfd*)calloc(nfds, sizeof(struct pollfd));
	for (i = 0; usb_fds[i]; ++i) {
		fds[i].fd = usb_fds[i]->fd;
		fds[i].events = usb_fds[i]->events;
		set_coe(usb_fds[i]->fd);
	}
	if (tap_fd != -1) {
		fds[i].fd = tap_fd;
		fds[i].events = POLLIN;
		fds[i].revents = 0;
	}
	free(usb_fds);

	return 0;
}

void cb_add_pollfd(int fd, short events, void *user_data)
{
	alloc_fds();
}

void cb_remove_pollfd(int fd, void *user_data)
{
	alloc_fds();
}

#if WIJET_SUPPORT == 1

static int wijet_initialization(void)
{
	unsigned char syncBuf[60];
	struct libusb_transfer *utransfer = NULL;
	int i;

	WIMAX_PRINT(LOG_INFO, "Wimax: wijet_initialization()");

	/* WiJET is in modem mode, get mac address. */
	if(wijet_transaction(wijet_get_mac, sizeof(wijet_get_mac), syncBuf, 60, "get mac address", WIJET_WRITE_THEN_READ)  < 0 ){
		WIMAX_PRINT(LOG_ERR, "Wijet get mac address failed"); 
		return -1;
	}
	memcpy(wd_status.mac, &syncBuf[6], 6); 
	memcpy(wd_status.bsid, &syncBuf[6], 6); // Actually, it's useless, only used to display on web. fixme future.
	WIMAX_PRINT(LOG_INFO, "WiMAX: mac address = %02X:%02X:%02X:%02X:%02X:%02X", 
				wd_status.mac[0], wd_status.mac[1], wd_status.mac[2], wd_status.mac[3], wd_status.mac[4], wd_status.mac[5]);
init_eap:
#if EAP_OVER_USB == 1
    usb_eap_init(&wd_status);
#endif

	/* Prepare one rx transfer and wait for call back */
	WIMAX_PRINT(LOG_INFO, "Wimax : Continuous async read start...");
	for(i=0; i<MAX_RX_BUFF_CNT; i++){
		utransfer = libusb_alloc_transfer(0);
		if (!utransfer){
			WIMAX_PRINT(LOG_ERR, "Fail to allocate transfer for rx....i=%d", i);
			return -ENOMEM;
		}
		drx_buf[i].used = 1;
		drx_buf[i].utransfer = utransfer;
		libusb_fill_bulk_transfer(utransfer, devh, EP_IN, drx_buf[i].buf, MAX_PACKET_LEN, cb_req, &drx_buf[i], 0);
		libusb_submit_transfer(utransfer);
	}

	if(init_wijet() < 0){
		WIMAX_PRINT(LOG_INFO, "Wijet: initialization failed");
		return -1;
	}

	WIMAX_PRINT(LOG_INFO, "Wimax: Wijet initialization SUCCESS!!!!!!!!!");

    /* Initialize SSL library */
	WIMAX_PRINT(LOG_INFO, "Wimax: Initialize SSL library!!!!!!!!!");
	SSL_load_error_strings();   /* readable error messages */
	SSL_library_init();         /* initialize library */
	//actions_to_seed_PRNG();   /* Pseudo Random Number Generator */
	ERR_load_crypto_strings();
	// SSL_CTX_set_verify ?? SSL_CTX_set_session_id_context ??
	return 0;
}

#endif // WIJET_SUPPORT

static int init(void)
{
	unsigned char req_data[MAX_PACKET_LEN];
	int len;
	int r;

	alloc_transfers();
#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_INFO, "Wimax : Continuous async read start...");
#endif
	CHECK_DISCONNECTED(libusb_submit_transfer(req_transfer));

	len = fill_protocol_info_req(req_data,
			USB_HOST_SUPPORT_SELECTIVE_SUSPEND | USB_HOST_SUPPORT_DL_SIX_BYTES_HEADER |
			USB_HOST_SUPPORT_UL_SIX_BYTES_HEADER | USB_HOST_SUPPORT_DL_MULTI_PACKETS);
	set_data(req_data, len);

	process_events_by_mask(500, WDS_PROTO_FLAGS);

	len = fill_mac_lowlevel_req(req_data);
	set_data(req_data, len);

	process_events_by_mask(500, WDS_OTHER);

	len = fill_init_cmd(req_data);
	set_data(req_data, len);

	len = fill_string_info_req(req_data);
	set_data(req_data, len);

	process_events_by_mask(500, WDS_CHIP | WDS_FIRMWARE);

#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_NOTICE, "Wimax : Chip info: %s", wd_status.chip);
	WIMAX_PRINT(LOG_NOTICE, "Wimax : Firmware info: %s", wd_status.firmware);
	printf("Wimax : Chip info: %s, Firmware info: %s\n", wd_status.chip, wd_status.firmware);
#endif

	len = fill_diode_control_cmd(req_data, diode_on);
	set_data(req_data, len);

	len = fill_mac_req(req_data);
	set_data(req_data, len);

	process_events_by_mask(500, WDS_MAC);

#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_NOTICE, "Wimax : MAC: %02x:%02x:%02x:%02x:%02x:%02x", wd_status.mac[0], wd_status.mac[1], wd_status.mac[2], wd_status.mac[3], wd_status.mac[4], wd_status.mac[5]);
	printf("Wimax : MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", wd_status.mac[0], wd_status.mac[1], wd_status.mac[2], wd_status.mac[3], wd_status.mac[4], wd_status.mac[5]);
#endif

	len = fill_string_info_req(req_data);
	set_data(req_data, len);

	process_events_by_mask(500, WDS_CHIP | WDS_FIRMWARE);

	len = fill_auth_policy_req(req_data);
	set_data(req_data, len);

	process_events_by_mask(500, WDS_OTHER);

	len = fill_auth_method_req(req_data);
	set_data(req_data, len);

	process_events_by_mask(500, WDS_OTHER);

	len = fill_auth_set_cmd(req_data, ssid);
	set_data(req_data, len);

	return 0;
}

static int scan_loop(void)
{
	unsigned char req_data[MAX_PACKET_LEN];
	int len;
	char buf[40];

	sprintf(buf, "echo %d > /var/run/madwimax.pid", (int)getpid());
	system(buf);
	system("echo 0 > /var/state/wimaxStatus2");
	while (1 == 1) {
		WIMAX_PRINT(LOG_INFO, "Wimax : link status %d \n", wd_status.link_status);
	#if WIMAX_DEBUG == 1
		printf("Wimax : link status %d \n", wd_status.link_status);
	#endif

#if WIJET_SUPPORT == 1
		if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid == 0x7F00){	
			process_events_by_mask(3000, 0); // delay 3 seconds
			/* issue scan command */
			if(wd_status.link_status != 2){ // If link status=2, scan has no result so just discard it.
				if(wijet_transaction(wijet_scan, sizeof(wijet_scan), 0, 0, "wijet_scan", WIJET_ONLY_WRITE) < 0 ){
					WIMAX_PRINT(LOG_INFO, "WiJET scan command failed !");    
				}
			}
	#if EAP_OVER_USB == 1
			/* 30 seconds and handshake fail. */
			if(wd_status.HsTimer){
				wd_status.HsTimer--;
				if(wd_status.HsTimer == 0){
					/* Handshake timer timeout */
					WIMAX_PRINT(LOG_INFO, "Handshake timeout !!!!!!!!!!!!!!!!");    
					usb_eap_disconnect();
				}
			}
	#endif
			continue;
		}
#endif

		if (wd_status.link_status == 0) {
			len = fill_find_network_req(req_data, 1);
			set_data(req_data, len);

			process_events_by_mask(5000, WDS_LINK_STATUS);

			if (wd_status.link_status == 0) {
				WIMAX_PRINT(LOG_INFO, "Wimax : find network level 1");
			#if WIMAX_DEBUG == 1
				printf("Wimax : find network level 1.\n");
			#endif
			} else {
				WIMAX_PRINT(LOG_INFO, "Wimax : Network found.");
			#if WIMAX_DEBUG == 1
				printf("Wimax : Network found.\n");
			#endif
			}
		} else {
			len = fill_connection_params_req(req_data);
			set_data(req_data, len);

			process_events_by_mask(500, WDS_RSSI | WDS_CINR | WDS_TXPWR | WDS_FREQ | WDS_BSID);
		#if WIMAX_DEBUG
			WIMAX_PRINT(LOG_INFO, "Wimax : RSSI: %d   CINR: %f   TX Power: %d   Frequency: %d", wd_status.rssi, wd_status.cinr, wd_status.txpwr, wd_status.freq);
			WIMAX_PRINT(LOG_INFO, "Wimax : BSID: %02x:%02x:%02x:%02x:%02x:%02x", wd_status.bsid[0], wd_status.bsid[1], wd_status.bsid[2], wd_status.bsid[3], wd_status.bsid[4], wd_status.bsid[5]);
			printf("Wimax : RSSI: %d   CINR: %f   TX Power: %d   Frequency: %d\n", wd_status.rssi, wd_status.cinr, wd_status.txpwr, wd_status.freq);
			printf("Wimax : BSID: %02x:%02x:%02x:%02x:%02x:%02x\n", wd_status.bsid[0], wd_status.bsid[1], wd_status.bsid[2], wd_status.bsid[3], wd_status.bsid[4], wd_status.bsid[5]);
		#endif
			len = fill_state_req(req_data);
			set_data(req_data, len);

			process_events_by_mask(500, WDS_STATE);
		#if WIMAX_DEBUG
			WIMAX_PRINT(LOG_INFO, "Wimax : State: %s   Number: %d   Response: %d", wimax_states[wd_status.state], wd_status.state, wd_status.link_status);
			printf("Wimax : State: %s   Number: %d   Response: %d\n", wimax_states[wd_status.state], wd_status.state, wd_status.link_status);
		#endif
			if (first_nego_flag) {
				first_nego_flag = 0;
				len = fill_find_network_req(req_data, 2);
				set_data(req_data, len);
			}

			process_events_by_mask(5000, WDS_LINK_STATUS);
		}
	}
	return 0;
}

/* print version */
void version()
{
	printf("madwimax-0.1.1 \n");
}

static void exit_release_resources(int code)
{
	int i;
	system("rm -f /var/run/madwimax.pid");
	system("echo 0 > /var/state/wimaxStatus2");
	if(tap_fd >= 0) {
#if WIJET_SUPPORT==1
		if(wd_status.dev_vid == 0x1076 && wd_status.dev_pid == 0x7F00){
			if_down();
		}else
#endif
		system("wimax.sh down");
		while (wait(NULL) > 0) {}
		if_release();
		while (wait(NULL) > 0) {}
	}
	if(ctx != NULL) {
#if WIJET_SUPPORT==0 // after unplug device, rx callback function with read error : 5 and all resources would be freed in cb_req().
		if(req_transfer != NULL) {
			libusb_cancel_transfer(req_transfer);
			libusb_free_transfer(req_transfer);
		}
#else
		if(wd_status.dev_vid== 0x1076 && wd_status.dev_pid== 0x7F00){
			for(i=0; i<MAX_RX_BUFF_CNT; i++){
				if(drx_buf[i].used && drx_buf[i].utransfer){
					libusb_cancel_transfer(drx_buf[i].utransfer);
					libusb_free_transfer(drx_buf[i].utransfer);
					drx_buf[i].used =0;
				}
			}
		}else if(req_transfer != NULL) {
			libusb_cancel_transfer(req_transfer);
			libusb_free_transfer(req_transfer);
		}
#endif
		libusb_set_pollfd_notifiers(ctx, NULL, NULL, NULL);
		if(fds != NULL) {
			free(fds);
		}
		if(devh != NULL) {
			libusb_release_interface(devh, 0);
			if (kernel_driver_active)
				libusb_attach_kernel_driver(devh, 0);
			libusb_unlock_events(ctx);
			libusb_close(devh);
		}
		libusb_exit(ctx);
	}
#if WIMAX_DEBUG
	WIMAX_PRINT(LOG_INFO, "Wimax : madwimax exit");
	printf("Wimax : madwimax exit\n");
#endif
	exit(code);
}

static void sighandler_exit(int signum)
{
	exit_release_resources(0);
}

static void sighandler_wait_child(int signum)
{
	int status;
	wait3(&status, WNOHANG, NULL);
#if WIMAX_DEBUG == 1
	WIMAX_PRINT(LOG_INFO, "Wimax : Child exited with status %d", status);
#endif
}

static void sighandler_set_info(int signum)
{
	int rssi, rssi_percent;
	FILE *f = fopen("/var/state/wimax", "w");
	if (f) {
		if (wd_status.link_status >= 1) {
			fprintf(f, "%d %02x:%02x:%02x:%02x:%02x:%02x %02x:%02x:%02x:%02x:%02x:%02x ", wd_status.link_status, 
				wd_status.bsid[0], wd_status.bsid[1], wd_status.bsid[2], wd_status.bsid[3], wd_status.bsid[4], wd_status.bsid[5], 
				wd_status.mac[0], wd_status.mac[1], wd_status.mac[2], wd_status.mac[3], wd_status.mac[4], wd_status.mac[5]);
			rssi = (int)wd_status.rssi;
			if (rssi >= 0 || rssi < -150) {		// fix overflow
				rssi = -150;
				rssi_percent = 0;
			} else {
				rssi_percent = rssi;
				if (rssi_percent >= -85) {
					rssi_percent = (rssi_percent + 85);	// percentage : (rssi+85)*(100/85)
					rssi_percent = rssi_percent + (rssi_percent >> 3) + (rssi_percent >> 4);
				}
				if (rssi_percent <= 0)
					rssi_percent = 1;
				if (rssi_percent >= 100)
					rssi_percent = 99;
			}
			if ( wd_status.cinr < -100.0 ||  wd_status.cinr > 100.0)	// fix overflow
				fprintf(f, "%d --- %d", rssi, rssi_percent);
			else
				fprintf(f, "%d %2.2f %d", rssi, wd_status.cinr, rssi_percent);
		} else {
			fprintf(f, "%d --- --- ", wd_status.link_status);
			fprintf(f, "-150 --- 0");
		}
		if (strlen(wd_status.firmware) > 0)
			fprintf(f, " %s", wd_status.firmware);
		else
			fprintf(f, " ---");
		fclose(f);
	}
}

int main(int argc, char **argv)
{
	struct sigaction sigact;
	int r = 1;

	sigact.sa_handler = sighandler_exit;
	sigemptyset(&sigact.sa_mask);
	sigact.sa_flags = 0;
	sigaction(SIGINT, &sigact, NULL);
	sigaction(SIGTERM, &sigact, NULL);
	sigaction(SIGQUIT, &sigact, NULL);
	sigact.sa_handler = sighandler_wait_child;
	sigaction(SIGCHLD, &sigact, NULL);

	sigact.sa_handler = sighandler_set_info;
	sigaction(SIGUSR1, &sigact, NULL);

	if (daemonize) {
		daemon(0, 0);
	}

	WIMAX_PRINT(LOG_NOTICE, "Wimax : Driver initialization!!!!!!!!!!!!");
	memset(&wd_status, 0, sizeof(struct wimax_dev_status));

	r = libusb_init(&ctx);
	if (r < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : failed to initialise libusb");
	#if WIMAX_DEBUG == 1
		printf("Wimax : failed to initialise libusb\n");
	#endif
		exit_release_resources(1);
	}

	devh = find_wimax_device();
	if (devh == NULL) {
		WIMAX_PRINT(LOG_ERR, "Wimax : Could not find/open device");
	#if WIMAX_DEBUG == 1
		printf("Wimax : Could not find/open device\n");
	#endif
		exit_release_resources(1);
	}

#if WIJET_SUPPORT == 1
	if(wd_status.dev_vid == 0x1076 && wd_status.dev_pid == 0x7F40){
		WIMAX_PRINT(LOG_NOTICE, "Wimax : Wijet storage device found");
		detach_dvd = 1; /* storage device use : interface1, alt interface0 */
	}else
#endif
	WIMAX_PRINT(LOG_NOTICE, "Wimax : Device found");
#if WIMAX_DEBUG == 1
	printf("Wimax : Device found\n");
#endif

	if (detach_dvd && libusb_kernel_driver_active(devh, IF_DVD) == 1) {
		WIMAX_PRINT(LOG_NOTICE, "Wimax : libusb kernel driver active! Try to detach kernel driver!");
		r = libusb_detach_kernel_driver(devh, IF_DVD);
		if (r < 0) {
			WIMAX_PRINT(LOG_ERR, "Wimax : kernel driver detach error %d", r);
		#if WIMAX_DEBUG == 1
			printf("Wimax : kernel driver detach error %d\n", r);
		#endif
		} else {
			WIMAX_PRINT(LOG_INFO, "Wimax : detached pseudo-DVD kernel driver");
		#if WIMAX_DEBUG == 1
				printf("Wimax : detached pseudo-DVD kernel driver\n");
		#endif
		}
	}

	if (libusb_kernel_driver_active(devh, IF_MODEM) == 1) {
		kernel_driver_active = 1;
		r = libusb_detach_kernel_driver(devh, IF_MODEM);
		if (r < 0) {
			WIMAX_PRINT(LOG_ERR, "Wimax : kernel driver detach error %d", r);
		#if WIMAX_DEBUG == 1
			printf("Wimax : kernel driver detach error %d\n", r);
		#endif
		} else {
			WIMAX_PRINT(LOG_ERR, "Wimax : detached modem kernel driver");
		#if WIMAX_DEBUG == 1
			printf("Wimax : detached modem kernel driver\n");
		#endif
		}
	}

	if(detach_dvd)
		r = libusb_claim_interface(devh, IF_DVD);
	else
		r = libusb_claim_interface(devh, IF_MODEM);
	if (r < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : Claim usb interface error %d", r); // LIBUSB_ERROR_NOT_FOUND 
	#if WIMAX_DEBUG == 1
		printf("Wimax : Claim usb interface error %d\n", r);
	#endif
		exit_release_resources(1);
	}

	WIMAX_PRINT(LOG_INFO, "Wimax : Claimed interface %d", detach_dvd?IF_DVD:IF_MODEM);
#if WIMAX_DEBUG == 1
	printf("Wimax : Claimed interface\n");
#endif

#if WIJET_SUPPORT == 1
	if(wd_status.dev_vid == 0x1076 && wd_status.dev_pid == 0x7F40){
		int result;
		char buf[64] __attribute__ ((aligned (2)));
		int cnt = 0;

		WIMAX_PRINT(LOG_NOTICE, "Wimax : Wijet storage mode switch to modem mode");
mode_switch:
		cnt++;
		/* dev_handle, bmRequest, bRequest, wValue, wIndex, data, wLength, timeout */
		result = libusb_control_transfer (devh, 0xA1, 0xA0, 0, 1, buf, 64, 0);
		if(result<0){
			WIMAX_PRINT(LOG_ERR, "Wimax : libusb_control_transfer() failed!!!!error %d", result); //LIBUSB_ERROR_TIMEOUT
			if(cnt < 19){
				goto mode_switch;
			}else{
				WIMAX_PRINT(LOG_ERR, "Wimax : libusb_control_transfer() tried 20 times and give up! failed!!!!");
			}
		}else{
			WIMAX_PRINT(LOG_NOTICE, "Wimax : Storage mode switch to modem mode command success!!!Try %d times!!", cnt);
		}
		exit_release_resources(0);
	}
#endif

	alloc_fds();
	libusb_set_pollfd_notifiers(ctx, cb_add_pollfd, cb_remove_pollfd, NULL);

#if WIJET_SUPPORT == 1
	EP_IN = (2 | LIBUSB_ENDPOINT_IN); /* default endpoint number for Samsung */
	EP_OUT = (4 | LIBUSB_ENDPOINT_OUT);
	if(wd_status.dev_vid == 0x1076 && wd_status.dev_pid == 0x7F00){
		EP_IN = (2 | LIBUSB_ENDPOINT_IN); /* default endpoint number for Wijet */
		EP_OUT = (1 | LIBUSB_ENDPOINT_OUT);
		r = wijet_initialization();
	}else
#endif
	r = init();
	if (r < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : init error %d", r);
	#if WIMAX_DEBUG == 1
		printf("Wimax : init error %d\n", r);
	#endif
		exit_release_resources(1);
	}

	if_create();
	cb_add_pollfd(tap_fd, POLLIN, NULL);

	WIMAX_PRINT(LOG_INFO, "Wimax : before scan_loop()");
	r = scan_loop();
	if (r < 0) {
		WIMAX_PRINT(LOG_ERR, "Wimax : scan_loop error %d", r);
	#if WIMAX_DEBUG == 1
		printf("Wimax : scan_loop error %d\n", r);
	#endif
		exit_release_resources(1);
	}

	exit_release_resources(0);
	return 0;
}


#if EAP_OVER_USB == 1

int usb_eap_disconnect(void)
{
	int ret = 0;

#if 0 //fanny fixme.....
	if(!wd_status.Ready)
	{
		WIMAX_PRINT(LOG_ERR, "Wimax Modem not ready");
		ret |= 1;
	}
#endif

	if(ret != 0) return ret;
	if_down();
	wd_status.link_status = 0;
	wd_status.HsTimer = 0; //stop handshake timer

	wd_status.eap_interface.disconnect();
	return ret;
}

int usb_eap_connect(void)
{
	int ret = 0;

#if 0 //fanny fixme.....
	if(!WimaxDev.Ready)
	{
		WIMAX_PRINT(LOG_ERR, "Wimax Modem not ready");
		ret |= 1;
	}
#endif

	if(strlen(wd_status.eap_interface.identity) == 0)
	{
		WIMAX_PRINT(LOG_ERR, "No EAP identity");
		ret |= 2;
	}

	if(strlen(wd_status.eap_interface.mschap2_data.username) == 0)
	{
		WIMAX_PRINT(LOG_ERR, "NO EAP-TTLS Phase 2 username ");
		ret |= 4;
	}

	if(strlen(wd_status.eap_interface.mschap2_data.password) == 0)
	{
		WIMAX_PRINT(LOG_ERR, "NO EAP-TTLS Phase 2 password");
		ret |= 8;
	}

	if( wd_status.network_idx_selected >= wd_status.num_of_network_found )
	{
		WIMAX_PRINT(LOG_ERR, "Wijet: no network found in wijet_connect()");
		wd_status.link_status = 0;
		ret |= 32;
	}

	if(ret != 0) return ret;

	//fix_me clean up previous active method anyway, study more, if doing this while handshaking
	//in progress, we could have trouble.
	eap_cleanup(&wd_status.eap_interface.activemethod);

	WIMAX_PRINT(LOG_INFO, "EAP trigger connecting....  id = %s, username = %s, password = %s",
				    wd_status.eap_interface.identity,
				    wd_status.eap_interface.mschap2_data.username,
				    wd_status.eap_interface.mschap2_data.password);

    wd_status.trigger_connect = 1;

	return ret;
}

int usb_eap_set_username(char * username)
{
	strcpy(phase2_username, username);
}

int usb_eap_set_password(char * password)
{
	strcpy(phase2_password, password);
}

int usb_eap_set_id(char * identity)
{
	strcpy(eap_identity, identity);
}

int usb_eap_set_cipher(char * cipher)
{
	cipher_id = atoi(cipher);
}

int usb_eap_set_auto_mode(char * mode)
{
	auto_mode = atoi(mode);
}

int usb_eap_get_info(char * info, int len)
{
	char cipher_suite_str[30];

	switch(cipher_id)
	{
		case 0:
			strcpy(cipher_suite_str, "RSA_RC4_128_MD5");
			break;

		case 1:
			strcpy(cipher_suite_str, "RSA_AES_256_SHA");
			break;

		case 2:
			strcpy(cipher_suite_str, "DHE_RSA_AES_256_SHA");
			break;

		default:
			strcpy(info, "Unknown Cipher Suite");
			break;

	}

	if( (strlen(eap_identity) + 
		 strlen(phase2_username) + 
		 strlen(phase2_password) + 
		 strlen("OFF") + 
		 strlen(cipher_suite_str)) > (len - 140) ) return 0;

#if 1
	sprintf(info, "%s%s%s%s%s%s%s%s%s%s%s%s",
		          "\n\r  EAP Configuration:",
				  "\n\r    EAP identity : ", eap_identity,
				  "\n\r    User Name : ", phase2_username,
				  "\n\r    Password : ", phase2_password,
                  "\n\r    Auto Connect : ", auto_mode? "ON": "OFF",
				  "\n\r    Cipher Suite : ", cipher_suite_str,
		          "\n\r");      
#else

	sprintf(info, "\n\rEAP Configuration:\n\r  EAP identity : %s\n\r  User Name : %s\n\r  Password : %s\n\r\
						Auto Connect : %s\n\r  Cipher Suite : %s\n\r",		                
                       eap_identity,
					   phase2_username,
					   phase2_password,
					   auto_mode? "ON": "OFF",
					   cipher_suite_str);
#endif
	return 1;
}

#endif //EAP_OVER_USB


