Why does the PL330 always trigger a RX Timeout when reading? - linux

we are using a CycloneV FPGA system with an integrated ARM Cortex-A9 dual core processor system. This processor system is running angstrom linux with a 4.1 kernel and has a pl330 DMA core. We are trying to write a kernel module to use this DMAC to push data into the FPGA fabric and read from it.
Writing to the FPGA works (albeit pretty slow), but everytime we try to read I can see the read transfer taking place, but the linux dmaengine reports a timeout and on kernel-level, the transfer is never finished.
Does somebody know why this might happen? I can comment out the timeout logic and get data out of it, so I'm guessing it might have to do with cache-coherency or something similar.
This is the read function in our kernel module:
static int dma_start_rx(struct platform_device *pdev, unsigned datalen, unsigned char *databuf, u32 burst_size, bool isDecrypt) {
struct mRiddler_dev *pdata = platform_get_drvdata(pdev);
struct dma_chan *dmaChan;
struct dma_slave_config dmaConf;
struct dma_async_tx_descriptor *dmaDesc = NULL;
dmaChan = pdata->dma_rx_chan;
memset(&dmaConf, 0, sizeof(dmaConf)); // Clear memory for DMA config
// Build DMA configuration
dmaConf.direction = DMA_DEV_TO_MEM;
if (isDecrypt)
dmaConf.src_addr = pdata->dma_datareg_phy + RID_DMA_DATA_READ_DECRYPT;
else
dmaConf.src_addr = pdata->dma_datareg_phy + RID_DMA_DATA_READ_ENCRYPT;
#ifdef DEBUG
pr_info("Accessing device address: %u, based on dma_datareg_phy: %u\n", dmaConf.src_addr, pdata->dma_datareg_phy);
#endif
dmaConf.src_addr_width = 8;
dmaConf.src_maxburst = burst_size;
pdata->dma_rx_addr = dma_map_single(&pdev->dev, databuf, datalen, DMA_FROM_DEVICE);
// Error handling
if (dma_mapping_error(&pdev->dev, pdata->dma_rx_addr)) {
pr_err("dma_map_single for RX failed!\n");
return -EINVAL;
}
// Setup slave config
dmaengine_slave_config(dmaChan, &dmaConf);
// Get DMA descriptor
dmaDesc = dmaengine_prep_slave_single(dmaChan, pdata->dma_rx_addr, datalen, dmaConf.direction, DMA_PREP_INTERRUPT);
// Error handling
if (!dmaDesc) {
dma_shutdown(pdata);
return -ENOMEM;
}
// Set callbacks
dmaDesc->callback = dma_rx_done;
dmaDesc->callback_param = pdata;
// Get cookie and submit transfer to queue
pdata->dma_rx_cookie = dmaengine_submit(dmaDesc);
// Error handling
if (dma_submit_error(pdata->dma_rx_cookie))
pr_err("RX cookie error on dmaengine_submit\n");
#ifdef DEBUG
pr_info("Issueing RX transfer to DMA engine...\n");
#endif
// Start DMA
dma_async_issue_pending(dmaChan);
return 0;
}
EDIT: Here's where the timeout is set up and the DMA read function is called:
if (numBytes > 0) {
#ifdef DEBUG
pr_info("Calling dma_start_rx with following values: numBytes=%d, burstSize=%d\n", numBytes, burstSize);
#endif
retVal = dma_start_rx(pdata->pdev, numBytes, pdata->dma_rx_buffer, burstSize, false);
if (retVal) {
pr_err("Error starting RX DMA!\n");
return retVal;
}
if (!wait_for_completion_timeout(&dma_read_complete, msecs_to_jiffies(timeout))) {
pr_err("Timeout while waiting for RX DMA\n");
dmaengine_terminate_all(pdata->dma_rx_chan);
return -ETIMEDOUT;
}
*offset = 0;
}
This is the dmesg output when I try to read from the device:
[ 326.486364] Issueing DMA transfer...
[ 326.489952] DMA TX transfer finished!
[ 335.607286] There are 4 words available in enc_tx FIFO!
[ 335.612497] DMA read 32 > requested bytes 16, adjusting...
[ 335.617992] Now reading 16 bytes in 2 words!
[ 335.622246] Adjusted burstSize/numWords, new values: 2 and 2
[ 335.627898] Calling dma_start_rx with following values: numBytes=16, burstSize=2
[ 335.635283] Accessing device address: 3221225480, based on dma_datareg_phy: 3221225472
[ 335.643168] Issueing RX transfer to DMA engine...
[ 336.643795] Timeout while waiting for RX DMA, cookie: 2

Related

Issues while reading Writting USB data

I am new to Linux operating system, i am trying to interface GSM module with USB,i interfaced USB with GSM module successfully, but i have problem while reading the data from GSM module, i can able to write data to GSM module but while reading its too difficult for me, some time i can able to read data but some time i can't able to read data, here i copy and pasted my code, please go through the code and please let me where i am failing to read data,
i need to write data normally to USB port and data should be read on interrupt based
USB configuration
int init_ttyusb0(int speed,int parity)
{
fd=open("/dev/ttyUSB1",O_RDWR|O_NDELAY|O_NOCTTY|O_APPEND,S_IRWXU|S_IRWXG|S_IRWXO);|O_NOCTTY|O_SYNC|O_NDELAY);
perror("open");
if(fd==-1)
{
perror("open");
printf("open usb driver failed\n");
//close(fd);
return 1;
}
saio.sa_handler= signal_handler_IO;
saio.sa_flags=0;
saio.sa_restorer=NULL;
sigaction(SIGIO,&saio,NULL);
fcntl(fd,F_SETFL,FNDELAY);
fcntl(fd,F_SETOWN,getpid());
tcgetattr(fd,&tty);
perror("tcgetattr");
cfsetospeed(&tty,speed);
perror("cfsetospeed");
cfsetispeed(&tty,speed);
perror("cfsetispeed");
tty.c_cflag=(tty.c_cflag&~CSIZE)|CS8; // 8bit character
tty.c_iflag&=~IGNBRK;
// tty.c_lflag=0; // No signaling break process
// tty.c_oflag=0; //No remaping no delay
tty.c_cc[VMIN]=0; // read dosen't block
tty.c_cc[VTIME]=10; //0.5 seconds
tty.c_iflag&=~(IXON|IXOFF|IXANY);
tty.c_cflag|=CLOCAL; // igniore modem controls
tty.c_cflag|=CREAD;
tty.c_cflag&=~(PARENB|PARODD);
tty.c_cflag&=~CSTOPB;
tty.c_cflag&=~CRTSCTS;
tty.c_cflag&=~ICANON; // Disabling cananicle from disbling newline
tty.c_cflag&=~ISIG; // Disabling interrupt signaling bits
// tty.c_iflag&=~(IXON|IXOFF|IXANY); // Disabling software flow control
tty.c_lflag&=~ECHO;
tty.c_lflag&=~ECHOE;
tty.c_lflag&=~ECHONL;
tty.c_lflag&=~(IXON|IXOFF|IXANY);
tty.c_oflag&=~OPOST;
tcsetattr(fd,TCSANOW,&tty);
perror("tcsetattr");
printf("UART configured.....................\n");
}
Interrupt handler
-----------------------
void signal_handler_IO(int status)
{
static int gsm_count
read(fd,&zfc_buffer[gsm_count],sizeof(zfc_buffer));
printf("%s\n",zfc_buffer);
memset(zfc_buffer,0,sizeof(zfc_buffer));
fcntl(fd,F_SETFL,!(O_ASYNC));
}
int main(void)
{
int init_ttyusb0(B9600,0)
while(1)
{
printf("\n eneter option > ");
scanf("%d",&operation);
int m=26;
switch(operation)
{
case 0:
memset(buf_rcv,0,sizeof(buf_rcv));
buf[0]='A';
buf[1]='T';
buf[2]='\r';
buf[3]='\0';
write(fd,"AT\r",3);
usleep((3+25)*100);
fcntl(fd,F_SETFL,O_ASYNC); // Enabling interrupt after write
usleep((3+25)*100);
perror("write");
break;
case 1:
write(fd,"AT+CFUN=1\r",10);
usleep(175000);
fcntl(fd,F_SETFL,O_ASYNC);// Enabling interrupt after write
break;
case 2:
memset(buf,0,sizeof(buf));
strcpy(buf,"AT+CCID\r");
write(fd,"AT+CPIN?\r",9);
usleep(150000);
fcntl(fd,F_SETFL,O_ASYNC);// Enabling interrupt after write
break;
}

No such device or address error when reading from TCA6408a I/O Expander using I2C

I have an application that I have cross-compiled for the USRP N310 that uses the TCA9548 I²C switch to read and write data to and from the TCA6408 I/O Expander on the daughterboard using the built-in Linux I2C driver. To do this, I've been following the tutorial "Implementing I2C drivers in UserSpace" attached here i2c tutorial. I've been able to test out this functionality successfully using the ZC706 embedded platform,however, I'm having issues reading data on the USRP platform. My code is the following
#include "i2c_dev.hpp"
int main()
{
int i2cfd;
__s32 num;
//Opening i2c adapter 6
printf("Opening bus adapter\n");
i2cfd = open("/dev/i2c-6", O_RDWR);
if ( i2cfd <0 )
{
printf("Failed to open /dev/i2c-6: %s\n",strerror( errno ));
return (1);
}
//Instatiating three objects of IO_Expander class
IO_Expander dba;
//Reading data from the IO Expander
printf("Setting slave address of device\n");
if (ioctl(i2cfd, I2C_SLAVE, 0x20) < 0) {
printf("Error setting slave address:%s\n",strerror(errno));
return(1);
}
printf("Reading data from the IO Expander for DB-A Object\n");
num =dba.read_data(i2cfd, 0x00);
if (num<0){
printf("Error reading data: %s\n",strerror(errno));
}
else {
printf("The input value is %d: \n", num);
}
printf("Leaving DB-A Object \n\n\n");
//Closing the adapter
close(i2cfd);
}
The printout I receive when running this application is a string error "No such device or address" when trying to read data from the I/O Expander.The slave address of the device is the 7 bit address of 010 0000 or hex 20 and the address of the input register is 0x00 according to the data sheet. At the moment, I'm unclear as to what's going wrong.I'm a bit of a newbie so I'm unsure of what to try next.
TCA9548 data sheet
TCA6408 data sheet

linux device driver control high-speed SPI ADC

Recently I have a project to use a Linux host to communicate with an ADC device(SPI communication). I use my knowledge to write a device driver for it.
The goal of this driver is to read ADC data and transfer them to userspace. My idea is when the Linux host gets the DRDY signal(data ready signal, the signal means the data of ADC can be read) from ADC, an interrupt will be triggered, and the SPI read API of the driver will read data from the SPI bus. the data will fill into a buffer, when the buffer is full, the driver sends a SIGNAL to the userspace program, and the data in the buffer will be read by the userspace.
Although this idea may not be a perfect plan to realize my goal, I finish the code above. Unfortunately, I face a question that makes my goal failed.
The SPI transfer API of the Linux host should be put into the bottom half of the interrupt(due to the sleep mechanic of SPI API), that is to say, if the sample rate of AD is too fast, the bottom half of the interrupt may read a delayed data of ADC, when I use 4kHz sample rate, there are 7997 interrupts, but only 7907 data has been read. When I use the 250Hz sample rate, my idea is OK. But, I must use at least 4ksps.
I do not know whether you have some experience with this kind of problem, or maybe my idea is not suited for the high-speed ADC, I hope you can give me some suggestions, thanks a lot.
Here is some core code of my idea. The SPI transfer function:
int get_ad_data(struct spi_device *ad_spi_dev)
{
int ret = -1;
gpio_set_value(ADS1299_CS_PIN, 0);
if( ad_spi_dev )
{
struct spi_transfer tr =
{
.tx_buf = &send_data,
.rx_buf = &get_data,
.len = 27,
};
ret = spi_sync_transfer(ad_spi_dev, &tr, 1);
}
printk("%02x, %02x, %02x\r\n",get_data[6],get_data[7],get_data[8]);
gpio_set_value(ADS1299_CS_PIN, 1);
return ret;
}
The interrupt handler:
static irqreturn_t drdy_handler(int irq, void *dev_id)
{
struct ads1299_dev *dev = dev_id;
schedule_work(&dev->drdy_irq.work_drdy);
return IRQ_HANDLED;
}
static void drdy_work(struct work_struct *work)
{
int ret;
ret = get_ad_data(ads1299_spi_dev);
}

FTDI LibMPSSE SPI

I recently purchased the FTDI C232HM-DDHSL-0 USB cable for use with SPI and I2C devices; my goal is to read the memory from an SPI memory chip that I removed from a router. However, I am having issues getting the libMPSSE library & 2xx drivers to send signals to a connected device.I hooked the leads of the C232 up to a Saleae logic analyzer and saw that no signals were being output!
Useful links for this question:
FTDI C232HM-DDHSL-0 (purchase):
https://www.ftdichip.com/Products/Cables/USBMPSSE.htm
C232HM-DDHSL-0 data sheet:
https://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_C232HM_MPSSE_CABLE.PDF
Memory Chip data sheet: https://www.macronix.com/Lists/Datasheet/Attachments/7370/MX25L6406E,%203V,%2064Mb,%20v1.9.pdf
2xx drivers: https://www.ftdichip.com/Drivers/D2XX.htm
MPSSE SPI library:
https://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm
I am using a Debian x86_64 machine. The MPSSE SPI download only had the i386 version of the library, so I downloaded the MPSSE SPI source (https://www.ftdichip.com/Support/SoftwareExamples/MPSSE/LibMPSSE-SPI.htm) and built the x86_64 bit version. There were no build errors or warnings.
I copied libftd2xx.so and libMPSSE.so to /usr/local/lib.
I plugged the cable in, then removed the following kernel modules:
ftdi_sio <-- Readme says to take take out
usbserial <-- Readme says to take out
usb_wann <-- Needed to remove to take out usbserial
qcserial <-- Needed to remove to take out usbserial
At this point I was hoping that my set up was done correctly.
I connected 6x cables to the 8 pin memory chip:
VCC <--> red lead
Ground <--> black lead
Chip select <--> brown lead
Data in <--> green
serial clock <--> orange
Data out <--> yellow
I did not connect hold and write protect pins on the memory chip. Recall my goal is to read the memory out of the chip, and the waveform for the read did not show those pins were necessary.
I am using this program:
/* Standard C libraries */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "ftd2xx.h"
#include "libMPSSE_spi.h"
FT_HANDLE ftHandle;
uint8 tx_buffer[4096] = {0};
uint8 rx_buffer[4096] = {0};
int main()
{
uint8 i = 0;
int sizeToTransfer = 0;
int sizeTransfered = 0;
FT_STATUS status = FT_OK;
FT_DEVICE_LIST_INFO_NODE devList = {0};
ChannelConfig channelConf = {0};
channelConf.ClockRate = 30000000; // 30Mhz
channelConf.LatencyTimer = 75;
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)*/
//
// Open the channel and dump some information
//
status = SPI_GetChannelInfo(0,&devList);
if (status != FT_OK)
{
printf("SPI_GetChannelInfo failed, status = %d\n", status);
return -1;
}
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); // TODO: Why blank?
printf("Description=%s\n",devList.Description);
printf("ftHandle=0x%p\n",devList.ftHandle);/*is 0 unless open*/
//
// Open channel 0
//
status = SPI_OpenChannel(0,&ftHandle);
if (status != FT_OK)
{
printf("SPI_OpenChannel failed, status = %d\n", status);
return -1;
}
//
// Initialize the channel: See configuration structure at top of main()
//
status = SPI_InitChannel(ftHandle,&channelConf);
if (status != FT_OK)
{
printf("SPI_InitChannel failed, status = %d\n", status);
return -1;
}
//
// Send the read command (0x03), and read what we get in our receive buffer
//
sizeToTransfer=8;
sizeTransfered=0;
tx_buffer[0] = 0x03;
status = SPI_ReadWrite(ftHandle, rx_buffer, tx_buffer, sizeToTransfer, &sizeTransfered,
SPI_TRANSFER_OPTIONS_SIZE_IN_BYTES|SPI_TRANSFER_OPTIONS_CHIPSELECT_DISABLE|SPI_TRANSFER_OPTIONS_CHIPSELECT_ENABLE);
if (status != FT_OK)
{
printf("SPI_ReadWrite failed, status = %d\n", status);
return -1;
}
//
// Dump the receive buffer to see if we got anything.
//
printf("Size transfered = %d\n", sizeTransfered);
i = 0;
while (i < sizeTransfered)
{
printf("%02x",rx_buffer[i]);
i++;
}
printf("\n");
//
// Cleanup
//
status = SPI_CloseChannel(ftHandle);
if (status != FT_OK)
{
printf("SPI_CloseChannel failed, ret = %d\n", status);
}
return 0;
}
To make and run the program:
To compile: gcc read_memory.c -lMPSSE -ldl
To run: sudo ./a.out
My output is:
// The driver can at least detect the cable.
Flags=0x2
Type=0x8
ID=0x4036001
LocId=0x204
SerialNumber=
Description=USB <-> Serial
ftHandle=0x(nil)
Size transfered = 8
// Junk in the receive buffer
ff037fffffffffff
Whether I keep the memory chip connected or disconnected I get the same junk in the receive buffer. I see no LEDs light up on the cable or anything to indicate it is "working." I have tried SPI_Write() and SPI_Read() and get similar behavior. I tested on an Ubuntu VM (running on Windows host) and saw the exact same behavior.
I am really excited to get this to work so if anyone can help me with what I am doing wrong it would be greatly appreciated! Thanks!
Set device first to MPSSE mode, and it should work. See quite minimum (Python) example below based on FTD2XX library only (tested in Windows). LibMPSSE is not required to run simple SPI. Data can be read by s = dev.read(nbytes), where nbytes is byte count. Find more information in nice tutorial: Driving an SPI device using MPSSE
import ftd2xx
OPS = 0x03 # Bit mask for SPI clock and data out
OE = 0x08 # CS
dev = ftd2xx.open(0) # open first available device
if dev:
dev.setTimeouts(1000, 1000)
dev.setBitMode(OPS, 2) # bit mask, MPSSE mode
dev.write(bytes((0x86, 59, 0))) # set SCK to 100kHz
dev.write(bytes((0x80, 0, OE + OPS))) # CS low
data = 0x55,
n = len(data) - 1
dev.write(bytes(((0x11, n % 256, n // 256) + data))) # write SPI data
dev.write(bytes((0x80, OE, OE + OPS))) # CS high
dev.close()

Linux kernel module to read out GPS device via USB

I'm writing a Linux kernel module to read out a GPS device (a u-blox NEO-7) via USB by using the book Linux Device Drivers.
I already can probe and read out data from the device successfully. But, there is a problem when reading the device with multiple applications simultaneously (I used "cat /dev/ublox" to read indefinitely). When the active/reading applications is cancelled via "Ctrl + C", the next reading attempt from the other application fails (exactly method call usb_submit_urb(...) returns -EINVAL).
I use following ideas for my implementation:
The kernel module methods should be re-entrant. Therefore, I use a mutex to protect critical sections. E.g. allowing only one reader simultaneously.
To safe ressources, I reuse the struct urb for different reading requests (see an explanation)
Device-specific data like USB endpoint address and so on is held in a device-specific struct called ublox_device.
After submitting the USB read request, the calling process is sent to sleep until the asynchronous complete handler is called.
I verified that the ideas are implemented correctly: I have run two instances of "cat /dev/ublox" simultaneously and I got the correct output (only one instance accessed the critical read section at a time). And also reusing the "struct urb" is working. Both instances read out data alternatively.
The problem only occurs if the currently active instance is cancelled via "Ctrl + C". I can solve the problem by not reusing the "struct urb" but I would like to avoid that. I.e. by allocating a new "struct urb" for each read request via usb_alloc_urb(...) (usually it is allocated once when probing the USB device).
My code follows the USB skeleton driver from Greg Kroah-Hartman who also reuse the "struct urb" for different reading requests.
Maybe someone has a clue what's going wrong here.
The complete code can be found on pastebin. Here is a small excerpt of the read method and the USB request complete handler.
static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos)
{
struct ublox_device *ublox_device = file->private_data;
...
return_value = mutex_lock_interruptible(&ublox_device->bulk_in_mutex);
if (return_value < 0)
return -EINTR;
...
retry:
usb_fill_bulk_urb(...);
ublox_device->read_in_progress = 1;
/* Next call fails if active application is cancelled via "Ctrl + C" */
return_value = usb_submit_urb(ublox_device->bulk_in_urb, GFP_KERNEL);
if (return_value) {
printk(KERN_ERR "usb_submit_urb(...) failed!\n");
ublox_device->read_in_progress = 0;
goto exit;
}
/* Go to sleep until read operation has finished */
return_value = wait_event_interruptible(ublox_device->bulk_in_wait_queue, (!ublox_device->read_in_progress));
if (return_value < 0)
goto exit;
...
exit:
mutex_unlock(&ublox_device->bulk_in_mutex);
return return_value;
}
static void ublox_read_bulk_callback(struct urb *urb)
{
struct ublox_device *ublox_device = urb->context;
int status = urb->status;
/* Evaluate status... */
...
ublox_device->transferred_bytes = urb->actual_length;
ublox_device->read_in_progress = 0;
wake_up_interruptible(&ublox_device->bulk_in_wait_queue);
}
Now, I allocate a new struct urb for each read request. This avoids the problem with the messed up struct urb after an active read request is cancelled by the calling application. The allocated struct is freed in the complete handler.
I will come back to LKML when I optimize my code. For now, it is okay to allocate a new struct urb for each single read request. The complete code of the kernel module is on pastebin.
static ssize_t ublox_read(struct file *file, char *buffer, size_t count, loff_t *pos)
{
struct ublox_device *ublox_device = file->private_data;
...
retry:
ublox_device->bulk_in_urb = usb_alloc_urb(0, GFP_KERNEL);
...
usb_fill_bulk_urb(...);
...
return_value = usb_submit_urb(ublox_device->bulk_in_urb, GFP_KERNEL);
...
}
static void ublox_read_bulk_callback(struct urb *urb)
{
struct ublox_device *ublox_device = urb->context;
...
usb_free_urb(ublox_device->bulk_in_urb);
...
}

Resources