1
votes

I am developing an acquisition device which requires DMA operation to transfer large data frames to the main memory. For now I am assuming the destination is a contiguous memory region so I am allocating it (say 1 MB) at boot time as described in section "Do-it-yourself allocation" from ldd2-ch13. The driver can then access that region by calling ioremap().

The current system works as follows:

  1. A memory-mapped control register enables a user-space application to Start/Stop the device

  2. Upon start, the device begins to transfer the acquired data to the assigned memory region continuously (and circularly) at ~8MB/s.

  3. The reserved memory region has the size of two frames in order to employ a double-buffering technique.
  4. Once a complete frame has been transferred, the device triggers an interrupt.

I have developed a simple char driver which provides a blocking read() function so that the user-space can read a new data frame every time an interrupt is received.

After running a few tests, I realized that the user-space application misses some frames when running the following code:

    for(i=0;i<NUM_FRAMES;i++) {

       read(dev_d,buf,FRAME_SIZE);/*Read frame*/

       for(j=0;j<FRAME_SIZE;j++) /*File dump*/
          fprintf(fp,"%d",buf[j]);

        fprintf(fp,"\n");
    }

I suspect that the application's process is being put to sleep between two subsequent reads, allowing the device to rewrite the memory location which should have already been read.

Since I have no experience in kernel development I would like to know how is the correct way to implement a driver for such a device in order to ensure synchronization. Basically I am trying to implement a simple shared memory communication for a real-time acquisition device and I need to guarantee that the OS is able to read all the acquired data frames.

1
You wrote that the data rate is ~8MB/s, but what is the frame size and hence the frame rate? Your read() rate has to be as fast as that frame rate. Your "file dump" loop is not efficient. This is a classic real-time problem: you have to process the data faster than it arrives, or buffer that data. Network interfaces increase double-buffering to N-buffers or rings. Maybe you'll have to also use multithreading, i.e. a dedicated input thread that always has a pending read() request.sawdust
Can you elaborate a little bit more about the splice() solution? Is that some way to enable the driver to directly write to a specific file without any user-space intervention?zml
Yes, exactly. If you want to transfer to a file, then you open the your acquisition device and a file. You can control the amount of data read/written, so if you know block sizes, you can insert/annotate your own framing. There is no copying from kernel to user to kernel memory. The data will go straight from driver to file. The file can be another device such as a serial port, etc.artless noise
" I believe the problem is that the user-space process is probably put to sleep" -- Yes, that is a problem, since you have 513 system calls performing I/O between each read(). The "novice mistake" you're making is not realizing the cycles consumed by performing inefficient I/O. Try fprintf()ing just the first 4 bytes, and see if there's still skipped frames. Then evaluate what kind of actual processing needs to be done, and how long that might take per frame. Make sure you have a workable top-level scheme or the big picture before you worry about implementation details.sawdust
Is the fprintf() just for debugging, or are you personally going to be reviewing this data in realtime? Why can't you write each of these frames as one block of 512-bytes of raw binary data rather than convert each byte to decimal in precious realtime? That's what I mean about the "big picture": if you're going to save the frames, just save it raw, and then convert it offline or a low-priority background process. "Do you know..." -- I have to admit ignorance about splice(). "read a whole a frame..." -- It's your driver; you tell us how/when a read request is satisfied!sawdust

1 Answers

2
votes

You are reading a very very old book. Here the link to the last version of the book (it is just very old): Linux Device Driver 3 - Memory Mapping. You can also read the DMA-API from the kernel documentation.

To make a kind of synchronization read Time, Delays and Deferred Work chapter. You can use the a waitqueue. You wait on read() and you *wake_up* when new frames are available.

About your code, it is not enough to understand your problem. But, if you think that you need to sleep/wait, you can implement the poll file_operation in your driver and use select() in user-space to ask if there is something to read.