# CONFIG_HSI_CHAR is not set
# CONFIG_HSI_FFL_TTY is not set
CONFIG_HSI_DLP=y
-CONFIG_HSI_DLP_TTY_NAME="IFX"
+CONFIG_HSI_DLP_IPC_TTY_NAME="IFX"
CONFIG_HSI_DLP_TTY_STATS=y
CONFIG_HSI_DLP_NET_NAME="rmnet"
CONFIG_HSI_DLP_FRAME_LENGTH=4096
CONFIG_HSI_DLP_HEADER_LENGTH=4
+CONFIG_HSI_FLASHING_DEV_NAME="IFX"
#
string "Base name for the IPC TTY"
default "IFX"
help
- Sets the base name for the TTY associated to this eDLP modem protocol
+ Sets the base name for the TTY associated to this IMC modem protocol
for LTE.
+ The base name will be appended to the tty interface used by the HSI
+ eDLP protocol.
+
+ If unsure, use the default value.
+
+config HSI_FLASHING_DEV_NAME
+ string "Base name for the Boot/Flashing driver"
+ default "IFX"
+ help
+ Sets the base name for the char device associated to this IMC Boot/Flashing protocol
+ The base name will be appended to the char device interface used by the HSI
+ flashing protocol
+
+ If unsure, use the default value.
config HSI_DLP_TTY_STATS
bool "Statistics to assess the performance of the protocol (TTY)"
help
Sets the base name for the NETWORK interfaces associated to this IMC
modem protocol for LTE.
+ The base name will be exported by the network interface used by the HSI
+ eDLP protocol
+
+ If unsure, use the default value.
config HSI_DLP_PDU_LENGTH
int "Fixed frame length"
obj-$(CONFIG_HSI_CMT_SPEECH) += cmt_speech.o
obj-$(CONFIG_HSI_FFL_TTY) += hsi_ffl_tty.o
obj-$(CONFIG_HSI_DLP) += hsi_dlp.o
-hsi_dlp-objs := dlp_main.o dlp_ctrl.o dlp_tty.o dlp_net.o dlp_debug.o
+hsi_dlp-objs := dlp_flash.o dlp_main.o dlp_ctrl.o dlp_tty.o dlp_net.o dlp_debug.o
#define DLP_ECHO_CMD_CHECKSUM 0xACAFFE
-#define DLP_CTRL_CMD_TO_STR(id) \
- (id == DLP_CMD_BREAK ? "BREAK": \
- (id == DLP_CMD_ECHO ? "ECHO": \
- (id == DLP_CMD_NOP) ? "NOP": \
- (id == DLP_CMD_CONF_CH) ? "CONF_CH": \
- (id == DLP_CMD_OPEN_CONN) ? "OPEN_CONN": \
- (id == DLP_CMD_CANCEL_CONN) ? "CANCEL_CONN": \
- (id == DLP_CMD_ACK) ? "ACK": \
- (id == DLP_CMD_NACK) ? "NACK": \
- (id == DLP_CMD_OPEN_CONN_OCTET) ? "OPEN_CONN_OCTET": \
+#define DLP_CTRL_CMD_TO_STR(id) \
+ (id == DLP_CMD_BREAK ? "BREAK" : \
+ (id == DLP_CMD_ECHO ? "ECHO" : \
+ (id == DLP_CMD_NOP) ? "NOP" : \
+ (id == DLP_CMD_CONF_CH) ? "CONF_CH" : \
+ (id == DLP_CMD_OPEN_CONN) ? "OPEN_CONN" : \
+ (id == DLP_CMD_CANCEL_CONN) ? "CANCEL_CONN" : \
+ (id == DLP_CMD_ACK) ? "ACK" : \
+ (id == DLP_CMD_NACK) ? "NACK" : \
+ (id == DLP_CMD_OPEN_CONN_OCTET) ? "OPEN_CONN_OCTET" : \
(id == DLP_CMD_CREDITS) ? "CREDITS" : "UNKNOWN"))
-#define DLP_CTRL_CTX dlp_drv.channels[DLP_CHANNEL_CTRL]->ch_data
+#define DLP_CTRL_CTX (dlp_drv.channels[DLP_CHANNEL_CTRL]->ch_data)
/* DLP commands list */
#define DLP_CMD_BREAK 0x0
};
/* Modem cold boot management */
-#define V1P35CNT_W 0x0E0 /* PMIC reg to power off the modem */
-#define V1P35_OFF 4
-#define V1P35_ON 6
+#define V1P35CNT_W 0x0E0 /* PMIC reg to power off the modem */
+#define V1P35_OFF 4
+#define V1P35_ON 6
+
#define COLD_BOOT_DELAY_OFF 20000 /* 20 ms (use of usleep_range) */
-#define COLD_BOOT_DELAY_ON 10000 /* 10 ms (use of usleep_range) */
+#define COLD_BOOT_DELAY_ON 10000 /* 10 ms (use of usleep_range) */
/* Delays for powering up/resetting the modem */
#define DLP_DURATION_ON1 60 /* ON1 pulse delay: 60 us */
PROLOG();
- CRITICAL("Modem CORE_DUMP 0x%x", gpio_get_value(ctrl_ctx->gpio_fcdp_rb));
+ CRITICAL("Modem CORE_DUMP 0x%x",
+ gpio_get_value(ctrl_ctx->gpio_fcdp_rb));
/* Call registered channels */
for (i = 0; i < DLP_CHANNEL_COUNT; i++) {
ch_ctx = dlp_drv.channels[i];
- if (ch_ctx && ch_ctx->modem_coredump_cb) {
+ if (ch_ctx && ch_ctx->modem_coredump_cb)
ch_ctx->modem_coredump_cb(ch_ctx);
- }
}
EPILOG();
ch_ctx = dlp_drv.channels[i];
if (ch_ctx) {
/* Call any register callback */
- if (ch_ctx->modem_reset_cb) {
+ if (ch_ctx->modem_reset_cb)
ch_ctx->modem_reset_cb(ch_ctx);
- }
/* Reset the credits value */
ch_ctx->credits = 0;
*
*
*/
-static int dlp_ctrl_free_gpios(struct dlp_channel *ch_ctx,
- struct device *dev)
+static int
+dlp_ctrl_free_gpios(struct dlp_channel *ch_ctx,
+ struct device *dev)
{
struct dlp_ctrl_context *ctrl_ctx = ch_ctx->ch_data;
*
* @return
*/
-static inline int dlp_ctrl_configure_gpio(int gpio,
- int direction,
- int value,
- const char *desc)
+static inline int
+dlp_ctrl_configure_gpio(int gpio,
+ int direction,
+ int value,
+ const char *desc)
{
int ret;
ret = gpio_request(gpio, "ifxHSIModem");
- if (direction) {
+ if (direction)
ret += gpio_direction_output(gpio, value);
- }
- else {
+ else
ret += gpio_direction_input(gpio);
- }
if (ret) {
CRITICAL("Unable to configure GPIO%d (%s)",
return ret;
}
-static int dlp_ctrl_setup_irq_gpio(struct dlp_channel *ch_ctx,
- struct device *dev)
+/*
+* @brief This function is:
+* - requesting all needed gpios
+* - requesting all needed irqs
+* - registering irqs callbacks
+*
+* @param ch_ctx : Channel context
+* @param dev : Device driver info
+*/
+static int
+dlp_ctrl_setup_irq_gpio(struct dlp_channel *ch_ctx,
+ struct device *dev)
{
int ret;
struct dlp_ctrl_context *ctrl_ctx = ch_ctx->ch_data;
PROLOG();
/* Configure the RESET_BB gpio */
- ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_rst_bbn, 1, 1, "RST_BB");
- if (ret) {
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_rst_bbn,
+ 1, 1, "RST_BB");
+ if (ret)
goto free_ctx4;
- }
/* Configure the ON gpio */
- ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_pwr_on, 1, 1, "ON");
- if (ret) {
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_pwr_on,
+ 1, 1, "ON");
+ if (ret)
goto free_ctx3;
- }
/* Configure the RESET_OUT gpio & irq */
- ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_rst_out, 0, 0, "RST_OUT");
- if (ret) {
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_mdm_rst_out,
+ 0, 0, "RST_OUT");
+ if (ret)
goto free_ctx2;
- }
ctrl_ctx->reset.rst_irq = gpio_to_irq(ctrl_ctx->gpio_mdm_rst_out);
if (ctrl_ctx->reset.rst_irq < 0) {
}
/* Configure the CORE_DUMP gpio & irq */
- ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_fcdp_rb, 0, 0, "CORE_DUMP");
- if (ret) {
+ ret = dlp_ctrl_configure_gpio(ctrl_ctx->gpio_fcdp_rb,
+ 0, 0, "CORE_DUMP");
+ if (ret)
goto free_all;
- }
ctrl_ctx->reset.cd_irq = gpio_to_irq(ctrl_ctx->gpio_fcdp_rb);
if (ctrl_ctx->reset.cd_irq < 0) {
unsigned char param2,
unsigned char param3)
{
- int flags = in_interrupt()? GFP_ATOMIC : GFP_KERNEL;
struct dlp_command *dlp_cmd;
+ int flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
/* Allocate DLP command */
dlp_cmd = kmalloc(sizeof(struct dlp_command), flags);
*/
static inline void dlp_ctrl_cmd_free(struct dlp_command *dlp_cmd)
{
- if (dlp_cmd)
- kfree(dlp_cmd);
+ kfree(dlp_cmd);
}
/*
hsi_channel = params.channel;
if ((hsi_channel < 0) || (hsi_channel >= DLP_CHANNEL_COUNT)) {
CRITICAL("CREDITS: Invalid channel id (%d)", hsi_channel);
- BUG_ON(1); /* FIXME: We have BIGGGGGG pb (To be removed ?) */
+ BUG_ON(1); /* FIXME: We have BIGGGGGG issue */
goto out;
}
spin_unlock_irqrestore(&ch_ctx->lock, flags);
/* Credits available ==> Notify the channel */
- if (ch_ctx->credits_available_cb) {
+ if (ch_ctx->credits_available_cb)
ch_ctx->credits_available_cb(ch_ctx);
- }
PTRACE_NO_FUNC("New CREDITS value: %d (+%d)\n", ret,
params.data3);
/* Check the requested PDU size */
if (ch_ctx->pdu_size != ret) {
- CRITICAL
- ("Unexpected PDU size: %d => Expected: %d (ch: %d)",
- ret, ch_ctx->pdu_size,
- ch_ctx->hsi_channel);
+ CRITICAL("Unexpected PDU size: %d => "
+ "Expected: %d (ch: %d)",
+ ret,
+ ch_ctx->pdu_size,
+ ch_ctx->hsi_channel);
response = DLP_CMD_NACK;
}
/* Set the modem state */
dlp_ctrl_set_modem_readiness(1);
}
- }
- while (ret);
+ } while (ret);
EPILOG();
}
*
* @param ch_ctx: IPC CTRL channel
*/
-static void dlp_ctrl_modem_power(struct dlp_channel *ch_ctx)
+void dlp_ctrl_modem_power(struct dlp_channel *ch_ctx)
{
struct dlp_channel *ctrl_ch = dlp_drv.channels[DLP_CHANNEL_CTRL];
struct dlp_ctrl_context *ctrl_ctx = ctrl_ch->ch_data;
}
/* Create a workqueue to check the modem readiness */
- ctrl_ctx->readiness_wq = create_singlethread_workqueue(DRVNAME "-mdmreadiness");
+ ctrl_ctx->readiness_wq = create_singlethread_workqueue(
+ DRVNAME "-mdmreadiness");
if (!ctrl_ctx->readiness_wq) {
CRITICAL("Unable to create modem readiness workqueue");
goto free_ctx;
ctrl_ctx->gpio_fcdp_rb = pd->gpio_fcdp_rb;
ret = dlp_ctrl_setup_irq_gpio(ch_ctx, dev);
- if (ret) {
+ if (ret)
goto free_ctx;
- }
/* Set ch_ctx, not yet done in the probe */
dlp_drv.channels[DLP_CHANNEL_CTRL] = ch_ctx;
*/
static int do_modem_reset(const char *val, struct kernel_param *kp)
{
- unsigned long reset_request;
+ long do_reset;
PROLOG();
- if (strict_strtoul(val, 16, &reset_request) < 0) {
+ if (strict_strtol(val, 16, &do_reset) < 0) {
EPILOG();
return -EINVAL;
}
- if (reset_request) {
- struct dlp_channel *ch_ctx = dlp_drv.channels[DLP_CHANNEL_CTRL];
-
- dlp_ctrl_modem_reset(ch_ctx);
- }
+ if (do_reset)
+ dlp_ctrl_modem_reset(dlp_drv.channels[DLP_CHANNEL_CTRL]);
EPILOG();
return 0;
}
/*
+* @brief Modem reset module_param set function
+* - This function is performing a modem reset
+*
+* @param val
+* @param kp
+*
+* @return 0
+*/
+static int do_modem_power(const char *val, struct kernel_param *kp)
+{
+ long do_power;
+
+ PROLOG();
+
+ if (strict_strtol(val, 16, &do_power) < 0) {
+ EPILOG();
+ return -EINVAL;
+ }
+
+ if (do_power)
+ dlp_ctrl_modem_power(dlp_drv.channels[DLP_CHANNEL_CTRL]);
+
+ EPILOG();
+ return 0;
+}
+
+/*
* Modem cold reset sysfs entries
*
* To do a modem cold reset we have to:
*/
static int clear_hangup_reasons(const char *val, struct kernel_param *kp)
{
- unsigned long reasons_to_clear;
+ long reasons_to_clear;
PROLOG();
- if (strict_strtoul(val, 16, &reasons_to_clear) < 0)
+ if (strict_strtol(val, 16, &reasons_to_clear) < 0)
return -EINVAL;
if (reasons_to_clear) {
module_param_call(cold_reset_modem, dp_modem_cold_reset, NULL, NULL, 0644);
module_param_call(reset_modem, do_modem_reset, get_modem_reset, NULL, 0644);
+module_param_call(power_modem, do_modem_power, NULL, NULL, 0644);
module_param_call(hangup_reasons, clear_hangup_reasons, get_hangup_reasons,
NULL, 0644);
int i;
if (should_dump)
- printk("-----\n");
+ printk(KERN_DEBUG "-----\n");
else if (is_rx)
PRINT_RX("-----\n");
else
for (i = 0; i < nb_bytes; i++) {
if (should_dump)
- printk("%02X ", data[i]);
+ printk(KERN_DEBUG "%02X ", data[i]);
else if (is_rx)
PRINT_RX("%02X ", data[i]);
else
/* Previous line was less than "bytes_per_line" */
if ((i & (bytes_per_line - 1)) != 0) {
if (should_dump)
- printk("\n");
+ printk(KERN_DEBUG "\n");
else if (is_rx)
PRINT_RX("\n");
else
}
if (should_dump)
- printk("-----\n");
+ printk(KERN_DEBUG "-----\n");
else if (is_rx)
PRINT_RX("-----\n");
else
int i;
if (should_dump)
- printk("-----\n");
+ printk(KERN_DEBUG "-----\n");
else if (is_rx)
PRINT_RX("-----\n");
else
for (i = 0; i < nb_words; i++) {
if (should_dump)
- printk("%08X ", data[i]);
+ printk(KERN_DEBUG "%08X ", data[i]);
else if (is_rx)
PRINT_RX("%08X ", data[i]);
else
/* Previous line was less than "words_per_line" */
if ((i & (words_per_line - 1)) != 0) {
if (should_dump)
- printk("\n");
+ printk(KERN_DEBUG "\n");
else if (is_rx)
PRINT_RX("\n");
else
}
if (should_dump)
- printk("-----\n");
+ printk(KERN_DEBUG "-----\n");
else if (is_rx)
PRINT_RX("-----\n");
else
is_rx = (pdu->ttype == HSI_MSG_READ);
if (is_rx)
- PRINT_RX("RX PDU (0x%p): \n", pdu);
+ PRINT_RX("RX PDU (0x%p):\n", pdu);
else
- PRINT_TX("TX PDU (0x%p): \n", pdu);
+ PRINT_TX("TX PDU (0x%p):\n", pdu);
/* Check the PDU signature */
data = sg_virt(sg);
- if (!dlp_pdu_header_valid(pdu)) {
+ if (!dlp_pdu_header_valid(pdu))
is_corrupted = 1;
- }
for (i = 0; i < pdu->sgt.nents; i++) {
data = sg_virt(sg);
if (dumped >= items_to_dump) {
break;
} else {
- if ((len + dumped) > items_to_dump) {
+ if ((len + dumped) > items_to_dump)
len = items_to_dump - dumped;
- }
}
} else {
len = items_to_dump - dumped;
--- /dev/null
+/*
+ * dlp_flash.c
+ *
+ * Intel Mobile Communication protocol driver for modem boot/flashing:
+ *
+ * Copyright (C) 2012 Intel Corporation. All rights reserved.
+ *
+ * Contact: Faouaz Tenoutit <faouazx.tenoutit@intel.com>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/hsi/hsi.h>
+#include <linux/uaccess.h>
+
+#include "dlp_main.h"
+
+
+#define DEBUG_TAG 0x8
+#define DEBUG_VAR dlp_drv.debug
+
+
+#define FLASHING_DEVNAME "tty"CONFIG_HSI_FLASHING_DEV_NAME
+
+/* Number of RX msg to push
+ * if the controller FIFO
+ */
+#define DLP_FLASH_NB_RX_MSG 10
+
+
+/*
+ * struct flashing_driver - HSI Modem flashing driver protocol
+ *
+ * @lock: spinlock to serialise access to the driver information
+ * @major: char device major number
+ * @tdev: char device type dev
+ * @dev: char device
+ * @cdev: char device
+ * @class: char device class
+ * @opened: This flasg is used to allow only ONE instance of this driver
+ * @read_wq: Read/Poll/Select wait event
+ * @rx_msgs: RX messages queue
+ * @ch_ctx: Flashing Channel context
+ * @reset_done: Flag used to reset the modem when sending the first TX data
+*/
+struct dlp_flash_ctx {
+ /* Char device registration */
+ int major;
+ dev_t tdev;
+ struct device *dev;
+ struct cdev cdev;
+ struct class *class;
+
+ /* Used to prevent multiple access to device */
+ unsigned int opened;
+
+ /* A waitqueue for poll/read operations */
+ wait_queue_head_t read_wq;
+
+ /* RX msg queue */
+ struct list_head rx_msgs;
+
+ struct dlp_channel *ch_ctx;
+ int reset_done;
+};
+
+
+/*
+ *
+ */
+static void dlp_flash_complete_rx(struct hsi_msg *msg);
+
+
+/*
+* @brief
+*
+* @param flash_ctx
+* @param value
+*/
+static inline void dlp_flash_set_opened(struct dlp_channel *ch_ctx,
+ int value)
+{
+ unsigned long flags;
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+
+ /* Set the open flag */
+ flash_ctx->opened = value;
+
+ /* Set the reset_done flag */
+ flash_ctx->reset_done = !value;
+
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+}
+
+/*
+* @brief
+*
+* @param flash_ctx
+* @param value
+*/
+static inline int dlp_flash_get_opened(struct dlp_channel *ch_ctx)
+{
+ int opened;
+ unsigned long flags;
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+
+ /* Set the open flag */
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ opened = flash_ctx->opened;
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+
+ return opened;
+}
+
+/*
+* @brief
+*
+* @param msg
+*/
+static inline void dlp_flash_free_msg(struct hsi_msg *msg)
+{
+ /* Delete the received msg */
+ dlp_pdu_free(msg, msg->sgt.sgl->length);
+}
+
+/*
+* @brief
+*
+* @param msg
+*/
+static void dlp_flash_msg_destruct(struct hsi_msg *msg)
+{
+ PROLOG("msg:0x%p", msg);
+
+ /* Delete the received msg */
+ dlp_flash_free_msg(msg);
+
+ EPILOG();
+}
+
+/*
+ * Push RX pdu on channel 0
+ *
+ */
+static int dlp_flash_push_rx_pdu(struct dlp_channel *ch_ctx)
+{
+ int ret;
+ struct hsi_msg *rx_msg;
+
+ PROLOG();
+
+ /* Allocate a new RX msg */
+ rx_msg = dlp_pdu_alloc(ch_ctx->hsi_channel,
+ HSI_MSG_READ,
+ DLP_FLASH_PDU_SIZE,
+ 1,
+ ch_ctx,
+ dlp_flash_complete_rx, dlp_flash_msg_destruct);
+
+ if (!rx_msg) {
+ CRITICAL("dlp_pdu_alloc(RX) failed");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Send the RX HSI msg */
+ ret = hsi_async(rx_msg->cl, rx_msg);
+ if (ret) {
+ CRITICAL("hsi_async() failed, ret:%d", ret);
+ ret = -EIO;
+ goto free_msg;
+ }
+
+ EPILOG();
+ return 0;
+
+free_msg:
+ /* Free the msg */
+ dlp_pdu_free(rx_msg, DLP_CTRL_PDU_SIZE);
+
+out:
+ EPILOG();
+ return ret;
+}
+
+/*
+* @brief Insert the provided msg at the begining (head) of the list
+*
+* @param ch_ctx
+* @param msg
+*/
+static void dlp_boot_rx_queue_head(struct dlp_channel *ch_ctx,
+ struct hsi_msg *msg)
+{
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ unsigned long flags;
+
+ /* Add at the begining of the list */
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ list_add(&msg->link, &flash_ctx->rx_msgs);
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+}
+
+/*
+* @brief Insert the provided msg at the end (tail) of the list
+*
+* @param ch_ctx
+* @param msg
+*/
+static void dlp_boot_rx_queue_tail(struct dlp_channel *ch_ctx,
+ struct hsi_msg *msg)
+{
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ unsigned long flags;
+
+ /* Add at the end of the list */
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ list_add_tail(&msg->link, &flash_ctx->rx_msgs);
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+}
+
+/*
+* @brief Remove & return the first list item (return NULL if empty)
+*
+* @param ch_ctx
+* @param msg
+*/
+static struct hsi_msg *dlp_boot_rx_dequeue(struct dlp_channel *ch_ctx)
+{
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ struct hsi_msg *msg = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+
+ if (!list_empty(&flash_ctx->rx_msgs)) {
+ /* Get the fist list item (head) */
+ msg = list_entry(flash_ctx->rx_msgs.next, struct hsi_msg, link);
+
+ /* Remove the item from the list */
+ list_del_init(&msg->link);
+ }
+
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+ return msg;
+}
+
+
+/*
+* @brief
+*
+* @param msg
+*/
+static void dlp_flash_complete_tx(struct hsi_msg *msg)
+{
+ PROLOG("msg:0x%p", msg);
+
+ /* Delete the received msg */
+ dlp_flash_free_msg(msg);
+
+ EPILOG();
+}
+
+/*
+* @brief
+*
+* @param msg
+*/
+static void dlp_flash_complete_rx(struct hsi_msg *msg)
+{
+ struct dlp_channel *ch_ctx = msg->context;
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ int ret;
+
+ PROLOG("msg:0x%p, actual_len:%d, pdu_len:%d", msg,
+ msg->actual_len, msg->sgt.sgl->length);
+
+ if (msg->status != HSI_STATUS_COMPLETED) {
+ CRITICAL("Invalid msg status: %d (ignored)", msg->status);
+
+ /* Push again the RX msg */
+ ret = hsi_async(msg->cl, msg);
+ if (ret) {
+ CRITICAL("hsi_async() failed, ret:%d ==> "
+ "FIFO will be empty", ret);
+
+ /* Delete the received msg */
+ dlp_flash_free_msg(msg);
+ }
+ } else {
+ /* Add the received msg to the RX queue */
+ dlp_boot_rx_queue_tail(ch_ctx, msg);
+
+ /* Wakeup any waiting clients for read/poll */
+ wake_up_interruptible(&flash_ctx->read_wq);
+ }
+
+ EPILOG();
+}
+
+
+/*
+ * Called when a process tries to open the device file
+ */
+static int dlp_flash_dev_open(struct inode *inode, struct file *filp)
+{
+ int ret = 0;
+ struct dlp_channel *ch_ctx = DLP_CHANNEL_CTX(DLP_CHANNEL_FLASH);
+
+ PROLOG("flags:0x%x", filp->f_flags);
+
+ /* Only ONE instance of this device can be opened */
+ if (dlp_flash_get_opened(ch_ctx)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ /* Save private data for futur use */
+ filp->private_data = ch_ctx;
+
+ /* Set the open flag */
+ dlp_flash_set_opened(ch_ctx, 1);
+
+ /* Push RX PDUs */
+ for (ret = DLP_FLASH_NB_RX_MSG; ret; ret--)
+ dlp_flash_push_rx_pdu(ch_ctx);
+
+out:
+ EPILOG();
+ return ret;
+}
+
+/*
+ * Called when a process closes the device file.
+ */
+static int dlp_flash_dev_close(struct inode *inode, struct file *filp)
+{
+ struct dlp_channel *ch_ctx = filp->private_data;
+
+ PROLOG();
+
+ /* Set the open flag */
+ dlp_flash_set_opened(ch_ctx, 0);
+
+ EPILOG();
+ return 0;
+}
+
+/*
+ * Called when a process, which already opened the dev file, attempts to
+ * read from it.
+ */
+static ssize_t dlp_flash_dev_read(struct file *filp,
+ char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ struct dlp_channel *ch_ctx = filp->private_data;
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ struct hsi_msg *msg;
+ int ret, to_copy, copied, available;
+ unsigned long flags;
+
+ PROLOG("count:%d", count);
+
+ /* Have some data to read ? */
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ ret = list_empty(&flash_ctx->rx_msgs);
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+
+ /* List empty ? */
+ if (ret) {
+ /* Descriptor opened in Non-Blocking mode ? */
+ if (filp->f_flags & O_NONBLOCK) {
+ copied = -EWOULDBLOCK;
+ goto out;
+ } else {
+ ret = wait_event_interruptible(flash_ctx->read_wq, 0);
+
+ PDEBUG("Wake up !");
+ if (ret) {
+ copied = -EINTR;
+ goto out;
+ }
+ }
+ }
+
+ copied = 0;
+ available = count;
+
+ /* Parse RX msgs queue */
+ while ((msg = dlp_boot_rx_dequeue(ch_ctx))) {
+ /* Check if we have enough of space in the user buffer */
+ to_copy = MIN(msg->actual_len, available);
+ if ((copied + to_copy) > available) {
+ /* Put the msg back in the RX queue */
+ dlp_boot_rx_queue_head(ch_ctx, msg);
+
+ /* Copy done (The user buffer is full) */
+ break;
+ }
+
+ /* Copy data to the user buffer */
+ ret = copy_to_user(data+copied, sg_virt(msg->sgt.sgl), to_copy);
+ if (ret) {
+ CRITICAL("Uanble to copy data to the user buffer");
+
+ /* Put the msg back in the RX queue */
+ dlp_boot_rx_queue_head(ch_ctx, msg);
+
+ /* Stop copying */
+ break;
+ }
+
+ copied += to_copy;
+ available -= to_copy;
+
+ /* Data read => Queue the RX msg */
+ /* Push again the RX msg */
+ ret = hsi_async(msg->cl, msg);
+ if (ret) {
+ CRITICAL("hsi_async() failed, ret:%d ==> "
+ "FIFO will be empty", ret);
+
+ /* Delete the received msg */
+ dlp_flash_free_msg(msg);
+ }
+ }
+
+ /* Update the position */
+ (*ppos) += copied;
+
+out:
+ EPILOG("bytes_read:%d, pos: %d", copied, (unsigned int)(*ppos));
+ return copied;
+}
+
+/*
+ * Called when a process writes to dev file
+ */
+static ssize_t dlp_flash_dev_write(struct file *filp,
+ const char __user *data,
+ size_t count,
+ loff_t *ppos)
+{
+ int ret;
+ struct dlp_channel *ch_ctx = filp->private_data;
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ struct hsi_msg *tx_msg = NULL;
+
+ PROLOG("count: %d", count);
+
+ /* A modem reset is need only the FIST write */
+ if (!flash_ctx->reset_done) {
+ flash_ctx->reset_done = 1;
+ dlp_ctrl_modem_reset(ch_ctx);
+ }
+
+ /* Allocate a new TX msg */
+ tx_msg = dlp_pdu_alloc(DLP_CHANNEL_CTRL,
+ HSI_MSG_WRITE,
+ count,
+ 1,
+ ch_ctx,
+ dlp_flash_complete_tx, dlp_flash_msg_destruct);
+
+ if (!tx_msg) {
+ CRITICAL("dlp_pdu_alloc(TX, len: %d) failed", count);
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Copy the user data */
+ memcpy(sg_virt(tx_msg->sgt.sgl), data, count);
+
+ /* Send the TX HSI msg */
+ ret = hsi_async(tx_msg->cl, tx_msg);
+ if (ret) {
+ CRITICAL("hsi_async(TX) failed (ret:%d)", ret);
+ ret = -EIO;
+ goto free_tx;
+ }
+
+ ret = count;
+ EPILOG();
+ return ret;
+
+free_tx:
+ dlp_flash_free_msg(tx_msg);
+
+out:
+ EPILOG();
+ return ret;
+}
+
+
+/*
+* @brief
+*
+* @param filp
+* @param wait
+*
+* @return
+*/
+static unsigned int dlp_flash_dev_poll(struct file *filp,
+ struct poll_table_struct *pt)
+{
+ struct dlp_channel *ch_ctx = filp->private_data;
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ unsigned long flags;
+ unsigned int ret = 0;
+
+ PROLOG();
+
+ poll_wait(filp, &flash_ctx->read_wq, pt);
+
+ /* Have some data to read ? */
+ spin_lock_irqsave(&ch_ctx->lock, flags);
+ if (!list_empty(&flash_ctx->rx_msgs))
+ ret = POLLIN | POLLRDNORM;
+ spin_unlock_irqrestore(&ch_ctx->lock, flags);
+
+ EPILOG("0x%d", ret);
+ return ret;
+}
+
+
+/*
+* Device driver file operations
+*/
+static const struct file_operations dlp_flash_ops = {
+ .open = dlp_flash_dev_open,
+ .read = dlp_flash_dev_read,
+ .write = dlp_flash_dev_write,
+ .poll = dlp_flash_dev_poll,
+ .release = dlp_flash_dev_close
+};
+
+/*
+* @brief
+*
+* @param index
+* @param dev
+*
+* @return
+*/
+struct dlp_channel *dlp_flash_ctx_create(unsigned int index, struct device *dev)
+{
+ int ret;
+ struct hsi_client *client = to_hsi_client(dev);
+ struct dlp_channel *ch_ctx;
+ struct dlp_flash_ctx *flash_ctx;
+
+ PROLOG();
+
+ /* Allocate channel struct data */
+ ch_ctx = kzalloc(sizeof(struct dlp_channel), GFP_KERNEL);
+ if (!ch_ctx) {
+ CRITICAL("Unable to allocate memory (ch_ctx)");
+ goto out;
+ }
+
+ /* Allocate the context private data */
+ flash_ctx = kzalloc(sizeof(struct dlp_flash_ctx), GFP_KERNEL);
+ if (!flash_ctx) {
+ CRITICAL("Unable to allocate memory (flash_ctx)");
+ goto free_ch;
+ }
+
+ /* Save params */
+ ch_ctx->ch_data = flash_ctx;
+ ch_ctx->hsi_channel = 0;
+ ch_ctx->pdu_size = DLP_FLASH_PDU_SIZE;
+ ch_ctx->rx.config = client->rx_cfg;
+ ch_ctx->tx.config = client->tx_cfg;
+
+ spin_lock_init(&ch_ctx->lock);
+ init_waitqueue_head(&flash_ctx->read_wq);
+ flash_ctx->ch_ctx = ch_ctx;
+ INIT_LIST_HEAD(&flash_ctx->rx_msgs);
+
+ /* Init the RX/TX contexts */
+ dlp_xfer_ctx_init(ch_ctx, &ch_ctx->tx,
+ 0, 0, 0, NULL, HSI_MSG_WRITE);
+
+ dlp_xfer_ctx_init(ch_ctx, &ch_ctx->rx,
+ 0, 0, 0, NULL, HSI_MSG_READ);
+
+ /* Register the device */
+ ret = alloc_chrdev_region(&flash_ctx->tdev, 0, 1, FLASHING_DEVNAME);
+ if (ret) {
+ CRITICAL("Unable to allocate the device (err: %d)", ret);
+ goto free_ctx;
+ }
+
+ flash_ctx->major = MAJOR(flash_ctx->tdev);
+ cdev_init(&flash_ctx->cdev, &dlp_flash_ops);
+ flash_ctx->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&flash_ctx->cdev, flash_ctx->tdev, 1);
+ if (ret) {
+ CRITICAL("Unable to register the device (err: %d)", ret);
+ goto unreg_reg;
+ }
+
+ flash_ctx->class = class_create(THIS_MODULE, DRVNAME);
+ if (IS_ERR(flash_ctx->class))
+ goto del_cdev;
+
+ flash_ctx->dev = device_create(flash_ctx->class,
+ NULL,
+ flash_ctx->tdev,
+ NULL, FLASHING_DEVNAME"%d", DLP_TTY_DEV_NUM);
+ if (IS_ERR(flash_ctx->dev)) {
+ CRITICAL("Unable to create the device (err: %ld)",
+ PTR_ERR(flash_ctx->dev));
+ goto del_class;
+ }
+
+ EPILOG();
+ return ch_ctx;
+
+del_class:
+ class_destroy(flash_ctx->class);
+
+del_cdev:
+ cdev_del(&flash_ctx->cdev);
+
+unreg_reg:
+ unregister_chrdev_region(flash_ctx->tdev, 1);
+
+free_ctx:
+ kfree(flash_ctx);
+
+free_ch:
+ kfree(ch_ctx);
+
+out:
+ EPILOG("Failed");
+ return NULL;
+}
+
+/*
+* @brief
+*
+* @param ch_ctx
+*
+* @return
+*/
+int dlp_flash_ctx_delete(struct dlp_channel *ch_ctx)
+{
+ struct dlp_flash_ctx *flash_ctx = ch_ctx->ch_data;
+ int ret = 0;
+
+ PROLOG();
+
+ /* Unregister the device */
+ cdev_del(&flash_ctx->cdev);
+ unregister_chrdev_region(flash_ctx->tdev, 1);
+ class_destroy(flash_ctx->class);
+
+ /* Free the BOOT/FLASHING context */
+ kfree(flash_ctx);
+
+ /* Free the ch_ctx */
+ kfree(ch_ctx);
+
+ EPILOG();
+ return ret;
+}
+
+static int dlp_flash_set_flashing_mode(const char *val, struct kernel_param *kp)
+{
+ long value;
+
+ if (strict_strtol(val, 16, &value) < 0)
+ return -EINVAL;
+
+ PROLOG();
+
+ dlp_set_flashing_mode(value);
+
+ EPILOG();
+ return 0;
+}
+
+module_param_call(set_flashing_mode, dlp_flash_set_flashing_mode,
+ NULL, NULL, 0644);
+
void *dlp_buffer_alloc(unsigned int buff_size, dma_addr_t * dma_addr)
{
void *buff;
- int flags = in_interrupt()? GFP_ATOMIC : GFP_KERNEL;
+ int flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
if (dlp_drv.is_dma_capable) {
buff = dma_alloc_coherent(dlp_drv.controller,
{
struct hsi_msg *new;
void *buffer;
- int flags = in_interrupt()? GFP_ATOMIC : GFP_KERNEL;
+ int flags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL;
PROLOG("%d, %s, size:%d",
hsi_channel,
inline unsigned int dlp_pdu_get_length(struct hsi_msg *pdu)
{
u32 *header = (u32 *)(sg_virt(pdu->sgt.sgl));
-
- return (header[2] & 0x3FFFF);
+ return header[2] & 0x3FFFF;
}
if ((!pdu->break_frame) && (pdu->status == HSI_STATUS_COMPLETED)) {
pdu->actual_len = dlp_pdu_get_length(pdu);
} else {
- CRITICAL
- ("Invalid RX pdu (0x%p) status [status: %d, break_frame: %d,"
- " actual_len: %d", pdu, pdu->status, pdu->break_frame,
- pdu->actual_len);
+ CRITICAL("Invalid RX pdu (0x%p)"
+ " status [status: %d,"
+ " break_frame: %d,"
+ " actual_len: %d",
+ pdu,
+ pdu->status,
+ pdu->break_frame,
+ pdu->actual_len);
pdu->actual_len = 1;
}
{
unsigned int used_len = pdu->actual_len + DLP_TTY_HEADER_LENGTH;
- return (pdu->sgt.sgl->length - used_len);
+ return pdu->sgt.sgl->length - used_len;
}
/**
* Returns the current state of the requested TX or RX context.
*/
static inline __must_check
- unsigned int _dlp_ctx_get_state(struct dlp_xfer_ctx *xfer_ctx)
+unsigned int _dlp_ctx_get_state(struct dlp_xfer_ctx *xfer_ctx)
{
- return (xfer_ctx->state & DLP_GLOBAL_STATE_MASK);
+ return xfer_ctx->state & DLP_GLOBAL_STATE_MASK;
}
/**
* This version adds the spinlock guarding
*/
inline __must_check
- unsigned int dlp_ctx_get_state(struct dlp_xfer_ctx *xfer_ctx)
+unsigned int dlp_ctx_get_state(struct dlp_xfer_ctx *xfer_ctx)
{
unsigned int state;
unsigned long flags;
sg = pdu->sgt.sgl;
- // FIXME: To be removed !!!
+ /* FIXME: To be removed !!! */
sg->length = xfer_ctx->payload_len + DLP_TTY_HEADER_LENGTH;
read_unlock_irqrestore(&xfer_ctx->lock, flags);
* Returns a reference to the last pdu of this FIFO or NULL if the FIFO is
* empty.
*/
-inline __must_check
- struct hsi_msg *dlp_fifo_tail(struct dlp_xfer_ctx *xfer_ctx,
- struct list_head *fifo)
+inline __must_check struct hsi_msg *
+dlp_fifo_tail(struct dlp_xfer_ctx *xfer_ctx,
+ struct list_head *fifo)
{
struct hsi_msg *pdu = NULL;
/* Empty ? */
- if (! list_empty(fifo)) {
+ if (!list_empty(fifo))
pdu = list_entry(fifo->prev, struct hsi_msg, link);
- }
return pdu;
}
/* Remove the pdu from the list */
list_del_init(&pdu->link);
- xfer_ctx->wait_len --;
+ xfer_ctx->wait_len--;
xfer_ctx->buffered -= pdu->actual_len;
out:
if (!pdu)
goto out;
- if (check_pdu) {
+ if (check_pdu)
ok = !pdu->break_frame;
- }
if (ok) {
/* Note that no error is returned upon transfer failure,
/* Dump the PDU */
if (pdu->ttype == HSI_MSG_WRITE) {
- dlp_pdu_dump(pdu, 0);
- /* dlp_dbg_dump_pdu(pdu, 16, 160, 0); */
+ /* dlp_pdu_dump(pdu, 0);
+ dlp_dbg_dump_pdu(pdu, 16, 160, 0); */
}
err = hsi_async(pdu->cl, pdu);
*/
static void dlp_hsi_start_rx_cb(struct hsi_client *cl)
{
- // FIXME: only ch_ctx[1]
- struct dlp_channel *ch_ctx = (struct dlp_channel *)hsi_client_drvdata(cl);
+ struct dlp_channel *ch_ctx = hsi_client_drvdata(cl);
struct dlp_xfer_ctx *xfer_ctx = &ch_ctx->rx;
PROLOG();
*/
static void dlp_hsi_stop_rx_cb(struct hsi_client *cl)
{
- // FIXME: only ch_ctx[1]
- struct dlp_channel *ch_ctx = (struct dlp_channel *)hsi_client_drvdata(cl);
+ struct dlp_channel *ch_ctx = hsi_client_drvdata(cl);
struct dlp_xfer_ctx *xfer_ctx = &ch_ctx->rx;
PROLOG();
* @param start_rx_cb : Start RX callback backup function
* @param stop_rx_cb : Stop RX callback backup function
*/
-void dlp_save_rx_callbacks(hsi_client_cb *start_rx_cb, hsi_client_cb *stop_rx_cb)
+void dlp_save_rx_callbacks(hsi_client_cb *start_rx_cb,
+ hsi_client_cb *stop_rx_cb)
{
PROLOG();
* @param start_rx_cb : Start RX callback to set
* @param stop_rx_cb : Stop RX callback to set
*/
-void dlp_restore_rx_callbacks(hsi_client_cb *start_rx_cb, hsi_client_cb *stop_rx_cb)
+void dlp_restore_rx_callbacks(hsi_client_cb *start_rx_cb,
+ hsi_client_cb *stop_rx_cb)
{
PROLOG();
EPILOG();
}
+/*
+* @brief This function will:
+* - Release the HSI client
+* - Update the HSI client configuration (Use IPC/Flashing config)
+* - Claim the HSI client again
+*
+* @param value : ON/OFF to activate/deactivate the flashing mode
+*/
+int dlp_set_flashing_mode(int value)
+{
+ int ret;
+
+ PROLOG();
+
+ /* Release the HSI controller */
+ dlp_hsi_port_unclaim();
+
+ /* Flush everything */
+ hsi_flush(dlp_drv.client);
+
+ if (value) {
+ /* Set the Boot/Flashing configs */
+ dlp_drv.client->tx_cfg = dlp_drv.flash_tx_cfg;
+ dlp_drv.client->rx_cfg = dlp_drv.flash_rx_cfg;
+
+ /* Disable the IPC RX start/stop cb */
+ dlp_save_rx_callbacks(&dlp_drv.start_rx_cb,
+ &dlp_drv.stop_rx_cb);
+ } else {
+ /* Set IPC configs */
+ dlp_drv.client->tx_cfg = dlp_drv.ipc_tx_cfg;
+ dlp_drv.client->rx_cfg = dlp_drv.ipc_rx_cfg;
+
+ /* Restore the IPC RX start/stop cb */
+ dlp_restore_rx_callbacks(&dlp_drv.start_rx_cb,
+ &dlp_drv.stop_rx_cb);
+ }
+
+ /* Claim the HSI port (to use for IPC) */
+ ret = dlp_hsi_port_claim();
+
+ EPILOG();
+ return ret;
+}
+
/**
* dlp_increase_pdus_pool - background work aimed at creating new pdus
* @work: a reference to the work context
while (!new) {
++retry;
if (retry == DLP_PDU_ALLOC_RETRY_MAX_CNT) {
- CRITICAL("Cannot allocate a pdu after %d retries...", retry);
+ CRITICAL("Cannot allocate a pdu after "
+ "%d retries...", retry);
retry = 0;
}
* @param data : Timeout callback user data
*/
void dlp_hangup_ctx_init(struct dlp_channel *ch_ctx,
- void (* work_func)(struct work_struct *work),
- void (* timeout_func)(unsigned long int param),
+ void (*work_func)(struct work_struct *work),
+ void (*timeout_func)(unsigned long int param),
void *data)
{
PROLOG();
write_unlock_irqrestore(&xfer_ctx->lock, flags);
/* No need to wait for the end of the calling work! */
- if (! is_hunging_up) {
+ if (!is_hunging_up) {
if (del_timer_sync(&ch_ctx->hangup.timer))
cancel_work_sync(&ch_ctx->hangup.work);
else
dlp_tty_ctx_delete, /* TTY */
dlp_net_ctx_delete, /* NET */
dlp_net_ctx_delete, /* NET */
- dlp_net_ctx_delete}; /* NET */
+ dlp_net_ctx_delete, /* NET */
+ dlp_flash_ctx_delete}; /* BOOT/FLASHING */
PROLOG();
dlp_tty_ctx_create, /* TTY */
dlp_net_ctx_create, /* NET */
dlp_net_ctx_create, /* NET */
- dlp_net_ctx_create}; /* NET */
+ dlp_net_ctx_create, /* NET */
+ dlp_flash_ctx_create}; /* BOOT/FLASHING */
PROLOG();
spin_lock_init(&dlp_drv.lock);
/* Warn if no DMA capability */
- if (!dlp_drv.is_dma_capable) {
+ if (!dlp_drv.is_dma_capable)
WARNING("HSI device is not DMA capable");
- }
/* Save IPC controller configs */
dlp_drv.ipc_rx_cfg = client->rx_cfg;
dlp_drv.flash_rx_cfg.channels = 1;
dlp_drv.flash_tx_cfg.channels = 1;
- /* FIXME: Claim the HSI port */
- ret = dlp_hsi_port_claim();
- if (ret) {
- goto out;
- }
+ /* Start in IPC mode */
+ dlp_set_flashing_mode(0);
/* Create DLP contexts */
for (i = 0; i < DLP_CHANNEL_COUNT; i++) {
dlp_drv.channels[i] = create_funcs[i] (i, dev);
- if (!dlp_drv.channels[i]) {
+ if (!dlp_drv.channels[i])
goto cleanup;
- }
}
/* */
hsi_client_set_drvdata(client, dlp_drv.channels[DLP_CHANNEL_TTY]);
/* Create /proc/hsi-dlp */
- // FIXME: Will be removed
- //if (dlp_drv.debug)
+ /* FIXME: Will be removed */
+ /*if (dlp_drv.debug) */
{
proc_create_data(DRVNAME, S_IRUGO, NULL, &dlp_proc_ops, NULL);
}
cleanup:
dlp_driver_cleanup();
-out:
EPILOG("%d", ret);
return ret;
}
MODULE_AUTHOR("Faouaz Tenoutit <faouazx.tenoutit@intel.com>");
MODULE_DESCRIPTION("LTE protocol driver over HSI for IMC modems");
MODULE_LICENSE("GPL");
-MODULE_VERSION("1.1-HSI-LTE");
+MODULE_VERSION("1.2-HSI-LTE");
/* Compute the TX and RX, FIFO depth from the buffering requirements */
/* For optimal performances the DLP_HSI_TX_CTRL_FIFO size shall be set to 2 at
* least to allow back-to-back transfers. */
-#define DLP_TX_MAX_LEN ((DLP_MIN_TX_BUFFERING+DLP_PAYLOAD_LENGTH-1)/DLP_PAYLOAD_LENGTH)
-#define DLP_RX_MAX_LEN ((DLP_MIN_RX_BUFFERING+DLP_PAYLOAD_LENGTH-1)/DLP_PAYLOAD_LENGTH)
+#define DLP_TX_MAX_LEN ((DLP_MIN_TX_BUFFERING+DLP_PAYLOAD_LENGTH-1) \
+ /DLP_PAYLOAD_LENGTH)
+
+#define DLP_RX_MAX_LEN ((DLP_MIN_RX_BUFFERING+DLP_PAYLOAD_LENGTH-1) \
+ /DLP_PAYLOAD_LENGTH)
#define DLP_HSI_TX_CTRL_FIFO 2
#define DLP_HSI_TX_WAIT_FIFO max(DLP_TX_MAX_LEN-DLP_HSI_TX_CTRL_FIFO, 1)
/* PDU size for CTRL channel */
#define DLP_CTRL_PDU_SIZE 4 /* 4 Bytes */
+/* PDU size for Flashing channel */
+#define DLP_FLASH_PDU_SIZE 4 /* 4 Bytes */
+
/* Alignment params */
#define DLP_PACKET_ALIGN_AP 16
#define DLP_PACKET_ALIGN_CP 16
#define DLP_HDR_COMPLETE_PACKET (0x3 << 29)
#ifndef MIN
-#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
+ * Number of /dev/tty exported for IPC
+ * Currently only one is used (/dev/ttyIFX0)
+ */
+#define DLP_TTY_DEV_NUM 1
+
+
+/*
* Get a ref to the given channel context
*/
#define DLP_CHANNEL_CTX(hsi_ch) dlp_drv.channels[hsi_ch]
DLP_CHANNEL_NET2, /* HSI Channel 3 */
DLP_CHANNEL_NET3, /* HSI Channel 4 */
+ DLP_CHANNEL_FLASH, /* HSI Channel 0 */
DLP_CHANNEL_COUNT
};
/*
* HSI transfer complete callback prototype
*/
-typedef void (*xfer_complete_cb) (struct hsi_msg * pdu);
+typedef void (*xfer_complete_cb) (struct hsi_msg *pdu);
/*
* HSI client start/stop RX callback prototype
*/
-typedef void (*hsi_client_cb) (struct hsi_client * cl);
+typedef void (*hsi_client_cb) (struct hsi_client *cl);
/**
* struct dlp_xfer_ctx - TX/RX transfer context
struct dlp_hangup_ctx hangup;
/* Reset & Coredump callbacks */
- void (*modem_coredump_cb) (struct dlp_channel * ch_ctx);
- void (*modem_reset_cb) (struct dlp_channel * ch_ctx);
+ void (*modem_coredump_cb) (struct dlp_channel *ch_ctx);
+ void (*modem_reset_cb) (struct dlp_channel *ch_ctx);
/* Credits callback */
- void (*credits_available_cb) (struct dlp_channel * ch_ctx);
+ void (*credits_available_cb) (struct dlp_channel *ch_ctx);
/* Channel sepecific data */
void *ch_data;
* @ipc_xx_cfg: HSI client configuration (Used for IPC RX)
* @flash_tx_cfg: HSI client configuration (Used for Boot/Flashing TX)
* @flash_rx_cfg: HSI client configuration (Used for Boot/Flashing RX)
+ * @start_rx_cb: HSI client start RX callback
+ * @stop_rx_cb: HSI client stop RX callback
*
* @debug: Debug variable
*/
int modem_ready;
spinlock_t lock;
+ /* RX start/stop callbacks */
+ hsi_client_cb start_rx_cb;
+ hsi_client_cb stop_rx_cb;
+
/* Modem boot/flashing */
struct hsi_config ipc_tx_cfg;
struct hsi_config ipc_rx_cfg;
struct hsi_config flash_tx_cfg;
struct hsi_config flash_rx_cfg;
+ /* Platform data */
+
+
/* Debug variables */
int debug;
};
* Context alloc/free function proptype
*/
typedef struct dlp_channel *(*dlp_context_create) (unsigned int index,
- struct device * dev);
+ struct device *dev);
-typedef int (*dlp_context_delete) (struct dlp_channel * ch_ctx);
+typedef int (*dlp_context_delete) (struct dlp_channel *ch_ctx);
+
+/****************************************************************************
+ *
+ * IPC/FLASH switching
+ *
+ ***************************************************************************/
+int dlp_set_flashing_mode(int on_off);
/****************************************************************************
*
* PDU handling
*
***************************************************************************/
-void *dlp_buffer_alloc(unsigned int buff_size, dma_addr_t * dma_addr);
+void *dlp_buffer_alloc(unsigned int buff_size, dma_addr_t *dma_addr);
void dlp_buffer_free(void *buff, dma_addr_t dma_addr, unsigned int buff_size);
inline unsigned int dlp_pdu_room_in(struct hsi_msg *pdu);
inline __attribute_const__
- unsigned char *dlp_pdu_data_ptr(struct hsi_msg *pdu, unsigned int offset);
+unsigned char *dlp_pdu_data_ptr(struct hsi_msg *pdu, unsigned int offset);
/****************************************************************************
*
*
***************************************************************************/
inline __must_check
- unsigned int dlp_ctx_get_state(struct dlp_xfer_ctx *xfer_ctx);
+unsigned int dlp_ctx_get_state(struct dlp_xfer_ctx *xfer_ctx);
inline void dlp_ctx_set_state(struct dlp_xfer_ctx *xfer_ctx,
unsigned int state);
*
***************************************************************************/
inline __must_check
- struct hsi_msg *dlp_fifo_tail(struct dlp_xfer_ctx *xfer_ctx,
+struct hsi_msg *dlp_fifo_tail(struct dlp_xfer_ctx *xfer_ctx,
struct list_head *fifo);
inline void _dlp_fifo_pdu_push(struct hsi_msg *pdu, struct list_head *fifo);
***************************************************************************/
void dlp_hangup_ctx_init(struct dlp_channel *ch_ctx,
- void (* work_func)(struct work_struct *work),
- void (* timeout_func)(unsigned long int param),
+ void (*work_func)(struct work_struct *work),
+ void (*timeout_func)(unsigned long int param),
void *data);
void dlp_hangup_ctx_deinit(struct dlp_channel *ch_ctx);
void dlp_ctrl_modem_reset(struct dlp_channel *ch_ctx);
+void dlp_ctrl_modem_power(struct dlp_channel *ch_ctx);
+
inline int dlp_ctrl_get_reset_ongoing(void);
inline void dlp_ctrl_set_reset_ongoing(int ongoing);
/****************************************************************************
*
+ * Boot/Flashing channel exported functions
+ *
+ ***************************************************************************/
+struct dlp_channel *dlp_flash_ctx_create(unsigned int index,
+ struct device *dev);
+
+int dlp_flash_ctx_delete(struct dlp_channel *ch_ctx);
+
+/****************************************************************************
+ *
* Global variables
*
***************************************************************************/
dev_kfree_skb(msg_param->skb);
/* Dump the PDU */
- // dlp_pdu_dump(pdu, 1);
+ /* dlp_pdu_dump(pdu, 1); */
/* Update statistics */
net_ctx->ndev->stats.tx_bytes += pdu->actual_len;
data_addr, offset, data_size);
/* Dump the first 160 bytes */
- // dlp_dbg_dump_data_as_byte(data_addr, MIN(data_size, 160), 16, 0);
+ /* dlp_dbg_dump_data_as_byte(data_addr,
+ MIN(data_size, 160), 16, 0); */
/*
* The packet has been retrieved from the transmission
*/
skb = netdev_alloc_skb_ip_align(net_ctx->ndev, data_size);
if (!skb) {
- CRITICAL
- ("No more memory (data_size: %d) - packet dropped\n",
- data_size);
+ CRITICAL("No more memory (data_size: %d)"
+ " - packet dropped\n", data_size);
net_ctx->ndev->stats.rx_dropped++;
goto out;
skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */
/* Dump the first 160 bytes */
- //dlp_dbg_dump_data_as_byte(skb->data, MIN(skb->len, 160), 16, 0);
+ /* dlp_dbg_dump_data_as_byte(skb->data,
+ MIN(skb->len, 160), 16, 0);*/
/* Push received packet up to the IP networking stack */
ret = netif_rx(skb);
net_ctx->ndev->stats.rx_bytes += data_size;
net_ctx->ndev->stats.rx_packets++;
}
- }
- while (more_packets);
+ } while (more_packets);
ret = 0;
PROLOG("%s, hsi_ch:%d", dev->name, ch_ctx->hsi_channel);
/* Check the modem readiness */
- if (! dlp_ctrl_modem_is_ready()) {
+ if (!dlp_ctrl_modem_is_ready()) {
CRITICAL("Unale to open NETWORK IF (Modem NOT ready) !");
ret = -EBUSY;
goto out;
}
- // FIXME: To be removed (done in dlp_net_ctx_create)
ret = dlp_ctrl_open_channel(ch_ctx);
if (ret) {
CRITICAL("dlp_ctrl_open_channel() failed !");
netif_stop_queue(dev);
ret = dlp_ctrl_close_channel(ch_ctx);
- if (ret) {
+ if (ret)
CRITICAL("dlp_ctrl_close_channel() failed !");
- }
-
- /* FIXME : Will flush everything */
- // hsi_flush(dlp_drv.client);
/* RX */
del_timer_sync(&rx_ctx->timer);
}
/* Dump the first 160 bytes */
- // dlp_dbg_dump_data_as_byte(skb->data, MIN(skb->len, 160), 16);
+ /* dlp_dbg_dump_data_as_byte(skb->data, MIN(skb->len, 160), 16); */
if (skb->len < ETH_ZLEN) {
- // WARNING("Padding received packet (too small size: %d)", skb->len);
-
- if (skb_padto(skb, ETH_ZLEN)) {
+ /* WARNING("Padding received packet (size: %d)", skb->len); */
+ if (skb_padto(skb, ETH_ZLEN))
return NETDEV_TX_OK;
- }
}
/* Set msg params */
/* Save the timestamp */
dev->trans_start = jiffies;
- /* Set the IP header */
-#if 0 // FIXME : Check if it is V4/6
- struct iphdr *iph;
-
- iph = ip_hdr(skb);
- iph->check = 0;
-
- {
- unsigned int saddr, daddr;
- char *s, *d;
-
-#define BYTE(b) (((int)b)&0xff)
-
- saddr = htonl(iph->saddr);
- daddr = htonl(iph->daddr);
-
- s = (char *)&saddr;
- d = (char *)&daddr;
-
- PDEBUG("\n\tver: 0x%x, ihl: 0x%x, tos: 0x%x, tot_len: %d\n"
- "\tid : 0x%x, frag_off: 0x%x\n"
- "\tttl: 0x%x, protocol: 0x%x, check: 0x%x\n"
- "\tsaddr: [%u.%u.%u.%u]\n"
- "\tdaddr: [%u.%u.%u.%u]\n",
- iph->version, iph->ihl, iph->tos, ntohs(iph->tot_len),
- ntohs(iph->id), ntohs(iph->frag_off),
- iph->ttl, iph->protocol, iph->check,
- BYTE(s[3]), BYTE(s[2]), BYTE(s[1]), BYTE(s[0]),
- BYTE(d[3]), BYTE(d[2]), BYTE(d[1]), BYTE(d[0]));
- }
-
- iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
-#endif
-
/* Compute the number of needed padding entries */
nb_padding = 1;
if (skb_has_frag_list(skb)) {
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+ align_size = IS_ALIGNED(skb_shinfo(skb)->frags[i].size,
+ DLP_PACKET_ALIGN_CP);
/* Size aligned ? */
- nb_padding += (IS_ALIGNED(skb_shinfo(skb)->frags[i].size,DLP_PACKET_ALIGN_CP) ? 0 : 1);
+ nb_padding += (align_size ? 0 : 1);
}
}
/* Set the size */
ptr++;
- (*ptr) = (DLP_HDR_NO_MORE_DESC | DLP_HDR_COMPLETE_PACKET | skb_len);
+ (*ptr) = (DLP_HDR_NO_MORE_DESC|DLP_HDR_COMPLETE_PACKET|skb_len);
(*ptr) += DLP_HDR_SPACE_CP;
/* Set the packet SG entry */
/* Update the offset value */
offset += skb_len + DLP_HDR_SPACE_CP;
}
- }
- while (i < nb_packets);
+ } while (i < nb_packets);
/* Write the padding entry (Check 4 bytes alignment) */
/*---------------------------------------------------*/
PRINT_TX("TX: Entry %d (Padding): offset: 0x%x, size:0x%x\n", i, offset,
padding_len);
-#if 0
- /* */
- u32 first_len, first_mapping;
- int frag, first_entry = entry;
-
- BUG_ON(1);
-
- /* give this initial chunk to the */
- first_len = skb_headlen(skb);
- first_mapping = dma_map_single(hp->dma_dev, skb->data, first_len,
- DMA_TO_DEVICE);
- entry = NEXT_TX(entry);
-
- for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) {
- skb_frag_t *this_frag = &skb_shinfo(skb)->frags[frag];
- u32 len, mapping, this_txflags;
-
- len = this_frag->size;
- mapping = dma_map_page(hp->dma_dev, this_frag->page,
- this_frag->page_offset, len,
- DMA_TO_DEVICE);
- // Process
-
- entry = NEXT_TX(entry);
- }
-#endif
-
ret = dlp_hsi_controller_push(&ch_ctx->tx, new);
if (ret) {
ret = NETDEV_TX_BUSY;
dev->watchdog_timeo = DLP_NET_TX_DELAY;
/* fill in the other fields */
-// dev->features = NETIF_F_SG | NETIF_F_NO_CSUM; // FIXME: wget is KO
+ /* dev->features = NETIF_F_SG | NETIF_F_NO_CSUM; FIXME: wget is KO */
dev->type = ARPHRD_NONE;
- dev->mtu = DLP_NET_PDU_SIZE; // FIXME: check wget crash
+ dev->mtu = DLP_NET_PDU_SIZE; /* FIXME: check wget crash */
dev->tx_queue_len = 10;
dev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
DLP_HSI_RX_WAIT_FIFO, DLP_HSI_RX_CTRL_FIFO,
dlp_net_complete_rx, HSI_MSG_READ);
- /* Open the HSI channel */
-#if 0 /* FIXME : to be activated when the modem boot time is optimized */
- ret = dlp_ctrl_open_channel(ch_ctx);
- if (ret) {
- CRITICAL("dlp_ctrl_open_channel() failed !");
- goto free_dev;
- }
-#endif
-
/* Allocate RX FIFOs in background */
queue_work(dlp_drv.recycle_wq, &ch_ctx->rx.increase_pool);
dlp_xfer_ctx_clear(&ch_ctx->rx);
dlp_xfer_ctx_clear(&ch_ctx->tx);
-#if 0 /* FIXME : Not supported by the modem */
- /* Release the HSI channel */
- ret = dlp_ctrl_send_cancel_conn_cmd(ch_ctx);
- if (ret) {
- CRITICAL("dlp_ctrl_send_cancel_conn_cmd() failed !");
- }
-#endif
-
/* Free the padding buffer */
dlp_buffer_free(net_ctx->net_padd,
net_ctx->net_padd_dma, DLP_NET_PDU_SIZE);
/**
* dlp_tty_pdu_data_ptr - helper function for getting the actual virtual address of
- * a pdu data, taking into account the header offset
+ * a pdu data, taking into account the header offset
+ *
* @pdu: a reference to the considered pdu
* @offset: an offset to add to the current virtual address of the pdu data
*
* Returns the virtual base address of the actual pdu data
*/
inline __attribute_const__
- unsigned char *dlp_tty_pdu_data_ptr(struct hsi_msg *pdu, unsigned int offset)
+unsigned char *dlp_tty_pdu_data_ptr(struct hsi_msg *pdu, unsigned int offset)
{
u32 *addr = sg_virt(pdu->sgt.sgl);
/* Skip the Signature (+2) & Start address (+2) */
addr += 4;
-
- return (((unsigned char *)addr) + offset);
+ return ((unsigned char *)addr) + offset;
}
/**
* - Pushing as much data as possible to the TTY interface
* - Recycling pdus that have been fully forwarded
* - Kicking a TTY insert
- * - Restart delayed job if some data is remaining in the waiting FIFO or if the
- * controller FIFO is not full yet.
+ * - Restart delayed job if some data is remaining in the waiting FIFO
+ * or if the controller FIFO is not full yet.
*/
static void _dlp_forward_tty(struct tty_struct *tty,
struct dlp_xfer_ctx *xfer_ctx)
no_more_tty_insert:
if (do_push) {
- /* Schedule a flip since called from complete_rx() in an interrupt
- * context instead of tty_flip_buffer_push() */
+ /* Schedule a flip since called from complete_rx()
+ * in an interrupt context instead of
+ * tty_flip_buffer_push() */
tty_schedule_flip(tty);
}
dlp_ctrl_set_reset_ongoing(0);
/* Dump the PDU */
- // dlp_pdu_dump(pdu, 1);
+ /* dlp_pdu_dump(pdu, 1); */
/* Recycle or Free the pdu */
write_lock_irqsave(&xfer_ctx->lock, flags);
dlp_ctx_update_status(rx_ctx);
/* Configure the DLP channel */
- // FIXME: To be removed (should be done in dlp_tty_ctx_create)
if (tty->count == 1) {
ret = dlp_ctrl_open_channel(ch_ctx);
if (ret) {
del_timer_sync(&ch_ctx->hangup.timer);
- // FIXME: FTE
- // hsi_flush(dlp_drv.client);
-
/* RX */
del_timer(&rx_ctx->timer);
dlp_tty_rx_fifo_wait_recycle(rx_ctx);
/* Close the HSI channel */
ret = dlp_ctrl_close_channel(ch_ctx);
- if (ret) {
+ if (ret)
CRITICAL("dlp_ctrl_close_channel() failed !");
- }
PDEBUG("tty port shut down");
EPILOG();
PROLOG();
/* Check the modem readiness */
- if (! dlp_ctrl_modem_is_ready()) {
+ if (!dlp_ctrl_modem_is_ready()) {
CRITICAL("Unale to open TTY (Modem NOT ready) !");
ret = -EBUSY;
goto out;
}
}
- if (!pdu) {
+ if (!pdu)
goto out;
- }
pdu->status = HSI_STATUS_PENDING;
/* Do a start TX on new frames only and after having marked
* the current frame as pending, e.g. don't touch ! */
- if (offset == 0) {
+ if (offset == 0)
dlp_hsi_start_tx(xfer_ctx);
- }
- else {
+ else
dlp_ctx_set_flag(xfer_ctx, TX_TTY_WRITE_PENDING_BIT);
- }
copied = min(avail, len);
updated_actual_len = pdu->actual_len + copied;
#endif
case HSI_DLP_MODEM_RESET:
- WARNING("Modem reset requested");
dlp_ctrl_modem_reset(ch_ctx);
break;
return ret;
break;
+ case HSI_DLP_SET_FLASHING_MODE:
+ ret = dlp_set_flashing_mode(arg);
+ break;
+
default:
return -ENOIOCTLCMD;
}
new_drv->driver_name = DRVNAME;
new_drv->name = IPC_TTYNAME;
new_drv->minor_start = 0;
- new_drv->num = 1;
+ new_drv->num = DLP_TTY_DEV_NUM;
new_drv->type = TTY_DRIVER_TYPE_SERIAL;
new_drv->subtype = SERIAL_TYPE_NORMAL;
new_drv->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
*/
#define HSI_DLP_NET_RESET_TX_STATS _IOW(HSI_DLP_MAGIC, 41, unsigned int)
-
+/*
+ * HSI_DLP_SET_FLASHING_MODE - Activate/Deactivate the flashing mode
+ */
+#define HSI_DLP_SET_FLASHING_MODE _IOW(HSI_DLP_MAGIC, 42, unsigned int)
#endif /* _HSI_DLP_H */