671 lines
20 KiB
C
671 lines
20 KiB
C
/*****************************************************************************
|
|
* sdla_gsm.c
|
|
*
|
|
* WANPIPE(tm) AFT W400 Hardware Support
|
|
*
|
|
* Authors: Moises Silva <moy@sangoma.com>
|
|
*
|
|
* Copyright: (c) 2011 Sangoma Technologies Inc.
|
|
*
|
|
* 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.
|
|
* ============================================================================
|
|
* Oct 06, 2011 Moises Silva Initial Version
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* The GSM card does not really have front end access, all register access should be done through card->hw_iface instead of fe-> pointer
|
|
*/
|
|
# include "wanpipe_includes.h"
|
|
# include "wanpipe_defines.h"
|
|
# include "wanpipe_debug.h"
|
|
# include "wanpipe_abstr.h"
|
|
# include "wanpipe_common.h"
|
|
# include "wanpipe_events.h"
|
|
# include "wanpipe.h"
|
|
# include "wanpipe_events.h"
|
|
# include "if_wanpipe_common.h"
|
|
# include "aft_core.h" /* for private_area_t */
|
|
# include "sdla_gsm.h"
|
|
|
|
extern WAN_LIST_HEAD(, wan_tdmv_) wan_tdmv_head;
|
|
|
|
static int wp_gsm_config(void *pfe);
|
|
static int wp_gsm_unconfig(void *pfe);
|
|
static int wp_gsm_post_init(void *pfe);
|
|
static int wp_gsm_post_unconfig(void* pfe);
|
|
static int wp_gsm_if_config(void *pfe, u32 mod_map, u8);
|
|
static int wp_gsm_if_unconfig(void *pfe, u32 mod_map, u8);
|
|
static int wp_gsm_disable_irq(void *pfe);
|
|
static int wp_gsm_intr(sdla_fe_t *);
|
|
static int wp_gsm_dchan_tx(sdla_fe_t *fe, void *src_data_buffer, u32 buffer_len);
|
|
static int wp_gsm_check_intr(sdla_fe_t *);
|
|
static int wp_gsm_polling(sdla_fe_t*);
|
|
static int wp_gsm_udp(sdla_fe_t*, void*, unsigned char*);
|
|
static unsigned int wp_gsm_active_map(sdla_fe_t* fe, unsigned char);
|
|
static unsigned char wp_gsm_fe_media(sdla_fe_t *fe);
|
|
static int wp_gsm_set_dtmf(sdla_fe_t*, int, unsigned char);
|
|
static int wp_gsm_intr_ctrl(sdla_fe_t*, int, u_int8_t, u_int8_t, unsigned int);
|
|
static int wp_gsm_event_ctrl(sdla_fe_t*, wan_event_ctrl_t*);
|
|
static int wp_gsm_get_link_status(sdla_fe_t *fe, unsigned char *status,int mod_no);
|
|
|
|
#if defined(AFT_TDM_API_SUPPORT)
|
|
static int wp_gsm_watchdog(void *card_ptr);
|
|
#endif
|
|
|
|
static void wp_gsm_toggle_pcm_loopback(sdla_t *card)
|
|
{
|
|
u32 reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_GLOBAL_REG, ®);
|
|
if (wan_test_bit(AFT_GSM_GLOBAL_PCM_LOOPBACK_BIT, ®)) {
|
|
wan_clear_bit(AFT_GSM_GLOBAL_PCM_LOOPBACK_BIT, ®);
|
|
DEBUG_EVENT("%s: PCM Loopback is now disabled\n", card->devname);
|
|
} else {
|
|
wan_set_bit(AFT_GSM_GLOBAL_PCM_LOOPBACK_BIT, ®);
|
|
DEBUG_EVENT("%s: PCM Loopback is now enabled\n", card->devname);
|
|
}
|
|
card->hw_iface.bus_write_4(card->hw, AFT_GSM_GLOBAL_REG, reg);
|
|
}
|
|
|
|
static void wp_gsm_dump_configuration(sdla_t *card)
|
|
{
|
|
u32 reg = 0;
|
|
int mod_no = card->wandev.comm_port + 1;
|
|
|
|
reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_GLOBAL_REG, ®);
|
|
DEBUG_EVENT("%s: Dumping Global Configuration (reg=%X)\n", card->devname, reg);
|
|
DEBUG_EVENT("%s: PLL Reset: %d\n", card->devname, wan_test_bit(AFT_GSM_PLL_RESET_BIT, ®));
|
|
DEBUG_EVENT("%s: PLL Phase Shift Overflow: %d\n", card->devname, wan_test_bit(AFT_GSM_PLL_PHASE_SHIFT_OVERFLOW_BIT, ®));
|
|
DEBUG_EVENT("%s: PLL Input Clock Lost: %d\n", card->devname, wan_test_bit(AFT_GSM_PLL_INPUT_CLOCK_LOST_BIT, ®));
|
|
DEBUG_EVENT("%s: PLL Output Clock Stopped: %d\n", card->devname, wan_test_bit(AFT_GSM_PLL_OUTPUT_CLOCK_STOPPED_BIT, ®));
|
|
DEBUG_EVENT("%s: PLL Locked Bit: %d\n", card->devname, wan_test_bit(AFT_GSM_PLL_LOCKED_BIT, ®));
|
|
DEBUG_EVENT("%s: SIM Muxing Error: %d\n", card->devname, wan_test_bit(AFT_GSM_SIM_MUXING_ERROR_BIT, ®));
|
|
DEBUG_EVENT("%s: PCM Loopback Bit: %d\n", card->devname, wan_test_bit(AFT_GSM_GLOBAL_PCM_LOOPBACK_BIT, ®));
|
|
DEBUG_EVENT("%s: Global Shutdown Bit: %d\n", card->devname, wan_test_bit(AFT_GSM_GLOBAL_SHUTDOWN_BIT, ®));
|
|
|
|
reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_CONFIG_REG), ®);
|
|
DEBUG_EVENT("%s: Dumping Module %d Configuration (reg=%X)\n", card->devname, mod_no, reg);
|
|
DEBUG_EVENT("%s: Module %d Power: %d\n", card->devname, mod_no, wan_test_bit(AFT_GSM_MOD_POWER_BIT, ®));
|
|
DEBUG_EVENT("%s: Module %d Reset: %d\n", card->devname, mod_no, wan_test_bit(AFT_GSM_MOD_RESET_BIT, ®));
|
|
DEBUG_EVENT("%s: Module %d Power Monitor: %d\n", card->devname, mod_no, wan_test_bit(AFT_GSM_MOD_POWER_MONITOR_BIT, ®));
|
|
DEBUG_EVENT("%s: Module %d Tx Monitor: %d\n", card->devname, mod_no, wan_test_bit(AFT_GSM_MOD_TX_MONITOR_BIT, ®));
|
|
DEBUG_EVENT("%s: Module %d SIM Inserted: %d\n", card->devname, mod_no, wan_test_bit(AFT_GSM_MOD_SIM_INSERTED_BIT, ®));
|
|
DEBUG_EVENT("%s: Module %d SIM Select: 0x%X\n", card->devname, mod_no, aft_gsm_get_sim_no(reg));
|
|
DEBUG_EVENT("%s: Module %d UART Baud Rate: 0x%X\n", card->devname, mod_no, aft_gsm_get_uart_baud_rate(reg));
|
|
DEBUG_EVENT("%s: Module %d Service: %d\n", card->devname, mod_no, wan_test_bit(AFT_GSM_MOD_SERVICE_BIT, ®));
|
|
}
|
|
|
|
|
|
int wp_gsm_iface_init(void *p_fe, void *pfe_iface)
|
|
{
|
|
sdla_fe_t *fe = p_fe;
|
|
sdla_fe_iface_t *fe_iface = pfe_iface;
|
|
|
|
fe_iface->config = &wp_gsm_config;
|
|
fe_iface->unconfig = &wp_gsm_unconfig;
|
|
|
|
fe_iface->post_init = &wp_gsm_post_init;
|
|
|
|
fe_iface->if_config = &wp_gsm_if_config;
|
|
fe_iface->if_unconfig = &wp_gsm_if_unconfig;
|
|
fe_iface->post_unconfig = &wp_gsm_post_unconfig;
|
|
fe_iface->active_map = &wp_gsm_active_map;
|
|
|
|
fe_iface->isr = &wp_gsm_intr;
|
|
fe_iface->disable_irq = &wp_gsm_disable_irq;
|
|
fe_iface->check_isr = &wp_gsm_check_intr;
|
|
|
|
fe_iface->polling = &wp_gsm_polling;
|
|
fe_iface->process_udp = &wp_gsm_udp;
|
|
fe_iface->get_fe_media = &wp_gsm_fe_media;
|
|
|
|
fe_iface->set_dtmf = &wp_gsm_set_dtmf;
|
|
fe_iface->intr_ctrl = &wp_gsm_intr_ctrl;
|
|
|
|
fe_iface->event_ctrl = &wp_gsm_event_ctrl;
|
|
#if defined(AFT_TDM_API_SUPPORT) || defined(AFT_API_SUPPORT)
|
|
fe_iface->watchdog = &wp_gsm_watchdog;
|
|
#endif
|
|
fe_iface->get_fe_status = &wp_gsm_get_link_status;
|
|
|
|
/* XXX this should be renamed to dchan_tx since is not BRI-specific XXX */
|
|
fe_iface->isdn_bri_dchan_tx = &wp_gsm_dchan_tx;
|
|
|
|
/*
|
|
* XXX Do we need this event initalization?? XXX
|
|
* WAN_LIST_INIT(&fe->event);
|
|
*/
|
|
wan_spin_lock_irq_init(&fe->lockirq, "wan_gsm_lock");
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_config(void *pfe)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_unconfig(void *pfe)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_post_unconfig(void *pfe)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#define UPDATE_SIM_STATUS(card, fe) \
|
|
if (card->wandev.te_link_state) { \
|
|
card->wandev.te_link_state(card); \
|
|
} else { \
|
|
DEBUG_ERROR("%s: ERROR: GSM module %d has no te_link_state pointer!\n", card->devname, mod_no); \
|
|
} \
|
|
if (card->wandev.te_report_alarms) { \
|
|
card->wandev.te_report_alarms(card, (fe->fe_status == FE_CONNECTED ? 0 : 1)); \
|
|
}
|
|
|
|
int wp_gsm_update_sim_status(void *pcard)
|
|
{
|
|
sdla_t *card = pcard;
|
|
u32 reg = 0;
|
|
sdla_fe_t *fe = &card->fe;
|
|
int sim_inserted = 0;
|
|
int mod_no = card->wandev.comm_port + 1;
|
|
mod_no = card->wandev.comm_port + 1;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_CONFIG_REG), ®);
|
|
sim_inserted = wan_test_bit(AFT_GSM_MOD_SIM_INSERTED_BIT, ®) ? WAN_FALSE : WAN_TRUE;
|
|
if (sim_inserted && fe->fe_status != FE_CONNECTED) {
|
|
DEBUG_EVENT("%s: GSM module %d has a SIM inserted! (%s -> %s)\n", card->devname, mod_no,
|
|
FE_STATUS_DECODE(fe->fe_status), FE_STATUS_DECODE(FE_CONNECTED));
|
|
fe->fe_status = FE_CONNECTED;
|
|
UPDATE_SIM_STATUS(card, fe);
|
|
} else if (!sim_inserted && fe->fe_status != FE_DISCONNECTED){
|
|
DEBUG_EVENT("%s: GSM module %d has no SIM inserted! (%s -> %s)\n", card->devname, mod_no,
|
|
FE_STATUS_DECODE(fe->fe_status), FE_STATUS_DECODE(FE_DISCONNECTED));
|
|
fe->fe_status = FE_DISCONNECTED;
|
|
UPDATE_SIM_STATUS(card, fe);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_post_init(void *pfe)
|
|
{
|
|
sdla_fe_t *fe = pfe;
|
|
sdla_t *card = fe->card;
|
|
wp_gsm_update_sim_status(card);
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_if_config(void *pfe, u32 mod_map, u8 usedby)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_if_unconfig(void *pfe, u32 mod_map, u8 usedby)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_disable_irq(void *pfe)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* voice active map (not dchan) */
|
|
static unsigned int wp_gsm_active_map(sdla_fe_t* fe, unsigned char line)
|
|
{
|
|
return (0x01 << (WAN_FE_LINENO(fe)));
|
|
}
|
|
|
|
static unsigned char wp_gsm_fe_media(sdla_fe_t *fe)
|
|
{
|
|
return fe->fe_cfg.media;
|
|
}
|
|
|
|
static int wp_gsm_set_dtmf(sdla_fe_t *fe, int mod_no, unsigned char val)
|
|
{
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int wp_gsm_polling(sdla_fe_t* fe)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_event_ctrl(sdla_fe_t *fe, wan_event_ctrl_t *ectrl)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
#include "sdla_gsm_inline.h"
|
|
static int wp_gsm_udp(sdla_fe_t *fe, void *p_udp_cmd, unsigned char *data)
|
|
{
|
|
wan_cmd_t *udp_cmd = (wan_cmd_t *)p_udp_cmd;
|
|
wan_femedia_t *fe_media = NULL;
|
|
sdla_t *card = fe->card;
|
|
wan_gsm_udp_t *gsm_udp = (wan_gsm_udp_t *)data;
|
|
int mod_no = 0;
|
|
u32 reg = 0;
|
|
|
|
switch (udp_cmd->wan_cmd_command) {
|
|
|
|
case WAN_GET_MEDIA_TYPE:
|
|
{
|
|
fe_media = (wan_femedia_t*)data;
|
|
memset(fe_media, 0, sizeof(wan_femedia_t));
|
|
fe_media->media = fe->fe_cfg.media;
|
|
fe_media->sub_media = fe->fe_cfg.sub_media;
|
|
fe_media->chip_id = 0x00;
|
|
fe_media->max_ports = MAX_GSM_MODULES;
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = sizeof(wan_femedia_t);
|
|
}
|
|
break;
|
|
|
|
case WAN_GSM_REGDUMP:
|
|
{
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
wp_gsm_dump_configuration(card);
|
|
}
|
|
break;
|
|
|
|
case WAN_GSM_UART_DEBUG:
|
|
{
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
if (gsm_udp && gsm_udp->u.uart_debug == WAN_TRUE) {
|
|
wan_set_bit(AFT_GSM_UART_DEBUG_ENABLED_BIT, &card->TracingEnabled);
|
|
DEBUG_EVENT("%s: UART debugging enabled\n", card->devname);
|
|
} else {
|
|
wan_clear_bit(AFT_GSM_UART_DEBUG_ENABLED_BIT, &card->TracingEnabled);
|
|
DEBUG_EVENT("%s: UART debugging disabled\n", card->devname);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WAN_GSM_AUDIO_DEBUG:
|
|
{
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
DEBUG_EVENT("%s: Audio debugging toggled\n", card->devname);
|
|
wan_set_bit(AFT_GSM_AUDIO_DEBUG_TOGGLE_BIT, &card->TracingEnabled);
|
|
}
|
|
break;
|
|
|
|
case WAN_GSM_PLL_RESET:
|
|
{
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
wp_gsm_pll_reset(card);
|
|
}
|
|
break;
|
|
|
|
case WAN_GSM_POWER_TOGGLE:
|
|
{
|
|
int map = 0;
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
reg = 0;
|
|
mod_no = card->wandev.comm_port + 1;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_CONFIG_REG), ®);
|
|
DEBUG_EVENT("%s: Performing power toggle on module %d\n", card->devname, mod_no);
|
|
if (wan_test_bit(AFT_GSM_MOD_POWER_MONITOR_BIT, ®)) {
|
|
map = wp_gsm_toggle_power(card->hw, (1 << (mod_no - 1)), WAN_FALSE);
|
|
} else {
|
|
map = wp_gsm_toggle_power(card->hw, (1 << (mod_no - 1)), WAN_TRUE);
|
|
}
|
|
if (map) {
|
|
DEBUG_ERROR("%s: Error: Timed out performing power toggle for module %d (mod_map=0x%X)\n", card->devname, mod_no, map);
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_TIMEOUT;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WAN_GSM_UPDATE_SIM_STATUS:
|
|
{
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
wp_gsm_update_sim_status(card);
|
|
}
|
|
break;
|
|
|
|
case WAN_GSM_PCM_LOOPBACK:
|
|
{
|
|
udp_cmd->wan_cmd_return_code = WAN_CMD_OK;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
wp_gsm_toggle_pcm_loopback(card);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
udp_cmd->wan_cmd_return_code = WAN_UDP_INVALID_CMD;
|
|
udp_cmd->wan_cmd_data_len = 0;
|
|
}
|
|
break;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int wp_gsm_watchdog(void *card_ptr)
|
|
{
|
|
wan_smp_flag_t flags;
|
|
sdla_t *card = card_ptr;
|
|
sdla_fe_t *fe = &card->fe;
|
|
sdla_gsm_param_t *gsm_data = &fe->fe_param.gsm;
|
|
private_area_t *chan = NULL;
|
|
int txlen = 0;
|
|
int avail = 0;
|
|
int kick_user = 0;
|
|
|
|
#if defined(CONFIG_PRODUCT_WANPIPE_TDM_VOICE)
|
|
if (card->wan_tdmv.sc) {
|
|
/* Do nothing if configured in TDM VOICE mode (DAHDI), the UART is serviced in the DAHDI tx/rx loop code */
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
wp_gsm_update_sim_status(card);
|
|
|
|
chan = card->u.aft.dev_to_ch_map[GSM_DCHAN_LOGIC_CHAN];
|
|
if (!chan || wan_test_bit(0,&chan->interface_down)) {
|
|
return 0;
|
|
}
|
|
|
|
/* tx until done and call wanpipe_wake_stack() when finished */
|
|
wan_spin_lock_irq(&fe->lockirq, &flags);
|
|
|
|
/* check if we have something to transmit and space to transmit it */
|
|
if (gsm_data->uart_txbuf_len) {
|
|
avail = wp_gsm_uart_check_tx_fifo(card);
|
|
if (avail) {
|
|
txlen = avail > (gsm_data->uart_txbuf_len - gsm_data->uart_tx_cnt)
|
|
? (gsm_data->uart_txbuf_len - gsm_data->uart_tx_cnt)
|
|
: avail;
|
|
wp_gsm_uart_tx_fifo(card, (gsm_data->uart_txbuf + gsm_data->uart_tx_cnt), txlen);
|
|
gsm_data->uart_tx_cnt += txlen;
|
|
if (gsm_data->uart_tx_cnt == gsm_data->uart_txbuf_len) {
|
|
/* everything was transmitted */
|
|
gsm_data->uart_txbuf_len = 0;
|
|
gsm_data->uart_tx_cnt = 0;
|
|
chan = card->u.aft.dev_to_ch_map[GSM_DCHAN_LOGIC_CHAN];
|
|
kick_user++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!wan_test_bit(0,&chan->uart_rx_status)){
|
|
/* service the rx fifo */
|
|
avail = wp_gsm_uart_rx_fifo(card, chan->uart_rx_buffer, sizeof(chan->uart_rx_buffer));
|
|
if (avail) {
|
|
chan->uart_rx_sz=avail;
|
|
wan_set_bit(0,&chan->uart_rx_status);
|
|
WAN_TASKLET_SCHEDULE((&chan->common.bh_task));
|
|
}
|
|
}
|
|
|
|
wan_spin_unlock_irq(&fe->lockirq, &flags);
|
|
|
|
if (kick_user) {
|
|
wanpipe_wake_stack(chan);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_intr_ctrl(sdla_fe_t *fe, int mod_no, u_int8_t type, u_int8_t mode, unsigned int ts_map)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_check_intr(sdla_fe_t *fe)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_intr(sdla_fe_t *fe)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int wp_gsm_dchan_tx(sdla_fe_t *fe, void *src_data_buffer, u32 buffer_len)
|
|
{
|
|
wan_smp_flag_t flags;
|
|
sdla_t *card = fe->card;
|
|
sdla_gsm_param_t *gsm_data = &fe->fe_param.gsm;
|
|
int ret = 0;
|
|
if (!card) {
|
|
DEBUG_ERROR("No card attached to GSM front end!\n");
|
|
return 0;
|
|
}
|
|
|
|
wan_spin_lock_irq(&fe->lockirq, &flags);
|
|
|
|
if (!src_data_buffer) {
|
|
/* they want us to perform a space check */
|
|
ret = gsm_data->uart_txbuf_len;
|
|
goto done;
|
|
}
|
|
if (gsm_data->uart_txbuf_len) {
|
|
ret = -EBUSY;
|
|
goto done;
|
|
}
|
|
if (buffer_len > GSM_MAX_UART_FRAME_LEN) {
|
|
/* too big of a frame to transmit for us */
|
|
ret = -EFBIG;
|
|
goto done;
|
|
}
|
|
memcpy(gsm_data->uart_txbuf, src_data_buffer, buffer_len);
|
|
gsm_data->uart_txbuf_len = buffer_len;
|
|
|
|
done:
|
|
wan_spin_unlock_irq(&fe->lockirq, &flags);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int wp_gsm_get_link_status(sdla_fe_t *fe, unsigned char *status, int mod_no)
|
|
{
|
|
u32 reg = 0;
|
|
sdla_t *card = fe->card;
|
|
if (!card) {
|
|
DEBUG_ERROR("No card attached to GSM front end!\n");
|
|
return 0;
|
|
}
|
|
/* ignore the provided module (that one is used for multiple modules in the same line/port */
|
|
mod_no = card->wandev.comm_port + 1;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_CONFIG_REG), ®);
|
|
if (!wan_test_bit(AFT_GSM_MOD_SIM_INSERTED_BIT, ®)) {
|
|
*status = FE_CONNECTED;
|
|
} else {
|
|
*status = FE_DISCONNECTED;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static char *format_uart_data(char *dest, void *indata, int len)
|
|
{
|
|
int i;
|
|
uint8_t *data = indata;
|
|
char *p = dest;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
switch(data[i]) {
|
|
case '\r':
|
|
sprintf(p, "\\r");
|
|
p+=2;
|
|
break;
|
|
case '\n':
|
|
sprintf(p, "\\n");
|
|
p+=2;
|
|
break;
|
|
case 0x1a:
|
|
sprintf(p, "<sub>");
|
|
p+=5;
|
|
break;
|
|
default:
|
|
*p = data[i];
|
|
p++;
|
|
}
|
|
}
|
|
*p = '\0';
|
|
return dest;
|
|
}
|
|
|
|
int wp_gsm_uart_rx_fifo(void *pcard, unsigned char *rx_buff, int reqlen)
|
|
{
|
|
u32 reg = 0;
|
|
int i = 0;
|
|
int mod_no = 0;
|
|
int num_bytes = 0;
|
|
char rx_debug[AFT_GSM_UART_RX_FIFO_SIZE * 2];
|
|
sdla_t *card = pcard;
|
|
|
|
mod_no = card->wandev.comm_port + 1;
|
|
|
|
/* Check UART status */
|
|
reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_UART_STAT_REG), ®);
|
|
|
|
if (wan_test_bit(AFT_GSM_UART_RX_FIFO_OVERFLOW_BIT, ®)) {
|
|
DEBUG_ERROR("%s: RX UART FIFO overflow!\n", card->devname, mod_no);
|
|
wan_clear_bit(AFT_GSM_UART_RX_FIFO_OVERFLOW_BIT, ®);
|
|
card->hw_iface.bus_write_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_UART_STAT_REG), reg);
|
|
}
|
|
|
|
if (reqlen > AFT_GSM_UART_RX_FIFO_SIZE) {
|
|
DEBUG_ERROR("%s: Can't read more than UART FIFO size %d bytes, requested = %d!\n", card->devname, AFT_GSM_UART_RX_FIFO_SIZE, reqlen);
|
|
return 0;
|
|
}
|
|
|
|
/* Try to read from the UART if needed */
|
|
num_bytes = (reg >> AFT_GSM_UART_RX_FIFO_DATA_COUNT_OFFSET) & AFT_GSM_UART_RX_FIFO_DATA_COUNT_MASK;
|
|
if (num_bytes > 0 && AFT_GSM_UART_RX_FIFO_SIZE >= num_bytes) {
|
|
for (i = 0; i < num_bytes; i++) {
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_UART_RX_REG), ®);
|
|
rx_buff[i] = (reg & 0xFF);
|
|
}
|
|
if (wan_test_bit(AFT_GSM_UART_DEBUG_ENABLED_BIT, &card->TracingEnabled)) {
|
|
DEBUG_EVENT("%s: UART RX %d bytes: %s\n", card->devname, num_bytes, format_uart_data(rx_debug, rx_buff, num_bytes));
|
|
}
|
|
return num_bytes;
|
|
} else if (num_bytes > 0) {
|
|
DEBUG_ERROR("%s: wtf? there is %d bytes to be received in the UART of GSM module %d "
|
|
"(max expected size = %d, requested = %d)\n", card->devname, num_bytes, mod_no, AFT_GSM_UART_RX_FIFO_SIZE, reqlen);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int wp_gsm_uart_check_tx_fifo(void *pcard)
|
|
{
|
|
u32 reg = 0;
|
|
int mod_no = 0;
|
|
int num_bytes = 0;
|
|
sdla_t *card = pcard;
|
|
|
|
mod_no = card->wandev.comm_port + 1;
|
|
|
|
/* Check UART status */
|
|
reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_UART_STAT_REG), ®);
|
|
num_bytes = AFT_GSM_UART_TX_FIFO_SIZE - (reg & AFT_GSM_UART_TX_FIFO_DATA_COUNT_MASK);
|
|
if (num_bytes < 0) {
|
|
DEBUG_ERROR("%s: wtf? available tx bytes in UART = %d\n", card->devname, (reg & AFT_GSM_UART_TX_FIFO_DATA_COUNT_MASK));
|
|
return 0;
|
|
}
|
|
return num_bytes;
|
|
}
|
|
|
|
int wp_gsm_uart_tx_fifo(void *pcard, unsigned char *tx_buff, int len)
|
|
{
|
|
u32 reg = 0;
|
|
int i = 0;
|
|
int mod_no = 0;
|
|
int num_bytes = 0;
|
|
char tx_debug[AFT_GSM_UART_TX_FIFO_SIZE * 2];
|
|
sdla_t *card = pcard;
|
|
|
|
mod_no = card->wandev.comm_port + 1;
|
|
|
|
/* Check UART status */
|
|
reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_UART_STAT_REG), ®);
|
|
|
|
/* Verify overflow (should we do something else?) */
|
|
if (wan_test_bit(AFT_GSM_UART_TX_FIFO_OVERFLOW_BIT, ®)) {
|
|
DEBUG_ERROR("%s: TX UART FIFO overflow in GSM module %d!\n", card->devname, mod_no);
|
|
wan_clear_bit(AFT_GSM_UART_TX_FIFO_OVERFLOW_BIT, ®);
|
|
card->hw_iface.bus_write_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_UART_STAT_REG), reg);
|
|
}
|
|
|
|
reg = 0;
|
|
num_bytes = AFT_GSM_UART_TX_FIFO_SIZE - (reg & AFT_GSM_UART_TX_FIFO_DATA_COUNT_MASK);
|
|
|
|
if (num_bytes < 0) {
|
|
DEBUG_ERROR("%s: wtf? available tx bytes in UART = %d\n", card->devname, (reg & AFT_GSM_UART_TX_FIFO_DATA_COUNT_MASK));
|
|
return 0;
|
|
}
|
|
|
|
if (!num_bytes) {
|
|
DEBUG_ERROR("%s: Can't transmit anything to GSM UART module %d at the moment (requested = %d)\n", card->devname, len);
|
|
return 0;
|
|
}
|
|
|
|
if (num_bytes < len) {
|
|
DEBUG_ERROR("%s: Can't transmit more than %d bytes to GSM UART module %d at the moment (requested = %d)\n", card->devname, num_bytes, len);
|
|
len = num_bytes;
|
|
}
|
|
|
|
/* write to the UART */
|
|
for (i = 0; i < len; i++) {
|
|
reg = (tx_buff[i] & 0xFF);
|
|
card->hw_iface.bus_write_4(card->hw, AFT_GSM_MOD_REG(mod_no, AFT_GSM_UART_TX_REG), reg);
|
|
}
|
|
if (wan_test_bit(AFT_GSM_UART_DEBUG_ENABLED_BIT, &card->TracingEnabled)) {
|
|
DEBUG_EVENT("%s: UART TX %d bytes: %s\n", card->devname, len, format_uart_data(tx_debug, tx_buff, len));
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int wp_gsm_pll_reset(void *pcard)
|
|
{
|
|
u32 reg = 0;
|
|
int timeout_loops = 0;
|
|
sdla_t *card = pcard;
|
|
|
|
/* Reset GSM PLL (This allows UART communication) */
|
|
DEBUG_EVENT("%s: Resetting GSM PLL (UART)\n", card->devname);
|
|
reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_GLOBAL_REG, ®);
|
|
|
|
wan_set_bit(AFT_GSM_PLL_RESET_BIT, ®);
|
|
card->hw_iface.bus_write_4(card->hw, AFT_GSM_GLOBAL_REG, reg);
|
|
WP_DELAY(10);
|
|
|
|
wan_clear_bit(AFT_GSM_PLL_RESET_BIT, ®);
|
|
card->hw_iface.bus_write_4(card->hw, AFT_GSM_GLOBAL_REG, reg);
|
|
WP_DELAY(10);
|
|
|
|
for (timeout_loops = (AFT_GSM_PLL_ENABLE_TIMEOUT_MS / AFT_GSM_PLL_ENABLE_CHECK_INTERVAL_MS);
|
|
timeout_loops;
|
|
timeout_loops--) {
|
|
reg = 0;
|
|
card->hw_iface.bus_read_4(card->hw, AFT_GSM_GLOBAL_REG, ®);
|
|
if (wan_test_bit(AFT_GSM_PLL_LOCKED_BIT, ®)) {
|
|
break;
|
|
}
|
|
timeout_loops--;
|
|
WP_DELAY(AFT_GSM_MODULE_POWER_TOGGLE_CHECK_INTERVAL_MS * 1000);
|
|
}
|
|
return 0;
|
|
}
|
|
|