Forcing raw serial mode in C - linux - c

Not sure how to word the title, but what I'm trying to do is test my micro controller with my Linux PC to ensure data is correct. After hours of searching, I found out that the stty command can change how data is managed through the serial port and it turned out that by default if xon or xoff characters are received from the port, they don't get displayed. At first I thought my computer was too slow that I was losing characters at 57.6Kbps but that wasn't the case.
Back in the day when I was playing with the serial mouse in QuickBasic for DOS, I could use this command to start the serial port:
OPEN "com1:1200,n,7,1,op0" for binary as #1
So what I want to do now is create something simple in C that would allow me to open up the serial port in the rawest mode possible. I want it so that whatever data I give to it is sent to it unmodified. I also want to receive data unmodified. so if the controller decides to send a character the PC would recognize as a special control code, I still want to see the character, not have the PC go funny just because a character matches a control code.
One idea I thought of is to create a fork to the stty program and use nearly every (50+?) parameters added to the program making the requirement of program stack space a bit high.
Another idea I thought of is to do direct I/O with the port address itself (using inb and outb) but I'm not sure if the kernel would run those commands through anything else before the data reaches the port, but I'd rather use that as a last option in case I ever replace my computer and the serial port value changes (or becomes a serial port made through USB to serial converter hardware).
so rather than inb and outb and those variants (like inw), and without executing stty with specifying 50+ parameters in my program, is there a function in C I can use (without requiring a special library not included with a standard linux distribution) to force the serial port device as a raw device so I can do any I/O on it without the kernel modifying or dropping data?

Related

How to set the baud rate of non-serial ttys?

A bit of a strange question here, but I'm wondering if there's any standard way to set the baud rate of non-serial interfaces, e.g. an SSH session to a Linux machine?
There are lots of examples of setting the baud rate for serial ttys in Linux, such as this:
struct termios slow;
tcgetattr(STDOUT_FILENO, &slow);
cfsetispeed(&slow, B1200);
cfsetospeed(&slow, B1200);
tcsetattr(STDOUT_FILENO, TCSANOW, &slow);
However, I've never been able to get any to work for me when working with ttys like an SSH connection. I'm not sure if it's just not supposed to work in those contexts, or if there's something else/different that needs to be done here.
The example compiles and runs just fine, but everything printed out to the screen is still at full speed. I've confirmed none of the functions runs into any issues, tried different baud rates, tried STDIN and STDOUT, set different flags in some of the examples floating around - none of that has any effect.
The stty command itself also seems to have no effect.
Is it possible to set the baud rate of an SSH session in the first place? Maybe it's not, and that's why it doesn't work.
If not, is there any way to achieve this? Short of a brute force solution like:
for each char in str:
print char
sleep 0.03
In case it wasn't obvious from the question, no, there's not a super useful application of this, but sometimes it's useful to simulate different baud rates without actually using a physical serial connection or anything like that, and it would be useful to handle the connection speed at the terminal layer or in the kernel somehow, rather than doing something dumb like printing everything out character by character. Can this be accomplished?
This must be accomplished entirely server-side (the connected SSH client should not have to do anything special), and the speed must be able to be set on demand just like with cfsetospeed.
One solution that does work is to use a pseudoterminal, which relays output from the PTY master to the actual file descriptor (e.g. a socket) one character at a time, with usleep inbetween each call to write.
However, this results in doing dozens to hundreds of calls to write() per second, which uses quite a bit of CPU. I doubt there's any way to space out writing a buffer gradually in the kernel, so this kind of system call overhead might be unavoidable, but if there was a more efficient way to do this, that would be great.

creating a serial loopback under VxWorks

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.

Targeting an Xbee using AT Commands Mode

I am trying to get one Xbee to talk to another. I understand that I can use putc to broadcast to all Xbees on a network but I want to target one specifically. I know that I need to use the hardware address on the XBee to do this and by using something called AT Commands mode but I don't think I understand it. For example, I have seen that to get into AT Commands mode I need to send '+++', however, I don't understand what to do. I tried this:
xbee.printf('+++');
but don't know where to look for some kind of message in response.
I don't understand this mode and can't seem to find anything which gives me an example for my situation or a relevant one.
I am using a normal Xbee on an FRDM-K64F.
Worked it out.
To do this, you first need to do:
xbee.printf("xxx");
This puts you into AT Command mode and you can then run AT Commands. You need to wait at least 1 second before and after going into AT Command mode for this to work:
wait(1)
To send to a specific Xbee, you need it's 16-bit address and this can be found from the underside of the Xbee (although there is an AT Command to get this). The first 8-bits are the Xbee's high address (this is the same for all Xbees) and the second is it's low address and you need to set both of these using AT Commands before sending to the specific Xbee will work. To set these you must do the following:
xbee.printf("atdh 0013A200\n\r");
xbee.printf("atdl 12345678\n\r");
You must do the \n\r part as this simulates an Enter button press which is needed to actually run the command.
The atdh command sets the high address and the atdl command sets the low address. Once this is done, your sending Xbee will be configured to send to a specific Xbee and will not broadcast to all on the network. So when you now do:
xbee.putc('Y');
it will go to the Xbee who's address you have set.
You may have to wait 10 seconds after running those commands because I don't think I/O works in AT Command mode (I have not worked out yet how to exit AT Command mode yet but will update when I do).

Init a serial communication with c library open() causes TX to send one bit on RPi

I'm trying to set up a serial communication between the RPI and an FPGA. However, there is an issue when using the standard C library open() to init the serial interface: I'm using a scope to monitor what is sent and received via the RX and TX lines. A call to open causes the TX line of the RPI to go low for the length of one bit. I do not see this behavior with other computers/linux PCs. The point is, the FPGA assumes a valid transmission, since he thinks it's a start bit, but it's not.
I checked with minicom installed on the RPI. Same thing. Starting minicom causes the TX line sending one bit. Once minicom has started, the communication runs as expected and all bytes have the correct frame size. Is there any way to suppress the TX line going low upon the open call to init the serial communication? Is this an expected behavior?
This is a super far-fetched hunch, but this code seems a bit suspicious, from the pl011_startup() function in the PL011 serial port driver:
/*
* Provoke TX FIFO interrupt into asserting.
*/
It seems as if it's twiddling the TX line when starting up the port, which would explain the pulse you're seeing. More investigation would surely be needed before concluding this is what happens, of course.
So, I guess my "answer" boils down to: that sounds weird, perhaps it's something with the driver?
Of course, one way of working around this is to apply some care in the FPGA end, assuming you have more control over it. "Proper" framing would take care of this, and make it clear that the spurious send can be discarded.
UPDATE: I meant that if "proper" messages were to be always framed by some sequence of bytes, the FPGA might be able to discard invalid ("unframed") data anyway, and thus become immune to the random pulse. For instance, messages could be defined to always start with SOH (start of header) or SOT (start of text) symbols (bytes with the values 0x01 and 0x02, respectively).

Send file over serial port with Linux and C

I'm developing an application that reads data from a serial port and sends it over a TCP connection, and vice versa. Unfortunately, while reading data from serial port, it never stops. It does not detect EOF mark, nor EOL or some other special character.
So, how could i detect an end of file (or "end of connection") over serial port in C and Linux?
Depends on how much control you have over the protocol used for the serial link. Unless the files implicitly include some end-of-file marker (and as I've understood your post they don't), you need to implement some kind of communication protocol in order to transfer files.
Some of the most simple procols used way back in the BBS days were XMODEM and it's derivatives. They may be simple enough for you to use.
If you have a full-blown computer at the other end of the serial line, it would probably be far simpler just to set up an PPP link over the serial line and do the communication over TCP/IP.
Serial link only sends bytes. There is no packet frame, no error checking so you can't send files reliably over raw serial link. You need to use some protocol like XMODEM, KERMIT etc.
It's not trivial to implement such protocol. It may be easier to run TCP/IP over the link if the other end is also a computer. Please check out SLIP or PPP.
The serial port gives "end of file" on a hangup condition, which is signalled out-of-band by the modem control lines (dropping of DCD). If you're connected with a null-modem, that'll never happen.
Use a framing mechanism, like the other answers have suggested. You may not need to go the whole hog with something like ZMODEM though - just prefixing your file with the file size and a CRC32 checksum should do, if the link is reasonably error-free and 8-bit clean.
Check your serial port configuration:
stty -F /dev/ttySx -a
Set cooked link instead of raw link:
stty -F /dev/ttySx cooked
cheap and dirty solution:
on linux, unix or osx just run 'screen -L <serial_device> <baudrate>'. At the other end write the output of your file to the console, in raw mode. -L logs it to a file the contents of which will be the file you transfered from the other end. Check the contents with xxd to verify they match what you had at the source.

Resources