how to verify tcp checksum [closed] - c

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
For some odd reason, i'm unable to properly verify the TCP checksum. I have code to check IP and UDP checksum, and it works perfectly fine, but for TCP something in my logic is amiss.
My struct definitions for these headers are fine as i can read the data perfectly fine (verified from wireshark). The only problem i'm having is that for TCP checksum, i'm unable to verify whether the checksum is actually correct. Any thoughts as to where i'm doing this wrong?
Very much appreciated.
checksum function
unsigned short in_cksum(unsigned short *addr,int len)
{
register int sum = 0;
u_short answer = 0;
register u_short *w = addr;
register int nleft = len;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (nleft > 1) {
sum += *w++;
nleft -= 2;
}
if (nleft == 1) {
*(u_char *)(&answer) = *(u_char *)w ;
sum += answer;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
answer = ~sum;
return(answer);
}
read TCP function (old, check edited version)
/* packets are read using the pcap libraries */
void readTCP(const u_char *packets) {
struct TCP_Header *tcp = (struct TCP_Header*) (packets + sizeof(struct Ethernet_Header) + sizeof(struct IP_Header));
struct IP_Header *ip = (struct IP_Header*) (packets + sizeof(struct Ethernet_Header));
struct TCP_Pseudo tcpPseudo;
char tcpcsumblock[sizeof(struct TCP_Pseudo) + sizeof(struct TCP_Header)];
/* tcp pseudo header */
memset(&tcpPseudo, 0, sizeof(struct TCP_Pseudo));
tcpPseudo.source_ip = ip->source_ip.s_addr;
tcpPseudo.destination_ip = ip->destination_ip.s_addr;
tcpPseudo.zero = 0;
tcpPseudo.protocol = 6;
tcpPseudo.length = htons(sizeof(struct TCP_Header));
/* grab tcp checksum and reset it */
int tcpCheckSum = htons(tcp->tcp_checksum);
tcp->tcp_checksum = 0;
/* place the data from the tcp pseudo infront of the tcp header */
memcpy(tcpcsumblock, &tcpPseudo, sizeof(TCPPseudoHeader));
memcpy(tcpcsumblock+sizeof(TCPPseudoHeader),tcp, sizeof(TCPHeader));
/* here is the issue, the checksum that i'm calculating isn't the correct checksum (i checked this by examing the packets from wireshark */
u_short checksum = in_cksum((unsigned short *)tcpcsumblock, sizeof(tcpcsumblock));
}
==EDIT==
new tcp function
/* packets are read using the pcap libraries */
void readTCP(const u_char *packets) {
struct TCP_Header *tcp = (struct TCP_Header*) (packets + sizeof(struct Ethernet_Header) + sizeof(struct IP_Header));
struct IP_Header *ip = (struct IP_Header*) (packets + sizeof(struct Ethernet_Header));
struct TCP_Pseudo tcpPseudo;
/* tcp pseudo header */
memset(&tcpPseudo, 0, sizeof(struct TCP_Pseudo));
tcpPseudo.source_ip = ip->source_ip;
tcpPseudo.destination_ip = ip->destination_ip;
tcpPseudo.zero = 0;
tcpPseudo.protocol = 6;
tcpPseudo.len = htons(ip->ip_len - (ip->ip_hdr_len * 4));
int len = sizeof(struct TCP_Pseudo) + tcpPseudo.len;
u_char tcpcsumblock[len];
memcpy(tcpcsumblock, &tcpPseudo, sizeof(struct TCP_Pseudo));
memcpy(tcpcsumblock + sizeof(struct TCP_Pseudo), (packets + sizeof(struct Ethernet_Header) + sizeof(struct IP_Header)), tcpPseudo.len);
/* here is the issue, the checksum that i'm calculating isn't the correct checksum (i checked this by examing the packets from wireshark */
u_short checksum = in_cksum((unsigned short *)ps_tcp, len);
char *cs = checksum ? "Invalid Checksum!" : "Valid!";
}
ip header
typedef struct IP_Header {
#if __BYTE_ORDER__ == __LITTLE_ENDIAN__
uint8_t ip_hdr_len:4; /* header length */
uint8_t ip_version:4; /* ip version */
#else
uint8_t ip_version:4; /* ip version */
uint8_t ip_hdr_len:4; /* The IP header length */
#endif
uint8_t ip_tos; /* type of service */
uint16_t ip_len; /* total length */
uint16_t ip_id; /* identification */
uint16_t ip_off; /* fragment offset field */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
uint8_t ip_ttl; /* time to live */
uint8_t ip_p; /* protocol */
uint16_t ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
} __attribute__ ((packed));
tcp header
typedef struct TCP_Header {
uint16_t tcp_source_port; /* source port */
uint16_t tcp_dest_port; /* destination port */
uint32_t tcp_seq; /* sequence */
uint32_t tcp_ack; /* acknowledgement number */
uint8_t tcp_offest; /* data offset */
#define TH_OFF(th) (((th)->th_offx2 & 0xf0) >> 4)
uint8_t tcp_flags; /* flags */
#define TH_FIN 0x01
#define TH_SYN 0x02
#define TH_RST 0x04
#define TH_PUSH 0x08
#define TH_ACK 0x10
#define TH_URG 0x20
#define TH_ECE 0x40
#define TH_CWR 0x80
#define TH_NS 0x100
#define TH_RS 0xE00
uint16_t tcp_window; /* window */
uint16_t tcp_sum; /* checksum */
uint16_t tcp_urp; /* urgent pointer */
} __attribute__ ((packed));
tcp pseudo header
typedef struct TCP_Pseudo {
struct in_addr src_ip; /* source ip */
struct in_addr dest_ip; /* destination ip */
uint8_t zeroes; /* = 0 */
uint8_t protocol; /* = 6 */
uint16_t len; /* length of TCPHeader */
} __attribute__ ((packed));

The problem is this line:
tcpPseudo.length = htons(sizeof(struct TCP_Header));
According to RFC 793:
The TCP Length is the TCP header length plus the data length in
octets (this is not an explicitly transmitted quantity, but is
computed), and it does not count the 12 octets of the pseudo
header.
You only set the TCP header length, but it should be TCP header length + data length.
The data length is the Total Length reported by the IP header minus the IP header length (field is named IHL in the IP header and must be multiplied by 4 to get the length in bytes) minus the size of the TCP header.
Yet since you want to add the length of the TCP header to the data length, you just have to subtract the IP header length form the total packet length and left over is the sum of TCP header and data length.
tcpPseudo.length = htons(ntohs(ip->total_length) - (ip->ihl * 4));
Also according to the RFC:
The checksum field is the 16 bit one's complement of the one's
complement sum of all 16 bit words in the header and text.
"And text" means all the data following the TCP header is also checksummed. Otherwise TCP could not guarantee that the data was transmitted correctly. Remember, TCP is a reliable protocol, that will retransmit corrupted data, but therefor it must also recognize when data got corrupted.
So the whole packet minus the IP header must be added to tcpcsumblock. Therefor tcpcsumblock must be big enough for whole packets to fit (in case of TCP, 1500 bytes are usually enough, though in theory an IP packet may be as big as 64 KB and will be fragmented if needed) and then you must add the pseudo header, the tcp header and everything to the end of the packet.
I wrote a working piece of code for you. I verified that this works correctly by feeding in some real-live data. As a bonus, this implementation performs a couple of sanity checks on the IP header, including verifying its checksum (since if that doesn't match, all header fields may contain bogus as the header got most likely corrupted), and it also doesn't need to allocate any dynamic memory or copy any data around, so it should be pretty fast. Especially for the last feature, I had to change the in_cksum function to accept a third parameter. If this parameter is zero, it will behave exactly as the version in your code, but by feeding the correct value, you can use this function to update an already calculated checksum as if the data you are going to checksum had directly followed the data you already did checksum before (pretty nifty, huh?)
uint16_t in_cksum (const void * addr, unsigned len, uint16_t init) {
uint32_t sum;
const uint16_t * word;
sum = init;
word = addr;
/*
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
* sequential 16 bit words to it, and at the end, fold back all the
* carry bits from the top 16 bits into the lower 16 bits.
*/
while (len >= 2) {
sum += *(word++);
len -= 2;
}
if (len > 0) {
uint16_t tmp;
*(uint8_t *)(&tmp) = *(uint8_t *)word;
sum += tmp;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return ((uint16_t)~sum);
}
void readTCP (const u_char *packets) {
uint16_t csum;
unsigned ipHdrLen;
unsigned ipPacketLen;
unsigned ipPayloadLen;
struct TCP_Pseudo pseudo;
const struct IP_Header * ip;
const struct TCP_Header * tcp;
// Verify IP header and calculate IP payload length
ip = (const struct IP_Header *)(packets + sizeof(struct Ethernet_Header));
ipHdrLen = ip->ip_hdr_len * 4;
if (ipHdrLen < sizeof(struct IP_Header)) {
// Packet is broken!
// IP packets must not be smaller than the mandatory IP header.
return;
}
if (in_cksum(ip, ipHdrLen, 0) != 0) {
// Packet is broken!
// Checksum of IP header does not verify, thus header is corrupt.
return;
}
ipPacketLen = ntohs(ip->ip_len);
if (ipPacketLen < ipHdrLen) {
// Packet is broken!
// The overall packet cannot be smaller than the header.
return;
}
ipPayloadLen = ipPacketLen - ipHdrLen;
// Verify that there really is a TCP header following the IP header
if (ip->ip_p != 6) {
// No TCP Packet!
return;
}
if (ipPayloadLen < sizeof(struct TCP_Header)) {
// Packet is broken!
// A TCP header doesn't even fit into the data that follows the IP header.
return;
}
// TCP header starts directly after IP header
tcp = (const struct TCP_Header *)((const u_char *)ip + ipHdrLen);
// Build the pseudo header and checksum it
pseudo.src_ip = ip->ip_src;
pseudo.dest_ip = ip->ip_dst;
pseudo.zeroes = 0;
pseudo.protocol = 6;
pseudo.len = htons(ipPayloadLen);
csum = in_cksum(&pseudo, (unsigned)sizeof(pseudo), 0);
// Update the checksum by checksumming the TCP header
// and data as if those had directly followed the pseudo header
csum = in_cksum(tcp, ipPayloadLen, (uint16_t)~csum);
char * cs = csum ? "Invalid Checksum!" : "Valid!";
printf("%s\n", cs);
}

Related

Copy SKB to USART DMA

Hello i am writing a network device driver. I already setup the USART.
i registrated the Network device in the Probe funktion like this:
my_net = alloc_etherdev(sizeof(struct stm32_port));
if (my_net == NULL){
return ENOMEM;
}
/* Set MAC address */
eth_hw_addr_random(my_net);
my_net -> netdev_ops = &my_net_ops;
SET_NETDEV_DEV(my_net, &pdev->dev);
return register_netdev(my_net);
now in the ndo_start_xmit methode my kernal always gets a panic when i try to copy the socked puffer to the CPU dma adress
static int my_net_send(struct sk_buff *skb, struct net_device *ndev)
{
//struct stm32_port *priv = netdev_priv(ndev);
//unsigned int entry = TX_BUF_L;
//unsigned int entry = tp->cur_tx;
struct stm32_port *lp = netdev_priv(ndev);
struct sk_buff *sk_buff;
unsigned pktlen = skb->len;
/* adjust the packet length to min. required
* and hope that the buffer is large enough
* to provide some random data.
*/
if (pktlen < ETH_ZLEN)
{
if (skb_padto(skb, ETH_ZLEN))
return NETDEV_TX_OK;
pktlen = ETH_ZLEN;
}
//pr_info("do_start_xmit(skb=%p, ndev=%p) len=%u\n", skb, ndev, pktlen);
netif_stop_queue(ndev);
//send
if (lp->tx_dma_busy)
return NETDEV_TX_BUSY;
//pr_info("copy to usart: %s", skb->data);
sk_buff = skb_get(skb);
pr_info("copy to usart: %s", sk_buff->data);
memcpy(&lp->tx_buf[0], sk_buff, pktlen);
//dma_addr_t dma_addr = dma_map_single(ndev->d, skb->data, skb->len, DMA_TO_DEVICE);
ndev->stats.tx_packets++;
ndev->stats.tx_bytes += pktlen;
dev_kfree_skb (skb);
netif_start_queue(ndev);
return NETDEV_TX_OK;
}
struct stm32_port {
struct uart_port port;
struct clk *clk;
struct stm32_usart_info *info;
struct dma_chan *rx_ch; /* dma rx channel */
dma_addr_t rx_dma_buf; /* dma rx buffer bus address */
unsigned char *rx_buf; /* dma rx buffer cpu address */
struct dma_chan *tx_ch; /* dma tx channel */
dma_addr_t tx_dma_buf; /* dma tx buffer bus address */
unsigned char *tx_buf; /* dma tx buffer cpu address */
u32 cr1_irq; /* USART_CR1_RXNEIE or RTOIE */
u32 cr3_irq; /* USART_CR3_RXFTIE */
int last_res;
bool tx_dma_busy; /* dma tx busy */
bool hw_flow_control;
bool fifoen;
int wakeirq;
int rdr_mask; /* receive data register mask */
struct mctrl_gpios *gpios; /* modem control gpios */
};
Whats the correct way to copy the socked puffer. Sorry i am new to device drivers.

eBPF: modifying UDP payload and responding to client

I'm trying to modify the UDP payload I receive from the client to clone and redirect the packet in order to respond to it. Swapping the MAC and IP addresses is already done, as well as the cloning, but I don't know how to modify the UDP payload.
I want to send a payload with the following 3 bytes: 0x1a 0x31 0x0f back to the client, either by creating a new payload (better) and replacing it in the packet or by replacement. How can I do that?
Here is my code so far:
UPDATED BASED ON COMMENTS (Now only needs checksum recalculation):
int pingpong(struct __sk_buff *skb)
{
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
struct ethhdr *eth = data;
struct iphdr *ip;
struct udphdr *udpdata;
if ((void *)eth + sizeof(*eth) > data_end) {
return TC_ACT_UNSPEC;
}
ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end) {
return TC_ACT_UNSPEC;
}
udpdata = (void *)ip + sizeof(*ip);
if ((void *)udpdata + sizeof(*udpdata) > data_end) {
return TC_ACT_UNSPEC;
}
if (eth->h_proto != htons(ETH_P_IP)) {
return TC_ACT_UNSPEC;
}
if (ip->protocol != IPPROTO_UDP) {
return TC_ACT_UNSPEC;
}
unsigned int payload_size;
unsigned char *payload;
payload_size = ntohs(udpdata->len) - sizeof(*udpdata);
payload = (unsigned char *)udpdata + sizeof(*udpdata);
if ((void *)payload + payload_size > data_end) {
return TC_ACT_UNSPEC;
}
// 1. Swap the MACs
__u8 tmp_mac[ETH_ALEN];
memcpy(tmp_mac, eth->h_dest, ETH_ALEN);
memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
memcpy(eth->h_source, tmp_mac, ETH_ALEN);
// 2. Swap the IPs
if (eth->h_proto == htons(ETH_P_IP)) {
__u32 tmp_ip = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = tmp_ip;
}
// 3. Swap the ports
udpdata->source = port;
udpdata->dest = srcport;
// 4. Change the payload to be 0x1a 0x31 0x0f
bpf_skb_adjust_room(skb, -1, BPF_ADJ_ROOM_NET, 0);
uint8_t byte1 = 0x1a;
uint8_t byte2 = 0x31;
uint8_t byte3 = 0x0f;
int ret = bpf_skb_store_bytes(skb, payload_offset, &byte1, sizeof(byte1), 0);
ret = bpf_skb_store_bytes(skb, payload_offset+1, &byte2, sizeof(byte2), 0);
ret = bpf_skb_store_bytes(skb, payload_offset+2, &byte3, sizeof(byte3), 0);
// Re-calculate the checksum
bpf_l4_csum_replace(skb, L4_CSUM_OFF, datap[0], byte1, 0);
bpf_l4_csum_replace(skb, L4_CSUM_OFF, datap[1], byte2, 0);
bpf_l4_csum_replace(skb, L4_CSUM_OFF, datap[2], byte3, 0);
// Not working!
// Final: Redirect to be sent
bpf_clone_redirect(skb, skb->ifindex, 0);
}
If I just change the payload without removing the 1 byte using the adjust_room function, it is sent, but the last byte is 00. I want to remove that.
Any tips please? Thanks!
As suggested by #Qeole, you can use the following to change the size and content of your UDP payload:
// Remove 1 byte after IP header.
bpf_skb_adjust_room(ctx, -1, BPF_ADJ_ROOM_NET, 0);
... re-check packet pointers or verifier will complain ...
// Need to rewrite the UDP header as the extra space was added before it.
*new_udphdr = *old_udphdr;
// Write payload.
udp_payload[0] = 0x1a
udp_payload[1] = 0x31
udp_payload[2] = 0x0f

How can I receive Ethernet frames with ibverbs?

I want to write a simple test program to receive Ethernet frames using the ibverbs API.
The code below compiles and runs but never receives any packets. I'm using Mellanox ConnectX-3 hardware on Ubuntu 18.
Questions:
If, while running this RX program, I ping the Inifiniband interface from another machine, then ping receives responses. I would not expect that because the ping requests should be grabbed by the RX program and the Linux IP stack should not see them and therefore should not respond. What should happen?
Is there anything obvious wrong with my code?
Do I need a steering rule? If I remove the call of ibv_create_flow() should I just receive all packets the interface sees?
#include <infiniband/verbs.h>
#include <stdio.h>
#include <stdlib.h>
#define PORT_NUM 1
#define MAX_MSG_SIZE 1500 // The maximum size of each received packet.
#define RQ_NUM_DESC 512 // Max packets that can be received without processing.
// The MAC of the interface we are listening on.
#define DEST_MAC { 0x00, 0x0d, 0x3a, 0x47, 0x1c, 0x2e }
#define FATAL_ERROR(msg, ...) { fprintf(stderr, "ERROR: " msg "\n", ##__VA_ARGS__); exit(-1); }
int main() {
// Get the list of devices.
int num_devices = 0;
struct ibv_device **dev_list = ibv_get_device_list(&num_devices);
if (!dev_list)
FATAL_ERROR("Failed to get IB devices list.");
// Choose the first device.
struct ibv_device *ib_dev = dev_list[0];
if (!ib_dev)
FATAL_ERROR("IB device not found.");
printf("Found %i Infiniband device(s).\n", num_devices);
printf("Using device '%s'.\n", ibv_get_device_name(ib_dev));
// Get the device context.
struct ibv_context *context = ibv_open_device(ib_dev);
if (!context)
FATAL_ERROR("Couldn't get context for device.");
// Allocate a protection domain (PD) that will group memory
// regions (MR) and rings.
struct ibv_pd *pd = ibv_alloc_pd(context);
if (!pd)
FATAL_ERROR("Couldn't allocate protection domain.");
// Create Complition Queue (CQ).
struct ibv_cq *cq = ibv_create_cq(context, RQ_NUM_DESC, NULL, NULL, 0);
if (!cq)
FATAL_ERROR("Couldn't create completion queue. errno = %d.", errno);
// Create Queue Pair (QP).
struct ibv_qp_init_attr qp_init_attr = {
.qp_context = NULL,
.send_cq = cq, // Report receive completion to CQ.
.recv_cq = cq,
.cap = {
.max_send_wr = 0, // No send ring.
.max_recv_wr = RQ_NUM_DESC, // Max num packets in ring.
.max_recv_sge = 1, // Only one pointer per descriptor.
},
.qp_type = IBV_QPT_RAW_PACKET, // Use Ethernet packets.
};
struct ibv_qp *qp = ibv_create_qp(pd, &qp_init_attr);
if (!qp)
FATAL_ERROR("Couldn't create queue pair.");
// Initialize the QP (receive ring) and assign a port.
struct ibv_qp_attr qp_attr = { 0 };
qp_attr.qp_state = IBV_QPS_INIT;
qp_attr.port_num = PORT_NUM;
int qp_flags = IBV_QP_STATE | IBV_QP_PORT;
if (ibv_modify_qp(qp, &qp_attr, qp_flags) < 0)
FATAL_ERROR("Failed to initialize queue pair.");
// Move ring state to ready-to-receive. This is needed in
// order to be able to receive packets.
memset(&qp_attr, 0, sizeof(qp_attr));
qp_flags = IBV_QP_STATE;
qp_attr.qp_state = IBV_QPS_RTR;
if (ibv_modify_qp(qp, &qp_attr, qp_flags) < 0)
FATAL_ERROR("Failed to put queue pair into ready-to-receive state.");
// Allocate memory for packet buffer.
int buf_size = MAX_MSG_SIZE * RQ_NUM_DESC; // Maximum size of data to be accessed by hardware.
void *buf = malloc(buf_size);
if (!buf)
FATAL_ERROR("Couldn't allocate memory.");
// Register the user memory so it can be accessed by the HW directly.
struct ibv_mr *mr = ibv_reg_mr(pd, buf, buf_size, IBV_ACCESS_LOCAL_WRITE);
if (!mr)
FATAL_ERROR("Couldn't register memory region.");
// Create a scatter/gather entry.
struct ibv_sge sg_entry;
sg_entry.length = MAX_MSG_SIZE;
sg_entry.lkey = mr->lkey;
// Create a receive work request.
struct ibv_recv_wr wr;
wr.num_sge = 1;
wr.sg_list = &sg_entry;
wr.next = NULL;
// Post a load of receive work requests onto the receive queue.
struct ibv_recv_wr *bad_wr;
for (int n = 0; n < RQ_NUM_DESC; n++) {
// Each descriptor points to max MTU size buffer.
sg_entry.addr = (uint64_t)buf + MAX_MSG_SIZE * n;
// When a packet is received, a work completion will be created
// corresponding to this work request. It will contain this field.
wr.wr_id = n;
// Post the receive buffer to the ring.
int rv = ibv_post_recv(qp, &wr, &bad_wr);
if (rv != 0) {
FATAL_ERROR("Posting recv failed with error code %i.", rv);
}
}
// Create steering rule.
struct raw_eth_flow_attr {
struct ibv_flow_attr attr;
struct ibv_flow_spec_eth spec_eth;
} __attribute__((packed)) flow_attr = {
.attr = {
.comp_mask = 0,
.type = IBV_FLOW_ATTR_NORMAL,
.size = sizeof(flow_attr),
.priority = 0,
.num_of_specs = 1,
.port = PORT_NUM,
.flags = 0,
},
.spec_eth = {
.type = IBV_FLOW_SPEC_ETH,
.size = sizeof(struct ibv_flow_spec_eth),
.val = {
.dst_mac = DEST_MAC,
.src_mac = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
.ether_type = 0,
.vlan_tag = 0,
},
.mask = {
.dst_mac = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
.src_mac = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
.ether_type = 0,
.vlan_tag = 0,
}
}
};
// Register steering rule to intercept packet to DEST_MAC and place packet in
// ring pointed by qp.
struct ibv_flow *eth_flow = ibv_create_flow(qp, &flow_attr.attr);
if (!eth_flow)
FATAL_ERROR("Couldn't attach steering flow. Does DEST_MAC match that of the local NIC?");
printf("Receiving.\n");
while (1) {
// Wait for CQ event upon message received, and print a message
struct ibv_wc wc;
int msgs_completed = ibv_poll_cq(cq, 1, &wc);
if (msgs_completed > 0) {
printf("Message %ld received size %d\n", wc.wr_id, wc.byte_len);
sg_entry.addr = (uint64_t)buf + wc.wr_id * MAX_MSG_SIZE;
wr.wr_id = wc.wr_id;
// After processed need to post back the buffer.
int rv = ibv_post_recv(qp, &wr, &bad_wr);
if (rv != 0) {
FATAL_ERROR("Re-posting recv failed with error code %i.", rv);
}
}
else if (msgs_completed < 0) {
FATAL_ERROR("Polling error.");
}
}
}
Take a look at this example from Mellanox: https://community.mellanox.com/s/article/raw-ethernet-programming--basic-introduction---code-example
To receive everything the interface sees, you can use the experimental api #include <infiniband/verbs_exp.h>, then when creating the steering rule, use ibv_exp_flow_attr and set the type to IBV_EXP_FLOW_ATTR_SNIFFER.
Please refer to https://github.com/Mellanox/libvma/wiki/Architecture
VMA implements native RDMA verbs API. The native RDMA verbs have been extended into the Ethernet RDMA-capable NICs, enabling the packets to pass directly between the user application and the InfiniBand HCA or Ethernet NIC, bypassing the kernel and its TCP/UDP handling network stack.

Init_libMPSSE(), status = SPI_GetNumChannels(noChannels); always returns 0

I am using FT2232H-56Q. I want to use the SPI channels. I downloaded the libMPSSE-SPI example.
I am using the sample example "sample-static.c". When I run the exe I always get this message:
Press any key to continue . . . Number of available SPI channels = 0
This happens even when the device is connected.
Code:
/*!
* \file sample-static.c
*
* \author FTDI
* \date 20110512
*
* Copyright © 2000-2014 Future Technology Devices International Limited
*
*
* THIS SOFTWARE IS PROVIDED BY FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FUTURE TECHNOLOGY DEVICES INTERNATIONAL LIMITED
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* Project: libMPSSE
* Module: SPI Sample Application - Interfacing 94LC56B SPI EEPROM
*
* Rivision History:
* 0.1 - 20110512 - Initial version
* 0.2 - 20110801 - Changed LatencyTimer to 255
* Attempt to open channel only if available
* Added & modified macros
* Included stdlib.h
* 0.3 - 20111212 - Added comments
* 0.41 - 20140903 - Fixed compilation warnings
* Added testing of SPI_ReadWrite()
*/
/******************************************************************************/
/* Include files */
/******************************************************************************/
/* Standard C libraries */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <assert.h>
//#include <vector.h>
#include <stdbool.h>
//#include <random>
/* OS specific libraries */
#ifdef _WIN32
#include<windows.h>
#endif
/* Include D2XX header*/
#include "ftd2xx.h"
/* Include libMPSSE header */
#include "libMPSSE_spi.h"
/******************************************************************************/
/* Macro and type defines */
/******************************************************************************/
/* Helper macros */
#define APP_CHECK_STATUS(exp) {if(exp!=FT_OK){printf("%s:%d:%s(): status(0x%x) \
!= FT_OK\n",__FILE__, __LINE__, __FUNCTION__,exp);exit(1);}else{;}};
#define CHECK_NULL(exp){if(exp==NULL){printf("%s:%d:%s(): NULL expression \
encountered \n",__FILE__, __LINE__, __FUNCTION__);exit(1);}else{;}};
/* Application specific macro definations */
#define SPI_DEVICE_BUFFER_SIZE 256
#define SPI_WRITE_COMPLETION_RETRY 10
#define START_ADDRESS_EEPROM 0x00 /*read/write start address inside the EEPROM*/
#define END_ADDRESS_EEPROM 0x10
#define RETRY_COUNT_EEPROM 10 /* number of retries if read/write fails */
#define CHANNEL_TO_OPEN 0 /*0 for first available channel, 1 for next... */
#define SPI_SLAVE_0 0
#define SPI_SLAVE_1 1
#define SPI_SLAVE_2 2
#define DATA_OFFSET 4
#define USE_WRITEREAD 0
/******************************************************************************/
/* Global variables */
/******************************************************************************/
static FT_HANDLE ftHandle;
static uint8 buffer[SPI_DEVICE_BUFFER_SIZE] = {0};
static FT_DEVICE_LIST_INFO_NODE *devList;
static uint32 numberChannels;
/******************************************************************************/
/* Public function definitions */
/******************************************************************************/
/**
* #brief Initializes the comBridge
* Must be called first
* #param[out] noChannels: Number of available channels
* #param[out] channelId[]: Description of the devices with their index being identical to the channel selector in all other functions
* #return Device status with FT_OK = 0
*/
FT_STATUS comBridge_initialize(uint32 *noChannels, char *channelId[]) {
Init_libMPSSE();
FT_STATUS status = FT_OK;
// get number of channels
status = SPI_GetNumChannels(noChannels);
numberChannels = *noChannels;
APP_CHECK_STATUS(status);
#ifdef VERBOSE
printf("ComBridge initialized\n");
printf(" SPI channels %d\n\n", *noChannels);
#endif // VERBOSE
// get channel infos
devList = (FT_DEVICE_LIST_INFO_NODE *)calloc(*noChannels, sizeof(FT_DEVICE_LIST_INFO_NODE));
for (uint32 channelIndex = 0; channelIndex < *noChannels; channelIndex++) {
status = SPI_GetChannelInfo(channelIndex, devList);
APP_CHECK_STATUS(status);
strcpy(channelId[channelIndex], devList->Description);
#ifdef VERBOSE
printf("Channel %d info\n", channelIndex);
printf(" Flags 0x%x\n", devList->Flags);
printf(" Type 0x%x\n", devList->Type);
printf(" ID 0x%x\n", devList->ID);
printf(" LocId 0x%x\n", devList->LocId);
printf(" SerialNumber %s\n", devList->SerialNumber);
printf(" Description %s\n", devList->Description);
//printf(" ftHandle=0x%x\n", (unsigned int)devList->ftHandle);
printf("\n");
#endif // VERBOSE
};
return status;
}
/**
* #brief Opens and initilizes a SPI channel
* Must be called after comBridge_initialize() and for each channel individually
* Latency = 255 ms, that "xDBUS3 of MPSSE is chip select" and that "chip select is active high" are predefined and cannot be changed manually
* #param[in] channel: Selects a channel via the index of the corresponding element in the channelId[] array
* #param[in] clockRate_Hz: Clock rate of the SPI bus in Hz (0..10 MHz)
* #param[in] spiMode: Selects SPI Mode (SPI_CONFIG_OPTION_MODE0 or SPI_CONFIG_OPTION_MODE3)
* #return Device status with FT_OK = 0
*/
FT_STATUS comBridge_setup(uint32 channel, uint32 clockRate_Hz, uint32 spiMode) {
FT_STATUS status = FT_OK;
assert(channel < numberChannels);
assert(clockRate_Hz <= 10000000);
assert((spiMode == SPI_CONFIG_OPTION_MODE0) | (spiMode == SPI_CONFIG_OPTION_MODE3));
status = SPI_OpenChannel(channel, &devList[channel].ftHandle);
APP_CHECK_STATUS(status);
ChannelConfig channelConf = {
5000, // ClockRate
255, // LatencyTime
SPI_CONFIG_OPTION_MODE0 | SPI_CONFIG_OPTION_CS_DBUS3 | SPI_CONFIG_OPTION_CS_ACTIVELOW, // configOptions
0x00000000 // Pin
};
channelConf.ClockRate = clockRate_Hz;
channelConf.configOptions = spiMode | SPI_CONFIG_OPTION_CS_DBUS3 | SPI_CONFIG_OPTION_CS_ACTIVELOW;
status = SPI_InitChannel(devList[channel].ftHandle, &channelConf);
APP_CHECK_STATUS(status);
#ifdef VERBOSE
printf("Channel %d opened\n", channel);
printf(" ClockRate %d\n", channelConf.ClockRate);
printf(" Latency %d\n", channelConf.LatencyTimer);
printf(" Config %d\n", channelConf.configOptions);
printf(" Pin %d\n\n", channelConf.Pin);
//printf(" Handle=%ld\n", (uint32)devList[channel].ftHandle);
#endif // VERBOSE
// sets all GPIO pins as input
status = FT_WriteGPIO(devList[channel].ftHandle, 0b00000000, 0b00000000);
APP_CHECK_STATUS(status);
return status;
}
/*!
* \brief Writes to EEPROM
*
* This function writes a byte to a specified address within the 93LC56B EEPROM
*
* \param[in] slaveAddress Address of the I2C slave (EEPROM)
* \param[in] registerAddress Address of the memory location inside the slave to where the byte
* is to be written
* \param[in] data The byte that is to be written
* \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
* \sa Datasheet of 93LC56B http://ww1.microchip.com/downloads/en/DeviceDoc/21794F.pdf
* \note
* \warning
*/
static FT_STATUS read_byte(uint8 slaveAddress, uint8 address, uint16 *data)
{
uint32 sizeToTransfer = 0;
uint32 sizeTransfered;
uint8 writeComplete=0;
uint32 retry=0;
FT_STATUS status;
/* CS_High + Write command + Address */
sizeToTransfer=1;
sizeTransfered=0;
buffer[0] = 0xC0;/* Write command (3bits)*/
buffer[0] = buffer[0] | ( ( address >> 3) & 0x0F );/*5 most significant add bits*/
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE);
APP_CHECK_STATUS(status);
/*Write partial address bits */
sizeToTransfer=4;
sizeTransfered=0;
buffer[0] = ( address & 0x07 ) << 5; /* least significant 3 address bits */
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BITS);
APP_CHECK_STATUS(status);
/*Read 2 bytes*/
sizeToTransfer=2;
sizeTransfered=0;
status = SPI_Read(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
APP_CHECK_STATUS(status);
*data = (uint16)(buffer[1]<<8);
*data = (*data & 0xFF00) | (0x00FF & (uint16)buffer[0]);
return status;
}
/*!
* \brief Reads from EEPROM
*
* This function reads a byte from a specified address within the 93LC56B EEPROM
*
* \param[in] slaveAddress Address of the I2C slave (EEPROM)
* \param[in] registerAddress Address of the memory location inside the slave from where the
* byte is to be read
* \param[in] *data Address to where the byte is to be read
* \return Returns status code of type FT_STATUS(see D2XX Programmer's Guide)
* \sa Datasheet of 93LC56B http://ww1.microchip.com/downloads/en/DeviceDoc/21794F.pdf
* \note
* \warning
*/
static FT_STATUS write_byte(uint8 slaveAddress, uint8 address, uint16 data)
{
uint32 sizeToTransfer = 0;
uint32 sizeTransfered=0;
uint8 writeComplete=0;
uint32 retry=0;
FT_STATUS status;
/* Write command EWEN(with CS_High -> CS_Low) */
sizeToTransfer=11;
sizeTransfered=0;
buffer[0]=0x9F;/* SPI_EWEN -> binary 10011xxxxxx (11bits) */
buffer[1]=0xFF;
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BITS|
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE|
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
APP_CHECK_STATUS(status);
/* CS_High + Write command + Address */
sizeToTransfer=1;
sizeTransfered=0;
buffer[0] = 0xA0;/* Write command (3bits) */
buffer[0] = buffer[0] | ( ( address >> 3) & 0x0F );/*5 most significant add bits*/
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE);
APP_CHECK_STATUS(status);
/*Write 3 least sig address bits */
sizeToTransfer=3;
sizeTransfered=0;
buffer[0] = ( address & 0x07 ) << 5; /* least significant 3 address bits */
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BITS);
APP_CHECK_STATUS(status);
/* Write 2 byte data + CS_Low */
sizeToTransfer=2;
sizeTransfered=0;
buffer[0] = (uint8)(data & 0xFF);
buffer[1] = (uint8)((data & 0xFF00)>>8);
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
APP_CHECK_STATUS(status);
/* Wait until D0 is high */
#if 1
/* Strobe Chip Select */
sizeToTransfer=0;
sizeTransfered=0;
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BITS|
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE);
APP_CHECK_STATUS(status);
#ifndef __linux__
Sleep(10);
#endif
sizeToTransfer=0;
sizeTransfered=0;
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BITS|
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
APP_CHECK_STATUS(status);
#else
retry=0;
state=FALSE;
SPI_IsBusy(ftHandle,&state);
while((FALSE==state) && (retry<SPI_WRITE_COMPLETION_RETRY))
{
printf("SPI device is busy(%u)\n",(unsigned)retry);
SPI_IsBusy(ftHandle,&state);
retry++;
}
#endif
/* Write command EWEN(with CS_High -> CS_Low) */
sizeToTransfer=11;
sizeTransfered=0;
buffer[0]=0x8F;/* SPI_EWEN -> binary 10011xxxxxx (11bits) */
buffer[1]=0xFF;
status = SPI_Write(ftHandle, buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BITS|
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE|
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
APP_CHECK_STATUS(status);
return status;
}
/*!
* \brief Main function / Entry point to the sample application
*
* This function is the entry point to the sample application. It opens the channel, writes to the
* EEPROM and reads back.
*
* \param[in] none
* \return Returns 0 for success
* \sa
* \note
* \warning
*/
int main()
{
uint32 noChannels = 0;
char *channelId[10];
char channelIdArray[10][46] = { { '\0' } };
for (int i = 0; i < sizeof(channelId); i++)
channelId[i] = channelIdArray[i];
comBridge_initialize(&noChannels, channelId);
for (uint32 channel = 0; channel < noChannels; channel++)
{
//cout << "Channel " << channel << ": " << channelId[channel] << endl;
comBridge_setup(channel, 100000, SPI_CONFIG_OPTION_MODE0);
comBridge_release(channel);
}
FT_STATUS status = FT_OK;
FT_DEVICE_LIST_INFO_NODE devList = {0};
ChannelConfig channelConf = {0};
uint8 address = 0;
uint32 channels = 0;
uint16 data = 0;
uint8 i = 0;
uint8 latency = 255;
channelConf.ClockRate = 5000;
channelConf.LatencyTimer = latency;
channelConf.configOptions = SPI_CONFIG_OPTION_MODE0 | SPI_CONFIG_OPTION_CS_DBUS3;// | SPI_CONFIG_OPTION_CS_ACTIVELOW;
channelConf.Pin = 0x00000000;/*FinalVal-FinalDir-InitVal-InitDir (for dir 0=in, 1=out)*/
/* init library */
#ifdef _MSC_VER
Init_libMPSSE();
#endif
status = SPI_GetNumChannels(&channels);
APP_CHECK_STATUS(status);
printf("Number of available SPI channels = %d\n",(int)channels);
if(channels>0)
{
for(i=0;i<channels;i++)
{
status = SPI_GetChannelInfo(i,&devList);
APP_CHECK_STATUS(status);
printf("Information on channel number %d:\n",i);
/* print the dev info */
printf(" Flags=0x%x\n",devList.Flags);
printf(" Type=0x%x\n",devList.Type);
printf(" ID=0x%x\n",devList.ID);
printf(" LocId=0x%x\n",devList.LocId);
printf(" SerialNumber=%s\n",devList.SerialNumber);
printf(" Description=%s\n",devList.Description);
printf(" ftHandle=0x%x\n",(unsigned int)devList.ftHandle);/*is 0 unless open*/
}
/* Open the first available channel */
status = SPI_OpenChannel(CHANNEL_TO_OPEN,&ftHandle);
APP_CHECK_STATUS(status);
printf("\nhandle=0x%x status=0x%x\n",(unsigned int)ftHandle,status);
status = SPI_InitChannel(ftHandle,&channelConf);
APP_CHECK_STATUS(status);
#if USE_WRITEREAD
{
uint8 k,l;
uint8 inBuffer[100];
uint8 outBuffer[]={0x81,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,30,31,32,33,34,35,36,37,38,39};
uint32 sizeToTransfer,sizeTransferred;
for(k=0; k<5; k++)
{
printf("LoopCount = %u ",(unsigned)k);
sizeToTransfer=10;
sizeTransferred=0;
#if 1 // BYTES
status = SPI_ReadWrite(ftHandle, inBuffer, outBuffer+k, sizeToTransfer, &sizeTransferred,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE|
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
#else // BITES
status = SPI_ReadWrite(ftHandle, inBuffer, outBuffer+k, sizeToTransfer*8, &sizeTransferred,
SPI_TRANSFER_OPTIONS_SIZE_IN_BITS|
SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE|
SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE);
#endif
APP_CHECK_STATUS(status);
printf("status=0x%x sizeTransferred=%u\n", status, sizeTransferred);
for(l=0;l<sizeToTransfer;l++)
printf("0x%x\n",(unsigned)inBuffer[l]);
printf("\n");
}
}
#else // USE_WRITEREAD
for(address=START_ADDRESS_EEPROM;address<END_ADDRESS_EEPROM;address++)
{
printf("writing address = %02d data = %d\n",address,address+DATA_OFFSET);
write_byte(SPI_SLAVE_0, address,(uint16)address+DATA_OFFSET);
}
for(address=START_ADDRESS_EEPROM;address<END_ADDRESS_EEPROM;address++)
{
read_byte(SPI_SLAVE_0, address,&data);
printf("reading address = %02d data = %d\n",address,data);
}
#endif // USE_WRITEREAD
status = SPI_CloseChannel(ftHandle);
}
#ifdef _MSC_VER
Cleanup_libMPSSE();
#endif
#ifndef __linux__
system("pause");
#endif
return 0;
}
I also have the same problem with FT2232H / win7Pro.
The root cause is libMPSSE get wrong locID and judge that the device is not available since D2XX always returns LocId=0 to libMPSSE.
ftdi_mid.c
case FT_DEVICE_2232H:
if(((devList.LocId & 0xf)==1)|| ((devList.LocId & 0xf)==2))
{
isMPSSEAvailable = MID_MPSSE_AVAILABLE;
}
break;
I modify the condition and re-compile libMPSSE with mingw64. It just works.
I had a similar problem on Ubuntu 19.10. The root cause of this was the kernel module that was interacting with FTDI once it was connected and preventing me to connect with it.
The solution was to unload that kernel module:
sudo rmmod ftdi_sio
It is also mentioned in the libftd2xx ReadMe file:
If the message "FT_Open failed" appears:
Perhaps the kernel automatically loaded another driver for the
FTDI USB device.
sudo lsmod
If "ftdi_sio" is listed:
Unload it (and its helper module, usbserial), as follows.
sudo rmmod ftdi_sio
sudo rmmod usbserial
Otherwise, it's possible that libftd2xx does not recognise your
device's Vendor and Product Identifiers. Call FT_SetVIDPID before
calling FT_Open/FT_OpenEx/FT_ListDevices.
I've wrote a letter to FTDI Chip with the question. The support send an updated library that works. The version of library is 0.6 (beta).

UART/DMA interrupt not being triggered on dsPIC33

I have a dsPIC33 with an explorer board 16. The entire code base is fairly lengthy but the problem is isolated to the DMA interrupt. It is not being triggered even though I have pretty much copied the Microchip UARTloopback example. I have read the Microchip manual section for both the UART and DMA thoroughly and the loopback example does work, however when it is implemented with my code it does not. I have checked my entire project to make sure that the DMA channels are not being overwritten or used anywhere else in the project. The same applies to the UART section. I will be including the entire file c file and h file. Functions to focus on are the uart_commInit, BEACON_uart_commPuts, and _DMA3Interrupt. The BEACON_uart_commPuts works and prints to the terminal. Everything compiles
uart.c
/**
* This source file contains functions for using the UARTs.
*
* #todo Write uart_monitor() or ISR to check for UART parity, framing error and DMA collisions
*
* #ingroup CSuart
* #defgroup CSuart UART
* #{
* High level functions for UART with DMA. Function names
* beginning with uart_comm... are for communicating with the transceiver. The
* UART peripheral used for the transceiver can be changed by editing the macros
* in CSuart.h.
*/
/* System & Local Includes */
#include <p33Fxxxx.h>
#include <string.h>
#include "CSdefine.h"
#include "types.h"
#include "CSuart.h"
/* Type and Constant Definitions*/
// Handshake control signals for Devboard USB
#define HS0 BIT4 // RC4
#define HS1 BIT3 // RC3
#define HS2 BIT2 // RC2
#define HS3 BIT5 // RD5
#define HS4 BIT2 // RD2
#define HS5 BIT1 // RD1
// Motherboard control signals
#define CS_SD_BAR BIT5 // RE5
#define OE_USB_BAR BIT1 // RC1
#define OE_MHX_BAR BIT2 // RE2
#define ON_SD_BAR BIT4 // RE4
#define ON_MHX_BAR BIT3 // RE3
/* Global & Local Variables */
// DMA TX and RX buffers for comm UART
static uint16 txBuff[RADIO_TX_BUFFER_SIZE] __attribute__((space(dma)));
static uint16 rxBuff[RADIO_RX_BUFFER_SIZE] __attribute__((space(dma)));
static uint16 tx0Buff[BEACON_TX_BUFFER_SIZE] __attribute__((space(dma)));
static uint16 rx0Buff[BEACON_TX_BUFFER_SIZE] __attribute__((space(dma)));
static uint16 *lastRead = (uint16 *) &rxBuff; // Points to next transfer to be read from DMA rxBuff
static uint16 *lastRead0 = (uint16 *) &rx0Buff; // Points to next transfer to be read from DMA rxBuff
/* Functions */
/******************************************************************************
**** ****
** **
csk_usb_open() Enable USB on the Dev/Flight hardware
** **
**** ****
******************************************************************************/
void csk_usb_open(void) {
// Disable all control signals to avoid spurious
// writes when -OE_USB goes active (LOW).
PORTD |= (HS5+HS4+HS3 );
TRISD &= ~(HS5+HS4+HS3 );
//
// // Configure -OE_USB as an output, and make
// // it active (i.e., LOW)
PORTC &= ~OE_USB_BAR;
TRISC &= ~OE_USB_BAR;
} /* csk_usb_open() */
/******************************************************************************
**** ****
** **
csk_usb_close() // Disable USB on the Dev/flight hardware
** **
**** ****
******************************************************************************/
void csk_usb_close(void) {
// Restore -OE_USB to an input.
PORTC |= OE_USB_BAR;
TRISC |= OE_USB_BAR;
} /* csk_usb_close() */
/**
* Initializes the UART port with DMA for use for satellite comm (ie. the transceiver).
* This should be called sometime during startup.
*/
void uart_commInit(){
// Configure UART for 9600 8N1 with interrupts to the DMA module
RADIO_UMODEbits.UEN = 0b10; //UxTX, UxRX, UxCTS and UxRTS pins are enabled and used
RADIO_UMODEbits.PDSEL = 0b00; //8-bit data, no parity
RADIO_UBRG = ((Fcyc/38400)/16)-1; //9600 baud //38400 //((Fcyc/38400)/16)-1
//COMM_UBRG = 259; // 9600 baud # 80MHz
RADIO_USTAbits.UTXISEL0 = 0; // Interrupt when there is room in TX FIFO
RADIO_USTAbits.URXISEL = 0; // Interrupt when a character is received
// Configure DMA channel 1 as Comm Uart transmit
DMA1CONbits.DIR = 1; // DMA reads from SRAM, writes to peripheral
DMA1CONbits.AMODE = 00; // Addr. mode is register indirect w/ post increment
DMA1CONbits.MODE = 0b01;// One-shot, no ping-pong mode.
DMA1CONbits.SIZE = 1; // Transfer 1 byte at a time
DMA1REQbits.IRQSEL = 0b0001100; // DMA1 writes to UART1 TX (the Comm uart port.)
DMA1PAD = (volatile unsigned int) &RADIO_UTXREG; //Tell DMA module address of COMM TX register
DMA1STA = __builtin_dmaoffset(txBuff); // Tell DMA module start address of our dma tx buffer
IFS0bits.DMA1IF = 0; // Clear DMA interrupt flag
//IEC0bits.DMA1IE = 1; // Enable DMA interrupt
//Configure DMA channel 0 as Comm Uart receive
DMA0CONbits.MODE = 0b00; // Continuous no Ping-Pong mode
DMA0CNT = RADIO_RX_BUFFER_SIZE - 1;
DMA0REQbits.IRQSEL = 0b0001011; // DMA0 reads from UART1 RX (the Comm uart port.)
DMA0PAD = (volatile unsigned int) &RADIO_URXREG;
DMA0STA = __builtin_dmaoffset(rxBuff); // Tell DMA start addr. of buffer A
//DMA0STB = __builtin_dmaoffset(rxBuffB); // Tell DMA start addr. of buffer B
IFS0bits.DMA0IF = 0; // Clear DMA interrupt flag
//IEC0bits.DMA0IE = 1; // Enable DMA interrupt
DMA0CONbits.CHEN = 1; // Enable RX DMA channel only. TX channel is one-shot mode.
// UART config for CLI
BEACON_UMODEbits.STSEL = 0; //1-stop bit
BEACON_UMODEbits.PDSEL = 0; //No Parity, 8-data bits
BEACON_UMODEbits.ABAUD = 0; // Autobaud Disabled
BEACON_UBRG = ((Fcyc/9600)/16)-1; // 9600 baud # 80Mhz
BEACON_USTAbits.UTXISEL0 = 0; // interrupt after 1 Tx char is transmited
//BEACON_USTAbits.UTXISEL1 = 0;
BEACON_USTAbits.URXISEL = 0; // interrrupt after 1 Rx char is received
BEACON_UMODEbits.UARTEN = 1; // Enable UART
BEACON_USTAbits.UTXEN = 1; // Enable Uart Tx
IEC4bits.U2EIE = 0; // Enable error interrupt
//Tx config for CLI
DMA4REQ = 0x001F; // Uart2 transmiter
DMA4PAD = (volatile unsigned int) &BEACON_UTXREG;// Tell DMA module address of COMM TX register
DMA4CONbits.AMODE = 0; // Addr. mode is register indirect x/ post increment
DMA4CONbits.MODE = 1; // One-Shot
DMA4CONbits.DIR = 1; // reads from RAM writes to peripheral
DMA4CONbits.SIZE = 1;// Transfers 1 byte at a time
// count needs to be pushed into interrupt and counted there, will avoid weird shit
DMA4CNT = 7; // 11 DMA requests
DMA4STA = __builtin_dmaoffset(tx0Buff); // Tell DMA module start address of our dma tx buffer
IFS2bits.DMA4IF = 0; // Clear DMA interrupt flag
IEC2bits.DMA4IE = 1; // Enable DMA interrupt
// Rx config for CLI
DMA3REQ = 0x001E; // Uart2 Receive
DMA3PAD = (volatile unsigned int) &BEACON_URXREG; // Tell DMA module address of COMM RX register
DMA3CONbits.AMODE = 0; // Addr. mode is register indirect x/ post increment
DMA3CONbits.MODE = 2; // Ping-Pong
DMA3CONbits.DIR = 0; // reads from peripheral writes to RAM
DMA3CONbits.SIZE = 0; // Transfers 1 word at a time
DMA3CNT = 7; // 11 DMA requests
DMA3STA = __builtin_dmaoffset(tx0Buff); // Tell DMA start addr. of receive buffer
DMA3STB = __builtin_dmaoffset(rx0Buff); // Tell DMA start addr. of receive buffer
DMA3CONbits.CHEN = 1; // Enable RX DMA channel only. TX channel is one-shot mode.
IFS2bits.DMA3IF = 0; // Clear DMA interrupt
IEC2bits.DMA3IE = 1; // Enable DMA interrupt
// END of added code
// these bits must be at end of file
RADIO_UMODEbits.UARTEN = 1; //enable uart
RADIO_USTAbits.UTXEN = 1; //transmit enabled
}
/**
* Writes a string to the comm Port. The length must be less than COMM_TX_BUFFER_SIZE
* or this function will have no effect.
* #param str The string to write.
* #param len The number of bytes to write
* #pre uart_commInit() must be called before calling this function.
* #note This function call is blocking. It waits until the last transfer is
* finished to begin current transmission. Once current transmission has been
* started then it will return because the DMA module handles transmission of data.
*/
void BEACON_uart_commPuts(char *str, uint16 len){
#if DEBUG_OUTPUT
uint16 *targetAddr;
if(len == 0 || len > BEACON_TX_BUFFER_SIZE)
return; //TODO Handle error
while(!BEACON_commIsTXReady()); // Blocking!
targetAddr = (uint16 *) (DMA4STA + ((unsigned int) &_DMA_BASE)); // Address of DMA buffer
memcpy((void *) targetAddr, (const void *) str, len); // Copy data into DMA buffer
DMA4CNT = len-1; // Send len # of characters
DMA4CONbits.CHEN = 1; // Turn on the DMA channel
DMA4REQbits.FORCE = 1; // Start DMA transfer to comm UART
#endif
}
/**
* Retrieves a string from the Comm. Port. The caller must ensure that the 'str'
* parameter can handle the number of bytes being requested in parameter 'len'.
* #param str The string to store the received characters in.
* #param len The maximum number of characters to return. The number of chars
* copied to str will be less than or equal to len.
* #return int16 The number of bytes copied to str if non-negative or an error
* code if negative.
* #pre uart_commInit() must be called before calling this function.
* #todo Add overrun detection
* #todo Implement error codes if necessary.
*/
int16 RADIO_uart_commGets(char *str, uint16 len){
/* The DMA transfers 2 bytes of data into the DMA buffer for every byte it
* receives from the UART (see dsPIC33 reference manual DMA chapter). The
* lower byte is the data received by the UART and the upper byte has 2
* status bits. There is one DMA receive buffer (no Ping-Pong mode) which the
* DMA starts to fill 2 bytes at a time until the buffer is full then goes
* back to the beginning and starts to put new characters in the beginning
* of the buffer again. This code reads up to 'len' bytes (not transfers!) from the buffer
* starting from the last location readmup to the last byte available in
* the DMA buffer. If the DMA rolled over to the beginning since the last read,
* this function reads until the end of the buffer, then from the beginning.
* This should be called often enough to prevent overruns.
*
* NOTE: For every 2 bytes this function reads from the DMA buffer it only
* returns one because the upper byte of each transfer are status bits.
*/
uint16 numTransfers, i; // # of DMA transfers (not bytes!) to return to caller.
uint16 *lastArrived = (uint16 *)(DMA0STA + ((unsigned int) &_DMA_BASE)); // Points after most recent transfer received in DMA buffer
//static uint16 *lastRead = (uint16 *) &rxBuff; // Points to next transfer to be read
if(lastArrived < lastRead){ // Rollover occured
// (1) # of transfers till end of buffer.
// (2) # of transfers in the beginning of buffer.
numTransfers = (uint16) ((rxBuff + RADIO_RX_BUFFER_SIZE - 1) - lastRead);
numTransfers += (uint16) (lastArrived - rxBuff);
}else{
numTransfers = (uint16) (lastArrived - lastRead);
}
if(len < numTransfers) // Don't give caller more bytes than they can handle
numTransfers = len;
for(i = 0; i < numTransfers; i++){ // Now copy chars into caller's buffer
/*if(i == 0){
uart_commGetc();
continue;
}*/
if(lastRead >= rxBuff + RADIO_RX_BUFFER_SIZE){ // Handle rollover
lastRead = rxBuff; // Start reading from beginning now
}
// Mask off status byte and only copy the lower char to callers buffer
*str = (char) (*lastRead & 0xFF);
str++; // Increment index into caller's buffer
lastRead++; // Increment read index
}
return i; // # of bytes returned in parameter 1.
}
int16 BEACON_uart_commGets(char *str, uint16 len){
/* The DMA transfers 2 bytes of data into the DMA buffer for every byte it
* receives from the UART (see dsPIC33 reference manual DMA chapter). The
* lower byte is the data received by the UART and the upper byte has 2
* status bits. There is one DMA receive buffer (no Ping-Pong mode) which the
* DMA starts to fill 2 bytes at a time until the buffer is full then goes
* back to the beginning and starts to put new characters in the beginning
* of the buffer again. This code reads up to 'len' bytes (not transfers!) from the buffer
* starting from the last location readmup to the last byte available in
* the DMA buffer. If the DMA rolled over to the beginning since the last read,
* this function reads until the end of the buffer, then from the beginning.
* This should be called often enough to prevent overruns.
*
* NOTE: For every 2 bytes this function reads from the DMA buffer it only
* returns one because the upper byte of each transfer are status bits.
*/
uint16 numTransfers, i; // # of DMA transfers (not bytes!) to return to caller.
uint16 *lastArrived = (uint16 *)(DMA3STA + ((unsigned int) &_DMA_BASE)); // Points after most recent transfer received in DMA buffer
//static uint16 *lastRead = (uint16 *) &rxBuff; // Points to next transfer to be read
if(lastArrived < lastRead){ // Rollover occured
// (1) # of transfers till end of buffer.
// (2) # of transfers in the beginning of buffer.
numTransfers = (uint16) ((rx0Buff + BEACON_RX_BUFFER_SIZE - 1) - lastRead);
numTransfers += (uint16) (lastArrived - rx0Buff);
}else{
numTransfers = (uint16) (lastArrived - lastRead);
}
if(len < numTransfers) // Don't give caller more bytes than they can handle
numTransfers = len;
for(i = 0; i < numTransfers; i++){ // Now copy chars into caller's buffer
/*if(i == 0){
uart_commGetc();
continue;
}*/
if(lastRead >= rx0Buff + BEACON_RX_BUFFER_SIZE){ // Handle rollover
lastRead = rx0Buff; // Start reading from beginning now
}
// Mask off status byte and only copy the lower char to callers buffer
*str = (char) (*lastRead & 0xFF);
str++; // Increment index into caller's buffer
lastRead++; // Increment read index
}
return i; // # of bytes returned in parameter 1.
}
/**
* Retrieves a single character from the comm receive buffer.
* #return (int16) returns a signed integer. If the value is less than zero there
* were no characters in the UART receive buffer to return. Otherwise, the return
* value should be cast to a char and used accordingly.
* #pre uart_commInit() must be called before calling this function.
*/
int16 RADIO_uart_commGetc(){
int16 retValue;
if(RADIO_uart_commNumRXBytes() > 0){
if(lastRead >= rxBuff + RADIO_RX_BUFFER_SIZE){ // Handle rollover
lastRead = rxBuff; // Start reading from beginning now
}
retValue = (int16) *lastRead;
lastRead++;
return retValue;
}
return -1;
}
/**
* Retrieves a single character from the comm receive buffer.
* #return (int16) returns a signed integer. If the value is less than zero there
* were no characters in the UART receive buffer to return. Otherwise, the return
* value should be cast to a char and used accordingly.
* #pre uart_commInit() must be called before calling this function.
*/
int16 BEACON_uart_commGetc(){
int16 retValue;
if(BEACON_uart_commNumRXBytes() > 0){
if(lastRead >= rx0Buff + BEACON_RX_BUFFER_SIZE){ // Handle rollover
lastRead = rx0Buff; // Start reading from beginning now
}
retValue = (int16) *lastRead;
lastRead++;
return retValue;
}
return -1;
}
/**
* Looks for and retrieves a string from the comm port up to and including the
* specified terminating character. If the terminating character does not exist
* in the uart buffer no characters are copied to str and -1 is returned.
* #param str A buffer to stored the received data in
* #param len Maximum number of characters to retrieve
* #param terminator the terminating character
* #return The number of characters copied to str, -1 if no terminator not found or buffer to small for command
*/
int16 uart_commGetToChar(char *str, uint16 len, char terminator){
BOOL foundChar = FALSE;
int16 i, strLen = 0; // strLen is the value we will return
uint16 *tempPtr = lastRead; // Points after the last read DMA transfer (transfer = 2 bytes)
uint16 *lastArrived = (uint16 *)(DMA0STA + ((unsigned int) &_DMA_BASE)); // Points after most recent transfer received in DMA buffer
len = (len < (i = RADIO_uart_commNumRXBytes())) ? len : i; // If numCharsReceived > len then len = numCharsReceived
// Look for null terminating character
while(!foundChar){ // Loop until char is found or 'till end of received chars
if(strLen >= len){
return -1; // Provided buffer not long enough or terminating char not found.
}
if((char) (*tempPtr & 0xFF) == terminator){ // Found terminating char?
foundChar = TRUE;
strLen++; // To count terminating character
}else{
strLen++; // Increment # of transfers we're planning to copy to user's buffer
tempPtr++; // Increment pointer to the receive buffer
if(tempPtr >= rxBuff + RADIO_RX_BUFFER_SIZE){ // Handle rollover in rxBuff
tempPtr = rxBuff; // Start reading from beginning now
}
}
}
for(i = 0; i < strLen; i++){ // Now copy chars into caller's buffer
if(lastRead >= rxBuff + RADIO_RX_BUFFER_SIZE){ // Handle rollover in rxBuff
lastRead = rxBuff; // Start reading from beginning now
}
// Mask off status byte and only copy the lower char to caller's buffer
*str = (char) (*lastRead & 0xFF);
str++; // Increment index into caller's buffer
lastRead++; // Increment read index
}
return strLen; // # of bytes INCLUDING terminating character
}
/**
* Returns the number of unread characters in the comm UART buffer.
* #return (int16) number of unread chars in comm UART buffer
* #pre uart_commInit() must be called before calling this function.
*/
int16 RADIO_uart_commNumRXBytes(){
int16 retValue;
uint16 *lastReceivedChar = (uint16 *)(DMA0STA + ((unsigned int) &_DMA_BASE));
if(lastReceivedChar >= lastRead){
return (int16) (lastReceivedChar - lastRead);
}else{
retValue = (int16) ((uint16 *)(&rxBuff + RADIO_TX_BUFFER_SIZE) - lastRead);
retValue += (uint16) (lastReceivedChar - (uint16 *)&rxBuff);
return retValue;
}
}
int16 BEACON_uart_commNumRXBytes(){
int16 retValue;
uint16 *lastReceivedChar = (uint16 *)(DMA2STA + ((unsigned int) &_DMA_BASE));
if(lastReceivedChar >= lastRead){
return (int16) (lastReceivedChar - lastRead);
}else{
retValue = (int16) ((uint16 *)(&rx0Buff + BEACON_TX_BUFFER_SIZE) - lastRead);
retValue += (uint16) (lastReceivedChar - (uint16 *)&rx0Buff);
return retValue;
}
}
/* Interrupt Handlers */
//void __attribute__((__interrupt__,no_auto_psv)) _U2EInterrupt(void){
// TODO Handle Uart Error
//}
/**
* Comm receive DMA interrupt service routine. Currently not used.
*/
void __attribute__((__interrupt__,no_auto_psv)) _DMA0Interrupt(void){
IFS0bits.DMA0IF = 0; // Clear DMA interrupt flag
}
/**
* Comm transmit DMA interrupt service routine. Currently not used.
*/
void __attribute__((__interrupt__,no_auto_psv)) _DMA1Interrupt(void){
IFS0bits.DMA1IF = 0; // Clear DMA interrupt flag
}
/**
* Comm receive DMA interrupt service routine.
*/
void __attribute__((__interrupt__,no_auto_psv)) _DMA3Interrupt(void){
static unsigned int BufferCount = 0;
if(BufferCount == 0){
DMA4STA = __builtin_dmaoffset(tx0Buff);
}
else{
DMA4STA = __builtin_dmaoffset(rx0Buff);
}
DMA4CONbits.CHEN = 1; // Re-enable DMA3 channel
DMA4REQbits.FORCE = 1; // manual start the transfers, if we doing predetermined transfer size
BufferCount ^= 1;
IFS2bits.DMA3IF = 0; // Clear DMA3 Interrupt flag
}
/**
* Comm transmit DMA interrupt service routine. Currently not used.
*/
void __attribute__((__interrupt__,no_auto_psv)) _DMA4Interrupt(void){
IFS2bits.DMA4IF = 0; // Clear DMA interrupt flag
}
/**
* #}
*/
uart.h
/**
* This header file provides function prototypes and macros for UART ports.
*
* #ingroup CSuart
*/
/**
* #ingroup CSuart
* #{
*/
#ifndef CSUART_H
#define CSUART_H
/* Include Files */
#include <p33Fxxxx.h>
/**
* Register definitions for Comm port uart
* Change these values to use the other uart port.
* WARNING: Must also change values in uart_commInit(). (DMA1REQbits.IRQSEL)
* #cond
*/
/*NOTE:(8/20/14, 3:53pm) these macros have been changed, U2 -> U1 */
#define RADIO_UMODE U1MODE
#define RADIO_UMODEbits U1MODEbits
#define RADIO_USTA U1STA
#define RADIO_USTAbits U1STAbits
#define RADIO_URXREG U1RXREG
#define RADIO_UTXREG U1TXREG
#define RADIO_UBRG U1BRG
#define RADIO_URXIF IFS0bits.U1RXIF // Comm uart receive interrupt flag
#define RADIO_UTXIF IFS0bits.U1TXIF // Comm uart transmite interrupt flag
#define RADIO_RXIntEnable() {IEC0bits.U1RXIE = 1;}
#define RADIO_TXIntEnable() {IEC0bits.U1TXIE = 1;}
#define RADIO_ErrorIntEnable() {IEC4bits.U1EIE = 1;}
#define RADIO_RXIntDisable() {IEC0bits.U1RXIE = 0;}
#define RADIO_TXIntDisable() {IEC0bits.U1TXIE = 0;}
#define RADIO_ErrorIntDisable() {IEC4bits.U1EIE = 0;}
#define BEACON_UMODEbits U2MODEbits
#define BEACON_USTAbits U2STAbits
#define BEACON_URXREG U2RXREG
#define BEACON_UTXREG U2TXREG
#define BEACON_UBRG U2BRG
#define BEACON_IFSbits IFS2bits
#define RADIO_TX_BUFFER_SIZE 283 /// Size of comm port's DMA transmit buffer in bytes
#define RADIO_RX_BUFFER_SIZE 128 /// Size of comm port's DMA receive buffer in bytes
#define BEACON_TX_BUFFER_SIZE 128 /// Size of comm port's DMA transmit buffer in bytes
#define BEACON_RX_BUFFER_SIZE 128 /// Size of comm port's DMA receive buffer in bytes
/**
* Returns 1 when the comm port UART is ready to transmit, and 0 otherwise.
*/
#define RADIO_commIsTXReady() (RADIO_USTAbits.TRMT)
#define BEACON_commIsTXReady() (BEACON_USTAbits.TRMT)
/* Function Prototypes */
void uart_commInit();
void BEACON_uart_commPuts(char *str, uint16 len);
void uart0_commPuts(char *str, uint16 len);
int16 RADIO_uart_commGets(char *str, uint16 len);
//int16 uart0_commGets(char *str, uint16 len);
int16 RADIO_uart_commGetc();
//int16 uart0_commGetc();
int16 uart_commGetToChar(char *str, uint16 len, char terminator);
//int16 uart0_commGetToChar(char *str, uint16 len, char terminator);
int16 RADIO_uart_commNumRXBytes();
//int16 uart0_commNumRXBytes();
/**
* #}
*/
#endif /* CSUART_H */

Resources