I am writing a program that interfaces with a particular serial device. The serial device has two channels, and a hardware rx and tx buffer for each channel. Basically, at any given time , you can read/write to either channel on the device.
I am trying to read data from a channel, validate it (and perhaps use some of the data), and then transmit it. Reads are accomplished with iotctl calls to the device, while writes are accomplished with a call to the write() system call.
The main issue I have is with data throughput. I'd like to have an individual thread handle reading and writing for each channel (i.e., a read thread and write thread for each of the two channels). However, I have hit a snag. Everything on the device, from Linux's perspective is accessed via one single device, and I'm not sure that Linux notes that the device has multiple channels.
As a result, currently I open a single file descriptor to the device and perform my reads and writes serially. I'd like to go to the threaded approach, but I'm wondering if concurrent ioctl() and write() calls would cause issues. I understand that read() and write() and not thread safe, but I was wondering if there's any way around that (perhaps calling open() twice, one with read privileges, one with write privileges).
Thanks for your help. Also, I want to avoid having to write my own driver, but that may be an inevitable conclusion...
Also, as a side note, I'm particularly concerned that the device has extremely small hardware buffers. Is there any way to determine how much space the OS uses for a software buffer for data? That is, can I determine whether or not the OS has it's own buffer that is used to prevent overflow of the hardware buffer? The device in question is an I2C UART Bridge.
You can use semaphore to make a mutual exclusion between read/write thread
sem_t sync_rw;
/*init semaphore */
err=sem_init(&sync_rw,0,1); /* shared between thread and initialized with 1 */
if( err != 0 )
{
perror("cannot init semaphore \n");
return -1;
}
in thread write function you do this :
sem_wait(&sync_rw);
write(...)
sem_post(&sync_rw);
same for thread reader :
sem_wait(&sync_rw);
iotctl(...)
sem_post(&sync_rw);
finally :
sem_destroy(&sync_rw);
Related
I want to write C code for an embedded system such that the data received at the RS232 port should be read continuously without creating a separate "RS232 TASK" for reading the data.
Can anyone help me with this?
I just need a basic approach for reading data without task creation.
Identify the function that tells you if some data was received. Commonly it returns a boolean value or the number of received bytes. (BTW, most protocols on RS232 allows 5 to 8 data bits per transmission.)
Use that function in a conditional block to call the next function that actually reads one or more received bytes. In case that nothing was received, this prevents your loop to block.
Example (without knowing how the functions are named in your case):
/* any task */ {
for (;;) /* or any other way of looping */ {
/* do some stuff, if needed */
if (areRs232DataAvailable()) {
uint8_t data = fetchRs232ReceivedByte();
/* handle received data */
}
/* do some stuff, if needed */
}
}
I would ask why you think reading data from a UART (which I assume is what you mean by "RS-232") requires a task at all? A solution will depend a great deal on your platform and environment and you have not specified other than FreeRTOS which does not provide any serial I/O support.
If your platform or device library already includes serial I/O, then you might use that, but at the very lowest level, the UART will have a status register with a "data available" bit, and a register or FIFO containing that data. You can simply poll the data availability, then read the data.
To avoid data loss while the processor is perhaps busy with other tasks, you would use either interrupts or DMA. At the very least the UART will be capable of generating an interrupt on receipt of a character. The interrupt handler would place the new data into a FIFO buffer (such as an RTOS message queue), and tasks that receive serial data simply read from the buffer asynchronously.
DMA works similarly, but you queue the data in response to the DMA interrupt. That will reduce the interrupt rate, but you have to deal with the possibility of a partially full DMA buffer waiting indefinitely. Also not all platforms necessarily support UART as a DMA source, or even DMA at all.
Say you have a process that receives a large file from a server.
If you do not perform a recv() call does it stay on a buffer of your ethernet controller forever?
If another process needs to receive data and the buffer is full from another process does it need to wait until the other process performs a recv() or the buffer times out?
If you have multiple process sending and receiving data does it have to wait until the buffer is empty.? Or can it multiplex it and keep track at the driver level or some part of the socket library?
edit: spelling
If you do not perform a recv() call does it stay on a buffer of your
ethernet controller forever?
No, data never stays in the ethernet controller's buffer for very long; the kernel will read the data out of the Ethernet controller's buffer and into your socket's buffer (in the computer's regular RAM) as quickly as it can. If your socket's buffer is full, then the incoming data will be discarded.
If another process needs to receive data and the buffer is full from
another process does it need to wait until the other process performs
a recv() or the buffer times out?
Each socket has its own separate buffer in the computer's main RAM, and each process has its own socket(s), so processes do not have to wait for each others' buffers to empty.
If you have multiple process sending and receiving data does it have
to wait until the buffer is empty.?
See the answer to question 2, as it answers this question also.
This is a bit of perfectly spherical chicken in a vacuum type of answer. But your question is very broad and has a lot of what ifs depending on the NIC, the OS, and many other things.
But lets assume your are on a modernish full-blown OS, with modernish ethernet controller.
No. That all handled by the by kernel and protocol stuff. The kernel can't let the buffer on the network controller fill up while it's waiting for you. Otherwise it will block other processes from accessing the network. So it will buffer it up until you are ready. For some protocols there are mechanism where one device can tell the other device not to send any more data. (ei. TCP Receive Window Size, once the sender sent that amount of data it will stop until the receiver acknowledges it somehow)
It's basically the same answer as above, the OS handles the details. From your point of you, your recv() will not block any other processes ability to recv().
This is more interesting, modern NIC are queue based. You have n-number of transmit/receive queues, and in most cases, filters can be attached to them. This allows the NIC to do a lot of the functionality that normally would have to be done by the OS (that's called offloading) but back to the point. With these NICs, you have have multiple I/O without multiplexing. Though generally, especially on consumer grade NIC, the number of queues will be pretty low. Usually 4. So there will be some multiplexing involved.
I'm fairly new to VxWorks OS and hence wouldn't mind explanations in case I differ in my understanding of things under the hood when they differ from more traditional OSes like Linux and the likes. With that out of the way, let me begin my actual question.
I am trying to create a loop-back test for testing changes I made to the serial UART driver on the board. Since I do not want to use a cross cable to actually short two UART ports externally, I've connected both of those ports to my dev machine. One configured as an output port from the dev machine's perspective (and consequently as an Input port on the board) and the other an input port (an output port on the board). I am actually doing the loopback using a shared memory buffer which I am protecting using a mutex. So there are two threads on the board, one of which reads from the input port, copies data to the shared memory and the other reads from the memory and sends it over the output port.
And I am using regular open, read and write calls in my VxWorks application (by the way I think it is part of the application code as I call the functions from usrAppInit.c not withstanding the fact that I can even call driver routines from here! (Is it because of a flat memory model vis-a-vis Linux?? Anyhow).
Now I these ports on VxWorks have been opened in a non blocking mode and here's the code snippet which configures one of the ports:
if( (fdIn = open(portstrIn, O_RDONLY | O_NOCTTY, 0) ) == ERROR) {
return 1;
}
if(((status = ioctl(fdIn, FIOSETOPTIONS, OPT_RAW))) == ERROR)
{
return 1;
}
/* set the baud rate to 115200
*/
if((status = ioctl(fdIn, FIOBAUDRATE, 115200)) == ERROR)
{
return 1;
}
/* set the HW options
*/
if((status = ioctl(fdIn, SIO_HW_OPTS_SET, (CS8 | 0 | 0))) == ERROR)
{
return 1;
}
And similarly the output port is also configured. These two are part of two separate tasks spawned using taskSpawn and have the same priority of 100. However what I am annoyed by, is that when I write to the in port from my dev machine (using a python script), the read call on the board get's sort of staggered (I wonder if that's the right way to refer to it). It is most likely due to the short availability of hardware buffer space on the UART input buffer (or some such). This is usually not much of a problem if that is all I am doing.
However, as explained earlier, I am trying to copy the whole received character stream into a common memory buffer (guarded by a mutex of course) which is then read by another task and then re-transmitted over another serial port (sort of an in memory loopback if you will)
In lieu of that aforementioned staggering of the read calls, I thought of holding the mutex as long as there are characters to be read from the Inport and once there are no chars to be read, release the mutex and since this is VxWorks, do an explicit taskDelay(0) to schedule the next ready task (my other task). However since this is a blocking read, I am (as expected) stuck on the read call due to which my other thread never gets a chance to execute.
I did think of checking if the buffer was full and then doing the explicit task switch however if any of you have a better idea, I'm all ears.
Also just to see how this staggered read thing works from the perspective of the kernel, I timed it using a time(NULL) call just before and right after the read. So surprisingly, the very first chunk shows up a number, every other chunk after that (if it's a part of the same data block coming from the outside) shows 0. Could anyone explain that as well?
Keen to hear
I don't have 50 rep points for commenting, but without a loopback cable attached, the only way you can arrive at testing serial loopback behavior is to switch the uart into loopback mode. This often means making changes to the specific hardware part driver.
I'm having one of those mental-block moments in trying to implement what should be a fairly simple routine, for serial IO.
The situation is that we have an embedded linux board (think Pi / Beagle) which communicates with another device on the UART using the standard Linux termios code.
The problem is we have two clashing requirements:
In the transmit direction, we want to block on the Linux messaging queue msgrcv() function until a message arrives for us to send.
In the receive direction, we need to wait/block for an incoming message (which can have a termination character for canonical mode operation).
Tx and Rx are asynchronous and not related to each other - either could want to happen at any time.
Polling would be a pain as it introduces an overhead in CPU cycles and delay in response.
One approach would be to split this into two threads, one handling the Tx and blocking on msgrcv(), and the other on the Rx and blocking on UART read() in canonical mode - but that would introduce the pain of setting up semaphores between the Tx & Rx processes and both having to repeatedly open & close the serial port, and the Rx thread would presumably end up having to poll the semaphore in case the Tx wanted control, putting us back to polling.
I would stress that I'm relatively new to all this Linux stuff, so am entirely ready to be shown the bleeding obvious solution/method/call/operation that I'm missing here.
Is there some way to be blocking on the UART Rx but still able to transmit on demand?
In the end I followed Martin James' suggestion, doing something like this:
fd = open(serial_port);
pthread_create(TxThread, fd);
pthread_create(RxThread, fd);
Linux seems entirely happy with this, both threads do their jobs with no problem.
It might be simpler to have a thread for the msgqueue, in combination with a pipe, so you can wait on the pipe and the serial port using select()/poll()/... in your serial tx/rx thread.
Fortunately you don't even have to do that. It turns out that on Linux (but not on all other unixes!) message queue IDs are also file descriptors, so you can just wait on the message queue and the serial port in one poll() call. You only have to check which one is active to figure out what you have to do.
See also Why is there no poll/select-like mechanism for message queues.
I am working with serial port programming and i have following doubt please clear it
Does Read/Write system call reads and writes data directly from/to UART buffer?
Which buffer does Rx and Tx of "/proc/tty/driver/serial" refers to.
Please clarify.
read()and write() access data via a so called file-descriptor return by a call to open().
open() gets passed in the name of a serial device.
Serial devices could be named "/dev/ttySX" with X={0..N}.
Why are you worried about:
Does Read/Write system call reads and writes data directly from/to
UART buffer?
The kernel takes care of the actual read/write calls to a file regardless if its a device or a data file, in the case of device, IOCTL calls will be made to the device in question and handle it behind the scenes.
Which buffer does Rx and Tx of "/proc/tty/driver/serial" refers to.
Again, that is handled by the kernel and related IOCTL calls to the serial driver by way of device descriptor to the actual serial port.
If still in doubt, a refresher on serial port programming under Un*x environments such as Linux/FreeBSD would be recommended.