1
0
mirror of git://sigrok.org/libserialport synced 2023-08-10 21:13:24 +03:00

1 Commits

Author SHA1 Message Date
15018e762b Doxyfile: Set version to 0.1.0. 2014-05-06 22:51:46 +02:00
19 changed files with 1002 additions and 3274 deletions

33
.gitignore vendored
View File

@ -1,18 +1,19 @@
*.[ao]
*.l[ao]
.deps/
.libs/
/INSTALL
/Makefile
/Makefile.in
/aclocal.m4
/autom4te.cache/
/autostuff/
/config.*
/configure
/configure.lineno
/libserialport-*.tar.*
/libserialport.h
/libserialport.pc
/libtool
stamp-h?
INSTALL
Makefile
Makefile.in
aclocal.m4
autom4te.cache/
autostuff/
config.log
config.status
configure
libserialport.la
libserialport.pc
libserialport.h
libtool
serialport.lo
serialport.o
linux_termios.lo
linux_termios.o

24
AUTHORS
View File

@ -2,26 +2,6 @@
AUTHORS
-------------------------------------------------------------------------------
Martin Ling conceived the idea for the library, designed the API and wrote much
of the implementation and documentation.
Please check the source code files and/or git history and/or ChangeLog for
a list of all authors and contributors.
The initial codebase was adapted from serial code in libsigrok, written by Bert
Vermeulen and Uwe Hermann, who gave permission for it to be relicensed under
the LGPL3+ for inclusion in libserialport.
The package is maintained by Uwe Hermann, with input from Martin Ling.
Aurelien Jacobs made several contributions, including the USB metadata API,
improvements to the port enumeration code, and eliminating the previous
dependence on libudev for enumeration on Linux.
Uffe Jakobsen contributed the FreeBSD support.
Matthias Heidbrink contributed support for non-standard baudrates on Mac OS X.
Bug fixes have been contributed by Bert Vermeulen, silverbuddy, Marcus
Comstedt, Antti Nykanen, Michael B. Trausch and Janne Huttunen.
For a full history of contributions, see the git history.
Others have helped the project by contributing bug reports and discussion.

133
Doxyfile
View File

@ -1,4 +1,4 @@
# Doxyfile 1.8.8
# Doxyfile 1.8.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@ -38,7 +38,7 @@ PROJECT_NAME = "libserialport"
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = "0.1.1"
PROJECT_NUMBER = "0.1.0"
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@ -70,14 +70,6 @@ OUTPUT_DIRECTORY = doxy
CREATE_SUBDIRS = NO
# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
# characters to appear in the names of generated files. If set to NO, non-ASCII
# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
# U+3044.
# The default value is: NO.
ALLOW_UNICODE_NAMES = NO
# The OUTPUT_LANGUAGE tag is used to specify the language in which all
# documentation generated by doxygen is written. Doxygen will use this
# information to generate all constant output in the proper language.
@ -269,12 +261,9 @@ OPTIMIZE_OUTPUT_VHDL = NO
# extension. Doxygen has a built-in mapping, but you can override or extend it
# using this tag. The format is ext=language, where ext is a file extension, and
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
# Fortran. In the later case the parser tries to guess whether the code is fixed
# or free formatted code, this is the default for Fortran type files), VHDL. For
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make
# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
# (default is Fortran), use: inc=Fortran f=C.
#
# Note For files without extension you can use no_extension as a placeholder.
#
@ -680,7 +669,8 @@ LAYOUT_FILE =
# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
# For LaTeX the style of the bibliography can be controlled using
# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
# search path. See also \cite for info how to create references.
# search path. Do not use file names with spaces, bibtex cannot handle them. See
# also \cite for info how to create references.
CITE_BIB_FILES =
@ -977,25 +967,6 @@ USE_HTAGS = NO
VERBATIM_HEADERS = YES
# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the
# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
# cost of reduced performance. This can be particularly helpful with template
# rich C++ code for which doxygen's built-in parser lacks the necessary type
# information.
# Note: The availability of this option depends on whether or not doxygen was
# compiled with the --with-libclang option.
# The default value is: NO.
CLANG_ASSISTED_PARSING = NO
# If clang assisted parsing is enabled you can provide the compiler with command
# line options that you would normally use when invoking the compiler. Note that
# the include paths will already be set by doxygen for the files and directories
# specified with INPUT and INCLUDE_PATH.
# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
CLANG_OPTIONS =
#---------------------------------------------------------------------------
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
@ -1088,15 +1059,13 @@ HTML_FOOTER =
HTML_STYLESHEET =
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
# cascading style sheets that are included after the standard style sheets
# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user-
# defined cascading style sheet that is included after the standard style sheets
# created by doxygen. Using this option one can overrule certain style aspects.
# This is preferred over using HTML_STYLESHEET since it does not replace the
# standard style sheet and is therefor more robust against future updates.
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra stylesheet files is of importance (e.g. the last
# stylesheet in the list overrules the setting of the previous ones in the
# list). For an example see the documentation.
# Doxygen will copy the style sheet file to the output directory. For an example
# see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
@ -1261,8 +1230,7 @@ GENERATE_CHI = NO
CHM_INDEX_ENCODING =
# The BINARY_TOC flag controls whether a binary table of contents is generated (
# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
# enables the Previous and Next buttons.
# YES) or a normal table of contents ( NO) in the .chm file.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
@ -1502,11 +1470,11 @@ SEARCHENGINE = YES
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
# implemented using a web server instead of a web client using Javascript. There
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
# setting. When disabled, doxygen will generate a PHP script for searching and
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
# and searching needs to be provided by external tools. See the section
# "External Indexing and Searching" for details.
# are two flavours of web server based searching depending on the
# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for
# searching and an index file used by the script. When EXTERNAL_SEARCH is
# enabled the indexing and searching needs to be provided by external tools. See
# the section "External Indexing and Searching" for details.
# The default value is: NO.
# This tag requires that the tag SEARCHENGINE is set to YES.
@ -1634,19 +1602,17 @@ EXTRA_PACKAGES =
#
# Note: Only use a user-defined header if you know what you are doing! The
# following commands have a special meaning inside the header: $title,
# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
# $projectbrief, $projectlogo. Doxygen will replace $title with the empy string,
# for the replacement values of the other commands the user is refered to
# HTML_HEADER.
# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will
# replace them by respectively the title of the page, the current date and time,
# only the current date, the version number of doxygen, the project name (see
# PROJECT_NAME), or the project number (see PROJECT_NUMBER).
# This tag requires that the tag GENERATE_LATEX is set to YES.
LATEX_HEADER =
# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
# generated LaTeX document. The footer should contain everything after the last
# chapter. If it is left blank doxygen will generate a standard footer. See
# LATEX_HEADER for more information on how to generate a default footer and what
# special commands can be used inside the footer.
# chapter. If it is left blank doxygen will generate a standard footer.
#
# Note: Only use a user-defined footer if you know what you are doing!
# This tag requires that the tag GENERATE_LATEX is set to YES.
@ -1670,7 +1636,7 @@ LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
# the PDF file directly from the LaTeX files. Set this option to YES to get a
# higher quality PDF documentation.
# The default value is: YES.
@ -1796,13 +1762,6 @@ MAN_OUTPUT = man
MAN_EXTENSION = .3
# The MAN_SUBDIR tag determines the name of the directory created within
# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
# MAN_EXTENSION with the initial . removed.
# This tag requires that the tag GENERATE_MAN is set to YES.
MAN_SUBDIR =
# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
# will generate one additional man file for each entity documented in the real
# man page(s). These additional files only source the real man page, but without
@ -1830,6 +1789,18 @@ GENERATE_XML = NO
XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_SCHEMA =
# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
# validating XML parser to check the syntax of the XML files.
# This tag requires that the tag GENERATE_XML is set to YES.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
@ -1857,15 +1828,6 @@ GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
# If the DOCBOOK_PROGRAMLISTING tag is set to YES doxygen will include the
# program listings (including syntax highlighting and cross-referencing
# information) to the DOCBOOK output. Note that enabling this will significantly
# increase the size of the DOCBOOK output.
# The default value is: NO.
# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@ -1985,9 +1947,9 @@ PREDEFINED =
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
# an all uppercase name, and do not end with a semicolon. Such function macros
# are typically used for boiler-plate code, and will confuse the parser if not
# remove all refrences to function-like macros that are alone on a line, have an
# all uppercase name, and do not end with a semicolon. Such function macros are
# typically used for boiler-plate code, and will confuse the parser if not
# removed.
# The default value is: YES.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
@ -2007,7 +1969,7 @@ SKIP_FUNCTION_MACROS = YES
# where loc1 and loc2 can be relative or absolute paths or URLs. See the
# section "Linking to external documentation" for more information about the use
# of tag files.
# Note: Each tag file must have a unique name (where the name does NOT include
# Note: Each tag file must have an unique name (where the name does NOT include
# the path). If a tag file is not located in the directory in which doxygen is
# run, you must also specify the path to the tagfile here.
@ -2085,7 +2047,7 @@ HIDE_UNDOC_RELATIONS = NO
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: YES.
# The default value is: NO.
HAVE_DOT = YES
@ -2099,7 +2061,7 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0
# When you want a differently looking font in the dot files that doxygen
# When you want a differently looking font n the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
@ -2237,9 +2199,7 @@ DIRECTORY_GRAPH = YES
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd,
# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo,
# gif:cairo:gd, gif:gd, gif:gd:gd and svg.
# Possible values are: png, jpg, gif and svg.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
@ -2282,15 +2242,6 @@ MSCFILE_DIRS =
DIAFILE_DIRS =
# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
# path where java can find the plantuml.jar file. If left blank, it is assumed
# PlantUML is not used or called during a preprocessing step. Doxygen will
# generate a warning when it encounters a \startuml command in this case and
# will not generate output for the diagram.
# This tag requires that the tag HAVE_DOT is set to YES.
PLANTUML_JAR_PATH =
# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
# that will be shown in the graph. If the number of nodes in a graph becomes
# larger than this value, doxygen will truncate the graph, which is visualized

View File

@ -19,36 +19,22 @@
##
ACLOCAL_AMFLAGS = -I autostuff
AM_LIBTOOLFLAGS = --silent
GNUMAKEFLAGS = --no-print-directory
# Enable more compiler warnings.
AM_CFLAGS = -std=c99 -Wall -Wextra -pedantic -Wmissing-prototypes -Wshadow
AM_CPPFLAGS = -I$(top_srcdir)
lib_LTLIBRARIES = libserialport.la
libserialport_la_SOURCES = serialport.c libserialport_internal.h
libserialport_la_SOURCES = serialport.c
if LINUX
libserialport_la_SOURCES += linux.c linux_termios.c linux_termios.h
endif
if WIN32
libserialport_la_SOURCES += windows.c
endif
if MACOSX
libserialport_la_SOURCES += macosx.c
endif
if FREEBSD
libserialport_la_SOURCES += freebsd.c
libserialport_la_SOURCES += linux_termios.c linux_termios.h
endif
libserialport_la_LIBADD = $(SP_LIBS)
libserialport_la_LIBADD = $(LIBOBJS)
libserialport_la_LDFLAGS = -version-info $(SP_LIB_VERSION) -no-undefined
if MACOSX
libserialport_la_LDFLAGS += -framework IOKit -framework CoreFoundation
endif
libserialport_la_LDFLAGS = $(SP_LIB_LDFLAGS)
nodist_include_HEADERS = libserialport.h
library_includedir = $(includedir)
library_include_HEADERS = libserialport.h
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libserialport.pc
@ -57,12 +43,11 @@ EXTRA_DIST = Doxyfile
MAINTAINERCLEANFILES = ChangeLog
.PHONY: ChangeLog doc
.PHONY: ChangeLog
ChangeLog:
git --git-dir '$(top_srcdir)/.git' log >$@ || touch $@
git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog
dist-hook: ChangeLog
doc: $(nodist_include_HEADERS) $(top_srcdir)/Doxyfile
doc: $(library_include_HEADERS) $(top_srcdir)/Doxyfile
doxygen $(top_srcdir)/Doxyfile

76
NEWS
View File

@ -1,79 +1,3 @@
0.1.1 (2016-01-27)
------------------
Note: This release does NOT change the libserialport API or ABI in
backwards-incompatible ways. Programs using libserialport should
continue to work fine without recompiling or relinking.
* New API calls:
- sp_get_port_description(): Obtain a user friendly port description.
- sp_get_port_transport(): Obtain the underlying transport type.
- sp_get_port_usb_bus_address(): Obtain the USB bus number & device address.
- sp_get_port_usb_vid_pid(): Obtain the USB VID and PID.
- sp_get_port_usb_manufacturer(): Obtain the USB manufacturer string.
- sp_get_port_usb_product(): Obtain the USB product string.
- sp_get_port_usb_serial(): Obtain the USB serial number string.
- sp_get_port_bluetooth_address(): Obtain the Bluetooth MAC address.
- sp_blocking_read_next(): Read bytes from the specified serial port,
returning as soon as any data is available.
* API related additions:
- enum sp_mode: Add SP_MODE_READ_WRITE.
- Add enum sp_transport with the following enum entries:
SP_TRANSPORT_NATIVE, SP_TRANSPORT_USB, SP_TRANSPORT_BLUETOOTH.
* Fix the build on platforms where port enumeration and/or port
metadata is not available or implemented.
* Build system:
- Don't set/override "user variables" such as CFLAGS or LDFLAGS, since
those are meant to be controlled by the "user" (bug #577).
- Modernize and cleanup autotools setup.
* Remove trailing commas in libserialport.h enum definitions to allow
the code to build with the -pedantic compiler option.
* Fix various issues to allow successful builds with -std=c99.
* Fix various (non-fatal) compiler warnings.
* Fix various memory leaks (bug #419, among others).
* Fix a potential overflow of the timeout parameter of poll().
* Fix ReadFile() ERROR_IO_PENDING in sp_nonblocking_read() (bug #707).
* Fix a glibc >= 2.20 compiler warning for deprecated _BSD_SOURCE (bug #716).
* Rename a 'signals' parameter to 'signal_mask' to avoid a conflict with Qt.
* Fix two potential segfaults in sp_get_port_handle() and sp_get_config_*()
(which only occurred if the user incorrectly passed in a NULL argument).
* sp_list_ports(): Actually set list_ptr NULL as documented.
* Use "Port not open" debug message when a closed port is used (bug #710).
* Linux:
- The libudev requirement has been dropped (/sys is now used directly).
- Fix a build issue in the get_termios_get_ioctl() call (bug #396).
- Fix sp_flush() so that it only flushes the requested buffers.
- Fix a build error if BOTHER is not available (bug #363).
- If present, add the USB "description" for better port identification.
- Handle the case when /sys/class/tty/ entries are not symlinks.
* Windows:
- MinGW-w64 (mingw-w64.sf.net) is now required for building, and MinGW
(mingw.org) is no longer supported (bug #393).
- Fix a build issue due to a missing <initguid.h> #include.
- Fix a build issue due to the missing -lsetupapi linker argument.
- Use libtool's -no-undefined option to allow shared (DLL) builds.
- Fix a compile error due to multiply or not defined GUIDs (bug #416).
- Fix a build issue related to the SP_PRIV/SP_API macros (bug #553).
- Fix a bug where an uninitialized pointer was free()'d (bug #512).
- Fix a bug wrt the SetupDiOpenDevRegKey() return code (bug #499).
- Fix a bug wrt restart of RX/error wait operations after read (bug #421).
- Fix a bug wrt WaitCommEvents() and/or fAbortOnError handling (bug #341).
- Fix USB iSerial queries on USB composite devices.
- Strip CR/LF from end of system error messages (bug #585).
- Avoid unnecessary calls to the SetCommTimeouts() function (bug #586).
- Fix a bug in the Windows implementation of sp_blocking_read_next().
- Fix an ERROR_SEM_TIMEOUT related issue in sp_blocking_write().
* FreeBSD:
- Implement serial port enumeration for FreeBSD systems.
* Android:
- Fix a portability issue wrt the unavailable 'serial_struct' (bug #376).
- Fix a portability issue wrt a readlinkat() call (bug #377).
* Mac OS X:
- Fix port listing on Mac OS X 10.11 (El Capitan).
* README: Various documentation updates and fixes.
* AUTHORS: Add/update list of contributors.
* Doxygen API docs: Various documentation updates and fixes.
0.1.0 (2014-05-06)
------------------

37
README
View File

@ -11,7 +11,6 @@ transparently on any platform supported by the library.
The operations that are supported are:
- Port enumeration (obtaining a list of serial ports on the system).
- Obtaining port metadata (USB device information, Bluetooth address, etc).
- Opening and closing ports.
- Setting port parameters (baud rate, parity, etc).
- Reading, writing and flushing data.
@ -25,29 +24,33 @@ Status
The library should build and work on any Windows or Unix-based system. If it
does not, please submit a bug.
Enumeration is currently implemented on Windows, Mac OS X, FreeBSD and Linux.
On other systems enumeration is not supported, but ports can still be opened
by name and then used.
Enumeration is currently only implemented on Windows, Mac OS X and Linux. On
other systems enumeration is not supported, but ports can still be opened by
name and then used.
If you know how to enumerate available ports on another OS, please submit a bug
with this information, or better still a patch implementing it.
Future
======
Future versions will add additional API calls for obtaining metadata about a
port, e.g. for USB devices the USB VID and PID of the underlying device.
Dependencies
============
No other libraries are required.
On Linux, libudev is required. On other systems no other libraries are required.
The libudev dependency could be eliminated in favour of direct sysfs queries at
the cost of some brevity. This is not currently a priority but if you feel like
doing this feel free to submit a patch.
Building
========
The package uses a GNU style build system and requires a Unix style shell.
Windows builds can be created natively with the MinGW-w64 toolchain and
MSYS2 environment, or cross-compiled using a MinGW-w64 toolchain:
http://mingw-w64.sourceforge.net/
The "old" MinGW from http://mingw.org/ is not supported.
On Windows it can be built with the MinGW toolchain and MSYS environment.
Run "./autogen.sh" to generate the build system, "./configure" to setup, then
"make" to build the library and "make install" to install it.
@ -56,13 +59,3 @@ API
===
Doxygen API documentation is included.
It can also be viewed online at:
http://sigrok.org/api/libserialport/unstable/
Website
=======
http://sigrok.org/wiki/Libserialport

View File

@ -1,4 +1,4 @@
#!/bin/sh -e
#!/bin/sh
##
## This file is part of the libserialport project.
##
@ -19,8 +19,42 @@
## along with this program. If not, see <http://www.gnu.org/licenses/>.
##
test -n "$srcdir" || srcdir=`dirname "$0"`
test -n "$srcdir" || srcdir=.
OS=`uname`
LIBTOOLIZE=libtoolize
ACLOCAL_DIR=
if [ "x$OS" = "xDarwin" ]; then
LIBTOOLIZE=glibtoolize
if [ -d /sw/share/aclocal ]; then
# fink installs aclocal macros here
ACLOCAL_DIR="-I /sw/share/aclocal"
elif [ -d /opt/local/share/aclocal ]; then
# Macports installs aclocal macros here
ACLOCAL_DIR="-I /opt/local/share/aclocal"
elif [ -d /usr/local/share/aclocal ]; then
# Homebrew installs aclocal macros here
ACLOCAL_DIR="-I /usr/local/share/aclocal"
elif [ -d /usr/share/aclocal ]; then
# Xcode installs aclocal macros here
ACLOCAL_DIR="-I /usr/share/aclocal"
fi
elif [ "x$OS" = "xMINGW32_NT-5.1" ]; then
# Windows XP
ACLOCAL_DIR="-I /usr/local/share/aclocal"
elif [ "x$OS" = "xMINGW32_NT-6.0" ]; then
# Windows Vista
ACLOCAL_DIR="-I /usr/local/share/aclocal"
elif [ "x$OS" = "xMINGW32_NT-6.1" ]; then
# Windows 7
ACLOCAL_DIR="-I /usr/local/share/aclocal"
fi
echo "Generating build system..."
${LIBTOOLIZE} --install --copy --quiet || exit 1
aclocal ${ACLOCAL_DIR} || exit 1
automake --add-missing --copy || exit 1
autoconf || exit 1
test -d "$srcdir/autostuff" || mkdir "$srcdir/autostuff"
autoreconf --force --install --verbose "$srcdir"

View File

@ -1,38 +1,32 @@
/**
* Set the {2} for the specified serial port.
*
* @param[in] port Pointer to a port structure. Must not be NULL.
* @param[in] {0} {3}.
* @param port Pointer to port structure.
* @param {0} {3}.
*
* @return SP_OK upon success, a negative error code otherwise.
*
* @since 0.1.0
*/
enum sp_return sp_set_{0}(struct sp_port *port, {1} {0});
/**
* Get the {2} from a port configuration.
*
* The user should allocate a variable of type {1} and
* pass a pointer to this to receive the result.
* The user should allocate a variable of type {1} and pass a pointer to this
* to receive the result.
*
* @param[in] config Pointer to a configuration structure. Must not be NULL.
* @param[out] {0}_ptr Pointer to a variable to store the result. Must not be NULL.
* @param config Pointer to configuration structure.
* @param {0}_ptr Pointer to variable to store result.
*
* @return SP_OK upon success, a negative error code otherwise.
*
* @since 0.1.0
*/
enum sp_return sp_get_config_{0}(const struct sp_port_config *config, {1} *{0}_ptr);
/**
* Set the {2} in a port configuration.
*
* @param[in] config Pointer to a configuration structure. Must not be NULL.
* @param[in] {0} {3}, or -1 to retain the current setting.
* @param config Pointer to configuration structure.
* @param {0} {3}, or -1 to retain current setting.
*
* @return SP_OK upon success, a negative error code otherwise.
*
* @since 0.1.0
*/
enum sp_return sp_set_config_{0}(struct sp_port_config *config, {1} {0});

View File

@ -24,124 +24,117 @@ AC_PREREQ([2.63])
# libserialport package version number (NOT the same as shared lib version!).
m4_define([sp_package_version_major], [0])
m4_define([sp_package_version_minor], [1])
m4_define([sp_package_version_micro], [1])
m4_define([sp_package_version_micro], [0])
m4_define([sp_package_version], [sp_package_version_major.sp_package_version_minor.sp_package_version_micro])
AC_INIT([libserialport], [sp_package_version], [martin-libserialport@earth.li],
[libserialport], [http://sigrok.org/wiki/Libserialport])
AC_CONFIG_HEADERS([config.h libserialport.h])
AC_CONFIG_MACRO_DIR([autostuff])
AC_CONFIG_AUX_DIR([autostuff])
AH_BOTTOM([#if HAVE_STRUCT_TERMIOS_C_ISPEED && HAVE_STRUCT_TERMIOS_C_OSPEED
# define HAVE_TERMIOS_SPEED 1
#endif
#if HAVE_STRUCT_TERMIOS2_C_ISPEED && HAVE_STRUCT_TERMIOS2_C_OSPEED
# define HAVE_TERMIOS2_SPEED 1
#endif])
AC_CANONICAL_TARGET
# We require at least automake 1.11 (needed for 'silent rules').
AM_INIT_AUTOMAKE([1.11 -Wall -Werror no-define check-news])
AM_SILENT_RULES([yes])
AM_INIT_AUTOMAKE([1.11 -Wall -Werror check-news])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
# Enable more compiler warnings via -Wall and -Wextra.
CFLAGS="$CFLAGS -Wall -Wextra"
# Checks for programs.
AC_PROG_CC
AC_PROG_CPP
AC_PROG_INSTALL
AC_PROG_LN_S
# Initialize libtool.
LT_INIT
AC_DEFINE([SP_PACKAGE_VERSION_MAJOR], [sp_package_version_major], [.])
AC_DEFINE([SP_PACKAGE_VERSION_MINOR], [sp_package_version_minor], [.])
AC_DEFINE([SP_PACKAGE_VERSION_MICRO], [sp_package_version_micro], [.])
AC_DEFINE([SP_PACKAGE_VERSION_STRING], ["sp_package_version"], [.])
AC_SUBST([SP_PACKAGE_VERSION], [sp_package_version])
# Initialize pkg-config.
# We require at least 0.22, as "Requires.private" behaviour changed there.
PKG_PROG_PKG_CONFIG([0.22])
# Library version for libserialport (NOT the same as the package version).
# Carefully read the libtool docs before updating these numbers!
# The algorithm for determining which number to change (and how) is nontrivial!
# http://www.gnu.org/software/libtool/manual/libtool.html#Updating-version-info
SP_LIB_VERSION_CURRENT=1
SP_LIB_VERSION_CURRENT=0
SP_LIB_VERSION_REVISION=0
SP_LIB_VERSION_AGE=1
AC_SUBST([SP_LIB_VERSION],
["$SP_LIB_VERSION_CURRENT:$SP_LIB_VERSION_REVISION:$SP_LIB_VERSION_AGE"])
SP_LIB_VERSION_AGE=0
SP_LIB_VERSION="$SP_LIB_VERSION_CURRENT:$SP_LIB_VERSION_REVISION:$SP_LIB_VERSION_AGE"
SP_LIB_LDFLAGS="-version-info $SP_LIB_VERSION"
AC_SUBST(SP_LIB_VERSION_CURRENT)
AC_SUBST(SP_LIB_VERSION_REVISION)
AC_SUBST(SP_LIB_VERSION_AGE)
AC_SUBST(SP_LIB_VERSION)
AC_SUBST(SP_LIB_LDFLAGS)
AC_DEFINE_UNQUOTED([SP_LIB_VERSION_CURRENT], [$SP_LIB_VERSION_CURRENT], [.])
AC_DEFINE_UNQUOTED([SP_LIB_VERSION_REVISION], [$SP_LIB_VERSION_REVISION], [.])
AC_DEFINE_UNQUOTED([SP_LIB_VERSION_AGE], [$SP_LIB_VERSION_AGE], [.])
AC_DEFINE_UNQUOTED([SP_LIB_VERSION_STRING], ["$SP_LIB_VERSION"], [.])
# Checks for libraries.
AM_CONDITIONAL([LINUX], [test -z "${host_os##linux*}"])
AM_CONDITIONAL([WIN32], [test -z "${host_os##mingw*}" || test -z "${host_os##cygwin*}"])
AM_CONDITIONAL([MACOSX], [test -z "${host_os##darwin*}"])
AM_CONDITIONAL([FREEBSD], [test -z "${host_os##freebsd*}"])
# This variable collects the pkg-config names of all detected libs.
# It is then used to construct the "Requires.private:" field in the
# libserialport.pc file.
SP_PKGLIBS=""
AM_COND_IF([WIN32], [SP_LIBS='-lsetupapi'], [SP_LIBS=])
AC_SUBST([SP_LIBS])
case $target_os in
*linux*)
AM_CONDITIONAL([LINUX], true)
# On Linux libudev is currently required for enumeration.
AC_ARG_WITH([libudev],
[AS_HELP_STRING([--with-libudev],
[use libudev for serial port enumeration @<:@default=check@:>@])],
[], [with_libudev=check])
AS_IF([test "x$with_libudev" != xno], [
PKG_CHECK_MODULES([libudev], [libudev >= 0],
[CFLAGS="$CFLAGS $libudev_CFLAGS"; LIBS="$LIBS $libudev_LIBS";
SP_PKGLIBS="$SP_PKGLIBS libudev"; AC_DEFINE(HAVE_LIBUDEV)])], [])
;;
*darwin*)
AM_CONDITIONAL([LINUX], false)
LDFLAGS="$LDFLAGS -Wl,-framework -Wl,IOKit -Wl,-framework -Wl,CoreFoundation"
AC_CHECK_HEADER(IOKit/IOKitLib.h, [], [AC_MSG_ERROR([IOKit/IOKitLib.h not found])])
;;
*)
AM_CONDITIONAL([LINUX], false)
esac
AM_COND_IF([MACOSX], [AC_CHECK_HEADER([IOKit/IOKitLib.h], [],
[AC_MSG_ERROR([IOKit/IOKitLib.h not found])])])
AS_CASE([$host_os], [linux*|darwin*|mingw*|cygwin*|freebsd*],, [
AC_DEFINE([NO_ENUMERATION], [1], [Enumeration is unsupported.])
AC_DEFINE([NO_PORT_METADATA], [1], [Port metadata is unavailable.])
])
AC_SYS_LARGEFILE
AC_SUBST(SP_PKGLIBS)
# Define size_t if not defined as standard.
AC_TYPE_SIZE_T
# Check for specific termios structures.
AC_CHECK_TYPES([struct termios2, struct termiox],,,
[[#include <linux/termios.h>]])
AC_CHECK_MEMBERS([struct termios.c_ispeed, struct termios.c_ospeed,
struct termios2.c_ispeed, struct termios2.c_ospeed],,,
[[#include <linux/termios.h>]])
AC_CHECK_TYPE([struct termios2], [AC_DEFINE(HAVE_TERMIOS2, 1)], [], [[#include <linux/termios.h>]])
AC_CHECK_TYPE([struct termiox], [AC_DEFINE(HAVE_TERMIOX, 1)], [], [[#include <linux/termios.h>]])
AC_CHECK_MEMBERS([struct termios.c_ispeed, struct termios.c_ospeed],
[AC_DEFINE(HAVE_TERMIOS_SPEED, 1)], [], [[#include <linux/termios.h>]])
AC_CHECK_MEMBERS([struct termios2.c_ispeed, struct termios2.c_ospeed],
[AC_DEFINE(HAVE_TERMIOS2_SPEED, 1)], [], [[#include <linux/termios.h>]])
# Check for the BOTHER definition, needed for setting arbitrary baud rates.
# We can't just #ifdef BOTHER in the code, because of the separation between
# code using libc headers and code using kernel termios.h headers.
AC_CHECK_DECLS([BOTHER],,, [[#include <linux/termios.h>]])
AC_SUBST(MAKEFLAGS, '--no-print-directory')
AC_SUBST(AM_LIBTOOLFLAGS, '--silent')
# Check for serial_struct.
AC_CHECK_TYPES([struct serial_struct],,, [[#include <linux/serial.h>]])
SP_PACKAGE_VERSION_MAJOR=sp_package_version_major
SP_PACKAGE_VERSION_MINOR=sp_package_version_minor
SP_PACKAGE_VERSION_MICRO=sp_package_version_micro
SP_PACKAGE_VERSION=sp_package_version
AC_CACHE_CHECK([for visibility control], [sp_cv_visibility_control], [
sp_saved_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -Werror"
AC_COMPILE_IFELSE([AC_LANG_SOURCE(
[[__attribute__((visibility("hidden"))) void foo(void) {}]])],
[sp_cv_visibility_control=attribute],
[AC_COMPILE_IFELSE([AC_LANG_SOURCE(
[[__declspec(dllexport) void foo(void) {}]])],
[sp_cv_visibility_control=declspec],
[sp_cv_visibility_control=none])])
CFLAGS=$sp_saved_CFLAGS
])
AS_CASE([$sp_cv_visibility_control],
[attribute], [SP_API='__attribute__((visibility("default")))'
SP_PRIV='__attribute__((visibility("hidden")))'],
[declspec], [SP_API='__declspec(dllexport)' SP_PRIV=],
[SP_API= SP_PRIV=])
AC_DEFINE_UNQUOTED([SP_API], [$SP_API], [Macro preceding public API functions])
AC_DEFINE_UNQUOTED([SP_PRIV], [$SP_PRIV], [Macro preceding private functions])
AC_SUBST(SP_PACKAGE_VERSION_MAJOR)
AC_SUBST(SP_PACKAGE_VERSION_MINOR)
AC_SUBST(SP_PACKAGE_VERSION_MICRO)
AC_SUBST(SP_PACKAGE_VERSION)
AC_CONFIG_FILES([Makefile libserialport.pc])
AC_CONFIG_FILES([Makefile libserialport.h libserialport.pc])
AC_OUTPUT
cat >&AS_MESSAGE_FD <<_EOF
libserialport configuration summary:
- Package version (major.minor.micro): $SP_PACKAGE_VERSION
- Library version (current:revision:age): $SP_LIB_VERSION
- Prefix: $prefix
- Building on: $build
- Building for: $host
_EOF
echo
echo "libserialport configuration summary:"
echo
echo " - Package version (major.minor.micro): $SP_PACKAGE_VERSION"
echo " - Library version (current:revision:age): $SP_LIB_VERSION"
echo " - Prefix: $prefix"
echo " - Building on: $build"
echo " - Building for: $host"
echo

384
freebsd.c
View File

@ -1,384 +0,0 @@
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2015 Uffe Jakobsen <uffe@uffe.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* FreeBSD platform specific serial port information:
*
* FreeBSD has two device nodes for each serial port:
*
* 1) a call-in port
* 2) a call-out port
*
* Quoting FreeBSD Handbook section 26.2.1
* (https://www.freebsd.org/doc/handbook/serial.html)
*
* In FreeBSD, each serial port is accessed through an entry in /dev.
* There are two different kinds of entries:
*
* 1) Call-in ports are named /dev/ttyuN where N is the port number,
* starting from zero. If a terminal is connected to the first serial port
* (COM1), use /dev/ttyu0 to refer to the terminal. If the terminal is on
* the second serial port (COM2), use /dev/ttyu1, and so forth.
* Generally, the call-in port is used for terminals. Call-in ports require
* that the serial line assert the "Data Carrier Detect" signal to work
* correctly.
*
* 2) Call-out ports are named /dev/cuauN on FreeBSD versions 10.x and higher
* and /dev/cuadN on FreeBSD versions 9.x and lower. Call-out ports are
* usually not used for terminals, but are used for modems. The call-out
* port can be used if the serial cable or the terminal does not support
* the "Data Carrier Detect" signal.
*
* FreeBSD also provides initialization devices (/dev/ttyuN.init and
* /dev/cuauN.init or /dev/cuadN.init) and locking devices (/dev/ttyuN.lock
* and /dev/cuauN.lock or /dev/cuadN.lock). The initialization devices are
* used to initialize communications port parameters each time a port is
* opened, such as crtscts for modems which use RTS/CTS signaling for flow
* control. The locking devices are used to lock flags on ports to prevent
* users or programs changing certain parameters.
*
* In line with the above device naming USB-serial devices have
* the following naming:
*
* 1) call-in ports: /dev/ttyUN where N is the port number
* 2) call-out ports: /dev/cuaUN where N is the port number
*
* See also: ucom(4), https://www.freebsd.org/cgi/man.cgi?query=ucom
*
* Getting USB serial port device description:
*
* In order to query USB serial ports for device description - the mapping
* between the kernel device driver instances must be correlated with the
* above mentioned device nodes.
*
* 1) For each USB-serial port (/dev/cuaUN) use libusb to lookup the device
* description and the name of the kernel device driver instance.
* 2) Query sysctl for the kernel device driver instance info.
* 3) Derive the ttyname and (serial) port count for the kernel device
* driver instance.
* 4) If sysctl ttyname and port matches /dev/cuaUN apply the libusb
* device description.
*/
#include <config.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <dirent.h>
#include <libusb20.h>
#include <libusb20_desc.h>
#include "libserialport.h"
#include "libserialport_internal.h"
#define DEV_CUA_PATH "/dev/cua"
//#define DBG(...) printf(__VA_ARGS__)
#define DBG(...)
static char *strrspn(const char *s, const char *charset)
{
char *t = (char *)s + strlen(s);
while (t != (char *)s)
if (!strchr(charset, *(--t)))
return ++t;
return t;
}
static int strend(const char *str, const char *pattern)
{
size_t slen = strlen(str);
size_t plen = strlen(pattern);
if (slen >= plen)
return (!memcmp(pattern, (str + slen - plen), plen));
return 0;
}
static int libusb_query_port(struct libusb20_device *dev, int idx,
char **drv_name_str, char **drv_inst_str)
{
int rc;
char *j;
char sbuf[FILENAME_MAX];
if (!drv_name_str || !drv_inst_str)
return -1;
rc = libusb20_dev_kernel_driver_active(dev, idx);
if (rc < 0)
return rc;
sbuf[0] = 0;
libusb20_dev_get_iface_desc(dev, idx, (char *)&sbuf,
(uint8_t)sizeof(sbuf) - 1);
if (sbuf[0] == 0)
return rc;
DBG("Device interface descriptor: idx=%003d '%s'\n", idx, sbuf);
j = strchr(sbuf, ':');
if (j > sbuf) {
sbuf[j - sbuf] = 0;
/*
* The device driver name may contain digits that
* is not a part of the device instance number - like "u3g".
*/
j = strrspn(sbuf, "0123456789");
if (j > sbuf) {
*drv_name_str = strndup(sbuf, j - sbuf);
*drv_inst_str = strdup((j));
}
}
return rc;
}
static int sysctl_query_dev_drv(const char *drv_name_str,
const char *drv_inst_str, char **ttyname, int *const ttyport_cnt)
{
int rc;
char sbuf[FILENAME_MAX];
char tbuf[FILENAME_MAX];
size_t tbuf_len;
if (!ttyname || !ttyport_cnt)
return -1;
snprintf(sbuf, sizeof(sbuf), "dev.%s.%s.ttyname", drv_name_str,
drv_inst_str);
tbuf_len = sizeof(tbuf) - 1;
if ((rc = sysctlbyname(sbuf, tbuf, &tbuf_len, NULL, 0)) != 0)
return rc;
tbuf[tbuf_len] = 0;
*ttyname = strndup(tbuf, tbuf_len);
DBG("sysctl: '%s' (%d) (%d): '%.*s'\n", sbuf, rc, (int)tbuf_len,
(int)tbuf_len, tbuf);
snprintf(sbuf, sizeof(sbuf), "dev.%s.%s.ttyports",
drv_name_str, drv_inst_str);
tbuf_len = sizeof(tbuf) - 1;
rc = sysctlbyname(sbuf, tbuf, &tbuf_len, NULL, 0);
if (rc == 0) {
*ttyport_cnt = *(uint32_t *)tbuf;
DBG("sysctl: '%s' (%d) (%d): '%d'\n", sbuf, rc,
(int)tbuf_len, (int)ttyport_cnt);
} else {
*ttyport_cnt = 0;
}
return rc;
}
static int populate_port_struct_from_libusb_desc(struct sp_port *const port,
struct libusb20_device *dev)
{
char tbuf[FILENAME_MAX];
/* Populate port structure from libusb description. */
struct LIBUSB20_DEVICE_DESC_DECODED *dev_desc =
libusb20_dev_get_device_desc(dev);
if (!dev_desc)
return -1;
port->transport = SP_TRANSPORT_USB;
port->usb_vid = dev_desc->idVendor;
port->usb_pid = dev_desc->idProduct;
port->usb_bus = libusb20_dev_get_bus_number(dev);
port->usb_address = libusb20_dev_get_address(dev);
if (libusb20_dev_req_string_simple_sync
(dev, dev_desc->iManufacturer, tbuf, sizeof(tbuf)) == 0) {
port->usb_manufacturer = strdup(tbuf);
}
if (libusb20_dev_req_string_simple_sync
(dev, dev_desc->iProduct, tbuf, sizeof(tbuf)) == 0) {
port->usb_product = strdup(tbuf);
}
if (libusb20_dev_req_string_simple_sync
(dev, dev_desc->iSerialNumber, tbuf, sizeof(tbuf)) == 0) {
port->usb_serial = strdup(tbuf);
}
/* If present, add serial to description for better identification. */
tbuf[0] = '\0';
if (port->usb_product && port->usb_product[0])
strncat(tbuf, port->usb_product, sizeof(tbuf) - 1);
else
strncat(tbuf, libusb20_dev_get_desc(dev), sizeof(tbuf) - 1);
if (port->usb_serial && port->usb_serial[0]) {
strncat(tbuf, " ", sizeof(tbuf) - 1);
strncat(tbuf, port->usb_serial, sizeof(tbuf) - 1);
}
port->description = strdup(tbuf);
port->bluetooth_address = NULL;
return 0;
}
SP_PRIV enum sp_return get_port_details(struct sp_port *port)
{
int rc;
struct libusb20_backend *be;
struct libusb20_device *dev, *dev_last;
char tbuf[FILENAME_MAX];
char *cua_sfx;
int cua_dev_found;
uint8_t idx;
int sub_inst;
DBG("Portname: '%s'\n", port->name);
if (!strncmp(port->name, DEV_CUA_PATH, strlen(DEV_CUA_PATH))) {
cua_sfx = port->name + strlen(DEV_CUA_PATH);
DBG("'%s': '%s'\n", DEV_CUA_PATH, cua_sfx);
} else {
RETURN_ERROR(SP_ERR_ARG, "Device name not recognized");
}
/* Native UART enumeration. */
if ((cua_sfx[0] == 'u') || (cua_sfx[0] == 'd')) {
port->transport = SP_TRANSPORT_NATIVE;
snprintf(tbuf, sizeof(tbuf), "cua%s", cua_sfx);
port->description = strdup(tbuf);
RETURN_OK();
}
/* USB device enumeration. */
dev = dev_last = NULL;
be = libusb20_be_alloc_default();
cua_dev_found = 0;
while (cua_dev_found == 0) {
dev = libusb20_be_device_foreach(be, dev_last);
if (!dev)
break;
libusb20_dev_open(dev, 0);
DBG("Device descriptor: '%s'\n", libusb20_dev_get_desc(dev));
for (idx = 0; idx <= UINT8_MAX - 1; idx++) {
char *drv_name_str = NULL;
char *drv_inst_str = NULL;
char *ttyname = NULL;
int ttyport_cnt;
rc = libusb_query_port(dev, idx, &drv_name_str, &drv_inst_str);
if (rc == 0) {
rc = sysctl_query_dev_drv(drv_name_str,
drv_inst_str, &ttyname, &ttyport_cnt);
if (rc == 0) {
/* Handle multiple subinstances of serial ports in the same driver instance. */
for (sub_inst = 0; sub_inst < ttyport_cnt; sub_inst++) {
if (ttyport_cnt == 1)
snprintf(tbuf, sizeof(tbuf), "%s", ttyname);
else
snprintf(tbuf, sizeof(tbuf), "%s.%d", ttyname, sub_inst);
if (!strcmp(cua_sfx, tbuf)) {
DBG("MATCH: '%s' == '%s'\n", cua_sfx, tbuf);
cua_dev_found = 1;
populate_port_struct_from_libusb_desc(port, dev);
break; /* Break out of sub instance loop. */
}
}
}
}
/* Clean up. */
if (ttyname)
free(ttyname);
if (drv_name_str)
free(drv_name_str);
if (drv_inst_str)
free(drv_inst_str);
if (cua_dev_found)
break; /* Break out of USB device port idx loop. */
}
libusb20_dev_close(dev);
dev_last = dev;
}
libusb20_be_free(be);
if (cua_dev_found == 0)
DBG("WARN: Found no match '%s' %s'\n", port->name, cua_sfx);
RETURN_OK();
}
SP_PRIV enum sp_return list_ports(struct sp_port ***list)
{
DIR *dir;
struct dirent entry;
struct dirent *result;
struct termios tios;
char name[PATH_MAX];
int fd, ret;
DEBUG("Enumerating tty devices");
if (!(dir = opendir("/dev")))
RETURN_FAIL("Could not open dir /dev");
DEBUG("Iterating over results");
while (!readdir_r(dir, &entry, &result) && result) {
ret = SP_OK;
if (entry.d_type != DT_CHR)
continue;
if (strncmp(entry.d_name, "cuaU", 4) != 0)
if (strncmp(entry.d_name, "cuau", 4) != 0)
if (strncmp(entry.d_name, "cuad", 4) != 0)
continue;
if (strend(entry.d_name, ".init"))
continue;
if (strend(entry.d_name, ".lock"))
continue;
snprintf(name, sizeof(name), "/dev/%s", entry.d_name);
DEBUG_FMT("Found device %s", name);
/* Check that we can open tty/cua device in rw mode - we need that. */
if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY | O_TTY_INIT | O_CLOEXEC)) < 0) {
DEBUG("Open failed, skipping");
continue;
}
/* Sanity check if we got a real tty. */
if (!isatty(fd)) {
close(fd);
continue;
}
ret = tcgetattr(fd, &tios);
close(fd);
if (ret < 0 || cfgetospeed(&tios) <= 0 || cfgetispeed(&tios) <= 0)
continue;
DEBUG_FMT("Found port %s", name);
DBG("%s: %s\n", __func__, entry.d_name);
*list = list_append(*list, name);
if (!list) {
SET_ERROR(ret, SP_ERR_MEM, "List append failed");
break;
}
}
closedir(dir);
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -6,8 +6,7 @@ includedir=@includedir@
Name: libserialport
Description: Cross-platform serial port access library.
URL: http://sigrok.org/wiki/Libserialport
Requires.private:
Version: @SP_PACKAGE_VERSION@
Requires.private: @SP_PKGLIBS@
Version: @VERSION@
Libs: -L${libdir} -lserialport
Libs.private: @SP_LIBS@
Cflags: -I${includedir}

View File

@ -1,237 +0,0 @@
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2014 Martin Ling <martin-libserialport@earth.li>
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef LIBSERIALPORT_LIBSERIALPORT_INTERNAL_H
#define LIBSERIALPORT_LIBSERIALPORT_INTERNAL_H
#ifdef __linux__
/* For timeradd, timersub, timercmp. */
#define _BSD_SOURCE 1 /* for glibc < 2.19 */
#define _DEFAULT_SOURCE 1 /* for glibc >= 2.20 */
#endif
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#ifdef _WIN32
#include <windows.h>
#include <tchar.h>
#include <setupapi.h>
#include <cfgmgr32.h>
#undef DEFINE_GUID
#define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) \
static const GUID name = { l,w1,w2,{ b1,b2,b3,b4,b5,b6,b7,b8 } }
#include <usbioctl.h>
#include <usbiodef.h>
#else
#include <limits.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <limits.h>
#include <poll.h>
#endif
#ifdef __APPLE__
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/serial/ioss.h>
#include <sys/syslimits.h>
#endif
#ifdef __linux__
#include <dirent.h>
#ifndef __ANDROID__
#include "linux/serial.h"
#endif
#include "linux_termios.h"
/* TCGETX/TCSETX is not available everywhere. */
#if defined(TCGETX) && defined(TCSETX) && defined(HAVE_STRUCT_TERMIOX)
#define USE_TERMIOX
#endif
#endif
/* TIOCINQ/TIOCOUTQ is not available everywhere. */
#if !defined(TIOCINQ) && defined(FIONREAD)
#define TIOCINQ FIONREAD
#endif
#if !defined(TIOCOUTQ) && defined(FIONWRITE)
#define TIOCOUTQ FIONWRITE
#endif
/* Non-standard baudrates are not available everywhere. */
#if (defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)) && defined(HAVE_DECL_BOTHER)
#define USE_TERMIOS_SPEED
#endif
struct sp_port {
char *name;
char *description;
enum sp_transport transport;
int usb_bus;
int usb_address;
int usb_vid;
int usb_pid;
char *usb_manufacturer;
char *usb_product;
char *usb_serial;
char *bluetooth_address;
#ifdef _WIN32
char *usb_path;
HANDLE hdl;
COMMTIMEOUTS timeouts;
OVERLAPPED write_ovl;
OVERLAPPED read_ovl;
OVERLAPPED wait_ovl;
DWORD events;
BYTE pending_byte;
BOOL writing;
BOOL wait_running;
#else
int fd;
#endif
};
struct sp_port_config {
int baudrate;
int bits;
enum sp_parity parity;
int stopbits;
enum sp_rts rts;
enum sp_cts cts;
enum sp_dtr dtr;
enum sp_dsr dsr;
enum sp_xonxoff xon_xoff;
};
struct port_data {
#ifdef _WIN32
DCB dcb;
#else
struct termios term;
int controlbits;
int termiox_supported;
int rts_flow;
int cts_flow;
int dtr_flow;
int dsr_flow;
#endif
};
#ifdef _WIN32
typedef HANDLE event_handle;
#else
typedef int event_handle;
#endif
/* Standard baud rates. */
#ifdef _WIN32
#define BAUD_TYPE DWORD
#define BAUD(n) {CBR_##n, n}
#else
#define BAUD_TYPE speed_t
#define BAUD(n) {B##n, n}
#endif
struct std_baudrate {
BAUD_TYPE index;
int value;
};
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
extern void (*sp_debug_handler)(const char *format, ...);
/* Debug output macros. */
#define DEBUG_FMT(fmt, ...) do { \
if (sp_debug_handler) \
sp_debug_handler(fmt ".\n", __VA_ARGS__); \
} while (0)
#define DEBUG(msg) DEBUG_FMT(msg, NULL)
#define DEBUG_ERROR(err, msg) DEBUG_FMT("%s returning " #err ": " msg, __func__)
#define DEBUG_FAIL(msg) do { \
char *errmsg = sp_last_error_message(); \
DEBUG_FMT("%s returning SP_ERR_FAIL: " msg ": %s", __func__, errmsg); \
sp_free_error_message(errmsg); \
} while (0);
#define RETURN() do { \
DEBUG_FMT("%s returning", __func__); \
return; \
} while (0)
#define RETURN_CODE(x) do { \
DEBUG_FMT("%s returning " #x, __func__); \
return x; \
} while (0)
#define RETURN_CODEVAL(x) do { \
switch (x) { \
case SP_OK: RETURN_CODE(SP_OK); \
case SP_ERR_ARG: RETURN_CODE(SP_ERR_ARG); \
case SP_ERR_FAIL: RETURN_CODE(SP_ERR_FAIL); \
case SP_ERR_MEM: RETURN_CODE(SP_ERR_MEM); \
case SP_ERR_SUPP: RETURN_CODE(SP_ERR_SUPP); \
default: RETURN_CODE(SP_ERR_FAIL); \
} \
} while (0)
#define RETURN_OK() RETURN_CODE(SP_OK);
#define RETURN_ERROR(err, msg) do { \
DEBUG_ERROR(err, msg); \
return err; \
} while (0)
#define RETURN_FAIL(msg) do { \
DEBUG_FAIL(msg); \
return SP_ERR_FAIL; \
} while (0)
#define RETURN_INT(x) do { \
int _x = x; \
DEBUG_FMT("%s returning %d", __func__, _x); \
return _x; \
} while (0)
#define RETURN_STRING(x) do { \
char *_x = x; \
DEBUG_FMT("%s returning %s", __func__, _x); \
return _x; \
} while (0)
#define RETURN_POINTER(x) do { \
void *_x = x; \
DEBUG_FMT("%s returning %p", __func__, _x); \
return _x; \
} while (0)
#define SET_ERROR(val, err, msg) do { DEBUG_ERROR(err, msg); val = err; } while (0)
#define SET_FAIL(val, msg) do { DEBUG_FAIL(msg); val = SP_ERR_FAIL; } while (0)
#define TRACE(fmt, ...) DEBUG_FMT("%s(" fmt ") called", __func__, __VA_ARGS__)
#define TRACE_VOID() DEBUG_FMT("%s() called", __func__)
#define TRY(x) do { int retval = x; if (retval != SP_OK) RETURN_CODEVAL(retval); } while (0)
SP_PRIV struct sp_port **list_append(struct sp_port **list, const char *portname);
/* OS-specific Helper functions. */
SP_PRIV enum sp_return get_port_details(struct sp_port *port);
SP_PRIV enum sp_return list_ports(struct sp_port ***list);
#endif

247
linux.c
View File

@ -1,247 +0,0 @@
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2013 Martin Ling <martin-libserialport@earth.li>
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "libserialport.h"
#include "libserialport_internal.h"
SP_PRIV enum sp_return get_port_details(struct sp_port *port)
{
/*
* Description limited to 127 char, anything longer
* would not be user friendly anyway.
*/
char description[128];
int bus, address;
unsigned int vid, pid;
char manufacturer[128], product[128], serial[128];
char baddr[32];
const char dir_name[] = "/sys/class/tty/%s/device/%s%s";
char sub_dir[32] = "", file_name[PATH_MAX];
char *ptr, *dev = port->name + 5;
FILE *file;
int i, count;
struct stat statbuf;
if (strncmp(port->name, "/dev/", 5))
RETURN_ERROR(SP_ERR_ARG, "Device name not recognized");
snprintf(file_name, sizeof(file_name), "/sys/class/tty/%s", dev);
if (lstat(file_name, &statbuf) == -1)
RETURN_ERROR(SP_ERR_ARG, "Device not found");
if (!S_ISLNK(statbuf.st_mode))
snprintf(file_name, sizeof(file_name), "/sys/class/tty/%s/device", dev);
count = readlink(file_name, file_name, sizeof(file_name));
if (count <= 0 || count >= (int)(sizeof(file_name) - 1))
RETURN_ERROR(SP_ERR_ARG, "Device not found");
file_name[count] = 0;
if (strstr(file_name, "bluetooth"))
port->transport = SP_TRANSPORT_BLUETOOTH;
else if (strstr(file_name, "usb"))
port->transport = SP_TRANSPORT_USB;
if (port->transport == SP_TRANSPORT_USB) {
for (i = 0; i < 5; i++) {
strcat(sub_dir, "../");
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "busnum");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%d", &bus);
fclose(file);
if (count != 1)
continue;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "devnum");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%d", &address);
fclose(file);
if (count != 1)
continue;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "idVendor");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%4x", &vid);
fclose(file);
if (count != 1)
continue;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "idProduct");
if (!(file = fopen(file_name, "r")))
continue;
count = fscanf(file, "%4x", &pid);
fclose(file);
if (count != 1)
continue;
port->usb_bus = bus;
port->usb_address = address;
port->usb_vid = vid;
port->usb_pid = pid;
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "product");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(description, sizeof(description), file))) {
ptr = description + strlen(description) - 1;
if (ptr >= description && *ptr == '\n')
*ptr = 0;
port->description = strdup(description);
}
fclose(file);
}
if (!file || !ptr)
port->description = strdup(dev);
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "manufacturer");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(manufacturer, sizeof(manufacturer), file))) {
ptr = manufacturer + strlen(manufacturer) - 1;
if (ptr >= manufacturer && *ptr == '\n')
*ptr = 0;
port->usb_manufacturer = strdup(manufacturer);
}
fclose(file);
}
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "product");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(product, sizeof(product), file))) {
ptr = product + strlen(product) - 1;
if (ptr >= product && *ptr == '\n')
*ptr = 0;
port->usb_product = strdup(product);
}
fclose(file);
}
snprintf(file_name, sizeof(file_name), dir_name, dev, sub_dir, "serial");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(serial, sizeof(serial), file))) {
ptr = serial + strlen(serial) - 1;
if (ptr >= serial && *ptr == '\n')
*ptr = 0;
port->usb_serial = strdup(serial);
}
fclose(file);
}
/* If present, add serial to description for better identification. */
if (port->usb_serial && strlen(port->usb_serial)) {
snprintf(description, sizeof(description),
"%s - %s", port->description, port->usb_serial);
if (port->description)
free(port->description);
port->description = strdup(description);
}
break;
}
} else {
port->description = strdup(dev);
if (port->transport == SP_TRANSPORT_BLUETOOTH) {
snprintf(file_name, sizeof(file_name), dir_name, dev, "", "address");
if ((file = fopen(file_name, "r"))) {
if ((ptr = fgets(baddr, sizeof(baddr), file))) {
ptr = baddr + strlen(baddr) - 1;
if (ptr >= baddr && *ptr == '\n')
*ptr = 0;
port->bluetooth_address = strdup(baddr);
}
fclose(file);
}
}
}
RETURN_OK();
}
SP_PRIV enum sp_return list_ports(struct sp_port ***list)
{
char name[PATH_MAX], target[PATH_MAX];
struct dirent entry, *result;
#ifdef HAVE_STRUCT_SERIAL_STRUCT
struct serial_struct serial_info;
int ioctl_result;
#endif
char buf[sizeof(entry.d_name) + 23];
int len, fd;
DIR *dir;
int ret = SP_OK;
struct stat statbuf;
DEBUG("Enumerating tty devices");
if (!(dir = opendir("/sys/class/tty")))
RETURN_FAIL("Could not open /sys/class/tty");
DEBUG("Iterating over results");
while (!readdir_r(dir, &entry, &result) && result) {
snprintf(buf, sizeof(buf), "/sys/class/tty/%s", entry.d_name);
if (lstat(buf, &statbuf) == -1)
continue;
if (!S_ISLNK(statbuf.st_mode))
snprintf(buf, sizeof(buf), "/sys/class/tty/%s/device", entry.d_name);
len = readlink(buf, target, sizeof(target));
if (len <= 0 || len >= (int)(sizeof(target) - 1))
continue;
target[len] = 0;
if (strstr(target, "virtual"))
continue;
snprintf(name, sizeof(name), "/dev/%s", entry.d_name);
DEBUG_FMT("Found device %s", name);
if (strstr(target, "serial8250")) {
/*
* The serial8250 driver has a hardcoded number of ports.
* The only way to tell which actually exist on a given system
* is to try to open them and make an ioctl call.
*/
DEBUG("serial8250 device, attempting to open");
if ((fd = open(name, O_RDWR | O_NONBLOCK | O_NOCTTY)) < 0) {
DEBUG("Open failed, skipping");
continue;
}
#ifdef HAVE_STRUCT_SERIAL_STRUCT
ioctl_result = ioctl(fd, TIOCGSERIAL, &serial_info);
#endif
close(fd);
#ifdef HAVE_STRUCT_SERIAL_STRUCT
if (ioctl_result != 0) {
DEBUG("ioctl failed, skipping");
continue;
}
if (serial_info.type == PORT_UNKNOWN) {
DEBUG("Port type is unknown, skipping");
continue;
}
#endif
}
DEBUG_FMT("Found port %s", name);
*list = list_append(*list, name);
if (!list) {
SET_ERROR(ret, SP_ERR_MEM, "List append failed");
break;
}
}
closedir(dir);
return ret;
}

View File

@ -33,42 +33,40 @@
* TCSETX/TCGETX ioctls used with struct termiox, others do not.
*/
#include <config.h>
#include <stdlib.h>
#include <linux/termios.h>
#include "linux_termios.h"
SP_PRIV unsigned long get_termios_get_ioctl(void)
int get_termios_get_ioctl(void)
{
#ifdef HAVE_STRUCT_TERMIOS2
#ifdef HAVE_TERMIOS2
return TCGETS2;
#else
return TCGETS;
#endif
}
SP_PRIV unsigned long get_termios_set_ioctl(void)
int get_termios_set_ioctl(void)
{
#ifdef HAVE_STRUCT_TERMIOS2
#ifdef HAVE_TERMIOS2
return TCSETS2;
#else
return TCSETS;
#endif
}
SP_PRIV size_t get_termios_size(void)
int get_termios_size(void)
{
#ifdef HAVE_STRUCT_TERMIOS2
#ifdef HAVE_TERMIOS2
return sizeof(struct termios2);
#else
return sizeof(struct termios);
#endif
}
#if (defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)) && defined(HAVE_DECL_BOTHER)
SP_PRIV int get_termios_speed(void *data)
#if defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)
int get_termios_speed(void *data)
{
#ifdef HAVE_STRUCT_TERMIOS2
#ifdef HAVE_TERMIOS2
struct termios2 *term = (struct termios2 *) data;
#else
struct termios *term = (struct termios *) data;
@ -79,9 +77,9 @@ SP_PRIV int get_termios_speed(void *data)
return term->c_ispeed;
}
SP_PRIV void set_termios_speed(void *data, int speed)
void set_termios_speed(void *data, int speed)
{
#ifdef HAVE_STRUCT_TERMIOS2
#ifdef HAVE_TERMIOS2
struct termios2 *term = (struct termios2 *) data;
#else
struct termios *term = (struct termios *) data;
@ -92,13 +90,13 @@ SP_PRIV void set_termios_speed(void *data, int speed)
}
#endif
#ifdef HAVE_STRUCT_TERMIOX
SP_PRIV size_t get_termiox_size(void)
#ifdef HAVE_TERMIOX
int get_termiox_size(void)
{
return sizeof(struct termiox);
}
SP_PRIV int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr)
int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr)
{
struct termiox *termx = (struct termiox *) data;
int flags = 0;
@ -111,7 +109,7 @@ SP_PRIV int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr)
return flags;
}
SP_PRIV void set_termiox_flow(void *data, int rts, int cts, int dtr, int dsr)
void set_termiox_flow(void *data, int rts, int cts, int dtr, int dsr)
{
struct termiox *termx = (struct termiox *) data;

View File

@ -20,15 +20,13 @@
#ifndef LIBSERIALPORT_LINUX_TERMIOS_H
#define LIBSERIALPORT_LINUX_TERMIOS_H
#include <stdlib.h>
SP_PRIV unsigned long get_termios_get_ioctl(void);
SP_PRIV unsigned long get_termios_set_ioctl(void);
SP_PRIV size_t get_termios_size(void);
SP_PRIV int get_termios_speed(void *data);
SP_PRIV void set_termios_speed(void *data, int speed);
SP_PRIV size_t get_termiox_size(void);
SP_PRIV int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr);
SP_PRIV void set_termiox_flow(void *data, int rts, int cts, int dtr, int dsr);
int get_termios_get_ioctl(void);
int get_termios_set_ioctl(void);
int get_termios_size(void);
int get_termios_speed(void *data);
void set_termios_speed(void *data, int speed);
int get_termiox_size(void);
int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr);
void set_termiox_flow(void *data, int rts, int cts, int dtr, int dsr);
#endif

243
macosx.c
View File

@ -1,243 +0,0 @@
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2013-2014 Martin Ling <martin-libserialport@earth.li>
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "libserialport.h"
#include "libserialport_internal.h"
SP_PRIV enum sp_return get_port_details(struct sp_port *port)
{
/*
* Description limited to 127 char, anything longer
* would not be user friendly anyway.
*/
char description[128];
int bus, address, vid, pid = -1;
char manufacturer[128], product[128], serial[128];
CFMutableDictionaryRef classes;
io_iterator_t iter;
io_object_t ioport, ioparent;
CFTypeRef cf_property, cf_bus, cf_address, cf_vendor, cf_product;
Boolean result;
char path[PATH_MAX], class[16];
DEBUG("Getting serial port list");
if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue)))
RETURN_FAIL("IOServiceMatching() failed");
if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes,
&iter) != KERN_SUCCESS)
RETURN_FAIL("IOServiceGetMatchingServices() failed");
DEBUG("Iterating over results");
while ((ioport = IOIteratorNext(iter))) {
if (!(cf_property = IORegistryEntryCreateCFProperty(ioport,
CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0))) {
IOObjectRelease(ioport);
continue;
}
result = CFStringGetCString(cf_property, path, sizeof(path),
kCFStringEncodingASCII);
CFRelease(cf_property);
if (!result || strcmp(path, port->name)) {
IOObjectRelease(ioport);
continue;
}
DEBUG_FMT("Found port %s", path);
IORegistryEntryGetParentEntry(ioport, kIOServicePlane, &ioparent);
if ((cf_property=IORegistryEntrySearchCFProperty(ioparent,kIOServicePlane,
CFSTR("IOClass"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, class, sizeof(class),
kCFStringEncodingASCII) &&
strstr(class, "USB")) {
DEBUG("Found USB class device");
port->transport = SP_TRANSPORT_USB;
}
CFRelease(cf_property);
}
if ((cf_property=IORegistryEntrySearchCFProperty(ioparent,kIOServicePlane,
CFSTR("IOProviderClass"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, class, sizeof(class),
kCFStringEncodingASCII) &&
strstr(class, "USB")) {
DEBUG("Found USB class device");
port->transport = SP_TRANSPORT_USB;
}
CFRelease(cf_property);
}
IOObjectRelease(ioparent);
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Interface Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents)) ||
(cf_property = IORegistryEntryCreateCFProperty(ioport,
CFSTR(kIOTTYDeviceKey), kCFAllocatorDefault, 0))) {
if (CFStringGetCString(cf_property, description, sizeof(description),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found description %s", description);
port->description = strdup(description);
}
CFRelease(cf_property);
} else {
DEBUG("No description for this device");
}
cf_bus = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("USBBusNumber"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
cf_address = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("USB Address"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
if (cf_bus && cf_address &&
CFNumberGetValue(cf_bus , kCFNumberIntType, &bus) &&
CFNumberGetValue(cf_address, kCFNumberIntType, &address)) {
DEBUG_FMT("Found matching USB bus:address %03d:%03d", bus, address);
port->usb_bus = bus;
port->usb_address = address;
}
if (cf_bus)
CFRelease(cf_bus);
if (cf_address)
CFRelease(cf_address);
cf_vendor = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("idVendor"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
cf_product = IORegistryEntrySearchCFProperty(ioport, kIOServicePlane,
CFSTR("idProduct"),
kCFAllocatorDefault,
kIORegistryIterateRecursively
| kIORegistryIterateParents);
if (cf_vendor && cf_product &&
CFNumberGetValue(cf_vendor , kCFNumberIntType, &vid) &&
CFNumberGetValue(cf_product, kCFNumberIntType, &pid)) {
DEBUG_FMT("Found matching USB VID:PID %04X:%04X", vid, pid);
port->usb_vid = vid;
port->usb_pid = pid;
}
if (cf_vendor)
CFRelease(cf_vendor);
if (cf_product)
CFRelease(cf_product);
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Vendor Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, manufacturer, sizeof(manufacturer),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found manufacturer %s", manufacturer);
port->usb_manufacturer = strdup(manufacturer);
}
CFRelease(cf_property);
}
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Product Name"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, product, sizeof(product),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found product name %s", product);
port->usb_product = strdup(product);
}
CFRelease(cf_property);
}
if ((cf_property = IORegistryEntrySearchCFProperty(ioport,kIOServicePlane,
CFSTR("USB Serial Number"), kCFAllocatorDefault,
kIORegistryIterateRecursively | kIORegistryIterateParents))) {
if (CFStringGetCString(cf_property, serial, sizeof(serial),
kCFStringEncodingASCII)) {
DEBUG_FMT("Found serial number %s", serial);
port->usb_serial = strdup(serial);
}
CFRelease(cf_property);
}
IOObjectRelease(ioport);
break;
}
IOObjectRelease(iter);
RETURN_OK();
}
SP_PRIV enum sp_return list_ports(struct sp_port ***list)
{
CFMutableDictionaryRef classes;
io_iterator_t iter;
char path[PATH_MAX];
io_object_t port;
CFTypeRef cf_path;
Boolean result;
int ret = SP_OK;
DEBUG("Creating matching dictionary");
if (!(classes = IOServiceMatching(kIOSerialBSDServiceValue))) {
SET_FAIL(ret, "IOServiceMatching() failed");
goto out_done;
}
DEBUG("Getting matching services");
if (IOServiceGetMatchingServices(kIOMasterPortDefault, classes,
&iter) != KERN_SUCCESS) {
SET_FAIL(ret, "IOServiceGetMatchingServices() failed");
goto out_done;
}
DEBUG("Iterating over results");
while ((port = IOIteratorNext(iter))) {
cf_path = IORegistryEntryCreateCFProperty(port,
CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
if (cf_path) {
result = CFStringGetCString(cf_path, path, sizeof(path),
kCFStringEncodingASCII);
CFRelease(cf_path);
if (result) {
DEBUG_FMT("Found port %s", path);
if (!(*list = list_append(*list, path))) {
SET_ERROR(ret, SP_ERR_MEM, "List append failed");
IOObjectRelease(port);
goto out;
}
}
}
IOObjectRelease(port);
}
out:
IOObjectRelease(iter);
out_done:
return ret;
}

File diff suppressed because it is too large Load Diff

553
windows.c
View File

@ -1,553 +0,0 @@
/*
* This file is part of the libserialport project.
*
* Copyright (C) 2013-2014 Martin Ling <martin-libserialport@earth.li>
* Copyright (C) 2014 Aurelien Jacobs <aurel@gnuage.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include "libserialport.h"
#include "libserialport_internal.h"
/* USB path is a string of at most 8 decimal numbers < 128 separated by dots. */
#define MAX_USB_PATH ((8 * 3) + (7 * 1) + 1)
static void enumerate_hub(struct sp_port *port, const char *hub_name,
const char *parent_path, DEVINST dev_inst);
static char *wc_to_utf8(PWCHAR wc_buffer, ULONG size)
{
WCHAR wc_str[(size / sizeof(WCHAR)) + 1];
char *utf8_str;
/* Zero-terminate the wide char string. */
memcpy(wc_str, wc_buffer, size);
wc_str[sizeof(wc_str) - 1] = 0;
/* Compute the size of the UTF-8 converted string. */
if (!(size = WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wc_str, -1,
NULL, 0, NULL, NULL)))
return NULL;
/* Allocate UTF-8 output buffer. */
if (!(utf8_str = malloc(size)))
return NULL;
/* Actually converted to UTF-8. */
if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wc_str, -1,
utf8_str, size, NULL, NULL)) {
free(utf8_str);
return NULL;
}
return utf8_str;
}
static char *get_root_hub_name(HANDLE host_controller)
{
USB_ROOT_HUB_NAME root_hub_name;
PUSB_ROOT_HUB_NAME root_hub_name_wc;
char *root_hub_name_utf8;
ULONG size = 0;
/* Compute the size of the root hub name string. */
if (!DeviceIoControl(host_controller, IOCTL_USB_GET_ROOT_HUB_NAME, 0, 0,
&root_hub_name, sizeof(root_hub_name), &size, NULL))
return NULL;
/* Allocate wide char root hub name string. */
size = root_hub_name.ActualLength;
if (!(root_hub_name_wc = malloc(size)))
return NULL;
/* Actually get the root hub name string. */
if (!DeviceIoControl(host_controller, IOCTL_USB_GET_ROOT_HUB_NAME,
NULL, 0, root_hub_name_wc, size, &size, NULL)) {
free(root_hub_name_wc);
return NULL;
}
/* Convert the root hub name string to UTF-8. */
root_hub_name_utf8 = wc_to_utf8(root_hub_name_wc->RootHubName, size);
free(root_hub_name_wc);
return root_hub_name_utf8;
}
static char *get_external_hub_name(HANDLE hub, ULONG connection_index)
{
USB_NODE_CONNECTION_NAME ext_hub_name;
PUSB_NODE_CONNECTION_NAME ext_hub_name_wc;
char *ext_hub_name_utf8;
ULONG size;
/* Compute the size of the external hub name string. */
ext_hub_name.ConnectionIndex = connection_index;
if (!DeviceIoControl(hub, IOCTL_USB_GET_NODE_CONNECTION_NAME,
&ext_hub_name, sizeof(ext_hub_name),
&ext_hub_name, sizeof(ext_hub_name), &size, NULL))
return NULL;
/* Allocate wide char external hub name string. */
size = ext_hub_name.ActualLength;
if (size <= sizeof(ext_hub_name)
|| !(ext_hub_name_wc = malloc(size)))
return NULL;
/* Get the name of the external hub attached to the specified port. */
ext_hub_name_wc->ConnectionIndex = connection_index;
if (!DeviceIoControl(hub, IOCTL_USB_GET_NODE_CONNECTION_NAME,
ext_hub_name_wc, size,
ext_hub_name_wc, size, &size, NULL)) {
free(ext_hub_name_wc);
return NULL;
}
/* Convert the external hub name string to UTF-8. */
ext_hub_name_utf8 = wc_to_utf8(ext_hub_name_wc->NodeName, size);
free(ext_hub_name_wc);
return ext_hub_name_utf8;
}
static char *get_string_descriptor(HANDLE hub_device, ULONG connection_index,
UCHAR descriptor_index)
{
char desc_req_buf[sizeof(USB_DESCRIPTOR_REQUEST) +
MAXIMUM_USB_STRING_LENGTH] = { 0 };
PUSB_DESCRIPTOR_REQUEST desc_req = (void *)desc_req_buf;
PUSB_STRING_DESCRIPTOR desc = (void *)(desc_req + 1);
ULONG size = sizeof(desc_req_buf);
desc_req->ConnectionIndex = connection_index;
desc_req->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8)
| descriptor_index;
desc_req->SetupPacket.wIndex = 0;
desc_req->SetupPacket.wLength = size - sizeof(*desc_req);
if (!DeviceIoControl(hub_device,
IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION,
desc_req, size, desc_req, size, &size, NULL)
|| size < 2
|| desc->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE
|| desc->bLength != size - sizeof(*desc_req)
|| desc->bLength % 2)
return NULL;
return wc_to_utf8(desc->bString, desc->bLength);
}
static void enumerate_hub_ports(struct sp_port *port, HANDLE hub_device,
ULONG nb_ports, const char *parent_path, DEVINST dev_inst)
{
char path[MAX_USB_PATH];
ULONG index = 0;
for (index = 1; index <= nb_ports; index++) {
PUSB_NODE_CONNECTION_INFORMATION_EX connection_info_ex;
ULONG size = sizeof(*connection_info_ex) + (30 * sizeof(USB_PIPE_INFO));
if (!(connection_info_ex = malloc(size)))
break;
connection_info_ex->ConnectionIndex = index;
if (!DeviceIoControl(hub_device,
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX,
connection_info_ex, size,
connection_info_ex, size, &size, NULL)) {
/*
* Try to get CONNECTION_INFORMATION if
* CONNECTION_INFORMATION_EX did not work.
*/
PUSB_NODE_CONNECTION_INFORMATION connection_info;
size = sizeof(*connection_info) + (30 * sizeof(USB_PIPE_INFO));
if (!(connection_info = malloc(size))) {
free(connection_info_ex);
continue;
}
connection_info->ConnectionIndex = index;
if (!DeviceIoControl(hub_device,
IOCTL_USB_GET_NODE_CONNECTION_INFORMATION,
connection_info, size,
connection_info, size, &size, NULL)) {
free(connection_info);
free(connection_info_ex);
continue;
}
connection_info_ex->ConnectionIndex = connection_info->ConnectionIndex;
connection_info_ex->DeviceDescriptor = connection_info->DeviceDescriptor;
connection_info_ex->DeviceIsHub = connection_info->DeviceIsHub;
connection_info_ex->DeviceAddress = connection_info->DeviceAddress;
free(connection_info);
}
if (connection_info_ex->DeviceIsHub) {
/* Recursively enumerate external hub. */
PCHAR ext_hub_name;
if ((ext_hub_name = get_external_hub_name(hub_device, index))) {
snprintf(path, sizeof(path), "%s%ld.",
parent_path, connection_info_ex->ConnectionIndex);
enumerate_hub(port, ext_hub_name, path, dev_inst);
}
free(connection_info_ex);
} else {
snprintf(path, sizeof(path), "%s%ld",
parent_path, connection_info_ex->ConnectionIndex);
/* Check if this device is the one we search for. */
if (strcmp(path, port->usb_path)) {
free(connection_info_ex);
continue;
}
/* Finally grab detailed information regarding the device. */
port->usb_address = connection_info_ex->DeviceAddress + 1;
port->usb_vid = connection_info_ex->DeviceDescriptor.idVendor;
port->usb_pid = connection_info_ex->DeviceDescriptor.idProduct;
if (connection_info_ex->DeviceDescriptor.iManufacturer)
port->usb_manufacturer = get_string_descriptor(hub_device,index,
connection_info_ex->DeviceDescriptor.iManufacturer);
if (connection_info_ex->DeviceDescriptor.iProduct)
port->usb_product = get_string_descriptor(hub_device, index,
connection_info_ex->DeviceDescriptor.iProduct);
if (connection_info_ex->DeviceDescriptor.iSerialNumber) {
port->usb_serial = get_string_descriptor(hub_device, index,
connection_info_ex->DeviceDescriptor.iSerialNumber);
if (port->usb_serial == NULL) {
//composite device, get the parent's serial number
char device_id[MAX_DEVICE_ID_LEN];
if (CM_Get_Parent(&dev_inst, dev_inst, 0) == CR_SUCCESS) {
if (CM_Get_Device_IDA(dev_inst, device_id, sizeof(device_id), 0) == CR_SUCCESS)
port->usb_serial = strdup(strrchr(device_id, '\\')+1);
}
}
}
free(connection_info_ex);
break;
}
}
}
static void enumerate_hub(struct sp_port *port, const char *hub_name,
const char *parent_path, DEVINST dev_inst)
{
USB_NODE_INFORMATION hub_info;
HANDLE hub_device;
ULONG size = sizeof(hub_info);
char *device_name;
/* Open the hub with its full name. */
if (!(device_name = malloc(strlen("\\\\.\\") + strlen(hub_name) + 1)))
return;
strcpy(device_name, "\\\\.\\");
strcat(device_name, hub_name);
hub_device = CreateFile(device_name, GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
free(device_name);
if (hub_device == INVALID_HANDLE_VALUE)
return;
/* Get the number of ports of the hub. */
if (DeviceIoControl(hub_device, IOCTL_USB_GET_NODE_INFORMATION,
&hub_info, size, &hub_info, size, &size, NULL))
/* Enumerate the ports of the hub. */
enumerate_hub_ports(port, hub_device,
hub_info.u.HubInformation.HubDescriptor.bNumberOfPorts, parent_path, dev_inst);
CloseHandle(hub_device);
}
static void enumerate_host_controller(struct sp_port *port,
HANDLE host_controller_device,
DEVINST dev_inst)
{
char *root_hub_name;
if ((root_hub_name = get_root_hub_name(host_controller_device))) {
enumerate_hub(port, root_hub_name, "", dev_inst);
free(root_hub_name);
}
}
static void get_usb_details(struct sp_port *port, DEVINST dev_inst_match)
{
HDEVINFO device_info;
SP_DEVINFO_DATA device_info_data;
ULONG i, size = 0;
device_info = SetupDiGetClassDevs(&GUID_CLASS_USB_HOST_CONTROLLER, NULL, NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
device_info_data.cbSize = sizeof(device_info_data);
for (i = 0; SetupDiEnumDeviceInfo(device_info, i, &device_info_data); i++) {
SP_DEVICE_INTERFACE_DATA device_interface_data;
PSP_DEVICE_INTERFACE_DETAIL_DATA device_detail_data;
DEVINST dev_inst = dev_inst_match;
HANDLE host_controller_device;
device_interface_data.cbSize = sizeof(device_interface_data);
if (!SetupDiEnumDeviceInterfaces(device_info, 0,
&GUID_CLASS_USB_HOST_CONTROLLER,
i, &device_interface_data))
continue;
if (!SetupDiGetDeviceInterfaceDetail(device_info,&device_interface_data,
NULL, 0, &size, NULL)
&& GetLastError() != ERROR_INSUFFICIENT_BUFFER)
continue;
if (!(device_detail_data = malloc(size)))
continue;
device_detail_data->cbSize = sizeof(*device_detail_data);
if (!SetupDiGetDeviceInterfaceDetail(device_info,&device_interface_data,
device_detail_data, size, &size,
NULL)) {
free(device_detail_data);
continue;
}
while (CM_Get_Parent(&dev_inst, dev_inst, 0) == CR_SUCCESS
&& dev_inst != device_info_data.DevInst) { }
if (dev_inst != device_info_data.DevInst) {
free(device_detail_data);
continue;
}
port->usb_bus = i + 1;
host_controller_device = CreateFile(device_detail_data->DevicePath,
GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (host_controller_device != INVALID_HANDLE_VALUE) {
enumerate_host_controller(port, host_controller_device, dev_inst_match);
CloseHandle(host_controller_device);
}
free(device_detail_data);
}
SetupDiDestroyDeviceInfoList(device_info);
return;
}
SP_PRIV enum sp_return get_port_details(struct sp_port *port)
{
/*
* Description limited to 127 char, anything longer
* would not be user friendly anyway.
*/
char description[128];
SP_DEVINFO_DATA device_info_data = { .cbSize = sizeof(device_info_data) };
HDEVINFO device_info;
int i;
device_info = SetupDiGetClassDevs(NULL, 0, 0,
DIGCF_PRESENT | DIGCF_ALLCLASSES);
if (device_info == INVALID_HANDLE_VALUE)
RETURN_FAIL("SetupDiGetClassDevs() failed");
for (i = 0; SetupDiEnumDeviceInfo(device_info, i, &device_info_data); i++) {
HKEY device_key;
DEVINST dev_inst;
char value[8], class[16];
DWORD size, type;
CONFIGRET cr;
/* Check if this is the device we are looking for. */
device_key = SetupDiOpenDevRegKey(device_info, &device_info_data,
DICS_FLAG_GLOBAL, 0,
DIREG_DEV, KEY_QUERY_VALUE);
if (device_key == INVALID_HANDLE_VALUE)
continue;
size = sizeof(value);
if (RegQueryValueExA(device_key, "PortName", NULL, &type, (LPBYTE)value,
&size) != ERROR_SUCCESS || type != REG_SZ) {
RegCloseKey(device_key);
continue;
}
RegCloseKey(device_key);
value[sizeof(value)-1] = 0;
if (strcmp(value, port->name))
continue;
/* Check port transport type. */
dev_inst = device_info_data.DevInst;
size = sizeof(class);
cr = CR_FAILURE;
while (CM_Get_Parent(&dev_inst, dev_inst, 0) == CR_SUCCESS &&
(cr = CM_Get_DevNode_Registry_PropertyA(dev_inst,
CM_DRP_CLASS, 0, class, &size, 0)) != CR_SUCCESS) { }
if (cr == CR_SUCCESS) {
if (!strcmp(class, "USB"))
port->transport = SP_TRANSPORT_USB;
}
/* Get port description (friendly name). */
dev_inst = device_info_data.DevInst;
size = sizeof(description);
while ((cr = CM_Get_DevNode_Registry_PropertyA(dev_inst,
CM_DRP_FRIENDLYNAME, 0, description, &size, 0)) != CR_SUCCESS
&& CM_Get_Parent(&dev_inst, dev_inst, 0) == CR_SUCCESS) { }
if (cr == CR_SUCCESS)
port->description = strdup(description);
/* Get more informations for USB connected ports. */
if (port->transport == SP_TRANSPORT_USB) {
char usb_path[MAX_USB_PATH] = "", tmp[MAX_USB_PATH];
char device_id[MAX_DEVICE_ID_LEN];
/* Recurse over parents to build the USB device path. */
dev_inst = device_info_data.DevInst;
do {
/* Verify that this layer of the tree is USB related. */
if (CM_Get_Device_IDA(dev_inst, device_id,
sizeof(device_id), 0) != CR_SUCCESS
|| strncmp(device_id, "USB\\", 4))
continue;
/* Discard one layer for composite devices. */
char compat_ids[512], *p = compat_ids;
size = sizeof(compat_ids);
if (CM_Get_DevNode_Registry_PropertyA(dev_inst,
CM_DRP_COMPATIBLEIDS, 0,
&compat_ids,
&size, 0) == CR_SUCCESS) {
while (*p) {
if (!strncmp(p, "USB\\COMPOSITE", 13))
break;
p += strlen(p) + 1;
}
if (*p)
continue;
}
/* Stop the recursion when reaching the USB root. */
if (!strncmp(device_id, "USB\\ROOT", 8))
break;
/* Prepend the address of current USB layer to the USB path. */
DWORD address;
size = sizeof(address);
if (CM_Get_DevNode_Registry_PropertyA(dev_inst, CM_DRP_ADDRESS,
0, &address, &size, 0) == CR_SUCCESS) {
strcpy(tmp, usb_path);
snprintf(usb_path, sizeof(usb_path), "%d%s%s",
(int)address, *tmp ? "." : "", tmp);
}
} while (CM_Get_Parent(&dev_inst, dev_inst, 0) == CR_SUCCESS);
port->usb_path = strdup(usb_path);
/* Wake up the USB device to be able to read string descriptor. */
char *escaped_port_name;
HANDLE handle;
if (!(escaped_port_name = malloc(strlen(port->name) + 5)))
RETURN_ERROR(SP_ERR_MEM, "Escaped port name malloc failed");
sprintf(escaped_port_name, "\\\\.\\%s", port->name);
handle = CreateFile(escaped_port_name, GENERIC_READ, 0, 0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, 0);
free(escaped_port_name);
CloseHandle(handle);
/* Retrieve USB device details from the device descriptor. */
get_usb_details(port, device_info_data.DevInst);
}
break;
}
SetupDiDestroyDeviceInfoList(device_info);
RETURN_OK();
}
SP_PRIV enum sp_return list_ports(struct sp_port ***list)
{
HKEY key;
TCHAR *value, *data;
DWORD max_value_len, max_data_size, max_data_len;
DWORD value_len, data_size, data_len;
DWORD type, index = 0;
char *name;
int name_len;
int ret = SP_OK;
DEBUG("Opening registry key");
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\SERIALCOMM"),
0, KEY_QUERY_VALUE, &key) != ERROR_SUCCESS) {
SET_FAIL(ret, "RegOpenKeyEx() failed");
goto out_done;
}
DEBUG("Querying registry key value and data sizes");
if (RegQueryInfoKey(key, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
&max_value_len, &max_data_size, NULL, NULL) != ERROR_SUCCESS) {
SET_FAIL(ret, "RegQueryInfoKey() failed");
goto out_close;
}
max_data_len = max_data_size / sizeof(TCHAR);
if (!(value = malloc((max_value_len + 1) * sizeof(TCHAR)))) {
SET_ERROR(ret, SP_ERR_MEM, "Registry value malloc failed");
goto out_close;
}
if (!(data = malloc((max_data_len + 1) * sizeof(TCHAR)))) {
SET_ERROR(ret, SP_ERR_MEM, "Registry data malloc failed");
goto out_free_value;
}
DEBUG("Iterating over values");
while (
value_len = max_value_len + 1,
data_size = max_data_size,
RegEnumValue(key, index, value, &value_len,
NULL, &type, (LPBYTE)data, &data_size) == ERROR_SUCCESS)
{
if (type == REG_SZ) {
data_len = data_size / sizeof(TCHAR);
data[data_len] = '\0';
#ifdef UNICODE
name_len = WideCharToMultiByte(CP_ACP, 0, data, -1, NULL, 0, NULL, NULL);
#else
name_len = data_len + 1;
#endif
if (!(name = malloc(name_len))) {
SET_ERROR(ret, SP_ERR_MEM, "Registry port name malloc failed");
goto out;
}
#ifdef UNICODE
WideCharToMultiByte(CP_ACP, 0, data, -1, name, name_len, NULL, NULL);
#else
strcpy(name, data);
#endif
DEBUG_FMT("Found port %s", name);
if (!(*list = list_append(*list, name))) {
SET_ERROR(ret, SP_ERR_MEM, "List append failed");
free(name);
goto out;
}
free(name);
}
index++;
}
out:
free(data);
out_free_value:
free(value);
out_close:
RegCloseKey(key);
out_done:
return ret;
}