Opened 12 years ago

Last modified 20 months ago

#8789 new bug

[USB serial] only performs BSerialPort::Read() in 'blocking' mode

Reported by: dsuden Owned by: nobody
Priority: normal Milestone: R1
Component: Kits/Device Kit Version: R1/Development
Keywords: Cc: ttcoder
Blocked By: Blocking:
Platform: x86

Description

We're finding that /bin/write works fairly well when communicating with serial devices, but /bin/read never returns when invoked from Terminal. You have to hit CTRL-C to abort it. So software written to communicate with the serial driver doesn't work for us.

Change History (21)

comment:1 by dsuden, 12 years ago

Component: - GeneralDrivers/TTY
Owner: changed from nobody to bonefish
Platform: Allx86
Version: R1/alpha3R1/Development

comment:2 by bonefish, 12 years ago

Please specify what kind of serial device you're talking about. We have two different drivers: One for USB serial devices -- which is rumored to work -- and one for the standard serial ports fewer and fewer mainboards provide -- this one is known to be incomplete.

comment:3 by ttcoder, 12 years ago

Cc: degea@… added

Dane's using USB-serial.

comment:4 by bonefish, 12 years ago

Component: Drivers/TTYDrivers/USB
Owner: changed from bonefish to mmlr

comment:5 by siarzhuk, 12 years ago

Which type of USB-RS232 hardware (FTDI/PL2303/SiLabsCP2101) do you use? Does this read session receive any data send by device it connected to?

comment:6 by mmlr, 12 years ago

It is not just rumored to work, it actually does, I use it to control my tv via a usb to serial adapter. Note however that when you just use the device node to read from and write to, you are using standard serial line settings that may very well not apply to the device you are talking to. If you want to use it that way you have to configure the line first, using stty (usually with the 'sane' option and then specify the appropriate baud rate). I am not sure that the settings are carried over when reopening a new file handle though, so this might not work. If it does, you can also just redirect anything into and out of the serial device node.

Alternatively, a small wrapper program using the BSerialPort API should work. Look in src/tests/kits/device/bserial, there's serial_io that pretty much does that. You could modify it to the settings you need, or better yet implement command line arguments to set them.

comment:7 by ttcoder, 12 years ago

Wondering about stty -- since /bin/write works (at least for a dozen calls or so, it tends to become troublesome after a while), it probably means the default settings of bauds/parity/etc are correct? why would write work and not read ? Maybe we can't assume so though, let's test it --

Dane's busy for a few days, so I'll edit a summary of the suggestions made so far for him to read when he's back:

  • look closely at the USBserial device to see exactly what kind of hardware vendor/model that is.
  • (EDIT) move the USB-to-serial device to a different USB port (of a different type: ohci/ehci/uhci or something)
  • test again in Terminal, invoking stty with proper settings before invoking write and read; we'll do it together via BeShare, I gotta look up its man page first and get up to speed with it.
  • test again with TuneTracker, making sure it's a Haiku build of TT, not a BeOS build (doubtful it has an impact, but can't rule it out since this factor has an impact on media_server code so why not on code linked against libdevice.so too)

A general note @ everyone: whatever USBserial bug exists for us, it is not a Terminal-only thing, it all started when trying to make TuneTracker work with BroadcastTools(tm) switchers :-) We have edited this ticket focusing on /bin/read so that the bug could/might be reproducible without using TuneTracker, but the original problem lies in C++ code, not bash and commands.. We are unable to access the USBserial device in any way, either with bash scripts (which admittedly we never tried for serial stuff before) nor with TuneTracker (which has always been working in BeOS).. I'm not sure we can rule out a C++ problem though, maybe I should post a minimalist C++ test case that reproduces the bug ? Will look into src/tests/ when Dane is back. To be more rigorous I'll probably build it in BeOS too, so that I can come back to you saying "I tried in both -- this works in BeOS but not in Haiku".

We gotta beat that one...

Last edited 12 years ago by ttcoder (previous) (diff)

comment:8 by phoudoin, 12 years ago

May I ask where could I find /bin/read & /bin/write source code? Do I miss something or these tools aren't included out-of-box in Haiku images (beside the shell builtin "read" command, I mean)?

Last edited 12 years ago by phoudoin (previous) (diff)

comment:9 by ttcoder, 12 years ago

Summary: /bin/read doesn't work for serial communicationread doesn't work for serial communication (neither bash's nor BSerialPort::Read())

Sorry, bad habit of mine -- I prefix commands (and "commands") with "/bin" when talking about them.. I'm not in Haiku ATM but running which (or whence) will probably show it's a Bash built-in, not a separate command indeed. Modified the ticket title accordingly. Anyway we need Dane's input to solve this reading issue.

(edit)
for the sake of reproducibility: our writing is done with

echo *0.. > /dev/ports/usb0 ..etc (not /bin/write, there is no such command of course oops)

and reading tests were done with just

read var < /dev/ports/usb0

if I recall correctly.
(~edit)

.. ttcoder (waiting for Dane's return)

Last edited 12 years ago by ttcoder (previous) (diff)

comment:10 by ttcoder, 12 years ago

Dane's back! Answers:

  • Dane says there is starcom icusb232 int1 written on the USB-to-serial adapter that we use to connect our BT box device.

I'm holding back the results of Tunetracker testing for now: I'm starting to suspect a sync/async reading issue, as only the first Read() call returns, but the thread remains stuck on the next (second) Read() call. Will work on a mini test case in C++. (it's somewhat the same for Terminal: some "read" calls return, some don't).

	mPort.SetDataRate(B_9600_BPS);
	mPort.SetDataBits(B_DATA_BITS_8);
	mPort.SetStopBits(B_STOP_BITS_1);
	mPort.SetParityMode(B_NO_PARITY);
	mPort.SetFlowControl(B_NOFLOW_CONTROL);

	// ### Set to 'false', otherwise Read() will leak 130 bytes per call on BeOS !
	mPort.SetBlocking( false );  <------

Will keep this ticket updated.

comment:11 by ttcoder, 12 years ago

So it turns out SetBlocking(false) is simply not enforced at all. Didn't realize that before due to the unit sending data before Read() was first invoked and due to plain simple clumsyness on my part.

So I guess this ticket description should be worded quite differently, as the bug is more benign than I feared. Still no good for us though, as my tunetracker code is not structured at all for sync serial reading :-) I really need non-blocking reading. So if non-blocking cannot be fixed (easily/soon) I'll have to restructure my code to run in an independant thread, and ifdef-out those changes (because BeOS build still needs non-blocking code, otherwise it leaks memory). Anyhow here's the minimum code to reproduce the bug I guess:

/*
	gcc -ldevice miniserialtest.cpp  && ./serialtest
*/

// libroot.so
#include <stdio.h>

// libdevice.so
#include <device/SerialPort.h>


int main( int argc, char ** argv )
{
	printf("USB-serial test-case running (argc=%d)\n", argc);

	BSerialPort mPort;
	mPort.SetDataRate(B_9600_BPS);
	mPort.SetDataBits(B_DATA_BITS_8);
	mPort.SetStopBits(B_STOP_BITS_1);
	mPort.SetParityMode(B_NO_PARITY);
	mPort.SetFlowControl(B_NOFLOW_CONTROL);
	mPort.SetBlocking( false );//true ); 

	if( mPort.Open("/dev/ports/usb0") <= 0 )
	{
		perror( "can't open port" );
		exit(0);
	}

	mPort.ClearInput();
	mPort.ClearOutput();

	char buf[2] = { 0 };

	printf( "before Read()...\n" );
	int numBytes = mPort.Read( buf, 1 );
	printf( "after Read(), got %d...!\nOn a usb-serial device you will only see this line after traffic gets sent to the serial port, meaning mPort.SetBlocking( false ) is NOT enforced\n", numBytes );
}

Update: just realized that BSerialPort::HasDatapending() might save my skin !! (from rewriting my code to work-around the bug I mean).

comment:12 by ttcoder, 12 years ago

(sorry, meant to say BSerialPort::WaitForInput()). Anyway no luck, it did not save the day.. Findings:

  • WaitForInput() seems to be unusable, no matter the use scenario it returns 0x80000005: Invalid Argument
  • Looking at the headers I noticed there exists a NumCharsAvailable() too, but it's marked as TODO: implement
  • Read() continues to work fairly well... but synchronously (i.e. blocking, even if the port is configured as SetBlocking( false))

Now for the weird one: there seems to be a side-effect of the driver on my userland code of some sort; namely, an if(variable>=2) statement in my code is not executed despite the variable being == 2, unless I make neutral changes to the code adding printf()s;

Rephrasing:

  • the first line looks like this:
	printf("USB-serial test-case running (argc=%d)\n", argc);

and correctly prints "argc=2" when I pass an argument.

YET an if() at the end of main() looking like this:

		if( argc >= 2 )
		{
			char buf[128] = { 0 };
			
			puts( "Enter string to send to BT box (e.g. *0U or *0111 or *011A ...), followed by <enter> :" );

is never entered... until after I add some tracing printf()s higher-up in the program. Does not look like memory corruption per se. I've looked for beginner-style mistakes (we're all subject to misplaced semicolons when tired eh? :-) in my code and there's none. Go figure. Not ruling out a mis-communication between me & Dane though (it occured on his setup). Another possible theory: maybe the if() is entered but the puts() fails to send text to Terminal for some reason, as a side-effect of all the ioctl()s being executed behind the scenes, and these ioctl()s wrongs are righted only if peppering my code with printf() calls...

Anyway bottom-line for now, all the doors are closing on me, except the rewrite-my-code-to-be-synchronous one. Dang.

comment:13 by ttcoder, 12 years ago

Summary: read doesn't work for serial communication (neither bash's nor BSerialPort::Read())[USB serial] only performs BSerialPort::Read() in 'blocking' mode

Whew! Found a successful work-around. I simply add this after ffd=port.Open():

#ifdef __HAIKU__
	int flags = fcntl( ffd, F_GETFL );
	fcntl( ffd, F_SETFL, flags | O_NONBLOCK );
#endif

I got the idea while reading the SerialPort.cpp implementation. This fixes both the serial-test-case outlined above, and tunetracker itself: the Read() calls are now non-blocking, everything works as it should.

Also had to make my code more flexible for parsing lines, since the USB-serial driver appears to convert some \r to \n and vice-versa, for some reason.

Now we're back in business. This ticket can be set to low priority if nobody else needs it to be fixed.

comment:14 by leavengood, 12 years ago

I'm glad you figured it out, I'm sorry we couldn't be more help. So I guess SetBlocking needs to be forwarded to the USB serial driver? If you can narrow down the line ending issues too that might help this get fixed. But if your code works for you now I guess your work is done :)

comment:15 by ttcoder, 12 years ago

No worry ryan Dane and I are just glad it works ;-) Here's the code from the horse's mouth (well technically it's from a source file but whatever!), with the part used in BeOS commented out, and the part that works in both BeOS and Haiku just beneath it:

......
//AMENDED in 4.6.3.5:
//		if( mInputBuf[mOffset] == 0x0A )
		if( mInputBuf[mOffset] == '\n' || mInputBuf[mOffset] == '\r' )
......

in reply to:  13 comment:16 by phoudoin, 12 years ago

Replying to ttcoder:

Also had to make my code more flexible for parsing lines, since the USB-serial driver appears to convert some \r to \n and vice-versa, for some reason.

Sounds like BSerialPort object (or usb_serial driver) is not setting the port in canonical mode by default. I will look into it why.

comment:17 by dsuden, 10 years ago

We've been using USB2Serial with no problems for quite awhile...maybe this one could also be taken off the "active duty roster." :-)

comment:18 by ttcoder, 10 years ago

The underlying problem is still there (maybe a proper fix will still be needed some day, by izcorp ? I hear they use usb2serial support, and they explicitely filed a ticket about usb2serial a couple months ago here -- very glad they've just gone public with their involvement in Haiku BTW ;-) but yes we've been using the workaround mentionned above successfully for years so the fix is low-priority for us.

comment:19 by waddlesplash, 20 months ago

Component: Drivers/USBKits/Device Kit
Owner: changed from mmlr to nobody

comment:20 by ttcoder, 20 months ago

Cc: ttcoder added; degea@… removed

comment:21 by bipolar, 20 months ago

FWIW, the test case from comment:11 also blocks when opening /dev/port/pc_serial1 (with a loop-back cable connected to it).

Modifying Haiku's BSerialPort::_DeviceControl() so it (un)sets O_NONBLOCK depending on the state of BSerialPort's fBlocking (using fcntl as in comment:13), makes that test run as expected (for both SetBlocking() true/false values).

Guess I found one of the many reasons why my old serial_mouse driver misbehaves in Haiku, but works otherwise OK on BeOS. (edit: or at least one of the reasons why my tests trying to find the differences only managed to confuse me further :-D).

Last edited 20 months ago by bipolar (previous) (diff)
Note: See TracTickets for help on using tickets.