mirror of
git://sigrok.org/libserialport
synced 2023-08-10 21:13:24 +03:00
Compare commits
134 Commits
libserialp
...
libserialp
Author | SHA1 | Date | |
---|---|---|---|
0c3f38b81b | |||
3fcdc9f7d5 | |||
24a38c8094 | |||
aee7d69195 | |||
888fb45d66 | |||
02c8a1424d | |||
aacba60dca | |||
8062087ab3 | |||
4d8195fe3f | |||
ae2c5825e7 | |||
127d8d0ce7 | |||
1a584c45b0 | |||
962143bc5c | |||
c37c9f348d | |||
07f61c7131 | |||
87f4a6f021 | |||
f1c916ede1 | |||
ef08a6d52b | |||
f77bb46df5 | |||
d931da1042 | |||
e2b7ba1214 | |||
56fe6e3207 | |||
6b8eee0682 | |||
49fd7b1bc2 | |||
deef6e528c | |||
ec4b55ae25 | |||
46d8b0a039 | |||
e5c2630eee | |||
0188a545c7 | |||
76222ee578 | |||
c3cee38c3b | |||
772c586133 | |||
27911925c1 | |||
bbe566fe1c | |||
8ebfaf8966 | |||
21ae5ce197 | |||
94a314a806 | |||
27a58c0521 | |||
9a0c4bbdbe | |||
480d750c59 | |||
ff6da776e8 | |||
6392641545 | |||
6aad5bdd47 | |||
5f64c28551 | |||
eb50b1aca6 | |||
9eb9f07130 | |||
1b91c6eadb | |||
025c264448 | |||
a630a469f6 | |||
f14809393a | |||
ad036cc8ef | |||
3dd4a2ae39 | |||
35578b063e | |||
e4f44cf5f2 | |||
13efecf888 | |||
3f2f48fc01 | |||
777347a6bd | |||
8c6de591a5 | |||
1ff591d7b3 | |||
5ca38cdfe0 | |||
613c48f191 | |||
b3e619c8b2 | |||
f11a67ed5f | |||
144a598f19 | |||
3cb7aa98c2 | |||
cd71af4b93 | |||
a24671ade9 | |||
1cda3e3dfc | |||
23e7fa372a | |||
ac323d80e0 | |||
e019e72ec0 | |||
b344a40bb2 | |||
948f63f0fd | |||
5122d60849 | |||
8d12e620f2 | |||
34442106ef | |||
47fcf8ec85 | |||
6c6aebe78c | |||
dc422c04af | |||
2b40f81454 | |||
cfa5af67fd | |||
64155cba8a | |||
7c081505e5 | |||
7c1101dc08 | |||
ea17bfca3d | |||
5497520891 | |||
ccd512d51c | |||
f446cfbf65 | |||
e31f2c6b8b | |||
b328a48b0f | |||
59182fbbbf | |||
ad7498553c | |||
d66bbceaa7 | |||
78940e6921 | |||
6c444ade55 | |||
7aeb6736bd | |||
276ef1b92f | |||
eb82be98a7 | |||
a46ee5b4d7 | |||
da8730199a | |||
6692d8c13f | |||
d179da05c0 | |||
653cef3ab1 | |||
546e9ae5c5 | |||
23ef3bf125 | |||
d38c7d2c70 | |||
0666ccc7ca | |||
7f450f46bd | |||
c51b846eee | |||
7890cef6cf | |||
9caa2e86aa | |||
79a8004637 | |||
942a6d349e | |||
f3ccf157cb | |||
900904638b | |||
34906a229b | |||
8eb18f7b5a | |||
9a8d04bca6 | |||
5a8810e2b2 | |||
12056e2f75 | |||
5bd33b7c8b | |||
970f279ae4 | |||
e4ce975a54 | |||
48a4076f69 | |||
235269990b | |||
e59ecb3359 | |||
65172beffe | |||
e33dcf90a0 | |||
a93fb46888 | |||
98cac5d63e | |||
073c86bd07 | |||
f095f84164 | |||
da2ede60e4 | |||
8edf649caf |
33
.gitignore
vendored
33
.gitignore
vendored
@ -1,19 +1,18 @@
|
||||
*.[ao]
|
||||
*.l[ao]
|
||||
.deps/
|
||||
.libs/
|
||||
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
|
||||
/INSTALL
|
||||
/Makefile
|
||||
/Makefile.in
|
||||
/aclocal.m4
|
||||
/autom4te.cache/
|
||||
/autostuff/
|
||||
/config.*
|
||||
/configure
|
||||
/configure.lineno
|
||||
/libserialport-*.tar.*
|
||||
/libserialport.h
|
||||
/libserialport.pc
|
||||
/libtool
|
||||
stamp-h?
|
||||
|
24
AUTHORS
24
AUTHORS
@ -2,6 +2,26 @@
|
||||
AUTHORS
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Please check the source code files and/or git history and/or ChangeLog for
|
||||
a list of all authors and contributors.
|
||||
Martin Ling conceived the idea for the library, designed the API and wrote much
|
||||
of the implementation and documentation.
|
||||
|
||||
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.
|
||||
|
131
Doxyfile
131
Doxyfile
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.6
|
||||
# Doxyfile 1.8.8
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@ -70,6 +70,14 @@ 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.
|
||||
@ -261,9 +269,12 @@ 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, 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 (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.
|
||||
#
|
||||
# Note For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
@ -669,8 +680,7 @@ 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. Do not use file names with spaces, bibtex cannot handle them. See
|
||||
# also \cite for info how to create references.
|
||||
# search path. See also \cite for info how to create references.
|
||||
|
||||
CITE_BIB_FILES =
|
||||
|
||||
@ -967,6 +977,25 @@ 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
|
||||
#---------------------------------------------------------------------------
|
||||
@ -1059,13 +1088,15 @@ HTML_FOOTER =
|
||||
|
||||
HTML_STYLESHEET =
|
||||
|
||||
# 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
|
||||
# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
|
||||
# cascading style sheets that are 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 file to the output directory. For an example
|
||||
# see the documentation.
|
||||
# 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.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
@ -1230,7 +1261,8 @@ 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.
|
||||
# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it
|
||||
# enables the Previous and Next buttons.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
|
||||
|
||||
@ -1470,11 +1502,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 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.
|
||||
# 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.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag SEARCHENGINE is set to YES.
|
||||
|
||||
@ -1602,17 +1634,19 @@ 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. 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).
|
||||
# $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.
|
||||
# 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.
|
||||
# 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.
|
||||
#
|
||||
# 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.
|
||||
@ -1636,7 +1670,7 @@ LATEX_EXTRA_FILES =
|
||||
|
||||
PDF_HYPERLINKS = YES
|
||||
|
||||
# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
|
||||
# If the USE_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.
|
||||
@ -1762,6 +1796,13 @@ 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
|
||||
@ -1789,18 +1830,6 @@ 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
|
||||
@ -1828,6 +1857,15 @@ 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
|
||||
#---------------------------------------------------------------------------
|
||||
@ -1947,9 +1985,9 @@ PREDEFINED =
|
||||
EXPAND_AS_DEFINED =
|
||||
|
||||
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
|
||||
# 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
|
||||
# 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
|
||||
# removed.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
@ -1969,7 +2007,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 an unique name (where the name does NOT include
|
||||
# Note: Each tag file must have a 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.
|
||||
|
||||
@ -2047,7 +2085,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: NO.
|
||||
# The default value is: YES.
|
||||
|
||||
HAVE_DOT = YES
|
||||
|
||||
@ -2061,7 +2099,7 @@ HAVE_DOT = YES
|
||||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font n the dot files that doxygen
|
||||
# When you want a differently looking font in 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
|
||||
@ -2199,7 +2237,9 @@ 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, jpg, gif and svg.
|
||||
# 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.
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
@ -2242,6 +2282,15 @@ 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
|
||||
|
35
Makefile.am
35
Makefile.am
@ -19,22 +19,36 @@
|
||||
##
|
||||
|
||||
ACLOCAL_AMFLAGS = -I autostuff
|
||||
AM_LIBTOOLFLAGS = --silent
|
||||
GNUMAKEFLAGS = --no-print-directory
|
||||
|
||||
AM_CPPFLAGS = -I$(top_srcdir)
|
||||
# Enable more compiler warnings.
|
||||
AM_CFLAGS = -std=c99 -Wall -Wextra -pedantic -Wmissing-prototypes -Wshadow
|
||||
|
||||
lib_LTLIBRARIES = libserialport.la
|
||||
|
||||
libserialport_la_SOURCES = serialport.c
|
||||
libserialport_la_SOURCES = serialport.c libserialport_internal.h
|
||||
if LINUX
|
||||
libserialport_la_SOURCES += linux_termios.c linux_termios.h
|
||||
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
|
||||
endif
|
||||
|
||||
libserialport_la_LIBADD = $(LIBOBJS)
|
||||
libserialport_la_LIBADD = $(SP_LIBS)
|
||||
|
||||
libserialport_la_LDFLAGS = $(SP_LIB_LDFLAGS)
|
||||
libserialport_la_LDFLAGS = -version-info $(SP_LIB_VERSION) -no-undefined
|
||||
if MACOSX
|
||||
libserialport_la_LDFLAGS += -framework IOKit -framework CoreFoundation
|
||||
endif
|
||||
|
||||
library_includedir = $(includedir)
|
||||
library_include_HEADERS = libserialport.h
|
||||
nodist_include_HEADERS = libserialport.h
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libserialport.pc
|
||||
@ -43,11 +57,12 @@ EXTRA_DIST = Doxyfile
|
||||
|
||||
MAINTAINERCLEANFILES = ChangeLog
|
||||
|
||||
.PHONY: ChangeLog
|
||||
.PHONY: ChangeLog doc
|
||||
|
||||
ChangeLog:
|
||||
git --git-dir $(top_srcdir)/.git log > ChangeLog || touch ChangeLog
|
||||
git --git-dir '$(top_srcdir)/.git' log >$@ || touch $@
|
||||
|
||||
dist-hook: ChangeLog
|
||||
|
||||
doc: $(library_include_HEADERS) $(top_srcdir)/Doxyfile
|
||||
doc: $(nodist_include_HEADERS) $(top_srcdir)/Doxyfile
|
||||
doxygen $(top_srcdir)/Doxyfile
|
||||
|
76
NEWS
76
NEWS
@ -1,3 +1,79 @@
|
||||
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
37
README
@ -11,6 +11,7 @@ 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.
|
||||
@ -24,33 +25,29 @@ 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 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.
|
||||
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.
|
||||
|
||||
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
|
||||
============
|
||||
|
||||
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.
|
||||
No other libraries are required.
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
The package uses a GNU style build system and requires a Unix style shell.
|
||||
On Windows it can be built with the MinGW toolchain and MSYS environment.
|
||||
|
||||
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.
|
||||
|
||||
Run "./autogen.sh" to generate the build system, "./configure" to setup, then
|
||||
"make" to build the library and "make install" to install it.
|
||||
@ -59,3 +56,13 @@ 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
|
||||
|
||||
|
44
autogen.sh
44
autogen.sh
@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/sh -e
|
||||
##
|
||||
## This file is part of the libserialport project.
|
||||
##
|
||||
@ -19,42 +19,8 @@
|
||||
## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
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 -n "$srcdir" || srcdir=`dirname "$0"`
|
||||
test -n "$srcdir" || srcdir=.
|
||||
|
||||
test -d "$srcdir/autostuff" || mkdir "$srcdir/autostuff"
|
||||
autoreconf --force --install --verbose "$srcdir"
|
||||
|
@ -1,32 +1,38 @@
|
||||
/**
|
||||
* Set the {2} for the specified serial port.
|
||||
*
|
||||
* @param port Pointer to port structure.
|
||||
* @param {0} {3}.
|
||||
* @param[in] port Pointer to a port structure. Must not be NULL.
|
||||
* @param[in] {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 config Pointer to configuration structure.
|
||||
* @param {0}_ptr Pointer to variable to store 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.
|
||||
*
|
||||
* @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 config Pointer to configuration structure.
|
||||
* @param {0} {3}, or -1 to retain current setting.
|
||||
* @param[in] config Pointer to a configuration structure. Must not be NULL.
|
||||
* @param[in] {0} {3}, or -1 to retain the 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});
|
||||
|
153
configure.ac
153
configure.ac
@ -24,117 +24,124 @@ 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], [0])
|
||||
m4_define([sp_package_version_micro], [1])
|
||||
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])
|
||||
|
||||
AC_CANONICAL_TARGET
|
||||
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])
|
||||
|
||||
# We require at least automake 1.11 (needed for 'silent rules').
|
||||
AM_INIT_AUTOMAKE([1.11 -Wall -Werror check-news])
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
AM_INIT_AUTOMAKE([1.11 -Wall -Werror no-define check-news])
|
||||
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
|
||||
|
||||
# Initialize pkg-config.
|
||||
# We require at least 0.22, as "Requires.private" behaviour changed there.
|
||||
PKG_PROG_PKG_CONFIG([0.22])
|
||||
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])
|
||||
|
||||
# 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=0
|
||||
SP_LIB_VERSION_CURRENT=1
|
||||
SP_LIB_VERSION_REVISION=0
|
||||
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)
|
||||
SP_LIB_VERSION_AGE=1
|
||||
AC_SUBST([SP_LIB_VERSION],
|
||||
["$SP_LIB_VERSION_CURRENT:$SP_LIB_VERSION_REVISION:$SP_LIB_VERSION_AGE"])
|
||||
|
||||
# Checks for libraries.
|
||||
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"], [.])
|
||||
|
||||
# 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_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*}"])
|
||||
|
||||
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([WIN32], [SP_LIBS='-lsetupapi'], [SP_LIBS=])
|
||||
AC_SUBST([SP_LIBS])
|
||||
|
||||
AC_SUBST(SP_PKGLIBS)
|
||||
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
|
||||
|
||||
# Define size_t if not defined as standard.
|
||||
AC_TYPE_SIZE_T
|
||||
|
||||
# Check for specific termios structures.
|
||||
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>]])
|
||||
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_SUBST(MAKEFLAGS, '--no-print-directory')
|
||||
AC_SUBST(AM_LIBTOOLFLAGS, '--silent')
|
||||
# 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>]])
|
||||
|
||||
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
|
||||
# Check for serial_struct.
|
||||
AC_CHECK_TYPES([struct serial_struct],,, [[#include <linux/serial.h>]])
|
||||
|
||||
AC_SUBST(SP_PACKAGE_VERSION_MAJOR)
|
||||
AC_SUBST(SP_PACKAGE_VERSION_MINOR)
|
||||
AC_SUBST(SP_PACKAGE_VERSION_MICRO)
|
||||
AC_SUBST(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_CONFIG_FILES([Makefile libserialport.h libserialport.pc])
|
||||
AC_CONFIG_FILES([Makefile libserialport.pc])
|
||||
|
||||
AC_OUTPUT
|
||||
|
||||
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
|
||||
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
|
||||
|
384
freebsd.c
Normal file
384
freebsd.c
Normal file
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* 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
@ -6,7 +6,8 @@ includedir=@includedir@
|
||||
Name: libserialport
|
||||
Description: Cross-platform serial port access library.
|
||||
URL: http://sigrok.org/wiki/Libserialport
|
||||
Requires.private: @SP_PKGLIBS@
|
||||
Version: @VERSION@
|
||||
Requires.private:
|
||||
Version: @SP_PACKAGE_VERSION@
|
||||
Libs: -L${libdir} -lserialport
|
||||
Libs.private: @SP_LIBS@
|
||||
Cflags: -I${includedir}
|
||||
|
237
libserialport_internal.h
Normal file
237
libserialport_internal.h
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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
Normal file
247
linux.c
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
@ -33,40 +33,42 @@
|
||||
* TCSETX/TCGETX ioctls used with struct termiox, others do not.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/termios.h>
|
||||
#include "linux_termios.h"
|
||||
|
||||
int get_termios_get_ioctl(void)
|
||||
SP_PRIV unsigned long get_termios_get_ioctl(void)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS2
|
||||
#ifdef HAVE_STRUCT_TERMIOS2
|
||||
return TCGETS2;
|
||||
#else
|
||||
return TCGETS;
|
||||
#endif
|
||||
}
|
||||
|
||||
int get_termios_set_ioctl(void)
|
||||
SP_PRIV unsigned long get_termios_set_ioctl(void)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS2
|
||||
#ifdef HAVE_STRUCT_TERMIOS2
|
||||
return TCSETS2;
|
||||
#else
|
||||
return TCSETS;
|
||||
#endif
|
||||
}
|
||||
|
||||
int get_termios_size(void)
|
||||
SP_PRIV size_t get_termios_size(void)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS2
|
||||
#ifdef HAVE_STRUCT_TERMIOS2
|
||||
return sizeof(struct termios2);
|
||||
#else
|
||||
return sizeof(struct termios);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)
|
||||
int get_termios_speed(void *data)
|
||||
#if (defined(HAVE_TERMIOS_SPEED) || defined(HAVE_TERMIOS2_SPEED)) && defined(HAVE_DECL_BOTHER)
|
||||
SP_PRIV int get_termios_speed(void *data)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS2
|
||||
#ifdef HAVE_STRUCT_TERMIOS2
|
||||
struct termios2 *term = (struct termios2 *) data;
|
||||
#else
|
||||
struct termios *term = (struct termios *) data;
|
||||
@ -77,9 +79,9 @@ int get_termios_speed(void *data)
|
||||
return term->c_ispeed;
|
||||
}
|
||||
|
||||
void set_termios_speed(void *data, int speed)
|
||||
SP_PRIV void set_termios_speed(void *data, int speed)
|
||||
{
|
||||
#ifdef HAVE_TERMIOS2
|
||||
#ifdef HAVE_STRUCT_TERMIOS2
|
||||
struct termios2 *term = (struct termios2 *) data;
|
||||
#else
|
||||
struct termios *term = (struct termios *) data;
|
||||
@ -90,13 +92,13 @@ void set_termios_speed(void *data, int speed)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TERMIOX
|
||||
int get_termiox_size(void)
|
||||
#ifdef HAVE_STRUCT_TERMIOX
|
||||
SP_PRIV size_t get_termiox_size(void)
|
||||
{
|
||||
return sizeof(struct termiox);
|
||||
}
|
||||
|
||||
int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr)
|
||||
SP_PRIV int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr)
|
||||
{
|
||||
struct termiox *termx = (struct termiox *) data;
|
||||
int flags = 0;
|
||||
@ -109,7 +111,7 @@ int get_termiox_flow(void *data, int *rts, int *cts, int *dtr, int *dsr)
|
||||
return flags;
|
||||
}
|
||||
|
||||
void set_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)
|
||||
{
|
||||
struct termiox *termx = (struct termiox *) data;
|
||||
|
||||
|
@ -20,13 +20,15 @@
|
||||
#ifndef LIBSERIALPORT_LINUX_TERMIOS_H
|
||||
#define LIBSERIALPORT_LINUX_TERMIOS_H
|
||||
|
||||
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);
|
||||
#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);
|
||||
|
||||
#endif
|
||||
|
243
macosx.c
Normal file
243
macosx.c
Normal file
@ -0,0 +1,243 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
1238
serialport.c
1238
serialport.c
File diff suppressed because it is too large
Load Diff
553
windows.c
Normal file
553
windows.c
Normal file
@ -0,0 +1,553 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
Reference in New Issue
Block a user