diff options
author | Lars Hanisch <dvb@flensrocker.de> | 2011-02-02 14:18:45 +0100 |
---|---|---|
committer | Lars Hanisch <dvb@flensrocker.de> | 2011-02-02 14:18:45 +0100 |
commit | c03cb92fb43baab9136bd9122d757359e0590fda (patch) | |
tree | dbd9851e29274ba4b0189f3f859c1a29d20b8a88 | |
download | vdr-plugin-dynamite-c03cb92fb43baab9136bd9122d757359e0590fda.tar.gz vdr-plugin-dynamite-c03cb92fb43baab9136bd9122d757359e0590fda.tar.bz2 |
initial commit of version 0.0.5c
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | HISTORY | 72 | ||||
-rw-r--r-- | Makefile | 118 | ||||
-rw-r--r-- | README | 156 | ||||
-rw-r--r-- | dynamicdevice.c | 743 | ||||
-rw-r--r-- | dynamicdevice.h | 127 | ||||
-rw-r--r-- | dynamite.c | 341 | ||||
-rw-r--r-- | monitor.c | 220 | ||||
-rw-r--r-- | monitor.h | 58 | ||||
-rw-r--r-- | patches/sc-1.0.0pre-subdevice.patch | 215 | ||||
-rw-r--r-- | patches/vdr-1.7.16-dynamite-subdevice.patch | 361 | ||||
-rw-r--r-- | udev.c | 110 | ||||
-rw-r--r-- | udev.h | 41 |
13 files changed, 2902 insertions, 0 deletions
@@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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) <year> <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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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 Lesser General +Public License instead of this License. @@ -0,0 +1,72 @@ +VDR Plugin 'dynamite' Revision History +-------------------------------------- + +2011-01-06: Version 0.0.1 + +- Initial revision. + +(developing and releasing were fast the last days + so skipping some meaningless changes...) + +2011-01-08: Version 0.0.4a + +- rework patch for vdr to it won't collide with other patches +- LSTD outputs an asterisk behind the number of the primary device +- the number corresponds to the index in the array so it won't + change for one device if others are attached/detached. +- DETD called with a number tries to detach the device at the + given position in the array. + +2011-01-09: Version 0.0.4b + +- protect device array with a mutex +- add Service Interface "dynamite-AttachDevice-v0.1" and + "dynamite-DetachDevice-v0.1" + the pointer passed in "Data" is interpreted as a devpath like + the SVDRP command ATTD and DETD. + Returns always "true" on these commands even if Data is NULL + as suggested in the docs. +- don't detach device which are receiving something with a + priority > 0. + +2011-01-09: Version 0.0.4c + +- add new commands for locking/unlocking devices so the can + be protected from accidently detaching (LCKD / UNLD) +- extend the Service interface with these commands + "dynamite-LockDevice-v0.1" / "dynamite-UnlockDevice-v0.1" + +2011-01-11: Version 0.0.4e + +- add new command SCND + +2011-01-11: Version 0.0.4i + +- now it seems to work... :-) + +2011-01-27: Version 0.0.4j + +- add "GetTS" watchdog +- detach player and transfer mode on detach so all handles are freed + +2011-01-27: Version 0.0.5 + +- add udev monitor for auto attaching new dvb-frontends +- you can use "dynamite --log-udev" on the vdr commandline for logging + lots of udev events + +2011-01-27: Version 0.0.5a + +- refactor udev wrapper + +2011-01-29: Version 0.0.5b + +- add new command "SDGT" for setting a default "GetTS"-timeout for all attached + and "to be attached" devices +- add "dynamite.DefaultGetTSTimeout" to setup.conf +- speed up dvb-device creation +- add alternate (more readable) commands for SVDRP like "AttachDevice" for "ATTD" + +2011-01-30: Version 0.0.5c + +- correct LDFLAGS in Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f3f7775 --- /dev/null +++ b/Makefile @@ -0,0 +1,118 @@ +# +# Makefile for a Video Disk Recorder plugin +# +# $Id$ + +# The official name of this plugin. +# This name will be used in the '-P...' option of VDR to load the plugin. +# By default the main source file also carries this name. +# IMPORTANT: the presence of this macro is important for the Make.config +# file. So it must be defined, even if it is not used here! +# +PLUGIN = dynamite + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' $(PLUGIN).c | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -g -O2 -Wall -Woverloaded-virtual -Wno-parentheses +LDFLAGS += -ludev + +### The directory environment: + +VDRDIR = ../../.. +LIBDIR = ../../lib +TMPDIR = /tmp + +### Make sure that necessary options are included: + +include $(VDRDIR)/Make.global + +### Allow user defined options to overwrite defaults: + +-include $(VDRDIR)/Make.config + +### The version number of VDR's plugin API (taken from VDR's "config.h"): + +APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) + +### The name of the distribution archive: + +ARCHIVE = $(PLUGIN)-$(VERSION) +PACKAGE = vdr-$(ARCHIVE) + +### Includes and Defines (add further entries here): + +INCLUDES += -I$(VDRDIR)/include + +DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' +ifdef YAVDR_PATCHES +DEFINE += -DYAVDRPATCHES +endif + +### The object files (add further files here): + +OBJS = $(PLUGIN).o dynamicdevice.o monitor.o udev.o + +### The main target: + +all: libvdr-$(PLUGIN).so i18n + +### Implicit rules: + +%.o: %.c + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR = po +LOCALEDIR = $(VDRDIR)/locale +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/$(PLUGIN).pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(wildcard *.c) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='<see README>' -o $@ $^ + +%.po: $(I18Npot) + msgmerge -U --no-wrap --no-location --backup=none -q $@ $< + @touch $@ + +$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo + @mkdir -p $(dir $@) + cp $< $@ + +.PHONY: i18n +i18n: $(I18Nmsgs) $(I18Npot) + +### Targets: + +libvdr-$(PLUGIN).so: $(OBJS) + $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LDFLAGS) -o $@ + @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) + +dist: clean + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE) + @cp -a * $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) + @-rm -rf $(TMPDIR)/$(ARCHIVE) + @echo Distribution package created as $(PACKAGE).tgz + +clean: + @-rm -f $(OBJS) $(DEPFILE) *.so *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot @@ -0,0 +1,156 @@ +This is a "plugin" for the Video Disk Recorder (VDR). + +Written by: Lars Hanisch <dvb@flensrocker.de> + +Project's homepage: <not yet assigned> + +Latest version available at: http://www.vdr-portal.de/board/thread.php?threadid=102903 + +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. +See the file COPYING for more information. + +Description +----------- +This plugin turns the dvbdevices into hotpluggable devices. They +can be dynamically attached and detached while vdr is running. +They are replaced with a "dumb device" which can receive nothing. +Don't forget to patch the vdr-source, you find the necessary one +in the "patches" directory of the plugin sources. + +How it works +------------ +It creates as many devices as it can till the global vdr-device-array +is full. After this plugin is loaded every device created by a +plugin can't be used by vdr. So make sure dynamite is loaded as +the last plugin. + +If a device is dynamically attached to vdr, this plugin creates an +instance of the corresponding cDevice-subclass and plugs it into +a free "dumb device" mentioned above. Now this device can do +everything its subdevice can do. + +If a device is detached the corresponding subdevice is deleted and +the "dumb device" is dumb again. + +A device with receivers at priorities > 0 can't be detached. + +How to make other plugins "explosive" +------------------------------------- +Like cDvbDeviceProbe there is a list of probe-objects where a plugin +can hook into (see cDynamicDeviceProbe at the end of patched device.h). +The Probe-function gets an devicepath and if the plugin can handle +this it returns a pointer to a new created device on the heap. +Don't forget to pass through "ParentDevice" parameter to the cDevice +constructor! + +You don't need to remember this pointer, it will be deleted by dynamite. +The devicepath doesn't need to be a valid path like +/dev/dvb/adapter0/frontend0 or /dev/video0 etc. since the plugin decides +what it should do with this string. In dynamite.c you can see an example +of an dynamic dummy device creator. It reacts on the devicepath +"dummydevice" followed by a number e.g. "dummydevice0". + +If a plugin wants its device to be dynamically attached/detached it must +not create its devices in its Initialize function. Instead it should +queue the found devicepathes with the helper function +"cDynamicDeviceProbe::QueueDynamicDeviceCommand". After initialization +the dynamite-plugin is calling every cDynamicDeviceProbe with the queued +devicepathes in its Initialize-function. See the patches directory for examples. + +The devices have to close all their handles in their destructor and clean +up after them. Otherwise this is a potential source of memory leaks. +Plugins which creates a cDvbDeviceProbe should replace it with a +cDynamicDeviceProbe (you can use "#ifdef __DYNAMIC_DEVICE_PROBE" for +conditional compiling). + +How to attach/detach a device +----------------------------- +There a some new SVDRP commands for the dynamic devices. The string in quotes +above the command is for internal Service-interface of the vdr-plugins. + +"dynamite-AttachDevice-v0.1" +ATTD devicepath + Asks all cDynamicDeviceProbe-objects to attach the given devicepath + till one returns a valid pointer. You can control the order by the + order of the plugins you load + alternate command: AttachDevice + +"dynamite-DetachDevice-v0.1" +DETD devicepath + Looks through its remembered devicepaths and deletes the attached + device if found. Case is important! + Any timeouts or locks set to this slot will be reset to its defaults. + alternate command: DetachDevice + +"dynamite-ScanDevices-v0.1" +SCND '/dev/path/glob*/pattern*' + Scan filesystem with pattern and try to attach each found device + don't forget to enclose the pattern with single quotes + e.g. SCND '/dev/dvb/adapter*/frontend*' + alternate command: ScanDevices + +"dynamite-LockDevice-v0.1" +LCKD /dev/path/to/device + Lock the device so it can't be detached + alternate command: LockDevice + +"dynamite-UnlockDevice-v0.1" +UNLD /dev/path/to/device + Remove the lock of the device so it can be detached + alternate command: UnlockDevice + +(no Service-interface) +LSTD + Lists all devices managed by this plugin. The first column is an id, + the second column is the devicepath passed with ATTD + The id can be used with the DETD and UNLD commands instead of the path. + An asterisk behind the id means that this device is locked and + can't be detached. + alternate command: ListDevices + +"dynamite-SetGetTSTimeout-v0.1" +SGTT /dev/path/to/device seconds + Sets the \"GetTSPacket\"-watchdog timeout to specified amount of seconds + If the device returns no data (Data == NULL) for this period of time, + the device will be detached. Usefull if you want to reload the driver etc. + A value of 0 seconds disables the watchdog. + alternate command: SetGetTSTimeout + +"dynamite-SetDefaultGetTSTimeout-v0.1" +SDGT seconds + Sets the \"GetTSPacket\"-watchdog timeout for all attached devices + and all devices that will be attached. + alternate command: SetDefaultGetTSTimeout + +Don't forget to prefix them with "plug dynamite"... + +Parameters in setup.conf +------------------------ +dynamite.DefaultGetTSTimeout = 0 + +"GetTS" watchdog +---------------- +Some DVB cards are known to be unstable - sometimes their driver just hangs +and vdr won't get any data anymore. Mostly you have to stop vdr, reload the +drivers and restart vdr again. But that would affect other recordings etc. +If you have such a card the "Auto-Detach" feature may be useful. Just set +the "GetTS" timeout to 10 or 15 seconds (or whatever you like) and dynamite +will automatically detach this device if its GetTSPacket method returns +no data for this period of time. +WARNING: You have to attach this device manually again! For now there's no +automatism to reload driver (or whatever is needed) to reanimate the device. + +Known issues +------------ +If a device managed by this plugin is the primary device it cannot be +detached. That would imply that vdr searches for a new primary device +or should be forced to transfer mode or something else. These +circumstances are under research... + +TODO +---- +* implement interface for other plugins to use the udev monitor +* implement some OSD functionality for detaching, locking etc. diff --git a/dynamicdevice.c b/dynamicdevice.c new file mode 100644 index 0000000..f918b22 --- /dev/null +++ b/dynamicdevice.c @@ -0,0 +1,743 @@ +#include "dynamicdevice.h" +#include <glob.h> +#include <vdr/transfer.h> + +int cDynamicDevice::defaultGetTSTimeout = 0; +cDvbDeviceProbe *cDynamicDevice::dvbprobe = NULL; +int cDynamicDevice::numDynamicDevices = 0; +cMutex cDynamicDevice::arrayMutex; +cDynamicDevice *cDynamicDevice::dynamicdevice[MAXDEVICES] = { NULL }; + +int cDynamicDevice::IndexOf(const char *DevPath, int &NextFreeIndex) +{ + cMutexLock lock(&arrayMutex); + NextFreeIndex = -1; + int index = -1; + for (int i = 0; ((index < 0) || (NextFreeIndex < 0)) && (i < numDynamicDevices); i++) { + if (dynamicdevice[i]->devpath == NULL) { + if (NextFreeIndex < 0) + NextFreeIndex = i; + } + else if (index < 0) { + if (strcmp(DevPath, **dynamicdevice[i]->devpath) == 0) + index = i; + } + } + return index; +} + +bool cDynamicDevice::ProcessQueuedCommands(void) +{ + for (cDynamicDeviceProbe::cDynamicDeviceProbeItem *dev = cDynamicDeviceProbe::commandQueue.First(); dev; dev = cDynamicDeviceProbe::commandQueue.Next(dev)) { + switch (dev->cmd) { + case ddpcAttach: + { + AttachDevice(*dev->devpath); + break; + } + case ddpcDetach: + { + DetachDevice(*dev->devpath); + break; + } + } + } + cDynamicDeviceProbe::commandQueue.Clear(); + return true; +} + +void cDynamicDevice::DetachAllDevices(void) +{ + cMutexLock lock(&arrayMutex); + for (int i = 0; i < numDynamicDevices; i++) + dynamicdevice[i]->DeleteSubDevice(); +} + +cString cDynamicDevice::ListAllDevices(int &ReplyCode) +{ + cMutexLock lock(&arrayMutex); + cString devices; + int count = 0; + for (int i = 0; i < numDynamicDevices; i++) { + if ((dynamicdevice[i]->devpath != NULL) && (dynamicdevice[i]->subDevice != NULL)) { + count++; + devices = cString::sprintf("%s%d%s %s\n", (count == 1) ? "" : *devices + , i + 1 + , ((PrimaryDevice() == dynamicdevice[i]) || !dynamicdevice[i]->isDetachable) ? "*" : "" + , **dynamicdevice[i]->devpath); + } + } + if (count == 0) { + ReplyCode = 901; + return cString::sprintf("there are no attached devices"); + } + return devices; +} + +cString cDynamicDevice::AttachDevicePattern(const char *Pattern) +{ + if (!Pattern) + return "invalid pattern"; + cString reply; + glob_t result; + if (glob(Pattern, GLOB_MARK, 0, &result) == 0) { + for (uint i = 0; i < result.gl_pathc; i++) { + cDynamicDeviceProbe::QueueDynamicDeviceCommand(ddpcAttach, result.gl_pathv[i]); + reply = cString::sprintf("%squeued %s for attaching\n", (i == 0) ? "" : *reply, result.gl_pathv[i]); + } + } + globfree(&result); + return reply; +} + +eDynamicDeviceReturnCode cDynamicDevice::AttachDevice(const char *DevPath) +{ + if (!DevPath) + return ddrcNotSupported; + + cMutexLock lock(&arrayMutex); + int freeIndex = -1; + int index = IndexOf(DevPath, freeIndex); + int adapter = -1; + int frontend = -1; + + if (index >= 0) { + esyslog("dynamite: %s is already attached", DevPath); + return ddrcAlreadyAttached; + } + + if (freeIndex < 0) { + esyslog("dynamite: no more free slots for %s", DevPath); + return ddrcNoFreeDynDev; + } + + cDevice::nextParentDevice = dynamicdevice[freeIndex]; + + for (cDynamicDeviceProbe *ddp = DynamicDeviceProbes.First(); ddp; ddp = DynamicDeviceProbes.Next(ddp)) { + if (ddp->Attach(dynamicdevice[freeIndex], DevPath)) + goto attach; // a plugin has created the actual device + } + + // if it's a dvbdevice try the DvbDeviceProbes as a fallback for unpatched plugins + if (sscanf(DevPath, "/dev/dvb/adapter%d/frontend%d", &adapter, &frontend) == 2) { + for (cDvbDeviceProbe *dp = DvbDeviceProbes.First(); dp; dp = DvbDeviceProbes.Next(dp)) { + if (dp != dvbprobe) { + if (dp->Probe(adapter, frontend)) + goto attach; + } + } + new cDvbDevice(adapter, frontend, dynamicdevice[freeIndex]); + goto attach; + } + + esyslog("dynamite: can't attach %s", DevPath); + return ddrcNotSupported; + +attach: + while (!dynamicdevice[freeIndex]->Ready()) + cCondWait::SleepMs(2); + dynamicdevice[freeIndex]->devpath = new cString(DevPath); + isyslog("dynamite: attached device %s to dynamic device slot %d", DevPath, freeIndex + 1); + return ddrcSuccess; +} + +eDynamicDeviceReturnCode cDynamicDevice::DetachDevice(const char *DevPath) +{ + if (!DevPath) + return ddrcNotSupported; + + cMutexLock lock(&arrayMutex); + int freeIndex = -1; + int index = -1; + if (isnumber(DevPath)) + index = strtol(DevPath, NULL, 10) - 1; + else + index = IndexOf(DevPath, freeIndex); + + if ((index < 0) || (index >= numDynamicDevices)) { + esyslog("dynamite: device %s not found", DevPath); + return ddrcNotFound; + } + + if (!dynamicdevice[index]->isDetachable) { + esyslog("dynamite: detaching of device %s is not allowed", DevPath); + return ddrcNotAllowed; + } + + if (dynamicdevice[index] == PrimaryDevice()) { + esyslog("dynamite: detaching of primary device %s is not supported", DevPath); + return ddrcIsPrimaryDevice; + } + + if (dynamicdevice[index]->Receiving(false)) { + esyslog("dynamite: can't detach device %s, it's receiving something important", DevPath); + return ddrcIsReceiving; + } + + dynamicdevice[index]->DeleteSubDevice(); + isyslog("dynamite: detached device %s", DevPath); + return ddrcSuccess; +} + +eDynamicDeviceReturnCode cDynamicDevice::SetLockDevice(const char *DevPath, bool Lock) +{ + if (!DevPath) + return ddrcNotSupported; + + cMutexLock lock(&arrayMutex); + int freeIndex = -1; + int index = -1; + if (isnumber(DevPath)) + index = strtol(DevPath, NULL, 10) - 1; + else + index = IndexOf(DevPath, freeIndex); + + if ((index < 0) || (index >= numDynamicDevices)) + return ddrcNotFound; + + dynamicdevice[index]->isDetachable = !Lock; + isyslog("dynamite: %slocked device %s", Lock ? "" : "un", DevPath); + return ddrcSuccess; +} + +eDynamicDeviceReturnCode cDynamicDevice::SetGetTSTimeout(const char *DevPath, int Seconds) +{ + if (!DevPath || (Seconds < 0)) + return ddrcNotSupported; + + cMutexLock lock(&arrayMutex); + int freeIndex = -1; + int index = -1; + if (isnumber(DevPath)) + index = strtol(DevPath, NULL, 10) - 1; + else + index = IndexOf(DevPath, freeIndex); + + if ((index < 0) || (index >= numDynamicDevices)) + return ddrcNotFound; + + dynamicdevice[index]->getTSTimeout = Seconds; + if (Seconds == 0) + isyslog("dynamite: disable GetTSTimeout on device %s", DevPath); + else + isyslog("dynamite: set GetTSTimeout on device %s to %d seconds", DevPath, Seconds); + return ddrcSuccess; +} + +void cDynamicDevice::SetDefaultGetTSTimeout(int Seconds) +{ + if (Seconds >= 0) { + defaultGetTSTimeout = Seconds; + cMutexLock lock(&arrayMutex); + for (int i = 0; i < numDynamicDevices; i++) + dynamicdevice[i]->getTSTimeout = Seconds; + isyslog("dynamite: set default GetTSTimeout on all devices to %d seconds", Seconds); + } +} + +bool cDynamicDevice::IsAttached(const char *DevPath) +{ + cMutexLock lock(&arrayMutex); + int freeIndex = -1; + int index = IndexOf(DevPath, freeIndex); + return ((index >= 0) && (index >= numDynamicDevices)); +} + +cDynamicDevice::cDynamicDevice() +:index(-1) +,devpath(NULL) +,isDetachable(true) +,getTSTimeout(defaultGetTSTimeout) +{ + index = numDynamicDevices; + if (numDynamicDevices < MAXDEVICES) { + dynamicdevice[index] = this; + numDynamicDevices++; + } + else + esyslog("dynamite: ERROR: too many dynamic devices!"); +} + +cDynamicDevice::~cDynamicDevice() +{ + DeleteSubDevice(); +} + +void cDynamicDevice::DeleteSubDevice() +{ + if (subDevice) { + Cancel(3); + if (cTransferControl::ReceiverDevice() == this) + cControl::Shutdown(); + subDevice->Detach(player); + subDevice->DetachAllReceivers(); + subDevice->StopSectionHandler(); + delete subDevice; + subDevice = NULL; + } + if (devpath) { + delete devpath; + devpath = NULL; + } + isDetachable = true; + getTSTimeout = defaultGetTSTimeout; +} + +void cDynamicDevice::MakePrimaryDevice(bool On) +{ + if (subDevice) + subDevice->MakePrimaryDevice(On); + cDevice::MakePrimaryDevice(On); +} + +bool cDynamicDevice::HasDecoder(void) const +{ + if (subDevice) + return subDevice->HasDecoder(); + return cDevice::HasDecoder(); +} + +cSpuDecoder *cDynamicDevice::GetSpuDecoder(void) +{ + if (subDevice) + return subDevice->GetSpuDecoder(); + return cDevice::GetSpuDecoder(); +} + +bool cDynamicDevice::HasCi(void) +{ + if (subDevice) + return subDevice->HasCi(); + return cDevice::HasCi(); +} + +uchar *cDynamicDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int SizeY) +{ + if (subDevice) + return subDevice->GrabImage(Size, Jpeg, Quality, SizeX, SizeY); + return cDevice::GrabImage(Size, Jpeg, Quality, SizeX, SizeY); +} + +void cDynamicDevice::SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat) +{ + if (subDevice) + return subDevice->SetVideoDisplayFormat(VideoDisplayFormat); + cDevice::SetVideoDisplayFormat(VideoDisplayFormat); +} + +void cDynamicDevice::SetVideoFormat(bool VideoFormat16_9) +{ + if (subDevice) + return subDevice->SetVideoFormat(VideoFormat16_9); + cDevice::SetVideoFormat(VideoFormat16_9); +} + +eVideoSystem cDynamicDevice::GetVideoSystem(void) +{ + if (subDevice) + return subDevice->GetVideoSystem(); + return cDevice::GetVideoSystem(); +} + +void cDynamicDevice::GetVideoSize(int &Width, int &Height, double &VideoAspect) +{ + if (subDevice) + return subDevice->GetVideoSize(Width, Height, VideoAspect); + cDevice::GetVideoSize(Width, Height, VideoAspect); +} + +void cDynamicDevice::GetOsdSize(int &Width, int &Height, double &PixelAspect) +{ + if (subDevice) + return subDevice->GetOsdSize(Width, Height, PixelAspect); + cDevice::GetOsdSize(Width, Height, PixelAspect); +} + +bool cDynamicDevice::SetPid(cPidHandle *Handle, int Type, bool On) +{ + if (subDevice) + return subDevice->SetPid(Handle, Type, On); + return cDevice::SetPid(Handle, Type, On); +} + +int cDynamicDevice::OpenFilter(u_short Pid, u_char Tid, u_char Mask) +{ + if (subDevice) + return subDevice->OpenFilter(Pid, Tid, Mask); + return cDevice::OpenFilter(Pid, Tid, Mask); +} + +void cDynamicDevice::CloseFilter(int Handle) +{ + if (subDevice) + return subDevice->CloseFilter(Handle); + cDevice::CloseFilter(Handle); +} + +bool cDynamicDevice::ProvidesSource(int Source) const +{ + if (subDevice) + return subDevice->ProvidesSource(Source); + return cDevice::ProvidesSource(Source); +} + +bool cDynamicDevice::ProvidesTransponder(const cChannel *Channel) const +{ + if (subDevice) + return subDevice->ProvidesTransponder(Channel); + return cDevice::ProvidesTransponder(Channel); +} + +bool cDynamicDevice::ProvidesTransponderExclusively(const cChannel *Channel) const +{ + if (subDevice) + return subDevice->ProvidesTransponderExclusively(Channel); + return cDevice::ProvidesTransponderExclusively(Channel); +} + +bool cDynamicDevice::ProvidesChannel(const cChannel *Channel, int Priority, bool *NeedsDetachReceivers) const +{ + if (subDevice) + return subDevice->ProvidesChannel(Channel, Priority, NeedsDetachReceivers); + return cDevice::ProvidesChannel(Channel, Priority, NeedsDetachReceivers); +} + +int cDynamicDevice::NumProvidedSystems(void) const +{ + if (subDevice) + return subDevice->NumProvidedSystems(); + return cDevice::NumProvidedSystems(); +} + +const cChannel *cDynamicDevice::GetCurrentlyTunedTransponder(void) const +{ + if (subDevice) + return subDevice->GetCurrentlyTunedTransponder(); + return cDevice::GetCurrentlyTunedTransponder(); +} + +bool cDynamicDevice::IsTunedToTransponder(const cChannel *Channel) +{ + if (subDevice) + return subDevice->IsTunedToTransponder(Channel); + return cDevice::IsTunedToTransponder(Channel); +} + +bool cDynamicDevice::MaySwitchTransponder(void) +{ + if (subDevice) + return subDevice->MaySwitchTransponder(); + return cDevice::MaySwitchTransponder(); +} + +bool cDynamicDevice::SetChannelDevice(const cChannel *Channel, bool LiveView) +{ + if (subDevice) + return subDevice->SetChannelDevice(Channel, LiveView); + return cDevice::SetChannelDevice(Channel, LiveView); +} + +bool cDynamicDevice::HasLock(int TimeoutMs) +{ + if (subDevice) + return subDevice->HasLock(TimeoutMs); + return cDevice::HasLock(TimeoutMs); +} + +bool cDynamicDevice::HasProgramme(void) +{ + if (subDevice) + return subDevice->HasProgramme(); + return cDevice::HasProgramme(); +} + +int cDynamicDevice::GetAudioChannelDevice(void) +{ + if (subDevice) + return subDevice->GetAudioChannelDevice(); + return GetAudioChannelDevice(); +} + +void cDynamicDevice::SetAudioChannelDevice(int AudioChannel) +{ + if (subDevice) + return subDevice->SetAudioChannelDevice(AudioChannel); + cDevice::SetAudioChannelDevice(AudioChannel); +} + +void cDynamicDevice::SetVolumeDevice(int Volume) +{ + if (subDevice) + return subDevice->SetVolumeDevice(Volume); + cDevice::SetVolumeDevice(Volume); +} + +void cDynamicDevice::SetDigitalAudioDevice(bool On) +{ + if (subDevice) + return subDevice->SetDigitalAudioDevice(On); + cDevice::SetDigitalAudioDevice(On); +} + +void cDynamicDevice::SetAudioTrackDevice(eTrackType Type) +{ + if (subDevice) + return subDevice->SetAudioTrackDevice(Type); + cDevice::SetAudioTrackDevice(Type); +} + +void cDynamicDevice::SetSubtitleTrackDevice(eTrackType Type) +{ + if (subDevice) + return subDevice->SetSubtitleTrackDevice(Type); + cDevice::SetSubtitleTrackDevice(Type); +} + +bool cDynamicDevice::CanReplay(void) const +{ + if (subDevice) + return subDevice->CanReplay(); + return cDevice::CanReplay(); +} + +bool cDynamicDevice::SetPlayMode(ePlayMode PlayMode) +{ + if (subDevice) + return subDevice->SetPlayMode(PlayMode); + return cDevice::SetPlayMode(PlayMode); +} + +int64_t cDynamicDevice::GetSTC(void) +{ + if (subDevice) + return subDevice->GetSTC(); + return cDevice::GetSTC(); +} + +bool cDynamicDevice::IsPlayingVideo(void) const +{ + if (subDevice) + return subDevice->IsPlayingVideo(); + return cDevice::IsPlayingVideo(); +} + +bool cDynamicDevice::HasIBPTrickSpeed(void) +{ + if (subDevice) + return subDevice->HasIBPTrickSpeed(); + return cDevice::HasIBPTrickSpeed(); +} + +void cDynamicDevice::TrickSpeed(int Speed) +{ + if (subDevice) + return subDevice->TrickSpeed(Speed); + cDevice::TrickSpeed(Speed); +} + +void cDynamicDevice::Clear(void) +{ + if (subDevice) + return subDevice->Clear(); + cDevice::Clear(); +} + +void cDynamicDevice::Play(void) +{ + if (subDevice) + return subDevice->Play(); + cDevice::Play(); +} + +void cDynamicDevice::Freeze(void) +{ + if (subDevice) + return subDevice->Freeze(); + cDevice::Freeze(); +} + +void cDynamicDevice::Mute(void) +{ + if (subDevice) + return subDevice->Mute(); + cDevice::Mute(); +} + +void cDynamicDevice::StillPicture(const uchar *Data, int Length) +{ + if (subDevice) + return subDevice->StillPicture(Data, Length); + cDevice::StillPicture(Data, Length); +} + +bool cDynamicDevice::Poll(cPoller &Poller, int TimeoutMs) +{ + if (subDevice) + return subDevice->Poll(Poller, TimeoutMs); + return cDevice::Poll(Poller, TimeoutMs); +} + +bool cDynamicDevice::Flush(int TimeoutMs) +{ + if (subDevice) + return subDevice->Flush(TimeoutMs); + return cDevice::Flush(TimeoutMs); +} + +int cDynamicDevice::PlayVideo(const uchar *Data, int Length) +{ + if (subDevice) + return subDevice->PlayVideo(Data, Length); + return cDevice::PlayVideo(Data, Length); +} + +int cDynamicDevice::PlayAudio(const uchar *Data, int Length, uchar Id) +{ + if (subDevice) + return subDevice->PlayAudio(Data, Length, Id); + return cDevice::PlayAudio(Data, Length, Id); +} + +int cDynamicDevice::PlaySubtitle(const uchar *Data, int Length) +{ + if (subDevice) + return subDevice->PlaySubtitle(Data, Length); + return cDevice::PlaySubtitle(Data, Length); +} + +int cDynamicDevice::PlayPesPacket(const uchar *Data, int Length, bool VideoOnly) +{ + if (subDevice) + return subDevice->PlayPesPacket(Data, Length, VideoOnly); + return cDevice::PlayPesPacket(Data, Length, VideoOnly); +} + +int cDynamicDevice::PlayPes(const uchar *Data, int Length, bool VideoOnly) +{ + if (subDevice) + return subDevice->PlayPes(Data, Length, VideoOnly); + return cDevice::PlayPes(Data, Length, VideoOnly); +} + +int cDynamicDevice::PlayTsVideo(const uchar *Data, int Length) +{ + if (subDevice) + return subDevice->PlayTsVideo(Data, Length); + return cDevice::PlayTsVideo(Data, Length); +} + +int cDynamicDevice::PlayTsAudio(const uchar *Data, int Length) +{ + if (subDevice) + return subDevice->PlayTsAudio(Data, Length); + return cDevice::PlayTsAudio(Data, Length); +} + +int cDynamicDevice::PlayTsSubtitle(const uchar *Data, int Length) +{ + if (subDevice) + return subDevice->PlayTsSubtitle(Data, Length); + return cDevice::PlayTsSubtitle(Data, Length); +} + +int cDynamicDevice::PlayTs(const uchar *Data, int Length, bool VideoOnly) +{ + if (subDevice) + return subDevice->PlayTs(Data, Length, VideoOnly); + return cDevice::PlayTs(Data, Length, VideoOnly); +} + +bool cDynamicDevice::Ready(void) +{ + if (subDevice) + return subDevice->Ready(); + return cDevice::Ready(); +} + +bool cDynamicDevice::OpenDvr(void) +{ + if (subDevice) { + getTSWatchdog = 0; + return subDevice->OpenDvr(); + } + return cDevice::OpenDvr(); +} + +void cDynamicDevice::CloseDvr(void) +{ + if (subDevice) + return subDevice->CloseDvr(); + cDevice::CloseDvr(); +} + +bool cDynamicDevice::GetTSPacket(uchar *&Data) +{ + if (subDevice) { + bool r = subDevice->GetTSPacket(Data); + if (getTSTimeout > 0) { + if (Data == NULL) { + if (getTSWatchdog == 0) + getTSWatchdog = time(NULL); + else if ((time(NULL) - getTSWatchdog) > getTSTimeout) { + const char *d = NULL; + if (devpath) + d = **devpath; + esyslog("dynamite: device %s hasn't delivered any data for %d seconds, it will be detached", d, getTSTimeout); + cDynamicDeviceProbe::QueueDynamicDeviceCommand(ddpcDetach, *devpath); + return false; + } + } + else + getTSWatchdog = 0; + } + return r; + } + return cDevice::GetTSPacket(Data); +} + +#ifdef YAVDR_PATCHES +//opt-21_internal-cam-devices.dpatch +bool cDynamicDevice::HasInternalCam(void) +{ + if (subDevice) + return subDevice->HasInternalCam(); + return cDevice::HasInternalCam(); +} + +//opt-44_rotor.dpatch +bool cDynamicDevice::SendDiseqcCmd(dvb_diseqc_master_cmd cmd) +{ + if (subDevice) + return subDevice->SendDiseqcCmd(cmd); + return cDevice::SendDiseqcCmd(cmd); +} + +//opt-64_lnb-sharing.dpatch +void cDynamicDevice::SetLnbNrFromSetup(void) +{ + if (subDevice) + return subDevice->SetLnbNrFromSetup(); + cDevice::SetLnbNrFromSetup(); +} + +int cDynamicDevice::LnbNr(void) const +{ + if (subDevice) + return subDevice->LnbNr(); + return cDevice::LnbNr(); +} + +bool cDynamicDevice::IsShareLnb(const cDevice *Device) +{ + if (subDevice) + return subDevice->IsShareLnb(Device); + return cDevice::IsShareLnb(Device); +} + +bool cDynamicDevice::IsLnbConflict(const cChannel *Channel) +{ + if (subDevice) + return subDevice->IsLnbConflict(Channel); + return cDevice::IsLnbConflict(Channel); +} +#endif diff --git a/dynamicdevice.h b/dynamicdevice.h new file mode 100644 index 0000000..1fe9831 --- /dev/null +++ b/dynamicdevice.h @@ -0,0 +1,127 @@ +#ifndef __DYNAMITEDEVICE_H +#define __DYNAMITEDEVICE_H + +#include <vdr/dvbdevice.h> + +enum eDynamicDeviceReturnCode { ddrcSuccess, + ddrcNoFreeDynDev, + ddrcAlreadyAttached, + ddrcNotFound, + ddrcIsPrimaryDevice, + ddrcIsReceiving, + ddrcNotAllowed, + ddrcNotSupported + }; + +class cDynamicDevice : public cDevice { +private: + static int defaultGetTSTimeout; + + static int numDynamicDevices; + static cMutex arrayMutex; + static cDynamicDevice *dynamicdevice[MAXDEVICES]; + static int IndexOf(const char *DevPath, int &NextFreeIndex); +public: + static cDvbDeviceProbe *dvbprobe; + static int NumDynamicDevices(void) { return numDynamicDevices; } + ///< Returns the total number of dynamic devices. + static bool ProcessQueuedCommands(void); + static void DetachAllDevices(void); + static cString ListAllDevices(int &ReplyCode); // for SVDRP command LSTD + static cString AttachDevicePattern(const char *Pattern); + static eDynamicDeviceReturnCode AttachDevice(const char *DevPath); + static eDynamicDeviceReturnCode DetachDevice(const char *DevPath); + static eDynamicDeviceReturnCode SetLockDevice(const char *DevPath, bool Lock); + static eDynamicDeviceReturnCode SetGetTSTimeout(const char *DevPath, int Seconds); + static void SetDefaultGetTSTimeout(int Seconds); + static bool IsAttached(const char *DevPath); +private: + int index; + cString *devpath; + bool isDetachable; + time_t getTSWatchdog; + int getTSTimeout; +public: + cDynamicDevice(); + void DeleteSubDevice(void); +protected: + virtual ~cDynamicDevice(); + virtual bool Ready(void); + virtual void MakePrimaryDevice(bool On); +public: + virtual bool HasDecoder(void) const; + virtual cSpuDecoder *GetSpuDecoder(void); + virtual bool ProvidesSource(int Source) const; + virtual bool ProvidesTransponder(const cChannel *Channel) const; + virtual bool ProvidesTransponderExclusively(const cChannel *Channel) const; + virtual bool ProvidesChannel(const cChannel *Channel, int Priority = -1, bool *NeedsDetachReceivers = NULL) const; + virtual int NumProvidedSystems(void) const; + virtual const cChannel *GetCurrentlyTunedTransponder(void) const; + virtual bool IsTunedToTransponder(const cChannel *Channel); + virtual bool MaySwitchTransponder(void); +protected: + virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView); +public: + virtual bool HasLock(int TimeoutMs = 0); + virtual bool HasProgramme(void); +protected: + virtual bool SetPid(cPidHandle *Handle, int Type, bool On); +public: + virtual int OpenFilter(u_short Pid, u_char Tid, u_char Mask); + virtual void CloseFilter(int Handle); + virtual bool HasCi(void); + virtual uchar *GrabImage(int &Size, bool Jpeg = true, int Quality = -1, int SizeX = -1, int SizeY = -1); + virtual void SetVideoDisplayFormat(eVideoDisplayFormat VideoDisplayFormat); + virtual void SetVideoFormat(bool VideoFormat16_9); + virtual eVideoSystem GetVideoSystem(void); + virtual void GetVideoSize(int &Width, int &Height, double &VideoAspect); + virtual void GetOsdSize(int &Width, int &Height, double &PixelAspect); +protected: + virtual void SetAudioTrackDevice(eTrackType Type); + virtual void SetSubtitleTrackDevice(eTrackType Type); + virtual int GetAudioChannelDevice(void); + virtual void SetAudioChannelDevice(int AudioChannel); + virtual void SetVolumeDevice(int Volume); + virtual void SetDigitalAudioDevice(bool On); + virtual bool CanReplay(void) const; + virtual bool SetPlayMode(ePlayMode PlayMode); + virtual int PlayVideo(const uchar *Data, int Length); + virtual int PlayAudio(const uchar *Data, int Length, uchar Id); + virtual int PlaySubtitle(const uchar *Data, int Length); + virtual int PlayPesPacket(const uchar *Data, int Length, bool VideoOnly = false); + virtual int PlayTsVideo(const uchar *Data, int Length); + virtual int PlayTsAudio(const uchar *Data, int Length); + virtual int PlayTsSubtitle(const uchar *Data, int Length); +public: + virtual int64_t GetSTC(void); + virtual bool IsPlayingVideo(void) const; + virtual bool HasIBPTrickSpeed(void); + virtual void TrickSpeed(int Speed); + virtual void Clear(void); + virtual void Play(void); + virtual void Freeze(void); + virtual void Mute(void); + virtual void StillPicture(const uchar *Data, int Length); + virtual bool Poll(cPoller &Poller, int TimeoutMs = 0); + virtual bool Flush(int TimeoutMs = 0); + virtual int PlayPes(const uchar *Data, int Length, bool VideoOnly = false); + virtual int PlayTs(const uchar *Data, int Length, bool VideoOnly = false); +protected: + virtual bool OpenDvr(void); + virtual void CloseDvr(void); + virtual bool GetTSPacket(uchar *&Data); + +#ifdef YAVDR_PATCHES +//opt-21_internal-cam-devices.dpatch + virtual bool HasInternalCam(void); +//opt-44_rotor.dpatch + virtual bool SendDiseqcCmd(dvb_diseqc_master_cmd cmd); +//opt-64_lnb-sharing.dpatch + virtual void SetLnbNrFromSetup(void); + virtual int LnbNr(void) const; + virtual bool IsShareLnb(const cDevice *Device); + virtual bool IsLnbConflict(const cChannel *Channel); +#endif + }; + +#endif //__DYNAMITEDEVICE_H diff --git a/dynamite.c b/dynamite.c new file mode 100644 index 0000000..79db409 --- /dev/null +++ b/dynamite.c @@ -0,0 +1,341 @@ +/* + * dynamite.c: A plugin for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + */ + +#include <vdr/plugin.h> +#include "dynamicdevice.h" +#include "monitor.h" + +static const char *VERSION = "0.0.5c"; +static const char *DESCRIPTION = "attach/detach devices on the fly"; +static const char *MAINMENUENTRY = NULL; + +class cDynamiteDvbDeviceProbe : public cDvbDeviceProbe { +private: + static bool firstProbe; +public: + virtual bool Probe(int Adapter, int Frontend) + { + if (firstProbe) { + firstProbe = false; + while (cDevice::NumDevices() < MAXDVBDEVICES) + new cDynamicDevice; + } + isyslog("dynamite: grab dvb device %d/%d", Adapter, Frontend); + cDynamicDevice::AttachDevice(*cString::sprintf("/dev/dvb/adapter%d/frontend%d", Adapter, Frontend)); + return true; // grab all dvbdevices + } + }; + +bool cDynamiteDvbDeviceProbe::firstProbe = true; + +class cDynamiteDeviceProbe : public cDynamicDeviceProbe { +private: + class cDummyDevice: public cDevice { + public: + cDummyDevice(cDevice *ParentDevice):cDevice(ParentDevice) {} + virtual ~cDummyDevice() {}; + }; +public: + virtual cDevice *Attach(cDevice *ParentDevice, const char *DevPath) + { + int nr; + if (sscanf(DevPath, "dummydevice%d", &nr) == 1) + return new cDummyDevice(ParentDevice); + return NULL; + } + }; + +class cPluginDynamite : public cPlugin { +private: + cDynamiteDeviceProbe *probe; +public: + cPluginDynamite(void); + virtual ~cPluginDynamite(); + virtual const char *Version(void) { return VERSION; } + virtual const char *Description(void) { return DESCRIPTION; } + virtual const char *CommandLineHelp(void); + virtual bool ProcessArgs(int argc, char *argv[]); + virtual bool Initialize(void); + virtual bool Start(void); + virtual void Stop(void); + virtual void Housekeeping(void); + virtual void MainThreadHook(void); + virtual cString Active(void); + virtual time_t WakeupTime(void); + virtual const char *MainMenuEntry(void) { return MAINMENUENTRY; } + virtual cOsdObject *MainMenuAction(void); + virtual cMenuSetupPage *SetupMenu(void); + virtual bool SetupParse(const char *Name, const char *Value); + virtual bool Service(const char *Id, void *Data = NULL); + virtual const char **SVDRPHelpPages(void); + virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode); + }; + +cPluginDynamite::cPluginDynamite(void) +{ + cDynamicDevice::dvbprobe = new cDynamiteDvbDeviceProbe; + // make sure we're the first one you cares for dvbdevices + cDvbDeviceProbe *firstDvbProbe = DvbDeviceProbes.First(); + if (firstDvbProbe != cDynamicDevice::dvbprobe) + DvbDeviceProbes.Move(cDynamicDevice::dvbprobe, firstDvbProbe); + probe = new cDynamiteDeviceProbe; + cUdevMonitor::AddFilter("dvb", new cUdevDvbFilter()); +} + +cPluginDynamite::~cPluginDynamite() +{ + cUdevMonitor::ShutdownAllMonitors(); + cUdev::Free(); + if (cDynamicDevice::dvbprobe) + delete cDynamicDevice::dvbprobe; + if (probe) + delete probe; +} + +const char *cPluginDynamite::CommandLineHelp(void) +{ + return " --log-udev log all udev events to syslog (useful for diagnostics)\n"; +} + +bool cPluginDynamite::ProcessArgs(int argc, char *argv[]) +{ + for (int i = 0; i < argc; i++) { + if (strcmp(argv[i], "--log-udev") == 0) + cUdevMonitor::AddFilter(NULL, new cUdevLogFilter()); + } + return true; +} + +bool cPluginDynamite::Initialize(void) +{ + // create dynamic devices + while (cDevice::NumDevices() < MAXDEVICES) + new cDynamicDevice; + if (!cDynamicDevice::ProcessQueuedCommands()) + esyslog("dynamite: can't process all queued commands"); + return true; +} + +bool cPluginDynamite::Start(void) +{ + if (!cDynamicDevice::ProcessQueuedCommands()) + esyslog("dynamite: can't process all queued commands"); + return true; +} + +void cPluginDynamite::Stop(void) +{ + cDynamicDevice::DetachAllDevices(); +} + +void cPluginDynamite::Housekeeping(void) +{ + // Perform any cleanup or other regular tasks. +} + +void cPluginDynamite::MainThreadHook(void) +{ + // Perform actions in the context of the main program thread. + // WARNING: Use with great care - see PLUGINS.html! + if (!cDynamicDevice::ProcessQueuedCommands()) + esyslog("dynamite: can't process all queued commands"); +} + +cString cPluginDynamite::Active(void) +{ + // Return a message string if shutdown should be postponed + return NULL; +} + +time_t cPluginDynamite::WakeupTime(void) +{ + // Return custom wakeup time for shutdown script + return 0; +} + +cOsdObject *cPluginDynamite::MainMenuAction(void) +{ + // Perform the action when selected from the main VDR menu. + return NULL; +} + +cMenuSetupPage *cPluginDynamite::SetupMenu(void) +{ + // Return a setup menu in case the plugin supports one. + return NULL; +} + +bool cPluginDynamite::SetupParse(const char *Name, const char *Value) +{ + int replyCode; + if (strcasecmp(Name, "DefaultGetTSTimeout") == 0) + SVDRPCommand("SetDefaultGetTSTimeout", Value, replyCode); + else + return false; + return true; +} + +bool cPluginDynamite::Service(const char *Id, void *Data) +{ + if (strcmp(Id, "dynamite-AttachDevice-v0.1") == 0) { + if (Data != NULL) + cDynamicDeviceProbe::QueueDynamicDeviceCommand(ddpcAttach, (const char*)Data); + return true; + } + if (strcmp(Id, "dynamite-ScanDevices-v0.1") == 0) { + if (Data != NULL) + cDynamicDevice::AttachDevicePattern((const char*)Data); + return true; + } + if (strcmp(Id, "dynamite-DetachDevice-v0.1") == 0) { + if (Data != NULL) + cDynamicDeviceProbe::QueueDynamicDeviceCommand(ddpcDetach, (const char*)Data); + return true; + } + if (strcmp(Id, "dynamite-LockDevice-v0.1") == 0) { + if (Data != NULL) + cDynamicDevice::SetLockDevice((const char*)Data, true); + return true; + } + if (strcmp(Id, "dynamite-UnlockDevice-v0.1") == 0) { + if (Data != NULL) + cDynamicDevice::SetLockDevice((const char*)Data, false); + return true; + } + if (strcmp(Id, "dynamite-SetGetTSTimeout-v0.1") == 0) { + if (Data != NULL) { + int replyCode; + SVDRPCommand("SetGetTSTimeout", (const char*)Data, replyCode); + } + return true; + } + if (strcmp(Id, "dynamite-SetDefaultGetTSTimeout-v0.1") == 0) { + if (Data != NULL) { + int replyCode; + SVDRPCommand("SetDefaultGetTSTimeout", (const char*)Data, replyCode); + } + return true; + } + return false; +} + +const char **cPluginDynamite::SVDRPHelpPages(void) +{ + static const char *HelpPages[] = { + "ATTD devpath\n" + " Asks all cDynamicDeviceProbe-objects to attach the given devicepath\n" + " till one returns a valid pointer. You can control the order by the\n" + " order of the plugins you load\n" + " e.g. /dev/dvb/adapter0/frontend0\n" + " alternate command: AttachDevice", + "DETD devpath\n" + " Looks through its remembered devicepaths and deletes the attached\n" + " device if found. Case is important!\n" + " Any timeouts or locks set to this slot will be reset to its defaults\n" + " alternate command: DetachDevice", + "SCND '/dev/path/glob*/pattern*'\n" + " Scan filesystem with pattern and try to attach each found device\n" + " don't forget to enclose the pattern with single quotes\n" + " e.g. SCND '/dev/dvb/adapter*/frontend*'\n" + " alternate command: ScanDevices", + "LCKD /dev/path/to/device\n" + " alternate command: LockDevice", + " Lock the device so it can't be detached\n" + " alternate command: LockDevice", + "UNLD /dev/path/to/device\n" + " Remove the lock of the device so it can be detached\n" + " alternate command: UnlockDevice", + "LSTD\n" + " Lists all devices managed by this plugin. The first column is an id,\n" + " the second column is the devicepath passed with ATTD\n" + " The id can be used with the DETD and UNLD commands instead of the path.\n" + " An asterisk behind the id means that this device is locked and\n" + " can't be detached.\n" + " alternate command: ListDevices", + "SGTT /dev/path/to/device seconds\n" + " Sets the \"GetTSPacket\"-watchdog timeout to specified amount of seconds\n" + " If the device returns no data (Data == NULL) for this period of time,\n" + " the device will be detached. Usefull if you want to reload the driver etc.\n" + " A value of 0 seconds disables the watchdog.\n" + " alternate command: SetGetTSTimeout", + "SDGT seconds\n" + " Sets the \"GetTSPacket\"-watchdog timeout for all attached devices\n" + " and all devices that will be attached.\n" + " alternate command: SetDefaultGetTSTimeout", + NULL + }; + return HelpPages; +} + +cString cPluginDynamite::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode) +{ + if ((strcasecmp(Command, "ATTD") == 0) || (strcasecmp(Command, "AttachDevice") == 0)) { + cDynamicDeviceProbe::QueueDynamicDeviceCommand(ddpcAttach, Option); + return cString::sprintf("queued command for attaching %s", Option); + } + + if ((strcasecmp(Command, "DETD") == 0) || (strcasecmp(Command, "DetachDevice") == 0)) { + cDynamicDeviceProbe::QueueDynamicDeviceCommand(ddpcDetach, Option); + return cString::sprintf("queued command for detaching %s", Option); + } + + if ((strcasecmp(Command, "SCND") == 0) || (strcasecmp(Command, "ScanDevices") == 0)) + return cDynamicDevice::AttachDevicePattern(Option); + + if ((strcasecmp(Command, "LSTD") == 0) || (strcasecmp(Command, "ListDevices") == 0)) + return cDynamicDevice::ListAllDevices(ReplyCode); + + int lock = 0; + if ((strcasecmp(Command, "LCKD") == 0) || (strcasecmp(Command, "LockDevice") == 0)) + lock = 1; + else if ((strcasecmp(Command, "UNLD") == 0) || (strcasecmp(Command, "UnlockDevice") == 0)) + lock = 2; + if (lock > 0) { + switch (cDynamicDevice::SetLockDevice(Option, lock == 1)) { + case ddrcSuccess: + return cString::sprintf("%slocked device %s", (lock == 2) ? "un" : "", Option); + case ddrcNotFound: + { + ReplyCode = 550; + return cString::sprintf("device %s not found", Option); + } + default: + { + ReplyCode = 550; + return cString::sprintf("can't %slock device %s and I don't know why...", (lock == 2) ? "un" : "", Option); + } + } + } + + if ((strcasecmp(Command, "SGTT") == 0) || (strcasecmp(Command, "SetGetTSTimeout") == 0)) { + cString ret; + int len = strlen(Option); + if (len > 0) { + char *devPath = new char[len]; + int seconds = -1; + if ((sscanf(Option, "%s %d", devPath, &seconds) == 2) && (seconds >= 0)) { + cDynamicDevice::SetGetTSTimeout(devPath, seconds); + if (seconds > 0) + ret = cString::sprintf("set GetTS-Timeout on device %s to %d seconds", devPath, seconds); + else + ret = cString::sprintf("disable GetTS-Timeout on device %s", devPath); + } + delete [] devPath; + } + return ret; + } + + if ((strcasecmp(Command, "SDGT") == 0) || (strcasecmp(Command, "SetDefaultGetTSTimeout") == 0)) { + if (isnumber(Option)) { + int seconds = strtol(Option, NULL, 10); + cDynamicDevice::SetDefaultGetTSTimeout(seconds); + return cString::sprintf("set default GetTS-Timeout on all devices to %d seconds", seconds); + } + } + return NULL; +} + +VDRPLUGINCREATOR(cPluginDynamite); // Don't touch this! diff --git a/monitor.c b/monitor.c new file mode 100644 index 0000000..ff9392c --- /dev/null +++ b/monitor.c @@ -0,0 +1,220 @@ +#include "monitor.h" +#include "dynamicdevice.h" +#include <unistd.h> + +// --- cUdevMonitor ---------------------------------------------------------- + +cMutex cUdevMonitor::mutexMonitors; +int cUdevMonitor::numMonitors = 0; +cUdevMonitor *cUdevMonitor::monitors[MAXUDEVMONITORS] = { NULL }; + +cUdevMonitor *cUdevMonitor::Get(const char *Subsystem) +{ + cMutexLock lock(&mutexMonitors); + int i = 0; + while (i < numMonitors) { + if (monitors[i]) { + if ((Subsystem == NULL) && (*monitors[i]->subsystem == NULL)) + return monitors[i]; + if ((Subsystem != NULL) + && (*monitors[i]->subsystem != NULL) + && (strcmp(*monitors[i]->subsystem, Subsystem) == 0)) + return monitors[i]; + } + i++; + } + if (i < MAXUDEVMONITORS) { + numMonitors++; + cUdevMonitor *m = new cUdevMonitor(Subsystem); + m->index = i; + monitors[i] = m; + return m; + } + return NULL; +} + +bool cUdevMonitor::AddFilter(const char *Subsystem, cUdevFilter *Filter) +{ + if (Filter == NULL) + return false; + cUdevMonitor *m = Get(Subsystem); + if (m == NULL) { + delete Filter; + return false; + } + if (!m->AddFilter(Filter)) { + delete Filter; + return false; + } + return true; +} + +void cUdevMonitor::ShutdownAllMonitors(void) +{ + cMutexLock lock(&mutexMonitors); + for (int i = 0; i < numMonitors; i++) { + if (monitors[i]) { + delete monitors[i]; + monitors[i] = NULL; + } + } +} + +cUdevMonitor::cUdevMonitor(const char *Subsystem) +:monitor(NULL) +,index(-1) +,subsystem(Subsystem) +{ + struct udev *udev = cUdev::Init(); + if (udev) { + monitor = udev_monitor_new_from_netlink(udev, "udev"); + if (monitor && Subsystem) + udev_monitor_filter_add_match_subsystem_devtype(monitor, Subsystem, NULL); + } + SetDescription("dynamite udev monitor for subsystem %s", Subsystem); +} + +cUdevMonitor::~cUdevMonitor(void) +{ + if ((index >= 0) && (index < MAXUDEVMONITORS)) { + cMutexLock lock(&mutexMonitors); + if (monitors[index] == this) + monitors[index] = NULL; + } + Cancel(3); + filters.Clear(); + if (monitor) + udev_monitor_unref(monitor); + monitor = NULL; +} + +#define MONITOR_POLL_INTERVAL_MS 500 + +void cUdevMonitor::Action(void) +{ + if (monitor == NULL) + return; + udev_monitor_enable_receiving(monitor); + int fd = udev_monitor_get_fd(monitor); + if (fd == 0) + return; + fd_set fds; + struct timeval tv; + while (Running()) { + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = 0; + tv.tv_usec = MONITOR_POLL_INTERVAL_MS * 1000; + int ret = select(fd+1, &fds, NULL, NULL, &tv); + if (!Running()) + break; + if ((ret > 0) && FD_ISSET(fd, &fds)) { + udev_device *dev = udev_monitor_receive_device(monitor); + if (dev) { + cMutexLock lock(&filtersMutex); + cUdevDevice d(dev); + for (cUdevFilter *f = filters.First(); f; f = filters.Next(f)) + f->Process(d); + } + } + } + close(fd); +} + +bool cUdevMonitor::AddFilter(cUdevFilter *Filter) +{ + if (monitor == NULL) + return false; + if (Filter == NULL) + return false; + if (Filter->monitor == this) + return true; + if (Filter->monitor != NULL) + return false; + cMutexLock lock(&filtersMutex); + Filter->monitor = this; + filters.Add(Filter); + if (!Running()) + Start(); + return true; +} + +bool cUdevMonitor::DelFilter(cUdevFilter *Filter) +{ + if (monitor == NULL) + return false; + if (Filter == NULL) + return false; + if (Filter->monitor != this) + return false; + cMutexLock lock(&filtersMutex); + filters.Del(Filter); + Filter->monitor = NULL; + if (filters.Count() == 0) + Cancel(3); + return true; +} + +// --- cUdevFilter ----------------------------------------------------------- + +cUdevFilter::cUdevFilter(void) +:monitor(NULL) +{ +} + +cUdevFilter::~cUdevFilter(void) +{ +} + +// --- cUdevLogFilter -------------------------------------------------------- + +static void InternLogProcess(int Level, cUdevDevice &Device) +{ + char *indent = new char[Level + 2]; + indent[0] = '+'; + int i = 0; + while (i < Level) { + indent[i + 1] = '-'; + i++; + } + indent[i + 1] = '\0'; + const char *action = Device.GetAction(); + const char *subsystem = Device.GetPropertyValue("SUBSYSTEM"); + const char *syspath = Device.GetSyspath(); + const char *devpath = Device.GetPropertyValue("DEVPATH"); + const char *devname = Device.GetPropertyValue("DEVNAME"); + isyslog("dynamite: udev: %s action=%s, subsystem=%s, syspath=%s, devpath=%s, devname=%s", indent, action, subsystem, syspath, devpath, devname); + cUdevListEntry *devlink = Device.GetDevlinksList(); + while (devlink) { + isyslog("dynamite: udev: %s devlink: %s", indent, devlink->GetName()); + cUdevListEntry *tmp = devlink->GetNext(); + delete devlink; + devlink = tmp; + } + cUdevDevice *parent = Device.GetParent(); + if (parent != NULL) { + InternLogProcess(Level + 1, *parent); + delete parent; + } +} + +void cUdevLogFilter::Process(cUdevDevice &Device) +{ + InternLogProcess(0, Device); +} + +// --- cUdevDvbFilter -------------------------------------------------------- + +void cUdevDvbFilter::Process(cUdevDevice &Device) +{ + const char *action = Device.GetAction(); + if (action && (strcmp(action, "add") == 0)) { + const char *dvb_device_type = Device.GetPropertyValue("DVB_DEVICE_TYPE"); + if (!dvb_device_type || (strcmp(dvb_device_type, "frontend") != 0)) + return; + const char *devname = Device.GetPropertyValue("DEVNAME"); + if (!devname || cDynamicDevice::IsAttached(devname)) + return; + cDynamicDeviceProbe::QueueDynamicDeviceCommand(ddpcAttach, devname); + } +} diff --git a/monitor.h b/monitor.h new file mode 100644 index 0000000..659e504 --- /dev/null +++ b/monitor.h @@ -0,0 +1,58 @@ +#ifndef __DYNAMITEMONITOR_H +#define __DYNAMITEMONITOR_H + +#include <vdr/thread.h> +#include <vdr/tools.h> +#include "udev.h" + +#define MAXUDEVMONITORS 10 + +class cUdevFilter; + +class cUdevMonitor : public cThread { +private: + static cMutex mutexMonitors; + static int numMonitors; + static cUdevMonitor *monitors[MAXUDEVMONITORS]; + + struct udev_monitor *monitor; + int index; + cString subsystem; + cMutex filtersMutex; + cList<cUdevFilter> filters; + + cUdevMonitor(const char *Subsystem); +protected: + virtual void Action(void); +public: + static cUdevMonitor *Get(const char *Subsystem); + static bool AddFilter(const char *Subsystem, cUdevFilter *Filter); + static void ShutdownAllMonitors(void); + + virtual ~cUdevMonitor(void); + cString GetSubsystem() const { return subsystem; }; + bool AddFilter(cUdevFilter *Filter); + bool DelFilter(cUdevFilter *Filter); + }; + +class cUdevFilter : public cListObject { +friend class cUdevMonitor; +protected: + const cUdevMonitor *monitor; + virtual void Process(cUdevDevice &Device) = 0; +public: + cUdevFilter(void); + virtual ~cUdevFilter(void); + }; + +class cUdevLogFilter : public cUdevFilter { +protected: + virtual void Process(cUdevDevice &Device); + }; + +class cUdevDvbFilter : public cUdevFilter { +protected: + virtual void Process(cUdevDevice &Device); + }; + +#endif // __DYNAMITEMONITOR_H diff --git a/patches/sc-1.0.0pre-subdevice.patch b/patches/sc-1.0.0pre-subdevice.patch new file mode 100644 index 0000000..39ca3e5 --- /dev/null +++ b/patches/sc-1.0.0pre-subdevice.patch @@ -0,0 +1,215 @@ +diff --git a/device.c b/device.c +index fe10d5e..b29a993 100644 +--- a/device.c ++++ b/device.c +@@ -1334,6 +1334,17 @@ bool cScDeviceProbe::Probe(int Adapter, int Frontend) + // -- cScDevices --------------------------------------------------------------- + + int cScDevices::budget=0; ++int cScDevices::numScDevices = 0; ++cScDevice *cScDevices::scdevice[MAXDEVICES] = { NULL }; ++ ++cScDevice *cScDevices::GetScDevice(int CardIndex) ++{ ++ for (int n = 0; n < numScDevices; n++) { ++ if (scdevice[n] && (scdevice[n]->CardIndex() == CardIndex)) ++ return scdevice[n]; ++ } ++ return NULL; ++} + + void cScDevices::DvbName(const char *Name, int a, int f, char *buffer, int len) + { +@@ -1439,16 +1450,16 @@ void cScDevices::Startup(void) + { + if(ScSetup.ForceTransfer) + SetTransferModeForDolbyDigital(2); +- for(int n=cDevice::NumDevices(); --n>=0;) { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(n)); ++ for(int n=cScDevices::numScDevices; --n>=0;) { ++ cScDevice *dev=cScDevices::scdevice[n]; + if(dev) dev->LateInit(); + } + } + + void cScDevices::Shutdown(void) + { +- for(int n=cDevice::NumDevices(); --n>=0;) { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(n)); ++ for(int n=cScDevices::numScDevices; --n>=0;) { ++ cScDevice *dev=cScDevices::scdevice[n]; + if(dev) dev->EarlyShutdown(); + } + } +@@ -1490,19 +1501,36 @@ cScDevice::cScDevice(int Adapter, int Frontend, int cafd) + :cDvbDevice(Adapter) + #endif + { ++ lateInit = false; + #ifndef SASC + decsa=0; tsBuffer=0; cam=0; fullts=false; + ciadapter=0; hwciadapter=0; + fd_ca=cafd; fd_ca2=dup(fd_ca); fd_dvr=-1; + softcsa=(fd_ca<0); ++#ifdef __DYNAMIC_DEVICE_PROBE ++ if (parentDevice) ++ LateInit(); ++#endif + #else + softcsa=fullts=false; + cam=new cCam(this,Adapter); + #endif // !SASC ++ index = 0; ++ while ((index < cScDevices::numScDevices) && (index < MAXDEVICES) && cScDevices::scdevice[index]) ++ index++; ++ if (index < MAXDEVICES) { ++ cScDevices::scdevice[index] = this; ++ if (index == cScDevices::numScDevices) ++ cScDevices::numScDevices++; ++ } ++ else ++ esyslog("too many sc-devices!"); + } + + cScDevice::~cScDevice() + { ++ if ((index >= 0) && (index < MAXDEVICES) && (cScDevices::scdevice[index] == this)) ++ cScDevices::scdevice[index] = NULL; + #ifndef SASC + DetachAllReceivers(); + Cancel(3); +@@ -1528,6 +1556,8 @@ void cScDevice::EarlyShutdown(void) + + void cScDevice::LateInit(void) + { ++ if (lateInit) return; ++ lateInit = true; + int n=CardIndex(); + if(DeviceNumber()!=n) + PRINTF(L_GEN_ERROR,"CardIndex - DeviceNumber mismatch! Put SC plugin first on VDR commandline!"); +@@ -1538,10 +1568,16 @@ void cScDevice::LateInit(void) + PRINTF(L_GEN_INFO,"Budget mode forced on card %d",n); + softcsa=true; + } +- ++#ifdef __DYNAMIC_DEVICE_PROBE ++ cDevice *cidev = parentDevice ? parentDevice : this; ++ if(fd_ca2>=0) hwciadapter=cDvbCiAdapter::CreateCiAdapter(cidev,fd_ca2); ++ cam=new cCam(this,n); ++ ciadapter=new cScCiAdapter(cidev,n,cam); ++#else + if(fd_ca2>=0) hwciadapter=cDvbCiAdapter::CreateCiAdapter(this,fd_ca2); + cam=new cCam(this,n); + ciadapter=new cScCiAdapter(this,n,cam); ++#endif + if(softcsa) { + decsa=new cDeCSA(n); + if(IsPrimaryDevice() && HasDecoder()) { +diff --git a/device.h b/device.h +index 5ad83f9..454d6ea 100644 +--- a/device.h ++++ b/device.h +@@ -88,6 +88,8 @@ public: + + // ---------------------------------------------------------------- + ++class cScDevice; ++ + class cScDevices : public cDvbDevice { + private: + static int budget; +@@ -106,6 +108,10 @@ public: + static bool ForceBudget(int n); + static void DvbName(const char *Name, int a, int f, char *buffer, int len); + static int DvbOpen(const char *Name, int a, int f, int Mode, bool ReportError=false); ++ ++ static int numScDevices; ++ static cScDevice *scdevice[MAXDEVICES]; ++ static cScDevice *GetScDevice(int CardIndex); + }; + + // ---------------------------------------------------------------- +@@ -123,6 +129,8 @@ private: + bool softcsa, fullts; + cMutex cafdMutex; + cTimeMs lastDump; ++ int index; ++ bool lateInit; + // + #ifndef SASC + void LateInit(void); +diff --git a/sc.c b/sc.c +index 82960bf..9f01217 100644 +--- a/sc.c ++++ b/sc.c +@@ -1009,7 +1009,7 @@ void cSoftCAM::Shutdown(void) + + char *cSoftCAM::CurrKeyStr(int CardNum, int num) + { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(CardNum)); ++ cScDevice *dev=cScDevices::GetScDevice(CardNum); + char *str=0; + if(dev) { + if(dev->Cam()) str=dev->Cam()->CurrentKeyStr(num); +@@ -1020,8 +1020,8 @@ char *cSoftCAM::CurrKeyStr(int CardNum, int num) + + bool cSoftCAM::Active(bool log) + { +- for(int n=cDevice::NumDevices(); --n>=0;) { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(n)); ++ for(int n=cScDevices::numScDevices; --n>=0;) { ++ cScDevice *dev=cScDevices::scdevice[n]; + if(dev && dev->Cam() && dev->Cam()->Active(log)) return true; + } + return false; +@@ -1029,33 +1029,33 @@ bool cSoftCAM::Active(bool log) + + void cSoftCAM::SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on) + { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(CardNum)); ++ cScDevice *dev=cScDevices::GetScDevice(CardNum); + if(dev && dev->Cam()) dev->Cam()->LogEcmStatus(ecm,on); + } + + void cSoftCAM::AddHook(int CardNum, cLogHook *hook) + { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(CardNum)); ++ cScDevice *dev=cScDevices::GetScDevice(CardNum); + if(dev && dev->Cam()) dev->Cam()->AddHook(hook); + } + + bool cSoftCAM::TriggerHook(int CardNum, int id) + { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(CardNum)); ++ cScDevice *dev=cScDevices::GetScDevice(CardNum); + return dev && dev->Cam() && dev->Cam()->TriggerHook(id); + } + + void cSoftCAM::CaidsChanged(void) + { +- for(int n=cDevice::NumDevices(); --n>=0;) { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(n)); ++ for(int n=cScDevices::numScDevices; --n>=0;) { ++ cScDevice *dev=cScDevices::scdevice[n]; + if(dev) dev->CaidsChanged(); + } + } + + int cSoftCAM::FilterHandle(int CardNum) + { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(CardNum)); ++ cScDevice *dev=cScDevices::GetScDevice(CardNum); + return dev ? dev->FilterHandle() : -1; + } + +@@ -1086,8 +1086,8 @@ void cScHousekeeper::Action(void) + while(Running()) { + if(++c==20) { + c=0; +- for(int n=cDevice::NumDevices(); --n>=0;) { +- cScDevice *dev=dynamic_cast<cScDevice *>(cDevice::GetDevice(n)); ++ for(int n=cScDevices::numScDevices; --n>=0;) { ++ cScDevice *dev=cScDevices::scdevice[n]; + if(dev && dev->Cam()) dev->Cam()->HouseKeeping(); + } + } diff --git a/patches/vdr-1.7.16-dynamite-subdevice.patch b/patches/vdr-1.7.16-dynamite-subdevice.patch new file mode 100644 index 0000000..f0ee935 --- /dev/null +++ b/patches/vdr-1.7.16-dynamite-subdevice.patch @@ -0,0 +1,361 @@ +diff --git a/device.c b/device.c +index 681049b..aaee92f 100644 +--- a/device.c ++++ b/device.c +@@ -72,12 +72,18 @@ cDevice *cDevice::device[MAXDEVICES] = { NULL }; + cDevice *cDevice::primaryDevice = NULL; + cDevice *cDevice::avoidDevice = NULL; + cList<cDeviceHook> cDevice::deviceHooks; ++cDevice *cDevice::nextParentDevice = NULL; + +-cDevice::cDevice(void) ++cDevice::cDevice(cDevice *ParentDevice) + :patPmtParser(true) ++,parentDevice(ParentDevice) ++,subDevice(NULL) + { ++ if (!ParentDevice) ++ parentDevice = nextParentDevice; ++ cDevice::nextParentDevice = NULL; + cardIndex = nextCardIndex++; +- dsyslog("new device number %d", CardIndex() + 1); ++ dsyslog("new %sdevice number %d", parentDevice ? "sub-" : "", CardIndex() + 1); + + SetDescription("receiver on device %d", CardIndex() + 1); + +@@ -108,10 +114,14 @@ cDevice::cDevice(void) + for (int i = 0; i < MAXRECEIVERS; i++) + receiver[i] = NULL; + +- if (numDevices < MAXDEVICES) +- device[numDevices++] = this; ++ if (!parentDevice) { ++ if (numDevices < MAXDEVICES) ++ device[numDevices++] = this; ++ else ++ esyslog("ERROR: too many devices or \"dynamite\"-unpatched device creator!"); ++ } + else +- esyslog("ERROR: too many devices!"); ++ parentDevice->subDevice = this; + } + + cDevice::~cDevice() +@@ -120,6 +130,8 @@ cDevice::~cDevice() + DetachAllReceivers(); + delete liveSubtitle; + delete dvbSubtitleConverter; ++ if (parentDevice && (parentDevice->subDevice == this)) ++ parentDevice->subDevice = NULL; + } + + bool cDevice::WaitForAllDevicesReady(int Timeout) +@@ -158,6 +170,8 @@ int cDevice::NextCardIndex(int n) + + int cDevice::DeviceNumber(void) const + { ++ if (parentDevice) ++ return parentDevice->DeviceNumber(); + for (int i = 0; i < numDevices; i++) { + if (device[i] == this) + return i; +@@ -328,6 +342,10 @@ bool cDevice::HasCi(void) + + void cDevice::SetCamSlot(cCamSlot *CamSlot) + { ++ if (parentDevice) { ++ parentDevice->SetCamSlot(CamSlot); ++ return; ++ } + camSlot = CamSlot; + } + +@@ -531,6 +549,10 @@ bool cDevice::SetPid(cPidHandle *Handle, int Type, bool On) + + void cDevice::StartSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StartSectionHandler(); ++ return; ++ } + if (!sectionHandler) { + sectionHandler = new cSectionHandler(this); + AttachFilter(eitFilter = new cEitFilter); +@@ -542,6 +564,10 @@ void cDevice::StartSectionHandler(void) + + void cDevice::StopSectionHandler(void) + { ++ if (parentDevice) { ++ parentDevice->StopSectionHandler(); ++ return; ++ } + if (sectionHandler) { + delete nitFilter; + delete sdtFilter; +@@ -568,12 +594,20 @@ void cDevice::CloseFilter(int Handle) + + void cDevice::AttachFilter(cFilter *Filter) + { ++ if (parentDevice) { ++ parentDevice->AttachFilter(Filter); ++ return; ++ } + if (sectionHandler) + sectionHandler->Attach(Filter); + } + + void cDevice::Detach(cFilter *Filter) + { ++ if (parentDevice) { ++ parentDevice->Detach(Filter); ++ return; ++ } + if (sectionHandler) + sectionHandler->Detach(Filter); + } +@@ -1690,3 +1724,25 @@ uchar *cTSBuffer::Get(void) + } + return NULL; + } ++ ++// --- cDynamicDeviceProbe ------------------------------------------------------- ++ ++cList<cDynamicDeviceProbe> DynamicDeviceProbes; ++ ++cList<cDynamicDeviceProbe::cDynamicDeviceProbeItem> cDynamicDeviceProbe::commandQueue; ++ ++void cDynamicDeviceProbe::QueueDynamicDeviceCommand(eDynamicDeviceProbeCommand Cmd, const char *DevPath) ++{ ++ if (DevPath) ++ commandQueue.Add(new cDynamicDeviceProbeItem(Cmd, new cString(DevPath))); ++} ++ ++cDynamicDeviceProbe::cDynamicDeviceProbe(void) ++{ ++ DynamicDeviceProbes.Add(this); ++} ++ ++cDynamicDeviceProbe::~cDynamicDeviceProbe() ++{ ++ DynamicDeviceProbes.Del(this, false); ++} +diff --git a/device.h b/device.h +index cb3bc2c..0b6634b 100644 +--- a/device.h ++++ b/device.h +@@ -163,7 +163,6 @@ private: + static int nextCardIndex; + int cardIndex; + protected: +- cDevice(void); + virtual ~cDevice(); + virtual bool Ready(void); + ///< Returns true if this device is ready. Devices with conditional +@@ -191,8 +190,6 @@ protected: + ///< base class. + public: + bool IsPrimaryDevice(void) const { return this == primaryDevice; } +- int CardIndex(void) const { return cardIndex; } +- ///< Returns the card index of this device (0 ... MAXDEVICES - 1). + int DeviceNumber(void) const; + ///< Returns the number of this device (0 ... numDevices). + virtual bool HasDecoder(void) const; +@@ -365,9 +362,6 @@ public: + ///< Returns true if this device has a Common Interface. + void SetCamSlot(cCamSlot *CamSlot); + ///< Sets the given CamSlot to be used with this device. +- cCamSlot *CamSlot(void) const { return camSlot; } +- ///< Returns the CAM slot that is currently used with this device, +- ///< or NULL if no CAM slot is in use. + + // Image Grab facilities + +@@ -524,9 +518,6 @@ private: + cTsToPes tsToPesSubtitle; + bool isPlayingVideo; + protected: +- const cPatPmtParser *PatPmtParser(void) const { return &patPmtParser; } +- ///< Returns a pointer to the patPmtParser, so that a derived device +- ///< can use the stream information from it. + virtual bool CanReplay(void) const; + ///< Returns true if this device can currently start a replay session. + virtual bool SetPlayMode(ePlayMode PlayMode); +@@ -712,6 +703,30 @@ public: + ///< Detaches all receivers from this device for this pid. + void DetachAllReceivers(void); + ///< Detaches all receivers from this device. ++ ++// --- dynamite subdevice patch start --- ++ friend class cDynamicDevice; ++private: ++ static cDevice *nextParentDevice; ++ ///< Holds the parent device for the next subdevice ++ ///< so the dynamite-plugin can work with unpatched plugins ++protected: ++ cDevice *parentDevice; ++ cDevice *subDevice; ++ cDevice(cDevice *ParentDevice = NULL); ++ const cPatPmtParser *PatPmtParser(void) const { if (parentDevice) return parentDevice->PatPmtParser(); return &patPmtParser; } ++ ///< Returns a pointer to the patPmtParser, so that a derived device ++ ///< can use the stream information from it. ++public: ++ int CardIndex(void) const { if (parentDevice) return parentDevice->cardIndex; return cardIndex; } ++ ///< Returns the card index of this device (0 ... MAXDEVICES - 1). ++ cCamSlot *CamSlot(void) const { if (parentDevice) return parentDevice->CamSlot(); return camSlot; } ++ ///< Returns the CAM slot that is currently used with this device, ++ ///< or NULL if no CAM slot is in use. ++ bool IsSubDevice(void) const { return (parentDevice != NULL); } ++ bool HasSubDevice(void) const { return (subDevice != NULL); } ++ cDevice *SubDevice(void) const { return subDevice; } ++ // --- dynamite subdevice patch end --- + }; + + /// Derived cDevice classes that can receive channels will have to provide +@@ -735,4 +750,47 @@ public: + uchar *Get(void); + }; + ++/// A plugin that want to create devices handled by the dynamite-plugin needs to create ++/// a cDynamicDeviceProbe derived object on the heap in order to have its Probe() ++/// function called, where it can actually create the appropriate device. ++/// The cDynamicDeviceProbe object must be created in the plugin's constructor, ++/// and deleted in its destructor. ++/// The "DevPath" hasn't to be a physical device or a path in the filesystem. ++/// It can be any string a plugin may react on. ++ ++#define __DYNAMIC_DEVICE_PROBE ++ ++enum eDynamicDeviceProbeCommand { ddpcAttach, ddpcDetach }; ++ ++class cDynamicDeviceProbe : public cListObject { ++ friend class cDynamicDevice; ++private: ++ class cDynamicDeviceProbeItem : public cListObject { ++ public: ++ eDynamicDeviceProbeCommand cmd; ++ cString *devpath; ++ cDynamicDeviceProbeItem(eDynamicDeviceProbeCommand Cmd, cString *DevPath):cmd(Cmd),devpath(DevPath) {} ++ virtual ~cDynamicDeviceProbeItem() { if (devpath) delete devpath; } ++ }; ++ static cList<cDynamicDeviceProbeItem> commandQueue; ++ ///< A list where all attach/detach commands are queued ++ ///< so they can be processed in the MainThreadHook of ++ ///< the dynamite plugin. ++public: ++ static void QueueDynamicDeviceCommand(eDynamicDeviceProbeCommand Cmd, const char *DevPath); ++ ///< Plugins which support cDynamicDeviceProbe must use this function ++ ///< to queue the devices they normally create in their Initialize method. ++ ///< These devices are created as subdevices in the Start-method of the dynamite-plugin. ++ cDynamicDeviceProbe(void); ++ virtual ~cDynamicDeviceProbe(); ++ virtual cDevice *Attach(cDevice *ParentDevice, const char *DevPath) = 0; ++ ///< Probes for a device at the given device-path like /dev/dvb/adapter0/frontend0 ++ ///< or /dev/video0 etc. and creates the appropriate ++ ///< object derived from cDevice if applicable. ++ ///< Returns the device that has been created or NULL if not. ++ ///< The dynamite-plugin will delete the device if it is detached. ++ }; ++ ++extern cList<cDynamicDeviceProbe> DynamicDeviceProbes; ++ + #endif //__DEVICE_H +diff --git a/dvbci.c b/dvbci.c +index 5289bbd..ea54bdb 100644 +--- a/dvbci.c ++++ b/dvbci.c +@@ -41,6 +41,8 @@ cDvbCiAdapter::cDvbCiAdapter(cDevice *Device, int Fd) + cDvbCiAdapter::~cDvbCiAdapter() + { + Cancel(3); ++ if (device->IsSubDevice() || device->HasSubDevice()) ++ close(fd); + } + + int cDvbCiAdapter::Read(uint8_t *Buffer, int MaxLength) +diff --git a/dvbdevice.c b/dvbdevice.c +index f32b350..df2e679 100644 +--- a/dvbdevice.c ++++ b/dvbdevice.c +@@ -259,6 +259,7 @@ private: + int device; + int fd_frontend; + int adapter, frontend; ++ cDvbDevice *dvbdevice; + int tuneTimeout; + int lockTimeout; + time_t lastTimeoutReport; +@@ -273,7 +274,7 @@ private: + bool SetFrontend(void); + virtual void Action(void); + public: +- cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType); ++ cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType, cDvbDevice *Dvbdevice); + virtual ~cDvbTuner(); + const cChannel *GetTransponder(void) const { return &channel; } + bool IsTunedTo(const cChannel *Channel) const; +@@ -281,13 +282,14 @@ public: + bool Locked(int TimeoutMs = 0); + }; + +-cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType) ++cDvbTuner::cDvbTuner(int Device, int Fd_Frontend, int Adapter, int Frontend, fe_delivery_system FrontendType, cDvbDevice *Dvbdevice) + { + device = Device; + fd_frontend = Fd_Frontend; + adapter = Adapter; + frontend = Frontend; + frontendType = FrontendType; ++ dvbdevice = Dvbdevice; + tuneTimeout = 0; + lockTimeout = 0; + lastTimeoutReport = 0; +@@ -305,6 +307,8 @@ cDvbTuner::~cDvbTuner() + newSet.Broadcast(); + locked.Broadcast(); + Cancel(3); ++ if (dvbdevice && dvbdevice->IsSubDevice()) ++ close(fd_frontend); + } + + bool cDvbTuner::IsTunedTo(const cChannel *Channel) const +@@ -661,7 +665,8 @@ const char *DeliverySystems[] = { + NULL + }; + +-cDvbDevice::cDvbDevice(int Adapter, int Frontend) ++cDvbDevice::cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice) ++:cDevice(ParentDevice) + { + adapter = Adapter; + frontend = Frontend; +@@ -678,7 +683,7 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend) + + fd_ca = DvbOpen(DEV_DVB_CA, adapter, frontend, O_RDWR); + if (fd_ca >= 0) +- ciAdapter = cDvbCiAdapter::CreateCiAdapter(this, fd_ca); ++ ciAdapter = cDvbCiAdapter::CreateCiAdapter(parentDevice ? parentDevice : this, fd_ca); + + // The DVR device (will be opened and closed as needed): + +@@ -718,7 +723,7 @@ cDvbDevice::cDvbDevice(int Adapter, int Frontend) + else + p = (char *)"unknown modulations"; + isyslog("frontend %d/%d provides %s with %s (\"%s\")", adapter, frontend, DeliverySystems[frontendType], p, frontendInfo.name); +- dvbTuner = new cDvbTuner(CardIndex() + 1, fd_frontend, adapter, frontend, frontendType); ++ dvbTuner = new cDvbTuner(CardIndex() + 1, fd_frontend, adapter, frontend, frontendType, this); + } + } + else +diff --git a/dvbdevice.h b/dvbdevice.h +index ff606fd..0ac3a24 100644 +--- a/dvbdevice.h ++++ b/dvbdevice.h +@@ -123,7 +123,7 @@ private: + fe_delivery_system frontendType; + int fd_dvr, fd_ca; + public: +- cDvbDevice(int Adapter, int Frontend); ++ cDvbDevice(int Adapter, int Frontend, cDevice *ParentDevice = NULL); + virtual ~cDvbDevice(); + virtual bool Ready(void); + @@ -0,0 +1,110 @@ +#include "udev.h" +#include <linux/stddef.h> + +// --- cUdevListEntry -------------------------------------------------------- + +cUdevListEntry::cUdevListEntry(struct udev_list_entry *ListEntry) +:listEntry(ListEntry) +{ +} + +cUdevListEntry::~cUdevListEntry(void) +{ +} + +cUdevListEntry *cUdevListEntry::GetNext(void) const +{ + if (listEntry == NULL) + return NULL; + struct udev_list_entry *next = udev_list_entry_get_next(listEntry); + if (next == NULL) + return NULL; + return new cUdevListEntry(next); +} + +const char *cUdevListEntry::GetName(void) const +{ + if (listEntry == NULL) + return NULL; + return udev_list_entry_get_name(listEntry); +} + +const char *cUdevListEntry::GetValue(void) const +{ + if (listEntry == NULL) + return NULL; + return udev_list_entry_get_value(listEntry); +} + +// --- cUdevDevice ----------------------------------------------------------- + +cUdevDevice::cUdevDevice(udev_device *Device, bool DoUnref) +:device(Device) +,doUnref(DoUnref) +{ +} + +cUdevDevice::~cUdevDevice(void) +{ + if (doUnref && device) + udev_device_unref(device); +} + +const char *cUdevDevice::GetAction(void) const +{ + if (device == NULL) + return NULL; + return udev_device_get_action(device); +} + +cUdevListEntry *cUdevDevice::GetDevlinksList(void) const +{ + if (device == NULL) + return NULL; + struct udev_list_entry *listEntry = udev_device_get_devlinks_list_entry(device); + if (listEntry == NULL) + return NULL; + return new cUdevListEntry(listEntry); +} + +cUdevDevice *cUdevDevice::GetParent(void) const +{ + if (device == NULL) + return NULL; + struct udev_device *parent = udev_device_get_parent(device); + if (parent == NULL) + return NULL; + return new cUdevDevice(parent, false); +} + +const char *cUdevDevice::GetPropertyValue(const char *Key) const +{ + if (device == NULL) + return false; + return udev_device_get_property_value(device, Key); +} + +const char *cUdevDevice::GetSyspath(void) const +{ + if (device == NULL) + return false; + return udev_device_get_syspath(device); +} + +// --- cUdev ----------------------------------------------------------------- + +struct udev *cUdev::udev = NULL; + +struct udev *cUdev::Init(void) +{ + if (udev == NULL) + udev = udev_new(); + return udev; +} + +void cUdev::Free(void) +{ + if (udev) + udev_unref(udev); + udev = NULL; +} @@ -0,0 +1,41 @@ +#ifndef __DYNAMITEUDEV_H +#define __DYNAMITEUDEV_H + +#include <libudev.h> + +class cUdevListEntry { +private: + struct udev_list_entry *listEntry; +public: + cUdevListEntry(struct udev_list_entry *ListEntry); + virtual ~cUdevListEntry(void); + + cUdevListEntry *GetNext(void) const; + const char *GetName(void) const; + const char *GetValue(void) const; + }; + +class cUdevDevice { +private: + struct udev_device *device; + bool doUnref; +public: + cUdevDevice(udev_device *Device, bool DoUnref = true); + virtual ~cUdevDevice(void); + + const char *GetAction(void) const; + cUdevListEntry *GetDevlinksList(void) const; + cUdevDevice *GetParent(void) const; + const char *GetPropertyValue(const char *Key) const; + const char *GetSyspath(void) const; + }; + +class cUdev { +private: + static struct udev *udev; +public: + static struct udev *Init(void); + static void Free(void); + }; + +#endif // __DYNAMITEUDEV_H |