ecs87 ecs87 - 1 year ago 122
C# Question

c# SerialPort: how to send "0 byte" transfers (AKA ZLP: zero length packet)?

I've done some playing around with SerialPort (frustratingly so) and have finally hit a point where I absolutely have no idea why this isn't working. There's a USB CDC device that I'm trying to send hex commands to, the way I'm doing this is over the COM port interface it exposes. I can handshake with the device, when I say HI it replies with HI back, but then I send another command to it which must be followed by a zero byte packet or else the device stops responding altogether. Keep in mind, this zero byte packet has ABSOLUTELY nothing in it, meaning it doesn't have a \0 or a 0x00 or 0 or even a null (SerialPort throws an exception on null).

Now, one way I was able to circumvent this was to use libusbdotnet. I accessed the CDC device directly instead of the COM interface, set the endpoints correctly and sent hex commands like that. I'm able to successfully send "0 byte" packets using this method with the following c# code:

string zlpstring = "";
byte[] zlpbyte = Encoding.Default.GetBytes(zlpstring);
ecWrite = writer.SubmitAsyncTransfer(zlpbyte, 0, zlpbyte.Length, 100, out usbWriteTransfer);

zlpbyte is the buffer, 0 is the offset, zlpbyte.Length is the packet length in bytes, 100 is the timeout, and out usbWriteTransfer is the transfer context.

When I use this same method on the COM port:

string zlpstring = "";
byte[] zlpbyte = Encoding.Default.GetBytes(zlpstring);
_serialPort.Write(zlpbyte, 0, zlpbyte.Length);

the USB logger reports that absolutely nothing was sent. It's as if the COM port is ignoring the zero byte transfer. Before it's mentioned that "you cannot do this", there's various programs out there that can send a zero-byte packet to this exact device's COM port without doing ANY driver manipulation. This is what I'm going for, which is why I'm trying to ditch libusbdotnet and go straight to the COM port.


After some more toying around and a different USB logger I don't find zero bytes being sent but rather this:


I think this may be the issue. If a 0 byte was being sent then I assume it would show up as:

IRP_MJ_WRITE > UP > STATUS_SUCCESS > (blank) > (blank)

My program is sending back a response of 01 00 00 00, however while logging another successful program it's SETTING the wait mask:


If my assumptions are right, this question might've just turned into how do I set a serial port's/COM port's wait mask? There's absolutely nothing about this in the c# SerialPort class...which is why I can now see why so many articles called it "lacking". I also took a look around c++: this also does not seem to cover the wait mask. Using the USB filter libusb is starting to look a lot more pleasing each minute...(although I'm going to question myself forever why sending a zero byte works there but it doesn't over SerialPort).


I'm a moron. It was definitely a setting that the manufacturer probably didn't figure anyone would ever touch nor know how to set:

#define EV_RXFLAG 0x0001
SetCommMask(hSerial, EV_RXFLAG);

I then saw this over the USB logs:


Bingo. The RXFLAG was originally set to 0x0002. I couldn't find a way to change this in C# yet. So I had to do with some C++ code for now. It totally works, and sends the "zero byte" like it's supposed to without me actually sending it from the code. This setting I assume was the "handshake" method between my device and whatever else it's interacting with in Flash mode. Hope this helps someone else out there whose COM/Serial device is rejecting/discarding zero byte packets yet requiring ZLP at the same goofy?!

Answer Source

have you tried to concatenate an extra new line or carriage return or both at the end of the data?

I would say add a 0xA (new line), or 0xD (carriage return), or both 0xA and 0xD to the end of your byte array and see if you get something.

byte[] zlpbyte = new byte[1] {0};

_serialPort.Write(zlpbyte, 0, 1);

[Update] Based on our discussions, it appears that you are trying to have control over the control signals of the serial port. I have not tried it before but I can see that it is possible to set the control signals (if i understand the source properly) into certain states.

Try to set the Handshake property

public enum Handshake

I am not sure exactly how it affects the IOCTL settings but it should be able to affect it somehow I believe