Index: lirc-modules-source.conf =================================================================== --- lirc-modules-source.conf (revision 1) +++ lirc-modules-source.conf (working copy) @@ -2,7 +2,7 @@ # Comma separated list of lirc kernel drivers to build #gpio and parallel don't build -LIRC_MODULES="atiusb, bt829, i2c, igorplugusb, imon, it87, ite8709, mceusb, sasem, serial, sir, streamzap, ttusbir, ene0100, wpc8769l" +LIRC_MODULES="atiusb, bt829, i2c, igorplugusb, imon, it87, ite8709, mceusb, sasem, serial, sir, streamzap, ttusbir, ene0100, wpc8769l, zilog" # It87 module configuration LIRC_IT87_CFLAGS="" Index: dkms.conf =================================================================== --- dkms.conf (revision 1) +++ dkms.conf (working copy) @@ -67,3 +67,8 @@ BUILT_MODULE_NAME[15]="lirc_wpc8769l" BUILT_MODULE_LOCATION[15]="modules" DEST_MODULE_LOCATION[15]="/updates/lirc" + +BUILT_MODULE_NAME[16]="lirc_zilog" +BUILT_MODULE_LOCATION[16]="modules" +DEST_MODULE_LOCATION[16]="/updates/lirc" + Index: Makefile =================================================================== --- Makefile (revision 1) +++ Makefile (working copy) @@ -28,7 +28,7 @@ export KERNEL_LOCATION CC -DRIVERS=atiusb bt829 gpio i2c igorplugusb imon it87 ite8709 mceusb parallel sasem serial sir streamzap ttusbir ene0100 +DRIVERS=atiusb bt829 gpio i2c igorplugusb imon it87 ite8709 mceusb parallel sasem serial sir streamzap ttusbir ene0100 zilog all: $(DRIVERS) test: @@ -124,6 +124,11 @@ cp drivers/lirc_mceusb/lirc_mceusb.$(KEXT) modules @echo $(KVERS) $(KSRC) > modules/lirc_mceusb.$(KEXT).KVERS +zilog: modules sanity-check dev + $(MAKE) -C drivers SUBDIRS="lirc_zilog" + cp drivers/lirc_zilog/lirc_zilog.$(KEXT) modules + @echo $(KVERS) $(KSRC) > modules/lirc_zilog.$(KEXT).KVERS + parallel: DEFS += $(PARALLEL_CFLAGS) parallel: modules sanity-check dev $(MAKE) -C drivers SUBDIRS="lirc_parallel" DEFS="$(DEFS)" Index: drivers/lirc_zilog/.deps/lirc_zilog.Po =================================================================== --- drivers/lirc_zilog/.deps/lirc_zilog.Po (revision 0) +++ drivers/lirc_zilog/.deps/lirc_zilog.Po (revision 0) @@ -0,0 +1 @@ +# dummy Index: drivers/lirc_zilog/Makefile =================================================================== --- drivers/lirc_zilog/Makefile (revision 0) +++ drivers/lirc_zilog/Makefile (revision 0) @@ -0,0 +1,415 @@ +# Makefile.in generated automatically by automake 1.5 from Makefile.am. + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + + + +# $Id: Makefile.common,v 5.10 2008/09/27 16:08:39 lirc Exp $ + +# where the kernel sources are located + +SHELL = /bin/bash + +srcdir = . +top_srcdir = ../.. + +prefix = /usr +exec_prefix = ${prefix} + +bindir = ${exec_prefix}/bin +sbindir = ${exec_prefix}/sbin +libexecdir = ${exec_prefix}/libexec +datadir = ${prefix}/share +sysconfdir = /etc/lirc/ +sharedstatedir = ${prefix}/com +localstatedir = /var +libdir = ${prefix}/lib +infodir = ${prefix}/share/info +mandir = ${prefix}/share/man +includedir = ${prefix}/include +oldincludedir = /usr/include +pkgdatadir = $(datadir)/lirc +pkglibdir = $(libdir)/lirc +pkgincludedir = $(includedir)/lirc +top_builddir = ../.. + +ACLOCAL = ${SHELL} /build/buildd/lirc-0.8.6/missing --run aclocal +AUTOCONF = ${SHELL} /build/buildd/lirc-0.8.6/missing --run autoconf +AUTOMAKE = ${SHELL} /build/buildd/lirc-0.8.6/missing --run automake +AUTOHEADER = ${SHELL} /build/buildd/lirc-0.8.6/missing --run autoheader + +INSTALL = /usr/bin/install -c +INSTALL_PROGRAM = ${INSTALL} +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_SCRIPT = ${INSTALL} +INSTALL_HEADER = $(INSTALL_DATA) +transform = s,x,x, +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = +host_triplet = i486-pc-linux-gnu +AMTAR = ${SHELL} /build/buildd/lirc-0.8.6/missing --run tar +AR = ar +AS = @AS@ +AWK = mawk +CXX = g++ +CXXCPP = g++ -E +DEPDIR = .deps +DLLTOOL = @DLLTOOL@ +ECHO = echo +EGREP = /bin/grep -E +EXEEXT = +F77 = +GCJ = @GCJ@ +GCJFLAGS = @GCJFLAGS@ +INSTALL_STRIP_PROGRAM = ${SHELL} $(install_sh) -c -s +LIBTOOL = $(SHELL) $(top_builddir)/libtool +LIBUSB_CONFIG = /usr/bin/libusb-config +LN_S = ln -s +OBJDUMP = @OBJDUMP@ +OBJEXT = o +PACKAGE = lirc +PYTHON = /usr/bin/python +PYTHON_EXEC_PREFIX = ${exec_prefix} +PYTHON_PLATFORM = linux2 +PYTHON_PREFIX = ${prefix} +PYTHON_VERSION = 2.6 +RANLIB = ranlib +RC = @RC@ +STRIP = strip +VERSION = 0.8.6 +X_CFLAGS = +X_EXTRA_LIBS = +X_LIBS = +X_PRE_LIBS = -lSM -lICE +ac_pkss_mktemp = yes +am__include = include +am__quote = +daemon = +depmod = /sbin/depmod +devdir = /dev +driver = userspace +forkpty = -lutil +hw_module = hw_accent.o hw_alsa_usb.o hw_atilibusb.o hw_audio_alsa.o hw_awlibusb.o hw_bte.o hw_commandir.o hw_creative.o hw_creative_infracd.o hw_default.o hw_devinput.o hw_dsp.o hw_ea65.o hw_ftdi.o hw_hiddev.o hw_i2cuser.o hw_irlink.o hw_irman.o hw_livedrive_common.o hw_livedrive_midi.o hw_livedrive_seq.o hw_logitech.o hw_mouseremote.o hw_mp3anywhere.o hw_mplay.o hw_pcmak.o hw_pinsys.o hw_pixelview.o hw_silitek.o hw_tira.o hw_udp.o hw_uirt2.o hw_uirt2_common.o hw_uirt2_raw.o hw_usbx.o receive.o serial.o transmit.o +hw_module_libs = -lasound -L/usr/lib -lusb -L/usr/lib -lusb -L/usr/lib -lusb -lftdi -lirman +install_sh = /build/buildd/lirc-0.8.6/install-sh +irtty = +kerneldir = missing +kernelext = ko +lirc_driver = +lirc_major = 61 +lircd_conf = +lircmd_conf = +maintmode_daemons_extra = +mkfifo = /usr/bin/mkfifo +mknod = /bin/mknod +moduledir = /lib/modules/2.6.24-23-server/misc +pkgpyexecdir = ${pyexecdir}/lirc +pkgpythondir = ${pythondir}/lirc +pyexecdir = ${exec_prefix}/lib/python2.6/site-packages +pythondir = ${prefix}/lib/python2.6/site-packages +receive = +varrundir = /var/run +vga_progs = +x_progs = irxevent xmode2 + +EXTRA_PROGRAMS = automake_dummy +automake_dummy_SOURCES = lirc_zilog.c + +module_DATA = lirc_zilog.o + + +LIRC_DEVDIR = $(PWD) + +# some magic for using linux kernel settings +# when compiling module(s) +KBUILD_VERBOSE = 1 +LIRC_EXTRA_CFLAGS = -DIRCTL_DEV_MAJOR=$(lirc_major) -DEXPORT_SYMTAB $(DEFS) \ + $(DEFAULT_INCLUDES) -I$(LIRC_DEVDIR)/$(srcdir) -I$(LIRC_DEVDIR)/$(builddir) \ + -I$(LIRC_DEVDIR)/$(top_srcdir) -I$(LIRC_DEVDIR)/$(top_builddir) \ + -I$(KERNEL_LOCATION)/include/ \ + -I$(KERNEL_LOCATION)/drivers/media/video/ + + +CLEANFILES = $(module_DATA) .$(module_DATA).flags $(module_DATA:.o=.mod.c) $(module_DATA:.o=.ko) *~ +subdir = drivers/lirc_zilog +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +EXTRA_PROGRAMS = automake_dummy$(EXEEXT) +am_automake_dummy_OBJECTS = lirc_zilog.$(OBJEXT) +automake_dummy_OBJECTS = $(am_automake_dummy_OBJECTS) +automake_dummy_LDADD = $(LDADD) +automake_dummy_DEPENDENCIES = +automake_dummy_LDFLAGS = + +DEFS = -DHAVE_CONFIG_H +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = +LDFLAGS = -Wl,-Bsymbolic-functions +LIBS = +depcomp = $(SHELL) $(top_srcdir)/depcomp +DEP_FILES = $(DEPDIR)/lirc_zilog.Po +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = -g -O2 +DIST_SOURCES = $(automake_dummy_SOURCES) +DATA = $(module_DATA) + +DIST_COMMON = Makefile.am Makefile.in +SOURCES = $(automake_dummy_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +$(srcdir)/Makefile.in: Makefile.am $(srcdir)/../Makefile.common $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu drivers/lirc_zilog/Makefile +Makefile: + cd $(top_builddir) && \ + CONFIG_HEADERS= CONFIG_LINKS= \ + CONFIG_FILES=$(subdir)/$@ $(SHELL) ./config.status +automake_dummy$(EXEEXT): $(automake_dummy_OBJECTS) $(automake_dummy_DEPENDENCIES) + @rm -f automake_dummy$(EXEEXT) + $(LINK) $(automake_dummy_LDFLAGS) $(automake_dummy_OBJECTS) $(automake_dummy_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +include $(DEPDIR)/lirc_zilog.Po + +distclean-depend: + -rm -rf $(DEPDIR) + +.c.o: + source='$<' object='$@' libtool=no \ + depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' \ + $(CCDEPMODE) $(depcomp) \ + $(COMPILE) -c `test -f $< || echo '$(srcdir)/'`$< + +.c.obj: + source='$<' object='$@' libtool=no \ + depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' \ + $(CCDEPMODE) $(depcomp) \ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: + source='$<' object='$@' libtool=yes \ + depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' \ + $(CCDEPMODE) $(depcomp) \ + $(LTCOMPILE) -c -o $@ `test -f $< || echo '$(srcdir)/'`$< +CCDEPMODE = depmode=gcc3 +uninstall-info-am: + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || etags $(ETAGS_ARGS) $$tags $$unique $(LISP) + +GTAGS: + here=`CDPATH=: && cd $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH + +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + if test -f $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + $(mkinstalldirs) "$(distdir)/$$dir"; \ + fi; \ + if test -d $$d/$$file; then \ + cp -pR $$d/$$file $(distdir) \ + || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(DATA) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(moduledir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) stamp-h stamp-h[0-9]* + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: install-data-local install-moduleDATA + +install-exec-am: install-exec-local + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-info-am uninstall-local uninstall-moduleDATA + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libtool distclean distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags distdir dvi \ + dvi-am info info-am install install-am install-data \ + install-data-am install-data-local install-exec install-exec-am \ + install-exec-local install-info install-info-am install-man \ + install-moduleDATA install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool tags uninstall uninstall-am \ + uninstall-info-am uninstall-local uninstall-moduleDATA + +export LIRC_EXTRA_CFLAGS KERNEL_LOCATION module_DATA + +$(module_DATA): $(automake_dummy_SOURCES) $(top_builddir)/config.h ../lirc.h + @if test "$(srcdir)" != "$(builddir)" ; then \ + for f in $(automake_dummy_SOURCES) ; do \ + [ -e $$f ] || ln -s $(srcdir)/$$f $$f || exit $$? ; \ + done \ + fi + -cp $(srcdir)/../lirc_dev/Module*.symvers . + mv Makefile Makefile.automake + cp $(srcdir)/../Makefile.kernel Makefile + CPPFLAGS="" CFLAGS="" LDFLAGS="" \ + $(MAKE) -C $(KERNEL_LOCATION) SUBDIRS=$(LIRC_DEVDIR) modules \ + KBUILD_VERBOSE=$(KBUILD_VERBOSE) + mv Makefile.automake Makefile + +install-moduleDATA: $(module_DATA) + $(mkinstalldirs) $(DESTDIR)$(moduledir) + @list='$(module_DATA:.o=.ko)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(moduledir)/$$f"; \ + $(INSTALL_DATA) $$d$$p $(DESTDIR)$(moduledir)/$$f; \ + done + +uninstall-moduleDATA: + @list='$(module_DATA:.o=.ko)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(moduledir)/$$f"; \ + rm -f $(DESTDIR)$(moduledir)/$$f; \ + done + +#install-exec-local: mkdev +#uninstall-local: rmdev + +mkdev: + test -e $(DESTDIR)$(devdir)/lirc || ($(mkinstalldirs) $(DESTDIR)$(devdir) && /bin/mknod $(DESTDIR)$(devdir)/lirc c 61 0) + +rmdev: + -test -c $(DESTDIR)$(devdir)/lirc && $(RM) $(DESTDIR)$(devdir)/lirc + +#install-data-local: install-moduleDATA +# -/sbin/depmod -a +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: Index: drivers/lirc_zilog/lirc_zilog.c =================================================================== --- drivers/lirc_zilog/lirc_zilog.c (revision 0) +++ drivers/lirc_zilog/lirc_zilog.c (revision 0) @@ -0,0 +1,1392 @@ +/* + * i2c IR lirc driver for devices with zilog IR processors + * + * Copyright (c) 2000 Gerd Knorr + * modified for PixelView (BT878P+W/FM) by + * Michal Kochanowicz + * Christoph Bartelmus + * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by + * Ulrich Mueller + * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by + * Stefan Jahn + * modified for inclusion into kernel sources by + * Jerome Brock + * modified for Leadtek Winfast PVR2000 by + * Thomas Reitmayr (treitmayr@yahoo.com) + * modified for Hauppauge PVR-150 IR TX device by + * Mark Weaver + * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150 + * Jarod Wilson + * + * parts are cut&pasted from the lirc_i2c.c driver + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "drivers/lirc.h" +#include "drivers/kcompat.h" +#include "drivers/lirc_dev/lirc_dev.h" + +struct IR { + struct lirc_driver l; + + /* Device info */ + struct mutex ir_lock; + int open; + + /* RX device */ + struct i2c_client c_rx; + int have_rx; + + /* RX device buffer & lock */ + struct lirc_buffer buf; + struct mutex buf_lock; + + /* RX polling thread data */ + struct completion *t_notify; + struct completion *t_notify2; + int shutdown; + struct task_struct *task; + + /* RX read data */ + unsigned char b[3]; + + /* TX device */ + struct i2c_client c_tx; + int need_boot; + int have_tx; +}; + +/* Minor -> data mapping */ +static struct IR *ir_devices[MAX_IRCTL_DEVICES]; + +/* Block size for IR transmitter */ +#define TX_BLOCK_SIZE 99 + +/* Hauppauge IR transmitter data */ +struct tx_data_struct { + /* Boot block */ + unsigned char *boot_data; + + /* Start of binary data block */ + unsigned char *datap; + + /* End of binary data block */ + unsigned char *endp; + + /* Number of installed codesets */ + unsigned int num_code_sets; + + /* Pointers to codesets */ + unsigned char **code_sets; + + /* Global fixed data template */ + int fixed[TX_BLOCK_SIZE]; +}; + +static struct tx_data_struct *tx_data; +static struct mutex tx_data_lock; + +#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \ + ## args) +#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) + +#define ZILOG_HAUPPAUGE_IR_RX_NAME "Zilog/Hauppauge IR RX" +#define ZILOG_HAUPPAUGE_IR_TX_NAME "Zilog/Hauppauge IR TX" + +/* module parameters */ +static int debug; /* debug output */ +static int disable_rx; /* disable RX device */ +static int disable_tx; /* disable TX device */ +static int minor = -1; /* minor number */ + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \ + ## args); \ + } while (0) + +static int add_to_buf(struct IR *ir) +{ + __u16 code; + unsigned char codes[2]; + unsigned char keybuf[6]; + int got_data = 0; + int ret; + int failures = 0; + unsigned char sendbuf[1] = { 0 }; + + if (lirc_buffer_full(&ir->buf)) { + dprintk("buffer overflow\n"); + return -EOVERFLOW; + } + + /* + * service the device as long as it is returning + * data and we have space + */ + do { + /* + * Lock i2c bus for the duration. RX/TX chips interfere so + * this is worth it + */ + mutex_lock(&ir->ir_lock); + + /* + * Send random "poll command" (?) Windows driver does this + * and it is a good point to detect chip failure. + */ + ret = i2c_master_send(&ir->c_rx, sendbuf, 1); + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + if (failures >= 3) { + mutex_unlock(&ir->ir_lock); + zilog_error("unable to read from the IR chip " + "after 3 resets, giving up\n"); + return ret; + } + + /* Looks like the chip crashed, reset it */ + zilog_error("polling the IR receiver chip failed, " + "trying reset\n"); + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ + 999) / 1000); + ir->need_boot = 1; + + ++failures; + mutex_unlock(&ir->ir_lock); + continue; + } + + ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf)); + mutex_unlock(&ir->ir_lock); + if (ret != sizeof(keybuf)) { + zilog_error("i2c_master_recv failed with %d -- " + "keeping last read buffer\n", ret); + } else { + ir->b[0] = keybuf[3]; + ir->b[1] = keybuf[4]; + ir->b[2] = keybuf[5]; + dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]); + } + + /* key pressed ? */ +#ifdef I2C_HW_B_HDPVR + if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) { + if (got_data && (keybuf[0] == 0x80)) + return 0; + else if (got_data && (keybuf[0] == 0x00)) + return -ENODATA; + } else if ((ir->b[0] & 0x80) == 0) +#else + if ((ir->b[0] & 0x80) == 0) +#endif + return got_data ? 0 : -ENODATA; + + /* look what we have */ + code = (((__u16)ir->b[0] & 0x7f) << 6) | (ir->b[1] >> 2); + + codes[0] = (code >> 8) & 0xff; + codes[1] = code & 0xff; + + /* return it */ + lirc_buffer_write(&ir->buf, codes); + ++got_data; + } while (!lirc_buffer_full(&ir->buf)); + + return 0; +} + +/* + * Main function of the polling thread -- from lirc_dev. + * We don't fit the LIRC model at all anymore. This is horrible, but + * basically we have a single RX/TX device with a nasty failure mode + * that needs to be accounted for across the pair. lirc lets us provide + * fops, but prevents us from using the internal polling, etc. if we do + * so. Hence the replication. Might be neater to extend the LIRC model + * to account for this but I'd think it's a very special case of seriously + * messed up hardware. + */ +static int lirc_thread(void *arg) +{ + struct IR *ir = arg; + + if (ir->t_notify != NULL) + complete(ir->t_notify); + + dprintk("poll thread started\n"); + + do { + if (ir->open) { + set_current_state(TASK_INTERRUPTIBLE); + + /* + * This is ~113*2 + 24 + jitter (2*repeat gap + + * code length). We use this interval as the chip + * resets every time you poll it (bad!). This is + * therefore just sufficient to catch all of the + * button presses. It makes the remote much more + * responsive. You can see the difference by + * running irw and holding down a button. With + * 100ms, the old polling interval, you'll notice + * breaks in the repeat sequence corresponding to + * lost keypresses. + */ + schedule_timeout((260 * HZ) / 1000); + if (ir->shutdown) + break; + if (!add_to_buf(ir)) + wake_up_interruptible(&ir->buf.wait_poll); + } else { + /* if device not opened so we can sleep half a second */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/2); + } + } while (!ir->shutdown); + + if (ir->t_notify2 != NULL) + wait_for_completion(ir->t_notify2); + + ir->task = NULL; + if (ir->t_notify != NULL) + complete(ir->t_notify); + + dprintk("poll thread ended\n"); + return 0; +} + +static int set_use_inc(void *data) +{ + struct IR *ir = data; + + if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0) + return -ENODEV; + + /* lock bttv in memory while /dev/lirc is in use */ + /* + * this is completely broken code. lirc_unregister_driver() + * must be possible even when the device is open + */ + if (ir->c_rx.addr) + i2c_use_client(&ir->c_rx); + if (ir->c_tx.addr) + i2c_use_client(&ir->c_tx); + + return 0; +} + +static void set_use_dec(void *data) +{ + struct IR *ir = data; + + if (ir->c_rx.addr) + i2c_release_client(&ir->c_rx); + if (ir->c_tx.addr) + i2c_release_client(&ir->c_tx); + if (ir->l.owner != NULL) + module_put(ir->l.owner); +} + +/* safe read of a uint32 (always network byte order) */ +static int read_uint32(unsigned char **data, + unsigned char *endp, unsigned int *val) +{ + if (*data + 4 > endp) + return 0; + *val = ((*data)[0] << 24) | ((*data)[1] << 16) | + ((*data)[2] << 8) | (*data)[3]; + *data += 4; + return 1; +} + +/* safe read of a uint8 */ +static int read_uint8(unsigned char **data, + unsigned char *endp, unsigned char *val) +{ + if (*data + 1 > endp) + return 0; + *val = *((*data)++); + return 1; +} + +/* safe skipping of N bytes */ +static int skip(unsigned char **data, + unsigned char *endp, unsigned int distance) +{ + if (*data + distance > endp) + return 0; + *data += distance; + return 1; +} + +/* decompress key data into the given buffer */ +static int get_key_data(unsigned char *buf, + unsigned int codeset, unsigned int key) +{ + unsigned char *data, *endp, *diffs, *key_block; + unsigned char keys, ndiffs, id; + unsigned int base, lim, pos, i; + + /* Binary search for the codeset */ + for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) { + pos = base + (lim >> 1); + data = tx_data->code_sets[pos]; + + if (!read_uint32(&data, tx_data->endp, &i)) + goto corrupt; + + if (i == codeset) + break; + else if (codeset > i) { + base = pos + 1; + --lim; + } + } + /* Not found? */ + if (!lim) + return -EPROTO; + + /* Set end of data block */ + endp = pos < tx_data->num_code_sets - 1 ? + tx_data->code_sets[pos + 1] : tx_data->endp; + + /* Read the block header */ + if (!read_uint8(&data, endp, &keys) || + !read_uint8(&data, endp, &ndiffs) || + ndiffs > TX_BLOCK_SIZE || keys == 0) + goto corrupt; + + /* Save diffs & skip */ + diffs = data; + if (!skip(&data, endp, ndiffs)) + goto corrupt; + + /* Read the id of the first key */ + if (!read_uint8(&data, endp, &id)) + goto corrupt; + + /* Unpack the first key's data */ + for (i = 0; i < TX_BLOCK_SIZE; ++i) { + if (tx_data->fixed[i] == -1) { + if (!read_uint8(&data, endp, &buf[i])) + goto corrupt; + } else { + buf[i] = (unsigned char)tx_data->fixed[i]; + } + } + + /* Early out key found/not found */ + if (key == id) + return 0; + if (keys == 1) + return -EPROTO; + + /* Sanity check */ + key_block = data; + if (!skip(&data, endp, (keys - 1) * (ndiffs + 1))) + goto corrupt; + + /* Binary search for the key */ + for (base = 0, lim = keys - 1; lim; lim >>= 1) { + /* Seek to block */ + unsigned char *key_data; + pos = base + (lim >> 1); + key_data = key_block + (ndiffs + 1) * pos; + + if (*key_data == key) { + /* skip key id */ + ++key_data; + + /* found, so unpack the diffs */ + for (i = 0; i < ndiffs; ++i) { + unsigned char val; + if (!read_uint8(&key_data, endp, &val) || + diffs[i] >= TX_BLOCK_SIZE) + goto corrupt; + buf[diffs[i]] = val; + } + + return 0; + } else if (key > *key_data) { + base = pos + 1; + --lim; + } + } + /* Key not found */ + return -EPROTO; + +corrupt: + zilog_error("firmware is corrupt\n"); + return -EFAULT; +} + +/* send a block of data to the IR TX device */ +static int send_data_block(struct IR *ir, unsigned char *data_block) +{ + int i, j, ret; + unsigned char buf[5]; + + for (i = 0; i < TX_BLOCK_SIZE;) { + int tosend = TX_BLOCK_SIZE - i; + if (tosend > 4) + tosend = 4; + buf[0] = (unsigned char)(i + 1); + for (j = 0; j < tosend; ++j) + buf[1 + j] = data_block[i + j]; + dprintk("%02x %02x %02x %02x %02x", + buf[0], buf[1], buf[2], buf[3], buf[4]); + ret = i2c_master_send(&ir->c_tx, buf, tosend + 1); + if (ret != tosend + 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + i += tosend; + } + return 0; +} + +/* send boot data to the IR TX device */ +static int send_boot_data(struct IR *ir) +{ + int ret; + unsigned char buf[4]; + + /* send the boot block */ + ret = send_data_block(ir, tx_data->boot_data); + if (ret != 0) + return ret; + + /* kick it off? */ + buf[0] = 0x00; + buf[1] = 0x20; + ret = i2c_master_send(&ir->c_tx, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + ret = i2c_master_send(&ir->c_tx, buf, 1); + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Here comes the firmware version... (hopefully) */ + ret = i2c_master_recv(&ir->c_tx, buf, 4); + if (ret != 4) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return 0; + } + if (buf[0] != 0x80) { + zilog_error("unexpected IR TX response: %02x\n", buf[0]); + return 0; + } + zilog_notify("Zilog/Hauppauge IR blaster firmware version " + "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]); + + return 0; +} + +/* unload "firmware", lock held */ +static void fw_unload_locked(void) +{ + if (tx_data) { + if (tx_data->code_sets) + vfree(tx_data->code_sets); + + if (tx_data->datap) + vfree(tx_data->datap); + + vfree(tx_data); + tx_data = NULL; + dprintk("successfully unloaded IR blaster firmware\n"); + } +} + +/* unload "firmware" for the IR TX device */ +static void fw_unload(void) +{ + mutex_lock(&tx_data_lock); + fw_unload_locked(); + mutex_unlock(&tx_data_lock); +} + +/* load "firmware" for the IR TX device */ +static int fw_load(struct IR *ir) +{ + int ret; + unsigned int i; + unsigned char *data, version, num_global_fixed; + const struct firmware *fw_entry; + + /* Already loaded? */ + mutex_lock(&tx_data_lock); + if (tx_data) { + ret = 0; + goto out; + } + + /* Request codeset data file */ + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev); + if (ret != 0) { + zilog_error("firmware haup-ir-blaster.bin not available " + "(%d)\n", ret); + ret = ret < 0 ? ret : -EFAULT; + goto out; + } + dprintk("firmware of size %zu loaded\n", fw_entry->size); + + /* Parse the file */ + tx_data = vmalloc(sizeof(*tx_data)); + if (tx_data == NULL) { + zilog_error("out of memory\n"); + release_firmware(fw_entry); + ret = -ENOMEM; + goto out; + } + tx_data->code_sets = NULL; + + /* Copy the data so hotplug doesn't get confused and timeout */ + tx_data->datap = vmalloc(fw_entry->size); + if (tx_data->datap == NULL) { + zilog_error("out of memory\n"); + release_firmware(fw_entry); + vfree(tx_data); + ret = -ENOMEM; + goto out; + } + memcpy(tx_data->datap, fw_entry->data, fw_entry->size); + tx_data->endp = tx_data->datap + fw_entry->size; + release_firmware(fw_entry); fw_entry = NULL; + + /* Check version */ + data = tx_data->datap; + if (!read_uint8(&data, tx_data->endp, &version)) + goto corrupt; + if (version != 1) { + zilog_error("unsupported code set file version (%u, expected" + "1) -- please upgrade to a newer driver", + version); + fw_unload_locked(); + ret = -EFAULT; + goto out; + } + + /* Save boot block for later */ + tx_data->boot_data = data; + if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE)) + goto corrupt; + + if (!read_uint32(&data, tx_data->endp, + &tx_data->num_code_sets)) + goto corrupt; + + dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets); + + tx_data->code_sets = vmalloc( + tx_data->num_code_sets * sizeof(char *)); + if (tx_data->code_sets == NULL) { + fw_unload_locked(); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TX_BLOCK_SIZE; ++i) + tx_data->fixed[i] = -1; + + /* Read global fixed data template */ + if (!read_uint8(&data, tx_data->endp, &num_global_fixed) || + num_global_fixed > TX_BLOCK_SIZE) + goto corrupt; + for (i = 0; i < num_global_fixed; ++i) { + unsigned char pos, val; + if (!read_uint8(&data, tx_data->endp, &pos) || + !read_uint8(&data, tx_data->endp, &val) || + pos >= TX_BLOCK_SIZE) + goto corrupt; + tx_data->fixed[pos] = (int)val; + } + + /* Filch out the position of each code set */ + for (i = 0; i < tx_data->num_code_sets; ++i) { + unsigned int id; + unsigned char keys; + unsigned char ndiffs; + + /* Save the codeset position */ + tx_data->code_sets[i] = data; + + /* Read header */ + if (!read_uint32(&data, tx_data->endp, &id) || + !read_uint8(&data, tx_data->endp, &keys) || + !read_uint8(&data, tx_data->endp, &ndiffs) || + ndiffs > TX_BLOCK_SIZE || keys == 0) + goto corrupt; + + /* skip diff positions */ + if (!skip(&data, tx_data->endp, ndiffs)) + goto corrupt; + + /* + * After the diffs we have the first key id + data - + * global fixed + */ + if (!skip(&data, tx_data->endp, + 1 + TX_BLOCK_SIZE - num_global_fixed)) + goto corrupt; + + /* Then we have keys-1 blocks of key id+diffs */ + if (!skip(&data, tx_data->endp, + (ndiffs + 1) * (keys - 1))) + goto corrupt; + } + ret = 0; + goto out; + +corrupt: + zilog_error("firmware is corrupt\n"); + fw_unload_locked(); + ret = -EFAULT; + +out: + mutex_unlock(&tx_data_lock); + return ret; +} + +/* initialise the IR TX device */ +static int tx_init(struct IR *ir) +{ + int ret; + + /* Load 'firmware' */ + ret = fw_load(ir); + if (ret != 0) + return ret; + + /* Send boot block */ + ret = send_boot_data(ir); + if (ret != 0) + return ret; + ir->need_boot = 0; + + /* Looks good */ + return 0; +} + +/* do nothing stub to make LIRC happy */ +static loff_t lseek(struct file *filep, loff_t offset, int orig) +{ + return -ESPIPE; +} + +/* copied from lirc_dev */ +static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) +{ + struct IR *ir = (struct IR *)filep->private_data; + unsigned char buf[ir->buf.chunk_size]; + int ret = 0, written = 0; + DECLARE_WAITQUEUE(wait, current); + + dprintk("read called\n"); + if (ir->c_rx.addr == 0) + return -ENODEV; + + if (mutex_lock_interruptible(&ir->buf_lock)) + return -ERESTARTSYS; + + if (n % ir->buf.chunk_size) { + dprintk("read result = -EINVAL\n"); + mutex_unlock(&ir->buf_lock); + return -EINVAL; + } + + /* + * we add ourselves to the task queue before buffer check + * to avoid losing scan code (in case when queue is awaken somewhere + * between while condition checking and scheduling) + */ + add_wait_queue(&ir->buf.wait_poll, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* + * while we didn't provide 'length' bytes, device is opened in blocking + * mode and 'copy_to_user' is happy, wait for data. + */ + while (written < n && ret == 0) { + if (lirc_buffer_empty(&ir->buf)) { + /* + * According to the read(2) man page, 'written' can be + * returned as less than 'n', instead of blocking + * again, returning -EWOULDBLOCK, or returning + * -ERESTARTSYS + */ + if (written) + break; + if (filep->f_flags & O_NONBLOCK) { + ret = -EWOULDBLOCK; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } else { + lirc_buffer_read(&ir->buf, buf); + ret = copy_to_user((void *)outbuf+written, buf, + ir->buf.chunk_size); + written += ir->buf.chunk_size; + } + } + + remove_wait_queue(&ir->buf.wait_poll, &wait); + set_current_state(TASK_RUNNING); + mutex_unlock(&ir->buf_lock); + + dprintk("read result = %s (%d)\n", + ret ? "-EFAULT" : "OK", ret); + + return ret ? ret : written; +} + +/* send a keypress to the IR TX device */ +static int send_code(struct IR *ir, unsigned int code, unsigned int key) +{ + unsigned char data_block[TX_BLOCK_SIZE]; + unsigned char buf[2]; + int i, ret; + + /* Get data for the codeset/key */ + ret = get_key_data(data_block, code, key); + + if (ret == -EPROTO) { + zilog_error("failed to get data for code %u, key %u -- check " + "lircd.conf entries\n", code, key); + return ret; + } else if (ret != 0) + return ret; + + /* Send the data block */ + ret = send_data_block(ir, data_block); + if (ret != 0) + return ret; + + /* Send data block length? */ + buf[0] = 0x00; + buf[1] = 0x40; + ret = i2c_master_send(&ir->c_tx, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + ret = i2c_master_send(&ir->c_tx, buf, 1); + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Send finished download? */ + ret = i2c_master_recv(&ir->c_tx, buf, 1); + if (ret != 1) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + if (buf[0] != 0xA0) { + zilog_error("unexpected IR TX response #1: %02x\n", + buf[0]); + return -EFAULT; + } + + /* Send prepare command? */ + buf[0] = 0x00; + buf[1] = 0x80; + ret = i2c_master_send(&ir->c_tx, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + +#ifdef I2C_HW_B_HDPVR + /* + * The sleep bits aren't necessary on the HD PVR, and in fact, the + * last i2c_master_recv always fails with a -5, so for now, we're + * going to skip this whole mess and say we're done on the HD PVR + */ + if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) + goto done; +#endif + + /* + * This bit NAKs until the device is ready, so we retry it + * sleeping a bit each time. This seems to be what the windows + * driver does, approximately. + * Try for up to 1s. + */ + for (i = 0; i < 20; ++i) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((50 * HZ + 999) / 1000); + ret = i2c_master_send(&ir->c_tx, buf, 1); + if (ret == 1) + break; + dprintk("NAK expected: i2c_master_send " + "failed with %d (try %d)\n", ret, i+1); + } + if (ret != 1) { + zilog_error("IR TX chip never got ready: last i2c_master_send " + "failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Seems to be an 'ok' response */ + i = i2c_master_recv(&ir->c_tx, buf, 1); + if (i != 1) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return -EFAULT; + } + if (buf[0] != 0x80) { + zilog_error("unexpected IR TX response #2: %02x\n", buf[0]); + return -EFAULT; + } + +done: + /* Oh good, it worked */ + dprintk("sent code %u, key %u\n", code, key); + return 0; +} + +/* + * Write a code to the device. We take in a 32-bit number (an int) and then + * decode this to a codeset/key index. The key data is then decompressed and + * sent to the device. We have a spin lock as per i2c documentation to prevent + * multiple concurrent sends which would probably cause the device to explode. + */ +static ssize_t write(struct file *filep, const char *buf, size_t n, + loff_t *ppos) +{ + struct IR *ir = (struct IR *)filep->private_data; + size_t i; + int failures = 0; + + if (ir->c_tx.addr == 0) + return -ENODEV; + + /* Validate user parameters */ + if (n % sizeof(int)) + return -EINVAL; + + /* Lock i2c bus for the duration */ + mutex_lock(&ir->ir_lock); + + /* Send each keypress */ + for (i = 0; i < n;) { + int ret = 0; + int command; + + if (copy_from_user(&command, buf + i, sizeof(command))) { + mutex_unlock(&ir->ir_lock); + return -EFAULT; + } + + /* Send boot data first if required */ + if (ir->need_boot == 1) { + ret = send_boot_data(ir); + if (ret == 0) + ir->need_boot = 0; + } + + /* Send the code */ + if (ret == 0) { + ret = send_code(ir, (unsigned)command >> 16, + (unsigned)command & 0xFFFF); + if (ret == -EPROTO) { + mutex_unlock(&ir->ir_lock); + return ret; + } + } + + /* + * Hmm, a failure. If we've had a few then give up, otherwise + * try a reset + */ + if (ret != 0) { + /* Looks like the chip crashed, reset it */ + zilog_error("sending to the IR transmitter chip " + "failed, trying reset\n"); + + if (failures >= 3) { + zilog_error("unable to send to the IR chip " + "after 3 resets, giving up\n"); + mutex_unlock(&ir->ir_lock); + return ret; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ + 999) / 1000); + ir->need_boot = 1; + ++failures; + } else + i += sizeof(int); + } + + /* Release i2c bus */ + mutex_unlock(&ir->ir_lock); + + /* All looks good */ + return n; +} + +/* copied from lirc_dev */ +static unsigned int poll(struct file *filep, poll_table *wait) +{ + struct IR *ir = (struct IR *)filep->private_data; + unsigned int ret; + + dprintk("poll called\n"); + if (ir->c_rx.addr == 0) + return -ENODEV; + + mutex_lock(&ir->buf_lock); + + poll_wait(filep, &ir->buf.wait_poll, wait); + + dprintk("poll result = %s\n", + lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM"); + + ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM); + + mutex_unlock(&ir->buf_lock); + return ret; +} + +static int ioctl(struct inode *node, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + struct IR *ir = (struct IR *)filep->private_data; + int result; + unsigned long mode, features = 0; + + if (ir->c_rx.addr != 0) + features |= LIRC_CAN_REC_LIRCCODE; + if (ir->c_tx.addr != 0) + features |= LIRC_CAN_SEND_PULSE; + + switch (cmd) { + case LIRC_GET_LENGTH: + result = put_user((unsigned long)13, + (unsigned long *)arg); + break; + case LIRC_GET_FEATURES: + result = put_user(features, (unsigned long *) arg); + break; + case LIRC_GET_REC_MODE: + if (!(features&LIRC_CAN_REC_MASK)) + return -ENOSYS; + + result = put_user(LIRC_REC2MODE + (features&LIRC_CAN_REC_MASK), + (unsigned long *)arg); + break; + case LIRC_SET_REC_MODE: + if (!(features&LIRC_CAN_REC_MASK)) + return -ENOSYS; + + result = get_user(mode, (unsigned long *)arg); + if (!result && !(LIRC_MODE2REC(mode) & features)) + result = -EINVAL; + break; + case LIRC_GET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); + break; + case LIRC_SET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + + result = get_user(mode, (unsigned long *) arg); + if (!result && mode != LIRC_MODE_PULSE) + return -EINVAL; + break; + default: + return -EINVAL; + } + return result; +} + +/* + * Open the IR device. Get hold of our IR structure and + * stash it in private_data for the file + */ +static int open(struct inode *node, struct file *filep) +{ + struct IR *ir; + int ret; + + /* find our IR struct */ + unsigned minor = MINOR(node->i_rdev); + if (minor >= MAX_IRCTL_DEVICES) { + dprintk("minor %d: open result = -ENODEV\n", + minor); + return -ENODEV; + } + ir = ir_devices[minor]; + + /* increment in use count */ + mutex_lock(&ir->ir_lock); + ++ir->open; + ret = set_use_inc(ir); + if (ret != 0) { + --ir->open; + mutex_unlock(&ir->ir_lock); + return ret; + } + mutex_unlock(&ir->ir_lock); + + /* stash our IR struct */ + filep->private_data = ir; + + return 0; +} + +/* Close the IR device */ +static int close(struct inode *node, struct file *filep) +{ + /* find our IR struct */ + struct IR *ir = (struct IR *)filep->private_data; + if (ir == NULL) { + zilog_error("close: no private_data attached to the file!\n"); + return -ENODEV; + } + + /* decrement in use count */ + mutex_lock(&ir->ir_lock); + --ir->open; + set_use_dec(ir); + mutex_unlock(&ir->ir_lock); + + return 0; +} + +static struct lirc_driver lirc_template = { + .name = "lirc_zilog", + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .owner = THIS_MODULE +}; + +static int ir_remove(struct i2c_client *client); +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static const struct i2c_device_id ir_transceiver_id[] = { + /* Generic entry for any IR transceiver */ + { "ir_video", 0 }, + /* IR device specific entries should be added here */ + { "ir_tx_z8f0811_haup", 0 }, + { "ir_rx_z8f0811_haup", 0 }, + { } +}; + +static struct i2c_driver driver = { + .driver = { + .owner = THIS_MODULE, + .name = "Zilog/Hauppauge i2c IR", + }, + .probe = ir_probe, + .remove = ir_remove, + .command = ir_command, + .id_table = ir_transceiver_id, +}; + +static struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .llseek = lseek, + .read = read, + .write = write, + .poll = poll, + .ioctl = ioctl, + .open = open, + .release = close +}; + +static int ir_remove(struct i2c_client *client) +{ + struct IR *ir = i2c_get_clientdata(client); + + mutex_lock(&ir->ir_lock); + + if (ir->have_rx || ir->have_tx) { + DECLARE_COMPLETION(tn); + DECLARE_COMPLETION(tn2); + + /* end up polling thread */ + if (ir->task && !IS_ERR(ir->task)) { + ir->t_notify = &tn; + ir->t_notify2 = &tn2; + ir->shutdown = 1; + wake_up_process(ir->task); + complete(&tn2); + wait_for_completion(&tn); + ir->t_notify = NULL; + ir->t_notify2 = NULL; + } + + } else { + mutex_unlock(&ir->ir_lock); + zilog_error("%s: detached from something we didn't " + "attach to\n", __func__); + return -ENODEV; + } + + /* unregister lirc driver */ + if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { + lirc_unregister_driver(ir->l.minor); + ir_devices[ir->l.minor] = NULL; + } + + /* free memory */ + lirc_buffer_free(&ir->buf); + mutex_unlock(&ir->ir_lock); + kfree(ir); + + return 0; +} + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct IR *ir = NULL; + struct i2c_adapter *adap = client->adapter; + char buf; + int ret; + int have_rx = 0, have_tx = 0; + + dprintk("%s: adapter id=0x%x, client addr=0x%02x\n", + __func__, adap->id, client->addr); + + /* + * The external IR receiver is at i2c address 0x71. + * The IR transmitter is at 0x70. + */ + client->addr = 0x70; + + if (!disable_tx) { + if (i2c_master_recv(client, &buf, 1) == 1) + have_tx = 1; + dprintk("probe 0x70 @ %s: %s\n", + adap->name, have_tx ? "success" : "failed"); + } + + if (!disable_rx) { + client->addr = 0x71; + if (i2c_master_recv(client, &buf, 1) == 1) + have_rx = 1; + dprintk("probe 0x71 @ %s: %s\n", + adap->name, have_rx ? "success" : "failed"); + } + + if (!(have_rx || have_tx)) { + zilog_error("%s: no devices found\n", adap->name); + goto out_nodev; + } + + printk(KERN_INFO "lirc_zilog: chip found with %s\n", + have_rx && have_tx ? "RX and TX" : + have_rx ? "RX only" : "TX only"); + + ir = kzalloc(sizeof(struct IR), GFP_KERNEL); + + if (!ir) + goto out_nomem; + + ret = lirc_buffer_init(&ir->buf, 2, BUFLEN / 2); + if (ret) + goto out_nomem; + + mutex_init(&ir->ir_lock); + mutex_init(&ir->buf_lock); + ir->need_boot = 1; + + memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); + ir->l.minor = -1; + + /* I2C attach to device */ + i2c_set_clientdata(client, ir); + + /* initialise RX device */ + if (have_rx) { + DECLARE_COMPLETION(tn); + memcpy(&ir->c_rx, client, sizeof(struct i2c_client)); + + ir->c_rx.addr = 0x71; + strlcpy(ir->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME, + I2C_NAME_SIZE); + + /* try to fire up polling thread */ + ir->t_notify = &tn; + ir->task = kthread_run(lirc_thread, ir, "lirc_zilog"); + if (IS_ERR(ir->task)) { + ret = PTR_ERR(ir->task); + zilog_error("lirc_register_driver: cannot run " + "poll thread %d\n", ret); + goto err; + } + wait_for_completion(&tn); + ir->t_notify = NULL; + ir->have_rx = 1; + } + + /* initialise TX device */ + if (have_tx) { + memcpy(&ir->c_tx, client, sizeof(struct i2c_client)); + ir->c_tx.addr = 0x70; + strlcpy(ir->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME, + I2C_NAME_SIZE); + ir->have_tx = 1; + } + + /* set lirc_dev stuff */ + ir->l.code_length = 13; + ir->l.rbuf = &ir->buf; + ir->l.fops = &lirc_fops; + ir->l.data = ir; + ir->l.minor = minor; + ir->l.dev = &adap->dev; + ir->l.sample_rate = 0; + + /* register with lirc */ + ir->l.minor = lirc_register_driver(&ir->l); + if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { + zilog_error("ir_attach: \"minor\" must be between 0 and %d " + "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor); + ret = -EBADRQC; + goto err; + } + + /* store this for getting back in open() later on */ + ir_devices[ir->l.minor] = ir; + + /* + * if we have the tx device, load the 'firmware'. We do this + * after registering with lirc as otherwise hotplug seems to take + * 10s to create the lirc device. + */ + if (have_tx) { + /* Special TX init */ + ret = tx_init(ir); + if (ret != 0) + goto err; + } + + return 0; + +err: + /* undo everything, hopefully... */ + if (ir->c_rx.addr) + ir_remove(&ir->c_rx); + if (ir->c_tx.addr) + ir_remove(&ir->c_tx); + return ret; + +out_nodev: + zilog_error("no device found\n"); + return -ENODEV; + +out_nomem: + zilog_error("memory allocation failure\n"); + kfree(ir); + return -ENOMEM; +} + +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + /* nothing */ + return 0; +} + +static int __init zilog_init(void) +{ + int ret; + + zilog_notify("Zilog/Hauppauge IR driver initializing\n"); + + mutex_init(&tx_data_lock); + + request_module("firmware_class"); + + ret = i2c_add_driver(&driver); + if (ret) + zilog_error("initialization failed\n"); + else + zilog_notify("initialization complete\n"); + + return ret; +} + +static void __exit zilog_exit(void) +{ + i2c_del_driver(&driver); + /* if loaded */ + fw_unload(); + zilog_notify("Zilog/Hauppauge IR driver unloaded\n"); +} + +module_init(zilog_init); +module_exit(zilog_exit); + +MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)"); +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, " + "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver"); +MODULE_LICENSE("GPL"); +/* for compat with old name, which isn't all that accurate anymore */ +MODULE_ALIAS("lirc_pvr150"); + +module_param(minor, int, 0444); +MODULE_PARM_DESC(minor, "Preferred minor device number"); + +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Enable debugging messages"); + +module_param(disable_rx, bool, 0644); +MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device"); + +module_param(disable_tx, bool, 0644); +MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device"); Index: drivers/Makefile =================================================================== --- drivers/Makefile (revision 1) +++ drivers/Makefile (working copy) @@ -136,6 +136,7 @@ lirc_it87 \ lirc_ite8709 \ lirc_mceusb \ + lirc_zilog \ lirc_parallel \ lirc_sasem \ lirc_serial \