diff options
Diffstat (limited to 'drivers/omap_hsi/hsi_protocol_cmd.c')
-rw-r--r-- | drivers/omap_hsi/hsi_protocol_cmd.c | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/drivers/omap_hsi/hsi_protocol_cmd.c b/drivers/omap_hsi/hsi_protocol_cmd.c new file mode 100644 index 0000000..f28b049 --- /dev/null +++ b/drivers/omap_hsi/hsi_protocol_cmd.c @@ -0,0 +1,429 @@ +/* + * File - hsi_protocol_if_cmd.c + * + * Implements HSI protocol for Infineon Modem. + * + * Copyright (C) 2011 Samsung Electronics. All rights reserved. + * + * Author: Rupesh Gujare <rupesh.g@samsung.com> + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/sched.h> + +#include <linux/hsi_driver_if.h> +#include "hsi-protocol-if.h" + +extern struct if_hsi_iface hsi_protocol_iface; +extern wait_queue_head_t ipc_read_wait, ipc_write_wait; +int if_hsi_openchannel(struct if_hsi_channel *channel); +int if_hsi_closechannel(struct if_hsi_channel *channel); + +extern struct if_hsi_cmd hsi_cmd_history; +extern int tx_cmd_history_p; +extern int rx_cmd_history_p; + +/*Decode command from received PDU on channle 0*/ +int hsi_decode_cmd(u32 *cmd_data, u32 *cmd, u32 *ch, u32 *param) +{ + int ret = 0; + u32 data = *cmd_data; + u8 lrc_cal, lrc_act; + u8 val1, val2, val3; + + *cmd = ((data & 0xF0000000) >> 28); + + switch (*cmd) { + case HSI_LL_MSG_BREAK: + pr_err("Command MSG_BREAK Received.\n"); + break; + + case HSI_LL_MSG_OPEN_CONN: + *ch = ((data & 0x0F000000) >> 24); + *param = ((data & 0x00FFFF00) >> 8); + /*Check LRC*/ + val1 = ((data & 0xFF000000) >> 24); + val2 = ((data & 0x00FF0000) >> 16); + val3 = ((data & 0x0000FF00) >> 8); + lrc_act = (data & 0x000000FF); + lrc_cal = val1 ^ val2 ^ val3; + if (lrc_cal != lrc_act) + ret = -1; + break; + + case HSI_LL_MSG_CONN_READY: + case HSI_LL_MSG_CONN_CLOSED: + case HSI_LL_MSG_CANCEL_CONN: + case HSI_LL_MSG_NAK: + *ch = ((data & 0x0F000000) >> 24); + break; + + case HSI_LL_MSG_ACK: + *ch = ((data & 0x0F000000) >> 24); + *param = (data & 0x00FFFFFF); + //printk(KERN_INFO "ACK Received ch=%d, param=%d\n",*ch, *param); + break; + + case HSI_LL_MSG_CONF_RATE: + *ch = ((data & 0x0F000000) >> 24); + *param = ((data & 0x0F000000) >> 24); + break; + + case HSI_LL_MSG_OPEN_CONN_OCTET: + *ch = ((data & 0x0F000000) >> 24); + *param = (data & 0x00FFFFFF); + break; + + case HSI_LL_MSG_ECHO: + case HSI_LL_MSG_INFO_REQ: + case HSI_LL_MSG_INFO: + case HSI_LL_MSG_CONFIGURE: + case HSI_LL_MSG_ALLOCATE_CH: + case HSI_LL_MSG_RELEASE_CH: + case HSI_LL_MSG_INVALID: + *cmd = HSI_LL_MSG_INVALID; + *ch = HSI_LL_INVALID_CHANNEL; + ret = -1; + break; + } + return ret; +} + +int protocol_create_cmd(int cmd_type, unsigned int channel, void *arg) +{ + unsigned int command = 0; + int ret = 0; + + switch (cmd_type) { + case HSI_LL_MSG_BREAK: + { + command = 0; + } + break; + + case HSI_LL_MSG_OPEN_CONN: + { + unsigned int size = *(unsigned int *)arg; + unsigned int lcr = 0; + +/* if(size > 4) + size = (size & 0x3) ? ((size >> 2) + 1):(size >> 2); + else + size = 1;*/ + + command = ((HSI_LL_MSG_OPEN_CONN & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24) | + ((size & 0x0000FFFF) << 8); + + lcr = ((command & 0xFF000000) >> 24) ^ + ((command & 0x00FF0000) >> 16) ^ + ((command & 0x0000FF00) >> 8); + + command = command | (lcr & 0x000000FF); + } + break; + + case HSI_LL_MSG_CONN_READY: + { + command = ((HSI_LL_MSG_CONN_READY & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24); + } + break; + + case HSI_LL_MSG_CONN_CLOSED: + { + command = ((HSI_LL_MSG_CONN_CLOSED & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24); + } + break; + + case HSI_LL_MSG_CANCEL_CONN: + { + unsigned int role = *(unsigned int *)arg; + + command = ((HSI_LL_MSG_CANCEL_CONN & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24) | + ((role & 0x000000FF) << 16); + } + break; + + case HSI_LL_MSG_ACK: + { + unsigned int echo_params = *(unsigned int *)arg; + + command = ((HSI_LL_MSG_ACK & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24) | + ((echo_params & 0x00FFFFFF)); + } + break; + + case HSI_LL_MSG_NAK: + { + command = ((HSI_LL_MSG_NAK & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24); + } + break; + + case HSI_LL_MSG_CONF_RATE: + { + unsigned int baud_rate = *(unsigned int *)arg; + + command = ((HSI_LL_MSG_CONF_RATE & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24) | + ((baud_rate & 0x00FFFFFF)); + } + break; + + case HSI_LL_MSG_OPEN_CONN_OCTET: + { + unsigned int size = *(unsigned int *)arg; + + command = ((HSI_LL_MSG_OPEN_CONN_OCTET & 0x0000000F) << 28) | + ((channel & 0x000000FF) << 24) | + ((size & 0x00FFFFFF)); + + } + break; + + case HSI_LL_MSG_ECHO: + case HSI_LL_MSG_INFO_REQ: + case HSI_LL_MSG_INFO: + case HSI_LL_MSG_CONFIGURE: + case HSI_LL_MSG_ALLOCATE_CH: + case HSI_LL_MSG_RELEASE_CH: + case HSI_LL_MSG_INVALID: + ret = -1; + break; + } + return command; +} + +int set_tx_config(struct if_hsi_channel *ch, u32 mode, u32 max_channels) +{ + struct hst_ctx tx_config; + int ret; + + hsi_ioctl(ch->dev, HSI_IOCTL_GET_TX, &tx_config); + tx_config.mode = mode; + tx_config.channels = max_channels; + ret = hsi_ioctl(ch->dev, HSI_IOCTL_SET_TX, &tx_config); + return ret; +} + +static int saved_cmd_queue = 0; +static u32 cmd_saved[5]; +int hsi_protocol_send_command(u32 cmd, u32 channel, u32 param) +{ + struct if_hsi_channel *channel_zero; + u32 cmd_array[4] = {0x00000000, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC}, ret = -1; + + channel_zero = &hsi_protocol_iface.channels[0]; + cmd_array[0] = protocol_create_cmd(cmd, channel, ¶m); + pr_debug("[%s] CMD = %08x\n",__func__, cmd_array[0]); + while (channel_zero->tx_state != HSI_LL_TX_STATE_IDLE) { + cmd_saved[saved_cmd_queue] = cmd_array[0]; + saved_cmd_queue++; + pr_debug("(%s) cmd_saved : %x(%d)\n", __func__, cmd_array[0], saved_cmd_queue); + + return 0; + } + +send_retry: + + channel_zero->tx_state = HSI_LL_TX_STATE_TX; + + // For es 2.1 ver. + ret = hsi_proto_write(0, cmd_array, 4); + if (ret < 0) { + pr_err("(%s) Command Write failed, CMD->%X\n", __func__, cmd_array[0]); + channel_zero->tx_state = HSI_LL_TX_STATE_IDLE; + return -1; + } else { + channel_zero->tx_state = HSI_LL_TX_STATE_IDLE; + + pr_debug("[%s] CMD = %08x\n", __func__, cmd_array[0]); + + hsi_cmd_history.tx_cmd[tx_cmd_history_p] = cmd_array[0]; + hsi_cmd_history.tx_cmd_time[tx_cmd_history_p] = CURRENT_TIME; + tx_cmd_history_p++; + if (tx_cmd_history_p >= 50) + tx_cmd_history_p = 0; + + if (saved_cmd_queue) { + saved_cmd_queue--; + cmd_array[0] = cmd_saved[saved_cmd_queue]; + + goto send_retry; + } + + return 0; + } +} + +void rx_stm(u32 cmd, u32 ch, u32 param) +{ + struct if_hsi_channel *channel; + u32 size = 0, tmp_cmd = 0, ret, i; + channel = &hsi_protocol_iface.channels[ch]; + + switch (cmd) { + case HSI_LL_MSG_OPEN_CONN: + pr_err("ERROR... OPEN_CONN Not supported. Should use OPEN_CONN_OCTECT instead.\n"); + break; + + case HSI_LL_MSG_ECHO: + pr_err("ERROR... HSI_LL_MSG_ECHO not supported.\n"); + break; + + case HSI_LL_MSG_CONN_CLOSED: + switch (channel->tx_state) { + case HSI_LL_TX_STATE_WAIT_FOR_CONN_CLOSED: + channel->tx_state = HSI_LL_TX_STATE_IDLE; + + /* ACWAKE ->LOW */ + ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_DOWN, NULL); + if (ret == 0) + pr_debug("ACWAKE pulled low in %s()\n", __func__); + else + pr_err("ACWAKE pulled low in %s() ERROR : %d\n", __func__, ret); + + pr_debug("[%s] Received CONN_CLOSED. ch-> %d\n", __func__,ch); + break; + + default: + pr_err("Wrong STATE for CONN_CLOSED\n"); + } + break; + + case HSI_LL_MSG_CANCEL_CONN: + pr_debug("Received CANCEL_CONN\n"); + break; + + case HSI_LL_MSG_ACK: + switch (channel->tx_state) { + case HSI_LL_TX_STATE_WAIT_FOR_ACK: + case HSI_LL_TX_STATE_SEND_OPEN_CONN: + //printk(KERN_INFO "ACK received %s()\n",__func__); + + channel->tx_state = HSI_LL_TX_STATE_TX; + size = param; +#if 0 + // TEMP: send/read by 16 byte unit for v.11A(CP) + if ((size > 16) && (size % 16)) + size += (16 - (size % 16)); + else if (size < 16) + size = 16; +#endif + + // For es 2.1 ver. + if (size % 4) + size += (4 - (size % 4)); + + pr_debug("Writing %d bytes data on channel %d, tx_buf = %x, in %s()\n", size, ch, channel->tx_buf, __func__); + ret = hsi_proto_write(ch, channel->tx_buf, size); + channel->tx_state = HSI_LL_TX_STATE_WAIT_FOR_CONN_CLOSED; + wake_up_interruptible(&ipc_write_wait); + channel->tx_nak_count = 0; + break; + + case HSI_LL_TX_STATE_CLOSED:/* ACK as response to CANCEL_CONN */ + if (channel->rx_state == HSI_LL_RX_STATE_WAIT_FOR_CANCEL_CONN_ACK) + channel->rx_state = HSI_LL_RX_STATE_IDLE; + break; + + case HSI_LL_TX_STATE_WAIT_FOR_CONF_ACK: /* ACK as response to CONF_RATE */ + //TODO: SET CONF RATE + pr_debug("ACK Received for CONF_RATE\n"); + break; + + default: + pr_err("ACK Received for Unknown state\n"); + } + break; + + case HSI_LL_MSG_NAK: + switch (channel->tx_state) { + case HSI_LL_TX_STATE_WAIT_FOR_ACK: + printk(KERN_INFO "(%s) NAK received. ch->%d\n", __func__, ch); + //channel->tx_state = HSI_LL_TX_STATE_NACK; + if (channel->tx_nak_count < 10) { + msleep(10); + + tmp_cmd = ((HSI_LL_MSG_OPEN_CONN_OCTET & 0x0000000F) << 28) | + ((ch & 0x000000FF) << 24); + for (i = 49; i >= 0; i--) { + if ((hsi_cmd_history.tx_cmd[i] & 0xFFF00000) == tmp_cmd) + break; + } + size = (hsi_cmd_history.tx_cmd[i] & 0x000FFFFF); + + pr_debug("(%s) Re Send OPEN CONN ch->%d, size->%d, count->%d\n", __func__, ch, size, channel->tx_nak_count); + + hsi_protocol_send_command(HSI_LL_MSG_OPEN_CONN_OCTET, ch, size); + channel->tx_nak_count++; + } else { + hsi_protocol_send_command(HSI_LL_MSG_BREAK, ch, size); + pr_debug("(%s) Sending MSG_BREAK. ch->%d\n", __func__, ch); + //TODO Reset All channels and inform IPC write about failure (Possibly by sending signal) + } + break; + + case HSI_LL_TX_STATE_WAIT_FOR_CONF_ACK: /* NAK as response to CONF_RATE */ + channel->tx_state = HSI_LL_TX_STATE_IDLE; + break; + + default: + pr_err("ERROR - Received NAK in invalid state. state->%d\n", channel->tx_state); + } + break; + + case HSI_LL_MSG_CONF_RATE: + //TODO: Set Conf Rate + pr_debug("CONF_RATE Received\n"); + break; + + case HSI_LL_MSG_OPEN_CONN_OCTET: + switch (channel->rx_state) { + /* case HSI_LL_RX_STATE_CLOSED: */ + case HSI_LL_RX_STATE_IDLE: + pr_debug("OPEN_CONN_OCTET in %s(), ch-> %d\n", __func__, ch); + channel->rx_state = HSI_LL_RX_STATE_TO_ACK; + hsi_protocol_send_command(HSI_LL_MSG_ACK, ch, param); + + channel->rx_count = param; + channel->rx_state = HSI_LL_RX_STATE_RX; + wake_up_interruptible(&ipc_read_wait); + break; + + case HSI_LL_RX_STATE_BLOCKED: + /* TODO */ + break; + + default: + pr_err("OPEN_CONN_OCTET in invalid state, Current State -> %d\n", channel->rx_state); + pr_info("Sending NAK to channel-> %d\n", ch); + hsi_protocol_send_command(HSI_LL_MSG_NAK, ch, param); + } + break; + + default: + pr_err("Invalid Command encountered in rx_state()\n"); + } + +} |