2

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);
....snip
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.

EDIT:

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

IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_WAIT_ON_MASK)

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:

IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_SET_WAIT_MASK) DOWN STATUS_SUCCESS 01 00 00 00

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++: https://msdn.microsoft.com/en-us/library/aa363214(v=vs.85).aspx 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).

SECOND EDIT:

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:

IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_SET_WAIT_MASK) DOWN STATUS_SUCCESS 01 00 00 00

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 time...how goofy?!

ecs87
  • 23
  • 6
  • Why are you using default encoding? Default uses ASCII which filters non printable characters like \0. Try UTF8 Encoding. – jdweng Aug 10 '16 at 17:57
  • What does the com port say when you create byte[] zlpbyte = new byte[0]; and send zlpbyte? – Mong Zhu Aug 10 '16 at 18:01
  • Thanks for the replies jdweng and Mong Zhu. If I change encoding to UTF8 it treats characters the exact same (which is confusing...). Absolutely nothing changed after I compiled with Encoding.UTF8.GetBytes(zlpstring). The same thing happens with "byte[] zlpbyte = new byte[0];" as anything else that doesn't have a byte length of 1 or more...the port just discards it and it never shows up in the USB logger. – ecs87 Aug 10 '16 at 18:28

1 Answers1

0

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
  {
    None,
    XOnXOff,
    RequestToSend,
    RequestToSendXOnXOff,
  } 

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

CJC
  • 795
  • 8
  • 25
  • Thanks for the reply CJC. I tried both of these in both UTF8 and ASCII encoding. 0xA returns 10 bytes and 0xD returns 13 bytes. All of these bytes return 00. I'm trying to send a 0 byte packet with absolutely nothing (null I guess?) in the packet. I know it seems incredibly odd, but it's how the device determines when a file segment is finished uploading. – ecs87 Aug 10 '16 at 18:30
  • from your code, zlpstring = "" is not a null. it is a space ascii which is equivalent to 0x20. if you are trying to write a null to the device. Why dont you do something like this. See my updated answer – CJC Aug 10 '16 at 18:47
  • I think your explanation of saying send 0 bytes is not clear. I have a feeling you mean, you want to send a byte of information with data value of 0 or null. Right? Anyway my updated answer above. Please let me know if that is what you are trying to do, and if you like my answer give me an upvote and tick! thank you – CJC Aug 10 '16 at 18:50
  • Hi again CJC, I'm sorry if the explanation isn't clear, as the thought of a zero-byte transfer seems like something I'd never have to think about nor breakdown. I've always thought of a zero-byte packet as having a null identifier, or being 0 or 0x00. I didn't think about it not really having anything at all, which is what the COM port seems to want in order to realize that I'm done sending data and expect a response back from it ("OK, he sent a 0 byte packet to us, let's analyze this data he sent us before the 0 byte packet and send him back a response"). – ecs87 Aug 10 '16 at 19:10
  • I think I want to send a byte with a value of null...but the byte size has to be zero. As the above paragraph mentions, this is a very odd thing to ever think about. I've sent a byte with a value of 0x00 to the device, but it recognizes it as a 1 byte packet with data: 0x00 (your updated example is a perfect example of this). I need a 0 byte packet with data: (non existant if possible?). However, the serial port discards any type of 0 byte length packet that I send it. Unless the packet size is 1 byte or greater it doesn't show up in the logs for the COM port. – ecs87 Aug 10 '16 at 19:10
  • Sorry for the double/triple post. It wouldn't let me post my wall of text in one go. I have another wall of text...but it's just easier to just say there's updates in the OP! – ecs87 Aug 10 '16 at 19:11
  • Indeed it is much easier in this manner, no worries on the triple post. Sorry for the late reply, I was trying going through the serialport source code. Indeed it does seem like what you are trying to do is force the control signals for the serial port. Here is my recommendation; try to use the Handshake property in the Serialport class. I will give more information in the answer – CJC Aug 10 '16 at 21:01
  • Hi again CJC, I don't think it had anything to do with handshake. I tried changing it previously but it never changed the wait mask. The wait mask however...does seem like some kind of handshake mechanism, but isn't supported by the SerialPort class at all. I had to invoke a DLL to call something from c++ into c# and then set the "wait mask" whose real name ended up being EV_RXFLAG. I then saw this over the USB logs: IRP_MJ_DEVICE_CONTROL (IOCTL_SERIAL_SET_WAIT_MASK) DOWN STATUS_SUCCESS 01 00 00 00 which was exactly what the interface was looking for to automatically send ZLPs. Odd, right? – ecs87 Aug 11 '16 at 14:52
  • Hi, I was checking out the usb spec and also saw this link: http://stackoverflow.com/questions/3739901/when-do-usb-hosts-require-a-zero-length-in-packet-at-the-end-of-a-control-read-t. It has become apparent to me that it might be because you are sending less than what it thinks you are sending. thats why you are coming to the conclusion that a zlp is necessary. Please also note that your example of using the LibUSB where you call SubmitAsyncTransfer method. Your string is not of zero bytes. string zlpstring = "". I believe your deduction is not exactly correct. I think it is because – CJC Aug 11 '16 at 18:06
  • the libusb library which calls SubmitAsyncTransfer is doing something extra. one moment while i check the libusb source – CJC Aug 11 '16 at 18:07
  • Or maybe not. I dont know, theres too many layers for me and not enough time for me to check. I still feel that you should be able to get it to work. Are you working directly with the firmware of the usb device that you are communicating with? – CJC Aug 11 '16 at 18:25
  • I tried to look through SubmitAsyncTransfer as well before deciding that it was too many layers as well. And that link you posted is a thread that I've looked at over 10 times. That's exactly describing the issue I was experiencing...but no one was able to provide a way to signal to the device that they were done sending data, therefore the serialport is in a "stuck" state (until you send something valid: terminate what you just sent because the buffer is full). I was able to get it to work with the edits provided in the original post. – ecs87 Aug 11 '16 at 23:13
  • And I'm interfacing with a COM port exposed by Samsung devices' CDC interface when in flash/download mode (turn the phone off, then boot it on using vol down+home+power; press vol up at the warning screen then connect the phone to the computer). I assume that I'm working directly with the firmware, err...bootloader? This state is limited though. I can only read the PIT (partition information file), send data to partitions (some are signed so writing just anything to them will not work), and reboot the phone. I cannot read any part of the flash memory other than the PIT partition. – ecs87 Aug 11 '16 at 23:16
  • I think the thread post that we are both referring to mentions(the last answer) how that the spec says that its not necessary to indicate that the transmission is complete provided that the number of bytes sent corresponds with the number of bytes that the receiver is expecting, There are two conditions that might arise from this. 1, the number of bytes that the reciever is expecting is more than what is actually sent. or 2. the number of bytes that the reciever is expecting is less than what is actually recieved. – CJC Aug 11 '16 at 23:44
  • I still think you just need to append an additional 0x00 to the end of the transmssion and increase the length correspondingly. Although I am not looking at your code directly. I still find that string zlpstring = ""; byte[] zlpbyte = Encoding.Default.GetBytes(zlpstring); that you used to send via libusb is not a ZLP. You should really look into this. I think they are appending an additional 0x00 to the transmission or something – CJC Aug 11 '16 at 23:46
  • Ill give you an example. I remember one time, when i was working with a barcode reader. Some barcode scaners of the exact same product worked and some didnt work with my software. I took a while but eventually realized that for some of the scanners, they were configured to append particular suffix to the end of transmission a 0x00 (or 0x0D or 0x0A I cant remember exactly what condition) as the end of transmission. Without realizing that my software was hardcoded to look for a specific end of transmission. – CJC Aug 11 '16 at 23:49
  • anyway hope this helps. Although I can fully understand your frustration. Im sorry i cant be more helpful – CJC Aug 11 '16 at 23:50