android_kernel_modules_leno.../drivers/hsi/clients/hsi_logical.c

2125 lines
57 KiB
C

/* hsi_logical.c
*
* Copyright (C) 2011 Renesas. All rights reserved.
*
*
* Implementation of the HSI logical using RENESAS HSI protocol.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <linux/atomic.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/if_ether.h>
#include <linux/if_arp.h>
#include <linux/if_phonet.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <linux/scatterlist.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/hsi/hsi_logical.h>
#include <linux/hsi/hsi.h>
#include <linux/wakelock.h>
#include <linux/gpio.h>
struct wake_lock wake_resoft;
struct wake_lock wake_no_suspend_l2header;
struct wake_lock wake_temp;
/* Interface with upper layer (header in hsi_logical.h)*/
/*int hsi_logical_open(struct hsi_protocol * hsi_protocol_ctx);*/
/*int hsi_logical_close(struct hsi_protocol * hsi_protocol_ctx);*/
/*int hsi_logical_send(struct hsi_protocol * hsi_protocol_ctx,
struct sk_buff *skb, u16 client_id);*/
/* Configurations exchange */
static void hsi_logical_check_if_boot_done(
struct hsi_protocol *hsi_protocol_context);
static void hsi_logical_boot_info_req_complete(
struct hsi_msg *msg);
static void hsi_logical_boot_info_resp_complete(
struct hsi_msg *msg);
/* TX */
static int hsi_logical_alloc_msg_and_send_on_control_channel(
struct hsi_protocol_client *hsi_client_context,
u32 msg,
unsigned char channel_id,
void (*complete)(struct hsi_msg *msg));
static int hsi_logical_send_on_control_channel(
struct hsi_msg *msg,
struct hsi_protocol_client *hsi_data_client);
static void hsi_logical_tx_control_complete(
struct hsi_msg *msg);
static int hsi_ack_received(
struct hsi_protocol_client *hsi_client_context,
unsigned char channel_id);
static int hsi_nack_received(
struct hsi_protocol_client *hsi_client_context,
unsigned char channel_id);
static void hsi_logical_tx_data_complete(struct hsi_msg *msg);
/* RX */
static void hsi_logical_wait_rx_control(
struct hsi_client *cl,
unsigned char channel_id);
static void hsi_logical_wait_rx_control_l2h(
struct hsi_client *cl,
unsigned char channel_id);
static void hsi_logical_wait_rx_control_wrong_channel(
struct hsi_client *cl,
unsigned char channel_id);
static void hsi_logical_rx_control_complete(
struct hsi_msg *msg);
static void hsi_rx_data_ack(
struct hsi_protocol *hsi_protocol_context,
struct hsi_protocol_client *hsi_data_client);
static void hsi_rx_mem_test(
unsigned long in);
static int hsi_l2header_receive(
struct hsi_protocol_client *hsi_control_client,
unsigned char channel_id, u32 l2header);
static void hsi_logical_rx_data_complete(
struct hsi_msg *msg);
static void hsi_logical_rx_control_complete_step2(
struct hsi_msg *msg);
/* Destructor callbacks */
static void hsi_logical_data_free_msg_and_skb(struct hsi_msg *msg);
static void hsi_logical_data_free_msg(struct hsi_msg *msg);
static void hsi_logical_control_destructor(struct hsi_msg *msg);
static void hsi_logical_control_destructor_l2h(struct hsi_msg *msg);
static void hsi_logical_tx_control_destructor(struct hsi_msg *msg);
static void hsi_logical_rx_control_destructor(struct hsi_msg *msg);
static void hsi_logical_rx_control_destructor_l2h(struct hsi_msg *msg);
/* hsi_msg allocation */
static struct hsi_msg *hsi_logical_alloc_data(
struct sk_buff *skb,
gfp_t flags);
static struct hsi_msg *hsi_logical_get_control(
struct hsi_protocol_client *hsi_client_context);
static struct hsi_msg *hsi_logical_get_control_l2h(
struct hsi_protocol_client *hsi_client_context);
static struct hsi_msg *hsi_logical_alloc_tx_control(
struct hsi_protocol_client *hsi_client_context,
void (*complete)(struct hsi_msg *msg));
/* Libraries */
#ifdef HSI_LOGICAL_USE_DEBUG
static int hsi_logical_get_client_id(
unsigned char mapid);
#endif
static int hsi_logical_retrieve_data_client(
struct hsi_protocol_client *hsi_client_context,
unsigned char channel_id,
struct hsi_protocol_client **hsi_data_client);
static void hsi_logical_skb_to_msg(
struct sk_buff *skb,
struct hsi_msg *msg);
/*power*/
static void hsi_logical_inactivity_timer(unsigned long data);
void hsi_logical_start_tx(struct hsi_protocol *hsi_protocol_ctx);
void hsi_logical_stop_tx(struct hsi_protocol *hsi_protocol_ctx);
void hsi_logical_send_power_off(struct hsi_protocol *hsi_protocol_context);
/* Traces */
extern int hsi_logical_trace_state;
u32 debug_nb_crash_modem;
u32 init_spinlock;
#ifdef HSI_LOGICAL_USE_DEBUG
#define DPRINTK(...) {if (hsi_logical_trace_state == HSI_ALL) \
printk(KERN_DEBUG __VA_ARGS__); }
#define DPRINTK_HIGH(...) {if ((hsi_logical_trace_state == HSI_HIGH)\
|| (hsi_logical_trace_state == HSI_ALL) \
|| (hsi_logical_trace_state == HSI_HIGH_RX) \
|| (hsi_logical_trace_state == HSI_HIGH_TX)) \
printk(KERN_DEBUG __VA_ARGS__); }
#define DPRINTK_HIGH_RX(...) {if ((hsi_logical_trace_state == HSI_HIGH)\
|| (hsi_logical_trace_state == HSI_ALL) \
|| (hsi_logical_trace_state == HSI_HIGH_RX)) \
printk(KERN_DEBUG __VA_ARGS__); }
#define DPRINTK_HIGH_TX(...) {if ((hsi_logical_trace_state == HSI_HIGH)\
|| (hsi_logical_trace_state == HSI_ALL) \
|| (hsi_logical_trace_state == HSI_HIGH_TX)) \
printk(KERN_DEBUG __VA_ARGS__); }
#else
# define DPRINTK(...)
#endif
#ifdef HSI_V3_USED
void hsi_logical_do_power_sequence(
struct hsi_protocol *hsi_protocol_context,
int hsi_power_state);
#endif /*HSI_V3_USED*/
static void
hsi_logical_config_fail(unsigned long in)
{
struct hsi_protocol *hsi_protocol_ctx = (struct hsi_protocol *)in;
int pwr_on = hsi_protocol_ctx->cl[0]->gpio_pwr_on;
wake_unlock(&wake_resoft);
if (hsi_protocol_ctx->main_state == HSI_CONFIG_EXCHANGE_NO_DONE) {
EPRINTK("hsi_logical state %d, tx_boot_info_req %d, " \
"tx_boot_info_resp %d, rx_boot_info_resp %d, " \
" modem_config %x\n",
hsi_protocol_ctx->main_state,
hsi_protocol_ctx->cfg_ex.tx_boot_info_req_complete,
hsi_protocol_ctx->cfg_ex.tx_boot_info_resp_complete,
hsi_protocol_ctx->cfg_ex.rx_boot_info_resp_complete,
hsi_protocol_ctx->cfg_ex.received_modem_config);
EPRINTK("Configuration exchange with modem fails. HSI link is broken\n");
gpio_set_value(pwr_on, 0);
}
}
int
hsi_logical_open(struct hsi_protocol *hsi_protocol_ctx)
{
struct hsi_client *cl;
int err = 0;
int i;
DPRINTK("%s - NB HSI core clients: %d\n",
__func__,
hsi_protocol_ctx->nb_client);
if (init_spinlock == 1) {
init_spinlock = 0;
for (i = 0; i < hsi_protocol_ctx->nb_client; i++) {
spin_lock_init(&hsi_protocol_ctx->cl[i]->tx_lock);
spin_lock_init(&hsi_protocol_ctx->cl[i]->rx_lock);
}
spin_lock_init(&hsi_protocol_ctx->tx_activity_status_lock);
spin_lock_init(&hsi_protocol_ctx->ctrl_msg_array_lock);
spin_lock_init(&hsi_protocol_ctx->ctrl_msg_array_l2h_lock);
}
/* Alloc pool of control message */
for (i = 0; i < HSI_LOGICAL_NB_CONTROL_MSG; i++) {
u8 *buf;
hsi_protocol_ctx->hsi_ctrl_msg_array[i].msg =
hsi_alloc_msg(1, GFP_KERNEL | GFP_DMA);
if (!hsi_protocol_ctx->hsi_ctrl_msg_array[i].msg)
BUG();
buf = kmalloc(HSI_LOGICAL_CONTROL_MESSAGE_SIZE,
GFP_KERNEL | GFP_DMA);
if (!buf)
BUG();
sg_init_one(
hsi_protocol_ctx->hsi_ctrl_msg_array[i].msg->sgt.sgl,
buf,
HSI_LOGICAL_CONTROL_MESSAGE_SIZE);
hsi_protocol_ctx->hsi_ctrl_msg_array[i].free = 1;
}
/* Alloc pool of control message l2h*/
for (i = 0; i < HSI_LOGICAL_NB_CONTROL_MSG_L2H; i++) {
u8 *buf;
hsi_protocol_ctx->hsi_ctrl_msg_array_l2h[i].msg =
hsi_alloc_msg(1, GFP_KERNEL | GFP_DMA);
if (!hsi_protocol_ctx->hsi_ctrl_msg_array_l2h[i].msg)
BUG();
buf = kmalloc(HSI_LOGICAL_L2HEADER_MESSAGE_SIZE,
GFP_KERNEL | GFP_DMA);
if (!buf)
BUG();
sg_init_one(
hsi_protocol_ctx->hsi_ctrl_msg_array_l2h[i].msg->sgt.sgl,
buf,
HSI_LOGICAL_L2HEADER_MESSAGE_SIZE);
hsi_protocol_ctx->hsi_ctrl_msg_array_l2h[i].free = 1;
}
for (i = 0; i < hsi_protocol_ctx->nb_client; i++) {
cl = hsi_protocol_ctx->cl[i]->hsi_cl;
if (!cl) {
DPRINTK("Client %d not initialised\n", i);
err = -ENOMEM;
goto out;
}
setup_timer(&hsi_protocol_ctx->cl[i]->rx_mem,
hsi_rx_mem_test,
(unsigned long)hsi_protocol_ctx->cl[i]);
hsi_protocol_ctx->cl[i]->client_id = i;
DPRINTK("Client %d claim port\n", i);
err = hsi_claim_port(cl, 1);
if (err < 0) {
DPRINTK("HSI port %d already claimed\n", i);
goto out;
}
/* Several INIT */
spin_lock(&hsi_protocol_ctx->cl[i]->tx_lock);
hsi_protocol_ctx->cl[i]->send_state = HSI_TX_IDLE;
hsi_protocol_ctx->cl[i]->recv_state = HSI_RX_IDLE;
spin_unlock(&hsi_protocol_ctx->cl[i]->tx_lock);
/* Save top level HSI protocol context per client
(to be able to communicate with other
clients, especialy control client) */
hsi_protocol_ctx->cl[i]->hsi_top_protocol_context =
hsi_protocol_ctx;
}
/* Setup port using control client setup */
if (hsi_get_port(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl)->setup) {
hsi_setup(hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl);
} else {
DPRINTK("hsi_get_port(cl)->setup is NULL\n");
err = -ENOMEM;
goto out;
}
init_timer_deferrable(&hsi_protocol_ctx->inactivity_timer);
hsi_protocol_ctx->inactivity_timer.data =
(unsigned long)hsi_protocol_ctx;
hsi_protocol_ctx->inactivity_timer.function =
hsi_logical_inactivity_timer;
hsi_protocol_ctx->inactivity_timer_running = false;
hsi_protocol_ctx->main_state = HSI_CONFIG_EXCHANGE_NO_DONE;
hsi_protocol_ctx->cfg_ex.tx_boot_info_req_complete = 0;
hsi_protocol_ctx->cfg_ex.rx_boot_info_resp_complete = 0;
hsi_protocol_ctx->cfg_ex.tx_boot_info_resp_complete = 0;
setup_timer(&hsi_protocol_ctx->cfg_ex.to,
hsi_logical_config_fail,
(unsigned long)hsi_protocol_ctx);
mod_timer(&hsi_protocol_ctx->cfg_ex.to,
jiffies + msecs_to_jiffies(10000));
wake_lock_timeout(&wake_resoft, 31 * HZ);
/* Init is done - wait incomming data on RX control bank
As several l2 header can be received on a raw, allocate
several RX control message (one per bank).
Later each time a RX control message is completed another
one will be allocated.
=> hsi physical will always have a pool of HSI_NB_CLIENT msg*/
for (i = 0; i < HSI_NB_CLIENT; i++)
hsi_logical_wait_rx_control(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
HSI_CONTROL_CHANNEL);
hsi_logical_wait_rx_control_l2h(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
1);
hsi_logical_wait_rx_control_l2h(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
2);
hsi_logical_wait_rx_control_l2h(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
3);
hsi_logical_wait_rx_control_wrong_channel(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
4);
hsi_logical_wait_rx_control_wrong_channel(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
5);
hsi_logical_wait_rx_control_wrong_channel(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
6);
hsi_logical_wait_rx_control_wrong_channel(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl,
7);
hsi_logical_start_tx(hsi_protocol_ctx);
return 0;
out:
/* Free pool of control message */
for (i = 0; i < HSI_LOGICAL_NB_CONTROL_MSG; i++) {
kfree(sg_virt(hsi_protocol_ctx->
hsi_ctrl_msg_array[i].msg->
sgt.sgl));
hsi_free_msg(hsi_protocol_ctx->hsi_ctrl_msg_array[i].msg);
}
/* Free pool of control message l2h*/
for (i = 0; i < HSI_LOGICAL_NB_CONTROL_MSG_L2H; i++) {
kfree(sg_virt(hsi_protocol_ctx->
hsi_ctrl_msg_array_l2h[i].msg->
sgt.sgl));
hsi_free_msg(hsi_protocol_ctx->hsi_ctrl_msg_array_l2h[i].msg);
}
return err;
}
int
hsi_logical_close(struct hsi_protocol *hsi_protocol_ctx)
{
struct hsi_client *cl;
int err = 0;
int i;
EPRINTK("Modem crash occurs - NB modem reboot since power on : %d\n",
++debug_nb_crash_modem);
wake_lock_timeout(&wake_resoft, 30 * HZ);
del_timer(&hsi_protocol_ctx->cfg_ex.to);
/*hsi_logical_send_power_off(hsi_protocol_ctx);*/
spin_lock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
hsi_protocol_ctx->wake_state = 0;
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
if (netif_running(hsi_protocol_ctx->netdev))
netif_carrier_off(hsi_protocol_ctx->netdev);
/* scan the list of clients in reverse order so that control
channel is the last one */
/* Mixed mode messages are attached to control channel 0 */
/* they shall be released when all other channels are closed */
for (i = hsi_protocol_ctx->nb_client-1; i >= 0; i--) {
cl = hsi_protocol_ctx->cl[i]->hsi_cl;
if (!cl) {
DPRINTK("Client %d not initialised\n", i);
err = -ENOMEM;
goto out;
}
del_timer(&hsi_protocol_ctx->cl[i]->rx_mem);
spin_lock_bh(&hsi_protocol_ctx->cl[i]->tx_lock);
hsi_protocol_ctx->cl[i]->send_state = HSI_TX_CLOSE;
hsi_protocol_ctx->cl[i]->recv_state = HSI_RX_CLOSE;
spin_unlock_bh(&hsi_protocol_ctx->cl[i]->tx_lock);
}
hsi_flush(hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID]->hsi_cl);
netif_wake_subqueue(hsi_protocol_ctx->netdev,
HSI_LOG_CL_ID_TO_QUEUE_ID(hsi_protocol_ctx->cl[1]->client_id));
netif_wake_subqueue(hsi_protocol_ctx->netdev,
HSI_LOG_CL_ID_TO_QUEUE_ID(hsi_protocol_ctx->cl[2]->client_id));
netif_wake_subqueue(hsi_protocol_ctx->netdev,
HSI_LOG_CL_ID_TO_QUEUE_ID(hsi_protocol_ctx->cl[3]->client_id));
spin_lock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
{
del_timer(&hsi_protocol_ctx->inactivity_timer);
hsi_protocol_ctx->inactivity_timer_running = false;
}
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
/* Now that all FIFO are flushed release HSI IP */
for (i = 0; i < hsi_protocol_ctx->nb_client; i++) {
/* Perform HW release just once (confusion in
port comprehension != bank) */
cl = hsi_protocol_ctx->cl[i]->hsi_cl;
hsi_release_port(hsi_protocol_ctx->cl[i]->hsi_cl);
}
/* Free pool of control message */
for (i = 0; i < HSI_LOGICAL_NB_CONTROL_MSG; i++) {
kfree(sg_virt(hsi_protocol_ctx->
hsi_ctrl_msg_array[i].msg->
sgt.sgl));
hsi_free_msg(hsi_protocol_ctx->hsi_ctrl_msg_array[i].msg);
}
/* Free pool of control message l2h*/
for (i = 0; i < HSI_LOGICAL_NB_CONTROL_MSG_L2H; i++) {
kfree(sg_virt(hsi_protocol_ctx->
hsi_ctrl_msg_array_l2h[i].msg->
sgt.sgl));
hsi_free_msg(hsi_protocol_ctx->hsi_ctrl_msg_array_l2h[i].msg);
}
return 0;
out:
return err;
}
void
hsi_logical_start_tx(struct hsi_protocol *hsi_protocol_ctx)
{
DPRINTK("%s - %d", __func__, hsi_protocol_ctx->wake_state);
spin_lock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
#ifdef HSI_V3_USED
hsi_protocol_ctx->power_state = HSI_POWER_ON_STATE;
#endif
if (0 == hsi_protocol_ctx->wake_state) {
hsi_protocol_ctx->wake_state = 1;
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
DPRINTK_HIGH(">hsi_start_tx called by HSI logical");
hsi_start_tx(hsi_protocol_ctx->
cl[HSI_CONTROL_CLIENT_ID]->
hsi_cl);
} else {
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
}
}
void
hsi_logical_stop_tx(struct hsi_protocol *hsi_protocol_ctx)
{
#ifdef HSI_LOGICAL_POWER_SAVING
DPRINTK("%s - %d", __func__, hsi_protocol_ctx->wake_state);
spin_lock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
#ifdef HSI_V3_USED
if (hsi_protocol_ctx->power_state == HSI_POWER_SEND_OFF_STATE) {
/*End the power sequence now*/
hsi_protocol_ctx->power_state = HSI_POWER_OFF_STATE;
#endif /*HSI_V3_USED*/
if (1 == hsi_protocol_ctx->wake_state) {
hsi_protocol_ctx->wake_state = 0;
spin_unlock_bh(
&hsi_protocol_ctx->tx_activity_status_lock);
DPRINTK_HIGH("<hsi_stop_tx called by HSI logical");
hsi_stop_tx(hsi_protocol_ctx->
cl[HSI_CONTROL_CLIENT_ID]->
hsi_cl);
} else {
spin_unlock_bh(
&hsi_protocol_ctx->tx_activity_status_lock);
}
#ifdef HSI_V3_USED
} else {
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
}
#endif /*HSI_V3_USED*/
#endif /* HSI_LOGICAL_POWER_SAVING */
}
#ifdef HSI_USE_SEND_SCHEDULED
void
hsi_logical_send_work(struct work_struct *work)
{
struct hsi_protocol_client *hsi_data_client =
container_of(work, struct hsi_protocol_client, send_work);
#else
int
hsi_logical_do_send(struct hsi_protocol_client *hsi_data_client)
{
#endif /*HSI_USE_SEND_SCHEDULED*/
struct hsi_msg *msg = hsi_data_client->data_msg_to_be_send;
struct hsi_protocol *hsi_protocol_ctx =
hsi_data_client->hsi_top_protocol_context;
msg->destructor = hsi_logical_data_free_msg_and_skb;
msg->complete = hsi_logical_tx_data_complete;
msg->channel =
HSI_LOGICAL_SET_CHANNEL_ACCORDING_TO_PRIO(hsi_data_client->client_id);
hsi_data_client->send_state = HSI_TX_HDR_SENT;
DPRINTK("%s - L2 header sent: 0x%08x\n",
__func__,
hsi_data_client->tx_l2_header);
spin_lock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
/* Stop inactivity timer. It will be relaunched on TX
data completion callback */
del_timer(&hsi_protocol_ctx->inactivity_timer);
hsi_protocol_ctx->inactivity_timer_running = false;
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
hsi_logical_start_tx(hsi_protocol_ctx);
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_data_client,
hsi_data_client->tx_l2_header,
HSI_LOGICAL_SET_CHANNEL_ACCORDING_TO_PRIO(
hsi_data_client->client_id
),
hsi_logical_tx_control_complete);
#ifdef HSI_USE_SEND_SCHEDULED
return;
#else
return 0;
#endif /*HSI_USE_SEND_SCHEDULED*/
}
int
hsi_logical_send(struct hsi_protocol *hsi_protocol_ctx,
struct sk_buff *skb,
u16 client_id)
{
struct hsi_protocol_client *hsi_data_client ;
struct hsi_msg *msg;
u32 l2hdr;
#ifdef HSI_LOGICAL_USE_DEBUG
u8 mapid = MAPID_FROM_HEADER(*(u32 *)skb->data);
int client_id_according_to_mapid = hsi_logical_get_client_id(mapid);
DPRINTK_HIGH_TX("%s on mapid %d - client id %d data = 0x%08X\n",
__func__, mapid, client_id, *(u32 *)skb->data);
BUG_ON(client_id_according_to_mapid != client_id);
#endif
#ifdef HSI_LOGICAL_POWER_SAVING
wake_lock(&wake_temp);
#endif
hsi_data_client = hsi_protocol_ctx->cl[client_id];
/* Get L2 header of msg */
l2hdr = *(u32 *)skb->data;
/* Remove L2 header from data message */
skb_pull(skb, HSI_LOGICAL_CONTROL_MESSAGE_SIZE);
/* Build data message */
msg = hsi_logical_alloc_data(skb, GFP_ATOMIC | GFP_DMA);
DPRINTK("HSI LOGICAL- NB_FRAGS = %d total size: %d frags size: %d\n",
skb_shinfo(skb)->nr_frags, skb->len, skb->data_len);
if (!msg) {
DPRINTK("Dropping tx data: No memory\n");
goto drop;
}
spin_lock_bh(&hsi_data_client->tx_lock);
/* Build L2 header msg */
hsi_data_client->tx_l2_header = l2hdr;
/* Reset nb time L2 header has been sent */
hsi_data_client->nb_time_l2_header_sent = 0;
/* Associated net device client queue is stopped when client
is not ready to transmit */
if ((hsi_protocol_ctx->main_state != HSI_CONFIG_EXCHANGE_DONE)
|| (hsi_data_client->send_state != HSI_TX_IDLE)) {
int m = hsi_protocol_ctx->main_state;
int s = hsi_data_client->send_state;
spin_unlock_bh(&hsi_data_client->tx_lock);
hsi_free_msg(msg);
EPRINTK("%s - main_state %d, send_state %d\n",
__func__, m, s);
return -ENETDOWN;
} else {
hsi_data_client->send_state = HSI_TX_WAIT;
/* Save data message in data client context */
hsi_data_client->data_msg_to_be_send = msg;
spin_unlock_bh(&hsi_data_client->tx_lock);
#ifdef HSI_USE_SEND_SCHEDULED
return queue_work(hsi_data_client->send_wq,
&hsi_data_client->send_work);
#else
return hsi_logical_do_send(hsi_data_client);
#endif /*HSI_USE_SEND_SCHEDULED*/
}
drop:
return -ENOMEM;
}
static void
hsi_logical_boot_info_req_complete(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_control_client =
hsi_client_drvdata(msg->cl);
struct hsi_protocol *hsi_protocol_context =
hsi_control_client->hsi_top_protocol_context;
DPRINTK("%s - client ID %d - port nb %d - channel_id %d\n",
__func__,
hsi_control_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel);
if ((msg->status == HSI_STATUS_ERROR)
|| (hsi_control_client->client_id != HSI_CONTROL_CLIENT_ID)) {
DPRINTK("%s - TX control error !!\n", __func__);
hsi_logical_stop_tx(hsi_protocol_context);
BUG();
} else {
hsi_protocol_context->cfg_ex.tx_boot_info_req_complete = 1;
hsi_logical_check_if_boot_done(hsi_protocol_context);
}
}
static void
hsi_logical_boot_info_resp_complete(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_control_client =
hsi_client_drvdata(msg->cl);
struct hsi_protocol *hsi_protocol_context =
hsi_control_client->hsi_top_protocol_context;
DPRINTK("%s - client ID %d - port nb %d - channel_id %d\n",
__func__,
hsi_control_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel);
if ((msg->status == HSI_STATUS_ERROR)
|| (hsi_control_client->client_id != HSI_CONTROL_CLIENT_ID)) {
DPRINTK("%s - TX control error !!\n", __func__);
hsi_logical_stop_tx(hsi_protocol_context);
BUG();
} else {
hsi_protocol_context->cfg_ex.tx_boot_info_resp_complete = 1;
hsi_logical_check_if_boot_done(hsi_protocol_context);
}
}
static void
hsi_logical_check_if_boot_done(struct hsi_protocol *hsi_protocol_context)
{
DPRINTK("%s - tx_boot_info_req_complete %d" \
" - rx_boot_info_resp_complete %d - " \
"tx_boot_info_resp_complete %d\n",
__func__,
hsi_protocol_context->cfg_ex.tx_boot_info_req_complete,
hsi_protocol_context->cfg_ex.rx_boot_info_resp_complete,
hsi_protocol_context->cfg_ex.tx_boot_info_resp_complete);
if (hsi_protocol_context->main_state != HSI_CONFIG_EXCHANGE_DONE) {
if (hsi_protocol_context->cfg_ex.tx_boot_info_req_complete
&& hsi_protocol_context->cfg_ex.rx_boot_info_resp_complete
&& hsi_protocol_context->cfg_ex.tx_boot_info_resp_complete) {
/* Configuration exchange is finished */
/* Compare sent_ape_config and received_modem_config */
EPRINTK("Baseband HSI link is up:\n"
" - RX L2Header size %d bytes\n"
#ifdef HSI_LOGICAL_RXDATA_128_BYTES_PADDED
" - RX data 128 bytes padded\n"
#endif
#ifdef HSI_LOGICAL_POWER_SAVING
" - Power saving activated: %dms inactivity timer\n"
#else
" - Power saving deactivated\n"
#endif
, HSI_LOGICAL_L2HEADER_MESSAGE_SIZE
#ifdef HSI_LOGICAL_POWER_SAVING
, HSI_LOGICAL_INACTIVITY_TIMER
#endif
);
if (hsi_protocol_context->cfg_ex.sent_ape_config !=
hsi_protocol_context->cfg_ex.received_modem_config) {
/* TODO adapt config to match both peers */
EPRINTK("sent_ape_config: 0x%08x" \
" received_modem_config:0x%08x",
hsi_protocol_context->cfg_ex.sent_ape_config,
hsi_protocol_context->
cfg_ex.received_modem_config);
hsi_logical_stop_tx(hsi_protocol_context);
BUG();
/* If necessary setup again control bank */
/* hsi_setup(hsi_protocol_ctx->
cl[HSI_CONTROL_CLIENT_ID]->hsi_cl); */
}
del_timer(&hsi_protocol_context->cfg_ex.to);
wake_unlock(&wake_resoft);
spin_lock_bh(&hsi_protocol_context->
tx_activity_status_lock);
hsi_protocol_context->main_state =
HSI_CONFIG_EXCHANGE_DONE;
/* Start inactivity timer. On expiration, if
all queues are ready call hsi_stop_tx */
mod_timer(&hsi_protocol_context->inactivity_timer,
jiffies + msecs_to_jiffies(
HSI_LOGICAL_INACTIVITY_TIMER)
);
hsi_protocol_context->inactivity_timer_running = true;
spin_unlock_bh(&hsi_protocol_context->
tx_activity_status_lock);
/* Wake up all queues to allow transmission
at upper layer */
netif_tx_wake_all_queues(hsi_protocol_context->netdev);
}
}
}
static int
hsi_logical_alloc_msg_and_send_on_control_channel(
struct hsi_protocol_client *hsi_client_context,
u32 msg,
unsigned char channel_id,
void (*complete)(struct hsi_msg *msg))
{
struct hsi_msg *msg_ctrl;
u32 *data;
msg_ctrl = hsi_logical_alloc_tx_control(hsi_client_context, complete);
data = sg_virt(msg_ctrl->sgt.sgl);
*data = msg;
msg_ctrl->channel = channel_id ;
return hsi_logical_send_on_control_channel(msg_ctrl,
hsi_client_context);
}
/* Send current message on control channel */
static int
hsi_logical_send_on_control_channel(
struct hsi_msg *msg,
struct hsi_protocol_client *hsi_client)
{
struct hsi_protocol_client *hsi_control_client =
hsi_client->
hsi_top_protocol_context->cl[HSI_CONTROL_CLIENT_ID];
int err;
DPRINTK("%s - channel_id %d\n", __func__, msg->channel);
err = hsi_async_write(hsi_control_client->hsi_cl, msg);
if (err)
EPRINTK("%s - hsi_async_write error=%d\n", __func__, err);
return err;
}
static void
hsi_logical_tx_control_complete(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_control_client =
hsi_client_drvdata(msg->cl);
#ifdef HSI_LOGICAL_USE_DEBUG
if (msg->channel == 0) {
DPRINTK_HIGH_RX("%s - client ID %d" \
" - port nb %d - channel_id %d\n",
__func__,
hsi_control_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel);
} else {
DPRINTK_HIGH_TX("%s - client ID %d" \
" - port nb %d - channel_id %d\n",
__func__,
hsi_control_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel);
}
#endif
if (hsi_control_client->send_state != HSI_TX_CLOSE) {
if ((msg->status == HSI_STATUS_ERROR) ||
(hsi_control_client->client_id !=
HSI_CONTROL_CLIENT_ID)) {
EPRINTK("TX control error\n");
}
}
/* Free control message */
hsi_logical_tx_control_destructor(msg);
}
static int
hsi_ack_received(
struct hsi_protocol_client *hsi_control_client,
unsigned char channel_id)
{
struct hsi_protocol_client *hsi_data_client;
struct hsi_protocol *hsi_protocol_context;
int err = 0;
DPRINTK_HIGH_TX("%s - for channel_id %d\n", __func__, channel_id);
if (hsi_logical_retrieve_data_client(
hsi_control_client,
channel_id,
&hsi_data_client) < 0) {
EPRINTK("hsi_ack_received - ERROR mismatch with" \
" channel ID used in L2 Header\n");
return 0;
}
hsi_protocol_context = hsi_data_client->hsi_top_protocol_context;
spin_lock_bh(&hsi_data_client->tx_lock);
if (unlikely(hsi_data_client->send_state != HSI_TX_HDR_SENT)) {
/* Should never happen */
EPRINTK("hsi_ack_received - ERROR: state is %d !!\n",
hsi_data_client->send_state);
spin_unlock_bh(&hsi_data_client->tx_lock);
return 0;
}
hsi_data_client->send_state = HSI_TX_ACK_RCV;
spin_unlock_bh(&hsi_data_client->tx_lock);
/* Next operation performs the write operation*/
if (hsi_async_write(hsi_data_client->hsi_cl,
hsi_data_client->data_msg_to_be_send)) {
spin_lock_bh(&hsi_data_client->tx_lock);
hsi_data_client->send_state = HSI_TX_IDLE;
hsi_data_client->data_msg_to_be_send = NULL;
spin_unlock_bh(&hsi_data_client->tx_lock);
/* Send is aborted - wake up associated queue*/
netif_wake_subqueue(hsi_protocol_context->netdev,
HSI_LOG_CL_ID_TO_QUEUE_ID(hsi_data_client->client_id));
EPRINTK("hsi_ack_received hsi_async_write - ERROR: %d\n", err);
}
return 0;
}
int
hsi_nack_received(
struct hsi_protocol_client *hsi_control_client,
unsigned char channel_id)
{
struct hsi_protocol_client *hsi_data_client;
EPRINTK("%s - channel_id %d\n", __func__, channel_id);
if (hsi_logical_retrieve_data_client(hsi_control_client,
channel_id,
&hsi_data_client) < 0) {
EPRINTK("%s - ERROR mismatch with channel" \
" ID used in L2 Header\n", __func__);
return 0;
}
spin_lock_bh(&hsi_data_client->tx_lock);
if (unlikely(hsi_data_client->send_state != HSI_TX_HDR_SENT)) {
/* Should never happen */
EPRINTK("%s - ERROR: state is %d !!\n",
__func__,
hsi_data_client->send_state);
spin_unlock_bh(&hsi_data_client->tx_lock);
return 0;
}
spin_unlock_bh(&hsi_data_client->tx_lock);
if (unlikely(hsi_data_client->nb_time_l2_header_sent++ >= 2)) {
EPRINTK("%s - ERROR:" \
" nb_time_l2_header_sent = %d\n",
__func__,
hsi_data_client->nb_time_l2_header_sent);
/* Free data message */
hsi_logical_data_free_msg_and_skb(
hsi_data_client->data_msg_to_be_send);
} else {
/* Check WAKE line, in case TX is off,
need to start TX for sending L2H */
hsi_logical_start_tx(hsi_data_client->hsi_top_protocol_context);
/* Send again L2 header */
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_data_client,
hsi_data_client->tx_l2_header,
HSI_LOGICAL_SET_CHANNEL_ACCORDING_TO_PRIO(
hsi_data_client->client_id),
hsi_logical_tx_control_complete);
}
return 0;
}
static void
hsi_logical_tx_data_complete(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_data_client =
hsi_client_drvdata(msg->cl);
struct hsi_protocol *hsi_protocol_context =
hsi_data_client->hsi_top_protocol_context;
DPRINTK_HIGH_TX("%s - client ID %d" \
" - port nb %d - channel_id %d\n",
__func__,
hsi_data_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel);
if ((msg->status == HSI_STATUS_ERROR)
|| (hsi_data_client->client_id == HSI_CONTROL_CLIENT_ID)
|| (msg->channel == HSI_CONTROL_CHANNEL)) {
EPRINTK("TX data error\n");
}
hsi_logical_data_free_msg_and_skb(msg);
spin_lock_bh(&hsi_data_client->tx_lock);
if (hsi_data_client->send_state != HSI_TX_CLOSE) {
if (unlikely(hsi_data_client->send_state != HSI_TX_ACK_RCV))
EPRINTK("TX data error - wrong state !!\n");
hsi_data_client->send_state = HSI_TX_IDLE;
spin_unlock_bh(&hsi_data_client->tx_lock);
/* Send is now complete - wake up associated queue*/
netif_wake_subqueue(hsi_protocol_context->netdev,
HSI_LOG_CL_ID_TO_QUEUE_ID(hsi_data_client->client_id));
} else
spin_unlock_bh(&hsi_data_client->tx_lock);
DPRINTK("HSI netdevice Subqueue %d is now awake\n",
HSI_LOG_CL_ID_TO_QUEUE_ID(hsi_data_client->client_id));
/* Start inactivity timer. On expiration,
if all queues are ready call hsi_stop_tx */
spin_lock_bh(&hsi_protocol_context->tx_activity_status_lock);
{
mod_timer(&hsi_protocol_context->inactivity_timer,
jiffies + msecs_to_jiffies(HSI_LOGICAL_INACTIVITY_TIMER));
hsi_protocol_context->inactivity_timer_running = true;
}
spin_unlock_bh(&hsi_protocol_context->tx_activity_status_lock);
}
/* Allocate message on control port and wait reception */
static void
hsi_logical_wait_rx_control(struct hsi_client *cl, unsigned char channel_id)
{
struct hsi_msg *msg = hsi_logical_get_control(hsi_client_drvdata(cl));
DPRINTK("%s - on channel id %d\n", __func__, channel_id);
msg->channel = channel_id;
msg->complete = hsi_logical_rx_control_complete;
msg->destructor = hsi_logical_rx_control_destructor;
if (hsi_async_read(cl, msg))
EPRINTK("%s - hsi_async_read error\n", __func__);
}
/* Allocate message on control port and wait reception */
static void
hsi_logical_wait_rx_control_l2h(struct hsi_client *cl, unsigned char channel_id)
{
struct hsi_msg *msg = hsi_logical_get_control_l2h(hsi_client_drvdata(cl));
DPRINTK("%s - on channel id %d\n", __func__, channel_id);
msg->channel = channel_id;
msg->complete = hsi_logical_rx_control_complete;
msg->destructor = hsi_logical_rx_control_destructor_l2h;
if (hsi_async_read(cl, msg))
EPRINTK("%s - hsi_async_read error\n", __func__);
}
static void
hsi_logical_rx_control_complete_wrong_channel(struct hsi_msg *msg)
{
EPRINTK("%s Receive buffer on wrong channel: 0x%08x, channel_id %d\n",
__func__,
*(u32 *)sg_virt(msg->sgt.sgl), msg->channel);
msg->destructor(msg);
}
/* Allocate message on control port and wait reception */
static void
hsi_logical_wait_rx_control_wrong_channel(
struct hsi_client *cl,
unsigned char channel_id)
{
struct hsi_msg *msg = hsi_logical_get_control(hsi_client_drvdata(cl));
DPRINTK("%s - on channel id %d\n", __func__, channel_id);
msg->channel = channel_id;
msg->complete = hsi_logical_rx_control_complete_wrong_channel;
msg->destructor = hsi_logical_rx_control_destructor;
if (hsi_async_read(cl, msg))
EPRINTK("%s - hsi_async_read error\n", __func__);
}
static void
hsi_logical_rx_control_complete(struct hsi_msg *msg)
{
#ifdef HSI_USE_RCV_SCHEDULED
struct hsi_protocol_client *hsi_control_client =
hsi_client_drvdata(msg->cl);
struct hsi_protocol *hsi_protocol_ctx =
hsi_control_client->hsi_top_protocol_context;
DPRINTK_HIGH_RX("%s Receive control buffer content: 0x%08x, channel_id %d\n",
__func__,
*(u32 *)sg_virt(msg->sgt.sgl), msg->channel);
spin_lock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
/*Now we need to schedule the reception as start_tx will
generate some exceptions*/
if (0 == hsi_protocol_ctx->wake_state) {
DPRINTK("hsi logical woken up by modem (0x%p)\n", msg);
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
spin_lock_bh(&hsi_control_client->rcv_msgs_lock);
list_add_tail(&msg->link, &hsi_control_client->rcv_msgs);
spin_unlock_bh(&hsi_control_client->rcv_msgs_lock);
queue_work(hsi_control_client->rcv_wq,
&hsi_control_client->rcv_work);
} else {
spin_unlock_bh(&hsi_protocol_ctx->tx_activity_status_lock);
hsi_logical_rx_control_complete_step2(msg);
}
#else
hsi_logical_rx_control_complete_step2(msg);
#endif /* HSI_USE_RCV_SCHEDULED */
}
#ifdef HSI_USE_RCV_SCHEDULED
void
hsi_logical_rcv_work(struct work_struct *work)
{
struct hsi_msg *msg, *tmp_msg;
struct hsi_protocol_client *hsi_control_client =
container_of(work,
struct hsi_protocol_client,
rcv_work);
spin_lock_bh(&hsi_control_client->rcv_msgs_lock);
list_for_each_entry_safe(msg,
tmp_msg,
&hsi_control_client->rcv_msgs,
link) {
list_del(&msg->link);
spin_unlock_bh(&hsi_control_client->rcv_msgs_lock);
hsi_logical_rx_control_complete_step2(msg);
spin_lock_bh(&hsi_control_client->rcv_msgs_lock);
}
spin_unlock_bh(&hsi_control_client->rcv_msgs_lock);
DPRINTK("hsi: calling complete callback (0x%p)\n", msg);
}
#endif /* HSI_USE_RCV_SCHEDULED */
static void
hsi_logical_rx_control_complete_step2(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_control_client =
hsi_client_drvdata(msg->cl);
struct hsi_protocol *hsi_protocol_ctx =
hsi_control_client->hsi_top_protocol_context;
struct hsi_client *cl = msg->cl;
u32 control_msg;
unsigned char channel_id = msg->channel;
DPRINTK("hsi_logical_rx_control_complete - client ID %d" \
" - port nb %d - channel_id %d\n",
hsi_control_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel);
if (hsi_control_client->recv_state == HSI_RX_CLOSE) {
/* Free control message */
msg->destructor(msg);
return;
}
if (msg->status == HSI_STATUS_ERROR) {
EPRINTK("RX error on control bank\n");
goto out;
}
if (hsi_control_client->client_id != HSI_CONTROL_CLIENT_ID) {
EPRINTK("Not on control port !!\n");
goto out;
}
control_msg = *(u32 *) sg_virt(msg->sgt.sgl);
DPRINTK(" Receive control buffer content: 0x%08x\n",
*(u32 *)sg_virt(msg->sgt.sgl));
switch (MAPID_FROM_HEADER(control_msg)) {
case HSI_BOOT_INFO_REQ: /*We received a REQ from modem */
if (hsi_protocol_ctx->main_state != HSI_CONFIG_EXCHANGE_DONE) {
/* TODO for next release: analyse message content
to define the content of BOOT_INFO_RESP, and delay
sending of HSI_BOOT_INFO_RESP after the end of
config exchange.
Indeed if banks need to be reconfigured, we need
to block the modem to avoid receiving a L2 header
before a potential reconfigure of the banks.
For now we assume modem and APE configs are identical
=> keep sending of HSI_BOOT_INFO_RESP immediately
to allow the modem to send messages as soon as possible
(to speed the boot).
That means we can receive L2 header from modem,
even if config exchange is not completed from
APE point of view. This is authorized in HSI
logical (only TX is bloqued until end of config
exchange). */
if ((control_msg&CONF_VERSION_MASK) != HSI_SUPPORTED_VERSION) {
EPRINTK("Modem HSI version is not compatible in" \
"control_msg 0x%08x\n", control_msg);
goto out;
}
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_control_client,
(HSI_BOOT_INFO_RESP << 24) |
(control_msg & 0x00FFFFFF),
HSI_CONTROL_CHANNEL,
hsi_logical_boot_info_resp_complete);
/* TODO retrieve config from port config */
hsi_protocol_ctx->cfg_ex.sent_ape_config =
HSI_DEFAULT_BOOT_CONFIG;
/* Start config exchange by sending HSI_BOOT_INFO_REQ */
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_protocol_ctx->cl[HSI_CONTROL_CLIENT_ID],
(HSI_BOOT_INFO_REQ << 24) |
hsi_protocol_ctx->
cfg_ex.sent_ape_config,
HSI_CONTROL_CHANNEL,
hsi_logical_boot_info_req_complete);
}
hsi_logical_wait_rx_control(cl, channel_id);
break;
case HSI_BOOT_INFO_RESP: /* We received a RESP from modem */
if (hsi_protocol_ctx->main_state != HSI_CONFIG_EXCHANGE_DONE) {
hsi_protocol_ctx->cfg_ex.received_modem_config =
control_msg & 0x00FFFFFF;
hsi_protocol_ctx->cfg_ex.rx_boot_info_resp_complete = 1;
hsi_logical_check_if_boot_done(hsi_protocol_ctx);
}
hsi_logical_wait_rx_control(cl, channel_id);
break;
case HSI_ACK:
hsi_ack_received(hsi_control_client,
CHANNEL_FROM_ACK(control_msg));
hsi_logical_wait_rx_control(cl, channel_id);
break;
case HSI_NACK:
hsi_nack_received(hsi_control_client,
CHANNEL_FROM_ACK(control_msg));
hsi_logical_wait_rx_control(cl, channel_id);
break;
case HSI_POWER_OFF:
DPRINTK_HIGH("hsi_logical_rx_control_complete -" \
"receive POWER_OFF notification");
/*TRASHED*/
hsi_logical_wait_rx_control(cl, channel_id);
break;
default:
/* In that case message received on control channel.
As it is not a control message, it must be a L2 HEADER */
hsi_l2header_receive(
hsi_control_client,
msg->channel,
control_msg);
break;
}
out:
/* Free control message */
msg->destructor(msg);
}
static void hsi_rx_mem_test(unsigned long in)
{
struct hsi_protocol_client *hsi_data_client =
(struct hsi_protocol_client *)in;
struct hsi_protocol *hsi_protocol_context =
hsi_data_client->hsi_top_protocol_context;
struct hsi_client *cl;
cl = hsi_data_client->hsi_cl;
spin_lock_bh(&hsi_data_client->rx_lock);
if ((hsi_data_client->recv_state == HSI_RX_RVCING_DATA) ||
(hsi_data_client->recv_state == HSI_RX_HDR_RCV_WHILE_DATA_ONGOING)) {
spin_unlock_bh(&hsi_data_client->rx_lock);
hsi_rx_data_ack(hsi_protocol_context, hsi_data_client);
} else
spin_unlock_bh(&hsi_data_client->rx_lock);
}
static void hsi_rx_data_ack(struct hsi_protocol *hsi_protocol_context,
struct hsi_protocol_client *hsi_data_client)
{
struct hsi_msg *msg = NULL;
struct sk_buff *skb;
u32 l3len;
unsigned char channel_id = hsi_data_client->rx_l2_header_channel;
struct hsi_protocol_client *hsi_control_client =
hsi_data_client->
hsi_top_protocol_context->
cl[HSI_CONTROL_CLIENT_ID];
#ifdef HSI_LOGICAL_SEND_ACK_BEFORE_ALLOC
/* Check WAKE line, in case TX is off,
need to start TX for sending ACK */
hsi_logical_start_tx(hsi_protocol_context);
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_control_client,
(HSI_ACK<<24 | channel_id),
HSI_CONTROL_CHANNEL,
hsi_logical_tx_control_complete);
#endif
l3len = LENGTH_IN_HEADER(hsi_data_client->rx_l2_header);
#ifdef HSI_LOGICAL_RXDATA_128_BYTES_PADDED
if ((l3len%128) != 0)
l3len = l3len + 128 - l3len%128;
#endif
/* Allocate msg for reception */
skb = netdev_alloc_skb(hsi_protocol_context->netdev, l3len);
if (unlikely(!skb)) {
DPRINTK("create_rx_data_msg - No memory for rx skb\n");
goto err;
}
skb->dev = hsi_protocol_context->netdev;
skb_put(skb, l3len);
msg = hsi_logical_alloc_data(skb, GFP_ATOMIC | GFP_DMA);
if (unlikely(!msg)) {
DPRINTK("create_rx_data_msg - No memory for RX data msg\n");
dev_kfree_skb(skb);
goto err;
}
msg->destructor = hsi_logical_data_free_msg_and_skb;
msg->complete = hsi_logical_rx_data_complete;
msg->channel = channel_id;
/* Wait data message on corresponding port */
if (hsi_async_read(hsi_data_client->hsi_cl, msg)) {
/* SKB is already released by HSI physical */
EPRINTK("Error hsi_l2header_receive Failed to" \
"hsi_async_read data message\n");
return;
}
/* Next L2 header will be received on channel X */
hsi_logical_wait_rx_control_l2h(hsi_control_client->hsi_cl, channel_id);
/* Then send ACK */
DPRINTK_HIGH_RX("%s - send ACK for channel_id %d\n",
__func__,
channel_id);
#ifndef HSI_LOGICAL_SEND_ACK_BEFORE_ALLOC
/* Check WAKE line, in case TX is off,
need to start TX for sending ACK */
hsi_logical_start_tx(hsi_protocol_context);
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_control_client,
(HSI_ACK<<24 | channel_id),
HSI_CONTROL_CHANNEL,
hsi_logical_tx_control_complete);
#endif
wake_lock_timeout(&wake_no_suspend_l2header, 1 * HZ);
return;
err:
wake_lock_timeout(&wake_no_suspend_l2header, 5 * HZ);
hsi_data_client->rx_mem.function = hsi_rx_mem_test;
hsi_data_client->rx_mem.data = (unsigned long)hsi_data_client;
mod_timer(&hsi_data_client->rx_mem, jiffies + msecs_to_jiffies(50));
return;
}
static int
hsi_l2header_receive(struct hsi_protocol_client *hsi_control_client,
unsigned char channel_id,
u32 l2header)
{
struct hsi_protocol *hsi_protocol_context =
hsi_control_client->hsi_top_protocol_context;
struct hsi_protocol_client *hsi_data_client;
u32 l3len;
int client_id;
if (hsi_logical_retrieve_data_client(
hsi_control_client,
channel_id,
&hsi_data_client) < 0) {
EPRINTK("hsi_l2header_receive - ERROR mismatch with" \
" channel ID used in L2 Header\n");
return 0;
}
client_id = hsi_data_client->client_id;
#ifdef HSI_LOGICAL_USE_DEBUG
BUG_ON(hsi_logical_get_client_id(MAPID_FROM_HEADER(l2header)) != client_id);
#endif
l3len = LENGTH_IN_HEADER(l2header);
DPRINTK_HIGH_RX("hsi_l2header_receive - mapid %d - len %d" \
" - channel_id %d - corresponding client id %d\n",
MAPID_FROM_HEADER(l2header),
l3len,
channel_id,
client_id);
if (l3len > MAXIMUM_MSG_LENGTH_ALLOWED) {
/* length exceed max length => send NACK*/
EPRINTK("hsi_l2header 0x%08x received - length %d exceed" \
" max length send NACK\n", l2header, l3len);
goto nack;
}
if (l3len == 0) {
/* length exceed max length => send NACK*/
EPRINTK("hsi_l2header 0x%08x received - length %d exceed" \
" max length send NACK\n", l2header, l3len);
goto nack;
} else {
spin_lock_bh(&hsi_data_client->rx_lock);
if (hsi_data_client->recv_state == HSI_RX_IDLE) {
DPRINTK("hsi_l2header_receive RX state of data" \
" client is idle => send ACK\n");
hsi_data_client->recv_state = HSI_RX_RVCING_DATA;
} else if (hsi_data_client->recv_state == HSI_RX_RVCING_DATA) {
/* RX client is not ready
Wait previous RX completion before sending ACK */
EPRINTK("hsi_l2header_receive L2header while" \
" previous RX data ongoing: 0x%08x on channel_id: %d\n",
l2header,
channel_id);
hsi_data_client->pending_rx_l2_header = l2header;
hsi_data_client->pending_rx_l2_header_channel =
channel_id;
hsi_data_client->recv_state =
HSI_RX_HDR_RCV_WHILE_DATA_ONGOING;
spin_unlock_bh(&hsi_data_client->rx_lock);
return 0;
} else if (hsi_data_client->recv_state ==
HSI_RX_HDR_RCV_WHILE_DATA_ONGOING) {
EPRINTK("Error hsi_l2header_receive L2header" \
" while previous RX data ongoing AND " \
"there is already a pending RX L2 header\n");
/*BUG();*/
spin_unlock_bh(&hsi_data_client->rx_lock);
return 0;
}
/* Stop inactivity timer. It will be relaunched on RX
data completion callback */
spin_lock_bh(&hsi_protocol_context->tx_activity_status_lock);
del_timer(&hsi_protocol_context->inactivity_timer);
hsi_protocol_context->inactivity_timer_running = false;
spin_unlock_bh(&hsi_protocol_context->tx_activity_status_lock);
/* Save rx L2 header (to concatenete later with payload) */
hsi_data_client->rx_l2_header = l2header;
hsi_data_client->rx_l2_header_channel = channel_id;
spin_unlock_bh(&hsi_data_client->rx_lock);
hsi_rx_data_ack(hsi_protocol_context, hsi_data_client);
}
return 0;
nack:
/* Check WAKE line, in case TX is off,
need to start TX for sending NACK */
hsi_logical_start_tx(hsi_protocol_context);
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_control_client,
(HSI_NACK<<24 | channel_id),
HSI_CONTROL_CHANNEL,
hsi_logical_tx_control_complete);
/* Reallocate to receive the correct L2HEADER
Next L2 header will be received on channel X */
hsi_logical_wait_rx_control_l2h(hsi_control_client->hsi_cl, channel_id);
return -1;
}
static void
hsi_logical_rx_data_complete(struct hsi_msg *msg)
{
struct hsi_client *cl = msg->cl;
struct hsi_protocol_client *hsi_data_client = hsi_client_drvdata(cl);
struct hsi_protocol *hsi_protocol_context =
hsi_data_client->hsi_top_protocol_context;
struct sk_buff *skb = msg->context;
DPRINTK_HIGH_RX("hsi_logical_rx_data_complete - client ID %d - " \
"port nb %d - channel_id %d - Latest frame is 0x%08x\n",
hsi_data_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel,
*(u32 *)(sg_virt(msg->sgt.sgl) + skb->len - 4));
/* Start inactivity timer if not already running for TX. On expiration,
if all queues are ready call hsi_stop_tx */
spin_lock_bh(&hsi_protocol_context->tx_activity_status_lock);
{
if (hsi_protocol_context->inactivity_timer_running != true) {
mod_timer(&hsi_protocol_context->inactivity_timer,
jiffies + msecs_to_jiffies(HSI_LOGICAL_INACTIVITY_TIMER));
hsi_protocol_context->inactivity_timer_running = true;
}
}
spin_unlock_bh(&hsi_protocol_context->tx_activity_status_lock);
if ((msg->status == HSI_STATUS_ERROR)
|| (hsi_data_client->client_id == HSI_CONTROL_CLIENT_ID)
|| (msg->channel == HSI_CONTROL_CHANNEL)) {
EPRINTK("RX data error\n");
/* Delete msg and skb */
hsi_logical_data_free_msg_and_skb(msg);
return;
}
/* Add previously received L2 header */
skb_push(skb, HSI_LOGICAL_CONTROL_MESSAGE_SIZE);
*(u32 *)(skb->data) = hsi_data_client->rx_l2_header;
#ifdef HSI_LOGICAL_RXDATA_128_BYTES_PADDED
skb_trim(skb, 4 + LENGTH_IN_HEADER(hsi_data_client->rx_l2_header));
#endif
/* Delete only hsi msg - skb will be freed by appli */
hsi_logical_data_free_msg(msg);
spin_lock_bh(&hsi_data_client->rx_lock);
if (hsi_data_client->recv_state == HSI_RX_RVCING_DATA) {
hsi_data_client->recv_state = HSI_RX_IDLE;
spin_unlock_bh(&hsi_data_client->rx_lock);
/* Callback receive function */
hsi_data_client->hsi_top_protocol_context->receive_fn(skb);
} else if (hsi_data_client->recv_state ==
HSI_RX_HDR_RCV_WHILE_DATA_ONGOING) {
EPRINTK("hsi_logical_rx_data_complete - managing" \
"pending received L2 header: 0x%08x on channel_id %d\n",
hsi_data_client->pending_rx_l2_header,
hsi_data_client->pending_rx_l2_header_channel);
hsi_data_client->recv_state = HSI_RX_IDLE;
spin_unlock_bh(&hsi_data_client->rx_lock);
/* Callback receive function */
hsi_data_client->hsi_top_protocol_context->receive_fn(skb);
hsi_l2header_receive(
hsi_data_client->hsi_top_protocol_context->
cl[HSI_CONTROL_CLIENT_ID],
hsi_data_client->pending_rx_l2_header_channel,
hsi_data_client->pending_rx_l2_header);
} else if (hsi_data_client->recv_state == HSI_RX_CLOSE) {
spin_unlock_bh(&hsi_data_client->rx_lock);
kfree_skb(skb);
} else {
spin_unlock_bh(&hsi_data_client->rx_lock);
EPRINTK("hsi_logical_rx_data_complete - wrong " \
"state for RX client: %d\n",
hsi_data_client->recv_state);
}
}
static void
hsi_logical_data_free_msg_and_skb(struct hsi_msg *msg)
{
struct sk_buff *skb = msg->context;
DPRINTK("hsi_logical_data_free_msg_and_skb - port num %d" \
" - channel_id %d\n", (hsi_get_port(msg->cl))->num,
msg->channel);
msg->destructor = NULL;
dev_kfree_skb(skb);
hsi_free_msg(msg);
}
static void
hsi_logical_data_free_msg(struct hsi_msg *msg)
{
DPRINTK("hsi_logical_data_free_msg - port num %d" \
" - channel_id %d\n", (hsi_get_port(msg->cl))->num,
msg->channel);
msg->destructor = NULL;
hsi_free_msg(msg);
}
static void
hsi_logical_control_destructor(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_client =
hsi_client_drvdata(msg->cl);
struct hsi_protocol *hsi_protocol_context =
hsi_client->hsi_top_protocol_context;
int i = 0;
spin_lock_bh(&hsi_protocol_context->ctrl_msg_array_lock);
while ((msg != hsi_protocol_context->hsi_ctrl_msg_array[i].msg)
&& (i < HSI_LOGICAL_NB_CONTROL_MSG-1))
i++;
if (msg == hsi_protocol_context->hsi_ctrl_msg_array[i].msg) {
/* Msg to free found */
DPRINTK("hsi_logical_control_destructor - " \
"msg id %d is now free\n", i);
hsi_protocol_context->hsi_ctrl_msg_array[i].free = 1;
} else {
/* Msg not found ! */
BUG();
}
spin_unlock_bh(&hsi_protocol_context->ctrl_msg_array_lock);
}
static void
hsi_logical_control_destructor_l2h(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_client =
hsi_client_drvdata(msg->cl);
struct hsi_protocol *hsi_protocol_context =
hsi_client->hsi_top_protocol_context;
int i = 0;
spin_lock_bh(&hsi_protocol_context->ctrl_msg_array_l2h_lock);
while ((msg != hsi_protocol_context->hsi_ctrl_msg_array_l2h[i].msg)
&& (i < HSI_LOGICAL_NB_CONTROL_MSG_L2H-1))
i++;
if (msg == hsi_protocol_context->hsi_ctrl_msg_array_l2h[i].msg) {
/* Msg to free found */
DPRINTK("hsi_logical_control_destructor - " \
"msg id %d is now free\n", i);
hsi_protocol_context->hsi_ctrl_msg_array_l2h[i].free = 1;
} else {
/* Msg not found ! */
BUG();
}
spin_unlock_bh(&hsi_protocol_context->ctrl_msg_array_l2h_lock);
}
static void
hsi_logical_tx_control_destructor(struct hsi_msg *msg)
{
DPRINTK("hsi_logical_tx_control_destructor on port num %d -" \
"channel_id %d\n", (hsi_get_port(msg->cl))->num,
msg->channel);
hsi_logical_control_destructor(msg);
}
static void
hsi_logical_rx_control_destructor(struct hsi_msg *msg)
{
DPRINTK("hsi_logical_rx_control_destructor on port num %d-" \
" channel_id %d\n", (hsi_get_port(msg->cl))->num, msg->channel);
hsi_logical_control_destructor(msg);
}
static void
hsi_logical_rx_control_destructor_l2h(struct hsi_msg *msg)
{
DPRINTK("hsi_logical_rx_control_destructor_l2h on port num %d-" \
" channel_id %d\n", (hsi_get_port(msg->cl))->num, msg->channel);
hsi_logical_control_destructor_l2h(msg);
}
static struct hsi_msg *
hsi_logical_alloc_data(struct sk_buff *skb, gfp_t flags)
{
struct hsi_msg *msg;
msg = hsi_alloc_msg(skb_shinfo(skb)->nr_frags + 1, flags);
if (!msg)
goto out;
hsi_logical_skb_to_msg(skb, msg);
msg->context = skb;
return msg;
out:
EPRINTK("hsi_logical_alloc_data BUG\n") ;
BUG();
return NULL; /* Useless, just avoid Klockwork error */
}
static struct hsi_msg *
hsi_logical_get_control(struct hsi_protocol_client *hsi_client_context)
{
struct hsi_protocol *hsi_protocol_context =
hsi_client_context->hsi_top_protocol_context;
int i = 0;
spin_lock_bh(&hsi_protocol_context->ctrl_msg_array_lock);
/* Find a free msg */
while ((hsi_protocol_context->hsi_ctrl_msg_array[i].free == 0)
&& (i < HSI_LOGICAL_NB_CONTROL_MSG))
i++;
if (hsi_protocol_context->hsi_ctrl_msg_array[i].free == 0) {
/* No free msg !! */
BUG();
}
/* msg is now busy */
hsi_protocol_context->hsi_ctrl_msg_array[i].free = 0;
spin_unlock_bh(&hsi_protocol_context->ctrl_msg_array_lock);
DPRINTK("hsi_logical_get_control - msg id %d is now used\n", i);
return hsi_protocol_context->hsi_ctrl_msg_array[i].msg;
}
static struct hsi_msg *
hsi_logical_get_control_l2h(struct hsi_protocol_client *hsi_client_context)
{
struct hsi_protocol *hsi_protocol_context =
hsi_client_context->hsi_top_protocol_context;
int i = 0;
spin_lock_bh(&hsi_protocol_context->ctrl_msg_array_l2h_lock);
/* Find a free msg */
while ((i < HSI_LOGICAL_NB_CONTROL_MSG_L2H) &&
(hsi_protocol_context->hsi_ctrl_msg_array_l2h[i].free == 0))
i++;
if (hsi_protocol_context->hsi_ctrl_msg_array_l2h[i].free == 0) {
/* No free msg !! */
BUG();
}
/* msg is now busy */
hsi_protocol_context->hsi_ctrl_msg_array_l2h[i].free = 0;
spin_unlock_bh(&hsi_protocol_context->ctrl_msg_array_l2h_lock);
DPRINTK("hsi_logical_get_control l2h- msg id %d is now used\n", i);
return hsi_protocol_context->hsi_ctrl_msg_array_l2h[i].msg;
}
static struct hsi_msg *
hsi_logical_alloc_tx_control(struct hsi_protocol_client *hsi_client_context,
void (*complete)(struct hsi_msg *msg))
{
struct hsi_msg *msg = hsi_logical_get_control(hsi_client_context);
msg->destructor = hsi_logical_tx_control_destructor;
msg->complete = complete;
return msg;
}
#ifdef HSI_LOGICAL_USE_DEBUG
static int hsi_logical_get_client_id(unsigned char mapid)
{
int client_id;
switch (mapid) {
case MHI_L3_PHONET:
case MHI_L3_FILE:
case MHI_L3_SECURITY:
case MHI_L3_TEST:
case MHI_L3_MED_PRIO_TEST:
/* Medium prio */
client_id = HSI_LOGICAL_CLIENT_PRIO_MEDIUM;
break;
case MHI_L3_XFILE:
case MHI_L3_MHDP_DL:
case MHI_L3_MHDP_UL:
case MHI_L3_LOW_PRIO_TEST:
/* Low prio */
client_id = HSI_LOGICAL_CLIENT_PRIO_LOW;
break;
case MHI_L3_AUDIO:
case MHI_L3_TEST_PRIO:
case MHI_L3_HIGH_PRIO_TEST:
/*high prio*/
client_id = HSI_LOGICAL_CLIENT_PRIO_HIGH;
break;
default:
if ((mapid >= 128) && (mapid <= 191)) {
/* High prio */
client_id = HSI_LOGICAL_CLIENT_PRIO_HIGH;
} else if ((mapid >= 192) && (mapid <= 255)) {
/* Medium prio */
client_id = HSI_LOGICAL_CLIENT_PRIO_MEDIUM;
} else {
/* Undefined for now */
client_id = HSI_LOGICAL_CLIENT_PRIO_LOW;
}
break;
}
return client_id;
}
#endif
static int
hsi_logical_retrieve_data_client(
struct hsi_protocol_client *hsi_client_context,
unsigned char channel_id,
struct hsi_protocol_client **hsi_data_client)
{
if (unlikely(channel_id > HSI_MAX_CHANNEL_ID)) {
/* Should never happen */
DPRINTK("hsi_logical_retrieve_data_client" \
" - ERROR: wrong channel ID: %d !!\n", channel_id);
goto out;
}
*hsi_data_client =
hsi_client_context->
hsi_top_protocol_context->
cl[HSI_LOGICAL_GET_PRIO_ACCORDING_TO_CHANNEL(channel_id)];
if (unlikely(!hsi_data_client)) {
/* Should never happen */
DPRINTK("hsi_logical_retrieve_data_client - ERROR: Client" \
"is not allocated (channel ID: %d) !!\n",
channel_id);
goto out;
}
return 0;
out:
return -1;
}
static void hsi_logical_skb_to_msg(struct sk_buff *skb, struct hsi_msg *msg)
{
skb_frag_t *frag;
struct scatterlist *sg;
int i;
BUG_ON(msg->sgt.nents !=
(unsigned int)(skb_shinfo(skb)->nr_frags + 1));
sg = msg->sgt.sgl;
sg_set_buf(sg, skb->data, skb_headlen(skb));
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
sg = sg_next(sg);
BUG_ON(!sg);
frag = &skb_shinfo(skb)->frags[i];
sg_set_page(sg, frag->page.p, frag->size, frag->page_offset);
}
}
#ifdef HSI_V3_USED
static void
hsi_logical_power_off_complete(struct hsi_msg *msg)
{
struct hsi_protocol_client *hsi_control_client =
hsi_client_drvdata(msg->cl);
DPRINTK("hsi_logical_power_off_complete - client ID %d" \
" - port nb %d - channel_id %d\n",
hsi_control_client->client_id,
(hsi_get_port(msg->cl))->num,
msg->channel);
hsi_logical_do_power_sequence(
hsi_control_client->hsi_top_protocol_context,
HSI_POWER_OFF_STEP_2);
/* Free control message */
hsi_logical_tx_control_destructor(msg);
}
void
hsi_logical_send_power_off(struct hsi_protocol *hsi_protocol_context)
{
DPRINTK("hsi_logical_send_power_off");
hsi_logical_alloc_msg_and_send_on_control_channel(
hsi_protocol_context->cl[HSI_CONTROL_CLIENT_ID],
HSI_POWER_OFF<<24,
HSI_CONTROL_CHANNEL,
hsi_logical_power_off_complete);
}
void
hsi_logical_do_power_sequence(
struct hsi_protocol *hsi_protocol_context,
int hsi_power_state)
{
DPRINTK("hsi_logical_do_power_sequence step %d", hsi_power_state);
switch (hsi_power_state) {
case HSI_POWER_OFF_STEP_1:
hsi_protocol_context->power_state = HSI_POWER_SEND_OFF_STATE;
hsi_logical_send_power_off(hsi_protocol_context);
break;
case HSI_POWER_OFF_STEP_2:
hsi_logical_stop_tx(hsi_protocol_context);
break;
default:
break;
}
}
#endif /*HSI_V3_USED*/
static void
hsi_logical_inactivity_timer(unsigned long data)
{
struct hsi_protocol *hsi_protocol_context =
(struct hsi_protocol *)data;
int i;
int one_client_up = 0;
DPRINTK("hsi_logical_inactivity_timer");
spin_lock_bh(&hsi_protocol_context->tx_activity_status_lock);
{
hsi_protocol_context->inactivity_timer_running = false;
for (i = 1; i < HSI_NB_CLIENT; i++) {
if (__netif_subqueue_stopped(
hsi_protocol_context->netdev,
HSI_LOG_CL_ID_TO_QUEUE_ID(i))) {
one_client_up++;
break;
}
}
if ((!one_client_up) &&
(hsi_protocol_context->main_state != HSI_CONFIG_EXCHANGE_NO_DONE)) {
#ifdef HSI_V3_USED
/*start the power off sequence*/
hsi_logical_do_power_sequence(
hsi_protocol_context,
HSI_POWER_OFF_STEP_1);
#else
DPRINTK("hsi_logical_inactivity_timer expires." \
"No client want to transmit => Stop TX\n");
hsi_logical_stop_tx(hsi_protocol_context);
#endif /*HSI_V3_USED*/
#ifdef HSI_LOGICAL_POWER_SAVING
wake_unlock(&wake_temp);
#endif /*HSI_LOGICAL_POWER_SAVING*/
}
#ifdef HSI_LOGICAL_USE_DEBUG
else {
DPRINTK("hsi_logical_inactivity_timer expires." \
"Don't stop TX (client want to transmit)\n");
}
#endif
}
spin_unlock_bh(&hsi_protocol_context->tx_activity_status_lock);
}
static int __init hsi_logical_init(void)
{
DPRINTK("hsi_logical_init\n");
wake_lock_init(&wake_resoft, WAKE_LOCK_SUSPEND, "HSI_modem_upload");
wake_lock_init(&wake_no_suspend_l2header, WAKE_LOCK_SUSPEND,
"HSI_L2_header_no_suspend");
wake_lock_init(&wake_temp, WAKE_LOCK_SUSPEND,
"HSI_prevent_system_power");
#ifndef HSI_LOGICAL_POWER_SAVING
/*DEFINITIVELY TAKE WAKE LOCK*/
wake_lock(&wake_temp);
#endif /*HSI_LOGICAL_POWER_SAVING*/
init_spinlock = 1;
return 0;
}
module_init(hsi_logical_init);
static void __exit hsi_logical_exit(void)
{
DPRINTK("hsi_logical_exit\n");
}
module_exit(hsi_logical_exit);
MODULE_ALIAS("hsi_logical");
MODULE_AUTHOR("RMC");
MODULE_DESCRIPTION("HSI logical using RENESAS HSI protocol");
MODULE_LICENSE("GPL");