2015年6月6日 星期六

Seeking USB Serial Com Port in Windows Automatically : via PID VID


    After you read previous article, you might know how to operate a com port in Windows.

   But that example requires programmer (or user, if you modified that example being able to support inputting command line) to set a com port number, it is not consummate. I will fill the flaw in here.



     You need to check the device instance path zeroth. In here, I use CH340 (USB com port chip from China), I assure there is only one CH340 connected to the computer, I could seek the comport via pid(product id) and vid (vendor id).

     If you do not know what is vid/pid : Briefly say, it is USB device firmware replying computer what device it is in seral number. We could use pid/vid + google to indentify the device, even there is no correspending driver.






 You need to set hSerial as invalid in the begining of that code:


hSerial = INVALID_HANDLE_VALUE;

And you should replace that as the code below:

 hSerial = CreateFile(&comPortName[0], GENERIC_READ|GENERIC_WRITE, 0, NULL,
                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
as

 
  
 if(0 == strncmp(&comPortName[0], "\\\\.\\COM0", MAX_STR_LEN))
 {  
  unsigned int i;

  COMMCONFIG comConfig;
  DWORD dwSize;   
  dwSize = sizeof(comConfig);


  ZeroMemory(&comConfig, sizeof(COMMCONFIG));
  
  for(i = 1; i< 256; i++){
   TCHAR comName[16];

   sprintf(&comName[0], "COM%d", i);

   if(FALSE != GetDefaultCommConfig(&comName[0], &comConfig, &dwSize))
    printf("found %s\n", &comName[0]);
  }/*for */

 }


 if(0 == strncmp(&comPortName[0], "\\\\.\\COM0", MAX_STR_LEN))
 {    
  DWORD dwGuids;
  GUID *pGuids;  

  HDEVINFO hDevInfo; 
  SP_DEVICE_INTERFACE_DATA devInterfaceData; 
  SP_DEVINFO_DATA devInfoData;
  unsigned int index;

  dwGuids = 0;


  isSuc = SetupDiClassGuidsFromName("Ports", NULL, 0, &dwGuids);


  pGuids = (GUID*)malloc(dwGuids*sizeof(GUID));
  ZeroMemory(pGuids, dwGuids*sizeof(GUID));
  isSuc = SetupDiClassGuidsFromName("Ports", pGuids, dwGuids, &dwGuids);
  
  hDevInfo = SetupDiGetClassDevs(pGuids, NULL, NULL, 
  /*DIGCF_ALLCLASSES | DIGCF_PRESENT |*/ DIGCF_DEVICEINTERFACE);  

  index = 0;

  ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));  
  devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);


  while(SetupDiEnumDeviceInfo(hDevInfo, index, &devInfoData))
  {   

   TCHAR szDeviceInstanceID[1024];            

   index++;

   isSuc = CM_Get_Device_ID(devInfoData.DevInst, 
   &szDeviceInstanceID[0] , sizeof(szDeviceInstanceID), 0);

   //printf("%s\n", &szDeviceInstanceID[0]);

   if(0 == strncmp(&szDeviceInstanceID[0], "USB\\VID_1A86&PID_7523", 
      strlen("USB\\VID_1A86&PID_7523")) )
   {
    
    DWORD requiredSize;
    GUID classGuid;

    SP_DEVICE_INTERFACE_DATA   devInterfaceData;   
    SP_DEVICE_INTERFACE_DETAIL_DATA *pDevInterfaceDetailData;

    printf("&szDeviceInstanceID[0] = %s\n", &szDeviceInstanceID[0]);

    ZeroMemory(&devInterfaceData, sizeof(SP_DEVICE_INTERFACE_DATA)); 
    
    devInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); 
    classGuid = devInfoData.ClassGuid;

    isSuc = SetupDiEnumDeviceInterfaces(hDevInfo, &devInfoData, pGuids,
    0, &devInterfaceData);    

    
    isSuc = SetupDiGetDeviceInterfaceDetail(hDevInfo, 
    &devInterfaceData, NULL, NULL, &requiredSize, NULL);

    //printf ("%s\n", GetLastErrorMessage( GetLastError() ) );

    pDevInterfaceDetailData = (SP_INTERFACE_DEVICE_DETAIL_DATA*)malloc(requiredSize);       

    pDevInterfaceDetailData->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);

    isSuc = SetupDiGetDeviceInterfaceDetail( hDevInfo, 
    &devInterfaceData, pDevInterfaceDetailData, requiredSize,
    &requiredSize, &devInfoData);

    
    printf("devInterfaceDetailData.DevicePath = %s\n", 
    pDevInterfaceDetailData->DevicePath);

    hSerial = CreateFile(pDevInterfaceDetailData->DevicePath, 
    GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 
    FILE_ATTRIBUTE_NORMAL, NULL );
    
    free(pDevInterfaceDetailData); pDevInterfaceDetailData = NULL;

    if(INVALID_HANDLE_VALUE != hSerial)
    break;
   }
  }           

  free(pGuids); pGuids = NULL;
  SetupDiDestroyDeviceInfoList(hDevInfo);

  if(INVALID_HANDLE_VALUE == hSerial)
  {
   printf("auto seeking com number fail!!\n");
   return 0;
  }
 }
 else /*non auto seeking com port*/ 
 {
  hSerial = CreateFile(&comPortName[0], GENERIC_READ | GENERIC_WRITE, 
  0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
 }/*if auto seeking com port*/
 

("\\\\.\\COM0" be  auto-seeking mode in here.)
  
You have to modify the USB\\VID_1A86&PID_7523 as your device's.

( If you use FTDI's FT232B  or FT232R, that should be

FTDIBUS\\VID_0403+PID_6001



)




That would support auto-seeking com-port.

Note the line :


        while(SetupDiEnumDeviceInfo(hDevInfo, index, &devInfoData))
  
You would find NOT ONLY one device as VID_1A86&PID_7523, even you have plugged only one CH340. If you print that seeking result (it is just the line : printf("&szDeviceInstanceID[0] = %s\n", &szDeviceInstanceID[0]);), you would watch the printing as:


&szDeviceInstanceID[0] = USB\VID_1A86&PID_7523\6&123E8975&0&1
devInterfaceDetailData.DevicePath = \\?\usb#vid_1a86&pid_7523#6&123e8975&0&1#{4d
36e978-e325-11ce-bfc1-08002be10318}
&szDeviceInstanceID[0] = USB\VID_1A86&PID_7523\6&123E8975&0&2
devInterfaceDetailData.DevicePath = \\?\usb#vid_1a86&pid_7523#6&123e8975&0&2#{4d
36e978-e325-11ce-bfc1-08002be10318}
&szDeviceInstanceID[0] = USB\VID_1A86&PID_7523\6&123E8975&0&3
devInterfaceDetailData.DevicePath = \\?\usb#vid_1a86&pid_7523#6&123e8975&0&3#{4d
36e978-e325-11ce-bfc1-08002be10318}
&szDeviceInstanceID[0] = USB\VID_1A86&PID_7523\6&123E8975&0&4
devInterfaceDetailData.DevicePath = \\?\usb#vid_1a86&pid_7523#6&123e8975&0&4#{4d
36e978-e325-11ce-bfc1-08002be10318}
&szDeviceInstanceID[0] = USB\VID_1A86&PID_7523\6&123E8975&0&5
devInterfaceDetailData.DevicePath = \\?\usb#vid_1a86&pid_7523#6&123e8975&0&5#{4d
36e978-e325-11ce-bfc1-08002be10318}
&szDeviceInstanceID[0] = USB\VID_1A86&PID_7523\7&107B0B49&0&2
devInterfaceDetailData.DevicePath = \\?\usb#vid_1a86&pid_7523#7&107b0b49&0&2#{4d
36e978-e325-11ce-bfc1-08002be10318}
&szDeviceInstanceID[0] = USB\VID_1A86&PID_7523\7&14F85601&0&1
devInterfaceDetailData.DevicePath = \\?\usb#vid_1a86&pid_7523#7&14f85601&0&1#{4d
36e978-e325-11ce-bfc1-08002be10318}

I do not know why Windows lists so much device path. AlI I could do, is to try opening each. you could find that the actual one's last 4 characters (like &0&4, &0&2..etc) would change with port number, that depends on which USB socket you plug.