incubator-nuttx/drivers/can/can_sender.c

257 lines
6.9 KiB
C

/****************************************************************************
* drivers/can/can_sender.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/can/can_sender.h>
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: can_xmit_init
*
* Description:
* Initial dev sender.
*
****************************************************************************/
void can_sender_init(FAR struct can_txcache_s *cd_sender)
{
#if defined(CONFIG_CAN_TXPRIORITY) && CONFIG_CAN_TXFIFOSIZE <= 0
# error "CONFIG_CAN_TXFIFOSIZE should be positive non-zero value"
#endif
#ifdef CONFIG_CAN_TXPRIORITY
int i;
list_initialize(&cd_sender->tx_free);
list_initialize(&cd_sender->tx_pending);
list_initialize(&cd_sender->tx_sending);
for (i = 0; i < CONFIG_CAN_TXFIFOSIZE; i++)
{
list_add_tail(&cd_sender->tx_free, &cd_sender->tx_buffer[i].list);
}
#else
cd_sender->tx_head = 0;
cd_sender->tx_queue = 0;
cd_sender->tx_tail = 0;
#endif
}
/****************************************************************************
* Name: can_add_sendnode
*
* Description:
* Add message to sender.
*
****************************************************************************/
void can_add_sendnode(FAR struct can_txcache_s *cd_sender,
FAR struct can_msg_s *msg, int msglen)
{
#ifdef CONFIG_CAN_TXPRIORITY
FAR struct list_node *node;
FAR struct can_msg_node_s *msg_node;
FAR struct can_msg_node_s *tmp_node;
node = list_remove_head(&cd_sender->tx_free);
msg_node = container_of(node, struct can_msg_node_s, list);
memcpy(&msg_node->msg, msg, msglen);
list_for_every_entry(&cd_sender->tx_pending, tmp_node,
struct can_msg_node_s, list)
{
if (tmp_node->msg.cm_hdr.ch_id > msg->cm_hdr.ch_id)
{
/* Prioritize tx frame based on canid */
break;
}
}
if (&tmp_node->list == &cd_sender->tx_pending)
{
/* Inserted at the end of the linked list */
list_add_tail(&cd_sender->tx_pending, &msg_node->list);
}
else
{
list_add_before(&tmp_node->list, &msg_node->list);
}
#else
memcpy(&cd_sender->tx_buffer[cd_sender->tx_tail], msg, msglen);
/* Increment the tail of the circular buffer */
cd_sender->tx_tail++;
if (cd_sender->tx_tail >= CONFIG_CAN_TXFIFOSIZE)
{
cd_sender->tx_tail = 0;
}
#endif
}
/****************************************************************************
* Name: can_get_msg
*
* Description:
* Get send message from sender.
*
****************************************************************************/
FAR struct can_msg_s *can_get_msg(FAR struct can_txcache_s *cd_sender)
{
FAR struct can_msg_s *msg = NULL;
#ifdef CONFIG_CAN_TXPRIORITY
FAR struct can_msg_node_s *msg_node;
FAR struct can_msg_node_s *tmp_node;
if (list_is_empty(&cd_sender->tx_pending))
{
return NULL;
}
msg_node = list_first_entry(&cd_sender->tx_pending,
struct can_msg_node_s, list);
msg = &msg_node->msg;
/* Sort unconfirmed messages in ascending order */
list_for_every_entry(&cd_sender->tx_sending, tmp_node,
struct can_msg_node_s, list)
{
if (tmp_node->msg.cm_hdr.ch_id == msg->cm_hdr.ch_id)
{
/* In order to prevent messages with the same ID from being
* sent out of order, as long as there is a message with the
* same ID that has not been sent in H/W, no data will be
* written to H/W
*/
return NULL;
}
if (tmp_node->msg.cm_hdr.ch_id > msg->cm_hdr.ch_id)
{
break;
}
}
/* Move the node from tx_pending to tx_sending before
* sending(because dev_send() might call can_txdone()).
*/
list_delete(&msg_node->list);
if (&tmp_node->list == &cd_sender->tx_sending)
{
list_add_tail(&cd_sender->tx_sending, &msg_node->list);
}
else
{
list_add_before(&tmp_node->list, &msg_node->list);
}
#else
msg = &cd_sender->tx_buffer[cd_sender->tx_queue];
/* Increment the FIFO queue index before sending (because dev_send()
* might call can_txdone()).
*/
cd_sender->tx_queue++;
if (cd_sender->tx_queue == CONFIG_CAN_TXFIFOSIZE)
{
cd_sender->tx_queue = 0;
}
#endif
return msg;
}
/****************************************************************************
* Name: can_revert_msg
*
* Description:
* Rever msg in sender, because sending failed.
*
****************************************************************************/
void can_revert_msg(FAR struct can_txcache_s *cd_sender,
FAR struct can_msg_s *msg)
{
#ifdef CONFIG_CAN_TXPRIORITY
FAR struct can_msg_node_s *msg_node;
msg_node = container_of(msg, struct can_msg_node_s, msg);
list_delete(&msg_node->list);
list_add_head(&cd_sender->tx_pending, &msg_node->list);
#else
UNUSED(msg);
if (cd_sender->tx_queue == 0)
{
cd_sender->tx_queue = CONFIG_CAN_TXFIFOSIZE - 1;
}
else
{
cd_sender->tx_queue--;
}
#endif
}
/****************************************************************************
* Name: can_send_done
*
* Description:
* Release the sender resources, after the tragic message is successfully
* sent to the bus.
*
****************************************************************************/
void can_send_done(FAR struct can_txcache_s *cd_sender)
{
#ifdef CONFIG_CAN_TXPRIORITY
FAR struct list_node *node;
node = list_remove_head(&cd_sender->tx_sending);
list_add_head(&cd_sender->tx_free, node);
#else
/* Remove the message at the head of the xmit FIFO */
cd_sender->tx_head++;
if (cd_sender->tx_head >= CONFIG_CAN_TXFIFOSIZE)
{
cd_sender->tx_head = 0;
}
#endif
}