mirror of
https://github.com/wjwwood/serial.git
synced 2026-01-23 04:04:54 +08:00
commit
be80973ee7
@ -4,6 +4,11 @@ project(serial)
|
|||||||
# Find catkin
|
# Find catkin
|
||||||
find_package(catkin REQUIRED)
|
find_package(catkin REQUIRED)
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
find_library(IOKIT_LIBRARY IOKit)
|
||||||
|
find_library(FOUNDATION_LIBRARY Foundation)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
# If Linux, add rt and pthread
|
# If Linux, add rt and pthread
|
||||||
catkin_package(
|
catkin_package(
|
||||||
@ -25,18 +30,25 @@ set(serial_SRCS
|
|||||||
include/serial/serial.h
|
include/serial/serial.h
|
||||||
include/serial/v8stdint.h
|
include/serial/v8stdint.h
|
||||||
)
|
)
|
||||||
if(UNIX)
|
if(APPLE)
|
||||||
|
# If OSX
|
||||||
|
list(APPEND serial_SRCS src/impl/unix.cc)
|
||||||
|
list(APPEND serial_SRCS src/impl/list_ports/list_ports_osx.cc)
|
||||||
|
elseif(UNIX)
|
||||||
# If unix
|
# If unix
|
||||||
list(APPEND serial_SRCS src/impl/unix.cc)
|
list(APPEND serial_SRCS src/impl/unix.cc)
|
||||||
else()
|
list(APPEND serial_SRCS src/impl/list_ports/list_ports_linux.cc)
|
||||||
|
elseif()
|
||||||
# If windows
|
# If windows
|
||||||
list(APPEND serial_SRCS src/impl/win.cc)
|
list(APPEND serial_SRCS src/impl/win.cc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
## Add serial library
|
## Add serial library
|
||||||
add_library(${PROJECT_NAME} ${serial_SRCS})
|
add_library(${PROJECT_NAME} ${serial_SRCS})
|
||||||
if(UNIX AND NOT APPLE)
|
if(APPLE)
|
||||||
target_link_libraries(${PROJECT_NAME} rt)
|
target_link_libraries(${PROJECT_NAME} ${FOUNDATION_LIBRARY} ${IOKIT_LIBRARY})
|
||||||
|
elseif(UNIX)
|
||||||
|
target_link_libraries(${PROJECT_NAME} rt pthread)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
## Uncomment for example
|
## Uncomment for example
|
||||||
|
|||||||
@ -34,6 +34,7 @@ using std::exception;
|
|||||||
using std::cout;
|
using std::cout;
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
void my_sleep(unsigned long milliseconds) {
|
void my_sleep(unsigned long milliseconds) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -43,16 +44,46 @@ void my_sleep(unsigned long milliseconds) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void enumerate_ports()
|
||||||
|
{
|
||||||
|
vector<serial::PortInfo> devices_found = serial::list_ports();
|
||||||
|
|
||||||
|
vector<serial::PortInfo>::iterator iter = devices_found.begin();
|
||||||
|
|
||||||
|
while( iter != devices_found.end() )
|
||||||
|
{
|
||||||
|
serial::PortInfo device = *iter++;
|
||||||
|
|
||||||
|
printf( "(%s, %s, %s)\n", device.port.c_str(), device.description.c_str(),
|
||||||
|
device.hardware_id.c_str() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_usage()
|
||||||
|
{
|
||||||
|
cerr << "Usage: test_serial {-e|<serial port address>} ";
|
||||||
|
cerr << "<baudrate> [test string]" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
int run(int argc, char **argv)
|
int run(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if(argc < 3) {
|
if(argc < 2) {
|
||||||
cerr << "Usage: test_serial <serial port address> ";
|
print_usage();
|
||||||
cerr << "<baudrate> [test string]" << endl;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Argument 1 is the serial port
|
|
||||||
|
// Argument 1 is the serial port or enumerate flag
|
||||||
string port(argv[1]);
|
string port(argv[1]);
|
||||||
|
|
||||||
|
if( port == "-e" ) {
|
||||||
|
enumerate_ports();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else if( argc < 3 ) {
|
||||||
|
print_usage();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Argument 2 is the baudrate
|
// Argument 2 is the baudrate
|
||||||
unsigned long baud = 0;
|
unsigned long baud = 0;
|
||||||
#if defined(WIN32) && !defined(__MINGW32__)
|
#if defined(WIN32) && !defined(__MINGW32__)
|
||||||
|
|||||||
@ -695,6 +695,32 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Structure that describes a serial device.
|
||||||
|
*/
|
||||||
|
struct PortInfo {
|
||||||
|
|
||||||
|
/*! Address of the serial port (this can be passed to the constructor of Serial). */
|
||||||
|
std::string port;
|
||||||
|
|
||||||
|
/*! Human readable description of serial device if available. */
|
||||||
|
std::string description;
|
||||||
|
|
||||||
|
/*! Hardware ID (e.g. VID:PID of USB serial devices) or "n/a" if not available. */
|
||||||
|
std::string hardware_id;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Lists the serial ports available on the system
|
||||||
|
*
|
||||||
|
* Returns a vector of available serial ports, each represented
|
||||||
|
* by a serial::PortInfo data structure:
|
||||||
|
*
|
||||||
|
* \return vector of serial::PortInfo.
|
||||||
|
*/
|
||||||
|
std::vector<PortInfo>
|
||||||
|
list_ports();
|
||||||
|
|
||||||
} // namespace serial
|
} // namespace serial
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
331
src/impl/list_ports/list_ports_linux.cc
Normal file
331
src/impl/list_ports/list_ports_linux.cc
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
|
||||||
|
* This software is made available under the terms of the MIT licence.
|
||||||
|
* A copy of the licence can be obtained from:
|
||||||
|
* http://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdarg>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
#include <glob.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "serial/serial.h"
|
||||||
|
|
||||||
|
using serial::PortInfo;
|
||||||
|
using std::istringstream;
|
||||||
|
using std::ifstream;
|
||||||
|
using std::getline;
|
||||||
|
using std::vector;
|
||||||
|
using std::string;
|
||||||
|
using std::cout;
|
||||||
|
using std::endl;
|
||||||
|
|
||||||
|
static vector<string> glob(const vector<string>& patterns);
|
||||||
|
static string basename(const string& path);
|
||||||
|
static string dirname(const string& path);
|
||||||
|
static bool path_exists(const string& path);
|
||||||
|
static string realpath(const string& path);
|
||||||
|
static string usb_sysfs_friendly_name(const string& sys_usb_path);
|
||||||
|
static vector<string> get_sysfs_info(const string& device_path);
|
||||||
|
static string read_line(const string& file);
|
||||||
|
static string usb_sysfs_hw_string(const string& sysfs_path);
|
||||||
|
static string format(const char* format, ...);
|
||||||
|
|
||||||
|
vector<string>
|
||||||
|
glob(const vector<string>& patterns)
|
||||||
|
{
|
||||||
|
vector<string> paths_found;
|
||||||
|
|
||||||
|
if(patterns.size() == 0)
|
||||||
|
return paths_found;
|
||||||
|
|
||||||
|
glob_t glob_results;
|
||||||
|
|
||||||
|
int glob_retval = glob(patterns[0].c_str(), 0, NULL, &glob_results);
|
||||||
|
|
||||||
|
vector<string>::const_iterator iter = patterns.begin();
|
||||||
|
|
||||||
|
while(++iter != patterns.end())
|
||||||
|
{
|
||||||
|
glob_retval = glob(iter->c_str(), GLOB_APPEND, NULL, &glob_results);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int path_index = 0; path_index < glob_results.gl_pathc; path_index++)
|
||||||
|
{
|
||||||
|
paths_found.push_back(glob_results.gl_pathv[path_index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
globfree(&glob_results);
|
||||||
|
|
||||||
|
return paths_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
basename(const string& path)
|
||||||
|
{
|
||||||
|
size_t pos = path.rfind("/");
|
||||||
|
|
||||||
|
if(pos == std::string::npos)
|
||||||
|
return path;
|
||||||
|
|
||||||
|
return string(path, pos+1, string::npos);
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
dirname(const string& path)
|
||||||
|
{
|
||||||
|
size_t pos = path.rfind("/");
|
||||||
|
|
||||||
|
if(pos == std::string::npos)
|
||||||
|
return path;
|
||||||
|
else if(pos == 0)
|
||||||
|
return "/";
|
||||||
|
|
||||||
|
return string(path, 0, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
path_exists(const string& path)
|
||||||
|
{
|
||||||
|
struct stat sb;
|
||||||
|
|
||||||
|
if( stat(path.c_str(), &sb ) == 0 )
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
realpath(const string& path)
|
||||||
|
{
|
||||||
|
char* real_path = realpath(path.c_str(), NULL);
|
||||||
|
|
||||||
|
string result;
|
||||||
|
|
||||||
|
if(real_path != NULL)
|
||||||
|
{
|
||||||
|
result = real_path;
|
||||||
|
|
||||||
|
free(real_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
usb_sysfs_friendly_name(const string& sys_usb_path)
|
||||||
|
{
|
||||||
|
unsigned int device_number = 0;
|
||||||
|
|
||||||
|
istringstream( read_line(sys_usb_path + "/devnum") ) >> device_number;
|
||||||
|
|
||||||
|
string manufacturer = read_line( sys_usb_path + "/manufacturer" );
|
||||||
|
|
||||||
|
string product = read_line( sys_usb_path + "/product" );
|
||||||
|
|
||||||
|
string serial = read_line( sys_usb_path + "/serial" );
|
||||||
|
|
||||||
|
if( manufacturer.empty() && product.empty() && serial.empty() )
|
||||||
|
return "";
|
||||||
|
|
||||||
|
return format("%s %s %s", manufacturer.c_str(), product.c_str(), serial.c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string>
|
||||||
|
get_sysfs_info(const string& device_path)
|
||||||
|
{
|
||||||
|
string device_name = basename( device_path );
|
||||||
|
|
||||||
|
string friendly_name;
|
||||||
|
|
||||||
|
string hardware_id;
|
||||||
|
|
||||||
|
string sys_device_path = format( "/sys/class/tty/%s/device", device_name.c_str() );
|
||||||
|
|
||||||
|
if( device_name.compare(0,6,"ttyUSB") == 0 )
|
||||||
|
{
|
||||||
|
sys_device_path = dirname( dirname( realpath( sys_device_path ) ) );
|
||||||
|
|
||||||
|
if( path_exists( sys_device_path ) )
|
||||||
|
{
|
||||||
|
friendly_name = usb_sysfs_friendly_name( sys_device_path );
|
||||||
|
|
||||||
|
hardware_id = usb_sysfs_hw_string( sys_device_path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( device_name.compare(0,6,"ttyACM") == 0 )
|
||||||
|
{
|
||||||
|
sys_device_path = dirname( realpath( sys_device_path ) );
|
||||||
|
|
||||||
|
if( path_exists( sys_device_path ) )
|
||||||
|
{
|
||||||
|
friendly_name = usb_sysfs_friendly_name( sys_device_path );
|
||||||
|
|
||||||
|
hardware_id = usb_sysfs_hw_string( sys_device_path );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try to read ID string of PCI device
|
||||||
|
|
||||||
|
string sys_id_path = sys_device_path + "/id";
|
||||||
|
|
||||||
|
if( path_exists( sys_id_path ) )
|
||||||
|
hardware_id = read_line( sys_id_path );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( friendly_name.empty() )
|
||||||
|
friendly_name = device_name;
|
||||||
|
|
||||||
|
if( hardware_id.empty() )
|
||||||
|
hardware_id = "n/a";
|
||||||
|
|
||||||
|
vector<string> result;
|
||||||
|
result.push_back(friendly_name);
|
||||||
|
result.push_back(hardware_id);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
read_line(const string& file)
|
||||||
|
{
|
||||||
|
ifstream ifs(file.c_str(), ifstream::in);
|
||||||
|
|
||||||
|
string line;
|
||||||
|
|
||||||
|
if(ifs)
|
||||||
|
{
|
||||||
|
getline(ifs, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
format(const char* format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
size_t buffer_size_bytes = 256;
|
||||||
|
|
||||||
|
string result;
|
||||||
|
|
||||||
|
char* buffer = (char*)malloc(buffer_size_bytes);
|
||||||
|
|
||||||
|
if( buffer == NULL )
|
||||||
|
return result;
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
|
||||||
|
unsigned int loop_count = 0;
|
||||||
|
|
||||||
|
while(!done)
|
||||||
|
{
|
||||||
|
va_start(ap, format);
|
||||||
|
|
||||||
|
int return_value = vsnprintf(buffer, buffer_size_bytes, format, ap);
|
||||||
|
|
||||||
|
if( return_value < 0 )
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
else if( return_value >= buffer_size_bytes )
|
||||||
|
{
|
||||||
|
// Realloc and try again.
|
||||||
|
|
||||||
|
buffer_size_bytes = return_value + 1;
|
||||||
|
|
||||||
|
char* new_buffer_ptr = (char*)realloc(buffer, buffer_size_bytes);
|
||||||
|
|
||||||
|
if( new_buffer_ptr == NULL )
|
||||||
|
{
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer = new_buffer_ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = buffer;
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
if( ++loop_count > 5 )
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
usb_sysfs_hw_string(const string& sysfs_path)
|
||||||
|
{
|
||||||
|
string serial_number = read_line( sysfs_path + "/serial" );
|
||||||
|
|
||||||
|
if( serial_number.length() > 0 )
|
||||||
|
{
|
||||||
|
serial_number = format( "SNR=%s", serial_number.c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
string vid = read_line( sysfs_path + "/idVendor" );
|
||||||
|
|
||||||
|
string pid = read_line( sysfs_path + "/idProduct" );
|
||||||
|
|
||||||
|
return format("USB VID:PID=%s:%s %s", vid.c_str(), pid.c_str(), serial_number.c_str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<PortInfo>
|
||||||
|
serial::list_ports()
|
||||||
|
{
|
||||||
|
vector<PortInfo> results;
|
||||||
|
|
||||||
|
vector<string> search_globs;
|
||||||
|
search_globs.push_back("/dev/ttyACM*");
|
||||||
|
search_globs.push_back("/dev/ttyS*");
|
||||||
|
search_globs.push_back("/dev/ttyUSB*");
|
||||||
|
search_globs.push_back("/dev/tty.*");
|
||||||
|
search_globs.push_back("/dev/cu.*");
|
||||||
|
|
||||||
|
vector<string> devices_found = glob( search_globs );
|
||||||
|
|
||||||
|
vector<string>::iterator iter = devices_found.begin();
|
||||||
|
|
||||||
|
while( iter != devices_found.end() )
|
||||||
|
{
|
||||||
|
string device = *iter++;
|
||||||
|
|
||||||
|
vector<string> sysfs_info = get_sysfs_info( device );
|
||||||
|
|
||||||
|
string friendly_name = sysfs_info[0];
|
||||||
|
|
||||||
|
string hardware_id = sysfs_info[1];
|
||||||
|
|
||||||
|
PortInfo device_entry;
|
||||||
|
device_entry.port = device;
|
||||||
|
device_entry.description = friendly_name;
|
||||||
|
device_entry.hardware_id = hardware_id;
|
||||||
|
|
||||||
|
results.push_back( device_entry );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
281
src/impl/list_ports/list_ports_osx.cc
Normal file
281
src/impl/list_ports/list_ports_osx.cc
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
#include <sys/param.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <IOKit/IOKitLib.h>
|
||||||
|
#include <IOKit/serial/IOSerialKeys.h>
|
||||||
|
#include <IOKit/IOBSD.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "serial/serial.h"
|
||||||
|
|
||||||
|
using serial::PortInfo;
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
|
||||||
|
#define HARDWARE_ID_STRING_LENGTH 128
|
||||||
|
|
||||||
|
string cfstring_to_string( CFStringRef cfstring );
|
||||||
|
string get_device_path( io_object_t& serial_port );
|
||||||
|
string get_class_name( io_object_t& obj );
|
||||||
|
io_registry_entry_t get_parent_iousb_device( io_object_t& serial_port );
|
||||||
|
string get_string_property( io_object_t& device, const char* property );
|
||||||
|
uint16_t get_int_property( io_object_t& device, const char* property );
|
||||||
|
string rtrim(const string& str);
|
||||||
|
|
||||||
|
string
|
||||||
|
cfstring_to_string( CFStringRef cfstring )
|
||||||
|
{
|
||||||
|
char cstring[MAXPATHLEN];
|
||||||
|
string result;
|
||||||
|
|
||||||
|
if( cfstring )
|
||||||
|
{
|
||||||
|
Boolean success = CFStringGetCString( cfstring,
|
||||||
|
cstring,
|
||||||
|
sizeof(cstring),
|
||||||
|
kCFStringEncodingASCII );
|
||||||
|
|
||||||
|
if( success )
|
||||||
|
result = cstring;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
get_device_path( io_object_t& serial_port )
|
||||||
|
{
|
||||||
|
CFTypeRef callout_path;
|
||||||
|
string device_path;
|
||||||
|
|
||||||
|
callout_path = IORegistryEntryCreateCFProperty( serial_port,
|
||||||
|
CFSTR(kIOCalloutDeviceKey),
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
0 );
|
||||||
|
|
||||||
|
if (callout_path)
|
||||||
|
{
|
||||||
|
if( CFGetTypeID(callout_path) == CFStringGetTypeID() )
|
||||||
|
device_path = cfstring_to_string( static_cast<CFStringRef>(callout_path) );
|
||||||
|
|
||||||
|
CFRelease(callout_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return device_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
get_class_name( io_object_t& obj )
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
io_name_t class_name;
|
||||||
|
kern_return_t kern_result;
|
||||||
|
|
||||||
|
kern_result = IOObjectGetClass( obj, class_name );
|
||||||
|
|
||||||
|
if( kern_result == KERN_SUCCESS )
|
||||||
|
result = class_name;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
io_registry_entry_t
|
||||||
|
get_parent_iousb_device( io_object_t& serial_port )
|
||||||
|
{
|
||||||
|
io_object_t device = serial_port;
|
||||||
|
io_registry_entry_t parent = 0;
|
||||||
|
io_registry_entry_t result = 0;
|
||||||
|
kern_return_t kern_result = KERN_FAILURE;
|
||||||
|
string name = get_class_name(device);
|
||||||
|
|
||||||
|
// Walk the IO Registry tree looking for this devices parent IOUSBDevice.
|
||||||
|
while( name != "IOUSBDevice" )
|
||||||
|
{
|
||||||
|
kern_result = IORegistryEntryGetParentEntry( device,
|
||||||
|
kIOServicePlane,
|
||||||
|
&parent );
|
||||||
|
|
||||||
|
if(kern_result != KERN_SUCCESS)
|
||||||
|
{
|
||||||
|
result = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
device = parent;
|
||||||
|
|
||||||
|
name = get_class_name(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(kern_result == KERN_SUCCESS)
|
||||||
|
result = device;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
get_string_property( io_object_t& device, const char* property )
|
||||||
|
{
|
||||||
|
string property_name;
|
||||||
|
|
||||||
|
if( device )
|
||||||
|
{
|
||||||
|
CFStringRef property_as_cfstring = CFStringCreateWithCString (
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
property,
|
||||||
|
kCFStringEncodingASCII );
|
||||||
|
|
||||||
|
CFTypeRef name_as_cfstring = IORegistryEntryCreateCFProperty(
|
||||||
|
device,
|
||||||
|
property_as_cfstring,
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
0 );
|
||||||
|
|
||||||
|
if( name_as_cfstring )
|
||||||
|
{
|
||||||
|
if( CFGetTypeID(name_as_cfstring) == CFStringGetTypeID() )
|
||||||
|
property_name = cfstring_to_string( static_cast<CFStringRef>(name_as_cfstring) );
|
||||||
|
|
||||||
|
CFRelease(name_as_cfstring);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(property_as_cfstring)
|
||||||
|
CFRelease(property_as_cfstring);
|
||||||
|
}
|
||||||
|
|
||||||
|
return property_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t
|
||||||
|
get_int_property( io_object_t& device, const char* property )
|
||||||
|
{
|
||||||
|
uint16_t result = 0;
|
||||||
|
|
||||||
|
if( device )
|
||||||
|
{
|
||||||
|
CFStringRef property_as_cfstring = CFStringCreateWithCString (
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
property,
|
||||||
|
kCFStringEncodingASCII );
|
||||||
|
|
||||||
|
CFTypeRef number = IORegistryEntryCreateCFProperty( device,
|
||||||
|
property_as_cfstring,
|
||||||
|
kCFAllocatorDefault,
|
||||||
|
0 );
|
||||||
|
|
||||||
|
if(property_as_cfstring)
|
||||||
|
CFRelease(property_as_cfstring);
|
||||||
|
|
||||||
|
if( number )
|
||||||
|
{
|
||||||
|
if( CFGetTypeID(number) == CFNumberGetTypeID() )
|
||||||
|
{
|
||||||
|
bool success = CFNumberGetValue( static_cast<CFNumberRef>(number),
|
||||||
|
kCFNumberSInt16Type,
|
||||||
|
&result );
|
||||||
|
|
||||||
|
if( !success )
|
||||||
|
result = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
CFRelease(number);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string rtrim(const string& str)
|
||||||
|
{
|
||||||
|
string result = str;
|
||||||
|
|
||||||
|
string whitespace = " \t\f\v\n\r";
|
||||||
|
|
||||||
|
std::size_t found = result.find_last_not_of(whitespace);
|
||||||
|
|
||||||
|
if (found != std::string::npos)
|
||||||
|
result.erase(found+1);
|
||||||
|
else
|
||||||
|
result.clear();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<PortInfo>
|
||||||
|
serial::list_ports(void)
|
||||||
|
{
|
||||||
|
vector<PortInfo> devices_found;
|
||||||
|
CFMutableDictionaryRef classes_to_match;
|
||||||
|
io_iterator_t serial_port_iterator;
|
||||||
|
io_object_t serial_port;
|
||||||
|
mach_port_t master_port;
|
||||||
|
kern_return_t kern_result;
|
||||||
|
|
||||||
|
kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
|
||||||
|
|
||||||
|
if(kern_result != KERN_SUCCESS)
|
||||||
|
return devices_found;
|
||||||
|
|
||||||
|
classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue);
|
||||||
|
|
||||||
|
if (classes_to_match == NULL)
|
||||||
|
return devices_found;
|
||||||
|
|
||||||
|
CFDictionarySetValue( classes_to_match,
|
||||||
|
CFSTR(kIOSerialBSDTypeKey),
|
||||||
|
CFSTR(kIOSerialBSDAllTypes) );
|
||||||
|
|
||||||
|
kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &serial_port_iterator);
|
||||||
|
|
||||||
|
if (KERN_SUCCESS != kern_result)
|
||||||
|
return devices_found;
|
||||||
|
|
||||||
|
while ( (serial_port = IOIteratorNext(serial_port_iterator)) )
|
||||||
|
{
|
||||||
|
string device_path = get_device_path( serial_port );
|
||||||
|
io_registry_entry_t parent = get_parent_iousb_device( serial_port );
|
||||||
|
IOObjectRelease(serial_port);
|
||||||
|
|
||||||
|
if( device_path.empty() )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PortInfo port_info;
|
||||||
|
port_info.port = device_path;
|
||||||
|
port_info.description = "n/a";
|
||||||
|
port_info.hardware_id = "n/a";
|
||||||
|
|
||||||
|
string device_name = rtrim( get_string_property( parent, "USB Product Name" ) );
|
||||||
|
string vendor_name = rtrim( get_string_property( parent, "USB Vendor Name") );
|
||||||
|
string description = rtrim( vendor_name + " " + device_name );
|
||||||
|
if( !description.empty() )
|
||||||
|
port_info.description = description;
|
||||||
|
|
||||||
|
string serial_number = rtrim(get_string_property( parent, "USB Serial Number" ) );
|
||||||
|
uint16_t vendor_id = get_int_property( parent, "idVendor" );
|
||||||
|
uint16_t product_id = get_int_property( parent, "idProduct" );
|
||||||
|
|
||||||
|
if( vendor_id && product_id )
|
||||||
|
{
|
||||||
|
char cstring[HARDWARE_ID_STRING_LENGTH];
|
||||||
|
|
||||||
|
if(serial_number.empty())
|
||||||
|
serial_number = "None";
|
||||||
|
|
||||||
|
int ret = snprintf( cstring, HARDWARE_ID_STRING_LENGTH, "USB VID:PID=%04x:%04x SNR=%s",
|
||||||
|
vendor_id,
|
||||||
|
product_id,
|
||||||
|
serial_number.c_str() );
|
||||||
|
|
||||||
|
if( (ret >= 0) && (ret < HARDWARE_ID_STRING_LENGTH) )
|
||||||
|
port_info.hardware_id = cstring;
|
||||||
|
}
|
||||||
|
|
||||||
|
devices_found.push_back(port_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices_found;
|
||||||
|
}
|
||||||
127
src/impl/list_ports/list_ports_win.cc
Normal file
127
src/impl/list_ports/list_ports_win.cc
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
|
||||||
|
* This software is made available under the terms of the MIT licence.
|
||||||
|
* A copy of the licence can be obtained from:
|
||||||
|
* http://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "serial/serial.h"
|
||||||
|
#include <windows.h>
|
||||||
|
#include <SetupAPI.h>
|
||||||
|
#include <devguid.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
using serial::PortInfo;
|
||||||
|
using std::vector;
|
||||||
|
using std::string;
|
||||||
|
|
||||||
|
static const DWORD port_name_max_length = 256;
|
||||||
|
static const DWORD friendly_name_max_length = 256;
|
||||||
|
static const DWORD hardware_id_max_length = 256;
|
||||||
|
|
||||||
|
vector<PortInfo>
|
||||||
|
serial::list_ports()
|
||||||
|
{
|
||||||
|
decltype( serial::list_ports() ) devices_found;
|
||||||
|
|
||||||
|
HDEVINFO device_info_set = SetupDiGetClassDevs(
|
||||||
|
(const GUID *) &GUID_DEVCLASS_PORTS,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
DIGCF_PRESENT);
|
||||||
|
|
||||||
|
unsigned int device_info_set_index = 0;
|
||||||
|
SP_DEVINFO_DATA device_info_data;
|
||||||
|
|
||||||
|
device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||||
|
|
||||||
|
while(SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data))
|
||||||
|
{
|
||||||
|
device_info_set_index++;
|
||||||
|
|
||||||
|
// Get port name
|
||||||
|
|
||||||
|
HKEY hkey = SetupDiOpenDevRegKey(
|
||||||
|
device_info_set,
|
||||||
|
&device_info_data,
|
||||||
|
DICS_FLAG_GLOBAL,
|
||||||
|
0,
|
||||||
|
DIREG_DEV,
|
||||||
|
KEY_READ);
|
||||||
|
|
||||||
|
char port_name[port_name_max_length];
|
||||||
|
DWORD port_name_length = port_name_max_length;
|
||||||
|
|
||||||
|
LONG return_code = RegQueryValueEx(
|
||||||
|
hkey,
|
||||||
|
"PortName",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
(LPBYTE)port_name,
|
||||||
|
&port_name_length);
|
||||||
|
|
||||||
|
RegCloseKey(hkey);
|
||||||
|
|
||||||
|
if(return_code != EXIT_SUCCESS)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(port_name_length > 0 && port_name_length <= port_name_max_length)
|
||||||
|
port_name[port_name_length-1] = '\0';
|
||||||
|
else
|
||||||
|
port_name[0] = '\0';
|
||||||
|
|
||||||
|
// Ignore parallel ports
|
||||||
|
|
||||||
|
if(strstr(port_name, "LPT") != NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Get port friendly name
|
||||||
|
|
||||||
|
char friendly_name[friendly_name_max_length];
|
||||||
|
DWORD friendly_name_actual_length = 0;
|
||||||
|
|
||||||
|
BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty(
|
||||||
|
device_info_set,
|
||||||
|
&device_info_data,
|
||||||
|
SPDRP_FRIENDLYNAME,
|
||||||
|
NULL,
|
||||||
|
(PBYTE)friendly_name,
|
||||||
|
friendly_name_max_length,
|
||||||
|
&friendly_name_actual_length);
|
||||||
|
|
||||||
|
if(got_friendly_name == TRUE && friendly_name_actual_length > 0)
|
||||||
|
friendly_name[friendly_name_actual_length-1] = '\0';
|
||||||
|
else
|
||||||
|
friendly_name[0] = '\0';
|
||||||
|
|
||||||
|
// Get hardware ID
|
||||||
|
|
||||||
|
char hardware_id[hardware_id_max_length];
|
||||||
|
DWORD hardware_id_actual_length = 0;
|
||||||
|
|
||||||
|
BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty(
|
||||||
|
device_info_set,
|
||||||
|
&device_info_data,
|
||||||
|
SPDRP_HARDWAREID,
|
||||||
|
NULL,
|
||||||
|
(PBYTE)hardware_id,
|
||||||
|
hardware_id_max_length,
|
||||||
|
&hardware_id_actual_length);
|
||||||
|
|
||||||
|
if(got_hardware_id == TRUE && hardware_id_actual_length > 0)
|
||||||
|
hardware_id[hardware_id_actual_length-1] = '\0';
|
||||||
|
else
|
||||||
|
hardware_id[0] = '\0';
|
||||||
|
|
||||||
|
PortInfo port_entry;
|
||||||
|
port_entry.port = port_name;
|
||||||
|
port_entry.description = friendly_name;
|
||||||
|
port_entry.hardware_id = hardware_id;
|
||||||
|
|
||||||
|
devices_found.push_back(port_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupDiDestroyDeviceInfoList(device_info_set);
|
||||||
|
|
||||||
|
return devices_found;
|
||||||
|
}
|
||||||
@ -46,6 +46,9 @@
|
|||||||
<Link>
|
<Link>
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Lib>
|
||||||
|
<AdditionalDependencies>setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Lib>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
@ -61,6 +64,7 @@
|
|||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\..\src\impl\list_ports\list_ports_win.cc" />
|
||||||
<ClCompile Include="..\..\src\impl\win.cc" />
|
<ClCompile Include="..\..\src\impl\win.cc" />
|
||||||
<ClCompile Include="..\..\src\serial.cc" />
|
<ClCompile Include="..\..\src\serial.cc" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@ -21,6 +21,9 @@
|
|||||||
<ClCompile Include="..\..\src\impl\win.cc">
|
<ClCompile Include="..\..\src\impl\win.cc">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\..\src\impl\list_ports\list_ports_win.cc">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\..\include\serial\serial.h">
|
<ClInclude Include="..\..\include\serial\serial.h">
|
||||||
|
|||||||
3
visual_studio/serial/serial.vcxproj.user
Normal file
3
visual_studio/serial/serial.vcxproj.user
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
</Project>
|
||||||
Loading…
x
Reference in New Issue
Block a user