Import Upstream version 21.68

This commit is contained in:
Lu zhiping 2022-06-27 15:31:38 +08:00
commit e913968499
191 changed files with 322009 additions and 0 deletions

7
.cvsignore Normal file
View File

@ -0,0 +1,7 @@
hwinfo
hw
.depend
hwscan
todo
hwscanqueue
hwscand

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
changelog
Changelog
VERSION
*.o
.depend
hwinfo
hwinfo.pc

341
COPYING Normal file
View File

@ -0,0 +1,341 @@
----------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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 General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

1
MAINTAINER Normal file
View File

@ -0,0 +1 @@
Steffen Winterfeldt (snwint@suse.de)

134
Makefile Normal file
View File

@ -0,0 +1,134 @@
TOPDIR = $(CURDIR)
SUBDIRS = src
TARGETS = hwinfo hwinfo.pc changelog
CLEANFILES = hwinfo hwinfo.pc hwinfo.static hwscan hwscan.static hwscand hwscanqueue doc/libhd doc/*~
LIBS = -lhd
SLIBS = -lhd -luuid
TLIBS = -lhd_tiny
SO_LIBS = -luuid
TSO_LIBS =
export SO_LIBS
GIT2LOG := $(shell if [ -x ./git2log ] ; then echo ./git2log --update ; else echo true ; fi)
GITDEPS := $(shell [ -d .git ] && echo .git/HEAD .git/refs/heads .git/refs/tags)
BRANCH := $(shell [ -d .git ] && git branch | perl -ne 'print $$_ if s/^\*\s*//')
ifdef HWINFO_VERSION
VERSION := $(shell echo ${HWINFO_VERSION} > VERSION; cat VERSION)
else
VERSION := $(shell $(GIT2LOG) --version VERSION ; cat VERSION)
endif
PREFIX := hwinfo-$(VERSION)
include Makefile.common
ifeq "$(ARCH)" "x86_64"
LIBDIR ?= /usr/lib64
else
LIBDIR ?= /usr/lib
endif
ULIBDIR = $(LIBDIR)
# ia64
ifneq ($(filter i386 x86_64, $(ARCH)),)
SLIBS += -lx86emu
TLIBS += -lx86emu
SO_LIBS += -lx86emu
TSO_LIBS += -lx86emu
endif
SHARED_FLAGS =
OBJS_NO_TINY = names.o parallel.o modem.o
.PHONY: fullstatic static shared tiny doc diet tinydiet uc tinyuc
ifdef HWINFO_VERSION
changelog:
@true
else
changelog: $(GITDEPS)
$(GIT2LOG) --changelog changelog
endif
hwscan: hwscan.o $(LIBHD)
$(CC) hwscan.o $(LDFLAGS) $(CFLAGS) $(LIBS) -o $@
hwinfo: hwinfo.o $(LIBHD)
$(CC) hwinfo.o $(LDFLAGS) $(CFLAGS) $(LIBS) -o $@
hwscand: hwscand.o
$(CC) $< $(LDFLAGS) $(CFLAGS) -o $@
hwscanqueue: hwscanqueue.o
$(CC) $< $(LDFLAGS) $(CFLAGS) -o $@
hwinfo.pc: hwinfo.pc.in VERSION
VERSION=`cat VERSION`; \
sed -e "s,@VERSION@,$${VERSION},g" -e 's,@LIBDIR@,$(ULIBDIR),g;s,@LIBS@,$(LIBS),g' $< > $@.tmp && mv $@.tmp $@
# kept for compatibility
shared:
@make
tiny:
@make EXTRA_FLAGS=-DLIBHD_TINY LIBHD_BASE=libhd_tiny LIBS="$(TLIBS)" SO_LIBS="$(TSO_LIBS)"
tinyinstall:
@make EXTRA_FLAGS=-DLIBHD_TINY LIBHD_BASE=libhd_tiny LIBS="$(TLIBS)" SO_LIBS="$(TSO_LIBS)" install
tinystatic:
@make EXTRA_FLAGS=-DLIBHD_TINY LIBHD_BASE=libhd_tiny SHARED_FLAGS= LIBS="$(TLIBS)" SO_LIBS="$(TSO_LIBS)"
diet:
@make CC="diet gcc" EXTRA_FLAGS="-fno-pic -DDIET" SHARED_FLAGS= LIBS="$(SLIBS)"
tinydiet:
@make CC="diet gcc" EXTRA_FLAGS="-fno-pic -DLIBHD_TINY -DDIET" SHARED_FLAGS= LIBS="$(SLIBS)"
uc:
@make CC="/opt/i386-linux-uclibc/bin/i386-uclibc-gcc" EXTRA_FLAGS="-fno-pic -DUCLIBC" SHARED_FLAGS= LIBS="$(SLIBS)"
tinyuc:
@make CC="/opt/i386-linux-uclibc/usr/bin/gcc" EXTRA_FLAGS="-fno-pic -DLIBHD_TINY -DUCLIBC" SHARED_FLAGS= LIBS="$(SLIBS)"
static:
make SHARED_FLAGS= LIBS="$(SLIBS)"
fullstatic: static
$(CC) -static hwinfo.o $(LDFLAGS) $(SLIBS) -o hwinfo.static
strip -R .note -R .comment hwinfo.static
doc:
@cd doc ; doxygen libhd.doxy
install:
install -d -m 755 $(DESTDIR)/sbin $(DESTDIR)/usr/sbin $(DESTDIR)$(ULIBDIR) \
$(DESTDIR)$(ULIBDIR)/pkgconfig $(DESTDIR)/usr/include
install -m 755 hwinfo $(DESTDIR)/usr/sbin
install -m 755 src/ids/check_hd $(DESTDIR)/usr/sbin
install -m 755 src/ids/convert_hd $(DESTDIR)/usr/sbin
if [ -f $(LIBHD_SO) ] ; then \
install $(LIBHD_SO) $(DESTDIR)$(ULIBDIR) ; \
ln -snf $(LIBHD_NAME) $(DESTDIR)$(ULIBDIR)/$(LIBHD_SONAME) ; \
ln -snf $(LIBHD_SONAME) $(DESTDIR)$(ULIBDIR)/$(LIBHD_BASE).so ; \
else \
install -m 644 $(LIBHD) $(DESTDIR)$(ULIBDIR) ; \
fi
install -m 644 hwinfo.pc $(DESTDIR)$(ULIBDIR)/pkgconfig
install -m 644 src/hd/hd.h $(DESTDIR)/usr/include
perl -pi -e "s/define\s+HD_VERSION\b.*/define HD_VERSION\t\t$(LIBHD_MAJOR_VERSION)/" $(DESTDIR)/usr/include/hd.h
perl -pi -e "s/define\s+HD_MINOR_VERSION\b.*/define HD_MINOR_VERSION\t$(LIBHD_MINOR_VERSION)/" $(DESTDIR)/usr/include/hd.h
install -m 755 getsysinfo $(DESTDIR)/usr/sbin
install -m 755 src/isdn/cdb/mk_isdnhwdb $(DESTDIR)/usr/sbin
install -d -m 755 $(DESTDIR)/usr/share/hwinfo
install -d -m 755 $(DESTDIR)/var/lib/hardware/udi
install -m 644 src/isdn/cdb/ISDN.CDB.txt $(DESTDIR)/usr/share/hwinfo
install -m 644 src/isdn/cdb/ISDN.CDB.hwdb $(DESTDIR)/usr/share/hwinfo
archive: changelog
@if [ ! -d .git ] ; then echo no git repo ; false ; fi
mkdir -p package
git archive --prefix=$(PREFIX)/ $(BRANCH) > package/$(PREFIX).tar
tar -r -f package/$(PREFIX).tar --mode=0664 --owner=root --group=root --mtime="`git show -s --format=%ci`" --transform='s:^:$(PREFIX)/:' VERSION changelog src/hd/hd.h
xz -f package/$(PREFIX).tar

70
Makefile.common Normal file
View File

@ -0,0 +1,70 @@
# libhd/Makefile.common
ARCH := $(shell uname -m)
ifeq "$(ARCH)" "i486"
ARCH := i386
endif
ifeq "$(ARCH)" "i586"
ARCH := i386
endif
ifeq "$(ARCH)" "i686"
ARCH := i386
endif
ifeq "$(ARCH)" "parisc"
EXTRA_FLAGS := -fPIC $(EXTRA_FLAGS)
endif
LIBHD_VERSION := $(shell cat $(TOPDIR)/VERSION)
LIBHD_MINOR_VERSION := $(shell cut -d . -f 2 $(TOPDIR)/VERSION)
LIBHD_MAJOR_VERSION := $(shell cut -d . -f 1 $(TOPDIR)/VERSION)
RPM_OPT_FLAGS ?= -O2
CC ?= gcc
LD = ld
CFLAGS += $(RPM_OPT_FLAGS) -Wall -Wno-pointer-sign -pipe -g $(SHARED_FLAGS) $(EXTRA_FLAGS) -I$(TOPDIR)/src/hd
SHARED_FLAGS = -fPIC
LDFLAGS += -Lsrc
CFILES = $(sort $(wildcard *.c))
OBJS = $(CFILES:.c=.o)
LIBHD_BASE = libhd
LIBHD = $(TOPDIR)/src/$(LIBHD_BASE).a
LIBHD_SONAME = $(LIBHD_BASE).so.$(LIBHD_MAJOR_VERSION)
LIBHD_NAME = $(LIBHD_BASE).so.$(LIBHD_VERSION)
LIBHD_SO = $(TOPDIR)/src/$(LIBHD_NAME)
LIBHD_D = $(TOPDIR)/src/.lib
export CC TOPDIR CFLAGS LIBHD ARCH
.PHONY: all distclean clean install subdirs
%.o: %.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $<
all: subdirs $(TARGETS)
install: all
ifneq "$(SUBDIRS)" ""
subdirs:
@for i in $(SUBDIRS) ; do make -C $$i $(MAKECMDGOALS) || exit ; done
endif
clean: subdirs
@rm -rf $(OBJS) .depend $(CLEANFILES) *~
@rm -rf package
distclean: subdirs
@rm -rf $(OBJS) .depend $(CLEANFILES) $(DISTCLEANFILES) *~
@rm -rf package
ifneq "$(CFILES)" ""
ifeq ($(findstring $(MAKECMDGOALS), clean distclean),)
.depend: $(CFILES)
@$(CC) -MG -MM $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(CFILES) >$@
-include .depend
endif
endif

100
README-hwprobe.md Normal file
View File

@ -0,0 +1,100 @@
# hwprobe environment variable/kernel cmdline parameter
This document describes the `hwprobe` environment variable/kernel cmdline
parameter.
You can control the hardware probing using the environment variable
`hwprobe` and the kernel cmdline parameter `hwprobe`.
If `hwprobe` is set on the kernel cmdline, the environment variable
`hwprobe` is ignored. Otherwise, the meaning of both is exactly the same.
## Controlling probing flags
`hwprobe` controls which probing flags should *always* be set/cleared (These
settings *cannot* be overridden by command line switches). Examples:
* `hwprobe=-isapnp` - *never* do any isapnp probing
* `hwprobe=-braille,-modem` - don't look for braille displays & modems
The list of supported flags varies from version to version. To get a list of
the actual set of probing flags, call `hwinfo -all` (**Not** `--all`!) and look at the top of
the log (it lists all probing flags with their respective status there).
## Adding/removing hardware from results
`hwprobe` allows you to add and remove hardware from the probing results. In
this case the syntax is (-: remove, +: add at end of list, `<nothing>`: add at
begin of list):
hwprobe=[+-]<device_class>:<vendor_id>:<device_id>[:<unix_device_file>]
`<device_class>`, `<vendor_id>` and `<device_id>` are device ids as used by
libhd. See the output of `hwinfo` for examples. In connection with `-`, you can
use `*` as a placeholder that matches every id.
Note: `<unix_device_file>` is optional.
Note2: you cannot usefully *add* hardware that needs more info than that
given by the `hwprobe` entry. Disks & floppies are notable examples.
(But you can *remove* them.)
Here is a typical `hwinfo` output for a mouse, with the relevant ids
underlined (`<device_class>` is the combined `base_class` & `sub_class`),
(see 1st example below):
14: PS/2 00.0: 10500 PS/2 Mouse
^^^^^ --> <device_class>
[Created at mouse.110]
Vendor: s0200 "Unknown"
^^^^^ --> <vendor_id>
Model: 0002 "Generic PS/2 Mouse"
^^^^ --> <device_id>
Device File: /dev/psaux
^^^^^^^^^^ --> <unix_device_file>
Driver Info #0:
XFree86 Protocol: ps/2
GPM Protocol: ps2
Attached to: #8 (PS/2 Controller)
Examples:
hwprobe=+10500:s200:2:/dev/psaux
o add a ps/2 mouse [at the end of the hardware list]
hwprobe=10500:s200:2:/dev/psaux
o add a ps/2 mouse [at the start of the hardware list, so it
is our default mouse]
hwprobe=+10b00:s5001:0:/dev/ttyS0
o add a braille display connected to /dev/ttyS0
hwprobe=-10500:s200:2:/dev/psaux
o remove ps/2 mice attached to /dev/psaux
hwprobe=-10500:s200:2
o remove all ps/2 mice
hwprobe=-10500:*:*
o remove all ps/2 mice
hwprobe=-*:*:*:/dev/hdc
o remove /dev/hdc
hwprobe=+401:1274:5000
o add an ensoniq sound card
Graphics cards are slightly trickier:
hwprobe=+300:1014:b7
o add a Fire GL1 card
Note: this way you'll get a multihead config. You'll probably
rather want the following example.
hwprobe=-300:*:*,+300:1014:b7
o remove all graphics cards; then add a Fire GL1 card
hwprobe=+400:121a:1
o add a 3fx voodoo card (Note the class "400", not "300"!)
For more ids, see `src/ids/names.*` and `src/ids/drivers.*`.

37
README-legacy.md Normal file
View File

@ -0,0 +1,37 @@
# Legacy
This document describes some legacy features.
__Note: please do not do this, this is only kept to assist debugging.__
Legacy mode is activated when no option starting with "--" is given. In this case hwinfo
works as follows:
hwinfo [debug=deb_flag] [log=log_file] [list[+]=hw_item] [[+|-]probe_option1] [[+|-]probe_option2] ...
Examples:
* `hwinfo` - probes for nearly everything
* `hwinfo +all` - probes for everything
* `hwinfo log=hw_log` - default probing, output is written to hw_log. Please
don't use `hwinfo >some_log 2>&1` to store the output into a log file!
* `hwinfo -all +pci +int` - probe for pci devices
Note that `int` should almost always be active.
Some probing flags do not stand for complete modules but enable additional
features; e.g. `bios.vesa` or `block.cdrom`.
Example:
hwinfo -all +block +int
gives a list of all block devs
hwinfo -all +block.cdrom +int
additionally reads the iso9660 info.
The list of supported flags varies from version to version. To get a list of
the actual set of probing flags, call `hwinfo -all` and look at the top of
the log.

127
README.md Normal file
View File

@ -0,0 +1,127 @@
# hwinfo
## Overview
hwinfo/libhd are used to probe for the hardware present in the system. It can be
used to generate a system overview log which can be later used for support.
This project provides a hardware probing library `libhd.so` and a command line tool `hwinfo` using it.
A major project using this library is [YaST](https://yast.github.io), the SUSE installation tool.
To give you an idea what kind of information it provides, here's the output it gives when asked about the graphcs card:
```sh
# hwinfo --gfxcard
27: PCI 200.0: 0300 VGA compatible controller (VGA)
[Created at pci.378]
Unique ID: B35A.G9ppNwS+xM4
Parent ID: _Znp.nMBktMhAWbC
SysFS ID: /devices/pci0000:00/0000:00:02.0/0000:02:00.0
SysFS BusID: 0000:02:00.0
Hardware Class: graphics card
Model: "nVidia GF119 [NVS 310]"
Vendor: pci 0x10de "nVidia Corporation"
Device: pci 0x107d "GF119 [NVS 310]"
SubVendor: pci 0x10de "nVidia Corporation"
SubDevice: pci 0x094e
Revision: 0xa1
Driver: "nvidia"
Driver Modules: "nvidia"
Memory Range: 0xfa000000-0xfaffffff (rw,non-prefetchable)
Memory Range: 0xf0000000-0xf7ffffff (ro,non-prefetchable)
Memory Range: 0xf8000000-0xf9ffffff (ro,non-prefetchable)
I/O Ports: 0xe000-0xefff (rw)
Memory Range: 0xfb000000-0xfb07ffff (ro,non-prefetchable,disabled)
IRQ: 82 (3241635 events)
I/O Ports: 0x3c0-0x3df (rw)
Module Alias: "pci:v000010DEd0000107Dsv000010DEsd0000094Ebc03sc00i00"
Driver Info #0:
Driver Status: nouveau is not active
Driver Activation Cmd: "modprobe nouveau"
Driver Info #1:
Driver Status: nvidia is active
Driver Activation Cmd: "modprobe nvidia"
Config Status: cfg=new, avail=yes, need=no, active=unknown
Attached to: #9 (PCI bridge)
Primary display adapter: #27
```
If that's a bit too much information, you can ask it also for an abbreviated form. For example:
```sh
# hwinfo --short --disk --cdrom
disk:
/dev/sda WDC WD10EARS-00Y
/dev/sdb ST2000DM001-1CH1
cdrom:
/dev/sr0 PLDS DVD+-RW DS-8ABSH
```
You can influence libhd via the `hwprobe` environment variable resp. the `hwprobe` boot option.
This includes turning on or off
probing modules and also manually adding hardware devices (to some degree).
For example
```sh
export hwprobe=-bios
```
will turn off the `bios` probing module.
For details about `hwprobe` look [here](README-hwprobe.md).
For general usage instructions, see `hwinfo` manual page.
> ### Note
>
>
> `hwinfo` has a legacy interface, accepting `hwprobe`-like options as command argument (For example
> `hwinfo -bios` - note the single '`-`'). Please don't do this. If you are interested, you can
> read about it [here](README-legacy.md).
## Technical documentation
The hardware detection library makes use of a number of technical specifications.
[Here](specifications.md) is a compilation of external links to technical standards relevant to `libhd`.
## openSUSE Development
To build the library, simply run `make`. Install with `make install`.
Basically every new commit into the master branch of the repository will be auto-submitted
to all current SUSE products. No further action is needed except accepting the pull request.
Submissions are managed by a SUSE internal [jenkins](https://jenkins.io) node in the InstallTools tab.
Each time a new commit is integrated into the master branch of the repository,
a new submit request is created to the openSUSE Build Service. The devel project
is [system:install:head](https://build.opensuse.org/package/show/system:install:head/hwinfo).
For maintained branches, the package is submitted to a devel project but the final submission
must be triggered manually.
`*.changes` and version numbers are auto-generated from git commits, you don't have to worry about this.
The spec file is maintained in the Build Service only. If you need to change it for the `master` branch,
submit to the
[devel project](https://build.opensuse.org/package/show/system:install:head/hwinfo)
in the build service directly.
The current names of the devel projects for other branches can be seen in the jenkins logs.
Development happens mainly in the `master` branch. The branch is used for all current products.
In rare cases branching was unavoidable:
* branch `sl_11.1`: SLE 11 SP4
* branch `sle12`: SLE 12 (**not** SPx)
You can find more information about the changes auto-generation and the
tools used for jenkis submissions in the [linuxrc-devtools
documentation](https://github.com/openSUSE/linuxrc-devtools#opensuse-development).
## Updating Database for Pci and Usb Ids
For details about updating pci and usb ids look [here](src/ids/README.md).

1
doc/.cvsignore Normal file
View File

@ -0,0 +1 @@
libhd

84
doc/check_hd.1 Normal file
View File

@ -0,0 +1,84 @@
.TH CHECK_HD "1" "User Commands"
.SH NAME
manual page for check_hd
.SH SYNOPSIS
.B check_hd
[\fIoptions\fR] \fIfiles\fR
.SH DESCRIPTION
Try to put hardware data into a consistent form.
.TP
\fB\-\-check\fR
do a lot of checks and remove unnecessary data
.TP
\fB\-\-sort\fR
sort data
.TP
\fB\-\-reverse\fR
reverse sorting order
.TP
\fB\-\-split\fR
write separate entries for each key
.TP
\fB\-\-with\-source\fR
add comment to each item indicating info source
.TP
\fB\-\-mini\fR
minimal data base (basically driver info only)
.TP
\fB\-\-join\-keys\-first\fR
when combining similar items, join entries with
common keys first (default is common values first)
.TP
\fB\-\-cfile\fR file
create C file to be included in libhd
.TP
\fB\-\-no\-compact\fR
don't try to make C version as small as possible
.TP
\fB\-\-out\fR file
write results to file, default is "hd.ids"
.TP
\fB\-\-log\fR file
write log info to file, default is "hd.log"
.IP
Note: check_hd works with libhd/hwinfo internal format only;
to convert to other formats, use convert_hd
.PP
Try to put hardware data into a consistent form.
.TP
\fB\-\-check\fR
do a lot of checks and remove unnecessary data
.TP
\fB\-\-sort\fR
sort data
.TP
\fB\-\-reverse\fR
reverse sorting order
.TP
\fB\-\-split\fR
write separate entries for each key
.TP
\fB\-\-with\-source\fR
add comment to each item indicating info source
.TP
\fB\-\-mini\fR
minimal data base (basically driver info only)
.TP
\fB\-\-join\-keys\-first\fR
when combining similar items, join entries with
common keys first (default is common values first)
.TP
\fB\-\-cfile\fR file
create C file to be included in libhd
.TP
\fB\-\-no\-compact\fR
don't try to make C version as small as possible
.TP
\fB\-\-out\fR file
write results to file, default is "hd.ids"
.TP
\fB\-\-log\fR file
write log info to file, default is "hd.log"
.IP
Note: check_hd works with libhd/hwinfo internal format only;
to convert to other formats, use convert_hd

29
doc/convert_hd.1 Normal file
View File

@ -0,0 +1,29 @@
.TH CONVERT_HD "1" "User Commands"
.SH NAME
convert_hd
.SH SYNOPSIS
.B convert_hd
[\fIoptions\fR] \fIfiles\fR
.SH DESCRIPTION
Convert various hardware info to libhd/hwinfo internal format or to XML.
.TP
\fB\-\-ids\fR
write internal format (default) to "hd.ids"
.TP
\fB\-\-no\-ids\fR
do not write internal format
.TP
\fB\-\-xml\fR
write XML to "hd.xml", DTD to "hd.dtd"
.TP
\fB\-\-no\-xml\fR
do not write XML (default)
.TP
\fB\-\-with\-source\fR
add comment to each item indicating info source
.TP
\fB\-\-internal\-dtd\fR
generate internal dtd
.IP
Note: for more sophisticated operations on hardware data use check_hd.
.IP

26
doc/example1.c Normal file
View File

@ -0,0 +1,26 @@
#include <stdio.h>
#include <stdlib.h>
#include <hd.h>
int main(int argc, char **argv)
{
hd_data_t *hd_data;
hd_t *hd;
hd_data = calloc(1, sizeof *hd_data);
hd = hd_list(hd_data, hw_scsi, 1, NULL);
for(; hd; hd = hd->next) {
hd_dump_entry(hd_data, hd, stdout)
}
hd_free_hd_list(hd); /* free it */
hd_free_hd_data(hd_data);
free(hd_data);
return 0;
}

26
doc/example2.c Normal file
View File

@ -0,0 +1,26 @@
#include <stdio.h>
#include <stdlib.h>
#include <hd.h>
int main(int argc, char **argv)
{
hd_data_t *hd_data;
hd_t *hd;
unsigned display_idx;
hd_data = calloc(1, sizeof *hd_data);
hd = hd_list(hd_data, hw_display, 1, NULL);
display_idx = hd_display_adapter(hd_data);
hd_dump_entry(hd_data, hd_get_device_by_idx(hd_data, display_idx), stdout)
hd_free_hd_list(hd);
hd_free_hd_data(hd_data);
free(hd_data);
return 0;
}

1
doc/footer.html Normal file
View File

@ -0,0 +1 @@
</body></html>

10
doc/getsysinfo.1 Normal file
View File

@ -0,0 +1,10 @@
.TH GETSYSINFO "1" "getsysinfo" "User Commands"
.SH NAME
getsysinfo
.SH SYNOPSIS
.B getsysinfo
.SH DESCRIPTION
Collect some system data that are useful for debugging
hardware detection bugs.
.PP

125
doc/hwinfo.8 Normal file
View File

@ -0,0 +1,125 @@
.TH HWINFO 8 "June 2016" "hwinfo" "System configuration"
.\"
.nh
.SH NAME
hwinfo \- probe for hardware
.\"
.SH SYNOPSIS
.B hwinfo [
.I OPTIONS
.B ]
.\"
.SH DESCRIPTION
.B hwinfo
is used to probe for the hardware present in the system. It can be used to
generate a system overview log which can be later used for support.
.\"
.SH OPTIONS
Note that running \fBhwinfo\fR without any options is roughly equivalent
to 'hwinfo --all --log=-'.
.TP
\fB--<\f[BI]HARDWARE_ITEM\fB>\fR
This option can be given more than once.
Probe for a particular \fIHARDWARE_ITEM\fR. Available hardware items are:
all, arch, bios, block, bluetooth, braille, bridge, camera, cdrom, chipcard,
cpu, disk, dsl, dvb, fingerprint, floppy, framebuffer, gfxcard, hub, ide,
isapnp, isdn, joystick, keyboard, memory, mmc-ctrl, modem, monitor, mouse, netcard,
network, partition, pci, pcmcia, pcmcia-ctrl, pppoe, printer, redasd,
reallyall, scanner, scsi, smp, sound, storage-ctrl, sys, tape, tv, uml, usb,
usb-ctrl, vbe, wlan, xen, zip
Note that if this option is missing, no hardware probing will be done!
.TP
\fB--short\fR
Show only a summary. Use this option in addition to a hardware probing
option.
.TP
\fB--listmd\fR
Normally hwinfo does not report RAID devices. Add this option to see them.
.TP
\fB--only \fIDEVNAME\fR
This option can be given more than once. If you add this option
only entries in the device list matching \fIDEVNAME\fR will be shown.
Note that you also have to specify --<\fIHARDWARE_ITEM\fR> to trigger
any device probing.
.TP
\fB--save-config \fISPEC\fR
Store config for a particular device below /var/lib/hardware. \fISPEC\fR
can be a device name, an UDI, or 'all'. This option must be given in addition to
a hardware probing option.
.TP
\fB--show-config \fIUDI\fR
Show saved config data for a particular device.
.TP
\fB--map\fR
If disk names have changed (e.g. after a kernel update) this prints a list
of disk name mappings. Note that you must have used --save-config at some
point before for this can work.
.TP
\fB--debug \fIN\fR
Set debug level to \fIN\fR. The debug info is shown only in the log file.
If you specify a log file, the debug level is implicitly set to a reasonable value
(N is a bitmask of individual flags).
.TP
\fB--verbose\fR
Increase verbosity. Only together with --map.
.TP
\fB--log \fIFILE\fR
Write log info to \fIFILE\fR.
Don't forget to also specify --<\fIHARDWARE_ITEM\fR> to trigger any device probing.
.TP
\fB--dump-db \fIN\fR
Dump hardware data base. \fIN\fR is either 0 for the external data base in
/var/lib/hardware, or 1 for the internal data base.
.TP
\fB--version\fR
Print libhd version.
.TP
\fB--help\fR
Print usage.
.\"
.SH ENVIRONMENT
\fBhwprobe\fR can hold a comma-separated list of probing flags preceded by '+'
or '-' to be turned on or off. To get a complete list of supported flags,
run 'hwinfo -all' (note: not '--all') and look at the top of the output.
hwinfo also looks at /proc/cmdline for a \fBhwprobe\fR option.
.\"
.SH EXAMPLES
.TP
- show all disks
hwinfo --disk
.TP
- just an overview
hwinfo --short --block
.TP
- show a particular disk
hwinfo --disk --only /dev/sdb
.TP
- save disk config state
hwinfo --disk --save-config=all
.TP
- try 4 graphics card ports for monitor data (default: 3)
hwprobe=bios.ddc.ports=4 hwinfo --monitor
.TP
- monitor detection runs the Video BIOS to get the monitor data; dump a complete BIOS code execution trace to the log
hwprobe=bios.ddc.ports=1,x86emu=trace:dump:trace.only=0:dump.only=0 \
hwinfo --monitor --log=foo
.\"
.SH FILES
.TP
\fB/var/lib/hardware/hd.ids\fR
External hardware data base (in readable text form). Try the --dump-db option to see the format.
.TP
\fB/var/lib/hardware/udi\fR
Directory where persistent config data are stored (see --save-config option).
.\"
.SH BUGS
Not all hardware can be detected.
.\"
.SH "SEE ALSO"
More documentation in /usr/share/doc/packages/hwinfo.
.br
Source repository: git://git.opensuse.org/projects/hwinfo.git.
.\"

109
doc/libhd.doc Normal file
View File

@ -0,0 +1,109 @@
/*! \mainpage libhd documentation
<h2>Introduction</h2>
libhd is a hardware detection lib.
<h2>Changes</h2>
\ref libhd_5_12
<h2>Examples</h2>
\ref example1
\ref example2
*/
/*! \page examples Examples
\ref example1
\ref example2
<hr>
\subsection example1 Get list of SCSI controllers
\include example1.c
<hr>
\subsection example2 Get primary display controller
\include example2.c
*/
/*! \page changes Changes
\ref libhd_5_12
<hr>
\subsection libhd_5_12 libhd-5.12
<ul>
<li>changes in \ref hd_t
<ul>
<li>\ref hd_t::bus "bus" is now a struct, use \ref hd_t::bus "bus.id" instead.
<li>\ref hd_t::base_class "base_class" is now a struct, use \ref hd_t::base_class "base_class.id" instead.
<li>\ref hd_t::sub_class "sub_class" is now a struct, use \ref hd_t::sub_class "sub_class.id" instead.
<li>\ref hd_t::prog_if "prog_if" is now a struct, use \ref hd_t::prog_if "prog_if.id" instead.
<li>\ref hd_t::vendor "vendor" is a struct replacing vend and vend_name.
Use \ref hd_t::vendor "vendor.id" and \ref hd_t::vendor "vendor.name" now.
<li>\ref hd_t::device "device" is a struct replacing dev and dev_name.
Use \ref hd_t::device "device.id" and \ref hd_t::device "device.name" now.
<li>\ref hd_t::sub_vendor "sub_vendor" is a struct replacing sub_vend and sub_vend_name.
Use \ref hd_t::sub_vendor "sub_vendor.id" and \ref hd_t::sub_vendor "sub_vendor.name" now.
<li>\ref hd_t::sub_device "sub_device" is a struct replacing sub_dev and sub_dev_name.
Use \ref hd_t::sub_device "sub_device.id" and \ref hd_t::sub_device "sub_device.name" now.
<li>\ref hd_t::revision "revision" is a struct replacing rev and rev_name.
Use \ref hd_t::revision "revision.id" and \ref hd_t::revision "revision.name" now.
<li>\ref hd_t::compat_vendor "compat_vendor" is a struct replacing compat_vend.
Use \ref hd_t::compat_vendor "compat_vendor.id" now.
<li>\ref hd_t::compat_device "compat_device" is a struct replacing compat_dev.
Use \ref hd_t::compat_device "compat_device.id" now.
</ul>
<li>interface functions removed
<ul>
<li>hd_bus_name is gone. Use \ref hd_t::bus "bus.name" instead.
<li>hd_class_name is gone. Use \ref hd_t::base_class "base_class.name",
\ref hd_t::sub_class "sub_class.name" or \ref hd_t::prog_if "prog_if.name" instead.
<li>hd_vendor_name is gone. Use \ref hd_t::vendor "vendor.name" instead.
<li>hd_device_name is gone. Use \ref hd_t::device "device.name" instead.
<li>hd_sub_device_name is gone. Use \ref hd_t::sub_device "sub_device.name" instead.
<li>hd_find_device_by_name is gone.
</ul>
</ul>
<hr>
*/

898
doc/libhd.doxy Normal file
View File

@ -0,0 +1,898 @@
# Doxyfile 1.2.13.1
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project
#
# All text after a hash (#) is considered a comment and will be ignored
# The format is:
# TAG = value [value, ...]
# For lists items can also be appended using:
# TAG += value [value, ...]
# Values that contain spaces should be placed between quotes (" ")
#---------------------------------------------------------------------------
# General configuration options
#---------------------------------------------------------------------------
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME = libhd
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER = 5.0
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY = libhd
# 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.
# The default language is English, other supported languages are:
# Brazilian, Chinese, Croatian, Czech, Danish, Dutch, Finnish, French,
# German, Greek, Hungarian, Italian, Japanese, Korean, Norwegian, Polish,
# Portuguese, Romanian, Russian, Slovak, Slovene, Spanish and Swedish.
OUTPUT_LANGUAGE = English
# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
# documentation are documented, even if no documentation was available.
# Private class members and static file members will be hidden unless
# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
# will be included in the documentation.
EXTRACT_PRIVATE = NO
# If the EXTRACT_STATIC tag is set to YES all static members of a file
# will be included in the documentation.
EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
# defined locally in source files will be included in the documentation.
# If set to NO only classes defined in header files are included.
EXTRACT_LOCAL_CLASSES = YES
# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
# undocumented members of documented classes, files or namespaces.
# If set to NO (the default) these members will be included in the
# various overviews, but no documentation section is generated.
# This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy.
# If set to NO (the default) these class will be included in the various
# overviews. This option has no effect if EXTRACT_ALL is enabled.
HIDE_UNDOC_CLASSES = NO
# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
# include brief member descriptions after the members that are listed in
# the file and class documentation (similar to JavaDoc).
# Set to NO to disable this.
BRIEF_MEMBER_DESC = YES
# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
# the brief description of a member or function before the detailed description.
# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
# brief descriptions will be completely suppressed.
REPEAT_BRIEF = YES
# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
# Doxygen will generate a detailed section even if there is only a brief
# description.
ALWAYS_DETAILED_SEC = NO
# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited
# members of a class in the documentation of that class as if those members were
# ordinary class members. Constructors, destructors and assignment operators of
# the base classes will not be shown.
INLINE_INHERITED_MEMB = NO
# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
# path before files name in the file list and in the header files. If set
# to NO the shortest path that makes the file name unique will be used.
FULL_PATH_NAMES = NO
# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
# can be used to strip a user defined part of the path. Stripping is
# only done if one of the specified strings matches the left-hand part of
# the path. It is allowed to use relative paths in the argument list.
STRIP_FROM_PATH =
# The INTERNAL_DOCS tag determines if documentation
# that is typed after a \internal command is included. If the tag is set
# to NO (the default) then the documentation will be excluded.
# Set it to YES to include the internal documentation.
INTERNAL_DOCS = NO
# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
# doxygen to hide any special comment blocks from generated source code
# fragments. Normal C and C++ comments will always remain visible.
STRIP_CODE_COMMENTS = YES
# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
# file names in lower case letters. If set to YES upper case letters are also
# allowed. This is useful if you have classes or files whose names only differ
# in case and if your file system supports case sensitive file names. Windows
# users are adviced to set this option to NO.
CASE_SENSE_NAMES = YES
# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
# (but less readable) file names. This can be useful is your file systems
# doesn't support long names like on DOS, Mac, or CD-ROM.
SHORT_NAMES = NO
# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
# will show members with their full class and namespace scopes in the
# documentation. If set to YES the scope will be hidden.
HIDE_SCOPE_NAMES = NO
# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
# will generate a verbatim copy of the header file for each class for
# which an include is specified. Set to NO to disable this.
VERBATIM_HEADERS = YES
# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
# will put list of the files that are included by a file in the documentation
# of that file.
SHOW_INCLUDE_FILES = YES
# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
# will interpret the first line (until the first dot) of a JavaDoc-style
# comment as the brief description. If set to NO, the JavaDoc
# comments will behave just like the Qt-style comments (thus requiring an
# explict @brief command for a brief description.
JAVADOC_AUTOBRIEF = YES
# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
# member inherits the documentation from any documented member that it
# reimplements.
INHERIT_DOCS = YES
# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
# is inserted in the documentation for inline members.
INLINE_INFO = YES
# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
# will sort the (detailed) documentation of file and class members
# alphabetically by member name. If set to NO the members will appear in
# declaration order.
SORT_MEMBER_DOCS = YES
# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
# tag is set to YES, then doxygen will reuse the documentation of the first
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
# The TAB_SIZE tag can be used to set the number of spaces in a tab.
# Doxygen uses this value to replace tabs by spaces in code fragments.
TAB_SIZE = 8
# The GENERATE_TODOLIST tag can be used to enable (YES) or
# disable (NO) the todo list. This list is created by putting \todo
# commands in the documentation.
GENERATE_TODOLIST = YES
# The GENERATE_TESTLIST tag can be used to enable (YES) or
# disable (NO) the test list. This list is created by putting \test
# commands in the documentation.
GENERATE_TESTLIST = YES
# The GENERATE_BUGLIST tag can be used to enable (YES) or
# disable (NO) the bug list. This list is created by putting \bug
# commands in the documentation.
GENERATE_BUGLIST = YES
# This tag can be used to specify a number of aliases that acts
# as commands in the documentation. An alias has the form "name=value".
# For example adding "sideeffect=\par Side Effects:\n" will allow you to
# put the command \sideeffect (or @sideeffect) in the documentation, which
# will result in a user defined paragraph with heading "Side Effects:".
# You can put \n's in the value part of an alias to insert newlines.
ALIASES =
# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
ENABLED_SECTIONS =
# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
# the initial value of a variable or define consist of for it to appear in
# the documentation. If the initializer consists of more lines than specified
# here it will be hidden. Use a value of 0 to hide initializers completely.
# The appearance of the initializer of individual variables and defines in the
# documentation can be controlled using \showinitializer or \hideinitializer
# command in the documentation regardless of this setting.
MAX_INITIALIZER_LINES = 30
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C.
# For instance some of the names that are used will be different. The list
# of all members will be omitted, etc.
OPTIMIZE_OUTPUT_FOR_C = YES
# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
# at the bottom of the documentation of classes and structs. If set to YES the
# list will mention the files that were used to generate the documentation.
SHOW_USED_FILES = YES
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
# The QUIET tag can be used to turn on/off the messages that are generated
# by doxygen. Possible values are YES and NO. If left blank NO is used.
QUIET = NO
# The WARNINGS tag can be used to turn on/off the warning messages that are
# generated by doxygen. Possible values are YES and NO. If left blank
# NO is used.
WARNINGS = YES
# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
# automatically be disabled.
WARN_IF_UNDOCUMENTED = YES
# The WARN_FORMAT tag determines the format of the warning messages that
# doxygen can produce. The string should contain the $file, $line, and $text
# tags, which will be replaced by the file and line number from which the
# warning originated and the warning text.
WARN_FORMAT =
# The WARN_LOGFILE tag can be used to specify a file to which warning
# and error messages should be written. If left blank the output is written
# to stderr.
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
# The INPUT tag can be used to specify the files and/or directories that contain
# documented source files. You may enter file names like "myfile.cpp" or
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
INPUT = libhd.doc ../src
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank the following patterns are tested:
# *.c *.cc *.cxx *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp
# *.h++ *.idl
FILE_PATTERNS = *.c *.h
# The RECURSIVE tag can be used to turn specify whether or not subdirectories
# should be searched for input files as well. Possible values are YES and NO.
# If left blank NO is used.
RECURSIVE = YES
# The EXCLUDE tag can be used to specify files and/or directories that should
# excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE = ../src/ids
# If the value of the INPUT tag contains directories, you can use the
# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
# certain files from those directories.
EXCLUDE_PATTERNS =
# The EXAMPLE_PATH tag can be used to specify one or more files or
# directories that contain example code fragments that are included (see
# the \include command).
EXAMPLE_PATH = .
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
# and *.h) to filter out the source-files in the directories. If left
# blank all files are included.
EXAMPLE_PATTERNS = *.c
# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
# searched for input files to be used with the \include or \dontinclude
# commands irrespective of the value of the RECURSIVE tag.
# Possible values are YES and NO. If left blank NO is used.
EXAMPLE_RECURSIVE = NO
# The IMAGE_PATH tag can be used to specify one or more files or
# directories that contain image that are included in the documentation (see
# the \image command).
IMAGE_PATH =
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program
# by executing (via popen()) the command <filter> <input-file>, where <filter>
# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
# input file. Doxygen will then use the output that the filter program writes
# to standard output.
INPUT_FILTER =
# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
# INPUT_FILTER) will be used to filter the input files when producing source
# files to browse.
FILTER_SOURCE_FILES = NO
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
# If the SOURCE_BROWSER tag is set to YES then a list of source files will
# be generated. Documented entities will be cross-referenced with these sources.
SOURCE_BROWSER = NO
# Setting the INLINE_SOURCES tag to YES will include the body
# of functions and classes directly in the documentation.
INLINE_SOURCES = NO
# If the REFERENCED_BY_RELATION tag is set to YES (the default)
# then for each documented function all documented
# functions referencing it will be listed.
REFERENCED_BY_RELATION = YES
# If the REFERENCES_RELATION tag is set to YES (the default)
# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
# of all compounds will be generated. Enable this if the project
# contains a lot of classes, structs, unions or interfaces.
ALPHABETICAL_INDEX = NO
# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
# in which this list will be split (can be a number in the range [1..20])
COLS_IN_ALPHA_INDEX = 5
# In case all classes in a project start with a common prefix, all
# classes will be put under the same header in the alphabetical index.
# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
# should be ignored while generating the index headers.
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
# generate HTML output.
GENERATE_HTML = YES
# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `html' will be used as the default path.
HTML_OUTPUT =
# The HTML_HEADER tag can be used to specify a personal HTML header for
# each generated HTML page. If it is left blank doxygen will generate a
# standard header.
HTML_HEADER =
# The HTML_FOOTER tag can be used to specify a personal HTML footer for
# each generated HTML page. If it is left blank doxygen will generate a
# standard footer.
HTML_FOOTER = footer.html
# The HTML_STYLESHEET tag can be used to specify a user defined cascading
# style sheet that is used by each HTML page. It can be used to
# fine-tune the look of the HTML output. If the tag is left blank doxygen
# will generate a default style sheet
HTML_STYLESHEET =
# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
# files or namespaces will be aligned in HTML using tables. If set to
# NO a bullet list will be used.
HTML_ALIGN_MEMBERS = YES
# If the GENERATE_HTMLHELP tag is set to YES, additional index files
# will be generated that can be used as input for tools like the
# Microsoft HTML help workshop to generate a compressed HTML help file (.chm)
# of the generated HTML documentation.
GENERATE_HTMLHELP = NO
# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
# controls if a separate .chi index file is generated (YES) or that
# it should be included in the master .chm file (NO).
GENERATE_CHI = NO
# If the GENERATE_HTMLHELP tag is set to YES, 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.
BINARY_TOC = NO
# The TOC_EXPAND flag can be set to YES to add extra items for group members
# to the contents of the Html help documentation and to the tree view.
TOC_EXPAND = NO
# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
# top of each HTML page. The value NO (the default) enables the index and
# the value YES disables it.
DISABLE_INDEX = NO
# This tag can be used to set the number of enum values (range [1..20])
# that doxygen will group on one line in the generated HTML documentation.
ENUM_VALUES_PER_LINE = 4
# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be
# generated containing a tree-like index structure (just like the one that
# is generated for HTML Help). For this to work a browser that supports
# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+,
# or Internet explorer 4.0+). Note that for large projects the tree generation
# can take a very long time. In such cases it is better to disable this feature.
# Windows users are probably better off using the HTML help feature.
GENERATE_TREEVIEW = NO
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
# used to set the initial width (in pixels) of the frame in which the tree
# is shown.
TREEVIEW_WIDTH = 250
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
# generate Latex output.
GENERATE_LATEX = YES
# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `latex' will be used as the default path.
LATEX_OUTPUT =
# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
# LaTeX documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_LATEX = NO
# The PAPER_TYPE tag can be used to set the paper type that is used
# by the printer. Possible values are: a4, a4wide, letter, legal and
# executive. If left blank a4wide will be used.
PAPER_TYPE = a4wide
# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
# packages that should be included in the LaTeX output.
EXTRA_PACKAGES =
# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
# the generated latex document. The header should contain everything until
# the first chapter. If it is left blank doxygen will generate a
# standard header. Notice: only use this tag if you know what you are doing!
LATEX_HEADER =
# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
# is prepared for conversion to pdf (using ps2pdf). The pdf file will
# contain links (just like the HTML output) instead of page references
# This makes the output suitable for online browsing using a pdf viewer.
PDF_HYPERLINKS = NO
# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
# plain latex in the generated Makefile. Set this option to YES to get a
# higher quality PDF documentation.
USE_PDFLATEX = NO
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
# command to the generated LaTeX files. This will instruct LaTeX to keep
# running if errors occur, instead of asking the user for help.
# This option is also used when generating formulas in HTML.
LATEX_BATCHMODE = NO
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
# The RTF output is optimised for Word 97 and may not look very pretty with
# other RTF readers or editors.
GENERATE_RTF = NO
# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `rtf' will be used as the default path.
RTF_OUTPUT =
# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
# RTF documents. This may be useful for small projects and may help to
# save some trees in general.
COMPACT_RTF = NO
# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
# will contain hyperlink fields. The RTF file will
# contain links (just like the HTML output) instead of page references.
# This makes the output suitable for online browsing using WORD or other
# programs which support those fields.
# Note: wordpad (write) and others do not support links.
RTF_HYPERLINKS = NO
# Load stylesheet definitions from file. Syntax is similar to doxygen's
# config file, i.e. a series of assigments. You only have to provide
# replacements, missing definitions are set to their default value.
RTF_STYLESHEET_FILE =
# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
# generate man pages
GENERATE_MAN = NO
# The MAN_OUTPUT tag is used to specify where the man pages will be put.
# If a relative path is entered the value of OUTPUT_DIRECTORY will be
# put in front of it. If left blank `man' will be used as the default path.
MAN_OUTPUT =
# The MAN_EXTENSION tag determines the extension that is added to
# the generated man pages (default is the subroutine's section .3)
MAN_EXTENSION =
# 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 them the man command
# would be unable to find the correct page. The default is NO.
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
# If the GENERATE_XML tag is set to YES Doxygen will
# generate an XML file that captures the structure of
# the code including all documentation. Note that this
# feature is still experimental and incomplete at the
# moment.
GENERATE_XML = NO
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
# generate an AutoGen Definitions (see autogen.sf.net) file
# that captures the structure of the code including all
# documentation. Note that this feature is still experimental
# and incomplete at the moment.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
# evaluate all C-preprocessor directives found in the sources and include
# files.
ENABLE_PREPROCESSING = YES
# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
# names in the source code. If set to NO (the default) only conditional
# compilation will be performed. Macro expansion can be done in a controlled
# way by setting EXPAND_ONLY_PREDEF to YES.
MACRO_EXPANSION = NO
# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
# then the macro expansion is limited to the macros specified with the
# PREDEFINED and EXPAND_AS_PREDEFINED tags.
EXPAND_ONLY_PREDEF = NO
# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
# in the INCLUDE_PATH (see below) will be search if a #include is found.
SEARCH_INCLUDES = YES
# The INCLUDE_PATH tag can be used to specify one or more directories that
# contain include files that are not input files but should be processed by
# the preprocessor.
INCLUDE_PATH =
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the
# directories. If left blank, the patterns specified with FILE_PATTERNS will
# be used.
INCLUDE_FILE_PATTERNS =
# The PREDEFINED tag can be used to specify one or more macro names that
# are defined before the preprocessor is started (similar to the -D option of
# gcc). The argument of the tag is a list of macros of the form: name
# or name=definition (no spaces). If the definition and the = are
# omitted =1 is assumed.
PREDEFINED =
# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then
# this tag can be used to specify a list of macro names that should be expanded.
# The macro definition that is found in the sources will be used.
# Use the PREDEFINED tag if you want to use a different macro definition.
EXPAND_AS_DEFINED =
# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
# doxygen's preprocessor will remove all function-like macros that are alone
# on a line 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.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
#---------------------------------------------------------------------------
# The TAGFILES tag can be used to specify one or more tagfiles.
TAGFILES =
# When a file name is specified after GENERATE_TAGFILE, doxygen will create
# a tag file that is based on the input files it reads.
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES all external classes will be listed
# in the class index. If set to NO only the inherited external classes
# will be listed.
ALLEXTERNALS = NO
# The PERL_PATH should be the absolute path and name of the perl script
# interpreter (i.e. the result of `which perl').
PERL_PATH =
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or
# super classes. Setting the tag to NO turns the diagrams off. Note that this
# option is superceded by the HAVE_DOT option below. This is only a fallback. It is
# recommended to install and use dot, since it yield more powerful graphs.
CLASS_DIAGRAMS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz, 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)
HAVE_DOT = NO
# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect inheritance relations. Setting this tag to YES will force the
# the CLASS_DIAGRAMS tag to NO.
CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
# will generate a graph for each documented class showing the direct and
# indirect implementation dependencies (inheritance, containment, and
# class references variables) of the class with other documented classes.
COLLABORATION_GRAPH = YES
# If set to YES, the inheritance and collaboration graphs will show the
# relations between templates and their instances.
TEMPLATE_RELATIONS = YES
# If set to YES, the inheritance and collaboration graphs will hide
# inheritance and usage relations if the target is undocumented
# or is not a class.
HIDE_UNDOC_RELATIONS = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
# tags are set to YES then doxygen will generate a graph for each documented
# file showing the direct and indirect include dependencies of the file with
# other documented files.
INCLUDE_GRAPH = YES
# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
# documented header file showing the documented files that directly or
# indirectly include this file.
INCLUDED_BY_GRAPH = YES
# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
# will graphical hierarchy of all classes instead of a textual one.
GRAPHICAL_HIERARCHY = YES
# The tag DOT_PATH can be used to specify the path where the dot tool can be
# found. If left blank, it is assumed the dot tool can be found on the path.
DOT_PATH =
# The DOTFILE_DIRS tag can be used to specify one or more directories that
# contain dot files that are included in the documentation (see the
# \dotfile command).
DOTFILE_DIRS =
# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_WIDTH = 1024
# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height
# (in pixels) of the graphs generated by dot. If a graph becomes larger than
# this value, doxygen will try to truncate the graph, so that it fits within
# the specified constraint. Beware that most browsers cannot cope with very
# large images.
MAX_DOT_GRAPH_HEIGHT = 1024
# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
# generate a legend page explaining the meaning of the various boxes and
# arrows in the dot generated graphs.
GENERATE_LEGEND = YES
# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
# remove the intermedate dot files that are used to generate
# the various graphs.
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
# The SEARCHENGINE tag specifies whether or not a search engine should be
# used. If set to NO the values of all tags below this one will be ignored.
SEARCHENGINE = NO
# The CGI_NAME tag should be the name of the CGI script that
# starts the search engine (doxysearch) with the correct parameters.
# A script with this name will be generated by doxygen.
CGI_NAME =
# The CGI_URL tag should be the absolute URL to the directory where the
# cgi binaries are located. See the documentation of your http daemon for
# details.
CGI_URL =
# The DOC_URL tag should be the absolute URL to the directory where the
# documentation is located. If left blank the absolute path to the
# documentation, with file:// prepended to it, will be used.
DOC_URL =
# The DOC_ABSPATH tag should be the absolute path to the directory where the
# documentation is located. If left blank the directory on the local machine
# will be used.
DOC_ABSPATH =
# The BIN_ABSPATH tag must point to the directory where the doxysearch binary
# is installed.
BIN_ABSPATH =
# The EXT_DOC_PATHS tag can be used to specify one or more paths to
# documentation generated for other projects. This allows doxysearch to search
# the documentation for these projects as well.
EXT_DOC_PATHS =

8
doc/mk_isdnhwdb.1 Normal file
View File

@ -0,0 +1,8 @@
.TH mk_isdnhwdb "1" "mk_isdnhwdb" "User Commands"
.SH NAME
mk_isdnhwdb
.SH SYNOPSIS
.B mk_isdnhwdb
.SH DESCRIPTION
.PP

50
gen-hwcfg-disk.sh Executable file
View File

@ -0,0 +1,50 @@
#! /bin/sh
#
# gen-hwcfg-disk.sh
#
# Generates hwcfg file for all configured disks
#
if [ -x /sbin/ata_identify ]; then
ATA_ID=/sbin/ata_identify
elif [ -x /lib/klibc/bin/ata_identify ]; then
ATA_ID=/lib/klibc/bin/ata_identify
else
echo "ata_identify not found, please install udev"
exit 1
fi
hwcfg=/etc/sysconfig/hardware
if [ ! -d "$hwcfg" ]; then
echo "No hardware configuration directory found"
exit 1
fi
# IDE disks first
for ifname in /sys/block/hd*; do
id=$($ATA_ID /dev/${ifname##*/} 2> /dev/null)
if [ $? -eq 0 ]; then
filename="SATA_$id"
echo "Generate hwcfg file for $filename"
echo "DEVICE=${ifname##*/}" > ${hwcfg}/hwcfg-disk-id-${filename}
fi
done
# SCSI disks next
for ifname in /sys/block/sd*; do
if [ -d $ifname/device ]; then
read vendor < $ifname/device/vendor
if [ "$vendor" = "ATA" ]; then
# We need page 0x80 to get the serial number
page="-p 0x80"
else
page=
fi
scsi_id -g $page -s ${ifname#/sys} 2> /dev/null | while read vendor model serial; do
filename="${vendor}_${model}_${serial}"
echo "Generate hwcfg file for $filename"
echo "DEVICE=${ifname##*/}" > ${hwcfg}/hwcfg-disk-id-${filename}
done
fi
done

91
getsysinfo Executable file
View File

@ -0,0 +1,91 @@
#! /bin/sh
if [ "$1" ] ; then
cat <<EOF
Usage: getsysinfo
Collect some system data that are useful for debugging
hardware detection bugs.
EOF
exit 0
fi
# collect some system data
dir=`mktemp -d /tmp/getsysinfo.XXXXXXXXXX`
[ -d "$dir" ] || exit 1
host=`hostname`
[ "$host" ] || host=xxx
mkdir -p "$dir/$host"
for i in \
/proc/asound \
/proc/bus/input \
/proc/cpuinfo \
/proc/device-tree \
/proc/devices \
/proc/dma \
/proc/driver/nvram \
/proc/fb \
/proc/filesystems \
/proc/iSeries \
/proc/ide \
/proc/interrupts \
/proc/iomem \
/proc/ioports \
/proc/meminfo \
/proc/modules \
/proc/net/dev \
/proc/partitions \
/proc/scsi \
/proc/sys/dev/cdrom/info \
/proc/sys/dev/parport \
/proc/tty \
/proc/version \
/sys \
/var/log/boot.msg \
/var/lib/hardware/udi
do
if [ -e "$i" ] ; then
echo "$i"
cp -x -a --parents "$i" "$dir/$host" 2>/dev/null
chmod -R u+w,a+r,a+X "$dir/$host"
for i in `( cd "$dir/$host" ; find proc -type f -size 0 )` ; do
cat "/$i" >"$dir/$host/$i"
done
fi
done
echo /proc/mounts
perl -nl -e 'print unless (split)[0] =~ /none|automount|:/' /proc/mounts >"$dir/$host/proc/mounts"
mkdir -p "$dir/$host/var/log"
echo "
------ dmesg start ------
" >>"$dir/$host/var/log/boot.msg"
dmesg >>"$dir/$host/var/log/boot.msg"
if [ -x /usr/bin/lshal ] ; then
echo lshal
lshal >$dir/$host/lshal.txt 2>/dev/null
fi
file="$host.tar.gz"
tar -C "$dir" -zcf "$dir/$file" "$host"
rm -f "/tmp/$file"
if [ -e "/tmp/$file" ] ; then
echo "Warning: /tmp/$file exists, no info written"\!
rm -rf "$dir"
exit 1
fi
ln -nf "$dir/$file" "/tmp/$file"
rm -rf "$dir"
echo "
System data written to: /tmp/$file"

984
git2log Executable file
View File

@ -0,0 +1,984 @@
#! /usr/bin/perl
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#
# This script is maintained at https://github.com/openSUSE/linuxrc-devtools
#
# If you're in another project, this is just a copy.
# You may update it to the latest version from time to time...
#
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
use strict;
use Getopt::Long;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Indent = 1;
sub usage;
sub changelog_outdated;
sub get_github_project;
sub get_version;
sub get_tags;
sub get_log;
sub is_formatted_tag;
sub get_branch;
sub choose_tags;
sub add_head_tag;
sub tags_to_str;
sub format_log;
sub format_all_logs;
sub fix_dates;
sub add_line_breaks;
sub format_date_obs;
sub format_date_iso;
sub raw_date_to_s;
usage 0 if !@ARGV;
my @changelog_deps = qw ( .git/HEAD .git/refs/heads .git/refs/tags );
my $branch;
my $current_version;
my @tags;
my @all_tags;
my $config;
my $opt_log;
my $opt_version;
my $opt_branch;
my $opt_update;
my $opt_file;
my $opt_start;
my $opt_max;
my $opt_width = 66;
my $opt_width_fuzz = 8;
my $opt_sep_width = 68;
my $opt_format = 'internal'; # obs, internal
my $opt_merge_msg_before = 1; # log auto generated pr merge message before the commit messages (vs. after)
my $opt_join_author = 1; # join consecutive commit messages as long as they are by the same author
my $opt_keep_date = 1; # don't join consecutive commit messages if they have different time stamps
my $opt_default_email = 'opensuse-packaging@opensuse.org'; # default email to use in changelog
GetOptions(
'help' => sub { usage 0 },
'version' => \$opt_version,
'branch' => \$opt_branch,
'update' => \$opt_update,
'start=s' => \$opt_start,
'format=s' => \$opt_format,
'max=i' => \$opt_max,
'width=i' => \$opt_width,
'fuzz=i' => \$opt_width_fuzz,
'merge-msg=s' => sub { $opt_merge_msg_before = ($_[1] eq 'after' ? 0 : 1) },
'join-author!' => \$opt_join_author,
'keep-date!' => \$opt_keep_date,
'log|changelog' => \$opt_log,
'default-email=s' => \$opt_default_email,
) || usage 1;
# ensure we are used correctly
usage 1 if @ARGV > 1 || !($opt_log || $opt_version || $opt_branch);
$opt_file = @ARGV ? shift : '-';
die "no git repo\n" unless -d ".git";
# if update option has been give write changelog only if git refs are newer
exit 0 if $opt_update && $opt_file ne '-' && -f($opt_file) && !changelog_outdated($opt_file);
$opt_max = 2 if $opt_version || $opt_branch;
# gather some data
get_github_project;
get_branch;
get_log;
fix_dates;
get_tags;
choose_tags;
add_head_tag;
get_version;
# just print current branch
if($opt_branch) {
open my $f, ">$opt_file";
print $f $config->{branch} ? $config->{branch} : "master", "\n";
close $f;
exit 0;
}
# just print current version
if($opt_version) {
my $old_version;
if($opt_file ne '-' && open(my $f, $opt_file)) {
chomp($old_version = <$f>);
close $f;
}
if($config->{version} ne $old_version) {
open my $f, ">$opt_file";
print $f "$config->{version}\n";
close $f;
}
exit 0;
}
# set start tag
if($opt_start) {
my $x = is_formatted_tag $opt_start;
die "$opt_start: not a valid start tag\n" if !$x;
$x->{branch} = $config->{branch} if !$x->{branch};
$config->{start} = $x;
}
format_all_logs;
open my $f, ">$opt_file";
print $f $_->{formatted} for @{$config->{log}};
close $f;
exit 0;
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# usage(exit_code)
#
# Print help message and exit.
# - exit_code: exit code
#
# Function does not return.
#
sub usage
{
my $err = shift;
print <<" usage";
Usage: git2log [OPTIONS] [FILE]
Create changelog and project version from git repo.
--changelog Write changelog to FILE.
--version Write version number to FILE.
--branch Write current branch to FILE.
--start START_TAG Start with tag START_TAG.
--max N Write at most MAX long entries.
--update Write changelog or version only if FILE is outdated.
--format FORMAT Write log using FORMAT. Supported FORMATs are 'internal' (default) and 'obs'.
--width WIDTH Reformat log entries to be max WIDTH chars wide.
--fuzz FUZZ Allow log lines to be up to FUZZ chars longer as WIDTH to avoid
line breaks leaving tiny bits on the last line.
--merge-msg WHERE Log message about merges before or after the actual merge commit messages.
Valid values for WHERE are 'after' and 'before' (default).
--join-author Join consecutive commits as long as they are by the same author. (default)
--no-join-author Keep consecutive commits by the same author separate.
--keep-date Join consecutive commits only if they have the same date. (default)
--no-keep-date Join consecutive commits even if dates differ.
--default-email Use this email in changelog entries if no other suitable email could be
determined (default: opensuse-packaging\@opensuse.org).
--help Print this help text.
usage
exit $err;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# res = changelog_outdated(file)
#
# Return status of changelog file.
# - file: changelog file name
# - res: status
# 1: file is newer than the last git repo change and should be updated
# 0: file is still recent enough
#
# Relies on global var @changelog_deps.
#
sub changelog_outdated
{
my $file = $_[0];
my $changelog_time = (stat $file)[9];
return 1 if !defined $changelog_time;
for (@changelog_deps) {
return 1 if (stat)[9] > $changelog_time;
}
return 0;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_github_project()
#
# Set $config->{github_project} to the github project name.
#
sub get_github_project
{
if(`git config remote.origin.url` =~ m#github.com[:/]+(\S+/\S+)#) {
$config->{github_project} = $1;
$config->{github_project} =~ s/\.git$//;
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_version()
#
# Set $config->{branch} and $config->{version} to the current branch and
# version info.
#
# This might be taken directly from HEAD if HEAD is tagged or otherwise be
# exprapolated from the most recent tag (cf. add_head_tag()).
#
sub get_version
{
$config->{version} = "0.0";
my $tag = $config->{log}[0]{tags}[0];
if($tag->{version}) {
$config->{version} = $tag->{version};
$config->{branch} = $tag->{branch};
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_tags()
#
# Parse $config->{raw_log}, extract tag names, and split into per-tag
# sections.
#
# Only tags recognized by is_formatted_tag() are considered.
#
# Tags inside merge commits are ignored.
#
# The parsed logs is stored in $config->{log}, an array of log sections.
# Each section is a hash with these keys:
# - 'tags': array of tags for this section
# - 'commit': git commit id associated with these tags
# - 'lines': git log lines
#
sub get_tags
{
my $log_entry;
# the end of the merge commit if in a merge
my $merge;
for (@{$config->{raw_log}}) {
if(/^commit (\S+)( \((.*)\))?/) {
my $commit = $1;
my $tag_list = $3;
my $xtag;
# we have reached the end of the merge commit
undef $merge if $merge && $commit =~ /^$merge/;
# ignore tag info inside a merge commit
$tag_list = "" if $merge;
for my $t (split /, /, $tag_list) {
if($t =~ /tag: (\S+)/) {
my $tag = $1;
my $x = is_formatted_tag $tag;
push @$xtag, $x if $x;
}
}
if($xtag) {
if($log_entry) {
push @{$config->{log}}, $log_entry;
last if $opt_max && @{$config->{log}} >= $opt_max;
}
$log_entry = { commit => $commit, tags => $xtag };
}
else {
$log_entry = { commit => $commit } if !$log_entry;
}
}
elsif(!$merge && /^Merge: (\S+)/) {
# remember end of merge
$merge = $1;
}
push @{$log_entry->{lines}}, $_ if $log_entry;
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_log()
#
# Read git log and store lines as array in $config->{raw_log} (trailing
# newlines removed).
#
sub get_log
{
chomp(@{$config->{raw_log}} = `git log --pretty=medium --date=raw --topo-order --decorate`);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# hash_ref = is_formatted_tag(tag_name)
#
# Parse tag and return hash ref with branch and version number parts or
# undef if it doesn't match.
# - tag_name: tag as string
# - hash_ref: hash ref with internal tag representation (with keys 'branch' and 'version').
#
# This expects tags of the form "VERSION" or "BRANCH-VERSION" where VERSION
# consists of decimal numbers separated by dots '.' and BRANCH can be any
# string.
# (Note: it doesn't really have to be the name of an existing branch.)
#
# Tags not conforming to this convention are ignored.
#
sub is_formatted_tag
{
if($_[0] =~ /^((.+)-)?((\d+\.)*\d+)$/) {
return { branch => $2, version => $3 }
}
return undef;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# get_branch()
#
# Get currently active git branch and store in $config->{branch}.
#
# 'master' branch is represented by empty 'branch' key.
#
sub get_branch
{
chomp(my $branch = `git rev-parse --abbrev-ref HEAD`);
$branch = "" if $branch eq 'master';
$config->{branch} = $branch;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# res = tag_sort(a, b)
#
# Compare 2 tags.
# - a, b: refs to tag hash
# - res: -1, 0, 1
#
# This is used when we have to decide between alternative tags.
# (Prefer 'lesser' variant.)
#
sub tag_sort
{
my ($x, $y);
$x = length $a->{version};
$y = length $b->{version};
# longer version number first
return $y <=> $x if $y <=> $x;
return $a->{branch} cmp $b->{branch};
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# str = tag_to_str(tag_ref)
#
# Convert tag into string.
# - tag_ref: ref to hash with 'branch' and 'version' keys
# - str: string (e.g. "foo-1.44")
#
# 'master' branch is represented by missing/empty 'branch' key.
#
sub tag_to_str
{
my $tag = $_[0];
my $str;
$str = "$tag->{branch}-" if $tag->{branch} ne "";
$str .= $tag->{version};
return $str;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# str = tags_to_str(tag_array_ref)
#
# Convert array of tags into string.
# - tag_array_ref: ref to array of tags
# - str: string (e.g. "(tag1, tag2)"
#
# This function is used only internally for debugging.
#
sub tags_to_str
{
my $tags = $_[0];
my $str;
for my $t (@$tags) {
$str .= ", " if $str;
$str .= tag_to_str $t;
}
return "($str)";
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# choose_tags()
#
# Scan commit messages and extract tag & branch information.
#
# This stores the tag/branch info in $config->{log}[]{tags}.
#
sub choose_tags
{
my $branch = $config->{branch};
for my $x (@{$config->{log}}) {
# printf "# %s\n", tags_to_str($x->{tags});
# no tag info? -> ignore
next if !$x->{tags};
# single tag? -> remember branch info
if(@{$x->{tags}} == 1) {
$branch = $x->{tags}[0]{branch};
next;
}
# several tags? -> choose one
# any with current branch name?
my @t = grep { $_->{branch} eq $branch } @{$x->{tags}};
# no? -> choose among all
@t = @{$x->{tags}} if @t == 0;
# prefer longest version number, then alphanumerically smallest branch name
@t = sort tag_sort @t;
$branch = $t[0]{branch};
$x->{tags} = [ $t[0] ];
# printf "X %s\n", tags_to_str($x->{tags});
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# add_head_tag()
#
# Suggest tag for HEAD if there isn't one.
#
# Basically, use branch + version from most recent tag and increment version.
#
sub add_head_tag
{
return if @{$config->{log}} < 2;
# HEAD tagged already?
return if $config->{log}[0]{tags};
# the first tagged commit if HEAD isn't tagged
my $tag = { %{$config->{log}[1]{tags}[0]} };
# increment version
$tag->{version} =~ s/(\d+)$/$1 + 1/e;
$config->{log}[0]{tags}[0] = $tag;
# remember that the tag was generated
$config->{log}[0]{was_untagged} = 1;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# fix_dates()
#
# Adjust time stamps in entire git log.
#
# The time stamps of the git commits are not necessarily ordered by date.
# But the generated changelog is required to have a strictly monotonic time.
#
# We do this by going through the log in reverse and rewriting any dates we
# find whenever the date decreases.
#
# A minimum time difference of 1 second beween entries is maintained.
#
# Not very subtle but it works.
#
sub fix_dates
{
my $last_date;
for (reverse @{$config->{raw_log}}) {
# e.g. "Date: 1443184889 +0200"
if(/^(Date:\s+)(\S+)(\s+\S+)/) {
if(defined $last_date && $2 < $last_date) {
$_ = "$1$last_date$3\n";
}
else {
$last_date = $2;
}
# ensure a minimal time gap of 1 second
$last_date += 1;
}
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# format_all_logs()
#
# Format the entire git log.
#
# This is done for every code version individually (the log has already been
# split accordingly).
#
# If $config->{start} is set, use this as starting point. Else format the
# entire git log.
#
sub format_all_logs
{
# check if start tag actually exists - if not, print nothing
if($config->{start}) {
my $tag_found;
for (@{$config->{log}}) {
$tag_found = 1, last if grep { tag_to_str($config->{start}) eq tag_to_str($_) } @{$_->{tags}};
}
return if !$tag_found;
}
for (@{$config->{log}}) {
if($config->{start}) {
# stop if we meet the start tag
last if grep { tag_to_str($config->{start}) eq tag_to_str($_) } @{$_->{tags}};
}
format_log $_;
}
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# format_log(log)
#
# Format log messages.
# - log: is an array ref with individual commits
#
# All commits belong to a specific code version (stored in $log->{tag}).
# $log->{formatted} holds the result.
#
# The process is done in several individual steps, documented below in the code.
#
sub format_log
{
my $log = $_[0];
my $merge;
my $commit;
my $commits;
for (@{$log->{lines}}) {
if(/^commit (\S+)/) {
$commit = { ref => $1 };
push @{$commits}, $commit;
if(
$merge &&
$merge->{merge_end} eq substr($commit->{ref}, 0, length($merge->{merge_end}))
) {
undef $merge;
}
if($merge) {
$commit->{merge_ref} = $merge->{ref};
$commit->{date} = $merge->{date};
$commit->{author} = $merge->{author};
# add to all commits so it's not lost when we re-arrange
$commit->{merge_msg} = $merge->{msg};
}
next;
}
if(/^Merge: (\S+)/ && !$merge) {
if($commit) {
$merge = { merge_end => $1, ref => $commit->{ref} } unless $merge;
}
next;
}
if(/^Date:\s+(\S.*)/) {
$commit->{date} ||= $1 if $commit;
$merge->{date} ||= $1 if $merge;
next;
}
if(/^Author:\s+(\S.*)/) {
$commit->{author} ||= $1 if $commit;
$merge->{author} ||= $1 if $merge;
next;
}
if($merge) {
if(/^ Merge pull request (#\d+) from (\S+)/) {
if($config->{github_project}) {
push @{$merge->{msg}}, "merge gh#$config->{github_project}$1";
}
else {
push @{$merge->{msg}}, "merge pr $2";
}
}
elsif(/^ Merge branch '([^']+)'( into)?/) {
push @{$merge->{msg}}, "merge branch $1" if $2 eq "";
}
elsif(/^ Merge remote-tracking branch /) {
# ignore
}
elsif(s/^ //) {
push @{$commit->{lines}}, $_ unless /^# /;
}
}
elsif($commit) {
if(s/^ //) {
push @{$commit->{lines}}, $_ unless /^# /;
}
}
}
# Note: the individual steps below work on the array @$commits and modify
# its content.
# step 1
# - if there are paragraphs starting with '@log@' or '@+log@'
# - delete first paragraph (short summary)
# - else
# - keep only first paragraph
# - if there is a paragraph starting with '@-log', delete entire log
# - tag commits that have a '@log@' tag so we can delete untagged commits
# belonging to the same merge commit later
my $tagged_merges = {};
for my $commit (@$commits) {
# skip leading empty lines
for (@{$commit->{lines}}) {
last if !/^\s*$/;
shift @{$commit->{lines}};
}
my $para_cnt = 0;
my $delete_all = 0;
my $delete_first = 0;
for (@{$commit->{lines}}) {
$para_cnt++ if $_ eq "";
$para_cnt = 0, $delete_first = 1 if /^\@\+log\@/;
$delete_all = 1 if /^\@\-log\@/;
if(/^\@log\@/) {
$para_cnt = 0;
$commit->{clear} = 1;
$tagged_merges->{$commit->{merge_ref}} = 1 if $commit->{merge_ref} || $log->{was_untagged};
}
$_ = undef if $para_cnt;
}
shift @{$commit->{lines}} if $delete_first;
$commit->{lines} = [] if $delete_all;
}
# step 2
# - clean up tagged commits or commits belonging to tagged merges
for my $commit (@$commits) {
next unless $commit->{clear} || $tagged_merges->{$commit->{merge_ref}};
for (@{$commit->{lines}}) {
last if /^\@\+?log\@/;
$_ = undef;
}
}
# step 3
# - join lines
for my $commit (@$commits) {
my $lines;
my $line;
for (@{$commit->{lines}}) {
next if $_ eq "";
if(
s/^\s*[+\-][\-\s]*// ||
s/^\@\+?log\@// ||
$line eq ""
) {
s/^\s*//;
push @$lines, $line if $line ne "";
$line = $_;
}
else {
s/^\s*//;
$line .= " " if $line ne "";
$line .= $_;
}
}
push @$lines, $line if $line ne "";
$commit->{formatted} = $lines if $lines;
}
# step 4
# - fix small glitches
for my $commit (@$commits) {
next unless $commit->{formatted};
for (@{$commit->{formatted}}) {
s/(fate|bnc|bsc|boo)\s*(#\d+)/\L$1\E$2/ig;
}
}
# step 5
# - add merge info at the top or bottom (depending on $opt_merge_msg_before)
my $merge_logged;
for my $commit ($opt_merge_msg_before ? reverse(@$commits) : @$commits) {
next unless $commit->{formatted};
if($commit->{merge_ref} && !$merge_logged->{$commit->{merge_ref}}) {
$merge_logged->{$commit->{merge_ref}} = 1;
if($commit->{merge_msg}) {
if($opt_merge_msg_before) {
unshift @{$commit->{formatted}}, @{$commit->{merge_msg}};
}
else {
push @{$commit->{formatted}}, @{$commit->{merge_msg}};
}
}
}
}
# step 6
# - join commit messages with same author (optionally even with different dates)
my $commit0;
for my $commit (@$commits) {
next if !$commit->{formatted};
$commit0 = $commit, next if !$commit0;
if(
# $commit->{merge_ref} eq $commit0->{merge_ref} &&
(
$opt_join_author && ($commit->{author} eq $commit0->{author})
&& (!$opt_keep_date || $commit->{date} eq $commit0->{date})
)
|| $opt_format eq 'internal'
) {
unshift @{$commit0->{formatted}}, @{$commit->{formatted}};
delete $commit->{formatted};
}
else {
$commit0 = $commit;
}
}
# step 7
# - add version tag at the end of the first log entry
for my $commit (@$commits) {
next unless $commit->{formatted};
if($opt_format eq 'obs') {
push @{$commit->{formatted}}, $log->{tags}[0]{version} if defined $log->{tags}[0]{version};
}
else {
# push @{$commit->{formatted}}, tag_to_str($log->{tags}[0]);
}
last;
}
# step 8
# - remove identical lines
for my $commit (@$commits) {
next unless $commit->{formatted};
my %k;
$commit->{formatted} = [ grep { !$k{$_}++ } @{$commit->{formatted}} ]
}
# step 9
# - remove shortened lines (that match the beginning of other lines)
for my $commit (@$commits) {
next unless $commit->{formatted};
# return 1 if some other commit line starts with function arg
my $is_substr = sub {
my $str = $_[0];
$str =~ s/\s*…$//; # github likes to shorten lines with ' …'
my $str_len = length $str;
for (@{$commit->{formatted}}) {
return 1 if $str_len < length($_) && $str eq substr($_, 0, $str_len);
}
return 0;
};
$commit->{formatted} = [ grep { ! $is_substr->($_) } @{$commit->{formatted}} ]
}
# step 10
# - add line breaks
for my $commit (@$commits) {
next unless $commit->{formatted};
for (@{$commit->{formatted}}) {
$_ = add_line_breaks $_;
}
}
# step 11
# - generate final log message
#
# note: non-(open)suse email addresses are replaced by $opt_default_email
my $formated_log;
for my $commit (@$commits) {
next unless $commit->{formatted} && @{$commit->{formatted}};
if($opt_format eq 'obs') {
$formated_log .= "-" x $opt_sep_width . "\n";
$formated_log .= format_date_obs($commit->{date});
}
else {
$formated_log .= format_date_iso($commit->{date});
}
if($opt_format eq 'obs') {
my $auth = $commit->{author};
$auth =~ s/^.*<//;
$auth =~ s/>.*$//;
# replace non-suse e-mail addresses with a generic one
if($auth !~ /\@(suse\.(com|cz|de)|opensuse\.org)$/) {
$auth = $opt_default_email;
}
$formated_log .= " - $auth\n\n";
}
else {
$formated_log .= ":\t" . tag_to_str($log->{tags}[0]) . "\n";
}
for (@{$commit->{formatted}}) {
s/^/\t/mg if $opt_format eq 'internal';
$formated_log .= "$_\n";
}
$formated_log .= "\n";
}
$log->{formatted} = $formated_log;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# new_text = add_line_breaks(text)
#
# Add line breaks to text.
# - text: some text
# - new_text: same text, reformatted
#
# Lines are formatted to have a maximal length of $opt_width. If this causes
# the last line to be shorter than $opt_width_fuzz, it is appended to the
# previous line.
#
sub add_line_breaks
{
my @words = split /\s+/, @_[0];
my $remaining_len = length(join '', @words);
my $str = shift(@words);
my $len = length $str;
my $next_len;
my $word_len;
for (@words) {
$word_len = length;
$remaining_len -= $word_len;
$next_len = $len + $word_len + 1;
if(
$next_len >= $opt_width &&
$next_len + $remaining_len + 1 >= $opt_width + $opt_width_fuzz
) {
$str .= "\n $_";
$len = $word_len;
}
else {
$str .= " $_";
$len += $word_len + 1;
}
}
return "- " . $str;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# seconds = raw_date_to_s(git_date)
#
# Convert git raw date to seconds.
# - git_date: raw git format (e.g. "1443184889 +0200")
# - seconds: the seconds part (e.g. "1443184889")
#
sub raw_date_to_s
{
return (split / /, $_[0])[0];
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# date = format_date_obs(git_date)
#
# Convert git raw date to obs format.
# - git_date: raw git format (e.g. "1443184889 +0200")
# - date: obs format ("Fri Sep 25 12:41:29 UTC 2015")
#
sub format_date_obs
{
my @d = gmtime(raw_date_to_s($_[0]));
return
qw(Sun Mon Tue Wed Thu Fri Sat)[$d[6]] . " " .
qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[$d[4]] . " " .
$d[3] . " " .
sprintf("%02d:%02d:%02d", $d[2], $d[1], $d[0]) . " UTC " .
(1900 + $d[5]);
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# date = format_date_iso(git_date)
#
# Convert git raw date to iso format.
# - git_date: raw git format (e.g. "1443184889 +0200")
# - date: obs format ("2015-09-25")
#
sub format_date_iso
{
my @d = gmtime(raw_date_to_s($_[0]));
return sprintf("%04d-%02d-%02d", 1900 + $d[5], $d[4] + 1, $d[3]);
}

15
hwbootscan Normal file
View File

@ -0,0 +1,15 @@
#!/bin/bash
rm -f /var/lib/hardware/LOCK
/sbin/hwscan --silent --boot --fast --isapnp --pci --block --floppy --mouse
#
# create icons for static drives
#
if [ -e /etc/hotplug/hotplug.subfs.functions ]; then
. /etc/hotplug/hotplug.subfs.functions
coldplug_create_subfs
fi
exit 0

101
hwbootscan.rc Normal file
View File

@ -0,0 +1,101 @@
#! /bin/sh
# Copyright (c) 2002 SuSE GmbH Nuernberg, Germany. All rights reserved.
#
# Author: Marcus Meissner <meissner@suse.de>
#
# /etc/init.d/hwscan
#
# System startup script for boot hw probing and reconfiguration
#
### BEGIN INIT INFO
# Provides: hwscan
# Required-Start:
# X-UnitedLinux-Should-Start: hotplug kbd
# Required-Stop:
# Default-Start: 2 3 5
# Default-Stop:
# Description: Hardware scan and reconfiguration on boot.
### END INIT INFO
HWBOOTSCAN_BIN=/usr/sbin/hwbootscan
test -x $HWBOOTSCAN_BIN || exit 5
# Shell functions sourced from /etc/rc.status:
# rc_check check and set local and overall rc status
# rc_status check and set local and overall rc status
# rc_status -v ditto but be verbose in local rc status
# rc_status -v -r ditto and clear the local rc status
# rc_failed set local and overall rc status to failed
# rc_reset clear local rc status (overall remains)
# rc_exit exit appropriate to overall rc status
. /etc/rc.status
# First reset status of this service
rc_reset
# Return values acc. to LSB for all commands but status:
# 0 - success
# 1 - misc error
# 2 - invalid or excess args
# 3 - unimplemented feature (e.g. reload)
# 4 - insufficient privilege
# 5 - program not installed
# 6 - program not configured
# 7 - program is not running
#
# Note that starting an already running service, stopping
# or restarting a not-running service as well as the restart
# with force-reload (in case signalling is not supported) are
# considered a success.
case "$1" in
start)
echo -n "Starting hardware scan on boot"
rc_splash "YaST"
[ -n "$REDIRECT" ] && exec 0<> $REDIRECT 1>&0 2>&0
$HWBOOTSCAN_BIN
rc_check
rc_status -v
;;
stop)
rc_check
;;
restart)
## Stop the service and regardless of whether it was
## running or not, start it again.
# Remember status and be quiet
rc_check
;;
force-reload)
## Signal the daemon to reload its config. Most daemons
## do this on signal 1 (SIGHUP).
## If it does not support it, restart.
# Remember status and be quiet
rc_check
;;
reload)
## Like force-reload, but if daemon does not support
## signalling, do nothing (!)
# If it supports signalling:
rc_reset
;;
status)
## Check status with checkproc(8), if process is running
## checkproc will return with exit status 0.
# Status has a slightly different for the status command:
# 0 - service running
# 1 - service dead, but /var/run/ pid file exists
# 2 - service dead, but /var/lock/ lock file exists
# 3 - service not running
rc_reset
;;
*)
echo "Usage: $0 {start|stop|restart|force-reload|reload|status}"
exit 1
;;
esac
rc_exit

1899
hwinfo.c Normal file

File diff suppressed because it is too large Load Diff

12
hwinfo.pc.in Normal file
View File

@ -0,0 +1,12 @@
# Package Information for pkg-config
prefix=/usr
exec_prefix=${prefix}
libdir=@LIBDIR@
includedir=${prefix}/include
Name: hwinfo
Description: Hardware Detection Library
Version: @VERSION@
Libs: -L${libdir} @LIBS@
Cflags: -I${includedir}

625
hwscan.c Normal file
View File

@ -0,0 +1,625 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include "hd.h"
#include "hd_int.h"
struct option options[] = {
{ "help", 0, NULL, 'h' },
{ "verbose", 0, NULL, 'v' },
{ "version", 0, NULL, 400 },
{ "show", 1, NULL, 500 },
{ "list", 0, NULL, 501 },
{ "cfg", 1, NULL, 502 },
{ "avail", 1, NULL, 503 },
{ "need", 1, NULL, 504 },
{ "new", 0, NULL, 505 },
{ "fast", 0, NULL, 506 },
{ "silent", 0, NULL, 507 },
{ "boot", 0, NULL, 508 },
{ "active", 1, NULL, 509 },
{ "only", 1, NULL, 510 },
{ "sys", 0, NULL, 1000 + hw_sys },
{ "cpu", 0, NULL, 1000 + hw_cpu },
{ "keyboard", 0, NULL, 1000 + hw_keyboard },
{ "braille", 0, NULL, 1000 + hw_braille },
{ "mouse", 0, NULL, 1000 + hw_mouse },
{ "joystick", 0, NULL, 1000 + hw_joystick },
{ "printer", 0, NULL, 1000 + hw_printer },
{ "scanner", 0, NULL, 1000 + hw_scanner },
{ "chipcard", 0, NULL, 1000 + hw_chipcard },
{ "monitor", 0, NULL, 1000 + hw_monitor },
{ "tv", 0, NULL, 1000 + hw_tv },
{ "gfxcard", 0, NULL, 1000 + hw_display },
{ "framebuffer", 0, NULL, 1000 + hw_framebuffer },
{ "camera", 0, NULL, 1000 + hw_camera },
{ "sound", 0, NULL, 1000 + hw_sound },
{ "storage-ctrl", 0, NULL, 1000 + hw_storage_ctrl },
{ "storage_ctrl", 0, NULL, 1000 + hw_storage_ctrl },
{ "netcard", 0, NULL, 1000 + hw_network_ctrl },
{ "network-ctrl", 0, NULL, 1000 + hw_network_ctrl },
{ "network_ctrl", 0, NULL, 1000 + hw_network_ctrl },
{ "isdn", 0, NULL, 1000 + hw_isdn },
{ "modem", 0, NULL, 1000 + hw_modem },
{ "network", 0, NULL, 1000 + hw_network },
{ "disk", 0, NULL, 1000 + hw_disk },
{ "partition", 0, NULL, 1000 + hw_partition },
{ "cdrom", 0, NULL, 1000 + hw_cdrom },
{ "floppy", 0, NULL, 1000 + hw_floppy },
{ "update", 0, NULL, 1000 + hw_manual },
{ "usb-ctrl", 0, NULL, 1000 + hw_usb_ctrl },
{ "usb_ctrl", 0, NULL, 1000 + hw_usb_ctrl },
{ "usb", 0, NULL, 1000 + hw_usb },
{ "bios", 0, NULL, 1000 + hw_bios },
{ "pci", 0, NULL, 1000 + hw_pci },
{ "isapnp", 0, NULL, 1000 + hw_isapnp },
{ "bridge", 0, NULL, 1000 + hw_bridge },
{ "hub", 0, NULL, 1000 + hw_hub },
{ "scsi", 0, NULL, 1000 + hw_scsi },
{ "ide", 0, NULL, 1000 + hw_ide },
{ "memory", 0, NULL, 1000 + hw_memory },
{ "dvb", 0, NULL, 1000 + hw_dvb },
{ "pcmcia", 0, NULL, 1000 + hw_pcmcia },
{ "pcmcia_ctrl", 0, NULL, 1000 + hw_pcmcia_ctrl },
{ "ieee1394", 0, NULL, 1000 + hw_ieee1394 },
{ "firewire", 0, NULL, 1000 + hw_ieee1394 },
{ "ieee1394_ctrl", 0, NULL, 1000 + hw_ieee1394_ctrl },
{ "firewire_ctrl", 0, NULL, 1000 + hw_ieee1394_ctrl },
{ "hotplug", 0, NULL, 1000 + hw_hotplug },
{ "hotplug_ctrl", 0, NULL, 1000 + hw_hotplug_ctrl },
{ "zip", 0, NULL, 1000 + hw_zip },
{ "pppoe", 0, NULL, 1000 + hw_pppoe },
{ "dsl", 0, NULL, 1000 + hw_dsl },
{ "wlan", 0, NULL, 1000 + hw_wlan },
{ "block", 0, NULL, 1000 + hw_block },
{ "tape", 0, NULL, 1000 + hw_tape },
{ "vbe", 0, NULL, 1000 + hw_vbe },
{ "bluetooth", 0, NULL, 1000 + hw_bluetooth },
{ "all", 0, NULL, 1000 + hw_all },
{ }
};
int verbose = 0;
hd_hw_item_t scan_item[100] = { };
unsigned scan_items = 0;
int found_items = 0;
struct {
unsigned show:1;
unsigned scan:1;
unsigned list:1;
unsigned config_cfg:1;
unsigned config_avail:1;
unsigned config_need:1;
unsigned config_active:1;
unsigned new:1;
unsigned fast:1;
unsigned silent:1;
unsigned boot:1;
str_list_t *only;
} opt;
void help(void);
int do_scan(hd_hw_item_t *items);
int do_show(char *id);
int do_list(hd_hw_item_t *items);
int do_config(int type, char *val, char *id);
int fast_ok(hd_hw_item_t *items);
int has_item(hd_hw_item_t *items, hd_hw_item_t item);
int has_hw_class(hd_t *hd, hd_hw_item_t *items);
int main(int argc, char **argv)
{
int rc = 0;
#ifndef LIBHD_TINY
char *id = NULL;
char *config_cfg = NULL;
char *config_avail = NULL;
char *config_need = NULL;
char *config_active = NULL;
int i;
int ok = 0;
FILE *f;
opterr = 0;
while((i = getopt_long(argc, argv, "hv", options, NULL)) != -1) {
switch(i) {
case 'v':
verbose++;
break;
case 400:
printf("%s\n", hd_version());
ok = 1;
break;
case 500:
opt.show = 1;
id = optarg;
break;
case 501:
opt.list = 1;
break;
case 502:
opt.config_cfg = 1;
config_cfg = optarg;
break;
case 503:
opt.config_avail = 1;
config_avail = optarg;
break;
case 504:
opt.config_need = 1;
config_need = optarg;
break;
case 505:
opt.new = 1;
break;
case 506:
opt.fast = 1;
break;
case 507:
opt.silent = 1;
break;
case 508:
opt.boot = 1;
break;
case 509:
opt.config_active = 1;
config_active = optarg;
break;
case 510:
if(*optarg) add_str_list(&opt.only, optarg);
break;
case 1000 ... 1100:
opt.scan = 1;
if(scan_items + 1 < sizeof scan_item / sizeof *scan_item) {
scan_item[scan_items++] = i - 1000;
}
break;
default:
help();
return 1;
}
}
scan_item[scan_items] = 0;
if(opt.scan && !opt.list) {
if(argv[optind] || !scan_items) return help(), 1;
rc = do_scan(scan_item);
if(found_items) {
unlink(HARDWARE_DIR "/.update"); /* the old file */
unlink(HARDWARE_UNIQUE_KEYS "/.update"); /* so we trigger a rescan */
if((f = fopen(HARDWARE_UNIQUE_KEYS "/.update", "a"))) fclose(f);
}
ok = 1;
}
if(opt.show) {
do_show(id);
ok = 1;
}
if(opt.list) {
do_list(scan_item);
ok = 1;
}
if(opt.config_cfg) {
if(!argv[optind]) return help(), 1;
do_config(1, config_cfg, argv[optind]);
ok = 1;
}
if(opt.config_avail) {
if(!argv[optind]) return help(), 1;
do_config(2, config_avail, argv[optind]);
ok = 1;
}
if(opt.config_need) {
if(!argv[optind]) return help(), 1;
do_config(3, config_need, argv[optind]);
ok = 1;
}
if(opt.config_active) {
if(!argv[optind]) return help(), 1;
do_config(4, config_active, argv[optind]);
ok = 1;
}
if(!ok) help();
#endif /* !defined(LIBHD_TINY) */
return rc;
}
void help()
{
fprintf(stderr,
"Usage: hwscan [options]\n"
"Show information about currently known hardware.\n"
" --list show list of known hardware\n"
" --version show libhd version\n"
" --silent don't show hardware config changes\n"
" --boot run only if we haven't been disabled via 'hwprobe=-scan'\n"
" --cfg=state id change 'configured' status; id is one of the\n"
" ids from 'hwscan --list'\n"
" state is one of new, no, yes\n"
" --avail=state id change 'available' status\n"
" --need=state id change 'needed' status\n"
" --active=state id change 'active' status\n"
" --hw_item probe for hw_item and update status info\n"
" hw_item is one of:\n"
" all, bios, block, bluetooth, braille, bridge, camera, cdrom, chipcard, cpu,\n"
" disk, dsl, dvb, floppy, framebuffer, gfxcard, hub, ide, isapnp, isdn,\n"
" joystick, keyboard, memory, modem, monitor, mouse, netcard, network,\n"
" partition, pci, pcmcia, pcmcia-ctrl, pppoe, printer, scanner, scsi, smp,\n"
" sound, storage-ctrl, sys, tape, tv, usb, usb-ctrl, vbe, wlan, zip\n"
);
}
#ifndef LIBHD_TINY
int do_scan(hd_hw_item_t *items)
{
int run_config = 0;
hd_status_t status = { };
hd_data_t *hd_data;
hd_t *hd, *hd1;
int err = 0;
if(opt.fast) opt.fast = fast_ok(items);
hd_data = calloc(1, sizeof *hd_data);
if(opt.boot) {
/* look if we have been disabled */
hd_clear_probe_feature(hd_data, pr_all);
hd_scan(hd_data);
hd_set_probe_feature(hd_data, pr_scan);
if(!hd_probe_feature(hd_data, pr_scan)) {
hd_free_hd_data(hd_data);
free(hd_data);
return 0;
}
}
hd_data->only = opt.only;
opt.only = NULL;
hd_data->flags.list_all = 1;
hd_data->flags.fast = opt.fast;
hd = hd_list2(hd_data, items, 1);
if(hd) found_items = 1;
for(hd1 = hd; hd1; hd1 = hd1->next) {
err = hd_write_config(hd_data, hd1);
if(verbose >= 2) {
printf(
"write=%d %s: (cfg=%s, avail=%s, need=%s, active=%s",
err,
hd1->unique_id,
hd_status_value_name(hd1->status.configured),
hd_status_value_name(hd1->status.available),
hd_status_value_name(hd1->status.needed),
hd_status_value_name(hd1->status.active)
);
if(hd1->unix_dev_name) {
printf(", dev=%s", hd1->unix_dev_name);
}
printf(
") %s\n",
hd1->model
);
}
if(err) break;
}
if(err) {
fprintf(stderr,
"Error writing configuration for %s (%s)\n",
hd1->unique_id,
hd1->model
);
exit(1);
}
hd = hd_free_hd_list(hd);
if(opt.new) {
status.configured = status_new;
}
else {
status.reconfig = status_yes;
}
hd = hd_list_with_status2(hd_data, items, status);
if(hd) run_config = 1;
if(verbose) {
for(hd1 = hd; hd1; hd1 = hd1->next) {
printf(
"%s: (cfg=%s, avail=%s, need=%s, active=%s",
hd1->unique_id,
hd_status_value_name(hd1->status.configured),
hd_status_value_name(hd1->status.available),
hd_status_value_name(hd1->status.needed),
hd_status_value_name(hd1->status.active)
);
if(hd1->unix_dev_name) {
printf(", dev=%s", hd1->unix_dev_name);
}
printf(
") %s\n",
hd1->model
);
}
}
else if(!opt.silent) {
for(hd1 = hd; hd1; hd1 = hd1->next) printf("%s\n", hd1->unique_id);
}
hd = hd_free_hd_list(hd);
hd_free_hd_data(hd_data);
free(hd_data);
return run_config ^ 1;
}
int do_show(char *id)
{
hd_data_t *hd_data;
hd_t *hd;
hd_data = calloc(1, sizeof *hd_data);
if ( id[0] == '/' ){
int nr=0;
char *_id = 0;
hd_t *hd_manual;
hd_manual = hd_list(hd_data, hw_manual, 1, NULL);
for(hd = hd_manual; hd; hd = hd->next) {
if(hd->status.available != status_yes) continue;
if(!search_str_list(hd->unix_dev_names, id)) continue;
_id = hd->unique_id;
nr++;
}
if ( nr == 1 ) /* > 1 means our database is not okay */
hd = hd_read_config(hd_data, _id);
}else
hd = hd_read_config(hd_data, id);
if(hd) {
hd_data->debug = -1;
hd_dump_entry(hd_data, hd, stdout);
hd = hd_free_hd_list(hd);
}
else {
printf("no such hardware item: %s\n", id);
}
hd_free_hd_data(hd_data);
free(hd_data);
return 0;
}
int do_list(hd_hw_item_t *items)
{
hd_data_t *hd_data;
hd_t *hd, *hd_manual;
char *s;
char status[64];
int i;
hd_data = calloc(1, sizeof *hd_data);
hd_manual = hd_list(hd_data, hw_manual, 1, NULL);
for(hd = hd_manual; hd; hd = hd->next) {
if(opt.scan && ! has_hw_class(hd, items)) continue;
strcpy(status, "(");
i = 0;
if(hd->status.configured && (s = hd_status_value_name(hd->status.configured))) {
sprintf(status + strlen(status), "%scfg=%s", i ? ", " : "", s);
i++;
}
if(hd->status.available && (s = hd_status_value_name(hd->status.available))) {
sprintf(status + strlen(status), "%savail=%s", i ? ", " : "", s);
i++;
}
if(hd->status.needed && (s = hd_status_value_name(hd->status.needed))) {
sprintf(status + strlen(status), "%sneed=%s", i ? ", " : "", s);
i++;
}
if(hd->status.active && (s = hd_status_value_name(hd->status.active))) {
sprintf(status + strlen(status), "%sactive=%s", i ? ", " : "", s);
i++;
}
strcat(status, ")");
s = hd_hw_item_name(hd->hw_class);
if(!s) s = "???";
printf("%s: %-32s %-16s %s\n", hd->unique_id, status, s, hd->model);
if(hd->config_string) {
printf(" configured as: \"%s\"\n", hd->config_string);
}
}
hd_free_hd_list(hd_manual);
hd_free_hd_data(hd_data);
free(hd_data);
return 0;
}
int do_config(int type, char *val, char *id)
{
hd_data_t *hd_data;
hd_t *hd;
hd_status_value_t status = 0;
int i;
char *s;
hd_data = calloc(1, sizeof *hd_data);
if ( id[0] == '/' ){
int nr=0;
char *_id = 0;
hd_t *hd_manual;
hd_manual = hd_list(hd_data, hw_manual, 1, NULL);
for(hd = hd_manual; hd; hd = hd->next) {
if(hd->status.available != status_yes) continue;
if(!search_str_list(hd->unix_dev_names, id)) continue;
_id = hd->unique_id;
nr++;
}
if ( nr == 1 )
hd = hd_read_config(hd_data, _id);
}else
hd = hd_read_config(hd_data, id);
if(hd) {
for(i = 1; i < 8; i++) {
s = hd_status_value_name(i);
if(s && !strcmp(val, s)) {
status = i;
break;
}
}
if(!status) {
printf("invalid status: %s\n", val);
}
else {
switch(type) {
case 1:
hd->status.configured = status;
break;
case 2:
hd->status.available = status;
break;
case 3:
hd->status.needed = status;
break;
case 4:
hd->status.active = status;
break;
}
hd_write_config(hd_data, hd);
}
hd = hd_free_hd_list(hd);
}
else {
printf("no such hardware item: %s\n", id);
}
hd_free_hd_data(hd_data);
free(hd_data);
return 0;
}
/*
* Check whether a 'fast' scan would suffice to re-check the presence
* of all known hardware.
*/
int fast_ok(hd_hw_item_t *items)
{
hd_data_t *hd_data;
hd_t *hd, *hd1;
int ok = 1;
if(!has_item(items, hw_mouse) && !has_item(items, hw_storage_ctrl)) {
return 1;
}
hd_data = calloc(1, sizeof *hd_data);
hd_data->flags.list_all = 1;
hd = hd_list(hd_data, hw_manual, 1, NULL);
for(hd1 = hd; hd1; hd1 = hd1->next) {
/* serial mice */
if(hd1->hw_class == hw_mouse && hd1->bus.id == bus_serial) {
ok = 0;
break;
}
/* parallel zip */
if(hd1->hw_class == hw_storage_ctrl && hd1->bus.id == bus_parallel) {
ok = 0;
break;
}
}
hd_free_hd_data(hd_data);
free(hd_data);
return ok;
}
/* check if item is in items */
int has_item(hd_hw_item_t *items, hd_hw_item_t item)
{
while(*items) if(*items++ == item) return 1;
return 0;
}
/* check if one of items is in hw_class */
int has_hw_class(hd_t *hd, hd_hw_item_t *items)
{
while(*items) if(hd_is_hw_class(hd, *items++)) return 1;
return 0;
}
#endif /* !defined(LIBHD_TINY) */

257
hwscand.c Normal file
View File

@ -0,0 +1,257 @@
/* hwscan front end
Copyright 2004 by SUSE (<adrian@suse.de>) */
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "init_message.h"
#define TIMEOUT 2
#define LONG_TIMEOUT 0
#define BUFFERS 1024
int main( int argc, char **argv )
{
int ret, i;
key_t key = KEY;
int msgid;
int mode = 0;
int dev_nr = 0;
int lines = 0;
int block, usb, firewire, pci;
int dev_last_state[BUFFERS];
int dev_counter[BUFFERS];
char * command_device[NR_COMMANDS][BUFFERS];
time_t command_device_last[NR_COMMANDS][BUFFERS];
time_t last;
char **commands;
char **devices;
char buffer[32];
message m;
// are we running already, maybe ?
{
do {
ssize_t r;
char b[1024];
char link[1024];
int fd = open( PID_FILE, O_RDONLY );
if ( fd >= 0 && (r=read(fd,b,1023)) > 0 ){
close(fd);
b[r]='\0';
snprintf(link, 1023, "/proc/%s/exe", b);
if ( (r=readlink( link, b, 1023 )) > 0 ){
b[r]='\0';
if ( r<8 )
unlink(PID_FILE);
else if ( strcmp("/hwscand", b+strlen(b)-8) )
unlink(PID_FILE);
else
exit(1);
}else
unlink(PID_FILE);
}else if ( fd >= 0 )
unlink(PID_FILE);
} while ( 0 > (ret = open( PID_FILE, O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR ) ) );
sprintf(buffer, "%d", getpid());
if ( ret < 0 || write(ret,buffer,strlen(buffer)) <= 0 ){
perror("hwscand: unable to write pid file "PID_FILE);
exit(1);
}
close(ret);
}
// initialize ...
for ( i=0; i<NR_COMMANDS; i++ ){
command_device[i][0] = 0;
command_device_last[i][0] = 1;
}
last=block=usb=firewire=pci=0;
commands = (char**) malloc( BUFFERS * sizeof(char*) );
devices = (char**) malloc( BUFFERS * sizeof(char*) );
msgid = msgget(key, IPC_CREAT | 0600);
if (msgid < 0) {
perror("msgget");
exit(1);
}
while (1) {
if ( last || dev_nr )
mode = IPC_NOWAIT;
else
mode = 0;
if( msgrcv(msgid, &m, MESSAGE_BUFFER, 1, mode) >= 0 ){
char *p = m.mtext;
if ( p == 0 ){
fprintf( stderr, "hwscand: error, zero sized message\n" );
}else{
if ( p[0] == 'S' && strlen(p) > 1 ){
// scan calls
char z[2];
int c;
z[0] = *(p+1);
z[1] = '\0';
c = atoi(z);
if ( c < NR_COMMANDS ){
if ( ! command_with_device[c] ){
last = time(0L);
if ( LONG_TIMEOUT+command_device_last[c][0] < time(0L) )
command_device_last[c][0] = 0;
}else
for ( i=0; i<BUFFERS; i++ ){
if ( !command_device[c][i] ){
last = time(0L);
command_device[c][i] = strdup(p+2);
command_device[c][i+1] = 0;
command_device_last[c][i] = 0;
break;
}else if ( !strcmp(command_device[c][i], p+2) ){
last = time(0L);
if ( LONG_TIMEOUT+command_device_last[c][i] < time(0L) )
command_device_last[c][i] = 0;
break;
}
}
}
}
if ( p[0] == 'C' && lines < BUFFERS ){
last = time(0L);
// config calls
commands[lines] = strdup(p+1);
lines++;
}
if ( p[0] == 'A' && dev_nr < BUFFERS ){
// add scan devices
devices[dev_nr] = strdup(p+1);
dev_last_state[dev_nr] = 0;
dev_counter[dev_nr] = 0;
dev_nr++;
}
if ( p[0] == 'R' && dev_nr < BUFFERS ){
for ( i=0; i<dev_nr; i++ ){
if ( !strcmp(p+1, devices[i]) ){
int j;
free(devices[i]);
for ( j=i; j+1<dev_nr; j++ ){
devices[j] = devices[j+1];
dev_last_state[j] = dev_last_state[j+1];
dev_counter[j] = dev_counter[j+1];
}
dev_nr--;
}
}
}
}
#if DEBUG
printf("CALL RECEIVED %s\n", p);
#endif
}else{
// we do this only in scanning mode ...
sleep(1);
for ( i=0; i<dev_nr; i++ ){
if (dev_counter[i]<0) continue;
dev_counter[i]++;
if ( dev_counter[i] > 5 ){
int fd;
char buf[MESSAGE_BUFFER];
dev_counter[i] = 0;
fd = open( devices[i], O_RDONLY );
strcpy( buf, "/sbin/hwscan --fast --partition --only=");
strcat( buf, devices[i] );
if ( fd < 0 ){
if ( dev_last_state[i] )
system(buf);
dev_last_state[i] = 0;
}else{
if ( dev_last_state[i] == 0)
system(buf);
dev_last_state[i] = 1;
close(fd);
}
}
}
}
if ( last && (last+TIMEOUT <= time(0L)) ){
char buf[MESSAGE_BUFFER * NR_COMMANDS];
int run_really = 0;
last=0;
strcpy( buf, "/sbin/hwscan --fast --boot --silent" );
for ( i=0; i<NR_COMMANDS; i++ ){
if ( command_with_device[i] == 0 &&
command_device_last[i][0] == 0 ){
command_device_last[i][0] = time(0L);
strcat( buf, " --");
strcat( buf, command_args[i] );
run_really = 1;
} else {
int j;
int commappended = 0;
for ( j=0; j<BUFFERS; j++ ){
if ( !command_device[i][j] )
break;
if ( command_device_last[i][j] == 0 ){
if (!commappended) {
strcat( buf, " --");
strcat( buf, command_args[i] );
commappended = 1;
}
strcat( buf, " --only=" );
strcat( buf, command_device[i][j] );
command_device_last[i][j] = time(0L);
run_really = 1;
if (strlen(buf) > sizeof(buf) - MESSAGE_BUFFER)
break;
}
}
}
if (strlen(buf) > sizeof(buf) - MESSAGE_BUFFER) {
last = time(0L); /* call me again */
break;
}
}
if ( run_really ){
#if DEBUG
printf("RUN %s\n", buf);
#endif
system(buf);
#if DEBUG
printf("RUN quit %s\n", buf);
#endif
}
if ( lines ){
for (i=0; i<lines; i++){
#if DEBUG
printf("CALL DIRECT %s\n", commands[i]);
#endif
system(commands[i]);
#if DEBUG
printf("CALL quit %s\n", commands[i]);
#endif
free(commands[i]);
}
lines=0;
}
}
}
return 0;
}

126
hwscanqueue.c Normal file
View File

@ -0,0 +1,126 @@
/* hwscan front end
Copyright 2004 by SUSE (<adrian@suse.de>) */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "init_message.h"
int main( int argc, char **argv )
{
int ret;
unsigned short i;
key_t key = KEY;
int msgid;
message m;
char *device = argv[2];
if ( argc < 2 ){
fprintf( stderr, "help: hwscanqueue hwscan-commands\n" );
fprintf( stderr, "help: commands:\n" );
for ( i=0; i<NR_COMMANDS; i++ ){
fprintf( stderr, " --%s", command_args[i] );
if ( command_with_device[i] )
fprintf( stderr, " device" );
fprintf( stderr, "\n");
}
fprintf( stderr, " --avail=yes/no id\n" );
fprintf( stderr, " --scan=device\n" );
fprintf( stderr, " --stop=device\n" );
exit(1);
}
if ( !strncmp("--cfg=", argv[1], 6) && argc>2 )
snprintf( m.mtext, MESSAGE_BUFFER, "C/sbin/hwscan %s %s", argv[1], argv[2] );
else if ( !strncmp("--avail=", argv[1], 8) && argc>2 )
snprintf( m.mtext, MESSAGE_BUFFER, "C/sbin/hwscan %s %s", argv[1], argv[2] );
else if ( !strncmp("--scan=", argv[1], 7) )
snprintf( m.mtext, MESSAGE_BUFFER, "A%s", argv[1]+7 );
else if ( !strncmp("--stop=", argv[1], 7) )
snprintf( m.mtext, MESSAGE_BUFFER, "R%s", argv[1]+7 );
else if ( !strncmp("--", argv[1], 2) ){
for ( i=0; i<NR_COMMANDS; i++ ){
if ( !strcmp(argv[1]+2,command_args[i]) ){
#if DEBUG
printf("COMMAND %s\n", command_args[i] );
#endif
snprintf( m.mtext, MESSAGE_BUFFER, "S%d", i );
if (command_with_device[i]){
if ( !device ){
fprintf(stderr, "need a device for this command\n");
exit(1);
}
strncat( m.mtext, device, MESSAGE_BUFFER-3 );
}
break;
}
}
if ( i>=NR_COMMANDS ){
fprintf(stderr, "unknown command\n");
exit(1);
}
}else
exit(1);
if ( (msgid = msgget(key, IPC_CREAT | 0600)) < 0 ){
perror("unable to init.");
exit(1);
}
m.mtype = 1;
ret = msgsnd( msgid, &m, MESSAGE_BUFFER, IPC_NOWAIT);
#if DEBUG
printf("SEND %s, return %d\n", m.mtext, ret );
#endif
if ( ret < 0 )
perror("message send failed");
else{
// success ... start hwscand, if it is not yet running
ssize_t r;
char buffer[1024];
char link[1024];
int fd = open( PID_FILE, O_RDONLY );
if ( fd >= 0 && (r=read(fd,buffer,1023)) > 0 ){
close(fd);
buffer[r]='\0';
snprintf(link, 1023, "/proc/%s/exe", buffer);
if ( (r=readlink( link, buffer, 1023 )) > 0 ){
buffer[r]='\0';
if ( r<8 )
fd=-1;
else if ( strcmp("/hwscand", buffer+strlen(buffer)-8) )
fd=-1;
}else
fd=-1;
}else
fd=-1;
if ( fd < 0 ){
pid_t pid;
signal(SIGCHLD,SIG_IGN);
pid=fork();
if (pid==0){
/* Change directory to allow clean shut-down */
chdir("/");
/* Close std fds */
close(0);
close(1);
close(2);
/* Start hwscand */
execve("/sbin/hwscand", 0, 0);
}
}
}
exit(ret);
}

17
init_message.h Normal file
View File

@ -0,0 +1,17 @@
#define MESSAGE_BUFFER 1024
#define KEY 8024;
#define PID_FILE "/var/run/hwscand.pid"
// WARNING NEEDS TO BE <= 9
#define NR_COMMANDS 7
// WARNING NEEDS TO BE <= 9
static const char *command_args[] = { "block", "partition", "usb", "firewire", "pci", "pcmcia", "bluetooth" };
static const int command_with_device[] = { 1, 1, 0, 0, 0, 0, 0 };
typedef struct msgbuf {
long mtype;
char mtext[MESSAGE_BUFFER+1];
} message;
#define DEBUG 0

113
scripts/mci Executable file
View File

@ -0,0 +1,113 @@
#! /usr/bin/perl
sub addr2line;
$list = shift;
$bin = shift;
$ofs = shift;
die "usage: mci data_file binary\n" unless -f($list);
open F, $list;
while(<F>) {
if(/^;\s*(.+?)\s*$/) {
@i = split ' ', $1;
$i[0] = sprintf "%-24s", $i[0];
$i[1] = addr2line $i[1];
print "; ", join("\t", @i), "\n";
next
}
@i = split;
die "oops, format error" if @i > 3;
if(@i == 1) {
if($i[0] =~ /^>(\S+)/) {
unshift @funcs, $1;
}
elsif($i[0] =~ /<(\S+)/) {
if($funcs[0] eq $1) {
shift @funcs
}
else {
die "oops, strange data (line $.)\n"
}
}
else {
die "oops, format error"
}
}
else {
$func = $i[0];
$addr = $i[1];
$size = undef;
$size = @i == 2 ? undef : $i[2];
if(defined $size) {
if(exists $mem{$addr}) {
$x = addr2line $func;
$y = addr2line ${$mem{$addr}}[1];
print "malloc oops (line $.): mem $addr; old: $y, size ${$mem{$addr}}[0]; new: $x, size $size\n";
}
$mem{$addr} = [ $size, $func, @funcs ];
delete $lfree{$addr};
}
else {
if(!exists $mem{$addr}) {
$xx = "";
$first = 1;
for $f ($func, @funcs) {
$xx .= "<-" unless $first;
$first = 0;
$xx .= addr2line $f;
}
print "free oops (line $.): $addr ($xx) [last free: line $lfree{$addr}]\n";
}
delete $mem{$addr};
$lfree{$addr} .= " $.";
}
}
}
for (sort keys %mem) {
$total += oct(${$mem{$_}}[0]);
$cnt++;
# $x = `addr2line -s -e $bin ${$mem{$_}}[1]`;
# chomp $x;
# $x = $x =~ /\?{2}/ ? undef : "$x ";
$x = addr2line ${$mem{$_}}[1];
print "$_\t${$mem{$_}}[0]\t";
$first = 1;
for $f (@{$mem{$_}}[1..$#{$mem{$_}}]) {
print "<-" unless $first;
$first = 0;
print addr2line $f;
}
print "\n"
}
printf "total: %u bytes in %u blocks\n", $total, $cnt;
sub addr2line
{
my ($x, $y);
return $_[0] unless $bin;
$y = sprintf "0x%x", oct($_[0]) + $ofs;
return $addr_cache{$y} if exists $addr_cache{$y};
$x = `addr2line -s -e $bin $y`;
chomp $x;
$x = $x =~ /\?{2}/ ? $_[0] : $x;
$addr_cache{$y} = $x;
return $x;
}

46
specifications.md Normal file
View File

@ -0,0 +1,46 @@
# Links to external technical documentaion
## System Management BIOS (SMBIOS)
https://www.dmtf.org/standards/smbios
## VESA BIOS Extensions (VBE)
http://www.petesqbsite.com/sections/tutorials/tuts/vbe3.pdf
## PCI specification
https://pcisig.com/specifications
### device ids
- http://pci-ids.ucw.cz
## SDIO specification
https://www.sdcard.org/developers/overview/sdio/
### device ids
- https://github.com/systemd/systemd/blob/master/hwdb/sdio.ids
- https://wikidevi.com/wiki/Talk:Linux_Wi-Fi_device_entries
## USB specification
http://www.usb.org/developers/docs/
### device ids
- http://www.linux-usb.org/usb.ids
## SCSI specification
### SCSI commands
Google for 'SCSI Primary Commands 5 (SPC-5)' (or choose another version).
The latest draft is usually freely available but not directly downloadable.

2
src/.cvsignore Normal file
View File

@ -0,0 +1,2 @@
.lib
*.so*

3
src/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.lib
libhd.a
libhd.so*

32
src/Makefile Normal file
View File

@ -0,0 +1,32 @@
TOPDIR = ..
SUBDIRS = hd isdn ids
TARGETS = touch $(LIBHD) $(LIBHD_SO)
CLEANFILES = $(LIBHD) $(LIBHD_D) $(LIBHD_SO) *.so *.so.* *.a
.PHONY: touch
include $(TOPDIR)/Makefile.common
#ifeq "$(findstring $(ARCH), i386 alpha)" ""
#SUBDIRS := $(filter-out pnpdump, $(SUBDIRS))
#endif
$(LIBHD): $(OBJS)
ar r $@ $?
@rm -f $(LIBHD_D)
ifdef SHARED_FLAGS
$(LIBHD_SO): $(LIBHD)
$(CC) -shared $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -Wl,--whole-archive $(LIBHD) -Wl,--no-whole-archive \
-Wl,-soname=$(LIBHD_SONAME) \
-o $(LIBHD_SO) $(SO_LIBS)
ln -snf $(LIBHD_NAME) $(LIBHD_SONAME)
ln -snf $(LIBHD_SONAME) $(LIBHD_BASE).so
else
$(LIBHD_SO):
endif
subdirs: touch
touch:
@touch -r $(LIBHD) $(LIBHD_D) 2>/dev/null || true

2
src/hd/.cvsignore Normal file
View File

@ -0,0 +1,2 @@
.depend
version.h

3
src/hd/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
.depend
version.h

11
src/hd/Makefile Normal file
View File

@ -0,0 +1,11 @@
TOPDIR = ../..
TARGETS = $(LIBHD_D)
CLEANFILES = version.h
include $(TOPDIR)/Makefile.common
version.h: $(TOPDIR)/VERSION
@echo "#define HD_VERSION_STRING \"`cat $(TOPDIR)/VERSION`\"" >$@
$(LIBHD_D): $(OBJS)
ar r $(LIBHD) $?

1218
src/hd/bios.c Normal file

File diff suppressed because it is too large Load Diff

8
src/hd/bios.h Normal file
View File

@ -0,0 +1,8 @@
#define BIOS_ROM_START 0xc0000
#define BIOS_ROM_SIZE 0x40000
#define BIOS_RAM_START 0x400
#define BIOS_RAM_SIZE 0x100
void hd_scan_bios(hd_data_t *hd_data);
void get_vbe_info(hd_data_t *hd_data, vbe_info_t *vbe);

1871
src/hd/block.c Normal file

File diff suppressed because it is too large Load Diff

2
src/hd/block.h Normal file
View File

@ -0,0 +1,2 @@
void hd_scan_sysfs_block(hd_data_t *hd_data);
void hd_scan_sysfs_scsi(hd_data_t *hd_data);

584
src/hd/braille.c Normal file
View File

@ -0,0 +1,584 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/ioctl.h>
#include "hd.h"
#include "hd_int.h"
#include "braille.h"
/**
* @defgroup BRAILLEint Braille devices
* @ingroup libhdDEVint
* @brief Braille displays functions
*
* @{
*/
#if !defined(LIBHD_TINY) && !defined(__sparc__)
static unsigned do_alva(hd_data_t *hd_data, char *dev_name, int cnt);
static unsigned do_fhp(hd_data_t *hd_data, char *dev_name, unsigned baud, int cnt);
static unsigned do_fhp_new(hd_data_t *hd_data, char *dev_name, int cnt);
static unsigned do_ht(hd_data_t *hd_data, char *dev_name, int cnt);
static unsigned do_baum(hd_data_t *hd_data, char *dev_name, int cnt);
void hd_scan_braille(hd_data_t *hd_data)
{
hd_t *hd, *hd_tmp;
int cnt = 0;
unsigned *dev, *vend;
if(!hd_probe_feature(hd_data, pr_braille)) return;
hd_data->module = mod_braille;
/* some clean-up */
remove_hd_entries(hd_data);
dev = hd_shm_add(hd_data, NULL, sizeof *dev);
vend = hd_shm_add(hd_data, NULL, sizeof *vend);
if(!dev || !vend) return;
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_comm &&
hd->sub_class.id == sc_com_ser &&
hd->unix_dev_name &&
!hd->tag.skip_braille &&
!has_something_attached(hd_data, hd)
) {
cnt++;
*dev = *vend = 0;
hd_fork(hd_data, 10, 10);
if(hd_data->flags.forked) {
if(hd_probe_feature(hd_data, pr_braille_alva)) {
PROGRESS(1, cnt, "alva");
*vend = MAKE_ID(TAG_SPECIAL, 0x5001);
*dev = do_alva(hd_data, hd->unix_dev_name, cnt);
}
if(!*dev && hd_probe_feature(hd_data, pr_braille_fhp)) {
PROGRESS(1, cnt, "fhp_old");
*vend = MAKE_ID(TAG_SPECIAL, 0x5002);
*dev = do_fhp(hd_data, hd->unix_dev_name, B19200, cnt);
if(!*dev) {
PROGRESS(1, cnt, "fhp_el");
*dev = do_fhp(hd_data, hd->unix_dev_name, B38400, cnt);
}
}
if(!*dev && hd_probe_feature(hd_data, pr_braille_ht)) {
PROGRESS(1, cnt, "ht");
*vend = MAKE_ID(TAG_SPECIAL, 0x5003);
*dev = do_ht(hd_data, hd->unix_dev_name, cnt);
}
if(!*dev && hd_probe_feature(hd_data, pr_braille_baum)) {
PROGRESS(1, cnt, "baum");
*vend = MAKE_ID(TAG_SPECIAL, 0x5004);
*dev = do_baum(hd_data, hd->unix_dev_name, cnt);
}
if(!*dev && hd_probe_feature(hd_data, pr_braille_fhp)) {
PROGRESS(1, cnt, "fhp new");
*vend = MAKE_ID(TAG_SPECIAL, 0x5002);
*dev = do_fhp_new(hd_data, hd->unix_dev_name, cnt);
}
}
hd_fork_done(hd_data);
if(*dev && *vend) {
hd_tmp = add_hd_entry(hd_data, __LINE__, 0);
hd_tmp->base_class.id = bc_braille;
hd_tmp->bus.id = bus_serial;
hd_tmp->unix_dev_name = new_str(hd->unix_dev_name);
hd_tmp->attached_to = hd->idx;
hd_tmp->vendor.id = *vend;
hd_tmp->device.id = *dev;
}
}
}
hd_shm_clean(hd_data);
}
/*
* autodetect for Alva Braille-displays
* Author: marco Skambraks <marco@suse.de>
* Suse GmbH Nuernberg
*
* This is free software, placed under the terms of the
* GNU General Public License, as published by the Free Software
* Foundation. Please see the file COPYING for details.
*/
/* Communication codes */
#define BRL_ID "\033ID="
#define WAIT_DTR 700000
#define WAIT_FLUSH 200
unsigned do_alva(hd_data_t *hd_data, char *dev_name, int cnt)
{
int fd, i, timeout = 100;
struct termios oldtio, newtio; /* old & new terminal settings */
int model = -1;
unsigned char buffer[sizeof BRL_ID];
unsigned dev = 0;
PROGRESS(2, cnt, "alva open");
/* Open the Braille display device for random access */
fd = open(dev_name, O_RDWR | O_NOCTTY);
if(fd < 0) return 0;
tcgetattr(fd, &oldtio); /* save current settings */
/* Set flow control and 8n1, enable reading */
memset(&newtio, 0, sizeof newtio);
newtio.c_cflag = CRTSCTS | CS8 | CLOCAL | CREAD;
/* Ignore bytes with parity errors and make terminal raw and dumb */
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0; /* raw output */
newtio.c_lflag = 0; /* don't echo or generate signals */
newtio.c_cc[VMIN] = 0; /* set nonblocking read */
newtio.c_cc[VTIME] = 0;
PROGRESS(3, cnt, "alva init ok");
PROGRESS(4, cnt, "alva read data");
/* autodetecting ABT model */
/* to force DTR off */
cfsetispeed(&newtio, B0);
cfsetospeed(&newtio, B0);
tcsetattr(fd, TCSANOW, &newtio); /* activate new settings */
usleep(WAIT_DTR);
tcflush(fd, TCIOFLUSH); /* clean line */
usleep(WAIT_FLUSH);
/* DTR back on */
cfsetispeed(&newtio, B9600);
cfsetospeed(&newtio, B9600);
tcsetattr(fd, TCSANOW, &newtio); /* activate new settings */
usleep(WAIT_DTR); /* give time to send ID string */
if((i = read(fd, buffer, sizeof buffer)) == sizeof buffer) {
if(!strncmp(buffer, BRL_ID, sizeof BRL_ID - 1)) {
/* Find out which model we are connected to... */
switch(model = buffer[sizeof buffer - 1])
{
case 1:
case 2:
case 3:
case 4:
case 0x0b:
case 0x0d:
case 0x0e:
dev = MAKE_ID(TAG_SPECIAL, model);
break;
}
}
}
ADD2LOG("alva.%d@%s[%d]: ", timeout, dev_name, i);
if(i > 0) hd_log_hex(hd_data, 1, i, buffer);
ADD2LOG("\n");
PROGRESS(5, cnt, "alva read done");
/* reset serial lines */
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSAFLUSH, &oldtio);
close(fd);
return dev;
}
/*
* autodetect for Papenmeier Braille-displays
* Author: marco Skambraks <marco@suse.de>
* Suse GmbH Nuernberg
*
* This is free software, placed under the terms of the
* GNU General Public License, as published by the Free Software
* Foundation. Please see the file COPYING for details.
*/
unsigned do_fhp(hd_data_t *hd_data, char *dev_name, unsigned baud, int cnt)
{
int fd, i;
char crash[] = { 2, 'S', 0, 0, 0, 0 };
unsigned char buf[10];
struct termios oldtio, newtio; /* old & new terminal settings */
unsigned dev;
PROGRESS(2, cnt, "fhp open");
/* Now open the Braille display device for random access */
fd = open(dev_name, O_RDWR | O_NOCTTY);
if(fd < 0) return 0;
tcgetattr(fd, &oldtio); /* save current settings */
/* Set bps, flow control and 8n1, enable reading */
memset(&newtio, 0, sizeof newtio);
newtio.c_cflag = baud | CS8 | CLOCAL | CREAD;
/* Ignore bytes with parity errors and make terminal raw and dumb */
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0; /* raw output */
newtio.c_lflag = 0; /* don't echo or generate signals */
newtio.c_cc[VMIN] = 0; /* set nonblocking read */
newtio.c_cc[VTIME] = 0;
tcflush(fd, TCIFLUSH); /* clean line */
tcsetattr(fd, TCSANOW, &newtio); /* activate new settings */
PROGRESS(3, cnt, "fhp init ok");
crash[2] = 0x200 >> 8;
crash[3] = 0x200 & 0xff;
crash[5] = (7+10) & 0xff;
write(fd, crash, sizeof crash);
write(fd, "1111111111",10);
write(fd, "\03", 1);
crash[2] = 0x0 >> 8;
crash[3] = 0x0 & 0xff;
crash[5] = 5 & 0xff;
write(fd, crash, sizeof crash);
write(fd, "1111111111", 10);
write(fd, "\03", 1);
usleep(500000); /* 100000 should be enough */
PROGRESS(4, cnt, "fhp write ok");
i = read(fd, &buf, 10);
PROGRESS(5, cnt, "fhp read done");
ADD2LOG("fhp@%s[%d]: ", dev_name, i);
if(i > 0) hd_log_hex(hd_data, 1, i, buf);
ADD2LOG("\n");
dev = 0;
if(i == 10 && buf[0] == 0x02 && buf[1] == 0x49) {
switch(buf[2]) {
case 1:
case 2:
case 3:
case 64:
case 65:
case 66:
case 67:
case 68:
dev = buf[2];
dev = MAKE_ID(TAG_SPECIAL, dev);
break;
}
}
if(!dev) ADD2LOG("no fhp display: 0x%02x\n", i >= 2 ? buf[2] : 0);
/* reset serial lines */
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSAFLUSH, &oldtio);
close(fd);
return dev;
}
/*
* autodetect for Handy Tech Braille-displays
* Author: marco Skambraks <marco@suse.de>
* Suse GmbH Nuernberg
*
* This is free software, placed under the terms of the
* GNU General Public License, as published by the Free Software
* Foundation. Please see the file COPYING for details.
*/
unsigned do_ht(hd_data_t *hd_data, char *dev_name, int cnt)
{
int fd, i;
unsigned char code = 0xff, buf[2] = { 0, 0 };
struct termios oldtio, newtio;
unsigned dev = 0;
PROGRESS(2, cnt, "ht open");
fd = open(dev_name, O_RDWR | O_NOCTTY);
if(fd < 0) return 0;
tcgetattr(fd, &oldtio);
newtio = oldtio;
newtio.c_cflag = CLOCAL | PARODD | PARENB | CREAD | CS8;
newtio.c_iflag = IGNPAR;
newtio.c_oflag = 0;
newtio.c_lflag = 0;
newtio.c_cc[VMIN] = 0;
newtio.c_cc[VTIME] = 0;
i = 0;
/*
* Force down DTR, flush any pending data and then the port to what we
* want it to be
*/
if(
!(
cfsetispeed(&newtio, B0) ||
cfsetospeed(&newtio, B0) ||
tcsetattr(fd, TCSANOW, &newtio) ||
tcflush(fd, TCIOFLUSH) ||
cfsetispeed(&newtio, B19200) ||
cfsetospeed(&newtio, B19200) ||
tcsetattr(fd, TCSANOW, &newtio)
)
) {
/* Pause 20ms to let them take effect */
usleep(20 * 1000);
PROGRESS(3, cnt, "ht init ok");
write(fd, &code, 1); /* reset brl */
usleep(40 * 1000); /* wait for reset */
PROGRESS(4, cnt, "ht write ok");
read(fd, buf, 1);
i = 1;
PROGRESS(5, cnt, "ht read done");
if(buf[0] == 0xfe) { /* resetok now read id */
usleep(80 * 1000);
read(fd, buf + 1, 1);
i = 2;
PROGRESS(6, cnt, "ht read done");
switch(buf[1]) {
case 0x05:
case 0x09:
case 0x36:
case 0x38:
case 0x44:
case 0x72:
case 0x74:
case 0x78:
case 0x80:
case 0x84:
case 0x88:
case 0x89:
dev = buf[1];
dev = MAKE_ID(TAG_SPECIAL, dev);
break;
}
}
}
ADD2LOG("ht@%s[%d]: ", dev_name, i);
if(i > 0) hd_log_hex(hd_data, 1, i, buf);
ADD2LOG("\n");
if(!dev) ADD2LOG("no ht display: 0x%02x\n", buf[1]);
/* reset serial lines */
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSAFLUSH, &oldtio);
close(fd);
return dev;
}
/*
* autodetect for Baum Braille-displays
* Author: marco Skambraks <marco@suse.de>
* Suse GmbH Nuernberg
*
* This is free software, placed under the terms of the
* GNU General Public License, as published by the Free Software
* Foundation. Please see the file COPYING for details.
*/
#define BAUDRATE B19200 /* But both run at 19k2 */
#define MAXREAD 18
unsigned do_baum(hd_data_t *hd_data, char *dev_name, int cnt)
{
static char device_id[] = { 0x1b, 0x84 };
int fd;
struct termios oldtio, curtio;
unsigned char buf[MAXREAD + 1];
int i;
PROGRESS(2, cnt, "baum open");
fd = open(dev_name, O_RDWR | O_NOCTTY);
if(fd < 0) return 0;
tcgetattr(fd, &curtio);
oldtio = curtio;
cfmakeraw(&curtio);
/* no SIGTTOU to backgrounded processes */
curtio.c_lflag &= ~TOSTOP;
curtio.c_cflag = BAUDRATE | CS8 | CLOCAL | CREAD;
/* no input parity check, no XON/XOFF */
curtio.c_iflag &= ~(INPCK | ~IXOFF);
curtio.c_cc[VTIME] = 2; /* 0.1s timeout between chars on input */
curtio.c_cc[VMIN] = 0; /* no minimum input */
tcsetattr(fd, TCSAFLUSH, &curtio);
/* write ID-request */
write(fd, device_id, sizeof device_id);
/* wait for response */
usleep(250000);
PROGRESS(3, cnt, "baum write ok");
i = read(fd, buf, sizeof buf - 1);
buf[sizeof buf - 1] = 0;
PROGRESS(4, cnt, "baum read done");
ADD2LOG("baum@%s[%d]: ", dev_name, i);
if(i > 0) hd_log_hex(hd_data, 1, i, buf);
ADD2LOG("\n");
/* reset serial lines */
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSAFLUSH, &oldtio);
close(fd);
if(!strcmp(buf + 2, "Baum Vario40")) return MAKE_ID(TAG_SPECIAL, 1);
if(!strcmp(buf + 2, "Baum Vario80")) return MAKE_ID(TAG_SPECIAL, 2);
return 0;
}
unsigned do_fhp_new(hd_data_t *hd_data, char *dev_name, int cnt)
{
int i, fd, status = 0;
unsigned id;
unsigned char retstr[50] = "";
unsigned char brlauto[] = { 2, 0x42, 0x50, 0x50, 3 };
struct termios oldtio, tiodata = { };
PROGRESS(2, cnt, "fhp2 open");
fd = open(dev_name, O_RDWR | O_NONBLOCK | O_NOCTTY);
if(fd < 0) return 0;
fcntl(fd, F_SETFL, 0); // remove O_NONBLOCK
tcgetattr(fd, &oldtio);
/* Set bps, and 8n1, enable reading */
tiodata.c_cflag = (CLOCAL | CREAD | CS8);
tiodata.c_iflag = IGNPAR;
tiodata.c_lflag = 0;
tiodata.c_cc[VMIN] = 0;
tiodata.c_cc[VTIME] = 0;
if(
cfsetispeed(&tiodata, B0) ||
cfsetospeed(&tiodata, B0) ||
tcsetattr(fd, TCSANOW, &tiodata) ||
tcflush(fd, TCIOFLUSH) ||
cfsetispeed(&tiodata, B57600) ||
cfsetospeed(&tiodata, B57600) ||
tcsetattr(fd, TCSANOW, &tiodata)
) {
/* init error */
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSAFLUSH, &oldtio);
close(fd);
return 0;
}
tcflush(fd, TCIOFLUSH);
usleep(100 * 1000);
/* get status of inteface */
ioctl(fd, TIOCMGET, &status);
/* clear dtr-line */
status &= ~TIOCM_DTR;
/* set new status */
ioctl(fd, TIOCMSET, &status);
usleep(100 * 1000);
write(fd, brlauto, sizeof brlauto);
PROGRESS(3, cnt, "fhp2 write ok");
usleep(100 * 1000);
i = read(fd, retstr, 20);
PROGRESS(4, cnt, "fhp2 read done");
ADD2LOG("fhp2@%s[%d]: ", dev_name, i);
if(i > 0) hd_log_hex(hd_data, 1, i, retstr);
ADD2LOG("\n");
id = 0;
if(i == 10 && retstr[1] == 'J') {
id = retstr[3];
/* papenmeir new serial and usb device IDs */
switch(id) {
case 0x55:
case 0x57:
case 0x58:
id = MAKE_ID(TAG_SPECIAL, id);
break;
default:
ADD2LOG("unknown id %d\n", id);
id = 0;
}
}
/* reset serial lines */
tcflush(fd, TCIOFLUSH);
tcsetattr(fd, TCSAFLUSH, &oldtio);
close(fd);
return id;
}
#endif /* !defined(LIBHD_TINY) && !defined(__sparc__) */
/** @} */

1
src/hd/braille.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_braille(hd_data_t *hd_data);

1016
src/hd/cpu.c Normal file

File diff suppressed because it is too large Load Diff

1
src/hd/cpu.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_cpu(hd_data_t *hd_data);

14
src/hd/drm.c Normal file
View File

@ -0,0 +1,14 @@
#include <fcntl.h>
#include "hd.h"
#include "hd_int.h"
int is_kms_active(hd_data_t *hd_data) {
int kms = open("/sys/class/drm/card0", O_RDONLY) > 0;
ADD2LOG(" KMS detected: %d\n", kms);
return kms;
}

8
src/hd/drm.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef DRM_H
#define DRM_H
int is_kms_active(hd_data_t *hd_data);
#endif /* DRM_H */

299
src/hd/dvd.c Normal file
View File

@ -0,0 +1,299 @@
/*
* taken from HAL 0.5.14
* http://www.freedesktop.org/Software/hal
*
* Originally part of dvd+rw-tools by Andy Polyakov <appro@fy.chalmers.se>
* http://fy.chalmers.se/~appro/linux/DVD+RW/
*/
#define CREAM_ON_ERRNO(s) do { \
switch ((s)[2]&0x0F) \
{ case 2: if ((s)[12]==4) errno=EAGAIN; break; \
case 5: errno=EINVAL; \
if ((s)[13]==0) \
{ if ((s)[12]==0x21) errno=ENOSPC; \
else if ((s)[12]==0x20) errno=ENODEV; \
} \
break; \
} \
} while(0)
#define ERRCODE(s) ((((s)[2]&0x0F)<<16)|((s)[12]<<8)|((s)[13]))
#define SK(errcode) (((errcode)>>16)&0xF)
#define ASC(errcode) (((errcode)>>8)&0xFF)
#define ASCQ(errcode) ((errcode)&0xFF)
#ifndef _LARGEFILE_SOURCE
#define _LARGEFILE_SOURCE
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BITS
#define _FILE_OFFSET_BITS 64
#endif
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include <errno.h>
#include <string.h>
#include <mntent.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include <scsi/scsi.h>
#include <scsi/sg.h>
#include <poll.h>
#include <sys/time.h>
#include "dvd.h"
#if !defined(SG_FLAG_LUN_INHIBIT)
# if defined(SG_FLAG_UNUSED_LUN_INHIBIT)
# define SG_FLAG_LUN_INHIBIT SG_FLAG_UNUSED_LUN_INHIBIT
# else
# define SG_FLAG_LUN_INHIBIT 0
# endif
#endif
typedef enum {
NONE = CGC_DATA_NONE, // 3
READ = CGC_DATA_READ, // 2
WRITE = CGC_DATA_WRITE // 1
} Direction;
typedef struct ScsiCommand ScsiCommand;
struct ScsiCommand {
int fd;
int autoclose;
char *filename;
struct cdrom_generic_command cgc;
union {
struct request_sense s;
unsigned char u[18];
} _sense;
struct sg_io_hdr sg_io;
};
#define DIRECTION(i) (Dir_xlate[i]);
/* 1,CGC_DATA_WRITE
* 2,CGC_DATA_READ
* 3,CGC_DATA_NONE
*/
const int Dir_xlate[4] = {
0, // implementation-dependent...
SG_DXFER_TO_DEV, // 1,CGC_DATA_WRITE
SG_DXFER_FROM_DEV, // 2,CGC_DATA_READ
SG_DXFER_NONE // 3,CGC_DATA_NONE
};
static ScsiCommand *
scsi_command_new (void)
{
ScsiCommand *cmd;
cmd = (ScsiCommand *) malloc (sizeof (ScsiCommand));
memset (cmd, 0, sizeof (ScsiCommand));
cmd->fd = -1;
cmd->filename = NULL;
cmd->autoclose = 1;
return cmd;
}
static ScsiCommand *
scsi_command_new_from_fd (int f)
{
ScsiCommand *cmd;
cmd = scsi_command_new ();
cmd->fd = f;
cmd->autoclose = 0;
return cmd;
}
static void
scsi_command_free (ScsiCommand * cmd)
{
if (cmd->fd >= 0 && cmd->autoclose) {
close (cmd->fd);
cmd->fd = -1;
}
if (cmd->filename) {
free (cmd->filename);
cmd->filename = NULL;
}
free (cmd);
}
static int
scsi_command_transport (ScsiCommand * cmd, Direction dir, void *buf,
size_t sz)
{
int ret = 0;
cmd->sg_io.dxferp = buf;
cmd->sg_io.dxfer_len = sz;
cmd->sg_io.dxfer_direction = DIRECTION (dir);
if (ioctl (cmd->fd, SG_IO, &cmd->sg_io))
return -1;
if ((cmd->sg_io.info & SG_INFO_OK_MASK) != SG_INFO_OK) {
errno = EIO;
ret = -1;
if (cmd->sg_io.masked_status & CHECK_CONDITION) {
CREAM_ON_ERRNO ((unsigned char*)cmd->sg_io.sbp);
ret = ERRCODE ((unsigned char*)cmd->sg_io.sbp);
if (ret == 0)
ret = -1;
}
}
return ret;
}
static void
scsi_command_init (ScsiCommand * cmd, size_t i, int arg)
{
if (i == 0) {
memset (&cmd->cgc, 0, sizeof (cmd->cgc));
memset (&cmd->_sense, 0, sizeof (cmd->_sense));
cmd->cgc.quiet = 1;
cmd->cgc.sense = &cmd->_sense.s;
memset (&cmd->sg_io, 0, sizeof (cmd->sg_io));
cmd->sg_io.interface_id = 'S';
cmd->sg_io.mx_sb_len = sizeof (cmd->_sense);
cmd->sg_io.cmdp = cmd->cgc.cmd;
cmd->sg_io.sbp = cmd->_sense.u;
cmd->sg_io.flags = SG_FLAG_LUN_INHIBIT | SG_FLAG_DIRECT_IO;
}
cmd->sg_io.cmd_len = i + 1;
cmd->cgc.cmd[i] = arg;
}
int
get_dvd_profile(int fd)
{
ScsiCommand *cmd;
int retval = 0;
unsigned char page[20];
unsigned char *list;
int i, len;
cmd = scsi_command_new_from_fd (fd);
scsi_command_init (cmd, 0, 0x46);
scsi_command_init (cmd, 1, 2);
scsi_command_init (cmd, 8, 8);
scsi_command_init (cmd, 9, 0);
if (scsi_command_transport (cmd, READ, page, 8)) {
/* GET CONFIGURATION failed */
scsi_command_free (cmd);
return -1;
}
/* See if it's 2 gen drive by checking if DVD+R profile is an option */
len = 4 + (page[0] << 24 | page[1] << 16 | page[2] << 8 | page[3]);
if (len > 264) {
scsi_command_free (cmd);
/* insane profile list length */
return -1;
}
list = (unsigned char *) malloc (len);
scsi_command_init (cmd, 0, 0x46);
scsi_command_init (cmd, 1, 2);
scsi_command_init (cmd, 7, len >> 8);
scsi_command_init (cmd, 8, len);
scsi_command_init (cmd, 9, 0);
if (scsi_command_transport (cmd, READ, list, len)) {
/* GET CONFIGURATION failed */
scsi_command_free (cmd);
free (list);
return -1;
}
for (i = 12; i < list[11]; i += 4) {
int profile = (list[i] << 8 | list[i + 1]);
/* 0x13: DVD-RW Restricted Overwrite
* 0x14: DVD-RW Sequential
* 0x15: DVD-R Dual Layer Sequential
* 0x16: DVD-R Dual Layer Jump
* 0x1A: DVD+RW
* 0x1B: DVD+R
* 0x2A: DVD+RW DL
* 0x2B: DVD+R DL
* 0x40: BD-ROM
* 0x41: BD-R SRM
* 0x42: BR-R RRM
* 0x43: BD-RE
* 0x50: HD DVD-ROM
* 0x51: HD DVD-R
* 0x52: HD DVD-Rewritable
*/
switch (profile) {
case 0x13:
case 0x14:
retval |= DRIVE_CDROM_CAPS_DVDRW;
break;
case 0x15:
case 0x16:
retval |= DRIVE_CDROM_CAPS_DVDRDL;
break;
case 0x1B:
retval |= DRIVE_CDROM_CAPS_DVDPLUSR;
break;
case 0x1A:
retval |= DRIVE_CDROM_CAPS_DVDPLUSRW;
break;
case 0x2A:
retval |= DRIVE_CDROM_CAPS_DVDPLUSRWDL;
break;
case 0x2B:
retval |= DRIVE_CDROM_CAPS_DVDPLUSRDL;
break;
case 0x40:
retval |= DRIVE_CDROM_CAPS_BDROM;
break;
case 0x41:
case 0x42:
retval |= DRIVE_CDROM_CAPS_BDR;
break;
case 0x43:
retval |= DRIVE_CDROM_CAPS_BDRE;
break;
case 0x50:
retval |= DRIVE_CDROM_CAPS_HDDVDROM;
break;
case 0x51:
retval |= DRIVE_CDROM_CAPS_HDDVDR;
break;
case 0x52:
retval |= DRIVE_CDROM_CAPS_HDDVDRW;
break;
default:
break;
}
}
scsi_command_free (cmd);
free (list);
return retval;
}

19
src/hd/dvd.h Normal file
View File

@ -0,0 +1,19 @@
/*
* taken from HAL 0.5.14
* http://www.freedesktop.org/Software/hal
*/
#define DRIVE_CDROM_CAPS_DVDRW 1
#define DRIVE_CDROM_CAPS_DVDRDL 2
#define DRIVE_CDROM_CAPS_DVDPLUSR 4
#define DRIVE_CDROM_CAPS_DVDPLUSRW 8
#define DRIVE_CDROM_CAPS_DVDPLUSRWDL 16
#define DRIVE_CDROM_CAPS_DVDPLUSRDL 32
#define DRIVE_CDROM_CAPS_BDROM 64
#define DRIVE_CDROM_CAPS_BDR 128
#define DRIVE_CDROM_CAPS_BDRE 256
#define DRIVE_CDROM_CAPS_HDDVDROM 512
#define DRIVE_CDROM_CAPS_HDDVDR 1024
#define DRIVE_CDROM_CAPS_HDDVDRW 2048
int get_dvd_profile(int fd);

398
src/hd/edd.c Normal file
View File

@ -0,0 +1,398 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <endian.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "hd.h"
#include "hd_int.h"
#include "hddb.h"
#include "edd.h"
/**
* @defgroup EDDint EDD partition information
* @ingroup libhdINFOint
* @brief EDD disks layout / partition functions
*
* @{
*/
#if defined(__i386__) || defined(__x86_64__)
static void read_edd_info(hd_data_t *hd_data);
static int does_match(edd_info_t *ei, hd_t *hd, unsigned type);
static int does_match0(edd_info_t *ei, edd_info_t *ei0, unsigned type);
static int is_disk(hd_t *hd);
static uint64_t disk_size(hd_t *hd);
static int identical_disks(hd_t *hd1, hd_t *hd2);
void hd_scan_sysfs_edd(hd_data_t *hd_data)
{
if(!hd_probe_feature(hd_data, pr_edd)) return;
hd_data->module = mod_edd;
/* some clean-up */
remove_hd_entries(hd_data);
hd_data->flags.edd_used = 0;
if(hd_probe_feature(hd_data, pr_edd_mod)) {
PROGRESS(1, 0, "edd mod");
load_module(hd_data, "edd");
}
PROGRESS(2, 0, "edd info");
read_edd_info(hd_data);
}
void read_edd_info(hd_data_t *hd_data)
{
unsigned u, u1;
uint64_t ul0, edd_dev_path1, edd_dev_path2;
edd_info_t *ei;
str_list_t *sl, *sf_dir, *sf_dir_e, *sf_dir2;
char *sf_edd = NULL, *net_link = NULL;
unsigned char *edd_raw;
char *edd_bus, *edd_interface, *edd_link;
for(u = 0; u < sizeof hd_data->edd / sizeof *hd_data->edd; u++) {
free_mem(hd_data->edd[u].sysfs_id);
}
memset(hd_data->edd, 0, sizeof hd_data->edd);
sf_dir = read_dir("/sys/firmware/edd", 'd');
for(sf_dir_e = sf_dir; sf_dir_e; sf_dir_e = sf_dir_e->next) {
str_printf(&sf_edd, 0, "/sys/firmware/edd/%s", sf_dir_e->str);
if(
sscanf(sf_dir_e->str, "int13_dev%02x", &u) == 1 &&
u >= 0x80 &&
u <= 0xff
) {
u -= 0x80;
ei = hd_data->edd + u;
ei->valid = 1;
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "sectors"), &ul0, 0)) {
ei->sectors = ul0;
}
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "default_cylinders"), &ul0, 0)) {
ei->edd.cyls = ul0;
}
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "default_heads"), &ul0, 0)) {
ei->edd.heads = ul0;
}
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "default_sectors_per_track"), &ul0, 0)) {
ei->edd.sectors = ul0;
}
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "legacy_max_cylinder"), &ul0, 0)) {
ei->legacy.cyls = ul0 + 1;
}
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "legacy_max_head"), &ul0, 0)) {
ei->legacy.heads = ul0 + 1;
}
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "legacy_sectors_per_track"), &ul0, 0)) {
ei->legacy.sectors = ul0;
}
if(ei->sectors && ei->edd.heads && ei->edd.sectors) {
ei->edd.cyls = ei->sectors / (ei->edd.heads * ei->edd.sectors);
}
if(hd_attr_uint(get_sysfs_attr_by_path(sf_edd, "mbr_signature"), &ul0, 0)) {
ei->signature = ul0;
}
sl = hd_attr_list(get_sysfs_attr_by_path(sf_edd, "extensions"));
if(search_str_list(sl, "Fixed disk access")) hd_data->edd[u].ext_fixed_disk = 1;
if(search_str_list(sl, "Device locking and ejecting")) hd_data->edd[u].ext_lock_eject = 1;
if(search_str_list(sl, "Enhanced Disk Drive support")) hd_data->edd[u].ext_edd = 1;
if(search_str_list(sl, "64-bit extensions")) hd_data->edd[u].ext_64bit = 1;
edd_bus = edd_interface = NULL;
edd_dev_path1 = edd_dev_path2 = 0;
edd_raw = get_sysfs_attr_by_path2(sf_edd, "raw_data", &u1);
if(u1 >= 40) edd_bus = canon_str(edd_raw + 36, 4);
if(u1 >= 48) {
edd_interface = canon_str(edd_raw + 40, 8);
if(!strcmp(edd_interface, "FIBRE")) ei->ext_fibre = 1;
}
if(u1 >= 72) {
memcpy(&edd_dev_path1, edd_raw + 56, 8);
memcpy(&edd_dev_path2, edd_raw + 64, 8);
edd_dev_path1 = be64toh(edd_dev_path1); // wwid for fc
}
edd_link = hd_read_sysfs_link(sf_edd, "pci_dev");
if(edd_link) {
str_printf(&net_link, 0, "%s/net", edd_link);
sf_dir2 = read_dir("/sys/firmware/edd", 'D');
if(sf_dir2) ei->ext_net = 1;
free_str_list(sf_dir2);
}
ADD2LOG(
"edd: 0x%02x\n mbr sig: 0x%08x\n size: %"PRIu64"\n chs default: %u/%u/%u\n chs legacy: %u/%u/%u\n caps: %s%s%s%s%s%s\n",
u + 0x80,
ei->signature,
ei->sectors,
ei->edd.cyls,
ei->edd.heads,
ei->edd.sectors,
ei->legacy.cyls,
ei->legacy.heads,
ei->legacy.sectors,
ei->ext_fixed_disk ? "fixed " : "",
ei->ext_lock_eject ? "lock " : "",
ei->ext_edd ? "edd " : "",
ei->ext_64bit ? "64bit " : "",
ei->ext_fibre ? "fibre " : "",
ei->ext_net ? "net " : ""
);
ADD2LOG(" bus: %s\n interface: %s\n dev path: %016"PRIx64" %016"PRIx64"\n", edd_bus, edd_interface, edd_dev_path1, edd_dev_path2);
free_mem(edd_bus);
free_mem(edd_interface);
}
}
free_mem(sf_edd);
free_mem(net_link);
free_str_list(sf_dir);
}
void assign_edd_info(hd_data_t *hd_data)
{
hd_t *hd, *match_hd;
unsigned u, u1, u2, lba, matches, real_matches, match_edd, type;
bios_info_t *bt;
edd_info_t *ei;
for(hd = hd_data->hd; hd; hd = hd->next) {
if(is_disk(hd)) hd->rom_id = free_mem(hd->rom_id);
}
/* add BIOS drive ids to disks */
for(type = 0; type < 4; type++) {
for(u = 0; u < sizeof hd_data->edd / sizeof *hd_data->edd; u++) {
ei = hd_data->edd + u;
if(!ei->valid || ei->assigned) continue;
for(u1 = u2 = 0; u1 < sizeof hd_data->edd / sizeof *hd_data->edd; u1++) {
if(does_match0(ei, hd_data->edd + u1, type)) u2++;
}
/* not unique */
if(u2 != 1) continue;
matches = real_matches = 0;
match_hd = 0;
match_edd = 0;
for(hd = hd_data->hd; hd; hd = hd->next) {
if(!is_disk(hd) || hd->rom_id) continue;
if(does_match(ei, hd, type)) {
if(!matches) {
match_edd = u;
match_hd = hd;
}
else {
if(identical_disks(hd, match_hd)) real_matches--;
}
matches++;
real_matches++;
}
}
ADD2LOG(" %02x: matches %d (%d)\n", u + 0x80, real_matches, matches);
if(real_matches == 1) {
hd_data->flags.edd_used = 1;
hd_data->edd[match_edd].assigned = 1;
if(matches == 1) {
str_printf(&match_hd->rom_id, 0, "0x%02x", match_edd + 0x80);
ADD2LOG(" %s = %s (match %d)\n", match_hd->unix_dev_name, match_hd->rom_id, type);
}
else {
for(hd = hd_data->hd; hd; hd = hd->next) {
if(!is_disk(hd) || hd->rom_id) continue;
if(does_match(ei, hd, type)) {
str_printf(&hd->rom_id, 0, "0x%02x", match_edd + 0x80);
ADD2LOG(" %s = %s (match %d)\n", hd->unix_dev_name, hd->rom_id, type);
}
}
}
}
}
}
/* set lba support flag in BIOS data */
for(lba = u = 0; u < sizeof hd_data->edd / sizeof *hd_data->edd; u++) {
if(hd_data->edd[u].ext_fixed_disk) {
lba = 1;
break;
}
}
if(lba) {
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_internal &&
hd->sub_class.id == sc_int_bios &&
hd->detail &&
hd->detail->type == hd_detail_bios &&
(bt = hd->detail->bios.data)
) {
bt->lba_support = lba;
}
}
}
}
int does_match(edd_info_t *ei, hd_t *hd, unsigned type)
{
int i = 0;
uint64_t u64;
switch(type) {
case 0:
i = ei->signature == edd_disk_signature(hd) && ei->sectors == disk_size(hd);
break;
case 1:
i = ei->signature == edd_disk_signature(hd);
break;
case 2:
i = ei->sectors == disk_size(hd);
break;
case 3:
u64 = ei->edd.heads * ei->edd.sectors;
if(u64) {
i = ei->edd.cyls == disk_size(hd) / u64;
}
break;
}
return i;
}
int does_match0(edd_info_t *ei, edd_info_t *ei0, unsigned type)
{
int i = 0;
switch(type) {
case 0:
i = ei->signature == ei0->signature && ei->sectors == ei0->sectors;
break;
case 1:
i = ei->signature == ei0->signature;
break;
case 2:
i = ei->sectors == ei0->sectors;
break;
case 3:
i = ei->edd.cyls == ei0->edd.cyls;
break;
}
return i;
}
int is_disk(hd_t *hd)
{
return
hd->base_class.id == bc_storage_device &&
hd->sub_class.id == sc_sdev_disk;
}
uint64_t disk_size(hd_t *hd)
{
hd_res_t *res;
uint64_t ul = 0;
for(res = hd->res; res; res = res->next) {
if(
res->any.type == res_size &&
res->size.unit == size_unit_sectors
) {
ul = res->size.val1;
break;
}
}
return ul;
}
unsigned edd_disk_signature(hd_t *hd)
{
unsigned char *s = hd->block0;
if(!s) return 0;
return s[0x1b8] + (s[0x1b9] << 8) + (s[0x1ba] << 16) + (s[0x1bb] << 24);
}
int identical_disks(hd_t *hd1, hd_t *hd2)
{
char *s1 = NULL, *s2 = NULL;
str_list_t *sl;
for(sl = hd1->unix_dev_names; sl; sl = sl->next) {
if(!strncmp(sl->str, "/dev/disk/by-id/", sizeof "/dev/disk/by-id/" - 1)) {
s1 = sl->str;
break;
}
}
for(sl = hd2->unix_dev_names; sl; sl = sl->next) {
if(!strncmp(sl->str, "/dev/disk/by-id/", sizeof "/dev/disk/by-id/" - 1)) {
s2 = sl->str;
break;
}
}
if(!s1) s1 = hd1->serial;
if(!s2) s2 = hd1->serial;
if(s1 && s2 && !strcmp(s1, s2)) return 1;
return 0;
}
#endif /* defined(__i386__) || defined(__x86_64__) */
/** @} */

5
src/hd/edd.h Normal file
View File

@ -0,0 +1,5 @@
#if defined(__i386__) || defined(__x86_64__)
void hd_scan_sysfs_edd(hd_data_t *hd_data);
unsigned edd_disk_signature(hd_t *hd);
void assign_edd_info(hd_data_t *hd_data);
#endif

140
src/hd/fb.c Normal file
View File

@ -0,0 +1,140 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include <linux/fb.h>
#include "hd.h"
#include "hd_int.h"
#include "fb.h"
/**
* @defgroup Framebuffer Framebuffer devices
* @ingroup libhdDEVint
* @brief Scan framebuffer devices
*
* @{
*/
typedef struct {
unsigned width;
unsigned height;
double pix_clock;
double h_freq;
double v_freq;
} fb_info_t;
static fb_info_t *fb_get_info(hd_data_t *hd_data);
void hd_scan_fb(hd_data_t *hd_data)
{
fb_info_t *fb;
hd_t *hd;
hd_res_t *res;
unsigned imac_dev, imac_vend;
unsigned imac = 0;
monitor_info_t *mi = NULL;
if(!hd_probe_feature(hd_data, pr_fb)) return;
hd_data->module = mod_fb;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(1, 0, "read info");
fb = fb_get_info(hd_data);
if(fb) {
imac_dev = MAKE_ID(TAG_EISA, 0x9d03);
imac_vend = name2eisa_id("APP");
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_monitor) break;
}
if(hd && hd->device.id == imac_dev && hd->vendor.id == imac_vend) {
hd->tag.remove = 1;
remove_tagged_hd_entries(hd_data);
imac = 1;
hd = NULL;
}
/* add monitor entry based on fb data if we have no other info */
if(!hd) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_monitor;
if(imac) {
hd->vendor.id = imac_vend;
hd->device.id = imac_dev;
}
else {
hd->vendor.name = new_str("Generic");
hd->device.name = new_str("Monitor");
}
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->monitor.type = res_monitor;
res->monitor.width = fb->width;
res->monitor.height = fb->height;
res->monitor.vfreq = fb->v_freq + 0.5;
if(!hd->detail) {
mi = new_mem(sizeof *mi);
hd->detail = new_mem(sizeof *hd->detail);
hd->detail->type = hd_detail_monitor;
hd->detail->monitor.data = mi;
mi->min_vsync = 50;
mi->min_hsync = 31;
mi->max_vsync = fb->v_freq * 1.11 + 0.9;
mi->max_hsync = fb->h_freq / 1000.0 + 1.9;
if(mi->max_vsync <= mi->min_vsync) mi->max_vsync = mi->min_vsync + 10;
if(mi->max_hsync <= mi->min_hsync) mi->max_hsync = mi->min_hsync + 5;
/* round up */
mi->max_vsync = ((mi->max_vsync + 9) / 10) * 10;
}
}
}
}
fb_info_t *fb_get_info(hd_data_t *hd_data)
{
int fd;
struct fb_var_screeninfo fbv_info;
static fb_info_t fb_info;
fb_info_t *fb = NULL;
int h, v;
fd = open(DEV_FB, O_RDONLY);
if(fd < 0) fd = open(DEV_FB0, O_RDONLY);
if(fd < 0) return fb;
if(!ioctl(fd, FBIOGET_VSCREENINFO, &fbv_info)) {
h = fbv_info.left_margin + fbv_info.xres + fbv_info.right_margin + fbv_info.hsync_len;
v = fbv_info.upper_margin + fbv_info.yres + fbv_info.lower_margin + fbv_info.vsync_len;
if(fbv_info.pixclock && h && v) {
fb_info.width = fbv_info.xres;
fb_info.height = fbv_info.yres;
fb_info.pix_clock = 1e12 / fbv_info.pixclock;
fb_info.h_freq = fb_info.pix_clock / h;
fb_info.v_freq = fb_info.h_freq / v;
fb = &fb_info;
ADD2LOG("fb: size %d x %d\n", fb_info.width, fb_info.height);
ADD2LOG("fb: timing %.2f MHz, %.2f kHz, %.2f Hz\n", fb_info.pix_clock * 1e-6, fb_info.h_freq * 1e-3, fb_info.v_freq);
}
}
close(fd);
return fb;
}
/** @} */

1
src/hd/fb.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_fb(hd_data_t *hd_data);

175
src/hd/floppy.c Normal file
View File

@ -0,0 +1,175 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "hd.h"
#include "hd_int.h"
#include "klog.h"
#include "floppy.h"
/**
* @defgroup FLOPPYint Floppy devices
* @ingroup libhdDEVint
*
* This should currently be called *before* scan_misc() so we can try to get
* the floppy controller resources in scan_misc() by actually accessing the
* floppy drive. (Otherwise there would be a rather longish timeout.)
*
* This is all rather strange and should be rewritten...
*
* @{
*/
static void dump_floppy_data(hd_data_t *hd_data);
void hd_scan_floppy(hd_data_t *hd_data)
{
hd_t *hd;
char b0[10], b1[10], c;
unsigned u;
int fd, i, floppy_ctrls = 0, floppy_ctrl_idx = 0;
str_list_t *sl;
hd_res_t *res;
int floppy_stat[2] = { 1, 1 };
unsigned floppy_created = 0;
if(!hd_probe_feature(hd_data, pr_floppy)) return;
hd_data->module = mod_floppy;
/* some clean-up */
remove_hd_entries(hd_data);
hd_data->floppy = free_str_list(hd_data->floppy);
PROGRESS(1, 0, "get nvram");
/*
* Look for existing floppy controller entries (typically there will be
* *none*).
*/
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_storage && hd->sub_class.id == sc_sto_floppy) {
floppy_ctrls++;
floppy_ctrl_idx = hd->idx;
}
}
/*
* Is enough to load the nvram module.
*
* Note: although you must be root to access /dev/nvram, every
* user can read /proc/nvram.
*/
fd = open(DEV_NVRAM, O_RDONLY | O_NONBLOCK);
if(fd >= 0) close(fd);
if(
!(hd_data->floppy = read_file(PROC_NVRAM_24, 0, 0)) &&
!(hd_data->floppy = read_file(PROC_NVRAM_22, 0, 0))
);
if(hd_data->floppy && (hd_data->debug & HD_DEB_FLOPPY)) dump_floppy_data(hd_data);
if(!hd_data->klog) read_klog(hd_data);
for(sl = hd_data->klog; sl; sl = sl->next) {
if(sscanf(sl->str, "<4>floppy%u: no floppy controllers foun%c", &u, &c) == 2) {
if(u < sizeof floppy_stat / sizeof *floppy_stat) {
floppy_stat[u] = 0;
}
}
}
if(hd_data->floppy) {
PROGRESS(2, 0, "nvram info");
sl = hd_data->floppy;
}
else {
PROGRESS(2, 0, "klog info");
sl = hd_data->klog;
}
for(; sl; sl = sl->next) {
if(hd_data->floppy) {
i = sscanf(sl->str, " Floppy %u type : %8[0-9.]'' %8[0-9.]%c", &u, b0, b1, &c) == 4;
}
else {
i = sscanf(sl->str, "<6>Floppy drive(s): fd%u is %8[0-9.]%c", &u, b1, &c) == 3;
*b0 = 0;
}
if(i) {
if(
!floppy_ctrls &&
u < sizeof floppy_stat / sizeof *floppy_stat &&
floppy_stat[u]
) {
/* create one, if missing (there's no floppy without a controller...) */
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_storage;
hd->sub_class.id = sc_sto_floppy;
floppy_ctrl_idx = hd->idx;
floppy_ctrls++;
}
struct stat sbuf;
unsigned floppy_exists = 0;
char *floppy_name = NULL;
str_printf(&floppy_name, 0, "/dev/fd%u", u);
floppy_exists = stat(floppy_name, &sbuf) ? 0 : 1;
free_mem(floppy_name);
if(floppy_ctrls && !(floppy_created & (1 << u)) && floppy_exists) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_storage_device;
hd->sub_class.id = sc_sdev_floppy;
hd->bus.id = bus_floppy;
hd->slot = u;
str_printf(&hd->unix_dev_name, 0, "/dev/fd%u", u);
floppy_created |= 1 << u;
if(*b0) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->size.type = res_size;
res->size.val1 = str2float(b0, 2);
res->size.unit = size_unit_cinch;
}
/* 'k' or 'M' */
i = c == 'M' ? str2float(b1, 3) : str2float(b1, 0);
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->size.type = res_size;
res->size.val1 = i << 1;
res->size.val2 = 0x200;
res->size.unit = size_unit_sectors;
/* the only choice... */
if(floppy_ctrls == 1) hd->attached_to = floppy_ctrl_idx;
}
}
}
}
/*
* Add floppy data to the global log.
*/
void dump_floppy_data(hd_data_t *hd_data)
{
str_list_t *sl;
ADD2LOG("----- /proc/nvram -----\n");
for(sl = hd_data->floppy; sl; sl = sl->next) {
ADD2LOG(" %s", sl->str);
}
ADD2LOG("----- /proc/nvram end -----\n");
}
/** @} */

1
src/hd/floppy.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_floppy(hd_data_t *hd_data);

768
src/hd/hal.c Normal file
View File

@ -0,0 +1,768 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
// #define WITH_HAL 1
#if WITH_HAL
#ifndef DBUS_API_SUBJECT_TO_CHANGE
#define DBUS_API_SUBJECT_TO_CHANGE 1
#endif
#include <dbus/dbus.h>
#include <hal/libhal.h>
#endif
#include "hd.h"
#include "hd_int.h"
#include "hal.h"
/**
* @defgroup HALint Hardware abstraction (HAL) information
* @ingroup libhdInternals
*
* @{
*/
static void read_hal(hd_data_t *hd_data);
static void add_pci(hd_data_t *hd_data);
static void link_hal_tree(hd_data_t *hd_data);
static int hal_match_str(hal_prop_t *prop, const char *key, const char *val);
static int check_udi(const char *udi);
static FILE *hd_open_properties(const char *udi, const char *mode);
static char *skip_space(char *s);
static char *skip_non_eq_or_space(char *s);
static char *skip_nonquote(char *s);
static void parse_property(hal_prop_t *prop, char *str);
static void find_udi(hd_data_t *hd_data, hd_t *hd, int match);
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* read hal data
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
void hd_scan_hal(hd_data_t *hd_data)
{
if(!hd_probe_feature(hd_data, pr_hal)) return;
hd_data->module = mod_hal;
/* some clean-up */
remove_hd_entries(hd_data);
hd_data->hal = hd_free_hal_devices(hd_data->hal);
PROGRESS(1, 0, "read hal data");
read_hal(hd_data);
if(!hd_data->hal) return;
link_hal_tree(hd_data);
PROGRESS(1, 0, "pci sysfs");
hd_pci_read_data(hd_data);
PROGRESS(2, 0, "pci devices");
add_pci(hd_data);
}
void hd_scan_hal_basic(hd_data_t *hd_data)
{
hd_data->module = mod_hal;
/* some clean-up */
hd_data->hal = hd_free_hal_devices(hd_data->hal);
PROGRESS(1, 0, "read hal data");
read_hal(hd_data);
}
void read_hal(hd_data_t *hd_data)
{
#if WITH_HAL
DBusError error;
DBusConnection *conn;
LibHalContext *hal_ctx;
LibHalPropertySet *props;
LibHalPropertySetIterator it;
char **device_names, **slist, *s;
int i, num_devices, type;
hal_device_t *dev;
hal_prop_t *prop;
dbus_error_init(&error);
if(!(conn = dbus_bus_get(DBUS_BUS_SYSTEM, &error))) {
ADD2LOG(" hal: dbus_bus_get: %s: %s\n", error.name, error.message);
return;
}
ADD2LOG(" hal: connected to: %s\n", dbus_bus_get_unique_name(conn));
if(!(hal_ctx = libhal_ctx_new())) return;
if(!libhal_ctx_set_dbus_connection(hal_ctx, conn)) return;
if(!libhal_ctx_init(hal_ctx, &error)) {
ADD2LOG(" hal: libhal_ctx_init: %s: %s\n", error.name, error.message);
return;
}
dbus_error_init(&error);
if((device_names = libhal_get_all_devices(hal_ctx, &num_devices, &error))) {
ADD2LOG("----- hal device list -----\n");
for(i = 0; i < num_devices; i++) {
if(!(props = libhal_device_get_all_properties(hal_ctx, device_names[i], &error))) {
ADD2LOG(" hal: %s: %s\n", error.name, error.message);
dbus_error_init(&error);
continue;
}
dev = new_mem(sizeof *dev);
dev->udi = new_str(device_names[i]);
dev->next = hd_data->hal;
hd_data->hal = dev;
ADD2LOG(" %d: udi = '%s'\n", i, dev->udi);
for(libhal_psi_init(&it, props); libhal_psi_has_more(&it); libhal_psi_next(&it)) {
type = libhal_psi_get_type(&it);
prop = new_mem(sizeof *prop);
prop->next = dev->prop;
dev->prop = prop;
switch(type) {
case LIBHAL_PROPERTY_TYPE_STRING:
prop->type = p_string;
prop->key = new_str(libhal_psi_get_key(&it));
prop->val.str = new_str(libhal_psi_get_string(&it));
break;
case LIBHAL_PROPERTY_TYPE_INT32:
prop->type = p_int32;
prop->key = new_str(libhal_psi_get_key(&it));
prop->val.int32 = libhal_psi_get_int(&it);
break;
case LIBHAL_PROPERTY_TYPE_UINT64:
prop->type = p_uint64;
prop->key = new_str(libhal_psi_get_key(&it));
prop->val.uint64 = libhal_psi_get_uint64(&it);
break;
case LIBHAL_PROPERTY_TYPE_DOUBLE:
prop->type = p_double;
prop->key = new_str(libhal_psi_get_key(&it));
prop->val.d = libhal_psi_get_double(&it);
break;
case LIBHAL_PROPERTY_TYPE_BOOLEAN:
prop->type = p_bool;
prop->key = new_str(libhal_psi_get_key(&it));
prop->val.b = libhal_psi_get_bool(&it);
break;
case LIBHAL_PROPERTY_TYPE_STRLIST:
prop->type = p_list;
prop->key = new_str(libhal_psi_get_key(&it));
for(slist = libhal_psi_get_strlist(&it); *slist; slist++) {
add_str_list(&prop->val.list, *slist);
}
break;
default:
prop->type = p_invalid;
}
if((s = hd_hal_print_prop(prop))) {
ADD2LOG(" %s\n", s);
}
}
libhal_free_property_set(props);
if(i != num_devices - 1) ADD2LOG("\n");
}
ADD2LOG("----- hal device list end -----\n");
libhal_free_string_array(device_names);
dbus_error_free(&error);
}
else {
ADD2LOG(" hal: empty device list\n");
}
libhal_ctx_shutdown(hal_ctx, &error);
libhal_ctx_free(hal_ctx);
dbus_connection_unref(conn);
dbus_error_free(&error);
#endif /* WITH_HAL */
}
void link_hal_tree(hd_data_t *hd_data)
{
hal_device_t *dev;
hal_prop_t *prop;
for(dev = hd_data->hal; dev; dev = dev->next) {
prop = hal_get_str(dev->prop, "info.parent");
if(prop) {
dev->parent = hal_find_device(hd_data, prop->val.str);
}
}
}
hal_device_t *hal_find_device(hd_data_t *hd_data, char *udi)
{
hal_device_t *dev;
if(udi) {
for(dev = hd_data->hal; dev; dev = dev->next) {
if(!strcmp(dev->udi, udi)) return dev;
}
}
return NULL;
}
void hal_invalidate(hal_prop_t *prop)
{
if(prop->type == p_string) free_mem(prop->val.str);
if(prop->type == p_list) free_str_list(prop->val.list);
prop->type = p_invalid;
memset(&prop->val, 0, sizeof prop->val);
}
void hal_invalidate_all(hal_prop_t *prop, const char *key)
{
for(; (prop = hal_get_any(prop, key)); prop = prop->next) {
hal_invalidate(prop);
}
}
hal_prop_t *hal_get_any(hal_prop_t *prop, const char *key)
{
for(; prop; prop = prop->next) {
if(!strcmp(prop->key, key)) return prop;
}
return NULL;
}
hal_prop_t *hal_get_bool(hal_prop_t *prop, const char *key)
{
for(; prop; prop = prop->next) {
if(prop->type == p_bool && !strcmp(prop->key, key)) return prop;
}
return NULL;
}
hal_prop_t *hal_get_int32(hal_prop_t *prop, const char *key)
{
for(; prop; prop = prop->next) {
if(prop->type == p_int32 && !strcmp(prop->key, key)) return prop;
}
return NULL;
}
hal_prop_t *hal_get_str(hal_prop_t *prop, const char *key)
{
for(; prop; prop = prop->next) {
if(prop->type == p_string && !strcmp(prop->key, key)) return prop;
}
return NULL;
}
char *hal_get_useful_str(hal_prop_t *prop, const char *key)
{
for(; prop; prop = prop->next) {
if(prop->type == p_string && !strcmp(prop->key, key)) {
if(prop->val.str && strncmp(prop->val.str, "Unknown", sizeof "Unknown" - 1)) return prop->val.str;
break;
}
}
return NULL;
}
int hal_match_str(hal_prop_t *prop, const char *key, const char *val)
{
return val && (prop = hal_get_str(prop, key)) && !strcmp(prop->val.str, val);
}
hal_prop_t *hal_get_list(hal_prop_t *prop, const char *key)
{
for(; prop; prop = prop->next) {
if(prop->type == p_list && !strcmp(prop->key, key)) return prop;
}
return NULL;
}
hal_prop_t *hal_add_new(hal_prop_t **prop)
{
while(*prop) prop = &(*prop)->next;
*prop = new_mem(sizeof **prop);
hal_invalidate(*prop);
return *prop;
}
void add_pci(hd_data_t *hd_data)
{
hd_t *hd;
hal_prop_t *prop;
int i, j;
char *s;
hal_device_t *dev;
pci_t *pci;
for(dev = hd_data->hal ; dev; dev = dev->next) {
if(dev->used) continue;
if(!hal_match_str(dev->prop, "info.subsystem", "pci")) continue;
dev->used = 1;
hd = add_hd_entry(hd_data, __LINE__, 0);
if((prop = hal_get_str(dev->prop, "linux.sysfs_path"))) hd->sysfs_id = new_str(hd_sysfs_id(prop->val.str));
for(pci = hd_data->pci; pci; pci = pci->next) {
if(!strcmp(hd_sysfs_id(pci->sysfs_id), hd->sysfs_id)) {
hd->detail = new_mem(sizeof *hd->detail);
hd->detail->type = hd_detail_pci;
hd->detail->pci.data = pci;
break;
}
}
hd_pci_complete_data(hd);
hd->udi = new_str(dev->udi);
if(dev->parent) hd->parent_udi = new_str(dev->parent->udi);
if((prop = hal_get_int32(dev->prop, "pci.device_protocol"))) hd->prog_if.id = prop->val.int32;
if((prop = hal_get_int32(dev->prop, "pci.device_subclass"))) hd->sub_class.id = prop->val.int32;
if((prop = hal_get_int32(dev->prop, "pci.device_class"))) hd->base_class.id = prop->val.int32;
i = (prop = hal_get_int32(dev->prop, "pci.vendor_id")) ? prop->val.int32 : 0;
j = (prop = hal_get_int32(dev->prop, "pci.product_id")) ? prop->val.int32 : 0;
if(i || j) {
hd->vendor.id = MAKE_ID(TAG_PCI, i);
hd->device.id = MAKE_ID(TAG_PCI, j);
}
if((s = hal_get_useful_str(dev->prop, "pci.vendor"))) hd->vendor.name = new_str(s);
if((s = hal_get_useful_str(dev->prop, "pci.product"))) hd->device.name = new_str(s);
i = (prop = hal_get_int32(dev->prop, "pci.subsys_vendor_id")) ? prop->val.int32 : 0;
j = (prop = hal_get_int32(dev->prop, "pci.subsys_product_id")) ? prop->val.int32 : 0;
if(i || j) {
hd->sub_vendor.id = MAKE_ID(TAG_PCI, i);
hd->sub_device.id = MAKE_ID(TAG_PCI, j);
}
if((s = hal_get_useful_str(dev->prop, "pci.subsys_vendor"))) hd->sub_vendor.name = new_str(s);
if((s = hal_get_useful_str(dev->prop, "pci.subsys_product"))) hd->sub_device.name = new_str(s);
if((prop = hal_get_str(dev->prop, "linux.sysfs_path"))) hd->sysfs_id = new_str(hd_sysfs_id(prop->val.str));
if((prop = hal_get_str(dev->prop, "info.linux.driver"))) add_str_list(&hd->drivers, prop->val.str);
hd->hal_prop = dev->prop;
dev->prop = NULL;
}
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
!hd->detail ||
hd->detail->type != hd_detail_pci ||
!(pci = hd->detail->pci.data)
) continue;
pci->next = NULL;
}
hd_data->pci = NULL;
}
char *hd_hal_print_prop(hal_prop_t *prop)
{
static char *s = NULL;
str_list_t *sl;
switch(prop->type) {
case p_string:
str_printf(&s, 0, "%s = '%s'", prop->key, prop->val.str);
break;
case p_int32:
str_printf(&s, 0, "%s = %d (0x%x)", prop->key, prop->val.int32, prop->val.int32);
break;
case p_uint64:
str_printf(&s, 0, "%s = %"PRIu64"ull (0x%"PRIx64"ull)", prop->key, prop->val.uint64, prop->val.uint64);
break;
case p_double:
str_printf(&s, 0, "%s = %#g", prop->key, prop->val.d);
break;
case p_bool:
str_printf(&s, 0, "%s = %s", prop->key, prop->val.b ? "true" : "false");
break;
case p_list:
str_printf(&s, 0, "%s = { ", prop->key);
for(sl = prop->val.list; sl; sl = sl->next) {
str_printf(&s, -1, "'%s'%s", sl->str, sl->next ? ", " : "");
}
str_printf(&s, -1, " }");
break;
case p_invalid:
str_printf(&s, 0, "%s", prop->key);
break;
}
return s;
}
/*
* Ensure that udi is a sane path name.
*
* return:
* 0/1: fail/ok
*/
int check_udi(const char *udi)
{
if(
!udi ||
!strncmp(udi, "../", sizeof "../" - 1) ||
strstr(udi, "/../") ||
strstr(udi, "//")
) return 0;
return 1;
}
int hd_write_properties(const char *udi, hal_prop_t *prop)
{
FILE *f;
char *s;
f = hd_open_properties(udi, "w");
if(!f) return 1;
for(; prop; prop = prop->next) {
if(prop->type == p_invalid) continue;
s = hd_hal_print_prop(prop);
if(s) fprintf(f, "%s\n", s);
}
fclose(f);
return 0;
}
hal_prop_t *hd_read_properties(const char *udi)
{
char *path = NULL;
str_list_t *sl0, *sl;
hal_prop_t *prop_list = NULL, *prop_list_e = NULL, prop, *p;
if(!udi) return NULL;
while(*udi == '/') udi++;
if(!check_udi(udi)) return NULL;
str_printf(&path, 0, "%s/%s", hd_get_hddb_path("udi"), udi);
sl0 = read_file(path, 0, 0);
free_mem(path);
for(sl = sl0; sl; sl = sl->next) {
parse_property(&prop, sl->str);
if(prop.type != p_invalid) {
p = new_mem(sizeof *p);
*p = prop;
if(prop_list) {
prop_list_e->next = p;
prop_list_e = prop_list_e->next;
}
else {
prop_list = prop_list_e = p;
}
}
else {
prop.key = free_mem(prop.key);
}
}
free_str_list(sl0);
return prop_list;
}
FILE *hd_open_properties(const char *udi, const char *mode)
{
str_list_t *path, *sl;
struct stat sbuf;
char *dir = NULL;
int err, i;
FILE *f = NULL;
if(!udi) return f;
while(*udi == '/') udi++;
if(!check_udi(udi)) return f;
path = hd_split('/', udi);
if(!path) return f;
dir = new_str(hd_get_hddb_path("udi"));
for(err = 0, sl = path; sl->next; sl = sl->next) {
str_printf(&dir, -1, "/%s", sl->str);
i = lstat(dir, &sbuf);
if(i == -1 && errno == ENOENT) {
mkdir(dir, 0755);
i = lstat(dir, &sbuf);
}
if(i || !S_ISDIR(sbuf.st_mode)) {
err = 1;
break;
}
}
if(!err) {
str_printf(&dir, -1, "/%s", sl->str);
f = fopen(dir, mode);
}
free_mem(dir);
return f;
}
char *skip_space(char *s)
{
while(isspace(*s)) s++;
return s;
}
char *skip_non_eq_or_space(char *s)
{
while(*s && *s != '=' && !isspace(*s)) s++;
return s;
}
char *skip_nonquote(char *s)
{
while(*s && *s != '\'') s++;
return s;
}
void parse_property(hal_prop_t *prop, char *str)
{
char *s, *s1, *key, *s_val;
int l;
memset(prop, 0, sizeof *prop);
prop->type = p_invalid;
s = skip_space(str);
s = skip_non_eq_or_space(key = s);
*s++ = 0;
if(!*key) return;
s = skip_space(s);
if(*s == '=') s++;
s = skip_space(s);
prop->key = new_str(key);
if(!*s) return;
if(*s == '\'') {
s_val = s + 1;
s = strrchr(s_val, '\'');
*(s ?: s_val) = 0;
prop->type = p_string;
prop->val.str = strdup(s_val);
}
else if(*s == '{') {
s_val = skip_space(s + 1);
s1 = strrchr(s_val, '}');
if(s1) *s1 = 0;
prop->type = p_list;
while(*s_val++ == '\'') {
s = skip_nonquote(s_val);
if(*s) *s++ = 0;
add_str_list(&prop->val.list, s_val);
s_val = skip_nonquote(s);
}
}
else if(!strncmp(s, "true", 4)) {
s += 4;
prop->type = p_bool;
prop->val.b = 1;
}
else if(!strncmp(s, "false", 5)) {
s += 5;
prop->type = p_bool;
prop->val.b = 0;
}
else if(isdigit(*s) || *s == '+' || *s == '-' || *s == '.') {
*skip_non_eq_or_space(s) = 0;
if(strchr(s, '.')) {
prop->type = p_double;
prop->val.d = strtod(s, NULL);
}
else {
l = strlen(s);
if(l >= 2 && s[l - 2] == 'l' && s[l - 1] == 'l') {
prop->type = p_uint64;
s[l -= 2] = 0;
}
else {
prop->type = p_int32;
}
if(l >= 1 && s[l - 1] == 'u') s[--l] = 0;
if(prop->type == p_int32) {
prop->val.int32 = strtol(s, NULL, 0);
}
else {
prop->val.uint64 = strtoull(s, NULL, 0);
}
}
}
}
void hd_scan_hal_assign_udi(hd_data_t *hd_data)
{
hd_t *hd;
int i;
if(!hd_data->hal) return;
PROGRESS(2, 0, "assign udi");
for(i = 0; i < 3; i++) {
for(hd = hd_data->hd; hd; hd = hd->next) find_udi(hd_data, hd, i);
}
}
void find_udi(hd_data_t *hd_data, hd_t *hd, int match)
{
hal_device_t *dev;
char *h_sysfsid, *h_devname;
if(hd->udi) return;
dev = NULL;
/* device file first, thanks to usb devices */
/* based on device file */
if(
!dev &&
(
(match == 0 && hd->unix_dev_name) ||
(match == 1 && hd->unix_dev_name2) ||
(match == 2 && hd->unix_dev_names)
)
) for(dev = hd_data->hal; dev; dev = dev->next) {
h_devname = hal_get_useful_str(dev->prop, "linux.device_file");
if(!h_devname) h_devname = hal_get_useful_str(dev->prop, "block.device");
if(h_devname) {
if(match == 0 && hd->unix_dev_name && !strcmp(hd->unix_dev_name, h_devname)) break;
if(match == 1 && hd->unix_dev_name2 && !strcmp(hd->unix_dev_name2, h_devname)) break;
if(match == 2 && search_str_list(hd->unix_dev_names, h_devname)) break;
}
}
/* based on sysfs id, only once for match == 0 */
if(!dev && !match && hd->sysfs_id) for(dev = hd_data->hal; dev; dev = dev->next) {
h_sysfsid = hd_sysfs_id(hal_get_useful_str(dev->prop, "linux.sysfs_path"));
if(h_sysfsid && !strcmp(hd->sysfs_id, h_sysfsid)) break;
}
if(dev) {
hd->udi = new_str(dev->udi);
hd->hal_prop = dev->prop;
dev->prop = NULL;
}
}
/** @} */

3
src/hd/hal.h Normal file
View File

@ -0,0 +1,3 @@
void hd_scan_hal(hd_data_t *hd_data);
void hd_scan_hal_basic(hd_data_t *hd_data);
void hd_scan_hal_assign_udi(hd_data_t *hd_data);

6184
src/hd/hd.c Normal file

File diff suppressed because it is too large Load Diff

2951
src/hd/hd.h Normal file

File diff suppressed because it is too large Load Diff

244
src/hd/hd_int.h Normal file
View File

@ -0,0 +1,244 @@
#define PROC_CMDLINE "/proc/cmdline"
#define PROC_PCI_DEVICES "/proc/bus/pci/devices"
#define PROC_PCI_BUS "/proc/bus/pci"
#define PROC_CPUINFO "/proc/cpuinfo"
#define PROC_IOPORTS "/proc/ioports"
#define PROC_DMA "/proc/dma"
#define PROC_INTERRUPTS "/proc/interrupts"
#define PROC_NVRAM_22 "/proc/driver/nvram"
#define PROC_NVRAM_24 "/proc/nvram"
#define PROC_IDE "/proc/ide"
#define PROC_SCSI "/proc/scsi"
#define PROC_CDROM_INFO "/proc/sys/dev/cdrom/info"
#define PROC_NET_IF_INFO "/proc/net/dev"
#define PROC_MODULES "/proc/modules"
#define PROC_DRIVER_SERIAL "/proc/tty/driver/serial"
#define PROC_DRIVER_MACSERIAL "/proc/tty/driver/macserial"
#define PROC_PARPORT_22 "/proc/parport/" /* Final '/' is essential! */
#define PROC_PARPORT_24 "/proc/sys/dev/parport/parport"
#define PROC_KCORE "/proc/kcore"
// #define PROC_USB_DEVICES "/proc/bus/usb/devices"
#define PROC_USB_DEVICES "/proc/bus/usb/devices_please-use-sysfs-instead"
#define PROC_PROM "/proc/device-tree"
#define PROC_MEMINFO "/proc/meminfo"
#define PROC_VERSION "/proc/version"
#define PROC_ISAPNP "/proc/isapnp"
#define PROC_PARTITIONS "/proc/partitions"
#define PROC_APM "/proc/apm"
#define PROC_XEN_BALLOON "/proc/xen/balloon"
#define DEV_NVRAM "/dev/nvram"
#define DEV_PSAUX "/dev/psaux"
#define DEV_ADBMOUSE "/dev/adbmouse"
#define DEV_MEM "/dev/mem"
#define DEV_KBD "/dev/kbd"
#define DEV_CONSOLE "/dev/console"
#define DEV_OPENPROM "/dev/openprom"
#define DEV_SUNMOUSE "/dev/sunmouse"
#define DEV_MICE "/dev/input/mice"
#define DEV_FB "/dev/fb"
#define DEV_FB0 "/dev/fb0"
#define PROG_MODPROBE "/sbin/modprobe"
#define PROG_RMMOD "/sbin/rmmod"
#define PROG_CARDCTL "/sbin/cardctl"
#define PROG_UDEVINFO "/usr/bin/udevinfo"
#define PROG_UDEVADM "/usr/bin/udevadm"
#define KLOG_BOOT "/var/log/boot.msg"
#define ISAPNP_CONF "/etc/isapnp.conf"
#define KERNEL_22 0x020200
#define KERNEL_24 0x020400
#define KERNEL_26 0x020600
#if defined(__s390__) || defined(__s390x__) || defined(__alpha__) || defined(LIBHD_TINY)
#define WITH_ISDN 0
#else
#define WITH_ISDN 1
#endif
// maximum attribute size in sysfs we expect
// (this is to avoid accidentally reading unlimited data)
#define MAX_ATTR_SIZE 0x10000
#define PROGRESS(a, b, c) progress(hd_data, a, b, c)
#define ADD2LOG(a...) hd_log_printf(hd_data, a)
/*
* define to make (hd_t).unique_id a hex string, otherwise it is a
* base64-like string
*/
#undef NUMERIC_UNIQUE_ID
/*
* Internal probing module numbers. Use mod_name_by_idx() outside of libhd.
*/
enum mod_idx {
mod_none, mod_memory, mod_pci, mod_isapnp, mod_pnpdump, mod_net,
mod_floppy, mod_misc, mod_bios, mod_cpu, mod_monitor, mod_mouse, mod_scsi,
mod_serial, mod_usb, mod_adb, mod_modem, mod_parallel, mod_isa, mod_isdn,
mod_kbd, mod_prom, mod_sbus, mod_int, mod_braille, mod_xtra, mod_sys,
mod_manual, mod_fb, mod_veth, mod_pppoe, mod_pcmcia, mod_s390,
mod_sysfs, mod_dsl, mod_block, mod_edd, mod_input, mod_wlan, mod_hal
};
void *new_mem(size_t size);
void *resize_mem(void *, size_t);
void *add_mem(void *, size_t, size_t);
char *new_str(const char *);
void *free_mem(void *);
int have_common_res(hd_res_t *res1, hd_res_t *res2);
void join_res_io(hd_res_t **res1, hd_res_t *res2);
void join_res_irq(hd_res_t **res1, hd_res_t *res2);
void join_res_dma(hd_res_t **res1, hd_res_t *res2);
hd_res_t *free_res_list(hd_res_t *res);
hd_res_t *add_res_entry(hd_res_t **res, hd_res_t *new_res);
hd_t *add_hd_entry(hd_data_t *hd_data, unsigned line, unsigned count);
misc_t *free_misc(misc_t *m);
scsi_t *free_scsi(scsi_t *scsi, int free_all);
hd_detail_t *free_hd_detail(hd_detail_t *d);
devtree_t *free_devtree(hd_data_t *hd_data);
void hd_add_id(hd_data_t *hd_data, hd_t *hd);
char *isa_id2str(unsigned);
char *eisa_vendor_str(unsigned);
unsigned name2eisa_id(char *);
char *canon_str(char *, int);
int hex(char *string, int digits);
void hd_log(hd_data_t *hd_data, char *buf, ssize_t len);
void hd_log_printf(hd_data_t *hd_data, char *format, ...) __attribute__ ((format (printf, 2, 3)));
void hd_log_hex(hd_data_t *hd_data, int with_ascii, unsigned data_len, unsigned char *data);
void str_printf(char **buf, int offset, char *format, ...) __attribute__ ((format (printf, 3, 4)));
void hexdump(char **buf, int with_ascii, unsigned data_len, unsigned char *data);
str_list_t *search_str_list(str_list_t *sl, char *str);
str_list_t *add_str_list(str_list_t **sl, char *str);
str_list_t *free_str_list(str_list_t *list);
str_list_t *reverse_str_list(str_list_t *list);
str_list_t *read_file(char *file_name, unsigned start_line, unsigned lines);
str_list_t *read_dir(char *dir_name, int type);
char *hd_read_sysfs_link(char *base_dir, char *link_name);
void progress(hd_data_t *hd_data, unsigned pos, unsigned count, char *msg);
void remove_hd_entries(hd_data_t *hd_data);
void remove_tagged_hd_entries(hd_data_t *hd_data);
driver_info_t *free_driver_info(driver_info_t *di);
int str2float(char *s, int n);
char *float2str(int i, int n);
/* return the file name of a module */
char *mod_name_by_idx(unsigned idx);
int hd_timeout(void(*func)(void *), void *arg, int timeout);
str_list_t *read_kmods(hd_data_t *hd_data);
char *get_cmd_param(hd_data_t *hd_data, int field);
#ifdef __i386__
/* smp/smp.c */
int detectSMP(void);
#endif
void update_irq_usage(hd_data_t *hd_data);
int run_cmd(hd_data_t *hd_data, char *cmd);
int load_module_with_params(hd_data_t *hd_data, char *module, char *params);
int load_module(hd_data_t *hd_data, char *module);
int unload_module(hd_data_t *hd_data, char *module);
int probe_module(hd_data_t *hd_data, char *module);
int cmp_hd(hd_t *hd1, hd_t *hd2);
unsigned has_something_attached(hd_data_t *hd_data, hd_t *hd);
str_list_t *get_cmdline(hd_data_t *hd_data, char *key);
int detect_smp_bios(hd_data_t *hd_data);
int detect_smp_prom(hd_data_t *hd_data);
unsigned char *read_block0(hd_data_t *hd_data, char *dev, int *timeout);
void hd_copy(hd_t *dst, hd_t *src);
/* parameter for gather_resources(,,, which) */
#define W_IO (1 << 0)
#define W_DMA (1 << 1)
#define W_IRQ (1 << 2)
void gather_resources(misc_t *m, hd_res_t **r, char *name, unsigned which);
char *vend_id2str(unsigned vend);
int hd_getdisksize(hd_data_t *hd_data, char *dev, int fd, hd_res_t **geo, hd_res_t **size);
str_list_t *hd_split(char del, const char *str);
char *hd_join(char *del, str_list_t *str);
int is_pnpinfo(ser_device_t *mi, int ofs);
int is_pcmcia_ctrl(hd_data_t *hd_data, hd_t *hd);
void hd_fork(hd_data_t *hd_data, int timeout, int total_timeout);
void hd_fork_done(hd_data_t *hd_data);
void hd_shm_init(hd_data_t *hd_data);
void hd_shm_clean(hd_data_t *hd_data);
void hd_shm_done(hd_data_t *hd_data);
void *hd_shm_add(hd_data_t *hd_data, void *ptr, unsigned len);
int hd_is_shm_ptr(hd_data_t *hd_data, void *ptr);
void hd_move_to_shm(hd_data_t *hd_data);
void read_udevinfo(hd_data_t *hd_data);
hd_t *hd_find_sysfs_id(hd_data_t *hd_data, char *id);
hd_t *hd_find_sysfs_id_devname(hd_data_t *hd_data, char *id, char *devname);
int hd_attr_uint(char* attr, uint64_t* u, int base);
str_list_t *hd_attr_list(char *str);
char *hd_sysfs_id(char *path);
char *hd_sysfs_name2_dev(char *str);
char *hd_sysfs_dev2_name(char *str);
void hd_sysfs_driver_list(hd_data_t *hd_data);
char *hd_sysfs_find_driver(hd_data_t *hd_data, char *sysfs_id, int exact);
int hd_report_this(hd_data_t *hd_data, hd_t *hd);
str_list_t *hd_module_list(hd_data_t *hd_data, unsigned id);
char* get_sysfs_attr(const char* bus, const char* device, const char* attr);
char *get_sysfs_attr_by_path(const char *path, const char *attr);
char *get_sysfs_attr_by_path2(const char *path, const char *attr, unsigned *len);
void hd_pci_complete_data(hd_t *hd);
void hd_pci_read_data(hd_data_t *hd_data);
hal_device_t *hd_free_hal_devices(hal_device_t *dev);
char *hd_hal_print_prop(hal_prop_t *prop);
void hal_invalidate(hal_prop_t *prop);
void hal_invalidate_all(hal_prop_t *prop, const char *key);
hal_prop_t *hal_get_any(hal_prop_t *prop, const char *key);
hal_prop_t *hal_get_bool(hal_prop_t *prop, const char *key);
hal_prop_t *hal_get_int32(hal_prop_t *prop, const char *key);
hal_prop_t *hal_get_str(hal_prop_t *prop, const char *key);
hal_prop_t *hal_get_list(hal_prop_t *prop, const char *key);
char *hal_get_useful_str(hal_prop_t *prop, const char *key);
hal_device_t *hal_find_device(hd_data_t *hd_data, char *udi);
hal_prop_t *hal_add_new(hal_prop_t **prop);
char *hd_get_hddb_dir(void);
char *hd_get_hddb_path(char *sub);
int hd_mod_cmp(char *str1, char *str2);
int get_probe_val_int(hd_data_t *hd_data, enum probe_feature feature);
char *get_probe_val_str(hd_data_t *hd_data, enum probe_feature feature);
str_list_t *get_probe_val_list(hd_data_t *hd_data, enum probe_feature feature);
str_list_t *sort_str_list(str_list_t *sl0, int (*cmp_func)(const void *, const void *));
#ifdef __cplusplus
}
#endif

3108
src/hd/hddb.c Normal file

File diff suppressed because it is too large Load Diff

7
src/hd/hddb.h Normal file
View File

@ -0,0 +1,7 @@
void hddb_init(hd_data_t *hd_data);
unsigned device_class(hd_data_t *hd_data, unsigned vendor, unsigned device);
unsigned sub_device_class(hd_data_t *hd_data, unsigned vendor, unsigned device, unsigned sub_vendor, unsigned sub_device);
char *hid_tag_name(int tag);
char *hid_tag_name2(int tag);

56
src/hd/hddb_int.h Normal file
View File

@ -0,0 +1,56 @@
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* type defs for internal data base
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#define DATA_VALUE(a) ((a) & ~(-1 << 28))
#define DATA_FLAG(a) (((a) >> 28) & 0xf)
#define MAKE_DATA(a, b) ((a << 28) | (b))
#define FLAG_ID 0
#define FLAG_RANGE 1
#define FLAG_MASK 2
#define FLAG_STRING 3
#define FLAG_REGEXP 4
/* 5 - 7 reserved */
#define FLAG_CONT 8 /* bit mask, _must_ be bit 31 */
typedef enum hddb_entry_e {
he_other, he_bus_id, he_baseclass_id, he_subclass_id, he_progif_id,
he_vendor_id, he_device_id, he_subvendor_id, he_subdevice_id, he_rev_id,
he_bus_name, he_baseclass_name, he_subclass_name, he_progif_name,
he_vendor_name, he_device_name, he_subvendor_name, he_subdevice_name,
he_rev_name, he_serial, he_driver, he_requires,
he_detail_ccw_data_cu_model, he_hwclass /* 23 */,
/* add new entries _here_! */
he_nomask,
/* if he_nomask exceeds 31, adjust entry_mask_t & hddb_entry_mask_t */
he_class_id = he_nomask, he_driver_module_insmod, he_driver_module_modprobe,
he_driver_module_config, he_driver_xfree, he_driver_xfree_config,
he_driver_mouse, he_driver_display, he_driver_any
} hddb_entry_t;
static hddb_entry_t hddb_is_numeric[] = {
he_bus_id, he_baseclass_id, he_subclass_id, he_progif_id, he_vendor_id,
he_device_id, he_subvendor_id, he_subdevice_id, he_rev_id,
he_detail_ccw_data_cu_model, he_hwclass
};
static char *hddb_entry_strings[] = {
"other", "bus.id", "baseclass.id", "subclass.id", "progif.id",
"vendor.id", "device.id", "subvendor.id", "subdevice.id", "rev.id",
"bus.name", "baseclass.name", "subclass.name", "progif.name",
"vendor.name", "device.name", "subvendor.name", "subdevice.name",
"rev.name", "serial", "driver", "requires",
"detail.ccw.data.cu_model", "hwclass",
"class.id", "driver.module.insmod", "driver.module.modprobe",
"driver.module.config", "driver.xfree", "driver.xfree.config",
"driver.mouse", "driver.display", "driver.any"
};

1383
src/hd/hdp.c Normal file

File diff suppressed because it is too large Load Diff

0
src/hd/hdp.h Normal file
View File

96
src/hd/hwclass_names.h Normal file
View File

@ -0,0 +1,96 @@
typedef struct {
int key;
char *value;
} hash_t;
static char *key2value(hash_t *hash, int id);
static int value2key(hash_t *hash, char *str);
/* corresponds to hd_hw_item_t */
static hash_t hw_items[] = {
{ hw_sys, "system" },
{ hw_cpu, "cpu" },
{ hw_keyboard, "keyboard" },
{ hw_braille, "braille" },
{ hw_mouse, "mouse" },
{ hw_joystick, "joystick" },
{ hw_printer, "printer" },
{ hw_scanner, "scanner" },
{ hw_chipcard, "chipcard" },
{ hw_monitor, "monitor" },
{ hw_tv, "tv card" },
{ hw_display, "graphics card" },
{ hw_framebuffer, "framebuffer" },
{ hw_camera, "camera" },
{ hw_sound, "sound" },
{ hw_storage_ctrl, "storage" },
{ hw_network_ctrl, "network" },
{ hw_isdn, "isdn adapter" },
{ hw_modem, "modem" },
{ hw_network, "network interface" },
{ hw_disk, "disk" },
{ hw_partition, "partition" },
{ hw_cdrom, "cdrom" },
{ hw_floppy, "floppy" },
{ hw_manual, "manual" },
{ hw_usb_ctrl, "usb controller" },
{ hw_usb, "usb" },
{ hw_bios, "bios" },
{ hw_pci, "pci" },
{ hw_isapnp, "isapnp" },
{ hw_bridge, "bridge" },
{ hw_hub, "hub" },
{ hw_scsi, "scsi" },
{ hw_ide, "ide" },
{ hw_memory, "memory" },
{ hw_dvb, "dvb card" },
{ hw_pcmcia, "pcmcia" },
{ hw_pcmcia_ctrl, "pcmcia controller" },
{ hw_ieee1394, "firewire" },
{ hw_ieee1394_ctrl, "firewire controller" },
{ hw_hotplug, "hotplug" },
{ hw_hotplug_ctrl, "hotplug controller" },
{ hw_zip, "zip" },
{ hw_pppoe, "pppoe" },
{ hw_wlan, "wlan card" },
{ hw_redasd, "redasd" },
{ hw_dsl, "dsl adapter" },
{ hw_block, "block device" },
{ hw_tape, "tape" },
{ hw_vbe, "vesa bios" },
{ hw_bluetooth, "bluetooth" },
{ hw_unknown, "unknown" },
{ 0, NULL }
};
int value2key(hash_t *hash, char *str)
{
for(; hash->value; hash++) {
if(!strcmp(hash->value, str)) break;
}
return hash->key;
}
char *key2value(hash_t *hash, int id)
{
for(; hash->value; hash++) {
if(hash->key == id) break;
}
return hash->value;
}
char *hd_hw_item_name(hd_hw_item_t item)
{
return key2value(hw_items, item);
}
hd_hw_item_t hd_hw_item_type(char *name)
{
return name ? value2key(hw_items, name) : hw_none;
}

9486
src/hd/ibm-notebooks.h Normal file

File diff suppressed because it is too large Load Diff

456
src/hd/input.c Normal file
View File

@ -0,0 +1,456 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/input.h>
#include "hd.h"
#include "hd_int.h"
#include "hddb.h"
#include "input.h"
/**
* @defgroup INPUTint Input devices
* @ingroup libhdDEVint
* @brief Input device scan functions
*
* @{
*/
static void get_input_devices(hd_data_t *hd_data);
static char *all_bits(char *str);
static int test_bit(const char *str, unsigned bit);
void hd_scan_input(hd_data_t *hd_data)
{
if(!hd_probe_feature(hd_data, pr_input)) return;
hd_data->module = mod_input;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(1, 0, "joydev mod");
load_module(hd_data, "joydev");
PROGRESS(1, 1, "evdev mod");
load_module(hd_data, "evdev");
PROGRESS(2, 0, "input");
get_input_devices(hd_data);
}
// note: hd_data parameter is needed for ADD2LOG macro
void add_joystick_details(hd_data_t *hd_data, hd_t *h, const char *key, const char *abso)
{
// replace existing details
if (h->detail)
{
free_hd_detail(h->detail);
}
// add buttons and axis details
h->detail = new_mem(sizeof *h->detail);
h->detail->type = hd_detail_joystick;
joystick_t *jt = new_mem(sizeof jt);
unsigned u;
if(key) {
for(u = BTN_JOYSTICK; u < BTN_JOYSTICK + 16; u++) {
if(test_bit(key, u)) jt->buttons++;
}
}
ADD2LOG(" joystick buttons = %u\n", jt->buttons);
if(abso) {
for(u = ABS_X; u < ABS_VOLUME; u++) {
if(test_bit(abso, u)) jt->axes++;
}
}
ADD2LOG(" joystick axes = %u\n", jt->axes);
h->detail->joystick.data = jt;
}
#define INP_NAME "N: Name="
#define INP_HANDLERS "H: Handlers="
#define INP_KEY "B: KEY="
#define INP_REL "B: REL="
#define INP_ABS "B: ABS="
void get_input_devices(hd_data_t *hd_data)
{
hd_t *hd;
str_list_t *input, *sl, *sl1;
char *s;
unsigned ok, u, is_mouse, is_joystick;
unsigned bus, vendor, product, version;
unsigned mouse_buttons, mouse_wheels;
char *name = NULL, *handlers = NULL, *key = NULL, *rel = NULL, *abso = NULL;
size_t len;
str_list_t *handler_list;
hd_dev_num_t dev_num = { type: 'c', range: 1 };
input = read_file("/proc/bus/input/devices", 0, 0);
ADD2LOG("----- /proc/bus/input/devices -----\n");
for(sl = input; sl; sl = sl->next) {
ADD2LOG(" %s", sl->str);
}
ADD2LOG("----- /proc/bus/input/devices end -----\n");
for(ok = 0, sl = input; sl; sl = sl->next) {
if(*sl->str == '\n') {
ADD2LOG("bus = %u, name = %s\n", bus, name);
if(handlers) ADD2LOG(" handlers = %s\n", handlers);
if(key) ADD2LOG(" key = %s\n", key);
if(rel) ADD2LOG(" rel = %s\n", rel);
if(abso) ADD2LOG(" abs = %s\n", abso);
mouse_buttons = 0;
if(key) {
for(u = BTN_MOUSE; u < BTN_MOUSE + 8; u++) {
if(test_bit(key, u)) mouse_buttons++;
}
}
ADD2LOG(" mouse buttons = %u\n", mouse_buttons);
mouse_wheels = 0;
if(rel) {
for(u = REL_HWHEEL; u <= REL_MAX; u++) {
if(test_bit(rel, u)) mouse_wheels++;
}
}
ADD2LOG(" mouse wheels = %u\n", mouse_wheels);
if(ok && handlers) {
handler_list = hd_split(' ', handlers);
is_mouse = strstr(handlers, "mouse") ? 1 : 0;
is_joystick = strstr(handlers, "js") ? 1 : 0;
if( // HP Virtual Management Device
vendor == 0x03f0 &&
product == 0x1126 &&
mouse_buttons >= 3
) is_mouse = 1;
ADD2LOG(" is_mouse = %u\n", is_mouse);
ADD2LOG(" is_joystick = %u\n", is_joystick);
if(bus == BUS_USB) {
s = NULL;
for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "mouse%u", &u) == 1) {
str_printf(&s, 0, "/dev/input/mouse%u", u);
break;
}
}
if(!s && (is_mouse || is_joystick)) for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "event%u", &u) == 1) {
str_printf(&s, 0, "/dev/input/event%u", u);
break;
}
}
if(s) {
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
(hd->unix_dev_name2 && !strcmp(hd->unix_dev_name2, s)) ||
(hd->unix_dev_name && !strcmp(hd->unix_dev_name, s))
) {
if(!hd->base_class.id) {
if (is_mouse)
{
hd->base_class.id = bc_mouse;
hd->sub_class.id = sc_mou_usb;
}
else if (is_joystick)
{
hd->base_class.id = bc_joystick;
}
}
if (is_mouse)
{
hd_set_hw_class(hd, hw_mouse);
hd->compat_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0210);
hd->compat_device.id = MAKE_ID(TAG_SPECIAL, (mouse_wheels << 4) + mouse_buttons);
}
else if (is_joystick)
{
hd_set_hw_class(hd, hw_joystick);
/* add buttons and axis details */
add_joystick_details(hd_data, hd, key, abso);
}
if(hd->unix_dev_name) add_str_list(&hd->unix_dev_names, hd->unix_dev_name);
if(hd->unix_dev_name2) add_str_list(&hd->unix_dev_names, hd->unix_dev_name2);
for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "event%u", &u) == 1) {
str_printf(&s, 0, "/dev/input/event%u", u);
if(!search_str_list(hd->unix_dev_names, s)) add_str_list(&hd->unix_dev_names, s);
s = free_mem(s);
}
/* add /dev/input/jsN joystick device name */
else if (is_joystick)
{
if(sscanf(sl1->str, "js%u", &u) == 1) {
str_printf(&s, 0, "/dev/input/js%u", u);
if(!search_str_list(hd->unix_dev_names, s)) add_str_list(&hd->unix_dev_names, s);
if(!hd->unix_dev_name2) hd->unix_dev_name2 = new_str(s);
s = free_mem(s);
}
}
}
break;
}
}
}
s = free_mem(s);
}
else {
// keyboard
if(search_str_list(handler_list, "kbd") && test_bit(key, KEY_1)) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_keyboard;
hd->sub_class.id = sc_keyboard_kbd;
hd->bus.id = bus_ps2;
hd->vendor.id = MAKE_ID(0, vendor);
hd->device.id = MAKE_ID(0, product);
hd->compat_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0211);
hd->compat_device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
hd->device.name = new_str(name);
for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "event%u", &u) == 1) {
str_printf(&hd->unix_dev_name, 0, "/dev/input/event%u", u);
dev_num.major = 13;
dev_num.minor = 64 + u;
hd->unix_dev_num = dev_num;
break;
}
}
}
// mouse
else if(is_mouse) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->vendor.id = MAKE_ID(0, vendor);
hd->device.id = MAKE_ID(0, product);
hd->compat_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0210);
hd->compat_device.id = MAKE_ID(TAG_SPECIAL, (mouse_wheels << 4) + mouse_buttons);
hd->base_class.id = bc_mouse;
if(bus == BUS_ADB) {
hd->sub_class.id = sc_mou_bus;
hd->bus.id = bus_adb;
}
else {
hd->sub_class.id = sc_mou_ps2;
hd->bus.id = bus_ps2;
}
hd->device.name = new_str(name);
#if 0
/* Synaptics/Alps TouchPad */
if(
vendor == 2 &&
(product == 7 || product == 8) &&
test_bit(abso, ABS_X) &&
test_bit(abso, ABS_Y) &&
test_bit(abso, ABS_PRESSURE) &&
test_bit(key, BTN_TOOL_FINGER)
) {
}
#endif
hd->unix_dev_name = new_str(DEV_MICE);
dev_num.major = 13;
dev_num.minor = 63;
hd->unix_dev_num = dev_num;
for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "mouse%u", &u) == 1) {
str_printf(&hd->unix_dev_name2, 0, "/dev/input/mouse%u", u);
dev_num.major = 13;
dev_num.minor = 32 + u;
hd->unix_dev_num2 = dev_num;
break;
}
}
add_str_list(&hd->unix_dev_names, hd->unix_dev_name);
add_str_list(&hd->unix_dev_names, hd->unix_dev_name2);
for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "event%u", &u) == 1) {
s = NULL;
str_printf(&s, 0, "/dev/input/event%u", u);
add_str_list(&hd->unix_dev_names, s);
s = free_mem(s);
break;
}
}
}
// joystick
else if(is_joystick) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->vendor.id = MAKE_ID(0, vendor);
hd->device.id = MAKE_ID(0, product);
hd_set_hw_class(hd, hw_joystick);
hd->base_class.id = bc_joystick;
hd->device.name = new_str(name);
// gameport? (see <linux/input.h>)
if (bus == BUS_GAMEPORT) hd->bus.id = bus_gameport;
/* add buttons and axis details */
add_joystick_details(hd_data, hd, key, abso);
// add eventX device
for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "event%u", &u) == 1) {
str_printf(&hd->unix_dev_name, 0, "/dev/input/event%u", u);
dev_num.major = 13;
dev_num.minor = 64 + u;
hd->unix_dev_num = dev_num;
break;
}
}
// add jsX device
for(sl1 = handler_list; sl1; sl1 = sl1->next) {
if(sscanf(sl1->str, "js%u", &u) == 1) {
str_printf(&hd->unix_dev_name2, 0, "/dev/input/js%u", u);
break;
}
}
add_str_list(&hd->unix_dev_names, hd->unix_dev_name);
add_str_list(&hd->unix_dev_names, hd->unix_dev_name2);
}
else
{
ADD2LOG("unknown non-USB input device\n");
}
}
handler_list = free_str_list(handler_list);
}
ok = 0;
name = free_mem(name);
handlers = free_mem(handlers);
key = free_mem(key);
rel = free_mem(rel);
abso = free_mem(abso);
}
if(sscanf(sl->str, "I: Bus=%04x Vendor=%04x Product=%04x Version=%04x", &bus, &vendor, &product, &version) == 4) {
ok = 1;
continue;
}
if(!strncmp(sl->str, INP_NAME, sizeof INP_NAME - 1)) {
s = sl->str + sizeof INP_NAME;
len = strlen(s);
if(len > 2) {
name = canon_str(s, len - 2);
}
continue;
}
if(!strncmp(sl->str, INP_HANDLERS, sizeof INP_HANDLERS - 1)) {
s = sl->str + sizeof INP_HANDLERS - 1;
handlers = canon_str(s, strlen(s));
continue;
}
if(!strncmp(sl->str, INP_KEY, sizeof INP_KEY - 1)) {
s = sl->str + sizeof INP_KEY - 1;
key = canon_str(s, strlen(s));
key = all_bits(key);
continue;
}
if(!strncmp(sl->str, INP_REL, sizeof INP_REL - 1)) {
s = sl->str + sizeof INP_REL - 1;
rel = canon_str(s, strlen(s));
rel = all_bits(rel);
continue;
}
if(!strncmp(sl->str, INP_ABS, sizeof INP_ABS - 1)) {
s = sl->str + sizeof INP_ABS - 1;
abso = canon_str(s, strlen(s));
abso = all_bits(abso);
continue;
}
}
free_str_list(input);
}
char *all_bits(char *str)
{
str_list_t *sl, *sl0;
char *s = NULL;
unsigned long u;
if(!str) return NULL;
sl = sl0 = hd_split(' ', str);
for(; sl; sl = sl->next) {
u = strtoul(sl->str, NULL, 16);
str_printf(&s, -1, "%0*lx", (int) sizeof (unsigned long) * 2, u);
}
free_str_list(sl0);
free_mem(str);
return s;
}
int test_bit(const char *str, unsigned bit)
{
size_t len, ofs;
unsigned u, mask;
if(!str) return 0;
len = strlen(str);
ofs = bit >> 2;
mask = 1 << (bit & 3);
if(ofs >= len) return 0;
ofs = len - ofs - 1;
u = str[ofs] >= 'a' ? str[ofs] - 'a' + 10 : str[ofs] - '0';
return (u & mask) ? 1 : 0;
}
/** @} */

1
src/hd/input.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_input(hd_data_t *hd_data);

1349
src/hd/int.c Normal file

File diff suppressed because it is too large Load Diff

1
src/hd/int.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_int(hd_data_t *hd_data);

118
src/hd/isa.c Normal file
View File

@ -0,0 +1,118 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hd.h"
#include "hd_int.h"
#include "isa.h"
/**
* @defgroup ISAint ISA
* @ingroup libhdBUSint
* @brief ISA bus scan functions
*
* @{
*/
#if defined(__i386__)
static void scan_isa_isdn(hd_data_t *hd_data);
static isa_isdn_t *free_isa_isdn(isa_isdn_t *ii);
static void dump_isa_isdn_data(hd_data_t *hd_data, isa_isdn_t *ii);
void hd_scan_isa(hd_data_t *hd_data)
{
if(!hd_probe_feature(hd_data, pr_isa)) return;
hd_data->module = mod_isa;
/* some clean-up */
remove_hd_entries(hd_data);
// hd_data->isa = NULL;
if(hd_probe_feature(hd_data, pr_isa_isdn)) {
scan_isa_isdn(hd_data);
}
}
void scan_isa_isdn(hd_data_t *hd_data)
{
isa_isdn_t *ii0, *ii;
hd_t *hd;
hd_res_t *res;
PROGRESS(1, 0, "isdn");
ii0 = isdn_detect();
dump_isa_isdn_data(hd_data, ii0);
PROGRESS(1, 1, "isdn");
for(ii = ii0; ii; ii = ii->next) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->base_class.id = bc_isdn;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x3000 + ii->type);
hd->device.id = MAKE_ID(TAG_SPECIAL, ((ii->type << 8) + (ii->subtype & 0xff)) & 0xffff);
if(ii->has_io) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = ii->io;
res->io.access = acc_rw;
}
if(ii->has_irq) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->irq.type = res_irq;
res->irq.enabled = 1;
res->irq.base = ii->irq;
}
// #### ask libihw? -> isdn.c
}
free_isa_isdn(ii0);
}
isa_isdn_t *new_isa_isdn(isa_isdn_t **ii)
{
while(*ii) ii = &(*ii)->next;
return *ii = new_mem(sizeof **ii);
}
isa_isdn_t *free_isa_isdn(isa_isdn_t *ii)
{
isa_isdn_t *l;
for(; ii; ii = (l = ii)->next, free_mem(l));
return NULL;
}
void dump_isa_isdn_data(hd_data_t *hd_data, isa_isdn_t *ii)
{
ADD2LOG("---------- ISA ISDN raw data ----------\n");
for(; ii; ii = ii->next) {
ADD2LOG(" type %d, subtype %d", ii->type, ii->subtype);
if(ii->has_mem) ADD2LOG(", mem 0x%04x", ii->mem);
if(ii->has_io) ADD2LOG(", io 0x%04x", ii->io);
if(ii->has_irq) ADD2LOG(", irq %d", ii->irq);
ADD2LOG("\n");
}
ADD2LOG("---------- ISA ISDN raw data end ----------\n");
}
#endif /* defined(__i386__) */
/** @} */

12
src/hd/isa.h Normal file
View File

@ -0,0 +1,12 @@
typedef struct isa_isdn_s {
struct isa_isdn_s *next;
unsigned has_mem:1, has_io:1, has_irq:1;
unsigned type, subtype, mem, io, irq;
} isa_isdn_t;
isa_isdn_t *new_isa_isdn(isa_isdn_t **ii);
void hd_scan_isa(hd_data_t *hd_data);
isa_isdn_t *isdn_detect(void);

427
src/hd/isapnp.c Normal file
View File

@ -0,0 +1,427 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hd.h"
#include "hd_int.h"
#include "hddb.h"
#include "isapnp.h"
/**
* @defgroup ISAPnPint ISA PnP
* @ingroup libhdBUSint
* @brief ISA PnP scan functions
*
* @{
*/
#if defined(__i386__) || defined(__alpha__)
static void get_pnp_devs(hd_data_t *hd_data);
#if 0
static void get_read_port(hd_data_t *hd_data, isapnp_t *);
static void build_list(hd_data_t *hd_data, str_list_t *isapnp_list);
#endif
void hd_scan_isapnp(hd_data_t *hd_data)
{
#if 0
hd_t *hd;
hd_res_t *res;
int isapnp_ok;
str_list_t *isapnp_list = NULL, *sl;
#endif
if(!hd_probe_feature(hd_data, pr_isapnp)) return;
hd_data->module = mod_isapnp;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(1, 0, "pnp devices");
get_pnp_devs(hd_data);
#if 0
PROGRESS(1, 0, "read port");
if(!hd_data->isapnp) {
hd_data->isapnp = new_mem(sizeof *hd_data->isapnp);
}
else {
hd_data->isapnp->cards = 0;
/* just in case... */
hd_data->isapnp->card = free_mem(hd_data->isapnp->card);
/* keep the port */
}
if(!hd_data->isapnp->read_port) get_read_port(hd_data, hd_data->isapnp);
PROGRESS(3, 0, "get pnp data");
isapnp_list = read_file(PROC_ISAPNP, 0, 0);
if((hd_data->debug & HD_DEB_ISAPNP)) {
ADD2LOG("----- %s -----\n", PROC_ISAPNP);
for(sl = isapnp_list; sl; sl = sl->next) {
ADD2LOG(" %s", sl->str);
}
ADD2LOG("----- %s end -----\n", PROC_ISAPNP);
}
isapnp_ok = isapnp_list && hd_data->isapnp->read_port ? 1 : 1;
PROGRESS(4, 0, "build list");
if(isapnp_ok) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->base_class.id = bc_internal;
hd->sub_class.id = sc_int_isapnp_if;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = ISAPNP_ADDR_PORT;
res->io.range = 1;
res->io.access = acc_wo;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = ISAPNP_DATA_PORT;
res->io.range = 1;
res->io.access = acc_wo;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = hd_data->isapnp->read_port;
res->io.range = 1;
res->io.access = acc_ro;
}
build_list(hd_data, isapnp_list);
free_str_list(isapnp_list);
#endif
}
void get_pnp_devs(hd_data_t *hd_data)
{
hd_t *hd;
char *s, *t, buf[4];
unsigned u1, u2, u3;
str_list_t *sf_bus, *sf_bus_e;
char *sf_dev, *sf_dev2;
sf_bus = read_dir("/sys/bus/pnp/devices", 'l');
if(!sf_bus) {
ADD2LOG("sysfs: no such bus: pnp\n");
return;
}
for(sf_bus_e = sf_bus; sf_bus_e; sf_bus_e = sf_bus_e->next) {
sf_dev = new_str(hd_read_sysfs_link("/sys/bus/pnp/devices", sf_bus_e->str));
ADD2LOG(
" pnp device: name = %s\n path = %s\n",
sf_bus_e->str,
hd_sysfs_id(sf_dev)
);
if((s = get_sysfs_attr_by_path(sf_dev, "id"))) {
if(sscanf(s, "%3s%4x", buf, &u1) == 2 && (u2 = name2eisa_id(buf))) {
ADD2LOG(" id = %s %04x\n", eisa_vendor_str(u2), u1);
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->sysfs_id = new_str(hd_sysfs_id(sf_dev));
hd->sysfs_bus_id = new_str(sf_bus_e->str);
hd->bus.id = bus_isa;
hd->is.isapnp = 1;
hd->sub_vendor.id = u2;
hd->sub_device.id = MAKE_ID(TAG_EISA, u1);
if(sscanf(hd->sysfs_bus_id, "%2x:%2x.%2x", &u1, &u2, &u3) == 3) {
hd->slot = u2;
hd->func = u3;
}
sf_dev2 = new_str(sf_dev);
if((t = strrchr(sf_dev2, '/'))) *t = 0;
if((t = get_sysfs_attr_by_path(sf_dev2, "card_id"))) {
if(sscanf(t, "%3s%4x", buf, &u1) == 2 && (u2 = name2eisa_id(buf))) {
ADD2LOG(" card id = %s %04x\n", eisa_vendor_str(u2), u1);
hd->vendor.id = u2;
hd->device.id = MAKE_ID(TAG_EISA, u1);
}
}
if((t = get_sysfs_attr_by_path(sf_dev2, "name"))) {
hd->device.name = canon_str(t, strlen(t));
if(!strcasecmp(hd->device.name, "unknown")) {
hd->device.name = free_mem(hd->device.name);
}
}
free_mem(sf_dev2);
if(hd->sub_vendor.id == hd->vendor.id && hd->sub_device.id == hd->device.id) {
hd->sub_vendor.id = hd->sub_device.id = 0;
}
}
}
free_mem(sf_dev);
}
free_str_list(sf_bus);
}
#if 0
unsigned char *add_isapnp_card_res(isapnp_card_t *ic, int len, int type)
{
ic->res = add_mem(ic->res, sizeof *ic->res, ic->res_len);
ic->res[ic->res_len].len = len;
ic->res[ic->res_len].type = type;
ic->res[ic->res_len].data = new_mem(len);
if(type == RES_LOG_DEV_ID) { /* logical device id */
ic->log_devs++;
}
return ic->res[ic->res_len++].data;
}
isapnp_card_t *add_isapnp_card(isapnp_t *ip, int csn)
{
isapnp_card_t *c;
ip->card = add_mem(ip->card, sizeof *ip->card, ip->cards);
c = ip->card + ip->cards++;
c->csn = csn;
c->serial = new_mem(sizeof *c->serial * 8);
c->card_regs = new_mem(sizeof *c->card_regs * 0x30);
return c;
}
void get_read_port(hd_data_t *hd_data, isapnp_t *p)
{
hd_res_t *res;
p->read_port = 0;
res = NULL;
gather_resources(hd_data->misc, &res, "ISAPnP", W_IO);
if(res && res->any.type == res_io) p->read_port = res->io.base;
free_res_list(res);
}
void build_list(hd_data_t *hd_data, str_list_t *isapnp_list)
{
hd_t *hd = NULL;
str_list_t *sl;
char s1[4], s2[100];
int card, ldev, cdev_id, ldev_active = 0;
char *dev_name = NULL, *ldev_name = NULL;
unsigned dev_id = 0, vend_id = 0, base_class = 0, sub_class = 0, ldev_id;
unsigned u, ux[5];
int i, j;
hd_res_t *res;
for(sl = isapnp_list; sl; sl = sl->next) {
if(sscanf(sl->str, "Card %d '%3s%4x:%99[^']", &card, s1, &dev_id, s2) == 4) {
// ADD2LOG("\n\n** card %d >%s< %04x >%s<**\n", card, s1, dev_id, s2);
dev_name = free_mem(dev_name);
if(strcmp(s2, "Unknown")) dev_name = new_str(s2);
dev_id = MAKE_ID(TAG_EISA, dev_id);
vend_id = name2eisa_id(s1);
base_class = sub_class = 0;
if((u = device_class(hd_data, vend_id, dev_id))) {
base_class = u >> 8;
sub_class = u & 0xff;
}
#if 0
// ########## FIXME
if(
(ID_VALUE(vend_id) || ID_VALUE(dev_id)) &&
!((db_name = hd_device_name(hd_data, vend_id, dev_id)) && *db_name)
) {
if(dev_name) {
add_device_name(hd_data, vend_id, dev_id, dev_name);
}
}
#endif
continue;
}
if(sscanf(sl->str, " Logical device %d '%3s%4x:%99[^']", &ldev, s1, &ldev_id, s2) == 4) {
// ADD2LOG("\n\n** ldev %d >%s< %04x >%s<**\n", ldev, s1, ldev_id, s2);
ldev_name = free_mem(ldev_name);
if(strcmp(s2, "Unknown")) ldev_name = new_str(s2);
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->is.isapnp = 1;
hd->slot = card;
hd->func = ldev;
hd->vendor.id = vend_id;
hd->device.id = dev_id;
hd->base_class.id = base_class;
hd->sub_class.id = sub_class;
hd->sub_device.id = MAKE_ID(TAG_EISA, ldev_id);
hd->sub_vendor.id = name2eisa_id(s1);
if(hd->sub_vendor.id == hd->vendor.id && hd->sub_device.id == hd->device.id) {
hd->sub_vendor.id = hd->sub_device.id = 0;
}
if((u = sub_device_class(hd_data, hd->vendor.id, hd->device.id, hd->sub_vendor.id, hd->sub_device.id))) {
hd->base_class.id = u >> 8;
hd->sub_class.id = u & 0xff;
}
#if 0
# ############# FIXME
if(
(ID_VALUE(hd->sub_vendor.id) || ID_VALUE(hd->sub_device.id)) &&
!hd_sub_device_name(hd_data, hd->vend, hd->dev, hd->sub_vend, hd->sub_device.id)
) {
if(ldev_name) {
add_sub_device_name(hd_data, hd->vend, hd->dev, hd->sub_vend, hd->sub_device.id, ldev_name);
}
}
#endif
continue;
}
if(strstr(sl->str, "Device is not active")) {
ldev_active = 0;
continue;
}
if(strstr(sl->str, "Device is active")) {
ldev_active = 1;
continue;
}
if(hd && sscanf(sl->str, " Compatible device %3s%4x", s1, &cdev_id) == 2) {
// ADD2LOG("\n\n** cdev >%s< %04x **\n", s1, cdev_id);
hd->compat_device.id = MAKE_ID(TAG_EISA, cdev_id);
hd->compat_vendor.id = name2eisa_id(s1);
if(!(hd->base_class.id || hd->sub_class.id)) {
if((u = device_class(hd_data, hd->compat_vendor.id, hd->compat_device.id))) {
hd->base_class.id = u >> 8;
hd->sub_class.id = u & 0xff;
}
else if(hd->compat_vendor.id == MAKE_ID(TAG_EISA, 0x41d0)) {
/* 0x41d0 is 'PNP' */
switch((hd->compat_device.id >> 12) & 0xf) {
case 8:
hd->base_class.id = bc_network;
hd->sub_class.id = 0x80;
break;
case 0xa:
hd->base_class.id = bc_storage;
hd->sub_class.id = 0x80;
break;
case 0xb:
hd->base_class.id = bc_multimedia;
hd->sub_class.id = 0x80;
break;
case 0xc:
case 0xd:
hd->base_class.id = bc_modem;
break;
}
}
}
continue;
}
if(
hd &&
(j = sscanf(sl->str,
" Active port %x, %x, %x, %x, %x, %x",
ux, ux + 1, ux + 2, ux + 3, ux + 4, ux + 5
)) >= 1
) {
for(i = 0; i < j; i++) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = ldev_active ? 1 : 0;
res->io.base = ux[i];
res->io.access = acc_rw;
}
continue;
}
if(hd && (j = sscanf(sl->str, " Active IRQ %d [%x], %d [%x]", ux, ux + 1, ux + 2, ux + 3)) >= 1) {
for(i = 0; i < j; i += 2) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->irq.type = res_irq;
res->irq.enabled = ldev_active ? 1 : 0;
res->irq.base = ux[i];
}
continue;
}
if(hd && (j = sscanf(sl->str, " Active DMA %d, %d", ux, ux + 1)) >= 1) {
for(i = 0; i < j; i++) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->dma.type = res_dma;
res->dma.enabled = ldev_active ? 1 : 0;
res->dma.base = ux[i];
}
continue;
}
}
free_mem(dev_name);
free_mem(ldev_name);
}
#endif
#endif /* defined(__i386__) || defined(__alpha__) */
/** @} **/

57
src/hd/isapnp.h Normal file
View File

@ -0,0 +1,57 @@
/*
* These are fixed and write only. Reads are done from a port with a
* relocatable address...
*/
#define ISAPNP_ADDR_PORT 0x279
#define ISAPNP_DATA_PORT 0xa79
/*
* ISA PnP resource types
*/
#define RES_PNP_VERSION 0x01
#define RES_LOG_DEV_ID 0x02
#define RES_COMPAT_DEV_ID 0x03
#define RES_IRQ 0x04
#define RES_DMA 0x05
#define RES_START_DEP 0x06
#define RES_END_DEP 0x07
#define RES_IO 0x08
#define RES_FIXED_IO 0x09
#define RES_VENDOR_SMALL 0x0e
#define RES_END 0x0f
#define RES_MEM_RANGE 0x81
#define RES_ANSI_NAME 0x82
#define RES_UNICODE_NAME 0x83
#define RES_VENDOR_LARGE 0x84
#define RES_MEM32_RANGE 0x85
#define RES_FIXED_MEM32_RANGE 0x86
/*
* ISA PnP configuration regs
*/
#define CFG_MEM24 0x40
#define CFG_MEM32_0 0x76
#define CFG_MEM32_1 0x80
#define CFG_MEM32_2 0x90
#define CFG_MEM32_3 0xa0
#define CFG_IO_HI_BASE 0x60
#define CFG_IO_LO_BASE 0x61
#define CFG_IRQ 0x70
#define CFG_IRQ_TYPE 0x71
#define CFG_DMA 0x74
/* gather ISA-PnP info */
void hd_scan_isapnp(hd_data_t *hd_data);
/*
* Interface functions to the pnpdump lib.
*/
int pnpdump(hd_data_t *hd_data, int read_boards);
unsigned char *add_isapnp_card_res(isapnp_card_t *, int, int);
isapnp_card_t *add_isapnp_card(isapnp_t *, int);

315
src/hd/isdn.c Normal file
View File

@ -0,0 +1,315 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hd.h"
#include "hd_int.h"
#include "isdn.h"
#undef ISDN_TEST
/**
* @defgroup ISDNint ISDN devices
* @ingroup libhdDEVint
* @brief ISDN identify functions
*
* @{
*/
#ifndef LIBHD_TINY
#if !defined(__s390__) && !defined(__s390x__) && !defined(__alpha__)
void hd_scan_isdn(hd_data_t *hd_data)
{
hd_t *hd;
cdb_isdn_card *cic;
if(!hd_probe_feature(hd_data, pr_isdn)) return;
hd_data->module = mod_isdn;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(1, 0, "list");
#ifdef ISDN_TEST
{
hd_res_t *res;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->base_class.id = bc_isdn;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x3005);
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0500); // type, subtype
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = 0x0300;
res->io.access = acc_rw;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->base_class.id = bc_isdn;
hd->vendor.id = MAKE_ID(TAG_EISA, 0x1593);
hd->device.id = MAKE_ID(TAG_EISA, 0x0133); // type, subtype
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = 0x0240;
res->io.access = acc_rw;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->irq.type = res_irq;
res->irq.enabled = 1;
res->irq.base = 99;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->base_class.id = bc_isdn;
hd->vendor.id = MAKE_ID(TAG_EISA, 0x0e98);
hd->device.id = MAKE_ID(TAG_EISA, 0x0000); // type, subtype
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = 0x0180;
res->io.access = acc_rw;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = 0x0540;
res->io.access = acc_rw;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->irq.type = res_irq;
res->irq.enabled = 1;
res->irq.base = 77;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_pci;
hd->base_class.id = bc_isdn;
hd->vendor.id = MAKE_ID(TAG_PCI, 0x1244);
hd->device.id = MAKE_ID(TAG_PCI, 0x0a00); // type, subtype
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = 0xe000;
res->io.access = acc_rw;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->base_class.id = bc_isdn;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x3001);
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0100); // type, subtype
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = 0xe80;
res->io.access = acc_rw;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_isa;
hd->base_class.id = bc_isdn;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x3000);
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x1a00); // type, subtype
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = 0x400;
res->io.access = acc_rw;
}
#endif
for(hd = hd_data->hd; hd; hd = hd->next) {
if((cic = get_isdn_info(hd))) {
hd->base_class.id = bc_isdn;
hd->sub_class.id = 0;
free_mem(cic);
}
}
}
cdb_isdn_card *get_isdn_info(hd_t *hd)
{
cdb_isdn_card *cic0, *cic;
unsigned u0, u1;
if(hd->bus.id == bus_pci ||
hd->bus.id == bus_isa ||
hd->bus.id == bus_usb ||
hd->bus.id == bus_pcmcia ||
hd->bus.id == bus_cardbus) {
cic = NULL;
u0 = ID_VALUE(hd->vendor.id);
if(
hd->bus.id == bus_isa &&
ID_TAG(hd->vendor.id) == TAG_SPECIAL &&
u0 >= 0x3000 && u0 <= 0x3006 &&
ID_TAG(hd->device.id) == TAG_SPECIAL
) {
u0 = ID_VALUE(hd->device.id);
cic = hd_cdbisdn_get_card_from_type(u0 >> 8, u0 & 0xff);
}
if(
hd->bus.id == bus_isa &&
ID_TAG(hd->vendor.id) == TAG_EISA &&
ID_TAG(hd->device.id) == TAG_EISA
) {
u0 = ID_VALUE(hd->vendor.id);
u1 = ID_VALUE(hd->device.id);
cic = hd_cdbisdn_get_card_from_id(((u0 & 0xff) << 8) + ((u0 >> 8) & 0xff),
((u1 & 0xff) << 8) + ((u1 >> 8) & 0xff),
0xffff,0xffff);
}
if(hd->bus.id == bus_pci) {
cic = hd_cdbisdn_get_card_from_id(ID_VALUE(hd->vendor.id), ID_VALUE(hd->device.id),
ID_VALUE(hd->sub_vendor.id), ID_VALUE(hd->sub_device.id));
}
if(hd->bus.id == bus_usb &&
ID_TAG(hd->vendor.id) == TAG_USB &&
ID_TAG(hd->device.id) == TAG_USB) {
if (hd->revision.id == 0 && hd->revision.name) {
/* the revision is usually saved as string (1.00) */
sscanf(hd->revision.name, "%x.%x", &u1, &u0);
u0 = u0 | u1 << 8;
} else
u0 = ID_VALUE(hd->revision.id);
cic = hd_cdbisdn_get_card_from_id(ID_VALUE(hd->vendor.id), ID_VALUE(hd->device.id),
u0, 0xffff);
if (!cic) /* to get cards without revision info in database */
cic = hd_cdbisdn_get_card_from_id(ID_VALUE(hd->vendor.id), ID_VALUE(hd->device.id),
0xffff, 0xffff);
}
if((hd->bus.id == bus_pcmcia || hd->bus.id == bus_cardbus) &&
(hd->base_class.id == bc_network || hd->base_class.id == bc_isdn)) {
if (hd->drivers && hd->drivers->str) {
if (0 == strcmp(hd->drivers->str, "teles_cs")) {
cic = hd_cdbisdn_get_card_from_type(8, 0);
} else if (0 == strcmp(hd->drivers->str, "sedlbauer_cs")) {
cic = hd_cdbisdn_get_card_from_type(22, 2);
} else if (0 == strcmp(hd->drivers->str, "avma1_cs")) {
cic = hd_cdbisdn_get_card_from_type(26, 0);
} else if (0 == strcmp(hd->drivers->str, "fcpcmcia_cs")) {
cic = hd_cdbisdn_get_card_from_type(8002, 5);
} else if (0 == strcmp(hd->drivers->str, "elsa_cs")) {
cic = hd_cdbisdn_get_card_from_type(10, 11);
} else if (0 == strcmp(hd->drivers->str, "avm_cs")) {
cic = hd_cdbisdn_get_card_from_type(8001, 2);
}
}
}
if (cic && cic->Class && strcmp(cic->Class, "DSL")) {
cic0 = new_mem(sizeof *cic0);
memcpy(cic0, cic, sizeof *cic0);
return cic0;
}
}
return NULL;
}
void hd_scan_dsl(hd_data_t *hd_data)
{
hd_t *hd;
cdb_isdn_card *cic;
if(!hd_probe_feature(hd_data, pr_isdn)) return;
hd_data->module = mod_dsl;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(1, 0, "list");
#ifdef DSL_TEST
{
hd_res_t *res;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->bus.id = bus_pci;
hd->base_class.id = bc_dsl;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x1244);
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x2700); // type, subtype
}
#endif
for(hd = hd_data->hd; hd; hd = hd->next) {
if((cic = get_dsl_info(hd))) {
free_mem(cic);
}
}
}
cdb_isdn_card *get_dsl_info(hd_t *hd)
{
cdb_isdn_card *cic0, *cic;
cdb_isdn_vario *civ;
unsigned u0, u1;
if(hd->bus.id == bus_pci ||
hd->bus.id == bus_usb) {
cic = NULL;
if(hd->bus.id == bus_pci) {
cic = hd_cdbisdn_get_card_from_id(ID_VALUE(hd->vendor.id), ID_VALUE(hd->device.id),
ID_VALUE(hd->sub_vendor.id), ID_VALUE(hd->sub_device.id));
}
if(hd->bus.id == bus_usb &&
ID_TAG(hd->vendor.id) == TAG_USB &&
ID_TAG(hd->device.id) == TAG_USB) {
if (hd->revision.id == 0 && hd->revision.name) {
/* the revision is usually saved as string (1.00) */
sscanf(hd->revision.name, "%x.%x", &u1, &u0);
u0 = u0 | u1 << 8;
} else
u0 = ID_VALUE(hd->revision.id);
cic = hd_cdbisdn_get_card_from_id(ID_VALUE(hd->vendor.id), ID_VALUE(hd->device.id),
u0, 0xffff);
if (!cic) /* to get cards without revision info in database */
cic = hd_cdbisdn_get_card_from_id(ID_VALUE(hd->vendor.id), ID_VALUE(hd->device.id),
0xffff, 0xffff);
}
if (cic && cic->Class && !strcmp(cic->Class, "DSL")) {
hd->base_class.id = bc_dsl;
hd->sub_class.id = sc_dsl_unknown;
civ = hd_cdbisdn_get_vario(cic->vario);
if (civ && civ->interface) {
if (0 == strncmp(civ->interface, "CAPI20", 6)) {
hd->sub_class.id = sc_dsl_capi;
} else if (0 == strncmp(civ->interface, "pppoe", 5)) {
hd->sub_class.id = sc_dsl_pppoe;
}
}
cic0 = new_mem(sizeof *cic0);
memcpy(cic0, cic, sizeof *cic0);
return cic0;
}
}
return NULL;
}
#endif /* !defined(__s390__) && !defined(__s390x__) && !defined(__alpha__) */
#endif /* !defined(LIBHD_TINY) */
/** @} */

4
src/hd/isdn.h Normal file
View File

@ -0,0 +1,4 @@
void hd_scan_isdn(hd_data_t *hd_data);
void hd_scan_dsl(hd_data_t *hd_data);
cdb_isdn_card *get_isdn_info(hd_t *hd);
cdb_isdn_card *get_dsl_info(hd_t *hd);

311
src/hd/kbd.c Normal file
View File

@ -0,0 +1,311 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <linux/serial.h>
#ifndef TIOCGDEV
#define TIOCGDEV _IOR('T', 0x32, unsigned int)
#endif
/**
* @defgroup KDBint Keyboard devices
* @ingroup libhdDEVint
* @brief Keyboard device functions
*
* @{
*/
#ifdef __sparc__
#ifdef DIET
typedef unsigned int u_int;
#endif
#ifndef KIOCTYPE
/* Return keyboard type */
# define KIOCTYPE _IOR('k', 9, int)
#endif
#ifndef KIOCLAYOUT
/* Return Keyboard layout */
# define KIOCLAYOUT _IOR('k', 20, int)
#endif
#include <asm/openpromio.h>
#endif
#include "hd.h"
#include "hd_int.h"
#include "kbd.h"
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* Look for keyboards not covered by kernel input device driver, mainly
* some sort of serial consoles.
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#ifdef __sparc__
static void add_sun_console(hd_data_t *hd_data);
#else
static void add_serial_console(hd_data_t *hd_data);
#endif
void hd_scan_kbd(hd_data_t *hd_data)
{
hd_t *hd;
if(!hd_probe_feature(hd_data, pr_kbd)) return;
hd_data->module = mod_kbd;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(2, 0, "uml");
if(hd_is_uml(hd_data)) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_keyboard;
hd->sub_class.id = sc_keyboard_kbd;
hd->bus.id = bus_none;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0201);
hd->device.id = MAKE_ID(TAG_SPECIAL, 2);
}
PROGRESS(3, 0, "serial console");
#ifdef __sparc__
add_sun_console(hd_data);
#else
add_serial_console(hd_data);
#endif
}
#ifndef __sparc__
void add_serial_console(hd_data_t *hd_data)
{
hd_t *hd;
hd_res_t *res = NULL;
int fd, i;
str_list_t *cmd, *cmd0, *sl;
unsigned u, u1;
struct serial_struct ser_info;
unsigned tty_major = 0, tty_minor = 0;
char c, *dev = NULL, *s;
/* first, try console= option */
cmd = cmd0 = get_cmdline(hd_data, "console");
/* use last console entry */
if(cmd) while(cmd->next) cmd = cmd->next;
if(
cmd &&
(
/* everything != "ttyN" */
strncmp(cmd->str, "tty", 3) ||
!(cmd->str[3] == 0 || (cmd->str[3] >= '0' && cmd->str[3] <= '9'))
)
) {
sl = hd_split(',', cmd->str);
s = sl->str;
if(!strncmp(s, "/dev/", sizeof "/dev/" - 1)) s += sizeof "/dev/" - 1;
dev = new_str(s);
if(sl->next && (i = sscanf(sl->next->str, "%u%c%u", &u, &c, &u1)) >= 1) {
res = add_res_entry(&res, new_mem(sizeof *res));
res->baud.type = res_baud;
res->baud.speed = u;
if(i >= 2) res->baud.parity = c;
if(i >= 3) res->baud.bits = u1;
}
free_str_list(sl);
}
if(!dev && (fd = open(DEV_CONSOLE, O_RDWR | O_NONBLOCK | O_NOCTTY)) >= 0) {
if(ioctl(fd, TIOCGDEV, &u) != -1) {
tty_major = (u >> 8) & 0xfff;
tty_minor = (u & 0xff) | ((u >> 12) & 0xfff00);
ADD2LOG(DEV_CONSOLE ": major %u, minor %u\n", tty_major, tty_minor);
}
if (0)
;
#ifdef __powerpc__
else if(tty_major == 229 /* iseries hvc */) {
if (tty_minor >= 128) {
str_printf(&dev, 0, "hvsi%u", tty_minor-128);
} else {
str_printf(&dev, 0, "hvc%u", tty_minor);
}
} else if (tty_major == 204 /* SERIAL_PSC_MAJOR */ && tty_minor == 148 /* SERIAL_PSC_MINOR */) {
str_printf(&dev, 0, "ttyPSC0"); /* EFIKA5K2 */
}
#endif /* __powerpc__ */
else if(!ioctl(fd, TIOCGSERIAL, &ser_info)) {
ADD2LOG("serial console at line %d\n", ser_info.line);
str_printf(&dev, 0, "ttyS%d", ser_info.line);
}
close(fd);
}
if(dev) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_keyboard;
hd->sub_class.id = sc_keyboard_console;
hd->bus.id = bus_serial;
hd->device.name = new_str("serial console");
if(*dev) str_printf(&hd->unix_dev_name, 0, "/dev/%s", dev);
hd->res = res;
free_mem(dev);
}
free_str_list(cmd0);
}
#else /* defined(__sparc__) */
void add_sun_console(hd_data_t *hd_data)
{
int fd, kid, kid2, klay, ser_cons, i;
unsigned u, u1, u2;
char c1, c2;
struct serial_struct ser_info;
unsigned char buf[OPROMMAXPARAM];
struct openpromio *opio = (struct openpromio *) buf;
hd_t *hd;
hd_res_t *res;
if((fd = open(DEV_CONSOLE, O_RDWR | O_NONBLOCK | O_NOCTTY)) >= 0)
{
if(ioctl(fd, TIOCGSERIAL, &ser_info))
{
ser_cons = -1;
}
else
{
ser_cons = ser_info.line;
ADD2LOG("serial console at line %d\n", ser_cons);
}
close(fd);
if(ser_cons >= 0 && (fd = open(DEV_OPENPROM, O_RDWR | O_NONBLOCK)) >= 0)
{
sprintf(opio->oprom_array, "tty%c-mode", (ser_cons & 1) + 'a');
opio->oprom_size = sizeof buf - 0x100;
if(!ioctl(fd, OPROMGETOPT, opio))
{
if(opio->oprom_size < 0x100)
{
opio->oprom_array[opio->oprom_size] = 0;
ADD2LOG(
"prom(tty%c-mode) = \"%s\" (%d bytes)\n",
(ser_cons & 1) + 'a', opio->oprom_array,
opio->oprom_size
);
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_keyboard;
hd->sub_class.id = sc_keyboard_console;
hd->bus.id = bus_serial;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0203);
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0000);
str_printf(&hd->unix_dev_name, 0, "/dev/ttyS%d", ser_cons);
if((i = sscanf(opio->oprom_array, "%u,%u,%c,%u,%c",
&u, &u1, &c1, &u2, &c2)) >= 1)
{
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->baud.type = res_baud;
res->baud.speed = u;
if(i >= 2) res->baud.bits = u1;
if(i >= 3) res->baud.parity = c1;
if(i >= 4) res->baud.stopbits = u2;
if(i >= 5) res->baud.handshake = c2;
}
}
}
close(fd);
/* We have a serial console, so don't test for keyboard. Else
we will always find a PS/2 keyboard */
return;
}
}
PROGRESS(1, 0, "sun kbd");
if((fd = open(DEV_KBD, O_RDWR | O_NONBLOCK | O_NOCTTY)) >= 0)
{
if(ioctl(fd, KIOCTYPE, &kid)) kid = -1;
if(ioctl(fd, KIOCLAYOUT, &klay)) klay = -1;
close(fd);
if(kid != -1)
{
ADD2LOG("sun keyboard: type %d, layout %d\n", kid, klay);
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_keyboard;
hd->sub_class.id = sc_keyboard_kbd;
hd->bus.id = bus_serial;
if(kid == 4 && klay >= 0)
hd->prog_if.id = klay;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0202);
kid2 = kid;
if(kid == 4 && klay > 0x20)
kid2 = 5;
hd->device.id = MAKE_ID(TAG_SPECIAL, kid2);
if(kid2 == 5) {
if(klay == 0x22 || klay == 0x51)
{
hd->sub_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0202);
hd->sub_device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
}
else if(!(
klay == 0x21 || (klay >= 0x2f && klay <= 0x31) ||
klay == 0x50 || (klay >= 0x5e && klay <= 0x60)
))
{
hd->sub_vendor.id = MAKE_ID(TAG_SPECIAL, 0x0202);
hd->sub_device.id = MAKE_ID(TAG_SPECIAL, 0x0002);
}
}
}
}
else
{
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_keyboard) break;
}
if(!hd) {
/* We must have a PS/2 Keyboard */
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_keyboard;
hd->sub_class.id = sc_keyboard_kbd;
hd->bus.id = bus_ps2;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0201);
hd->device.id = MAKE_ID(TAG_SPECIAL, 1);
}
}
}
#endif /* __sparc__ */
/** @} */

1
src/hd/kbd.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_kbd(hd_data_t *hd_data);

188
src/hd/klog.c Normal file
View File

@ -0,0 +1,188 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/klog.h>
#include "hd.h"
#include "hd_int.h"
#include "klog.h"
/**
* @defgroup KLOGint Kernel log information
* @ingroup libhdINFOint
* @brief Kernel log information scan functions
*
* @{
*/
static int str_ok(str_list_t *sl);
static int str_list_cmp(str_list_t *sl1, str_list_t *sl2);
static void _read_klog(hd_data_t *hd_data);
/*
* Check if a string starts with '<[0-9]>'.
*/
int str_ok(str_list_t *sl)
{
return sl->str[0] == '<' && sl->str[2] == '>' && sl->str[1] >= '0' && sl->str[1] <= '9';
}
/*
* Check if sl1 is idential to sl2; sl1 may be shorter as sl2.
*
* Returns 0/1 if they are equal/not equal. If sl1 is NULL, 0 is returned.
*/
int str_list_cmp(str_list_t *sl1, str_list_t *sl2)
{
for(; sl1; sl1 = sl1->next, sl2 = sl2->next) {
if(!sl2 || strcmp(sl1->str, sl2->str)) return 1;
}
return 0;
}
/*
* Read kernel log info. Combine with /var/log/boot.msg.
* Remove time stamps.
*/
void read_klog(hd_data_t *hd_data)
{
str_list_t *sl, **sl_new;
char *str, *s;
_read_klog(hd_data);
free_str_list(hd_data->klog_raw);
hd_data->klog_raw = hd_data->klog;
hd_data->klog = NULL;
for(sl = hd_data->klog_raw, sl_new = &hd_data->klog; sl; sl = sl->next, sl_new = &(*sl_new)->next) {
str = add_str_list(sl_new, sl->str)->str;
if(str[0] == '<' && str[1] && str[2] == '>' && str[3] == '[') {
s = str + 4;
while(*s && *s != ']') s++;
if(*s) s++;
if(*s) s++; // skip space
for(str += 3; (*str++ = *s++););
}
}
}
/*
* Read kernel log info. Combine with /var/log/boot.msg.
*/
void _read_klog(hd_data_t *hd_data)
{
char buf[0x2000 + 1], *s;
int i, j, len, n;
str_list_t *sl, *sl1, *sl2, *sl_last, **ssl, *sl_next;
/* some clean-up */
hd_data->klog = free_str_list(hd_data->klog);
sl1 = read_file(KLOG_BOOT, 0, 0);
sl2 = NULL;
/*
* remove non-canonical lines (not starting with <[0-9]>) at the start and
* at the end
*/
/* note: the implementations assumes that at least *one* line is ok */
for(sl_last = NULL, sl = sl1; sl; sl = (sl_last = sl)->next) {
if(str_ok(sl)) {
if(sl_last) {
sl_last->next = NULL;
free_str_list(sl1);
sl1 = sl;
}
break;
}
}
for(sl_last = NULL, sl = sl1; sl; sl = (sl_last = sl)->next) {
if(!str_ok(sl)) {
if(sl_last) {
sl_last->next = NULL;
free_str_list(sl);
}
break;
}
}
n = klogctl(3, buf, sizeof buf - 1);
if(n <= 0) {
hd_data->klog = sl1;
return;
}
if(n > (int) sizeof buf - 1) n = sizeof buf - 1;
buf[n] = 0;
for(i = j = 0; i < n; i++) {
if(buf[i] == '\n') {
len = i - j + 1;
s = new_mem(len + 1);
memcpy(s, buf + j, len);
add_str_list(&sl2, s);
s = free_mem(s);
j = i + 1;
}
}
/* the 1st line may be incomplete */
if(sl2 && !str_ok(sl2)) {
sl_next = sl2->next;
sl2->next = NULL;
free_str_list(sl2);
sl2 = sl_next;
}
if(!sl1) {
hd_data->klog = sl2;
return;
}
if(sl1 && !sl2) {
hd_data->klog = sl1;
return;
}
/* now, try to join sl1 & sl2 */
for(sl_last = NULL, sl = sl1; sl; sl = (sl_last = sl)->next) {
if(!str_list_cmp(sl, sl2)) {
free_str_list(sl);
if(sl_last)
sl_last->next = NULL;
else
sl1 = NULL;
break;
}
}
/* append sl2 to sl1 */
for(ssl = &sl1; *ssl; ssl = &(*ssl)->next);
*ssl = sl2;
hd_data->klog = sl1;
}
/*
* Add some klog data to the global log.
*/
void dump_klog(hd_data_t *hd_data)
{
str_list_t *sl;
ADD2LOG("----- kernel log -----\n");
for(sl = hd_data->klog_raw; sl; sl = sl->next) {
ADD2LOG(" %s", sl->str);
}
ADD2LOG("----- kernel log end -----\n");
}
/** @} */

2
src/hd/klog.h Normal file
View File

@ -0,0 +1,2 @@
void read_klog(hd_data_t *hd_data);
void dump_klog(hd_data_t *hd_data);

942
src/hd/manual.c Normal file
View File

@ -0,0 +1,942 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <ctype.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "hd.h"
#include "hd_int.h"
#include "manual.h"
#include "hddb.h"
/**
* @defgroup Manualint UDI manual hardware
* @ingroup libhdInternals
* @brief Manual hardware information functions (/var/lib/hardware/udi/)
*
* @{
*/
#include <hwclass_names.h>
/* corresponds to hd_status_value_t */
static hash_t status_names[] = {
{ status_no, "no" },
{ status_yes, "yes" },
{ status_unknown, "unknown" },
{ status_new, "new" },
{ 0, NULL }
};
#ifndef LIBHD_TINY
static void prop2hd(hd_data_t *hd_data, hd_t *hd, int status_only);
static hal_prop_t *hal_get_new(hal_prop_t **list, const char *key);
static void hd2prop_add_int32(hal_prop_t **list, const char *key, int32_t i);
static void hd2prop_add_str(hal_prop_t **list, const char *key, const char *str);
static void hd2prop_add_list(hal_prop_t **list, const char *key, str_list_t *sl);
static void hd2prop_append_list(hal_prop_t **list, const char *key, char *str);
static void hd2prop(hd_data_t *hd_data, hd_t *hd);
static hal_prop_t *hd_manual_read_entry_old(const char *id);
static hal_prop_t *read_properties(hd_data_t *hd_data, const char *udi, const char *id);
void hd_scan_manual(hd_data_t *hd_data)
{
DIR *dir;
struct dirent *de;
int i, j;
hd_t *hd, *hd1, *next, *hdm, **next2;
char *s;
char *udi_dir[] = { "/org/freedesktop/Hal/devices", "", "" };
if(!hd_probe_feature(hd_data, pr_manual)) return;
hd_data->module = mod_manual;
/* some clean-up */
remove_hd_entries(hd_data);
for(hd = hd_data->manual; hd; hd = next) {
next = hd->next;
hd->next = NULL;
hd_free_hd_list(hd);
}
hd_data->manual = NULL;
next2 = &hd_data->manual;
s = NULL;
for(j = 0; j < sizeof udi_dir / sizeof *udi_dir; j++) {
str_printf(&s, 0, "%s%s", j == 2 ? "unique-keys" : "udi", udi_dir[j]);
if((dir = opendir(hd_get_hddb_path(s)))) {
i = 0;
while((de = readdir(dir))) {
if(*de->d_name == '.') continue;
PROGRESS(1, ++i, "read");
str_printf(&s, 0, "%s%s%s", udi_dir[j], *udi_dir[j] ? "/" : "", de->d_name);
if((hd = hd_read_config(hd_data, s))) {
if(hd->status.available != status_unknown) hd->status.available = status_no;
ADD2LOG(" got %s\n", hd->unique_id);
*next2 = hd;
next2 = &hd->next;
}
}
closedir(dir);
}
}
s = free_mem(s);
hd_data->flags.keep_kmods = 1;
for(hdm = hd_data->manual; hdm; hdm = next) {
next = hdm->next;
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->unique_id && hdm->unique_id && !strcmp(hd->unique_id, hdm->unique_id)) break;
}
if(hd) {
/* just update config status */
hd->status = hdm->status;
if(hd->status.available != status_unknown) hd->status.available = status_yes;
if(hdm->config_string) hd->config_string = new_str(hdm->config_string);
if(hdm->persistent_prop) {
hd->persistent_prop = hdm->persistent_prop;
hdm->persistent_prop = NULL;
}
}
else {
/* add new entry */
hd = add_hd_entry(hd_data, __LINE__, 0);
*hd = *hdm;
hd->next = NULL;
hd->tag.freeit = 0;
hdm->tag.remove = 1;
if(hd->status.available != status_unknown) hd->status.available = status_no;
// FIXME: do it really here?
if(hd->parent_id) {
for(hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
if(hd1->unique_id && !strcmp(hd1->unique_id, hd->parent_id)) {
hd->attached_to = hd1->idx;
break;
}
}
}
}
}
hd_data->flags.keep_kmods = 0;
for(hd = hd_data->manual; hd; hd = next) {
next = hd->next;
hd->next = NULL;
if(!hd->tag.remove) {
hd_free_hd_list(hd);
}
else {
free_mem(hd);
}
}
hd_data->manual = NULL;
}
void hd_scan_manual2(hd_data_t *hd_data)
{
hd_t *hd, *hd1;
/* add persistent properties */
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->persistent_prop) continue;
hd->persistent_prop = read_properties(hd_data, hd->udi, hd->unique_id);
prop2hd(hd_data, hd, 1);
if(hd->status.available != status_unknown) hd->status.available = status_yes;
}
/* check if it's necessary to reconfigure this hardware */
for(hd = hd_data->hd; hd; hd = hd->next) {
hd->status.reconfig = status_no;
if(hd->status.needed != status_yes) continue;
if(hd->status.available == status_no) {
hd->status.reconfig = status_yes;
continue;
}
if(hd->status.available != status_unknown) continue;
for(hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
if(hd1 == hd) continue;
if(
hd1->hw_class == hd->hw_class &&
hd1->status.configured == status_new &&
hd1->status.available == status_yes
) break;
}
if(hd1) hd->status.reconfig = status_yes;
}
}
char *hd_status_value_name(hd_status_value_t status)
{
return key2value(status_names, status);
}
/*
* read an entry - obsolete
*/
hd_manual_t *hd_manual_read_entry(hd_data_t *hd_data, const char *id)
{
return NULL;
}
/*
* read an entry
*/
hal_prop_t *hd_manual_read_entry_old(const char *id)
{
char path[PATH_MAX];
int line;
str_list_t *sl, *sl0;
char *s, *s1, *s2;
hal_prop_t *prop_list = NULL, *prop = NULL;
if(!id) return NULL;
snprintf(path, sizeof path, "%s/%s", hd_get_hddb_path("unique-keys"), id);
if(!(sl0 = read_file(path, 0, 0))) return prop_list;
for(line = 1, sl = sl0; sl; sl = sl->next, line++) {
s = sl->str;
while(isspace(*s)) s++;
if(!*s || *s == '#' || *s == ';') continue; /* empty lines & comments */
s2 = s;
s1 = strsep(&s2, "=");
if(!s2 && *s == '[') continue;
if(!s2) break;
if(s1) {
if(prop) {
prop->next = new_mem(sizeof *prop);
prop = prop->next;
}
else {
prop_list = prop = new_mem(sizeof *prop);
}
prop->type = p_string;
for(s = s1; *s; s++) if(*s >= 'A' && *s <= 'Z') *s += 'a' - 'A';
str_printf(&prop->key, 0, "hwinfo.%s", s1);
prop->val.str = canon_str(s2, strlen(s2));
}
}
free_str_list(sl0);
return prop_list;
}
/*
* write an entry
*/
int hd_manual_write_entry(hd_data_t *hd_data, hd_manual_t *entry)
{
return 0;
}
char *prop2hd_str(hal_prop_t *prop, const char *id)
{
return (prop = hal_get_str(prop, id)) ? new_str(prop->val.str) : NULL;
}
int32_t prop2hd_int32(hal_prop_t *prop, const char *id)
{
return (prop = hal_get_int32(prop, id)) ? prop->val.int32 : 0;
}
str_list_t *prop2hd_list(hal_prop_t *prop, const char *id)
{
str_list_t *sl0 = NULL, *sl;
prop = hal_get_list(prop, id);
if(prop) {
for(sl = prop->val.list; sl; sl = sl->next) {
add_str_list(&sl0, sl->str);
}
}
return sl0;
}
void prop2hd(hd_data_t *hd_data, hd_t *hd, int status_only)
{
hal_prop_t *prop, *list;
hd_res_t *res;
int i;
unsigned u, u0, u1, u2, u3, u4;
char *s;
uint64_t u64_0, u64_1;
str_list_t *sl;
list = hd->persistent_prop;
hd->config_string = prop2hd_str(list, "hwinfo.configstring");
if((prop = hal_get_str(list, "hwinfo.configured"))) {
hd->status.configured = value2key(status_names, prop->val.str);
}
if((prop = hal_get_str(list, "hwinfo.available"))) {
hd->status.available_orig =
hd->status.available = value2key(status_names, prop->val.str);
}
if((prop = hal_get_str(list, "hwinfo.needed"))) {
hd->status.needed = value2key(status_names, prop->val.str);
}
if((prop = hal_get_str(list, "hwinfo.active"))) {
hd->status.active = value2key(status_names, prop->val.str);
}
/*
* if the status info is completely missing, fake some:
* new hardware, autodetectable, not needed
*/
if(
!hd->status.configured &&
!hd->status.available &&
!hd->status.needed &&
!hd->status.invalid
) {
hd->status.configured = status_new;
hd->status.available = status_yes;
hd->status.needed = status_no;
}
if(!hd->status.active) hd->status.active = status_unknown;
if(status_only || !list) return;
hd->udi = prop2hd_str(list, "info.udi");
hd->unique_id = prop2hd_str(list, "hwinfo.uniqueid");
hd->parent_id = prop2hd_str(list, "hwinfo.parentid");
hd->child_ids = prop2hd_list(list, "hwinfo.childids");
hd->model = prop2hd_str(list, "hwinfo.model");
if((prop = hal_get_str(list, "hwinfo.hwclass"))) {
hd->hw_class = value2key(hw_items, prop->val.str);
}
hd->broken = prop2hd_int32(list, "hwinfo.broken");
hd->bus.id = prop2hd_int32(list, "hwinfo.busid");
hd->slot = prop2hd_int32(list, "hwinfo.slot");
hd->func = prop2hd_int32(list, "hwinfo.func");
hd->base_class.id = prop2hd_int32(list, "hwinfo.baseclass");
hd->sub_class.id = prop2hd_int32(list, "hwinfo.subclass");
hd->prog_if.id = prop2hd_int32(list, "hwinfo.progif");
hd->revision.id = prop2hd_int32(list, "hwinfo.revisionid");
hd->revision.name = prop2hd_str(list, "hwinfo.revisionname");
hd->vendor.id = prop2hd_int32(list, "hwinfo.vendorid");
hd->vendor.name = prop2hd_str(list, "hwinfo.vendorname");
hd->device.id = prop2hd_int32(list, "hwinfo.deviceid");
hd->device.name = prop2hd_str(list, "hwinfo.devicename");
hd->sub_vendor.id = prop2hd_int32(list, "hwinfo.subvendorid");
hd->sub_vendor.name = prop2hd_str(list, "hwinfo.subvendorname");
hd->sub_device.id = prop2hd_int32(list, "hwinfo.subdeviceid");
hd->sub_device.name = prop2hd_str(list, "hwinfo.subdevicename");
hd->compat_device.id = prop2hd_int32(list, "hwinfo.compatdeviceid");
hd->compat_device.name = prop2hd_str(list, "hwinfo.compatdevicename");
hd->serial = prop2hd_str(list, "hwinfo.serial");
hd->unix_dev_name = prop2hd_str(list, "hwinfo.unixdevice");
hd->unix_dev_name2 = prop2hd_str(list, "hwinfo.unixdevicealt");
hd->unix_dev_names = prop2hd_list(list, "hwinfo.unixdevicelist");
hd->drivers = prop2hd_list(list, "hwinfo.drivers");
hd->sysfs_id = prop2hd_str(list, "hwinfo.sysfsid");
hd->sysfs_bus_id = prop2hd_str(list, "hwinfo.sysfsbusid");
hd->sysfs_device_link = prop2hd_str(list, "hwinfo.sysfslink");
hd->rom_id = prop2hd_str(list, "hwinfo.romid");
hd->usb_guid = prop2hd_str(list, "hwinfo.usbguid");
hd->hotplug = prop2hd_int32(list, "hwinfo.hotplug");
if((s = hal_get_useful_str(list, "hwinfo.hwclasslist"))) {
for(u = 0; u < sizeof hd->hw_class_list / sizeof *hd->hw_class_list; u++) {
if(*s && s[1] && (i = hex(s, 2)) >= 0) {
hd->hw_class_list[u] = i;
s += 2;
}
else {
break;
}
}
}
u = prop2hd_int32(list, "hwinfo.features");
if(u & (1 << 0)) hd->is.agp = 1;
if(u & (1 << 1)) hd->is.isapnp = 1;
if(u & (1 << 2)) hd->is.softraiddisk = 1;
if(u & (1 << 3)) hd->is.zip = 1;
if(u & (1 << 4)) hd->is.cdr = 1;
if(u & (1 << 5)) hd->is.cdrw = 1;
if(u & (1 << 6)) hd->is.dvd = 1;
if(u & (1 << 7)) hd->is.dvdr = 1;
if(u & (1 << 8)) hd->is.dvdram = 1;
if(u & (1 << 9)) hd->is.pppoe = 1;
if(u & (1 << 10)) hd->is.wlan = 1;
if((prop = hal_get_list(list, "hwinfo.res.memory"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "0x%"SCNx64",0x%"SCNx64",%u,%u,%u", &u64_0, &u64_1, &u0, &u1, &u2) == 5) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_mem;
res->mem.base = u64_0;
res->mem.range = u64_1;
res->mem.enabled = u0;
res->mem.access = u1;
res->mem.prefetch = u2;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.physmemory"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "0x%"SCNx64"", &u64_0) == 1) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_phys_mem;
res->phys_mem.range = u64_0;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.io"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "0x%"SCNx64",0x%"SCNx64",%u,%u", &u64_0, &u64_1, &u0, &u1) == 4) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_io;
res->io.base = u64_0;
res->io.range = u64_1;
res->io.enabled = u0;
res->io.access = u1;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.interrupts"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u,%u,%u", &u0, &u1, &u2) == 3) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_irq;
res->irq.base = u0;
res->irq.triggered = u1;
res->irq.enabled = u2;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.dma"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u,%u", &u0, &u1) == 2) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_dma;
res->dma.base = u0;
res->dma.enabled = u1;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.size"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u,%u,%u", &u0, &u1, &u2) == 3) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_size;
res->size.unit = u0;
res->size.val1 = u1;
res->size.val2 = u2;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.baud"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u,%u,%u,%u,%u", &u0, &u1, &u2, &u3, &u4) == 5) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_baud;
res->baud.speed = u0;
res->baud.bits = u1;
res->baud.stopbits = u2;
res->baud.parity = (char) u3;
res->baud.handshake = (char) u4;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.cache"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u", &u0) == 1) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_cache;
res->cache.size = u0;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.diskgeometry"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u,%u,%u,%u", &u0, &u1, &u2, &u3) == 4) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_disk_geo;
res->disk_geo.cyls = u0;
res->disk_geo.heads = u1;
res->disk_geo.sectors = u2;
res->disk_geo.geotype = u3;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.monitor"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u,%u,%u,%u", &u0, &u1, &u2, &u3) == 4) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_monitor;
res->monitor.width = u0;
res->monitor.height = u1;
res->monitor.vfreq = u2;
res->monitor.interlaced = u3;
}
}
}
if((prop = hal_get_list(list, "hwinfo.res.framebuffer"))) {
for(sl = prop->val.list; sl; sl = sl->next) {
if(sscanf(sl->str, "%u,%u,%u,%u,%u", &u0, &u1, &u2, &u3, &u4) == 5) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->any.type = res_framebuffer;
res->framebuffer.width = u0;
res->framebuffer.height = u1;
res->framebuffer.bytes_p_line = u2;
res->framebuffer.colorbits = u3;
res->framebuffer.mode = u4;
}
}
}
hddb_add_info(hd_data, hd);
}
hal_prop_t *hal_get_new(hal_prop_t **list, const char *key)
{
hal_prop_t *prop;
prop = hal_get_any(*list, key);
if(!prop) {
prop = new_mem(sizeof *prop);
prop->next = *list;
*list = prop;
prop->key = new_str(key);
}
else {
hal_invalidate_all(prop, key);
}
return prop;
}
void hd2prop_add_int32(hal_prop_t **list, const char *key, int32_t i)
{
hal_prop_t *prop;
if(i) {
prop = hal_get_new(list, key);
prop->type = p_int32;
prop->val.int32 = i;
}
else {
hal_invalidate_all(*list, key);
}
}
void hd2prop_add_str(hal_prop_t **list, const char *key, const char *str)
{
hal_prop_t *prop;
if(str) {
prop = hal_get_new(list, key);
prop->type = p_string;
prop->val.str = new_str(str);
}
else {
hal_invalidate_all(*list, key);
}
}
void hd2prop_add_list(hal_prop_t **list, const char *key, str_list_t *sl)
{
hal_prop_t *prop;
if(sl) {
prop = hal_get_new(list, key);
prop->type = p_list;
for(; sl; sl = sl->next) {
add_str_list(&prop->val.list, sl->str);
}
}
else {
hal_invalidate_all(*list, key);
}
}
void hd2prop_append_list(hal_prop_t **list, const char *key, char *str)
{
hal_prop_t *prop;
str_list_t *sl = NULL;
if(!str) return;
prop = hal_get_list(*list, key);
if(!prop) {
add_str_list(&sl, str);
hd2prop_add_list(list, key, sl);
return;
}
add_str_list(&prop->val.list, str);
}
void hd2prop(hd_data_t *hd_data, hd_t *hd)
{
hal_prop_t **list;
char *s = NULL;
unsigned u;
hd_res_t *res;
list = &hd->persistent_prop;
hd2prop_add_str(list, "info.udi", hd->udi);
hd2prop_add_str(list, "hwinfo.uniqueid", hd->unique_id);
hd2prop_add_str(list, "hwinfo.parentid", hd->parent_id);
hd2prop_add_list(list, "hwinfo.childids", hd->child_ids);
hd2prop_add_str(list, "hwinfo.model", hd->model);
hd2prop_add_str(list, "hwinfo.configstring", hd->config_string);
hd2prop_add_str(list, "hwinfo.hwclass", key2value(hw_items, hd->hw_class));
hd2prop_add_str(list, "hwinfo.configured", key2value(status_names, hd->status.configured));
hd2prop_add_str(list, "hwinfo.available", key2value(status_names, hd->status.available));
hd2prop_add_str(list, "hwinfo.needed", key2value(status_names, hd->status.needed));
hd2prop_add_str(list, "hwinfo.active", key2value(status_names, hd->status.active));
hd2prop_add_int32(list, "hwinfo.broken", hd->broken);
hd2prop_add_int32(list, "hwinfo.bus", hd->bus.id);
hd2prop_add_int32(list, "hwinfo.slot", hd->slot);
hd2prop_add_int32(list, "hwinfo.func", hd->func);
hd2prop_add_int32(list, "hwinfo.baseclass", hd->base_class.id);
hd2prop_add_int32(list, "hwinfo.subclass", hd->sub_class.id);
hd2prop_add_int32(list, "hwinfo.progif", hd->prog_if.id);
hd2prop_add_int32(list, "hwinfo.revisionid", hd->revision.id);
hd2prop_add_str(list, "hwinfo.revisionname", hd->revision.name);
hd2prop_add_int32(list, "hwinfo.vendorid", hd->vendor.id);
hd2prop_add_str(list, "hwinfo.vendorname", hd->vendor.name);
hd2prop_add_int32(list, "hwinfo.deviceid", hd->device.id);
hd2prop_add_str(list, "hwinfo.devicename", hd->device.name);
hd2prop_add_int32(list, "hwinfo.subvendorid", hd->sub_vendor.id);
hd2prop_add_str(list, "hwinfo.subvendorname", hd->sub_vendor.name);
hd2prop_add_int32(list, "hwinfo.subdeviceid", hd->sub_device.id);
hd2prop_add_str(list, "hwinfo.subdevicename", hd->sub_device.name);
hd2prop_add_int32(list, "hwinfo.compatvendorid", hd->compat_vendor.id);
hd2prop_add_int32(list, "hwinfo.compatdeviceid", hd->compat_device.id);
hd2prop_add_str(list, "hwinfo.serial", hd->serial);
hd2prop_add_str(list, "hwinfo.unixdevice", hd->unix_dev_name);
hd2prop_add_str(list, "hwinfo.unixdevicealt", hd->unix_dev_name2);
hd2prop_add_list(list, "hwinfo.unixdevicelist", hd->unix_dev_names);
hd2prop_add_list(list, "hwinfo.drivers", hd->drivers);
hd2prop_add_str(list, "hwinfo.sysfsid", hd->sysfs_id);
hd2prop_add_str(list, "hwinfo.sysfsbusid", hd->sysfs_bus_id);
hd2prop_add_str(list, "hwinfo.sysfslink", hd->sysfs_device_link);
hd2prop_add_str(list, "hwinfo.romid", hd->rom_id);
hd2prop_add_str(list, "hwinfo.usbguid", hd->usb_guid);
hd2prop_add_int32(list, "hwinfo.hotplug", hd->hotplug);
for(u = 0; u < sizeof hd->hw_class_list / sizeof *hd->hw_class_list; u++) {
str_printf(&s, -1, "%02x", hd->hw_class_list[u]);
}
hd2prop_add_str(list, "hwinfo.hwclasslist", s);
s = free_mem(s);
u = 0;
if(hd->is.agp) u |= 1 << 0;
if(hd->is.isapnp) u |= 1 << 1;
if(hd->is.softraiddisk) u |= 1 << 2;
if(hd->is.zip) u |= 1 << 3;
if(hd->is.cdr) u |= 1 << 4;
if(hd->is.cdrw) u |= 1 << 5;
if(hd->is.dvd) u |= 1 << 6;
if(hd->is.dvdr) u |= 1 << 7;
if(hd->is.dvdram) u |= 1 << 8;
if(hd->is.pppoe) u |= 1 << 9;
if(hd->is.wlan) u |= 1 << 10;
hd2prop_add_int32(list, "hwinfo.features", u);
hal_invalidate_all(*list, "hwinfo.res.memory");
hal_invalidate_all(*list, "hwinfo.res.physmemory");
hal_invalidate_all(*list, "hwinfo.res.io");
hal_invalidate_all(*list, "hwinfo.res.interrupts");
hal_invalidate_all(*list, "hwinfo.res.dma");
hal_invalidate_all(*list, "hwinfo.res.size");
hal_invalidate_all(*list, "hwinfo.res.baud");
hal_invalidate_all(*list, "hwinfo.res.cache");
hal_invalidate_all(*list, "hwinfo.res.diskgeometry");
hal_invalidate_all(*list, "hwinfo.res.monitor");
hal_invalidate_all(*list, "hwinfo.res.framebuffer");
for(res = hd->res; res; res = res->next) {
switch(res->any.type) {
case res_mem:
str_printf(&s, 0,
"0x%"PRIx64",0x%"PRIx64",%u,%u,%u",
res->mem.base, res->mem.range, res->mem.enabled, res->mem.access, res->mem.prefetch
);
hd2prop_append_list(list, "hwinfo.res.memory", s);
break;
case res_phys_mem:
str_printf(&s, 0,
"0x%"PRIx64,
res->phys_mem.range
);
hd2prop_append_list(list, "hwinfo.res.physmemory", s);
break;
case res_io:
str_printf(&s, 0,
"0x%"PRIx64",0x%"PRIx64",%u,%u",
res->io.base, res->io.range, res->io.enabled, res->io.access
);
hd2prop_append_list(list, "hwinfo.res.io", s);
break;
case res_irq:
str_printf(&s, 0,
"%u,%u,%u",
res->irq.base, res->irq.triggered, res->irq.enabled
);
hd2prop_append_list(list, "hwinfo.res.interrupts", s);
break;
case res_dma:
str_printf(&s, 0,
"%u,%u",
res->dma.base, res->dma.enabled
);
hd2prop_append_list(list, "hwinfo.res.dma", s);
break;
case res_size:
str_printf(&s, 0,
"%u,%"PRIu64",%"PRIu64,
res->size.unit, res->size.val1, res->size.val2
);
hd2prop_append_list(list, "hwinfo.res.size", s);
break;
case res_baud:
str_printf(&s, 0,
"%u,%u,%u,0x%02x,0x%02x",
res->baud.speed, res->baud.bits, res->baud.stopbits,
(unsigned) res->baud.parity, (unsigned) res->baud.handshake
);
hd2prop_append_list(list, "hwinfo.res.baud", s);
break;
case res_cache:
str_printf(&s, 0,
"%u",
res->cache.size
);
hd2prop_append_list(list, "hwinfo.res.cache", s);
break;
case res_disk_geo:
str_printf(&s, 0,
"%u,%u,%u,%u",
res->disk_geo.cyls, res->disk_geo.heads, res->disk_geo.sectors, res->disk_geo.geotype
);
hd2prop_append_list(list, "hwinfo.res.diskgeometry", s);
break;
case res_monitor:
str_printf(&s, 0,
"%u,%u,%u,%u",
res->monitor.width, res->monitor.height, res->monitor.vfreq, res->monitor.interlaced
);
hd2prop_append_list(list, "hwinfo.res.monitor", s);
break;
case res_framebuffer:
str_printf(&s, 0,
"%u,%u,%u,%u,%u",
res->framebuffer.width, res->framebuffer.height, res->framebuffer.bytes_p_line,
res->framebuffer.colorbits, res->framebuffer.mode
);
hd2prop_append_list(list, "hwinfo.res.framebuffer", s);
break;
default:
break;
}
}
s = free_mem(s);
}
hal_prop_t *read_properties(hd_data_t *hd_data, const char *udi, const char *id)
{
hd_t *hd;
hal_prop_t *prop = NULL;
if(udi) {
prop = hd_read_properties(udi);
ADD2LOG(" prop read: %s (%s)\n", udi, prop ? "ok" : "failed");
}
if(prop) return prop;
if(id && !udi) {
/* try to find udi entry */
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->udi && hd->unique_id && !strcmp(id, hd->unique_id)) {
udi = hd->udi;
break;
}
}
if(udi) {
prop = hd_read_properties(udi);
ADD2LOG(" prop read: %s (%s)\n", udi, prop ? "ok" : "failed");
}
}
if(!prop) {
prop = hd_read_properties(id);
ADD2LOG(" prop read: %s (%s)\n", id, prop ? "ok" : "failed");
}
if(!prop) {
prop = hd_manual_read_entry_old(id);
ADD2LOG(" old prop read: %s (%s)\n", id, prop ? "ok" : "failed");
}
return prop;
}
hd_t *hd_read_config(hd_data_t *hd_data, const char *id)
{
hd_t *hd = NULL;
hal_prop_t *prop = NULL;
const char *udi = NULL;
/* only of we didn't already (check internal db pointer) */
/* prop2hd() makes db lookups */
if(!hd_data->hddb2[1]) hddb_init(hd_data);
if(id && *id == '/') {
udi = id;
id = NULL;
}
prop = read_properties(hd_data, udi, id);
if(prop) {
hd = new_mem(sizeof *hd);
hd->idx = ++(hd_data->last_idx);
hd->module = hd_data->module;
hd->line = __LINE__;
hd->tag.freeit = 1; /* make it a 'stand alone' entry */
hd->persistent_prop = prop;
prop2hd(hd_data, hd, 0);
}
return hd;
}
int hd_write_config(hd_data_t *hd_data, hd_t *hd)
{
char *udi;
if(!hd_report_this(hd_data, hd)) return 0;
hd2prop(hd_data, hd);
udi = hd->unique_id;
if(hd->udi) udi = hd->udi;
if(!udi) return 5;
return hd_write_properties(udi, hd->persistent_prop);
}
#endif /* LIBHD_TINY */
/** @} */

2
src/hd/manual.h Normal file
View File

@ -0,0 +1,2 @@
void hd_scan_manual(hd_data_t *hd_data);
void hd_scan_manual2(hd_data_t *hd_data);

851
src/hd/mdt.c Normal file
View File

@ -0,0 +1,851 @@
#if defined(__i386__) || defined (__x86_64__)
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <getopt.h>
#include <inttypes.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/io.h>
#include <sys/time.h>
#include <x86emu.h>
#include "hd.h"
#include "hd_int.h"
#define STR_SIZE 128
#define VBIOS_ROM 0xc0000
#define VBIOS_ROM_SIZE 0x10000
#define VBIOS_MEM 0xa0000
#define VBIOS_MEM_SIZE 0x10000
#define VBE_BUF 0x8000
#define ADD_RES(w, h, f, i) \
res[res_cnt].width = w, \
res[res_cnt].height = h, \
res[res_cnt].vfreq = f, \
res[res_cnt++].il = i;
#define LPRINTF(a...) hd_log_printf(vm->hd_data, a)
typedef struct vm_s {
x86emu_t *emu;
x86emu_memio_handler_t old_memio;
unsigned ports;
unsigned force:1;
unsigned timeout;
unsigned no_io:1;
unsigned all_modes:1;
unsigned mode;
unsigned mode_set:1;
unsigned trace_flags;
unsigned dump_flags;
int trace_only;
int dump_only;
int exec_count;
hd_data_t *hd_data;
} vm_t;
static void flush_log(x86emu_t *emu, char *buf, unsigned size);
static void vm_write_byte(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm);
static void vm_write_word(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm);
// static void vm_write_dword(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm);
static void copy_to_vm(x86emu_t *emu, unsigned dst, unsigned char *src, unsigned size, unsigned perm);
static void copy_from_vm(x86emu_t *emu, void *dst, unsigned src, unsigned len);
static int do_int(x86emu_t *emu, u8 num, unsigned type);
static vm_t *vm_new(void);
static void vm_free(vm_t *vm);
static unsigned vm_run(x86emu_t *emu, double *t);
static int vm_prepare(vm_t *vm);
static unsigned new_memio(x86emu_t *emu, u32 addr, u32 *val, unsigned type);
static double get_time(void);
static void *map_mem(vm_t *vm, unsigned start, unsigned size, int rw);
void print_vbe_info(vm_t *vm, x86emu_t *emu, unsigned mode);
void probe_all(vm_t *vm, vbe_info_t *vbe);
void get_video_mode(vm_t *vm, vbe_info_t *vbe);
void list_modes(vm_t *vm, vbe_info_t *vbe);
void print_edid(int port, unsigned char *edid);
int chk_edid_info(unsigned char *edid);
void get_vbe_info(hd_data_t *hd_data, vbe_info_t *vbe)
{
int i, err;
char *t;
unsigned u, tbits, dbits;
str_list_t *sl;
vm_t *vm;
PROGRESS(4, 1, "vbe info");
vm = vm_new();
vm->hd_data = hd_data;
hd_data->vm = vm;
for(sl = get_probe_val_list(hd_data, pr_x86emu); sl && (t = sl->str); sl = sl->next) {
err = 0;
u = 1;
tbits = dbits = 0;
while(*t == '+' || *t == '-') u = *t++ == '+' ? 1 : 0;
if(!strcmp(t, "trace")) tbits = X86EMU_TRACE_DEFAULT;
else if(!strcmp(t, "code")) tbits = X86EMU_TRACE_CODE;
else if(!strcmp(t, "regs")) tbits = X86EMU_TRACE_REGS;
else if(!strcmp(t, "data")) tbits = X86EMU_TRACE_DATA;
else if(!strcmp(t, "acc")) tbits = X86EMU_TRACE_ACC;
else if(!strcmp(t, "io")) tbits = X86EMU_TRACE_IO;
else if(!strcmp(t, "ints")) tbits = X86EMU_TRACE_INTS;
else if(!strcmp(t, "time")) tbits = X86EMU_TRACE_TIME;
else if(!strcmp(t, "dump")) dbits = X86EMU_DUMP_DEFAULT;
else if(!strcmp(t, "dump.regs")) dbits = X86EMU_DUMP_REGS;
else if(!strcmp(t, "dump.mem")) dbits = X86EMU_DUMP_MEM;
else if(!strcmp(t, "dump.mem.acc")) dbits = X86EMU_DUMP_ACC_MEM;
else if(!strcmp(t, "dump.mem.inv")) dbits = X86EMU_DUMP_INV_MEM;
else if(!strcmp(t, "dump.attr")) dbits = X86EMU_DUMP_ATTR;
else if(!strcmp(t, "dump.io")) dbits = X86EMU_DUMP_IO;
else if(!strcmp(t, "dump.ints")) dbits = X86EMU_DUMP_INTS;
else if(!strcmp(t, "dump.time")) dbits = X86EMU_DUMP_TIME;
else if(!strcmp(t, "force")) vm->force = u;
else if(!strncmp(t, "timeout=", sizeof "timeout=" - 1)) {
i = strtol(t + sizeof "timeout=" - 1, NULL, 0);
if(i) vm->timeout = i;
}
else if(!strncmp(t, "trace.only=", sizeof "trace.only=" - 1)) {
vm->trace_only = strtol(t + sizeof "trace.only=" - 1, NULL, 0);
}
else if(!strncmp(t, "dump.only=", sizeof "dump.only=" - 1)) {
vm->dump_only = strtol(t + sizeof "dump.only=" - 1, NULL, 0);
}
else err = 5;
if(err) {
ADD2LOG("x86emu: invalid flag '%s'\n", t);
}
else {
if(tbits) {
if(u) {
vm->trace_flags |= tbits;
}
else {
vm->trace_flags &= ~tbits;
}
}
if(dbits) {
if(u) {
vm->dump_flags |= dbits;
}
else {
vm->dump_flags &= ~dbits;
}
}
}
}
if(!vm_prepare(vm)) {
ADD2LOG("x86emu: could not init vm\n");
vm_free(vm);
return;
}
vm->ports = 3;
i = get_probe_val_int(hd_data, pr_bios_ddc_ports);
if(i > sizeof vbe->ddc_port / sizeof *vbe->ddc_port) i = sizeof vbe->ddc_port / sizeof *vbe->ddc_port;
if(i) vm->ports = i;
if(hd_probe_feature(hd_data, pr_bios_fb)) {
PROGRESS(4, 2, "mode info");
// there shouldn't any real io be needed for this
vm->no_io = 1;
list_modes(vm, vbe);
}
if(hd_probe_feature(hd_data, pr_bios_ddc)) {
PROGRESS(4, 3, "ddc info");
ADD2LOG("vbe: probing %d ports\n", vm->ports);
// for ddc probing we have to allow direct io accesses
vm->no_io = 0;
probe_all(vm, vbe);
}
if(hd_probe_feature(hd_data, pr_bios_mode)) {
PROGRESS(4, 4, "gfx mode");
// there shouldn't any real io be needed for this
vm->no_io = 1;
get_video_mode(vm, vbe);
}
vm_free(vm);
}
void flush_log(x86emu_t *emu, char *buf, unsigned size)
{
vm_t *vm = emu->private;
hd_data_t *hd_data = vm->hd_data;
if(!buf || !size || !hd_data) return;
hd_log(hd_data, buf, size);
}
unsigned vm_read_segofs16(x86emu_t *emu, unsigned addr)
{
return x86emu_read_word(emu, addr) + (x86emu_read_word(emu, addr + 2) << 4);
}
void vm_write_byte(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm)
{
x86emu_write_byte_noperm(emu, addr, val);
x86emu_set_perm(emu, addr, addr, perm | X86EMU_PERM_VALID);
}
void vm_write_word(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm)
{
x86emu_write_byte_noperm(emu, addr, val);
x86emu_write_byte_noperm(emu, addr + 1, val >> 8);
x86emu_set_perm(emu, addr, addr + 1, perm | X86EMU_PERM_VALID);
}
#if 0
void vm_write_dword(x86emu_t *emu, unsigned addr, unsigned val, unsigned perm)
{
x86emu_write_byte_noperm(emu, addr, val);
x86emu_write_byte_noperm(emu, addr + 1, val >> 8);
x86emu_write_byte_noperm(emu, addr + 2, val >> 16);
x86emu_write_byte_noperm(emu, addr + 3, val >> 24);
x86emu_set_perm(emu, addr, addr + 3, perm | X86EMU_PERM_VALID);
}
#endif
void copy_to_vm(x86emu_t *emu, unsigned dst, unsigned char *src, unsigned size, unsigned perm)
{
if(!size) return;
while(size--) vm_write_byte(emu, dst++, *src++, perm);
}
void copy_from_vm(x86emu_t *emu, void *dst, unsigned src, unsigned len)
{
unsigned char *p = dst;
unsigned u;
for(u = 0; u < len; u++) {
p[u] = x86emu_read_byte_noperm(emu, src + u);
}
}
int do_int(x86emu_t *emu, u8 num, unsigned type)
{
if((type & 0xff) == INTR_TYPE_FAULT) {
x86emu_stop(emu);
return 0;
}
// ignore ints != (0x10 or 0x42 or 0x6d)
if(num != 0x10 && num != 0x42 && num != 0x6d) return 1;
return 0;
}
vm_t *vm_new()
{
vm_t *vm;
vm = calloc(1, sizeof *vm);
vm->emu = x86emu_new(0, X86EMU_PERM_RW);
vm->emu->private = vm;
x86emu_set_log(vm->emu, 200000000, flush_log);
x86emu_set_intr_handler(vm->emu, do_int);
vm->trace_only = -1;
vm->dump_only = -1;
return vm;
}
void vm_free(vm_t *vm)
{
if(!vm) return;
x86emu_done(vm->emu);
free(vm);
}
unsigned vm_run(x86emu_t *emu, double *t)
{
vm_t *vm = emu->private;
unsigned err;
x86emu_log(emu, "=== emulation log %d %s===\n", vm->exec_count, vm->no_io ? "(no i/o) " : "");
*t = get_time();
if(vm->trace_only == -1 || vm->trace_only == vm->exec_count) {
emu->log.trace = vm->trace_flags;
}
else {
emu->log.trace = 0;
}
x86emu_reset_access_stats(emu);
iopl(3);
err = x86emu_run(emu, X86EMU_RUN_LOOP | X86EMU_RUN_NO_CODE | X86EMU_RUN_TIMEOUT);
iopl(0);
*t = get_time() - *t;
if(
vm->dump_flags &&
(vm->dump_only == -1 || vm->dump_only == vm->exec_count)
) {
x86emu_log(emu, "\n; - - - final state\n");
x86emu_dump(emu, vm->dump_flags);
x86emu_log(emu, "; - - -\n");
}
x86emu_log(emu, "=== emulation log %d end ===\n", vm->exec_count);
x86emu_clear_log(emu, 1);
vm->exec_count++;
return err;
}
int vm_prepare(vm_t *vm)
{
int ok = 0;
unsigned u;
unsigned char *p1, *p2;
LPRINTF("=== bios setup ===\n");
p1 = map_mem(vm, 0, 0x1000, 0);
if(!p1) {
LPRINTF("failed to read /dev/mem\n");
return ok;
}
copy_to_vm(vm->emu, 0x10*4, p1 + 0x10*4, 4, X86EMU_PERM_RW); // video bios entry
copy_to_vm(vm->emu, 0x42*4, p1 + 0x42*4, 4, X86EMU_PERM_RW); // old video bios entry
copy_to_vm(vm->emu, 0x6d*4, p1 + 0x6d*4, 4, X86EMU_PERM_RW); // saved video bios entry
copy_to_vm(vm->emu, 0x400, p1 + 0x400, 0x100, X86EMU_PERM_RW);
munmap(p1, 0x1000);
p2 = map_mem(vm, VBIOS_ROM, VBIOS_ROM_SIZE, 0);
if(!p2 || p2[0] != 0x55 || p2[1] != 0xaa || p2[2] == 0) {
if(p2) munmap(p2, VBIOS_ROM_SIZE);
LPRINTF("error: no video bios\n");
return ok;
}
copy_to_vm(vm->emu, VBIOS_ROM, p2, p2[2] * 0x200, X86EMU_PERM_RX);
munmap(p2, VBIOS_ROM_SIZE);
LPRINTF("video bios: size 0x%04x\n", x86emu_read_byte(vm->emu, VBIOS_ROM + 2) * 0x200);
LPRINTF("video bios: entry 0x%04x:0x%04x\n",
x86emu_read_word(vm->emu, 0x10*4 + 2),
x86emu_read_word(vm->emu, 0x10*4)
);
// initialize fake video memory
for(u = VBIOS_MEM; u < VBIOS_MEM + VBIOS_MEM_SIZE; u++) {
vm_write_byte(vm->emu, u, 0, X86EMU_PERM_RW);
}
// start address 0:0x7c00
x86emu_set_seg_register(vm->emu, vm->emu->x86.R_CS_SEL, 0);
vm->emu->x86.R_EIP = 0x7c00;
// int 0x10 ; hlt
vm_write_word(vm->emu, 0x7c00, 0x10cd, X86EMU_PERM_RX);
vm_write_byte(vm->emu, 0x7c02, 0xf4, X86EMU_PERM_RX);
// stack & buffer space
x86emu_set_perm(vm->emu, VBE_BUF, 0xffff, X86EMU_PERM_RW);
vm->emu->timeout = vm->timeout ?: 20;
vm->old_memio = x86emu_set_memio_handler(vm->emu, new_memio);
ok = 1;
return ok;
}
/*
* Use our own memory and i/o access handler to block all i/o accesses if vm->no_io
* is set.
*/
unsigned new_memio(x86emu_t *emu, u32 addr, u32 *val, unsigned type)
{
vm_t *vm = emu->private;
if(vm->no_io) {
if((type & ~0xff) == X86EMU_MEMIO_I) {
*val = 0;
return 0;
}
if((type & ~0xff) == X86EMU_MEMIO_O) {
return 0;
}
}
return vm->old_memio(emu, addr, val, type);
}
double get_time()
{
static struct timeval t0 = { };
struct timeval t1 = { };
gettimeofday(&t1, NULL);
if(!timerisset(&t0)) t0 = t1;
timersub(&t1, &t0, &t1);
return t1.tv_sec + t1.tv_usec / 1e6;
}
void *map_mem(vm_t *vm, unsigned start, unsigned size, int rw)
{
int fd;
void *p;
if(!size) return NULL;
fd = open("/dev/mem", rw ? O_RDWR : O_RDONLY);
if(fd == -1) return NULL;
p = mmap(NULL, size, rw ? PROT_READ | PROT_WRITE : PROT_READ, MAP_SHARED, fd, start);
if(p == MAP_FAILED) {
LPRINTF("error: [0x%x, %u]: mmap failed: %s\n", start, size, strerror(errno));
close(fd);
return NULL;
}
LPRINTF("[0x%x, %u]: mmap ok\n", start, size);
close(fd);
return p;
}
void list_modes(vm_t *vm, vbe_info_t *vbe)
{
x86emu_t *emu = NULL;
int err = 0, i;
double d1, d2;
unsigned char buf2[0x100], tmp[0x100];
unsigned u, ml;
unsigned modelist[0x100];
unsigned bpp;
int res_bpp;
vbe_mode_info_t *mi;
char s[64];
LPRINTF("=== running bios\n");
emu = x86emu_clone(vm->emu);
emu->x86.R_EAX = 0x4f00;
emu->x86.R_EBX = 0;
emu->x86.R_ECX = 0;
emu->x86.R_EDX = 0;
emu->x86.R_EDI = VBE_BUF;
x86emu_write_dword(emu, VBE_BUF, 0x32454256); // "VBE2"
err = vm_run(emu, &d1);
LPRINTF("=== vbe get info: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
emu->x86.R_AX == 0x4f ? "ok" : "failed",
d1,
emu->x86.R_EAX,
err
);
if(!err && emu->x86.R_AX == 0x4f) {
LPRINTF("=== vbe info\n");
vbe->ok = 1;
vbe->version = x86emu_read_word(emu, VBE_BUF + 0x04);
vbe->oem_version = x86emu_read_word(emu, VBE_BUF + 0x14);
vbe->memory = x86emu_read_word(emu, VBE_BUF + 0x12) << 16;
LPRINTF(
"version = %u.%u, oem version = %u.%u\n",
vbe->version >> 8, vbe->version & 0xff, vbe->oem_version >> 8, vbe->oem_version & 0xff
);
LPRINTF("memory = %uk\n", vbe->memory >> 10);
buf2[sizeof buf2 - 1] = 0;
u = vm_read_segofs16(emu, VBE_BUF + 0x06);
copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
vbe->oem_name = canon_str(buf2, strlen(buf2));
LPRINTF("oem name [0x%05x] = \"%s\"\n", u, vbe->oem_name);
u = vm_read_segofs16(emu, VBE_BUF + 0x16);
copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
vbe->vendor_name = canon_str(buf2, strlen(buf2));
LPRINTF("vendor name [0x%05x] = \"%s\"\n", u, vbe->vendor_name);
u = vm_read_segofs16(emu, VBE_BUF + 0x1a);
copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
vbe->product_name = canon_str(buf2, strlen(buf2));
LPRINTF("product name [0x%05x] = \"%s\"\n", u, vbe->product_name);
u = vm_read_segofs16(emu, VBE_BUF + 0x1e);
copy_from_vm(emu, buf2, u, sizeof buf2 - 1);
vbe->product_revision = canon_str(buf2, strlen(buf2));
LPRINTF("product revision [0x%05x] = \"%s\"\n", u, vbe->product_revision);
ml = vm_read_segofs16(emu, VBE_BUF + 0x0e);
for(vbe->modes = 0; vbe->modes < sizeof modelist / sizeof *modelist; ) {
u = x86emu_read_word(emu, ml + 2 * vbe->modes);
if(u == 0xffff) break;
modelist[vbe->modes++] = u;
}
LPRINTF("%u video modes:\n", vbe->modes);
vbe->mode = new_mem(vbe->modes * sizeof *vbe->mode);
for(i = 0; i < vbe->modes; i++) {
mi = vbe->mode + i;
mi->number = modelist[i];
emu = x86emu_done(emu);
emu = x86emu_clone(vm->emu);
emu->x86.R_EAX = 0x4f01;
emu->x86.R_EBX = 0;
emu->x86.R_ECX = modelist[i];
emu->x86.R_EDI = VBE_BUF;
err = vm_run(emu, &d1);
d2 += d1;
LPRINTF("=== vbe mode info [0x%04x]: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
modelist[i],
emu->x86.R_AX == 0x4f ? "ok" : "failed",
d1,
emu->x86.R_EAX,
err
);
if(err || emu->x86.R_AX != 0x4f) continue;
copy_from_vm(emu, tmp, VBE_BUF, sizeof tmp);
mi->attributes = tmp[0x00] + (tmp[0x01] << 8);
mi->width = tmp[0x12] + (tmp[0x13] << 8);
mi->height = tmp[0x14] + (tmp[0x15] << 8);
mi->bytes_p_line = tmp[0x10] + (tmp[0x11] << 8);
mi->win_A_start = (tmp[0x08] + (tmp[0x09] << 8)) << 4;
mi->win_B_start = (tmp[0x0a] + (tmp[0x0b] << 8)) << 4;
mi->win_A_attr = tmp[0x02];
mi->win_B_attr = tmp[0x03];
mi->win_gran = (tmp[0x04] + (tmp[0x05] << 8)) << 10;
mi->win_size = (tmp[0x06] + (tmp[0x07] << 8)) << 10;
bpp = res_bpp = 0;
switch(tmp[0x1b]) {
case 0:
bpp = -1;
break;
case 1:
bpp = 2;
break;
case 2:
bpp = 1;
break;
case 3:
bpp = 4;
break;
case 4:
bpp = 8;
break;
case 6:
bpp = tmp[0x1f] + tmp[0x21] + tmp[0x23];
res_bpp = tmp[0x19] - bpp;
if(res_bpp < 0) res_bpp = 0;
}
if(vbe->version >= 0x0200) {
mi->fb_start = tmp[0x28] + (tmp[0x29] << 8) + (tmp[0x2a] << 16) + (tmp[0x2b] << 24);
}
if(vbe->version >= 0x0300) {
mi->pixel_clock = tmp[0x3e] + (tmp[0x3f] << 8) + (tmp[0x40] << 16) + (tmp[0x41] << 24);
}
mi->pixel_size = bpp;
if(bpp == -1u) {
LPRINTF(" 0x%04x[%02x]: %ux%u, text\n", mi->number, mi->attributes, mi->width, mi->height);
}
else {
if(
(mi->attributes & 1) && /* mode is supported */
mi->fb_start
) {
if(!vbe->fb_start) vbe->fb_start = mi->fb_start;
}
*s = 0;
if(res_bpp) sprintf(s, "+%d", res_bpp);
LPRINTF(
" 0x%04x[%02x]: %ux%u+%u, %u%s bpp",
mi->number, mi->attributes, mi->width, mi->height, mi->bytes_p_line, mi->pixel_size, s
);
if(mi->pixel_clock) LPRINTF(", max. %u MHz", mi->pixel_clock/1000000);
if(mi->fb_start) LPRINTF(", fb: 0x%08x", mi->fb_start);
LPRINTF(", %04x.%x", mi->win_A_start, mi->win_A_attr);
if(mi->win_B_start || mi->win_B_attr) LPRINTF("/%04x.%x", mi->win_B_start, mi->win_B_attr);
LPRINTF(": %uk", mi->win_size >> 10);
if(mi->win_gran != mi->win_size) LPRINTF("/%uk", mi->win_gran >> 10);
LPRINTF("\n");
}
}
}
else {
LPRINTF("=== no vbe info\n");
}
x86emu_done(emu);
}
void probe_all(vm_t *vm, vbe_info_t *vbe)
{
x86emu_t *emu = NULL;
int err = 0, i;
unsigned port, cnt;
double d1, d2, timeout;
unsigned char edid[0x80];
LPRINTF("=== running bios\n");
timeout = get_time() + (vm->timeout ?: 20);
for(port = 0; port < vm->ports; port++) {
d1 = d2 = 0;
for(cnt = 0; cnt < 2 && get_time() <= timeout; cnt++) {
if(!vm->force) {
emu = x86emu_done(emu);
emu = x86emu_clone(vm->emu);
emu->x86.R_EAX = 0x4f15;
emu->x86.R_EBX = 0;
emu->x86.R_ECX = port;
emu->x86.R_EDX = 0;
emu->x86.R_EDI = 0;
emu->x86.R_ES = 0;
err = vm_run(emu, &d1);
d2 += d1;
LPRINTF("=== port %u, try %u: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
port,
cnt,
emu->x86.R_AX == 0x4f ? "ok" : "failed",
d1,
emu->x86.R_EAX,
err
);
if(err || emu->x86.R_AX != 0x4f) continue;
LPRINTF("=== port %u, try %u: bh = %d, bl = 0x%02x\n",
port,
cnt,
emu->x86.R_BH,
emu->x86.R_BL
);
if(!(emu->x86.R_BL & 3)) {
err = -1;
continue;
}
}
emu = x86emu_done(emu);
emu = x86emu_clone(vm->emu);
emu->x86.R_EAX = 0x4f15;
emu->x86.R_EBX = 1;
emu->x86.R_ECX = port;
emu->x86.R_EDX = 0;
emu->x86.R_EDI = VBE_BUF;
err = vm_run(emu, &d1);
d2 += d1;
LPRINTF("=== port %u, try %u: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
port,
cnt,
emu->x86.R_AX == 0x4f ? "ok" : "failed",
d1,
emu->x86.R_EAX,
err
);
if(err || emu->x86.R_AX == 0x4f) break;
}
if(!emu) {
LPRINTF("=== timeout\n");
break;
}
LPRINTF("=== port %u: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
port,
emu->x86.R_AX == 0x4f ? "ok" : "failed",
d2,
emu->x86.R_EAX,
err
);
copy_from_vm(emu, edid, VBE_BUF, sizeof edid);
LPRINTF("=== port %u: ddc data ===\n", port);
for(i = 0; i < 0x80; i++) {
LPRINTF("%02x", edid[i]);
LPRINTF((i & 15) == 15 ? "\n" : " ");
}
LPRINTF("=== port %u: ddc data end ===\n", port);
if(!err && emu->x86.R_AX == 0x4f) {
LPRINTF("=== port %u: monitor info ok\n", port);
vbe->ok = 1;
vbe->ddc_ports = port + 1;
memcpy(vbe->ddc_port[port], edid, sizeof *vbe->ddc_port);
}
else {
if(!err) err = -1;
LPRINTF("=== port %u: no monitor info\n", port);
}
emu = x86emu_done(emu);
}
}
void get_video_mode(vm_t *vm, vbe_info_t *vbe)
{
x86emu_t *emu = NULL;
int err = 0;
double d;
LPRINTF("=== running bios\n");
emu = x86emu_clone(vm->emu);
emu->x86.R_EAX = 0x4f03;
emu->x86.R_EBX = 0;
err = vm_run(emu, &d);
LPRINTF("=== vbe get current video mode: %s (time %.3fs, eax 0x%x, err = 0x%x)\n",
emu->x86.R_AX == 0x4f ? "ok" : "failed",
d,
emu->x86.R_EAX,
err
);
if(!err && emu->x86.R_AX == 0x4f) {
vbe->ok = 1;
vbe->current_mode = emu->x86.R_BX;
LPRINTF("=== current mode: 0x%04x\n", vbe->current_mode);
}
else {
LPRINTF("=== current mode: no info\n");
}
x86emu_done(emu);
}
#endif /* defined(__i386__) || defined (__x86_64__) */

240
src/hd/memory.c Normal file
View File

@ -0,0 +1,240 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "hd.h"
#include "hd_int.h"
#include "memory.h"
#include "klog.h"
/**
* @defgroup MEMint Memory information
* @ingroup libhdINFOint
* @brief Memory information functions
*
* @{
*/
uint64_t kcore_mem(hd_data_t *hd_data);
uint64_t klog_mem(hd_data_t *hd_data, uint64_t *alt);
uint64_t klog_mem2(hd_data_t *hd_data);
uint64_t meminfo_mem(hd_data_t *hd_data);
uint64_t meminfo_xen(hd_data_t *hd_data);
void hd_scan_memory(hd_data_t *hd_data)
{
hd_t *hd;
uint64_t kcore, klog, klog_alt, klog2, meminfo, msize0, msize1, memxen, u;
hd_res_t *res;
int i;
int exact;
if(!hd_probe_feature(hd_data, pr_memory)) return;
hd_data->module = mod_memory;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(1, 0, "main memory size");
kcore = kcore_mem(hd_data);
klog = klog_mem(hd_data, &klog_alt);
klog2 = klog_mem2(hd_data);
if(klog2 > klog) klog = klog2;
meminfo = meminfo_mem(hd_data);
memxen = meminfo_xen(hd_data);
msize0 = meminfo > klog ? meminfo : klog;
if(!msize0) msize0 = kcore;
msize1 = msize0;
exact = 0;
/* trust kcore value if it's approx. msize0 */
if(msize0 && kcore >= msize0 && ((kcore - msize0) << 4) / msize0 == 0) {
/* be a bit more restrictive here */
if(((kcore - msize0) << 6) / msize0 == 0) {
msize1 = kcore;
exact = 1;
}
msize0 = kcore;
}
if(meminfo > msize1) { msize1 = meminfo; exact = 0; }
if(klog_alt > msize0) msize0 = klog_alt;
// if we are in a xen vm, trust this value
if(memxen) {
msize0 = msize1 = memxen;
exact = 1;
}
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_internal;
hd->sub_class.id = sc_int_main_mem;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->mem.type = res_mem;
res->mem.range = msize0;
res->mem.access = acc_rw;
res->mem.enabled = 1;
/* round it somewhat */
for(i = 0, u = msize1; u; i++) {
u >>= 1;
}
if(i > 10) { /* We *do* have at least 1k memory, do we? */
msize1 >>= i - (exact ? 8 : 5);
msize1++;
msize1 >>= 1;
msize1 <<= i - (exact ? 7 : 4);
}
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->phys_mem.type = res_phys_mem;
res->phys_mem.range = msize1;
}
uint64_t kcore_mem(hd_data_t *hd_data)
{
uint64_t u = 0;
size_t ps = getpagesize();
struct stat sb;
if(!stat(PROC_KCORE, &sb)) {
u = sb.st_size;
if(u > ps) u -= ps;
#if 0
/* we'll assume no mem modules with less than 256k */
u += 1 << 17;
u &= -1 << 18;
#endif
}
ADD2LOG(" kcore mem: 0x%"PRIx64"\n", u);
return u;
}
uint64_t klog_mem(hd_data_t *hd_data, uint64_t *alt)
{
uint64_t u = 0, u0, u1, u2, u3, mem0 = 0, mem1 = 0;
str_list_t *sl;
char *s;
int i;
if(!hd_data->klog) read_klog(hd_data);
for(sl = hd_data->klog; sl; sl = sl->next) {
if(strstr(sl->str, "<6>Memory: ") == sl->str) {
if(sscanf(sl->str, "<6>Memory: %"SCNu64"k/%"SCNu64"k", &u0, &u1) == 2) {
mem0 = u1 << 10;
}
if(
(i = sscanf(sl->str, "<6>Memory: %"SCNu64"k available (%"SCNu64"k kernel code, %"SCNu64"k data, %"SCNu64"k", &u0, &u1, &u2, &u3)) == 4 || i == 1
) {
mem0 = (i == 1 ? u0 : u0 + u1 + u2 + u3) << 10;
}
if(
(s = strstr(sl->str, "[")) &&
sscanf(s, "[%"SCNx64",%"SCNx64"]", &u0, &u1) == 2 &&
u1 > u0
) {
mem1 = u1 - u0;
}
break;
}
}
u = mem0 ? mem0 : mem1;
#if 0
/* round it somewhat */
for(i = 0, u0 = u; u0; i++) {
u0 >>= 1;
}
if(i > 10) { /* We *do* have at least 1k memory, do we? */
u >>= i - 6;
u++;
u >>= 1;
u <<= i - 5;
}
#endif
ADD2LOG(" klog mem 0: 0x%"PRIx64"\n", mem0);
ADD2LOG(" klog mem 1: 0x%"PRIx64"\n", mem1);
ADD2LOG(" klog mem: 0x%"PRIx64"\n", u);
*alt = mem1;
return u;
}
uint64_t klog_mem2(hd_data_t *hd_data)
{
uint64_t u0, u1, mem = 0;
str_list_t *sl;
char buf[64];
if(!hd_data->klog) read_klog(hd_data);
for(sl = hd_data->klog; sl; sl = sl->next) {
if(strstr(sl->str, "<6>BIOS-provided physical RAM map:") == sl->str) {
for(sl = sl->next ; sl; sl = sl->next) {
ADD2LOG(" -- %s", sl->str);
if(sscanf(sl->str, "<%*d> BIOS-e820: %"SCNx64" - %"SCNx64" (%63s", &u0, &u1, buf) != 3) break;
if(strcmp(buf, "usable)")) continue;
if(u1 < u0) break;
mem += u1 - u0;
}
break;
}
}
ADD2LOG(" bios mem: 0x%"PRIx64"\n", mem);
return mem;
}
uint64_t meminfo_mem(hd_data_t *hd_data)
{
uint64_t u = 0, u0;
str_list_t *sl;
sl = read_file(PROC_MEMINFO, 0, 1);
if(sl && sscanf(sl->str, "MemTotal: %"SCNu64"", &u0) == 1) {
u = u0 << 10;
}
free_str_list(sl);
ADD2LOG(" meminfo: 0x%"PRIx64"\n", u);
return u;
}
uint64_t meminfo_xen(hd_data_t *hd_data)
{
uint64_t u = 0, u0;
str_list_t *sl;
sl = read_file(PROC_XEN_BALLOON, 0, 1);
if(sl && sscanf(sl->str, "Current allocation: %"SCNu64"", &u0) == 1) {
u = u0 << 10;
}
free_str_list(sl);
ADD2LOG(" xen balloon: 0x%"PRIx64"\n", u);
return u;
}
/** @} */

1
src/hd/memory.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_memory(hd_data_t *hd_data);

735
src/hd/misc.c Normal file
View File

@ -0,0 +1,735 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/hdreg.h>
#include "hd.h"
#include "hd_int.h"
#include "misc.h"
#include "klog.h"
static void read_ioports(misc_t *m);
static void read_dmas(misc_t *m);
static void read_irqs(misc_t *m);
static int active_vga_card(hd_t *);
static void dump_misc_proc_data(hd_data_t *hd_data);
static void dump_misc_data(hd_data_t *hd_data);
/**
* @defgroup MISCint Misc information
* @ingroup libhdINFOint
* @brief Misc information functions
*
* @{
*/
void hd_scan_misc(hd_data_t *hd_data)
{
hd_t *hd;
hd_res_t *res;
int fd, i;
char *s = NULL;
bios_info_t *bt = NULL;
char par[] = "parport0";
int fd_ser0, fd_ser1;
if(!hd_probe_feature(hd_data, pr_misc)) return;
hd_data->module = mod_misc;
/* some clean-up */
remove_hd_entries(hd_data);
hd_data->misc = free_misc(hd_data->misc);
PROGRESS(9, 0, "kernel log");
read_klog(hd_data);
PROGRESS(1, 0, "misc data");
hd_data->misc = new_mem(sizeof *hd_data->misc);
/* this is enough to load the module */
fd_ser0 = fd_ser1 = -1;
#if !defined(__sparc__)
/* On sparc, the close needs too long */
if(hd_probe_feature(hd_data, pr_misc_serial)) {
PROGRESS(1, 1, "open serial");
fd_ser0 = open("/dev/ttyS0", O_RDONLY | O_NONBLOCK);
fd_ser1 = open("/dev/ttyS1", O_RDONLY | O_NONBLOCK);
/* keep the devices open until the resources have been read */
}
#endif
/* this is enough to load the module */
if(!hd_data->flags.no_parport && hd_probe_feature(hd_data, pr_misc_par)) {
PROGRESS(1, 2, "open parallel");
/* what can the BIOS tell us? */
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_internal &&
hd->sub_class.id == sc_int_bios &&
hd->detail &&
hd->detail->type == hd_detail_bios &&
hd->detail->bios.data
) break;
}
if(hd) {
bt = hd->detail->bios.data;
if(bt->par_port0) {
str_printf(&s, 0, "io=0x%x", bt->par_port0);
if(bt->par_port1) {
str_printf(&s, -1, ",0x%x", bt->par_port1);
if(bt->par_port2) str_printf(&s, -1, ",0x%x", bt->par_port2);
}
str_printf(&s, -1, " irq=none,none,none");
}
unload_module(hd_data, "parport_probe");
unload_module(hd_data, "lp");
unload_module(hd_data, "parport_pc");
unload_module(hd_data, "parport");
/* now load it with the right io */
load_module(hd_data, "parport");
load_module_with_params(hd_data, "parport_pc", s);
free_mem(s);
}
/* now load the rest of the modules */
fd = open("/dev/lp0", O_RDONLY | O_NONBLOCK);
if(fd >= 0) close(fd);
}
/*
* floppy driver resources are allocated only temporarily,
* so we access it just before we read the resources
*/
if(hd_probe_feature(hd_data, pr_misc_floppy)) {
/* look for a floppy *device* entry... */
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_storage_device &&
hd->sub_class.id == sc_sdev_floppy &&
hd->unix_dev_name &&
!strncmp(hd->unix_dev_name, "/dev/fd", sizeof "/dev/fd" - 1)
) {
PROGRESS(1, 3, "read floppy");
i = 5;
hd->block0 = read_block0(hd_data, hd->unix_dev_name, &i);
hd->is.notready = hd->block0 ? 0 : 1;
if(i < 0) {
hd->tag.remove = 1;
ADD2LOG("misc.floppy: removing floppy entry %u (timed out)\n", hd->idx);
}
if(!hd->is.notready) {
struct hd_geometry geo;
int fd;
unsigned size, blk_size = 0x200;
fd = open(hd->unix_dev_name, O_RDONLY | O_NONBLOCK);
if(fd >= 0) {
if(!ioctl(fd, HDIO_GETGEO, &geo)) {
ADD2LOG("floppy ioctl(geo) ok\n");
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->disk_geo.type = res_disk_geo;
res->disk_geo.cyls = geo.cylinders;
res->disk_geo.heads = geo.heads;
res->disk_geo.sectors = geo.sectors;
res->disk_geo.geotype = geo_logical;
size = geo.cylinders * geo.heads * geo.sectors;
for(res = hd->res; res; res = res->next) {
if(res->any.type == res_size && res->size.unit == size_unit_sectors) {
res->size.val1 = size; res->size.val2 = blk_size;
break;
}
}
if(!res) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->size.type = res_size;
res->size.unit = size_unit_sectors;
res->size.val1 = size; res->size.val2 = blk_size;
}
}
close(fd);
}
}
break;
}
}
remove_tagged_hd_entries(hd_data);
}
PROGRESS(2, 1, "io");
read_ioports(hd_data->misc);
PROGRESS(2, 2, "dma");
read_dmas(hd_data->misc);
PROGRESS(2, 3, "irq");
read_irqs(hd_data->misc);
if((hd_data->debug & HD_DEB_MISC)) dump_misc_proc_data(hd_data);
if(fd_ser0 >= 0) close(fd_ser0);
if(fd_ser1 >= 0) close(fd_ser1);
/* now create some system generic entries */
/* FPU */
PROGRESS(3, 0, "FPU");
res = NULL;
gather_resources(hd_data->misc, &res, "fpu", 0);
if(res) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_internal;
hd->sub_class.id = sc_int_fpu;
hd->res = res;
}
/* DMA */
PROGRESS(3, 1, "DMA");
res = NULL;
gather_resources(hd_data->misc, &res, "dma1", 0);
gather_resources(hd_data->misc, &res, "dma2", 0);
gather_resources(hd_data->misc, &res, "dma page reg", 0);
gather_resources(hd_data->misc, &res, "cascade", W_DMA);
if(res) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_system;
hd->sub_class.id = sc_sys_dma;
hd->res = res;
}
/* PIC */
PROGRESS(3, 2, "PIC");
res = NULL;
gather_resources(hd_data->misc, &res, "pic1", 0);
gather_resources(hd_data->misc, &res, "pic2", 0);
gather_resources(hd_data->misc, &res, "cascade", W_IRQ);
if(res) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_system;
hd->sub_class.id = sc_sys_pic;
hd->res = res;
}
/* timer */
PROGRESS(3, 3, "timer");
res = NULL;
gather_resources(hd_data->misc, &res, "timer", 0);
if(res) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_system;
hd->sub_class.id = sc_sys_timer;
hd->res = res;
}
/* real time clock */
PROGRESS(3, 4, "RTC");
res = NULL;
gather_resources(hd_data->misc, &res, "rtc", 0);
if(res) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_system;
hd->sub_class.id = sc_sys_rtc;
hd->res = res;
}
/* keyboard */
res = NULL;
gather_resources(hd_data->misc, &res, "keyboard", 0);
if(res) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_input;
hd->sub_class.id = sc_inp_keyb;
hd->res = res;
}
/* parallel ports */
for(i = 0; i < 1; i++, par[sizeof par - 2]++) {
res = NULL;
gather_resources(hd_data->misc, &res, par, 0);
if(res) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_comm;
hd->sub_class.id = sc_com_par;
str_printf(&hd->unix_dev_name, 0, "/dev/lp%d", i);
hd->res = res;
}
}
/* floppy controller */
res = NULL;
gather_resources(hd_data->misc, &res, "floppy", 0);
gather_resources(hd_data->misc, &res, "floppy DIR", 0);
if(res) {
/* look for an existing entry */
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_storage && hd->sub_class.id == sc_sto_floppy) break;
}
/* missing, so create one */
if(!hd) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_storage;
hd->sub_class.id = sc_sto_floppy;
}
hd->res = res;
}
/*
* look for PS/2 port
*
* The catch is, that sometimes /dev/psaux is accessible only for root,
* so the open() may fail but there are irq events registered.
*
*/
fd = open(DEV_PSAUX, O_RDONLY | O_NONBLOCK);
if(fd >= 0) close(fd);
res = NULL;
gather_resources(hd_data->misc, &res, "PS/2 Mouse", 0);
if(res || fd >= 0) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_ps2;
if(res) {
hd->res = res;
}
}
}
void hd_scan_misc2(hd_data_t *hd_data)
{
hd_t *hd, *hd1;
misc_t *m;
hd_res_t *res, *res1, *res2;
int i;
if(!hd_probe_feature(hd_data, pr_misc)) return;
hd_data->module = mod_misc;
PROGRESS(5, 0, "misc data");
/* create some more system generic entries */
/* IDE */
// ###### add special ide detail to hd_t!!!
res = NULL;
gather_resources(hd_data->misc, &res, "ide0", 0);
gather_resources(hd_data->misc, &res, "ide1", 0);
gather_resources(hd_data->misc, &res, "ide2", 0);
gather_resources(hd_data->misc, &res, "ide3", 0);
if(res) {
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_storage &&
hd->sub_class.id == sc_sto_ide &&
have_common_res(hd->res, res)
) break;
}
if(hd) {
/* eg. PCI IDE controller, add resources */
join_res_io(&hd->res, res);
join_res_irq(&hd->res, res);
join_res_dma(&hd->res, res);
}
free_res_list(res);
}
/* VGA */
res = NULL;
gather_resources(hd_data->misc, &res, "vga+", 0);
gather_resources(hd_data->misc, &res, "vesafb", 0);
if(res) {
for(i = 0, hd1 = NULL, hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_display && hd->sub_class.id == sc_dis_vga) {
i++;
hd1 = hd;
}
}
if(i == 0) {
/* non-PCI VGA card ??? - really, we shouldn't care... */
/* FIX THIS !!! ############### */
#ifdef __alpha__
free_res_list(res);
#else
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_display;
hd->sub_class.id = sc_dis_vga;
hd->res = res;
#endif
}
else if(i == 1) {
/* 1 PCI vga card, add resources */
join_res_io(&hd1->res, res);
join_res_irq(&hd1->res, res);
join_res_dma(&hd1->res, res);
free_res_list(res);
hd_data->display = hd1->idx;
}
else {
/* more than 1: look again, now only 'active' cards */
for(i = 0, hd1 = NULL, hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_display &&
hd->sub_class.id == sc_dis_vga &&
active_vga_card(hd)
) {
i++;
hd1 = hd;
}
}
if(i == 1) {
/* 'the' active PCI vga card, add resources */
join_res_io(&hd1->res, res);
join_res_irq(&hd1->res, res);
join_res_dma(&hd1->res, res);
hd_data->display = hd1->idx;
}
else {
/* now, what??? */
ADD2LOG("Oopy, could not figure out *the* active display adapter!\n");
}
free_res_list(res);
}
}
/* serial ports */
res = NULL;
gather_resources(hd_data->misc, &res, "serial(auto)", 0);
gather_resources(hd_data->misc, &res, "serial(set)", 0);
gather_resources(hd_data->misc, &res, "serial", 0);
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_comm && hd->sub_class.id == sc_com_ser) {
for(res1 = hd->res; res1; res1 = res1->next) {
for(res2 = res; res2; res2 = res2->next) {
if(res1->any.type == res2->any.type) {
switch(res1->any.type) {
case res_irq:
if(res1->irq.base == res2->irq.base) {
res2->any.type = res_any;
}
break;
case res_io:
if(
res1->io.base == res2->io.base &&
(!res1->io.range || res1->io.range == res2->io.range)
) {
res1->io.range = res2->io.range;
res2->any.type = res_any;
}
break;
default: /* gcc -Wall */
break;
}
}
}
}
}
}
/* if any of the serial resources are unaccounted for, make an extra entry */
for(res2 = res; res2; res2 = res2->next) {
if(res2->any.type != res_any) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_comm;
hd->sub_class.id = sc_com_ser;
hd->prog_if.id = 0x80;
for(; res2; res2 = res2->next) {
if(res2->any.type != res_any) {
res1 = add_res_entry(&hd->res, new_mem(sizeof *res));
*res1 = *res2;
res1->next = NULL;
}
}
break;
}
}
free_res_list(res);
/* go through our list and assign event counts to irq entries */
m = hd_data->misc;
for(hd = hd_data->hd; hd; hd = hd->next) {
for(res = hd->res; res; res = res->next) {
if(res->irq.type == res_irq) {
for(i = 0; (unsigned) i < m->irq_len; i++) {
if(res->irq.base == m->irq[i].irq) {
res->irq.triggered = m->irq[i].events;
break;
}
}
}
}
}
/* look for entries with matching start address */
m = hd_data->misc;
for(hd = hd_data->hd; hd; hd = hd->next) {
for(res = hd->res; res; res = res->next) {
if(res->io.type == res_io) {
for(i = 0; (unsigned) i < m->io_len; i++) {
if(res->io.base == m->io[i].addr && res->io.range < m->io[i].size) {
res->io.range = m->io[i].size;
break;
}
}
}
}
}
if((hd_data->debug & HD_DEB_MISC)) dump_misc_data(hd_data);
}
/*
* read /proc/ioports
*/
void read_ioports(misc_t *m)
{
char buf[100];
misc_io_t *r;
uint64_t u, v;
str_list_t *sl;
if(!(m->proc_io = read_file(PROC_IOPORTS, 0, 0))) return;
for(sl = m->proc_io; sl; sl = sl->next) {
if(sscanf(sl->str, " %"PRIx64" - %"PRIx64" : %99[^\n]", &u, &v, buf) == 3) {
m->io = add_mem(m->io, sizeof *m->io, m->io_len);
r = m->io + m->io_len++;
r->addr = u;
r->size = v >= u ? v - u + 1 : 0;
r->dev = new_str(buf);
}
}
}
/*
* read /proc/dma
*/
void read_dmas(misc_t *m)
{
char buf[100];
misc_dma_t *d;
unsigned u;
str_list_t *sl;
if(!(m->proc_dma = read_file(PROC_DMA, 0, 0))) return;
for(sl = m->proc_dma; sl; sl = sl->next) {
if(sscanf(sl->str, " %u : %99[^\n]", &u, buf) == 2) {
m->dma = add_mem(m->dma, sizeof *m->dma, m->dma_len);
d = m->dma + m->dma_len++;
d->channel = u;
d->dev = new_str(buf);
}
}
}
/*
* read /proc/interrupts
*
* This is somewhat more tricky, as the irq event counts are done separately
* per cpu *and* there may be irq sharing.
*/
void read_irqs(misc_t *m)
{
char buf[100], buf2[100], *s;
misc_irq_t *ir;
int i, j;
unsigned u, v, k;
str_list_t *sl;
if(!(m->proc_irq = read_file(PROC_INTERRUPTS, 1, 0))) return;
for(sl = m->proc_irq; sl; sl = sl->next) {
/* irq */
i = 0;
if(sscanf(sl->str, " %u: %n", &u, &i) < 1) continue;
v = 0;
j = i;
/* add up all event counters */
while(j < (int) strlen(sl->str) && sscanf(sl->str + j, " %u %n", &k, &i) >= 1) {
if(!i) break;
v += k;
j += i;
}
/* device driver name string */
#if defined(__PPC__)
if(
sscanf(sl->str + j, " %*s Edge %99[^\n]", buf) == 1 ||
sscanf(sl->str + j, " %*s Level %99[^\n]", buf) == 1 ||
sscanf(sl->str + j, " %*s %99[^\n]", buf) == 1
) {
#else
#if defined(__alpha__) || defined(__sparc__)
if(sscanf(sl->str + j, " %99[^\n]", buf) == 1) {
#else /* __i386__ || __x86_64__ || __ia64__ */
if(sscanf(sl->str + j, " %*s %99[^\n]", buf) == 1) {
#endif
#endif
m->irq = add_mem(m->irq, sizeof *m->irq, m->irq_len);
ir = m->irq + m->irq_len++;
ir->irq = u;
ir->events = v;
/* split device driver names (separated by ',') */
s = buf;
while(*s && sscanf(s, " %99[^,] %n", buf2, &j) >= 1) {
ir->dev = add_mem(ir->dev, sizeof *ir->dev, ir->devs);
ir->dev[ir->devs++] = new_str(buf2);
s += j;
if(*s) s++; /* skip ',' */
}
}
}
}
void gather_resources(misc_t *m, hd_res_t **r, char *name, unsigned which)
{
int i, j;
hd_res_t *res;
if(!m) return;
if(!which) which = W_IO | W_DMA | W_IRQ;
if((which & W_IO)) for(i = 0; (unsigned) i < m->io_len; i++) {
if(!strcmp(name, m->io[i].dev)) {
res = add_res_entry(r, new_mem(sizeof **r));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = m->io[i].addr;
res->io.range = m->io[i].size;
res->io.access = acc_rw;
m->io[i].tag++;
}
}
if((which & W_DMA)) for(i = 0; (unsigned) i < m->dma_len; i++) {
if(!strcmp(name, m->dma[i].dev)) {
res = add_res_entry(r, new_mem(sizeof **r));
res->dma.type = res_dma;
res->dma.enabled = 1;
res->dma.base = m->dma[i].channel;
m->dma[i].tag++;
}
}
if((which & W_IRQ)) for(i = 0; (unsigned) i < m->irq_len; i++) {
for(j = 0; j < m->irq[i].devs; j++) {
if(!strcmp(name, m->irq[i].dev[j])) {
res = add_res_entry(r, new_mem(sizeof **r));
res->irq.type = res_irq;
res->irq.enabled = 1;
res->irq.base = m->irq[i].irq;
res->irq.triggered = m->irq[i].events;
m->irq[i].tag++;
}
}
}
}
int active_vga_card(hd_t *hd)
{
hd_res_t *res;
if(hd->bus.id != bus_pci) return 1;
for(res = hd->res; res; res = res->next) {
if(
(res->mem.type == res_mem && res->mem.enabled) ||
(res->io.type == res_io && res->io.enabled)
) return 1;
}
return 0;
}
/*
* Add some proc info to the global log.
*/
void dump_misc_proc_data(hd_data_t *hd_data)
{
str_list_t *sl;
ADD2LOG("----- /proc/ioports -----\n");
for(sl = hd_data->misc->proc_io; sl; sl = sl->next) {
ADD2LOG(" %s", sl->str);
}
ADD2LOG("----- /proc/ioports end -----\n");
ADD2LOG("----- /proc/interrupts -----\n");
for(sl = hd_data->misc->proc_irq; sl; sl = sl->next) {
ADD2LOG(" %s", sl->str);
}
ADD2LOG("----- /proc/interrupts end -----\n");
ADD2LOG("----- /proc/dma -----\n");
for(sl = hd_data->misc->proc_dma; sl; sl = sl->next) {
ADD2LOG(" %s", sl->str);
}
ADD2LOG("----- /proc/dma end -----\n");
}
/*
* Add the resource usage to the global log.
*/
void dump_misc_data(hd_data_t *hd_data)
{
misc_t *m = hd_data->misc;
int i, j;
ADD2LOG("----- misc resources -----\n");
for(i = 0; (unsigned) i < m->io_len; i++) {
ADD2LOG(
"i/o:%u 0x%04"PRIx64" - 0x%04"PRIx64" (0x%02"PRIx64") \"%s\"\n",
m->io[i].tag,
m->io[i].addr, m->io[i].addr + m->io[i].size - 1,
m->io[i].size, m->io[i].dev
);
}
for(i = 0; (unsigned) i < m->irq_len; i++) {
ADD2LOG(
"irq:%u %2u (%9u)",
m->irq[i].tag, m->irq[i].irq, m->irq[i].events
);
for(j = 0; j < m->irq[i].devs; j++) {
ADD2LOG(" \"%s\"", m->irq[i].dev[j]);
}
ADD2LOG("\n");
}
for(i = 0; (unsigned) i < m->dma_len; i++) {
ADD2LOG(
"dma:%u %u \"%s\"\n",
m->dma[i].tag, m->dma[i].channel, m->dma[i].dev
);
}
ADD2LOG("----- misc resources end -----\n");
}
/** @} */

2
src/hd/misc.h Normal file
View File

@ -0,0 +1,2 @@
void hd_scan_misc(hd_data_t *hd_data);
void hd_scan_misc2(hd_data_t *hd_data);

1087
src/hd/modem.c Normal file

File diff suppressed because it is too large Load Diff

1
src/hd/modem.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_modem(hd_data_t *hd_data);

674
src/hd/monitor.c Normal file
View File

@ -0,0 +1,674 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hd.h"
#include "hd_int.h"
#include "hddb.h"
#include "monitor.h"
/**
* @defgroup MONITORint Monitor (DDC) information
* @ingroup libhdINFOint
* @brief Monitor information functions
* @see http://en.wikipedia.org/wiki/Extended_Display_Identification_Data#EDID_1.3_data_format
* for the detailed EDID data structure description
*
* @{
*/
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* monitor info
*
* Read the info out of the 'SuSE=' entry in /proc/cmdline. It contains
* (among others) info from the EDID record got by our syslinux extension.
*
* We will try to look up our monitor id in the id file to get additional
* info.
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
#ifdef __PPC__
static void add_old_mac_monitor(hd_data_t *hd_data);
#endif
static void add_monitor(hd_data_t *hd_data, devtree_t *dt);
static int chk_edid_info(hd_data_t *hd_data, unsigned char *edid);
static void add_lcd_info(hd_data_t *hd_data, hd_t *hd, bios_info_t *bt);
static int mi_cmp(monitor_info_t **mi0, monitor_info_t **mi1);
static void add_edid_info(hd_data_t *hd_data, hd_t *hd, unsigned char *edid);
static void add_monitor_res(hd_t *hd, unsigned x, unsigned y, unsigned hz, unsigned il);
static void fix_edid_info(hd_data_t *hd_data, unsigned char *edid);
void hd_scan_monitor(hd_data_t *hd_data)
{
hd_t *hd, *hd2;
bios_info_t *bt;
devtree_t *dt;
pci_t *pci;
int found;
unsigned u;
if(!hd_probe_feature(hd_data, pr_monitor)) return;
hd_data->module = mod_monitor;
/* some clean-up */
remove_hd_entries(hd_data);
PROGRESS(1, 0, "ddc");
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_internal && hd->sub_class.id == sc_int_bios) break;
}
/* first, see if we got the full edid record from bios */
bt = NULL;
/* for testing: LIBHD_EDID points to a file with valid edid record */
{
char *s = getenv("LIBHD_EDID");
unsigned char edid[0x80];
FILE *f;
if(s && (f = fopen(s, "r"))) {
if(fread(edid, sizeof edid, 1, f) == 1) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_monitor;
add_edid_info(hd_data, hd, edid);
}
fclose(f);
return;
}
}
PROGRESS(2, 0, "bios");
if(
hd &&
hd->detail &&
hd->detail->type == hd_detail_bios &&
(bt = hd->detail->bios.data) &&
bt->vbe.ok
) {
int pid = 0;
int got_ddc_data = 0;
for(pid = 0; pid < bt->vbe.ddc_ports; pid++) {
if(chk_edid_info(hd_data, bt->vbe.ddc_port[pid])) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_monitor;
hd_set_hw_class(hd, hw_vbe);
hd->func = pid;
add_edid_info(hd_data, hd, bt->vbe.ddc_port[pid]);
got_ddc_data = 1;
}
}
if(got_ddc_data) {
return;
}
}
PROGRESS(3, 0, "pci");
found = 0;
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd &&
hd->detail &&
hd->detail->type == hd_detail_pci &&
(pci = hd->detail->pci.data)
) {
for(u = 0; u < sizeof pci->edid_len / sizeof *pci->edid_len; u++) {
if(pci->edid_len[u] >= 0x80 && chk_edid_info(hd_data, pci->edid_data[u])) {
hd2 = add_hd_entry(hd_data, __LINE__, 0);
hd2->base_class.id = bc_monitor;
hd2->slot = u;
hd2->attached_to = hd->idx;
add_edid_info(hd_data, hd2, pci->edid_data[u]);
found = 1;
}
}
}
}
if(found) return;
PROGRESS(4, 0, "internal db");
/* Maybe a LCD panel? */
if(bt && bt->lcd.width) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_monitor;
hd->sub_class.id = sc_mon_lcd;
hd_set_hw_class(hd, hw_vbe);
add_lcd_info(hd_data, hd, bt);
return;
}
PROGRESS(5, 0, "prom");
found = 0;
for(dt = hd_data->devtree; dt; dt = dt->next) {
if(dt->edid) {
add_monitor(hd_data, dt);
found = 1;
}
}
#if defined(__PPC__)
PROGRESS(6, 0, "old mac");
if(!found) {
add_old_mac_monitor(hd_data);
}
#endif
}
#if defined(__PPC__)
void add_old_mac_monitor(hd_data_t *hd_data)
{
hd_t *hd;
unsigned u1, u2;
str_list_t *sl;
static struct {
unsigned width, height, vfreq, interlaced;
} mode_list[20] = {
{ 512, 384, 60, 1 },
{ 512, 384, 60, 0 },
{ 640, 480, 50, 1 },
{ 640, 480, 60, 1 },
{ 640, 480, 60, 0 },
{ 640, 480, 67, 0 },
{ 640, 870, 75, 0 },
{ 768, 576, 50, 1 },
{ 800, 600, 56, 0 },
{ 800, 600, 60, 0 },
{ 800, 600, 72, 0 },
{ 800, 600, 75, 0 },
{ 832, 624, 75, 0 },
{ 1024, 768, 60, 0 },
{ 1024, 768, 70, 0 },
{ 1024, 768, 75, 0 },
{ 1024, 768, 75, 0 },
{ 1152, 870, 75, 0 },
{ 1280, 960, 75, 0 },
{ 1280, 1024, 75, 0 }
};
for(sl = hd_data->klog; sl; sl = sl->next) {
if(sscanf(sl->str, "<%*d>Monitor sense value = %i, using video mode %i", &u1, &u2) == 2) {
u2--;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_monitor;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0401);
hd->device.id = MAKE_ID(TAG_SPECIAL, (u1 & 0xfff) + 0x1000);
if((u1 = hd_display_adapter(hd_data))) {
hd->attached_to = u1;
}
if(u2 < sizeof mode_list / sizeof *mode_list) {
add_monitor_res(hd, mode_list[u2].width, mode_list[u2].height, mode_list[u2].vfreq, mode_list[u2].interlaced);
}
break;
}
}
}
#endif /* defined(__PPC__) */
void add_monitor(hd_data_t *hd_data, devtree_t *dt)
{
hd_t *hd, *hd2;
unsigned char *edid = dt->edid;
if(!chk_edid_info(hd_data, edid)) return;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_monitor;
for(hd2 = hd_data->hd; hd2; hd2 = hd2->next) {
if(
hd2->detail &&
hd2->detail->type == hd_detail_devtree &&
hd2->detail->devtree.data == dt
) {
hd->attached_to = hd2->idx;
break;
}
}
add_edid_info(hd_data, hd, edid);
}
/* do some checks to ensure we got a reasonable block */
int chk_edid_info(hd_data_t *hd_data, unsigned char *edid)
{
// no vendor or model info
if(!(edid[0x08] || edid[0x09] || edid[0x0a] || edid[0x0b])) return 0;
// no edid version or revision
if(!(edid[0x12] || edid[0x13])) return 0;
return 1;
}
void add_lcd_info(hd_data_t *hd_data, hd_t *hd, bios_info_t *bt)
{
monitor_info_t *mi = NULL;
hd_res_t *res = NULL;
hd->vendor.name = new_str(bt->lcd.vendor);
hd->device.name = new_str(bt->lcd.name);
add_monitor_res(hd, bt->lcd.width, bt->lcd.height, 60, 0);
mi = new_mem(sizeof *mi);
hd->detail = new_mem(sizeof *hd->detail);
hd->detail->type = hd_detail_monitor;
hd->detail->monitor.data = mi;
mi->min_vsync = 50;
mi->min_hsync = 31;
mi->max_vsync = 75;
mi->max_hsync = (mi->max_vsync * bt->lcd.height * 12) / 10000;
if (bt->lcd.xsize) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->size.type = res_size;
res->size.unit = size_unit_mm;
res->size.val1 = bt->lcd.xsize;
res->size.val2 = bt->lcd.ysize;
}
}
int mi_cmp(monitor_info_t **mi0, monitor_info_t **mi1)
{
return (*mi0)->hdisp * (*mi0)->vdisp - (*mi1)->hdisp * (*mi1)->vdisp;
}
void add_edid_info(hd_data_t *hd_data, hd_t *hd, unsigned char *edid)
{
hd_res_t *res;
hd_detail_monitor_t *mdetail;
monitor_info_t *mi_list[4], *mi;
unsigned mi_cnt = 0;
int i;
unsigned u, u1, u2, tag;
char *s;
unsigned width_mm = 0, height_mm = 0, manu_year = 0, manu_week = 0;
unsigned min_vsync = 0, max_vsync = 0, min_hsync = 0, max_hsync = 0;
unsigned hblank, hsync_ofs, hsync, vblank, vsync_ofs, vsync;
char *vendor = NULL, *serial = NULL, *name = NULL;
fix_edid_info(hd_data, edid);
if(edid[0x14] & 0x80) {
/* digital signal -> assume lcd */
hd->sub_class.id = 2;
}
u = (edid[8] << 8) + edid[9];
hd->vendor.id = MAKE_ID(TAG_EISA, u);
u = (edid[0xb] << 8) + edid[0xa];
hd->device.id = MAKE_ID(TAG_EISA, u);
if((u = device_class(hd_data, hd->vendor.id, hd->device.id))) {
if((u >> 8) == bc_monitor) hd->sub_class.id = u & 0xff;
}
if(edid[0x15] > 0 && edid[0x16] > 0) {
width_mm = edid[0x15] * 10;
height_mm = edid[0x16] * 10;
}
u = edid[0x23];
if(u & (1 << 7)) add_monitor_res(hd, 720, 400, 70, 0);
if(u & (1 << 6)) add_monitor_res(hd, 720, 400, 88, 0);
if(u & (1 << 5)) add_monitor_res(hd, 640, 480, 60, 0);
if(u & (1 << 4)) add_monitor_res(hd, 640, 480, 67, 0);
if(u & (1 << 3)) add_monitor_res(hd, 640, 480, 72, 0);
if(u & (1 << 2)) add_monitor_res(hd, 640, 480, 75, 0);
if(u & (1 << 1)) add_monitor_res(hd, 800, 600, 56, 0);
if(u & (1 << 0)) add_monitor_res(hd, 800, 600, 60, 0);
u = edid[0x24];
if(u & (1 << 7)) add_monitor_res(hd, 800, 600, 72, 0);
if(u & (1 << 6)) add_monitor_res(hd, 800, 600, 75, 0);
if(u & (1 << 5)) add_monitor_res(hd, 832, 624, 75, 0);
if(u & (1 << 4)) add_monitor_res(hd, 1024, 768, 87, 1);
if(u & (1 << 3)) add_monitor_res(hd, 1024, 768, 60, 0);
if(u & (1 << 2)) add_monitor_res(hd, 1024, 768, 70, 0);
if(u & (1 << 1)) add_monitor_res(hd, 1024, 768, 75, 0);
if(u & (1 << 0)) add_monitor_res(hd, 1280, 1024, 75, 0);
for(i = 0; i < 4; i++) {
u1 = (edid[0x26 + 2 * i] + 31) * 8;
u2 = edid[0x27 + 2 * i];
u = 0;
switch((u2 >> 6) & 3) {
case 1: u = (u1 * 3) / 4; break;
case 2: u = (u1 * 4) / 5; break;
case 3: u = (u1 * 9) / 16; break;
}
if(u) add_monitor_res(hd, u1, u, (u2 & 0x3f) + 60, 0);
}
manu_year = 1990 + edid[0x11];
manu_week = edid[0x10];
ADD2LOG(" detailed timings:\n");
/* max. 4 mi_list[] entries */
for(i = 0x36; i < 0x36 + 4 * 0x12; i += 0x12) {
tag = (edid[i] << 24) + (edid[i + 1] << 16) + (edid[i + 2] << 8) + edid[i + 3];
ADD2LOG(" #%d: ", (i - 0x36)/0x12);
hd_log_hex(hd_data, 1, 0x12, edid + i);
ADD2LOG("\n");
switch(tag) {
case 0xfc:
if(edid[i + 5]) {
/* name entry is splitted some times */
str_printf(&name, -1, "%s%s", name ? " " : "", canon_str(edid + i + 5, 0xd));
}
break;
case 0xfd:
u = 0;
u1 = edid[i + 5];
u2 = edid[i + 6];
if(u1 > u2 || !u1) u = 1;
min_vsync = u1;
max_vsync = u2;
u1 = edid[i + 7];
u2 = edid[i + 8];
if(u1 > u2 || !u1) u = 1;
min_hsync = u1;
max_hsync = u2;
if(u) {
min_vsync = max_vsync = min_hsync = max_hsync = 0;
ADD2LOG(" ddc oops: invalid freq data\n");
}
break;
case 0xfe:
if(!vendor && edid[i + 5]) {
vendor = canon_str(edid + i + 5, 0xd);
for(s = vendor; *s; s++) if(*s < ' ') *s = ' ';
}
break;
case 0xff:
if(!serial && edid[i + 5]) {
serial = canon_str(edid + i + 5, 0xd);
for(s = serial; *s; s++) if(*s < ' ') *s = ' ';
}
break;
default:
if(tag < 0x100) {
ADD2LOG(" unknown tag 0x%02x\n", tag);
}
else {
/* check for duplicates */
for(u1 = 1, u = 0x36; u < i; u += 0x12) {
u1 = memcmp(edid + u, edid + i, 0x12);
if(!u1) break;
}
if(!u1) {
ADD2LOG(" duplicate of #%d - skipped\n", (u - 0x36)/0x12);
break;
}
mi_list[mi_cnt++] = mi = new_mem(sizeof *mi);
mi->width_mm = width_mm;
mi->height_mm = height_mm;
mi->manu_year = manu_year;
mi->manu_week = manu_week;
u = (edid[i + 0] + (edid[i + 1] << 8)) * 10; /* pixel clock in kHz */
if(!u) break;
mi->clock = u;
u1 = edid[i + 2] + ((edid[i + 4] & 0xf0) << 4);
u2 = edid[i + 5] + ((edid[i + 7] & 0xf0) << 4);
if(!u1 || !u2 || u1 == 0xfff || u2 == 0xfff) break;
mi->width = u1;
mi->height = u2;
u1 = edid[i + 12] + ((edid[i + 14] & 0xf0) << 4);
u2 = edid[i + 13] + ((edid[i + 14] & 0xf) << 8);
if(!u1 || !u2 || u1 == 0xfff || u2 == 0xfff) break;
mi->width_mm = u1;
mi->height_mm = u2;
hblank = edid[i + 3] + ((edid[i + 4] & 0xf) << 8);
hsync_ofs = edid[i + 8] + ((edid[i + 11] & 0xc0) << 2);
hsync = edid[i + 9] + ((edid[i + 11] & 0x30) << 4);
vblank = edid[i + 6] + ((edid[i + 7] & 0xf) << 8);
vsync_ofs = ((edid[i + 10] & 0xf0) >> 4) + ((edid[i + 11] & 0x0c) << 2);
vsync = (edid[i + 10] & 0xf) + ((edid[i + 11] & 0x03) << 4);
mi->hdisp = mi->width;
mi->hsyncstart = mi->width + hsync_ofs;
mi->hsyncend = mi->width + hsync_ofs + hsync;
mi->htotal = mi->width + hblank;
ADD2LOG(
" h: %4u %4u %4u %4u (+%u +%u +%u)\n",
mi->hdisp, mi->hsyncstart, mi->hsyncend, mi->htotal,
hsync_ofs, hsync_ofs + hsync, hblank
);
mi->vdisp = mi->height;
mi->vsyncstart = mi->height + vsync_ofs;
mi->vsyncend = mi->height + vsync_ofs + vsync;
mi->vtotal = mi->height + vblank;
ADD2LOG(
" v: %4u %4u %4u %4u (+%u +%u +%u)\n",
mi->vdisp, mi->vsyncstart, mi->vsyncend, mi->vtotal,
vsync_ofs, vsync_ofs + vsync, vblank
);
u = edid[i + 17];
if(((u >> 3) & 3) == 3) {
mi->hflag = (u & 4) ? '+' : '-';
mi->vflag = (u & 2) ? '+' : '-';
ADD2LOG(" %chsync %cvsync\n", mi->hflag, mi->vflag);
}
u1 = mi->width + hblank;
u2 = mi->height + vblank;
if(u1 && u2) {
ADD2LOG(
" %.1f MHz, %.1f kHz, %.1f Hz\n",
(double) mi->clock / 1000,
(double) mi->clock / u1,
(double) mi->clock / u1 / u2 * 1000
);
}
}
}
}
for(u = 0; u < mi_cnt; u++) {
mi = mi_list[u];
mi->min_vsync = min_vsync;
mi->max_vsync = max_vsync;
mi->min_hsync = min_hsync;
mi->max_hsync = max_hsync;
mi->name = new_str(name);
mi->vendor = new_str(vendor);
mi->serial = new_str(serial);
}
if(mi_cnt) {
qsort(mi_list, mi_cnt, sizeof *mi_list, (int (*)(const void *, const void *)) mi_cmp);
for(i = 0; i < mi_cnt; i++) {
mi = mi_list[i];
mdetail = hd->detail && hd->detail->type == hd_detail_monitor ? &hd->detail->monitor : NULL;
hd->detail = new_mem(sizeof *hd->detail);
hd->detail->type = hd_detail_monitor;
hd->detail->monitor.data = mi;
hd->detail->monitor.next = mdetail;
hd->serial = new_str(mi->serial);
hd->vendor.name = new_str(mi->vendor);
hd->device.name = new_str(mi->name);
if(mi->width && mi->height) {
for(res = hd->res; res; res = res->next) {
if(
res->any.type == res_monitor &&
res->monitor.width == mi->width &&
res->monitor.height == mi->height
) break;
}
/* actually we could calculate the vsync value */
if(!res) add_monitor_res(hd, mi->width, mi->height, 60, 0);
/* do some sanity checks on display size, see bugs 155096, 186096, 213630 */
if(mi->width_mm && mi->height_mm) {
u = (mi->width_mm * mi->height * 16) / (mi->height_mm * mi->width);
u1 = width_mm ? (width_mm * 16) / mi->width_mm : 16;
u2 = height_mm ? (height_mm * 16) / mi->height_mm : 16;
if(
u <= 8 || u >= 32 || /* allow 1:2 distortion */
u1 <= 8 || u1 >= 32 || /* width cm & mm values disagree by factor >2 --> use cm values */
u2 <= 8 || u2 >= 32 || /* dto, height */
mi->width_mm < 100 || /* too small to be true... */
mi->height_mm < 100
) {
ADD2LOG(" ddc: strange size data (%ux%u mm^2), trying cm values\n", mi->width_mm, mi->height_mm);
/* ok, try cm values */
if(width_mm && height_mm) {
u = (width_mm * mi->height * 16) / (height_mm * mi->width);
if(u > 8 && u < 32 && width_mm >= 100 && height_mm >= 100) {
mi->width_mm = width_mm;
mi->height_mm = height_mm;
}
}
/* could not fix, clear */
if(u <= 8 || u >= 32 || mi->width_mm < 100 || mi->height_mm < 100) {
ADD2LOG(" ddc: cm values (%ux%u mm^2) didn't work either - giving up\n", width_mm, height_mm);
mi->width_mm = mi->height_mm = 0;
}
}
}
}
}
mi = mi_list[0];
if(mi->width_mm && mi->height_mm) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->size.type = res_size;
res->size.unit = size_unit_mm;
res->size.val1 = mi->width_mm; /* width */
res->size.val2 = mi->height_mm; /* height */
}
if(hd_data->debug) {
ADD2LOG("----- DDC info -----\n");
if(mi->vendor) {
ADD2LOG(" vendor: \"%s\"\n", mi->vendor);
}
if(mi->name) {
ADD2LOG(" model: \"%s\"\n", mi->name);
}
if(mi->serial) {
ADD2LOG(" serial: \"%s\"\n", mi->serial);
}
if(mi->width || mi->height) {
ADD2LOG(" size: %u x %u\n", mi->width, mi->height);
}
if(mi->width_mm || mi->height_mm) {
ADD2LOG(" size (mm): %u x %u\n", mi->width_mm, mi->height_mm);
}
if(mi->clock) {
ADD2LOG(" clock: %u kHz\n", mi->clock);
}
if(mi->min_hsync) {
ADD2LOG(" hsync: %u-%u kHz\n", mi->min_hsync, mi->max_hsync);
}
if(mi->min_vsync) {
ADD2LOG(" vsync: %u-%u Hz\n", mi->min_vsync, mi->max_vsync);
}
if(mi->manu_year) {
ADD2LOG(" manu. year: %u\n", mi->manu_year);
}
ADD2LOG("----- DDC info end -----\n");
}
}
free_mem(serial);
free_mem(vendor);
free_mem(name);
}
void add_monitor_res(hd_t *hd, unsigned width, unsigned height, unsigned vfreq, unsigned il)
{
hd_res_t *res;
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->monitor.type = res_monitor;
res->monitor.width = width;
res->monitor.height = height;
res->monitor.vfreq = vfreq;
res->monitor.interlaced = il;
}
/*
* This looks evil, but some Mac displays really lie at us.
*/
void fix_edid_info(hd_data_t *hd_data, unsigned char *edid)
{
unsigned vend, dev;
unsigned timing;
int fix = 0;
vend = (edid[8] << 8) + edid[9];
dev = (edid[0xb] << 8) + edid[0xa];
timing = (edid[0x24] << 8) + edid[0x23];
/* APP9214: Apple Studio Display */
if(vend == 0x0610 && dev == 0x9214 && timing == 0x0800) {
timing = 0x1000;
fix = 1;
}
if(fix) {
edid[0x23] = timing & 0xff;
edid[0x24] = (timing >> 8) & 0xff;
}
}
/** @} */

1
src/hd/monitor.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_monitor(hd_data_t *hd_data);

789
src/hd/mouse.c Normal file
View File

@ -0,0 +1,789 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include "hd.h"
#include "hd_int.h"
#include "mouse.h"
/**
* @defgroup MOUSEdev Mouse devices
* @ingroup libhdDEVint
* @brief Mouse detection
*
* @todo reset serial lines to old values (cf. modem.c)
* @{
*/
#ifndef LIBHD_TINY
#if 0
static unsigned read_data(hd_data_t *hd_data, int fd, unsigned char *buf, unsigned buf_size);
static void get_ps2_mouse(hd_data_t *hd_data);
static void test_ps2_open(void *arg);
#endif
static void get_serial_mouse(hd_data_t* hd_data);
static void add_serial_mouse(hd_data_t* hd_data);
static int _setspeed(int fd, int old, int new, int needtowrite, unsigned short flags);
static void setspeed(int fd, int new, int needtowrite, unsigned short flags);
static unsigned chk4id(ser_device_t *mi);
static ser_device_t *add_ser_mouse_entry(ser_device_t **sm, ser_device_t *new_sm);
static void dump_ser_mouse_data(hd_data_t *hd_data);
#if 0
static void get_sunmouse(hd_data_t *hd_data);
#endif
void hd_scan_mouse(hd_data_t *hd_data)
{
ser_device_t *sm, *sm_next;
if(!hd_probe_feature(hd_data, pr_mouse)) return;
hd_data->module = mod_mouse;
/* some clean-up */
remove_hd_entries(hd_data);
hd_data->ser_mouse = NULL;
#if 0
PROGRESS(1, 0, "ps/2");
get_ps2_mouse(hd_data);
#endif
PROGRESS(2, 0, "serial");
hd_fork(hd_data, 20, 20);
if(hd_data->flags.forked) {
get_serial_mouse(hd_data);
hd_move_to_shm(hd_data);
if((hd_data->debug & HD_DEB_MOUSE)) dump_ser_mouse_data(hd_data);
}
else {
/* take data from shm */
hd_data->ser_mouse = ((hd_data_t *) (hd_data->shm.data))->ser_mouse;
if((hd_data->debug & HD_DEB_MOUSE)) dump_ser_mouse_data(hd_data);
}
hd_fork_done(hd_data);
add_serial_mouse(hd_data);
hd_shm_clean(hd_data);
for(sm = hd_data->ser_mouse; sm; sm = sm_next) {
sm_next = sm->next;
free_mem(sm->dev_name);
free_mem(sm);
}
hd_data->ser_mouse = NULL;
#if 0
PROGRESS(3, 0, "sunmouse");
get_sunmouse(hd_data);
#endif
}
#if 0
unsigned read_data(hd_data_t *hd_data, int fd, unsigned char *buf, unsigned buf_size)
{
int k, len = 0;
unsigned char *bp;
while(
(unsigned) len < buf_size &&
(k = read(fd, buf + len, buf_size - len)) >= 0
) len += k;
bp = buf;
if(len && (*bp == 0xfe || *bp == 0xfa)) { bp++; len--; }
for(k = 0; k < len; k++) buf[k] = bp[k];
if((hd_data->debug & HD_DEB_MOUSE)) {
ADD2LOG("ps/2[%d]: ", len);
hd_log_hex(hd_data, 1, len, buf);
ADD2LOG("\n");
}
return len;
}
/*
* How it works:
*
* 1. There must exist a PS/2 controller entry (-> there is a PS/2 port).
* 2. If there are PS/2 mouse irq events, assume a PS/2 mouse is attached.
* 3. Otherwise:
* - open /dev/psaux
* - write the "get mouse info" command (0xe9)
* - read back the response, which should be either 0xfe "resend data"
* or, e.g. (0xfa) 0x20 0x02 0x3c (0xfa = "ACK" (should be swallowed
* by the psaux driver, but isn't), the rest are settings)
* - ignore the first byte if it is 0xfa or 0xfe
* - if there are at least 2 bytes left, assume a mouse is attached.
*
* Note1: we could use the command 0xfe "get mouse ID" instead. But that turned
* out to be less reliable, as this command returns only one byte, which
* is even 0.
* Note2: step 2 is mainly relevant if the mouse is already in use. In that
* case we would have problems reading back the respose of our command.
* (Typically the mouse driver will get it (and choke on it).)
*/
static void get_ps2_mouse(hd_data_t *hd_data)
{
hd_t *hd, *hd1;
hd_res_t *res;
int fd;
fd_set set;
struct timeval tv;
unsigned char cmd_mouse_info = 0xe9; /* read mouse info (3 bytes) */
unsigned char cmd_mouse_id = 0xf2; /* read mouse id (1 byte) */
unsigned char buf[100];
unsigned mouse_id = -1;
static unsigned char intelli_init[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 };
int buf_len = 0;
#ifdef __PPC__
int always_ps2_mouse = 0;
#endif
for(hd1 = hd_data->hd; hd1; hd1 = hd1->next) {
/* look for a PS/2 controller entry... */
if(hd1->base_class.id == bc_ps2) {
/* ...and see if there were irq events... */
for(res = hd1->res; res; res = res->next) {
if(res->irq.type == res_irq && res->irq.triggered) break;
}
#ifdef __PPC__
/*
* On PReP & CHRP, assume a PS/2 mouse to be attached.
* There seems to be no way to actually *detect* it.
*/
if(!res) {
hd_t *hd;
sys_info_t *st;
if((hd = hd_list(hd_data, hw_sys, 0, NULL))) {
if(
hd->detail &&
hd->detail->type == hd_detail_sys &&
(st = hd->detail->sys.data) &&
(
!strcmp(st->system_type, "PReP") ||
strstr(st->system_type, "CHRP") == st->system_type /* CHRP && CHRP64 */
)
) {
always_ps2_mouse = 1;
}
}
}
#endif
PROGRESS(1, 1, "ps/2");
/* open the mouse device... */
if(hd_timeout(test_ps2_open, NULL, 2) > 0) {
ADD2LOG("ps/2: open(%s) timed out\n", DEV_PSAUX);
fd = -2;
}
else {
fd = open(DEV_PSAUX, O_RDWR | O_NONBLOCK);
}
PROGRESS(1, 2, "ps/2");
if(fd >= 0) {
/* ...write the id command... */
PROGRESS(1, 3, "ps/2");
write(fd, intelli_init, sizeof intelli_init);
usleep(25000);
read_data(hd_data, fd, buf, sizeof buf);
if(write(fd, &cmd_mouse_id, 1) == 1) {
PROGRESS(1, 4, "ps/2");
usleep(50000); /* ...give it a chance to react... */
/* ...read the response... */
buf_len = read_data(hd_data, fd, buf, sizeof buf);
if(buf_len >= 1) mouse_id = buf[buf_len - 1];
// if we didn't get any response, try this
if(buf_len == 0 || (hd_data->debug & HD_DEB_MOUSE)) {
PROGRESS(1, 5, "ps/2");
if(write(fd, &cmd_mouse_info, 1) == 1) {
usleep(50000);
buf_len = read_data(hd_data, fd, buf, sizeof buf);
/*
* Assume a mouse to be attached if at least 2 bytes are
* returned.
*/
if(mouse_id == -1u && buf_len >= 2) mouse_id = 0;
}
}
PROGRESS(1, 6, "ps/2");
}
close(fd);
PROGRESS(1, 7, "ps/2");
/*
* The following code is apparently necessary on some board/mouse
* combinations. Otherwise the PS/2 mouse won't work.
*/
if((fd = open(DEV_PSAUX, O_RDONLY | O_NONBLOCK)) >= 0) {
PROGRESS(1, 8, "ps/2");
FD_ZERO(&set);
FD_SET(fd, &set);
tv.tv_sec = 0; tv.tv_usec = 1;
if(select(fd + 1, &set, NULL, NULL, &tv) == 1) {
PROGRESS(1, 9, "ps/2");
read(fd, buf, sizeof buf);
PROGRESS(1, 10, "ps/2");
}
PROGRESS(1, 11, "ps/2");
close(fd);
PROGRESS(1, 12, "ps/2");
}
}
else {
ADD2LOG("open(" DEV_PSAUX "): %s\n", fd == -1 ? strerror(errno) : "timeout");
}
if(mouse_id == -1u) {
/*
* Assume a PS/2 mouse is attached if the ps/2 controller has
* genetrated some events.
*/
if(
res
#ifdef __PPC__
|| always_ps2_mouse
#endif
) {
PROGRESS(1, 13, "ps/2");
mouse_id = 0;
}
}
if(mouse_id != -1u) {
PROGRESS(1, 14, "ps/2");
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_mouse;
hd->sub_class.id = sc_mou_ps2;
hd->bus.id = bus_ps2;
hd->unix_dev_name = new_str(DEV_MICE);
hd->attached_to = hd1->idx;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0200);
switch(mouse_id) {
case 3: /* 3 buttons + wheel */
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0004);
break;
case 4: /* 5 buttons + wheel */
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0005);
break;
default: /* 0 */
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0002);
}
}
/* there can only be one... */
break;
}
}
}
void test_ps2_open(void *arg)
{
open(DEV_PSAUX, O_RDWR | O_NONBLOCK);
}
#endif
#if 0
static void get_sunmouse(hd_data_t *hd_data)
{
hd_t *hd;
int fd;
int found;
found = 0;
/* Only search for Sun mouse if we have a Sun keyboard */
for(hd = hd_data->hd; hd; hd = hd->next)
{
if(hd->base_class.id == bc_keyboard &&
hd->sub_class.id == sc_keyboard_kbd &&
ID_TAG(hd->vendor.id) == TAG_SPECIAL && ID_VALUE(hd->vendor.id) == 0x0202)
found = 1;
}
if (found)
{
if ((fd = open(DEV_SUNMOUSE, O_RDONLY)) != -1)
{
/* FIXME: Should probably talk to the mouse to see
if the connector is not empty. */
close (fd);
PROGRESS(1, 1, "Sun Mouse");
hd = add_hd_entry (hd_data, __LINE__, 0);
hd->base_class.id = bc_mouse;
hd->sub_class.id = sc_mou_sun;
hd->bus.id = bus_serial;
hd->unix_dev_name = new_str(DEV_SUNMOUSE);
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0202);
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0000);
}
}
}
#endif
/*
* Gather serial mouse data and put it into hd_data->ser_mouse.
*/
void get_serial_mouse(hd_data_t *hd_data)
{
hd_t *hd;
int j, fd, fd_max = 0, sel, max_len;
unsigned modem_info;
fd_set set, set0;
struct timeval to;
ser_device_t *sm;
struct termios tio;
FD_ZERO(&set);
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_comm &&
hd->sub_class.id == sc_com_ser &&
hd->unix_dev_name &&
!hd->tag.skip_mouse &&
!has_something_attached(hd_data, hd)
) {
if((fd = open(hd->unix_dev_name, O_RDWR | O_NONBLOCK)) >= 0) {
if(tcgetattr(fd, &tio)) continue;
sm = add_ser_mouse_entry(&hd_data->ser_mouse, new_mem(sizeof *sm));
sm->dev_name = new_str(hd->unix_dev_name);
sm->fd = fd;
sm->tio = tio;
sm->hd_idx = hd->idx;
if(fd > fd_max) fd_max = fd;
FD_SET(fd, &set);
/*
* PnP COM spec black magic...
*/
setspeed(fd, 1200, 1, CS7);
modem_info = TIOCM_DTR | TIOCM_RTS;
ioctl(fd, TIOCMBIC, &modem_info);
}
}
}
if(!hd_data->ser_mouse) return;
/*
* 200 ms seems to be too fast for some mice...
*/
usleep(300000); /* PnP protocol */
for(sm = hd_data->ser_mouse; sm; sm = sm->next) {
modem_info = TIOCM_DTR | TIOCM_RTS;
ioctl(sm->fd, TIOCMBIS, &modem_info);
}
/* smaller buffer size, otherwise we might wait really long... */
max_len = sizeof sm->buf < 128 ? sizeof sm->buf : 128;
to.tv_sec = 0; to.tv_usec = 300000;
set0 = set;
for(;;) {
to.tv_sec = 0; to.tv_usec = 300000;
set = set0;
if((sel = select(fd_max + 1, &set, NULL, NULL, &to)) > 0) {
for(sm = hd_data->ser_mouse; sm; sm = sm->next) {
if(FD_ISSET(sm->fd, &set)) {
if((j = read(sm->fd, sm->buf + sm->buf_len, max_len - sm->buf_len)) > 0)
sm->buf_len += j;
if(j <= 0) FD_CLR(sm->fd, &set0); // #####
}
}
}
else {
break;
}
}
for(sm = hd_data->ser_mouse; sm; sm = sm->next) {
chk4id(sm);
/* reset serial lines */
tcflush(sm->fd, TCIOFLUSH);
tcsetattr(sm->fd, TCSAFLUSH, &sm->tio);
close(sm->fd);
}
}
/*
* Go through serial mouse data and add hd entries.
*/
void add_serial_mouse(hd_data_t *hd_data)
{
hd_t *hd;
char buf[4];
ser_device_t *sm;
for(sm = hd_data->ser_mouse; sm; sm = sm->next) {
if(sm->is_mouse) {
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_mouse;
hd->sub_class.id = sc_mou_ser;
hd->bus.id = bus_serial;
hd->unix_dev_name = new_str(sm->dev_name);
hd->attached_to = sm->hd_idx;
if(*sm->pnp_id) {
strncpy(buf, sm->pnp_id, 3);
buf[3] = 0;
hd->vendor.id = name2eisa_id(buf);
if(!hd->vendor.id) { /* in case it's a really strange one... */
hd->vendor.name = new_str(buf);
}
hd->device.id = MAKE_ID(TAG_EISA, strtol(sm->pnp_id + 3, NULL, 16));
hd->serial = new_str(sm->serial);
if(sm->user_name) hd->device.name = new_str(sm->user_name);
if(sm->vend) {
free_mem(hd->vendor.name);
hd->vendor.name = new_str(sm->vend);
}
if(sm->dev_id && strlen(sm->dev_id) >= 7) {
char buf[5], *s;
unsigned u1, u2;
u1 = name2eisa_id(sm->dev_id);
if(u1) {
strncpy(buf, sm->dev_id + 3, 4);
buf[4] = 0;
u2 = strtol(sm->dev_id + 3, &s, 16);
if(!*s) {
hd->compat_vendor.id = u1;
hd->compat_device.id = MAKE_ID(TAG_EISA, u2);
}
}
}
}
else {
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x0200);
hd->device.id = MAKE_ID(TAG_SPECIAL, 0x0003);
}
}
}
}
/*
* Baud setting magic taken from gpm.
*/
int _setspeed(int fd, int old, int new, int needtowrite, unsigned short flags)
{
struct termios tty;
char *c;
int err = 0;
flags |= CREAD | CLOCAL | HUPCL;
if(tcgetattr(fd, &tty)) return errno;
tty.c_iflag = IGNBRK | IGNPAR;
tty.c_oflag = 0;
tty.c_lflag = 0;
tty.c_line = 0;
tty.c_cc[VTIME] = 0;
tty.c_cc[VMIN] = 1;
switch (old)
{
case 9600: tty.c_cflag = flags | B9600; break;
case 4800: tty.c_cflag = flags | B4800; break;
case 2400: tty.c_cflag = flags | B2400; break;
case 1200:
default: tty.c_cflag = flags | B1200; break;
}
if(tcsetattr(fd, TCSAFLUSH, &tty)) return errno;
switch (new)
{
case 9600: c = "*q"; tty.c_cflag = flags | B9600; break;
case 4800: c = "*p"; tty.c_cflag = flags | B4800; break;
case 2400: c = "*o"; tty.c_cflag = flags | B2400; break;
case 1200:
default: c = "*n"; tty.c_cflag = flags | B1200; break;
}
if(needtowrite) {
err = 2 - write(fd, c, 2);
}
usleep(100000);
if(tcsetattr(fd, TCSAFLUSH, &tty)) return errno;
return err;
}
void setspeed(int fd, int new, int needtowrite, unsigned short flags)
{
int i;
for(i = 9600; i >= 1200; i >>= 1) {
_setspeed(fd, i, new, needtowrite, flags);
#if 0
if(err) {
fprintf(stderr, "%d, %d ", i, err);
perror("");
}
#endif
}
}
#if 0
/*
* Check for a PnP info field starting at ofs;
* returns either the length of the field or 0 if none was found.
*
* the minfo_t struct is updated with the PnP data
*/
int is_pnpinfo(ser_device_t *mi, int ofs)
{
int i;
unsigned char *s = mi->buf + ofs;
int len = mi->buf_len - ofs;
if(len <= 0) return 0;
switch(*s) {
case 0x08:
mi->bits = 6; break;
case 0x28:
mi->bits = 7; break;
default:
return 0;
}
if(len < 11) return 0;
/* six bit values */
if((s[1] & ~0x3f) || (s[2] & ~0x3f)) return 0;
mi->pnp_rev = (s[1] << 6) + s[2];
/* the eisa id */
for(i = 0; i < 7; i++) {
mi->pnp_id[i] = s[i + 3];
if(mi->bits == 6) mi->pnp_id[i] += 0x20;
}
mi->pnp_id[7] = 0;
/* now check the id */
for(i = 0; i < 3; i++) {
if(
(mi->pnp_id[i] < 'A' || mi->pnp_id[i] > 'Z') &&
mi->pnp_id[i] != '_'
) return 0;
}
for(i = 3; i < 7; i++) {
if(
(mi->pnp_id[i] < '0' || mi->pnp_id[i] > '9') &&
(mi->pnp_id[i] < 'A' || mi->pnp_id[i] > 'F')
) return 0;
}
if(
(mi->bits == 6 && s[10] == 0x09) ||
(mi->bits == 7 && s[10] == 0x29)
) {
return 11;
}
if(
(mi->bits != 6 || s[10] != 0x3c) &&
(mi->bits != 7 || s[10] != 0x5c)
) {
return 0;
}
/* skip extended info */
for(i = 11; i < len; i++) {
if(
(mi->bits == 6 && s[i] == 0x09) ||
(mi->bits == 7 && s[i] == 0x29)
) {
return i + 1;
}
}
/*
* some mice have problems providing the extended info -> return ok in
* these cases too
*/
if(
(mi->bits == 6 && s[10] == 0x3c) ||
(mi->bits == 7 && s[10] == 0x5c)
) {
return 11;
}
/* no end token... */
return 0;
}
#endif
unsigned chk4id(ser_device_t *mi)
{
int i;
#if 0
unsigned char fake[] =
{
// fake pnp data
};
mi->buf_len = sizeof fake;
memcpy(mi->buf, fake, mi->buf_len);
// for(i = 0; i < mi->buf_len; i++) mi->buf[i] += ' ';
#endif
if(!mi->buf_len) return 0;
for(i = 0; i < mi->buf_len; i++) {
if((mi->pnp = is_pnpinfo(mi, i))) break;
}
if(i == mi->buf_len) {
/* non PnP, but MS compatible */
if(*mi->buf == 'M')
mi->non_pnp = mi->buf_len - 1;
else
return 0;
}
mi->garbage = i;
for(i = 0; i < mi->garbage; i++) {
if(mi->buf[i] == 'M') {
mi->non_pnp = mi->garbage - i;
mi->garbage = i;
break;
}
}
if(mi->non_pnp || mi->bits == 6) mi->is_mouse = 1;
return mi->is_mouse;
}
ser_device_t *add_ser_mouse_entry(ser_device_t **sm, ser_device_t *new_sm)
{
while(*sm) sm = &(*sm)->next;
return *sm = new_sm;
}
void dump_ser_mouse_data(hd_data_t *hd_data)
{
int j;
ser_device_t *sm;
if(!(sm = hd_data->ser_mouse)) return;
ADD2LOG("----- serial mice -----\n");
for(; sm; sm = sm->next) {
ADD2LOG("%s\n", sm->dev_name);
if(sm->serial) ADD2LOG("serial: \"%s\"\n", sm->serial);
if(sm->class_name) ADD2LOG("class_name: \"%s\"\n", sm->class_name);
if(sm->dev_id) ADD2LOG("dev_id: \"%s\"\n", sm->dev_id);
if(sm->user_name) ADD2LOG("user_name: \"%s\"\n", sm->user_name);
if(sm->garbage) {
ADD2LOG(" garbage[%u]: ", sm->garbage);
hd_log_hex(hd_data, 1, sm->garbage, sm->buf);
ADD2LOG("\n");
}
if(sm->non_pnp) {
ADD2LOG(" non-pnp[%u]: ", sm->non_pnp);
hd_log_hex(hd_data, 1, sm->non_pnp, sm->buf + sm->garbage);
ADD2LOG("\n");
}
if(sm->pnp) {
ADD2LOG(" pnp[%u]: ", sm->pnp);
hd_log_hex(hd_data, 1, sm->pnp, sm->buf + sm->garbage + sm->non_pnp);
ADD2LOG("\n");
}
if((j = sm->buf_len - (sm->garbage + sm->non_pnp + sm->pnp))) {
ADD2LOG(" moves[%u]: ", j);
hd_log_hex(hd_data, 1, j, sm->buf + sm->garbage + sm->non_pnp + sm->pnp);
ADD2LOG("\n");
}
if(sm->is_mouse) ADD2LOG(" is mouse\n");
if(sm->pnp) {
ADD2LOG(" bits: %u\n", sm->bits);
ADD2LOG(" PnP Rev: %u.%02u\n", sm->pnp_rev / 100, sm->pnp_rev % 100);
ADD2LOG(" PnP ID: \"%s\"\n", sm->pnp_id);
}
if(sm->next) ADD2LOG("\n");
}
ADD2LOG("----- serial mice end -----\n");
}
#endif /* !defined(LIBHD_TINY) */
/** @} */

1
src/hd/mouse.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_mouse(hd_data_t *hd_data);

804
src/hd/net.c Normal file
View File

@ -0,0 +1,804 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <fcntl.h>
#include <inttypes.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t
#include <linux/if.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>
#include <linux/if_arp.h>
#include "hd.h"
#include "hd_int.h"
#include "net.h"
/**
* @defgroup NETint Network devices
* @ingroup libhdDEVint
* @brief Network device scan functions
*
* Gather network interface info
*
* @{
*/
static void get_ethtool_priv(hd_data_t *hd_data, hd_t *hd);
static void get_driverinfo(hd_data_t *hd_data, hd_t *hd);
static void get_linkstate(hd_data_t *hd_data, hd_t *hd);
static hd_res_t *get_phwaddr(hd_data_t *hd_data, hd_t *hd);
static void add_xpnet(hd_data_t *hdata);
static void add_uml(hd_data_t *hdata);
static void add_kma(hd_data_t *hdata);
static void add_if_name(hd_t *hd_card, hd_t *hd);
/*
* This is independent of the other scans.
*/
void hd_scan_net(hd_data_t *hd_data)
{
unsigned u;
int if_type, if_carrier;
hd_t *hd, *hd_card;
char *s, *t, *hw_addr;
hd_res_t *res, *res_hw, *res_phw, *res_lnk;
uint64_t ul0;
str_list_t *sf_class, *sf_class_e;
char *sf_cdev = NULL, *sf_dev = NULL;
char *sf_drv_name, *sf_drv;
if(!hd_probe_feature(hd_data, pr_net)) return;
hd_data->module = mod_net;
/* some clean-up */
remove_hd_entries(hd_data);
hd_data->net = free_str_list(hd_data->net);
PROGRESS(1, 0, "get network data");
sf_class = read_dir("/sys/class/net", 'l');
if(!sf_class) sf_class = read_dir("/sys/class/net", 'd');
if(!sf_class) {
ADD2LOG("sysfs: no such class: net\n");
return;
}
for(sf_class_e = sf_class; sf_class_e; sf_class_e = sf_class_e->next) {
str_printf(&sf_cdev, 0, "/sys/class/net/%s", sf_class_e->str);
hd_card = NULL;
ADD2LOG(
" net interface: name = %s, path = %s\n",
sf_class_e->str,
hd_sysfs_id(sf_cdev)
);
if_type = -1;
if(hd_attr_uint(get_sysfs_attr_by_path(sf_cdev, "type"), &ul0, 0)) {
if_type = ul0;
ADD2LOG(" type = %d\n", if_type);
}
if_carrier = -1;
if(hd_attr_uint(get_sysfs_attr_by_path(sf_cdev, "carrier"), &ul0, 0)) {
if_carrier = ul0;
ADD2LOG(" carrier = %d\n", if_carrier);
}
hw_addr = NULL;
if((s = get_sysfs_attr_by_path(sf_cdev, "address"))) {
hw_addr = canon_str(s, strlen(s));
ADD2LOG(" hw_addr = %s\n", hw_addr);
}
sf_dev = new_str(hd_read_sysfs_link(sf_cdev, "device"));
if(sf_dev) {
ADD2LOG(" net device: path = %s\n", hd_sysfs_id(sf_dev));
}
sf_drv_name = NULL;
sf_drv = hd_read_sysfs_link(sf_dev, "driver");
if(sf_drv) {
sf_drv_name = strrchr(sf_drv, '/');
if(sf_drv_name) sf_drv_name++;
ADD2LOG(
" net driver: name = %s, path = %s\n",
sf_drv_name,
hd_sysfs_id(sf_drv)
);
}
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_network_interface;
hd->sub_class.id = sc_nif_other;
hd->unix_dev_name = new_str(sf_class_e->str);
hd->sysfs_id = new_str(hd_sysfs_id(sf_cdev));
res_hw = NULL;
if(hw_addr && strspn(hw_addr, "0:") != strlen(hw_addr)) {
res_hw = new_mem(sizeof *res_hw);
res_hw->hwaddr.type = res_hwaddr;
res_hw->hwaddr.addr = new_str(hw_addr);
add_res_entry(&hd->res, res_hw);
}
res_phw = get_phwaddr(hd_data, hd);
if(if_carrier >= 0) {
res = new_mem(sizeof *res);
res->link.type = res_link;
res->link.state = if_carrier ? 1 : 0;
add_res_entry(&hd->res, res);
}
if(sf_drv_name) {
add_str_list(&hd->drivers, sf_drv_name);
}
else if(hd->res) {
get_driverinfo(hd_data, hd);
}
get_ethtool_priv(hd_data, hd);
switch(if_type) {
case ARPHRD_ETHER: /* eth */
hd->sub_class.id = sc_nif_ethernet;
break;
case ARPHRD_LOOPBACK: /* lo */
hd->sub_class.id = sc_nif_loopback;
break;
case ARPHRD_SIT: /* sit */
hd->sub_class.id = sc_nif_sit;
break;
case ARPHRD_FDDI: /* fddi */
hd->sub_class.id = sc_nif_fddi;
break;
case ARPHRD_IEEE802_TR: /* tr */
hd->sub_class.id = sc_nif_tokenring;
break;
#if 0
case ARPHRD_IEEE802: /* fc */
hd->sub_class.id = sc_nif_fc;
break;
#endif
default:
hd->sub_class.id = sc_nif_other;
}
if(!strcmp(hd->unix_dev_name, "lo")) {
hd->sub_class.id = sc_nif_loopback;
}
else if(sscanf(hd->unix_dev_name, "eth%u", &u) == 1) {
hd->sub_class.id = sc_nif_ethernet;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "tr%u", &u) == 1) {
hd->sub_class.id = sc_nif_tokenring;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "fddi%u", &u) == 1) {
hd->sub_class.id = sc_nif_fddi;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "ctc%u", &u) == 1) {
hd->sub_class.id = sc_nif_ctc;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "iucv%u", &u) == 1) {
hd->sub_class.id = sc_nif_iucv;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "hsi%u", &u) == 1) {
hd->sub_class.id = sc_nif_hsi;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "qeth%u", &u) == 1) {
hd->sub_class.id = sc_nif_qeth;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "escon%u", &u) == 1) {
hd->sub_class.id = sc_nif_escon;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "myri%u", &u) == 1) {
hd->sub_class.id = sc_nif_myrinet;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "sit%u", &u) == 1) {
hd->sub_class.id = sc_nif_sit; /* ipv6 over ipv4 tunnel */
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "wlan%u", &u) == 1) {
hd->sub_class.id = sc_nif_wlan;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "xp%u", &u) == 1) {
hd->sub_class.id = sc_nif_xp;
hd->slot = u;
}
else if(sscanf(hd->unix_dev_name, "usb%u", &u) == 1) {
hd->sub_class.id = sc_nif_usb;
hd->slot = u;
}
/* ##### add more interface names here */
else {
for(s = hd->unix_dev_name; *s; s++) if(isdigit(*s)) break;
if(*s && (u = strtoul(s, &s, 10), !*s)) {
hd->slot = u;
}
}
hd->bus.id = bus_none;
hd_card = NULL;
if(sf_dev) {
s = new_str(hd_sysfs_id(sf_dev));
hd->sysfs_device_link = new_str(s);
hd_card = hd_find_sysfs_id(hd_data, s);
// try one above, if not found
if(!hd_card) {
t = strrchr(s, '/');
if(t) {
*t = 0;
hd_card = hd_find_sysfs_id(hd_data, s);
}
}
/* if one card has several interfaces (as with PS3), check interface names, too */
if(
hd_card &&
hd_card->unix_dev_name &&
hd->unix_dev_name &&
strcmp(hd->unix_dev_name, hd_card->unix_dev_name)
) {
hd_card = hd_find_sysfs_id_devname(hd_data, s, hd->unix_dev_name);
}
s = free_mem(s);
if(hd_card) {
hd->attached_to = hd_card->idx;
/* for cards with strange pci classes */
hd_set_hw_class(hd_card, hw_network_ctrl);
/* add hw addr to network card */
if(res_hw) {
u = 0;
for(res = hd_card->res; res; res = res->next) {
if(
res->any.type == res_hwaddr &&
!strcmp(res->hwaddr.addr, res_hw->hwaddr.addr)
) {
u = 1;
break;
}
}
if(!u) {
res = new_mem(sizeof *res);
res->hwaddr.type = res_hwaddr;
res->hwaddr.addr = new_str(res_hw->hwaddr.addr);
add_res_entry(&hd_card->res, res);
}
}
/* add permanent hw addr to network card */
if(res_phw) {
u = 0;
for(res = hd_card->res; res; res = res->next) {
if(
res->any.type == res_phwaddr &&
!strcmp(res->hwaddr.addr, res_phw->hwaddr.addr)
) {
u = 1;
break;
}
}
if(!u) {
res = new_mem(sizeof *res);
res->hwaddr.type = res_phwaddr;
res->hwaddr.addr = new_str(res_phw->hwaddr.addr);
add_res_entry(&hd_card->res, res);
}
}
/*
* add interface names...
* but not wmasterX (bnc #441778)
*/
if(if_type != 801) add_if_name(hd_card, hd);
}
}
if(!hd_card && hw_addr) {
/* try to find card based on hwaddr (for prom-based cards) */
for(hd_card = hd_data->hd; hd_card; hd_card = hd_card->next) {
if(
hd_card->base_class.id != bc_network ||
hd_card->sub_class.id != 0
) continue;
for(res = hd_card->res; res; res = res->next) {
if(
res->any.type == res_hwaddr &&
!strcmp(hw_addr, res->hwaddr.addr)
) break;
}
if(res) {
hd->attached_to = hd_card->idx;
break;
}
}
}
hw_addr = free_mem(hw_addr);
/* fix card type */
if(hd_card) {
if(
(hd_card->base_class.id == 0 && hd_card->sub_class.id == 0) ||
(hd_card->base_class.id == bc_network && hd_card->sub_class.id == 0x80)
) {
switch(hd->sub_class.id) {
case sc_nif_ethernet:
hd_card->base_class.id = bc_network;
hd_card->sub_class.id = 0;
break;
case sc_nif_usb:
hd_card->base_class.id = bc_network;
hd_card->sub_class.id = 0x91;
break;
}
}
}
sf_dev = free_mem(sf_dev);
}
sf_cdev = free_mem(sf_cdev);
sf_class = free_str_list(sf_class);
if(hd_is_sgi_altix(hd_data)) add_xpnet(hd_data);
add_uml(hd_data);
add_kma(hd_data);
/* add link status info & dump eeprom */
for(hd = hd_data->hd ; hd; hd = hd->next) {
if(
hd->module == hd_data->module &&
hd->base_class.id == bc_network_interface
) {
char *buf = NULL;
str_list_t *sl0, *sl;
if(hd_probe_feature(hd_data, pr_net_eeprom) && hd->unix_dev_name) {
PROGRESS(2, 0, "eeprom dump");
str_printf(&buf, 0, "|/usr/sbin/ethtool -e %s 2>/dev/null", hd->unix_dev_name);
if((sl0 = read_file(buf, 0, 0))) {
ADD2LOG("----- %s %s -----\n", hd->unix_dev_name, "EEPROM dump");
for(sl = sl0; sl; sl = sl->next) {
ADD2LOG("%s", sl->str);
}
ADD2LOG("----- %s end -----\n", "EEPROM dump");
free_str_list(sl0);
}
free(buf);
}
for(res = hd->res; res; res = res->next) {
if(res->any.type == res_link) break;
}
if(!res) get_linkstate(hd_data, hd);
if(!(hd_card = hd_get_device_by_idx(hd_data, hd->attached_to))) continue;
for(res = hd->res; res; res = res->next) {
if(res->any.type == res_link) break;
}
if(res) {
for(res_lnk = hd_card->res; res_lnk; res_lnk = res_lnk->next) {
if(res_lnk->any.type == res_link) break;
}
if(res && !res_lnk) {
res_lnk = new_mem(sizeof *res_lnk);
res_lnk->link.type = res_link;
res_lnk->link.state = res->link.state;
add_res_entry(&hd_card->res, res_lnk);
}
}
hd_card->is.fcoe_offload = hd->is.fcoe_offload;
hd_card->is.iscsi_offload = hd->is.iscsi_offload;
hd_card->is.storage_only = hd->is.storage_only;
}
}
}
/*
* Get private flags via ethtool.
*/
void get_ethtool_priv(hd_data_t *hd_data, hd_t *hd)
{
int fd, err = 0;
unsigned u, len = 0;
struct ifreq ifr = {};
struct {
struct ethtool_sset_info hdr;
uint32_t buf[1];
} sset_info = { hdr:{ cmd:ETHTOOL_GSSET_INFO, sset_mask:1ULL << ETH_SS_PRIV_FLAGS } };
struct ethtool_gstrings *strings = NULL;
struct ethtool_value flags = { cmd:ETHTOOL_GPFLAGS };
if(!hd->unix_dev_name) return;
if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
strcpy(ifr.ifr_name, hd->unix_dev_name);
if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
ifr.ifr_data = &sset_info;
if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
ADD2LOG(" ethtool private flags: %u\n", len);
}
else {
ADD2LOG(" GSSET_INFO ethtool error: %s\n", strerror(errno));
err = 1;
}
if(len) strings = calloc(1, sizeof *strings + len * ETH_GSTRING_LEN);
if(!strings) err = 1;
if(!err) {
strings->cmd = ETHTOOL_GSTRINGS;
strings->string_set = ETH_SS_PRIV_FLAGS;
strings->len = len;
ifr.ifr_data = strings;
if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
for(u = 0; u < len; u++) strings->data[(u + 1) * ETH_GSTRING_LEN - 1] = 0;
}
else {
ADD2LOG(" GSTRINGS ethtool error: %s\n", strerror(errno));
err = 1;
}
}
if(len > 32) len = 32;
if(!err) {
ifr.ifr_data = &flags;
if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
for(u = 0; u < len; u++) {
char *key = strings->data + u * ETH_GSTRING_LEN;
unsigned val = (flags.data >> u) & 1;
ADD2LOG(" %s = %u\n", key, val);
// add 1 to get tri-state flags: 0 = unset, 1 = false, 2 = true
if(!strcmp(key, "FCoE offload support")) hd->is.fcoe_offload = val + 1;
if(!strcmp(key, "iSCSI offload support")) hd->is.iscsi_offload = val + 1;
if(!strcmp(key, "Storage only interface")) hd->is.storage_only = val + 1;
}
}
else {
ADD2LOG(" GPFLAGS ethtool error: %s\n", strerror(errno));
err = 1;
}
}
free(strings);
close(fd);
}
/*
* Get it the classical way, for drivers that don't support sysfs (veth).
*/
void get_driverinfo(hd_data_t *hd_data, hd_t *hd)
{
int fd;
struct ethtool_drvinfo drvinfo = { cmd:ETHTOOL_GDRVINFO };
struct ifreq ifr;
if(!hd->unix_dev_name) return;
if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
/* get driver info */
memset(&ifr, 0, sizeof ifr);
strcpy(ifr.ifr_name, hd->unix_dev_name);
ifr.ifr_data = (caddr_t) &drvinfo;
if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
ADD2LOG(" ethtool driver: %s\n", drvinfo.driver);
ADD2LOG(" ethtool bus: %s\n", drvinfo.bus_info);
add_str_list(&hd->drivers, drvinfo.driver);
}
else {
ADD2LOG(" GDRVINFO ethtool error: %s\n", strerror(errno));
}
close(fd);
}
/*
* Check network link status.
*/
void get_linkstate(hd_data_t *hd_data, hd_t *hd)
{
int fd;
struct ethtool_value linkstatus = { cmd:ETHTOOL_GLINK };
struct ifreq ifr;
hd_res_t *res;
if(!hd->unix_dev_name) return;
if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return;
if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return;
/* get driver info */
memset(&ifr, 0, sizeof ifr);
strcpy(ifr.ifr_name, hd->unix_dev_name);
ifr.ifr_data = (caddr_t) &linkstatus;
if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
ADD2LOG(" %s: ethtool link state: %d\n", hd->unix_dev_name, linkstatus.data);
res = new_mem(sizeof *res);
res->link.type = res_link;
res->link.state = linkstatus.data ? 1 : 0;
add_res_entry(&hd->res, res);
}
else {
ADD2LOG(" %s: GLINK ethtool error: %s\n", hd->unix_dev_name, strerror(errno));
}
close(fd);
}
/*
* Get permanent hardware address (it's not in sysfs).
*/
hd_res_t *get_phwaddr(hd_data_t *hd_data, hd_t *hd)
{
int fd;
struct ethtool_perm_addr *phwaddr = new_mem(sizeof (struct ethtool_perm_addr) + MAX_ADDR_LEN);
struct ifreq ifr;
hd_res_t *res = NULL;
phwaddr->cmd = ETHTOOL_GPERMADDR;
phwaddr->size = MAX_ADDR_LEN;
if(!hd->unix_dev_name) return res;
if(strlen(hd->unix_dev_name) > sizeof ifr.ifr_name - 1) return res;
if((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) return res;
/* get permanent hardware addr */
memset(&ifr, 0, sizeof ifr);
strcpy(ifr.ifr_name, hd->unix_dev_name);
ifr.ifr_data = (caddr_t) phwaddr;
if(ioctl(fd, SIOCETHTOOL, &ifr) == 0) {
int i;
char *addr = NULL;
if(phwaddr->size > 0) {
addr = new_mem(phwaddr->size * 3 + 1); // yes, we need an extra byte
for(i = 0; i < phwaddr->size; i++) {
sprintf(addr + 3 * i, "%02x:", phwaddr->data[i]);
}
addr[3 * i - 1] = 0;
}
ADD2LOG(" %s: ethtool permanent hw address[%d]: %s\n", hd->unix_dev_name, phwaddr->size, addr);
if(addr && strspn(addr, "0:") != strlen(addr)) {
res = new_mem(sizeof *res);
res->hwaddr.type = res_phwaddr;
res->hwaddr.addr = new_str(addr);
add_res_entry(&hd->res, res);
}
free_mem(addr);
}
else {
ADD2LOG(" %s: GLINK ethtool error: %s\n", hd->unix_dev_name, strerror(errno));
}
close(fd);
return res;
}
/*
* SGI Altix cross partition network.
*/
void add_xpnet(hd_data_t *hd_data)
{
hd_t *hd, *hd_card;
hd_res_t *res, *res2;
hd_card = add_hd_entry(hd_data, __LINE__, 0);
hd_card->base_class.id = bc_network;
hd_card->sub_class.id = 0x83;
hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x4002);
hd_card->device.id = MAKE_ID(TAG_SPECIAL, 1);
if(hd_module_is_active(hd_data, "xpnet")) {
add_str_list(&hd_card->drivers, "xpnet");
}
for(hd = hd_data->hd ; hd; hd = hd->next) {
if(
hd->module == hd_data->module &&
hd->base_class.id == bc_network_interface &&
hd->sub_class.id == sc_nif_xp
) {
hd->attached_to = hd_card->idx;
for(res = hd->res; res; res = res->next) {
if(res->any.type == res_hwaddr) break;
}
if(res) {
res2 = new_mem(sizeof *res2);
res2->hwaddr.type = res_hwaddr;
res2->hwaddr.addr = new_str(res->hwaddr.addr);
add_res_entry(&hd_card->res, res2);
}
add_if_name(hd_card, hd);
break;
}
}
}
/*
* UML veth devices.
*/
void add_uml(hd_data_t *hd_data)
{
hd_t *hd, *hd_card;
hd_res_t *res, *res2;
unsigned card_cnt = 0;
for(hd = hd_data->hd ; hd; hd = hd->next) {
if(
hd->module == hd_data->module &&
hd->base_class.id == bc_network_interface &&
search_str_list(hd->drivers, "uml virtual ethernet")
) {
hd_card = add_hd_entry(hd_data, __LINE__, 0);
hd_card->base_class.id = bc_network;
hd_card->sub_class.id = 0x00;
hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6010); // UML
hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
hd_card->slot = card_cnt++;
str_printf(&hd_card->device.name, 0, "Virtual Ethernet card %d", hd_card->slot);
hd->attached_to = hd_card->idx;
for(res = hd->res; res; res = res->next) {
if(res->any.type == res_hwaddr) break;
}
if(res) {
res2 = new_mem(sizeof *res2);
res2->hwaddr.type = res_hwaddr;
res2->hwaddr.addr = new_str(res->hwaddr.addr);
add_res_entry(&hd_card->res, res2);
}
add_if_name(hd_card, hd);
}
}
}
/*
* KMA veth devices.
*/
void add_kma(hd_data_t *hd_data)
{
hd_t *hd, *hd_card;
hd_res_t *res, *res2;
unsigned card_cnt = 0;
for(hd = hd_data->hd ; hd; hd = hd->next) {
if(
hd->module == hd_data->module &&
hd->base_class.id == bc_network_interface &&
search_str_list(hd->drivers, "kveth2")
) {
hd_card = add_hd_entry(hd_data, __LINE__, 0);
hd_card->base_class.id = bc_network;
hd_card->sub_class.id = 0x00;
hd_card->vendor.id = MAKE_ID(TAG_SPECIAL, 0x6012); // VirtualIron
hd_card->device.id = MAKE_ID(TAG_SPECIAL, 0x0001);
hd_card->slot = card_cnt++;
str_printf(&hd_card->device.name, 0, "Ethernet card %d", hd_card->slot);
hd->attached_to = hd_card->idx;
for(res = hd->res; res; res = res->next) {
if(res->any.type == res_hwaddr) break;
}
if(res) {
res2 = new_mem(sizeof *res2);
res2->hwaddr.type = res_hwaddr;
res2->hwaddr.addr = new_str(res->hwaddr.addr);
add_res_entry(&hd_card->res, res2);
}
add_if_name(hd_card, hd);
}
}
}
/*
* add interface name to card
*/
void add_if_name(hd_t *hd_card, hd_t *hd)
{
str_list_t *sl0;
if(hd->unix_dev_name) {
if(!search_str_list(hd_card->unix_dev_names, hd->unix_dev_name)) {
if(hd->sub_class.id == sc_nif_other) {
/* add at end */
add_str_list(&hd_card->unix_dev_names, hd->unix_dev_name);
}
else {
/* add at top */
sl0 = new_mem(sizeof *sl0);
sl0->next = hd_card->unix_dev_names;
sl0->str = new_str(hd->unix_dev_name);
hd_card->unix_dev_names = sl0;
}
free_mem(hd_card->unix_dev_name);
hd_card->unix_dev_name = new_str(hd_card->unix_dev_names->str);
}
}
}
/** @} */

1
src/hd/net.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_net(hd_data_t *hd_data);

300
src/hd/parallel.c Normal file
View File

@ -0,0 +1,300 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "hd.h"
#include "hd_int.h"
#include "parallel.h"
/**
* @defgroup PPORTint Parallel port devices
* @ingroup libhdDEVint
* @brief Parallel port device information
*
* @{
*/
#ifndef LIBHD_TINY
static void do_lp(hd_data_t *hd_data);
static void do_zip(hd_data_t *hd_data);
static void dump_parallel_data(hd_data_t *hd_data, str_list_t *sl);
void hd_scan_parallel(hd_data_t *hd_data)
{
if(!hd_probe_feature(hd_data, pr_parallel)) return;
hd_data->module = mod_parallel;
/* some clean-up */
remove_hd_entries(hd_data);
if(hd_probe_feature(hd_data, pr_parallel_lp)) do_lp(hd_data);
if(hd_probe_feature(hd_data, pr_parallel_zip)) do_zip(hd_data);
}
void do_lp(hd_data_t *hd_data)
{
hd_t *hd, *hd_i;
str_list_t *sl, *sl0;
hd_res_t *res;
char *pp = NULL, buf[256], unix_dev[] = "/dev/lp0", *s = NULL;
char *base_class, *device, *vendor, *cmd_set;
int i, j, port;
str_list_t *log = NULL;
PROGRESS(1, 0, "pp mod");
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_comm && hd->sub_class.id == sc_com_par) break;
}
/* ... if there seems to be a parallel interface, try to load it */
if(hd || 1) { /* always load it */
if(hd_data->kernel_version == KERNEL_22) {
unload_module(hd_data, "parport_probe");
probe_module(hd_data, "parport_probe");
} else {
unload_module(hd_data, "lp");
unload_module(hd_data, "parport_pc");
probe_module(hd_data, "parport_pc");
probe_module(hd_data, "lp");
}
}
for(i = 0; i < 3; i++, unix_dev[sizeof unix_dev - 2]++) {
PROGRESS(2, 1 + i, "lp read info");
port = 0;
// ##### read modes as well? (e.g: SPP,ECP,ECPEPP,ECPPS2)
if(hd_data->kernel_version == KERNEL_22)
str_printf(&pp, 0, PROC_PARPORT_22 "%d/hardware", i);
else
str_printf(&pp, 0, PROC_PARPORT_24 "%d/base-addr", i);
sl0 = read_file(pp, 0, 0);
if(!sl0) continue; /* file doesn't exist -> no parport entry */
str_printf(&s, 0, "%s\n", pp);
add_str_list(&log, s);
for(sl = sl0; sl; sl = sl->next) {
str_printf(&s, 0, " %s", sl->str);
add_str_list(&log, s);
if(hd_data->kernel_version == KERNEL_22) {
if(sscanf(sl->str, "base: %i", &j) == 1) port = j;
} else {
if(sscanf(sl->str, "%i", &j) == 1) port = j;
}
}
free_str_list(sl0);
if(hd_data->kernel_version == KERNEL_22)
str_printf(&pp, 0, PROC_PARPORT_22 "%d/autoprobe", i);
else
str_printf(&pp, 0, PROC_PARPORT_24 "%d/autoprobe", i);
sl0 = read_file(pp, 0, 0);
str_printf(&s, 0, "%s\n", pp);
add_str_list(&log, s);
base_class = device = vendor = cmd_set = NULL;
for(sl = sl0; sl; sl = sl->next) {
str_printf(&s, 0, " %s", sl->str);
add_str_list(&log, s);
// fprintf(stderr, "str = \"%s\"\n", sl->str);
if(sscanf(sl->str, "CLASS: %255[^\n;]", buf) == 1) base_class = new_str(buf);
if(sscanf(sl->str, "MODEL: %255[^\n;]", buf) == 1) device = new_str(buf);
if(sscanf(sl->str, "MANUFACTURER: %255[^\n;]", buf) == 1) vendor = new_str(buf);
if(sscanf(sl->str, "COMMAND SET: %255[^\n;]", buf) == 1) cmd_set = new_str(buf);
}
free_str_list(sl0);
/* default to printer */
if(!base_class && vendor && device) base_class = new_str("printer");
s = free_mem(s);
// fprintf(stderr, "port <0x%x\n", port);
// fprintf(stderr, "class <%s>\n", base_class);
// fprintf(stderr, "device <%s>\n", device);
// fprintf(stderr, "vendor <%s>\n", vendor);
// fprintf(stderr, "cmds <%s>\n", cmd_set);
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_comm &&
hd->sub_class.id == sc_com_par &&
hd->unix_dev_name &&
!strcmp(hd->unix_dev_name, unix_dev)
) break;
}
if(!hd) {
/* no entry ??? */
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_comm;
hd->sub_class.id = sc_com_par;
hd->unix_dev_name = new_str(unix_dev);
if(port) {
res = add_res_entry(&hd->res, new_mem(sizeof *res));
res->io.type = res_io;
res->io.enabled = 1;
res->io.base = port;
res->io.access = acc_rw;
}
}
// ##### check if ports match?
if(
base_class ||
(device && strcmp(device, "Unknown device")) ||
(vendor && strcmp(vendor, "Unknown vendor"))
) {
hd_i = hd;
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->attached_to = hd_i->idx;
hd->unix_dev_name = new_str(hd_i->unix_dev_name);
hd->base_class.id = bc_none;
if(base_class && !strcasecmp(base_class, "printer")) hd->base_class.id = bc_printer;
hd->bus.id = bus_parallel;
hd->vendor.name = new_str(vendor);
hd->device.name = new_str(device);
}
free_mem(base_class);
free_mem(device);
free_mem(vendor);
free_mem(cmd_set);
}
pp = free_mem(pp);
if((hd_data->debug & HD_DEB_PARALLEL)) dump_parallel_data(hd_data, log);
free_str_list(log);
}
void do_zip(hd_data_t *hd_data)
{
hd_t *hd, *hd_i;
int i, j, port, is_imm, is_ppa, is_imm0, is_ppa0;
char *pp = NULL, *s = NULL, *unix_dev = NULL;
str_list_t *log = NULL, *sl, *sl0;
int do_imm = hd_probe_feature(hd_data, pr_parallel_imm);
is_imm = is_imm0 = hd_module_is_active(hd_data, "imm");
is_ppa = is_ppa0 = hd_module_is_active(hd_data, "ppa");
if(!(is_imm || is_ppa)) {
for(hd = hd_data->hd; hd; hd = hd->next) {
if(hd->base_class.id == bc_comm && hd->sub_class.id == sc_com_par) break;
}
/* ... if there seems to be a parallel interface, try to load it */
if(hd) {
if(do_imm) {
PROGRESS(5, 0, "imm mod");
load_module(hd_data, "imm");
}
PROGRESS(5, 0, "ppa mod");
load_module(hd_data, "ppa");
is_imm = hd_module_is_active(hd_data, "imm");
is_ppa = hd_module_is_active(hd_data, "ppa");
if(do_imm && !is_imm) {
int fd;
char flush[2] = { 4, 12 };
fd = open("/dev/lp0", O_NONBLOCK | O_WRONLY);
if(fd != -1) {
write(fd, flush, sizeof flush);
close(fd);
}
}
}
}
if(!(is_imm || is_ppa)) return;
PROGRESS(6, 0, "zip read info");
for(i = 0; i < 16; i++) {
str_printf(&pp, 0, PROC_SCSI "/%s/%d", (i % 2) ? "ppa" : "imm", i / 2);
sl0 = read_file(pp, 0, 0);
if(!sl0) continue;
str_printf(&s, 0, "%s\n", pp);
add_str_list(&log, s);
port = -1;
for(sl = sl0; sl; sl = sl->next) {
str_printf(&s, 0, " %s", sl->str);
add_str_list(&log, s);
if(sscanf(sl->str, "Parport : parport%d", &j) == 1) port = j;
}
free_str_list(sl0);
pp = free_mem(pp);
s = free_mem(s);
unix_dev = free_mem(unix_dev);
if(port >= 0) {
str_printf(&unix_dev, 0, "/dev/lp%d", port);
}
hd = NULL;
if(unix_dev) {
for(hd = hd_data->hd; hd; hd = hd->next) {
if(
hd->base_class.id == bc_comm &&
hd->sub_class.id == sc_com_par &&
hd->unix_dev_name &&
!strcmp(hd->unix_dev_name, unix_dev)
) break;
}
if(!hd) {
/* no entry ??? */
hd = add_hd_entry(hd_data, __LINE__, 0);
hd->base_class.id = bc_comm;
hd->sub_class.id = sc_com_par;
hd->unix_dev_name = new_str(unix_dev);
}
}
hd_i = hd;
hd = add_hd_entry(hd_data, __LINE__, 0);
if(hd_i) {
hd->attached_to = hd_i->idx;
hd->unix_dev_name = new_str(hd_i->unix_dev_name);
}
hd->base_class.id = bc_storage;
hd->sub_class.id = sc_sto_scsi;
hd->bus.id = bus_parallel;
hd->vendor.id = MAKE_ID(TAG_SPECIAL, 0x1800);
hd->device.id = MAKE_ID(TAG_SPECIAL, (i % 2) ? 2 : 1);
}
if(!is_imm0) unload_module(hd_data, "imm");
if(!is_ppa0) unload_module(hd_data, "ppa");
if((hd_data->debug & HD_DEB_PARALLEL)) dump_parallel_data(hd_data, log);
free_mem(unix_dev);
free_str_list(log);
}
void dump_parallel_data(hd_data_t *hd_data, str_list_t *sl)
{
ADD2LOG("----- parallel info -----\n");
for(; sl; sl = sl->next) {
ADD2LOG("%s", sl->str);
}
ADD2LOG("----- parallel info end -----\n");
}
#endif /* ifndef LIBHD_TINY */
/** @} */

1
src/hd/parallel.h Normal file
View File

@ -0,0 +1 @@
void hd_scan_parallel(hd_data_t *hd_data);

Some files were not shown because too many files have changed in this diff Show More