SetupComm, SetCommState, SetCommTimeouts fail with

2019-08-14 17:28发布

问题:

i am opening a USB device:

for communication using CreateFile:

HANDLE hUsb = CreateFile("\\.\LCLD9",
      GENERIC_READ | GENERIC_WRITE, 
      0, 
      null, 
      OPEN_EXISTING, 
      FILE_FLAG_OVERLAPPED, 
      0);

The call succeeds (i.e. hUsb is not equal to INVALID_HANDLE_VALUE). But then it comes time to do what we do with every serial port:

  • SetupComm (set receive and transit buffer sizes)
  • SetCommState (set flow-control, baud rate, etc)
  • SetCommTimeouts (set timeouts)

Each of these calls with GetLastError code of 1. E.g.:

SetupComm(hUsb, 1024, 1024);

Why are operations to configure the serial device failing when using a "USB" serial device, but work when using a "virtual COM port"? Do USB devices not support such baud rates, buffers, flow control, and timeouts?

If this is a limitation/feature of Universal Serial devices, how can i detect that a handle refers to a "Universial Serial Device", rather than a "COMM Port"? For example, the user is the one who specifies which port to use:

  • \.\COM5
  • \.\LCLD9

Other serial functions that fail when talking to Universal Serial Bus serial device:

  • GetCommModemStatus (with error code 1)
  • ReadFile (with error code 6)
  • PurgeComm (with error code 6)
  • WriteFile (with error code 6)

Which begs the larger question, how do you communicate with a USB device once it's been opened with CreateFile?

回答1:

No, USB devices do not use these things. If your device is an actual USB-to-RS232 (or other slow serial), then you should be opening the COM port it associated with. Let the drivers handle the work of sending that data.

USB communication is not like COM ports. You can think of it more as an external PCI bus than a simple send-whatever-data-you-want line.



回答2:

Turns out that i don't have to do anything with Comm, because it's not a COM port. The reason my WriteFile was failing was because i was attempting to write to \\.\LCLD9 rather than \\.\LCLD9\.

The trailing backslash is critical; even though CreateFile returns success both ways.

void WriteToDisplay(String s)
{
   //Open the display
   var hLineDisplay = CreateFile("\\.\LCLD9\", GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0);

   //Write the command
   DWORD bytesWritten;
   WriteFile(hLineDisplay, s, s.Length, ref bytesWritten, nil);

   FileClose(hLineDisplay);
}

Anyone using Logic Controls LD9000 USB Line Display, the above is how you write to the display.

After reverse engineering their .NET Line Display driver i will also mention that the name of the port you use, e.g.:

  • \\.\LCLD9\
  • \\.\LCPD6\
  • \\.\LCPD3\

can be inferred from the full devicePath returned using the Windows Setup APIs. For example, my pole display's full device path is:

\\?\USB#VID_0FA8&PID_A090#6&DF2EE03&0&1#{A5DCBF10-6530-11D2-901F-00C04FB951ED}
                 \______/
                    |
                ProductID

The rule is to check the device path for Product IDs. In my case PID_A090 means it will be available as file \\.\LCLD9\. Other product IDs and their associated file paths:

Contains  DeviceName (trailing backslash is not optional)
========  ===============================================
PID_A030  \\.\LCPD3\
PID_A060  \\.\LCPD6\
PID_A090  \\.\LCLD9\

Note: Any code is released into the public domain. No attribution required.