3

I would like to copy data to user space from kernel module which receives data from serial port and transfers it to DMA, which in turn forwards the data to tty layer and finally to user space.

the current flow is serial driver FIFO--> DMA-->TTY layer -->User space (the data to tty layer is emptied from DMA upon expiration of timer)

What I want to achieve is

serial driver FIFO-->DMA-->user space. (I am OK with using timer to send the data to user space, if there is a better way let me know)

Also the kernel module handling the serialFIFO->DMA is not a character device. I would like to bypass tty layer completely. what is the best way to achieve so?

Any pointers/code snippet would be appreciated.

MPelletier
  • 16,256
  • 15
  • 86
  • 137
user1867459
  • 423
  • 2
  • 8
  • 27
  • I'd personally start by looking for a driver for another device with generally comparable requirements, for example something for a synchronous serial channel on an embedded system (just make sure it's for a normal linux, not a uClinux kernel where certain shortcuts can be taken). – Chris Stratton Sep 26 '13 at 16:13
  • Hi Chris,Thanks for your comments, It is for MontaVista based (2.6.32) embedded linux. I looked for such an example but didn't find any. Any thoughts on using mmap based driver to pass data to user space. I have been thinking on the following path. The driver will copy data to DMA and from DMA the data will be copied (memcpy) to mmap based memory from which user space program can read the data.I am also concerned about some of the issues with the approach. i.e how to notify user space of new data and avoiding data overwritten/corruption by DMA. – user1867459 Sep 26 '13 at 20:14
  • 2
    *"is not a character device"* -- Then it would have to be a **block** device, which does not make sense. *"...and transfers it to DMA"* -- DMA is a method for transferring data, not a destination for copying. Please explain what you think the benefits of bypassing the tty layer are. And do you understand what you give up, e.g. *tc[gs]etattr()* and friends? This is probably an XY problem. Are you trying to reduce latency? See this [question](http://stackoverflow.com/questions/4667141/high-delay-in-rs232-communication-on-a-pxa270) solved by using **ASYNC_LOW_LATENCY** – sawdust Sep 27 '13 at 02:13

1 Answers1

3

In >=3.10.5 the "serial FIFO" that you refer to is called a uart_port. These are defined in drivers/tty/serial.

I assume that what you want to do is to copy the driver for your UART to a new file, then instead of using uart_insert_char to insert characters from the UART RX FIFO, you want to insert the characters into a buffer that you can access from user space.

The way to do this is to create a second driver, a misc class device driver that has file operations, including mmap, and that allocates kernel memory that the driver's mmap file operation function associates with the userspace mapped memory. There is a good example of code for this written by Maxime Ripard. This example was written for a FIQ handled device, but you can use just the probe routine's dma_zalloc_coherent call and the mmap routine, with it's call to remap_pfn_range, to do the trick, that is, to associate a user space mmap on the misc device file with the alloc'ed memory.

You need to connect the memory that you allocated in your misc driver to the buffer that you write to in your UART driver using either a global void pointer, or else by using an exported symbol, if your misc driver is a module. Initialize the pointer to a known invalid value in the UART driver and test it to make sure the misc driver has assigned it before you try to insert characters to the address to which it points.

Note that you can't add an mmap function to the UART driver directly because the UART driver class does not support an mmap file operation. It only supports the operations defined in the include/linux/serial_core.h struct uart_ops.

Admittedly this is a cumbersome solution - two device drivers, but the alternative is to write a new device class, a UART device that has an mmap operation, and that would be a lot of work compared with the above solution although it would be elegant. No one has done this to date because as Jonathan Corbet say's "...not every device lends itself to the mmap abstraction; it makes no sense, for instance, for serial ports and other stream-oriented devices", though this is exactly what you are asking for.

I implemented this solution for a polling mode UART driver based on the mxs-auart.c code and Maxime's example. It was non-trivial effort but mostly because I am using a FIQ handler for the polling timer. You should allow two to three weeks to get the whole thing up and running.

The DMA aspect of your question depends on whether the UART supports DMA transfer mode. If so, then you should be able to set it using the serial flags. The i.MX28's PrimeCell auarts support DMA transfer but for my application there was no advantage over simply reading bytes directly from the UART RX FIFO.

Jonathan Ben-Avraham
  • 4,615
  • 2
  • 34
  • 37