Ejemplo n.º 1
0
/*
* @brief Called to destruct a fixed msg size (DLP_FLASH_PDU_SIZE)
*
* @param msg
*/
static inline void dlp_flash_msg_destruct_fix(struct hsi_msg *msg)
{
	PROLOG("msg:0x%p", msg);

	/* Delete the received msg */
	dlp_pdu_free(msg, msg->channel);

	EPILOG();
}
Ejemplo n.º 2
0
/**
 * Asynchronous TX message callback
 *
 * @msg: a reference to the HSI msg
 *
 * This function:
 *		- delete the hsi message
 */
static void dlp_ctrl_complete_tx_async(struct hsi_msg *msg)
{
	struct dlp_command *dlp_cmd = msg->context;

	/* Delete the received msg */
	dlp_pdu_free(msg, msg->channel);

	/* Delete the command */
	kfree(dlp_cmd);
}
Ejemplo n.º 3
0
/*
* @brief Called to destruct a variable msg size (_write function)
*
* @param msg
*/
static inline void dlp_flash_msg_destruct_var(struct hsi_msg *msg)
{
	PROLOG("msg:0x%p", msg);

	/* Delete the received msg
	 * (size is variable, so dont consider the default PDU size) */
	dlp_pdu_free(msg, -1);

	EPILOG();
}
Ejemplo n.º 4
0
/**
 * Synchronous TX message callback
 *
 * @msg: a reference to the HSI msg
 *
 * This function:
 *		- Set the xfer status
 *		- wakeup the caller
 *		- delete the hsi message
 */
static void dlp_ctrl_complete_tx(struct hsi_msg *msg)
{
	struct dlp_command *dlp_cmd = msg->context;
	struct dlp_channel *ch_ctx = dlp_cmd->channel;

	dlp_cmd->status = (msg->status == HSI_STATUS_COMPLETED) ? 0 : -EIO;

	/* Command done, notify the sender */
	complete(&ch_ctx->tx.cmd_xfer_done);

	/* Delete the received msg */
	dlp_pdu_free(msg, msg->channel);
}
Ejemplo n.º 5
0
/**
 * Push RX pdu for any modem command
 *
 */
static int dlp_ctrl_push_rx_pdu(struct dlp_channel *ch_ctx)
{
	int ret;
	struct hsi_msg *rx_msg;
	struct dlp_command *dlp_cmd;

	/* Allocate the DLP command */
	dlp_cmd = dlp_ctrl_cmd_alloc(ch_ctx, DLP_CMD_NOP, 0, 0, 0);
	if (!dlp_cmd) {
		pr_err(DRVNAME ": Out of memory (rx_pdu)\n");
		ret = -ENOMEM;
		goto out;
	}

	/* Allocate a new RX msg */
	rx_msg = dlp_pdu_alloc(DLP_CHANNEL_CTRL,
			       HSI_MSG_READ,
			       DLP_CTRL_RX_PDU_SIZE,
			       1,
			       dlp_cmd,
			       dlp_ctrl_complete_rx, dlp_ctrl_msg_destruct);

	if (!rx_msg) {
		pr_err(DRVNAME ": dlp_pdu_alloc() failed\n");
		ret = -ENOMEM;
		goto free_cmd;
	}

	/* Send the RX HSI msg */
	ret = hsi_async(rx_msg->cl, rx_msg);
	if (ret) {
		pr_err(DRVNAME ": RX push failed, ret:%d\n", ret);
		ret = -EIO;
		goto free_msg;
	}

	return 0;

free_msg:
	/* Free the msg */
	dlp_pdu_free(rx_msg, rx_msg->channel);

free_cmd:
	/* Delete the command */
	kfree(dlp_cmd);

out:
	return ret;
}
Ejemplo n.º 6
0
/*
 * 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_RX_PDU_SIZE,
				1,
				ch_ctx,
				dlp_flash_complete_rx,
				dlp_flash_msg_destruct_fix);

	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, rx_msg->channel);

out:
	EPILOG();
	return ret;
}
/*
 * Push RX pdu on channel 0
 *
 */
static int dlp_trace_push_rx_pdu(struct dlp_channel *ch_ctx)
{
	int ret;
	struct hsi_msg *rx_msg;

	/* Allocate a new RX msg */
	rx_msg = dlp_pdu_alloc(ch_ctx->hsi_channel,
				HSI_MSG_READ,
				DLP_TRACE_RX_PDU_SIZE,
				1,
				ch_ctx,
				dlp_trace_complete_rx,
				dlp_trace_msg_destruct);

	if (!rx_msg) {
		pr_err(DRVNAME": dlp_pdu_alloc(RX) failed\n");
		ret = -ENOMEM;
		goto out;
	}

	/* Send the RX HSI msg */
	ret = hsi_async(rx_msg->cl, rx_msg);
	if (ret) {
		pr_err(DRVNAME": hsi_async() failed, ret:%d\n", ret);
		ret = -EIO;
		goto free_msg;
	}

	return 0;

free_msg:
	/* Free the msg */
	dlp_pdu_free(rx_msg, rx_msg->channel);

out:
	return ret;
}
Ejemplo n.º 8
0
/**
 * Send an HSI msg AND wait for the status
 *
 * @ch_ctx: a reference to the channel context to use
 * @id: the DLP command id
 * @response_id: the expected response id
 * @interm_state: the intermidiate state (to be set when TX is OK)
 * @final_state: the final state (to be set when RX (response) is OK)
 * @param1: the DLP command params
 * @param2: the DLP command params
 * @param3: the DLP command params
 *
 * This function blocks the caller until a response is received
 * or the timeout expires
 */
static int dlp_ctrl_cmd_send(struct dlp_channel *ch_ctx,
				unsigned char id,
				unsigned char response_id,
				unsigned char interm_state,
				unsigned char final_state,
				unsigned char param1,
				unsigned char param2, unsigned char param3)
{
	int ret = 0, old_state;
	struct dlp_ctrl_context *ctrl_ctx;
	struct dlp_command *dlp_cmd;
	struct dlp_command_params expected_resp;
	struct hsi_msg *tx_msg = NULL;

	/* Check the link readiness (TTY still opened) */
	if (!dlp_tty_is_link_valid()) {
		if (EDLP_CTRL_TX_DATA_REPORT)
			pr_debug(DRVNAME ": CH%d (HSI CH%d) cmd 0x%X ignored (close:%d, Time out: %d)\n",
					ch_ctx->ch_id, ch_ctx->hsi_channel,
					id, dlp_drv.tty_closed,
					dlp_drv.tx_timeout);

		return ret;
	}

	ctrl_ctx = DLP_CTRL_CTX;

	mutex_lock(&ch_ctx->tx.cmd_sync);

	/* Save the current channel state */
	old_state = dlp_ctrl_get_channel_state(ch_ctx->hsi_channel);

	/* Backup RX callback */
	dlp_save_rx_callbacks(&ctrl_ctx->ehandler);

	/* Allocate the DLP command */
	dlp_cmd = dlp_ctrl_cmd_alloc(ch_ctx, id, param1, param2, param3);
	if (!dlp_cmd) {
		pr_err(DRVNAME ": Out of memory (dlp_cmd: 0x%X)\n", id);
		ret = -ENOMEM;
		goto out;
	}

	/* Allocate a new TX msg */
	tx_msg = dlp_pdu_alloc(DLP_CHANNEL_CTRL,
			       HSI_MSG_WRITE,
			       DLP_CTRL_TX_PDU_SIZE,
			       1,
			       dlp_cmd,
			       dlp_ctrl_complete_tx, dlp_ctrl_msg_destruct);

	if (!tx_msg) {
		pr_err(DRVNAME ": dlp_pdu_alloc(TX) failed\n");
		ret = -ENOMEM;
		goto free_cmd;
	}

	/* Copy the command data */
	memcpy(sg_virt(tx_msg->sgt.sgl),
	       &dlp_cmd->params, sizeof(struct dlp_command_params));

	/* Send the TX HSI msg */
	ret = hsi_async(tx_msg->cl, tx_msg);
	if (ret) {
		pr_err(DRVNAME ": Unable to send 0x%X cmd (ret:%d)\n",
			dlp_cmd->params.id, ret);

		/* Free the TX msg */
		dlp_pdu_free(tx_msg, tx_msg->channel);

		goto free_cmd;
	}

	/* Dump the TX command */
	if (EDLP_CTRL_TX_DATA_REPORT)
		pr_debug(DRVNAME ": CTRL_TX (0x%X)\n",
				*((u32 *)&dlp_cmd->params));

	/* Wait for TX msg to be sent */
	ret = wait_for_completion_timeout(&ch_ctx->tx.cmd_xfer_done,
					  msecs_to_jiffies(DLP_CMD_TX_TIMOUT));
	if (ret == 0) {
		pr_err(DRVNAME ": hsi_ch:%d, cmd:0x%X => TX timeout\n",
			dlp_cmd->params.channel, dlp_cmd->params.id);

		/* No need to call the complete sending call back,
		 * because of failure */
		tx_msg->complete = NULL;
		tx_msg->context = NULL;
		ret = -EIO;
		/* free only the cmd, because
		 * the message is already in the controller fifo.
		 * It will be freed when the controller fifo will be
		 * flushed/freed
		 */
		goto free_cmd;
	}

	/* TX msg sent, check the status */
	if (dlp_cmd->status) {
		pr_err(DRVNAME ": Failed to send cmd:0x%X\n",
				dlp_cmd->params.id);

		ret = -EIO;
		/* free only the command because
		 * the message has been already freed by the complete_tx
		 * callback
		 */
		goto free_cmd;
	}

	/* TX OK */
	/* 1. Set the intermidiate channel state */
	if (interm_state != DLP_CH_STATE_NONE)
		dlp_ctrl_set_channel_state(ch_ctx->hsi_channel, interm_state);

	/* Wait for response ? */
	if (response_id == DLP_CMD_NONE) {
		ret = 0;
		goto no_resp;
	}

	/* 2. Wait for the response */
	ret = wait_for_completion_timeout(&ch_ctx->rx.cmd_xfer_done,
					  msecs_to_jiffies(DLP_CMD_RX_TIMOUT));
	if (ret == 0) {
		pr_err(DRVNAME ": hsi_ch:%d, cmd:0x%X => RX timeout\n",
			dlp_cmd->params.channel, dlp_cmd->params.id);

		ret = -EIO;
		goto free_cmd;
	}

	/* Set the expected response params */
	expected_resp.id = response_id;
	expected_resp.channel = ch_ctx->hsi_channel;

	switch (id) {
	case DLP_CMD_CLOSE_CONN:
		expected_resp.data1 = param3;
		expected_resp.data2 = param2;
		expected_resp.data3 = CMD_ID(param1, id);
		break;

	case DLP_CMD_OPEN_CONN:
	default:
		expected_resp.data1 = param1;
		expected_resp.data2 = param2;
		expected_resp.data3 = CMD_ID(param3, id);
	}

	/* Check the received response params */
	ret = 0;
	if (memcmp(&ctrl_ctx->response.params,
				&expected_resp,
				sizeof(expected_resp))) {
		pr_err(DRVNAME": cmd 0x%X unexpected response 0x%X (expected 0x%X)",
			id,
			(unsigned int)(*(u32 *)&ctrl_ctx->response.params),
			(unsigned int)(*(u32 *)(&expected_resp)));

		tx_msg->context = NULL;
		ret = -EIO;
		goto free_cmd;
	}

no_resp:
	/* Response received & OK => set the new channel state */
	if (final_state != DLP_CH_STATE_NONE)
		dlp_ctrl_set_channel_state(ch_ctx->hsi_channel, final_state);

free_cmd:
	/* Free the DLP command */
	dlp_ctrl_cmd_free(dlp_cmd);

out:
	/* Restore RX callback */
	dlp_restore_rx_callbacks(&ctrl_ctx->ehandler);

	/* Restore the channel state in error case */
	if (ret)
		dlp_ctrl_set_channel_state(ch_ctx->hsi_channel, old_state);

	mutex_unlock(&ch_ctx->tx.cmd_sync);

	return ret;
}
Ejemplo n.º 9
0
static void dlp_ctrl_complete_rx(struct hsi_msg *msg)
{
	struct dlp_channel *ch_ctx;
	struct dlp_ctrl_context *ctrl_ctx;
	struct dlp_command *dlp_cmd = msg->context;
	struct dlp_command_params params, tx_params;
	unsigned long flags;
	int hsi_channel, elp_channel, ret, response, msg_complete, state;

	params.channel = 0; /* To please KlocWork */

	/* Check the link readiness (TTY still opened) */
	if (!dlp_tty_is_link_valid()) {
		if (EDLP_CTRL_RX_DATA_REPORT)
			pr_debug(DRVNAME ": CTRL: CH%d RX PDU ignored (close:%d, Time out: %d)\n",
				params.channel,
				dlp_drv.tty_closed, dlp_drv.tx_timeout);
		/* Delete the received msg */
		dlp_pdu_free(msg, msg->channel);

		/* Delete the command */
		dlp_ctrl_cmd_free(dlp_cmd);
		return;
	}

	/* Copy the reponse */
	memcpy(&params,
			sg_virt(msg->sgt.sgl),
			sizeof(struct dlp_command_params));

	ctrl_ctx = DLP_CTRL_CTX;
	response = -1;
	memcpy(&tx_params, &params, sizeof(struct dlp_command_params));
	msg_complete = (msg->status == HSI_STATUS_COMPLETED);

	/* Dump the RX command */
	if (EDLP_CTRL_RX_DATA_REPORT)
		pr_debug(DRVNAME ": CTRL_RX (0x%X)\n", *((u32 *)&params));

	hsi_channel = params.channel;
	if ((hsi_channel < 0) || (hsi_channel >= DLP_CHANNEL_COUNT)) {
		pr_err(DRVNAME ": Invalid HSI channel id (%d)\n", hsi_channel);
		goto push_rx;
	}

	elp_channel = dlp_drv.channels_hsi[hsi_channel].edlp_channel;
	ch_ctx = DLP_CHANNEL_CTX(elp_channel);
	switch (params.id) {
	case DLP_CMD_NOP:
		break;

	case DLP_CMD_CREDITS:
		if (!msg_complete)
			break;

		/* Increase the CREDITS counter */
		spin_lock_irqsave(&ch_ctx->lock, flags);
		ch_ctx->credits += params.data3;
		ret = ch_ctx->credits;
		spin_unlock_irqrestore(&ch_ctx->lock, flags);

		/* Credits available ==> Notify the channel */
		if (ch_ctx->credits_available_cb)
			ch_ctx->credits_available_cb(ch_ctx);

		/* CREDITS => ACK */
		response = DLP_CMD_ACK;

		/* Set the response params */
		tx_params.data1 = params.data3;
		tx_params.data2 = 0;
		tx_params.data3 = 0;
		break;

	case DLP_CMD_OPEN_CONN:
		if (!msg_complete)
			break;

		/* Ready ? if not, just save the OPEN_CONN request params */
		spin_lock_irqsave(&ctrl_ctx->open_lock, flags);
		state = dlp_ctrl_get_channel_state(hsi_channel);
		if ((state != DLP_CH_STATE_OPENING) && (state != DLP_CH_STATE_OPENED)) {
			struct dlp_hsi_channel *hsi_ch;

			hsi_ch = &dlp_drv.channels_hsi[hsi_channel];
			memcpy(&hsi_ch->open_conn, &params, sizeof(params));
			spin_unlock_irqrestore(&ctrl_ctx->open_lock, flags);

			response = -1;

			pr_debug(DRVNAME ": HSI CH%d OPEN_CONN received (postponed) when state is %d\n",
					params.channel, state);
			goto push_rx;
		} else
			spin_unlock_irqrestore(&ctrl_ctx->open_lock, flags);

		pr_debug(DRVNAME ": HSI CH%d OPEN_CONN received (size: %d)\n",
					params.channel,
					(params.data2 << 8) | params.data1);

		/* Check the PDU size */
		response = dlp_ctrl_check_pdu_size(ch_ctx, &params, &tx_params);
		break;

	case DLP_CMD_CLOSE_CONN:
		/* CLOSE_CONN => ACK */
		response = DLP_CMD_ACK;

		/* Set the response params */
		tx_params.data1 = params.data3;
		tx_params.data2 = 0;
		tx_params.data3 = CMD_ID(params.data3, params.id);
		pr_debug(DRVNAME ": ch%d close_conn received\n",
				params.channel);
		break;

	default:
		/* Save the modem response */
		ctrl_ctx->response.status = (msg_complete ? 0 : -EIO);

		memcpy(&ctrl_ctx->response.params,
		       &params, sizeof(struct dlp_command_params));

		/* Command done, notify the sender */
		complete(&ch_ctx->rx.cmd_xfer_done);
		break;
	}

	/* Any response to send ? */
	if (response == -1)
		goto push_rx;

	dlp_ctrl_send_response(ch_ctx, &tx_params, response);

push_rx:
	/* Push the RX msg again for futur response */
	ret = hsi_async(msg->cl, msg);
	if (ret) {
		pr_err(DRVNAME ": RX push failed, ret:%d\n", ret);

		/* We have A BIG PROBLEM if the RX msg cant be  */
		/* pushed again in the controller ==>           */
		/* No response could be received (FIFO empty)   */

		/* Delete the received msg */
		dlp_pdu_free(msg, msg->channel);

		/* Delete the command */
		dlp_ctrl_cmd_free(dlp_cmd);
	}
}
Ejemplo n.º 10
0
static int dlp_ctrl_send_response(struct dlp_channel *ch_ctx,
					struct dlp_command_params *tx_params,
					int response)
{
	int ret, state;
	struct hsi_msg *tx_msg;
	struct dlp_command *dlp_cmd;
	unsigned long flags;

	/* Allocate the eDLP response */
	dlp_cmd = dlp_ctrl_cmd_alloc(ch_ctx,
				     response,
				     tx_params->data1,
				     tx_params->data2, tx_params->data3);
	if (!dlp_cmd) {
		pr_err(DRVNAME ": Out of memory (dlp_cmd: 0x%X)\n", response);
		return -ENOMEM;
	}

	/* Allocate a new TX msg */
	tx_msg = dlp_pdu_alloc(DLP_CHANNEL_CTRL,
			       HSI_MSG_WRITE,
			       DLP_CTRL_TX_PDU_SIZE,
			       1,
			       dlp_cmd,
			       dlp_ctrl_complete_tx_async,
			       dlp_ctrl_msg_destruct);

	if (!tx_msg) {
		pr_err(DRVNAME ": TX alloc failed\n");

		/* Delete the command */
		kfree(dlp_cmd);
		return -ENOMEM;
	}

	/* Copy the command data */
	memcpy(sg_virt(tx_msg->sgt.sgl),
	       &dlp_cmd->params, sizeof(struct dlp_command_params));

	spin_lock_irqsave(&ch_ctx->lock, flags);
	state = dlp_ctrl_get_channel_state(ch_ctx->ch_id);
	if ((state != DLP_CH_STATE_OPENING) && (state != DLP_CH_STATE_OPENED) &&
			(ch_ctx->ch_id != DLP_CHANNEL_TRACE))
		spin_unlock_irqrestore(&ch_ctx->lock, flags);

	else {
		spin_unlock_irqrestore(&ch_ctx->lock, flags);
		/* Send the TX HSI msg */
		ret = hsi_async(tx_msg->cl, tx_msg);
		if (ret)
			pr_err(DRVNAME ": TX xfer failed ! (cmd:0x%X, ret:%d)\n",
				dlp_cmd->params.id, ret);

		else {
			/* Dump the TX command */
			if (EDLP_CTRL_TX_DATA_REPORT)
				pr_debug(DRVNAME ": CTRL_TX (0x%X)\n",
						*((u32 *)&dlp_cmd->params));
			return 0;
		}
	}

	/* Free the TX msg */
	dlp_pdu_free(tx_msg, tx_msg->channel);

	/* Delete the command */
	kfree(dlp_cmd);

	return 0;
}
/*
* @brief Called to destroy the allocated msg
*
* @param msg
*/
static inline void dlp_trace_msg_destruct(struct hsi_msg *msg)
{
	/* Delete the received msg */
	dlp_pdu_free(msg, msg->channel);
}