| Patch generated from the linuxtv staging/other branch, with a few |
| additional pending fixes merged in, and just about everything not |
| essential to the ir-core update chopped out. |
| |
| (Patch generated 2010.07.16) |
| |
| Signed-off-by: Jarod Wilson <jarod@redhat.com> |
| |
| |
| Documentation/DocBook/media-entities.tmpl | 1 |
| Documentation/DocBook/media.tmpl | 8 |
| Documentation/DocBook/v4l/lirc_device_interface.xml | 235 ++++ |
| Documentation/DocBook/v4l/remote_controllers.xml | 2 |
| Documentation/dvb/get_dvb_firmware | 19 |
| Documentation/video4linux/CARDLIST.cx23885 | 6 |
| drivers/input/evdev.c | 39 |
| drivers/input/input.c | 268 ++++ |
| drivers/media/IR/Kconfig | 34 |
| drivers/media/IR/Makefile | 3 |
| drivers/media/IR/imon.c | 5 |
| drivers/media/IR/ir-core-priv.h | 54 |
| drivers/media/IR/ir-jvc-decoder.c | 152 -- |
| drivers/media/IR/ir-lirc-codec.c | 283 ++++ |
| drivers/media/IR/ir-nec-decoder.c | 151 -- |
| drivers/media/IR/ir-raw-event.c | 167 +- |
| drivers/media/IR/ir-rc5-decoder.c | 167 -- |
| drivers/media/IR/ir-rc6-decoder.c | 153 -- |
| drivers/media/IR/ir-sony-decoder.c | 155 -- |
| drivers/media/IR/ir-sysfs.c | 261 ++-- |
| drivers/media/IR/keymaps/Makefile | 2 |
| drivers/media/IR/keymaps/rc-lirc.c | 41 |
| drivers/media/IR/keymaps/rc-rc6-mce.c | 105 + |
| drivers/media/IR/lirc_dev.c | 764 +++++++++++++ |
| drivers/media/IR/mceusb.c | 1143 ++++++++++++++++++++ |
| drivers/media/common/tuners/tda18271-fe.c | 8 |
| drivers/media/dvb/mantis/Kconfig | 14 |
| drivers/media/dvb/mantis/mantis_input.c | 5 |
| drivers/media/video/cx23885/cx23885-cards.c | 40 |
| drivers/media/video/cx23885/cx23885-core.c | 11 |
| drivers/media/video/cx23885/cx23885-dvb.c | 2 |
| drivers/media/video/cx23885/cx23885-input.c | 317 +---- |
| drivers/media/video/cx23885/cx23885-ir.c | 2 |
| drivers/media/video/cx23885/cx23885.h | 12 |
| drivers/media/video/cx88/cx88-cards.c | 9 |
| drivers/media/video/cx88/cx88-i2c.c | 6 |
| drivers/media/video/cx88/cx88-input.c | 46 |
| drivers/media/video/cx88/cx88.h | 1 |
| drivers/media/video/em28xx/em28xx-input.c | 80 - |
| drivers/media/video/em28xx/em28xx-video.c | 4 |
| drivers/media/video/em28xx/em28xx.h | 1 |
| drivers/media/video/hdpvr/hdpvr-core.c | 5 |
| drivers/media/video/ir-kbd-i2c.c | 14 |
| drivers/media/video/pvrusb2/pvrusb2-ioread.c | 5 |
| include/linux/input.h | 39 |
| include/media/ir-core.h | 8 |
| include/media/ir-kbd-i2c.h | 2 |
| include/media/lirc.h | 165 ++ |
| include/media/lirc_dev.h | 225 +++ |
| include/media/rc-map.h | 7 |
| 50 files changed, 3971 insertions(+), 1275 deletions(-) |
| |
| diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl |
| index 5d4d40f..6ae9715 100644 |
| |
| |
| @@ -218,6 +218,7 @@ |
| <!ENTITY sub-dev-teletext SYSTEM "v4l/dev-teletext.xml"> |
| <!ENTITY sub-driver SYSTEM "v4l/driver.xml"> |
| <!ENTITY sub-libv4l SYSTEM "v4l/libv4l.xml"> |
| +<!ENTITY sub-lirc_device_interface SYSTEM "v4l/lirc_device_interface.xml"> |
| <!ENTITY sub-remote_controllers SYSTEM "v4l/remote_controllers.xml"> |
| <!ENTITY sub-fdl-appendix SYSTEM "v4l/fdl-appendix.xml"> |
| <!ENTITY sub-close SYSTEM "v4l/func-close.xml"> |
| diff --git a/Documentation/DocBook/media.tmpl b/Documentation/DocBook/media.tmpl |
| index eea564b..f11048d 100644 |
| |
| |
| @@ -28,7 +28,7 @@ |
| <title>LINUX MEDIA INFRASTRUCTURE API</title> |
| |
| <copyright> |
| - <year>2009</year> |
| + <year>2009-2010</year> |
| <holder>LinuxTV Developers</holder> |
| </copyright> |
| |
| @@ -61,7 +61,7 @@ Foundation. A copy of the license is included in the chapter entitled |
| in fact it covers several different video standards including |
| DVB-T, DVB-S, DVB-C and ATSC. The API is currently being updated |
| to documment support also for DVB-S2, ISDB-T and ISDB-S.</para> |
| - <para>The third part covers other API's used by all media infrastructure devices</para> |
| + <para>The third part covers Remote Controller API</para> |
| <para>For additional information and for the latest development code, |
| see: <ulink url="http://linuxtv.org">http://linuxtv.org</ulink>.</para> |
| <para>For discussing improvements, reporting troubles, sending new drivers, etc, please mail to: <ulink url="http://vger.kernel.org/vger-lists.html#linux-media">Linux Media Mailing List (LMML).</ulink>.</para> |
| @@ -86,7 +86,7 @@ Foundation. A copy of the license is included in the chapter entitled |
| </author> |
| </authorgroup> |
| <copyright> |
| - <year>2009</year> |
| + <year>2009-2010</year> |
| <holder>Mauro Carvalho Chehab</holder> |
| </copyright> |
| |
| @@ -101,7 +101,7 @@ Foundation. A copy of the license is included in the chapter entitled |
| </revhistory> |
| </partinfo> |
| |
| -<title>Other API's used by media infrastructure drivers</title> |
| +<title>Remote Controller API</title> |
| <chapter id="remote_controllers"> |
| &sub-remote_controllers; |
| </chapter> |
| diff --git a/Documentation/DocBook/v4l/lirc_device_interface.xml b/Documentation/DocBook/v4l/lirc_device_interface.xml |
| new file mode 100644 |
| index 0000000..0413234 |
| |
| |
| @@ -0,0 +1,235 @@ |
| +<section id="lirc_dev"> |
| +<title>LIRC Device Interface</title> |
| + |
| + |
| +<section id="lirc_dev_intro"> |
| +<title>Introduction</title> |
| + |
| +<para>The LIRC device interface is a bi-directional interface for |
| +transporting raw IR data between userspace and kernelspace. Fundamentally, |
| +it is just a chardev (/dev/lircX, for X = 0, 1, 2, ...), with a number |
| +of standard struct file_operations defined on it. With respect to |
| +transporting raw IR data to and fro, the essential fops are read, write |
| +and ioctl.</para> |
| + |
| +<para>Example dmesg output upon a driver registering w/LIRC:</para> |
| + <blockquote> |
| + <para>$ dmesg |grep lirc_dev</para> |
| + <para>lirc_dev: IR Remote Control driver registered, major 248</para> |
| + <para>rc rc0: lirc_dev: driver ir-lirc-codec (mceusb) registered at minor = 0</para> |
| + </blockquote> |
| + |
| +<para>What you should see for a chardev:</para> |
| + <blockquote> |
| + <para>$ ls -l /dev/lirc*</para> |
| + <para>crw-rw---- 1 root root 248, 0 Jul 2 22:20 /dev/lirc0</para> |
| + </blockquote> |
| +</section> |
| + |
| +<section id="lirc_read"> |
| +<title>LIRC read fop</title> |
| + |
| +<para>The lircd userspace daemon reads raw IR data from the LIRC chardev. The |
| +exact format of the data depends on what modes a driver supports, and what |
| +mode has been selected. lircd obtains supported modes and sets the active mode |
| +via the ioctl interface, detailed at <xref linkend="lirc_ioctl"/>. The generally |
| +preferred mode is LIRC_MODE_MODE2, in which packets containing an int value |
| +describing an IR signal are read from the chardev.</para> |
| + |
| +<para>See also <ulink url="http://www.lirc.org/html/technical.html">http://www.lirc.org/html/technical.html</ulink> for more info.</para> |
| +</section> |
| + |
| +<section id="lirc_write"> |
| +<title>LIRC write fop</title> |
| + |
| +<para>The data written to the chardev is a pulse/space sequence of integer |
| +values. Pulses and spaces are only marked implicitly by their position. The |
| +data must start and end with a pulse, therefore, the data must always include |
| +an unevent number of samples. The write function must block until the data has |
| +been transmitted by the hardware.</para> |
| +</section> |
| + |
| +<section id="lirc_ioctl"> |
| +<title>LIRC ioctl fop</title> |
| + |
| +<para>The LIRC device's ioctl definition is bound by the ioctl function |
| +definition of struct file_operations, leaving us with an unsigned int |
| +for the ioctl command and an unsigned long for the arg. For the purposes |
| +of ioctl portability across 32-bit and 64-bit, these values are capped |
| +to their 32-bit sizes.</para> |
| + |
| +<para>The following ioctls can be used to change specific hardware settings. |
| +In general each driver should have a default set of settings. The driver |
| +implementation is expected to re-apply the default settings when the device |
| +is closed by user-space, so that every application opening the device can rely |
| +on working with the default settings initially.</para> |
| + |
| +<variablelist> |
| + <varlistentry> |
| + <term>LIRC_GET_FEATURES</term> |
| + <listitem> |
| + <para>Obviously, get the underlying hardware device's features. If a driver |
| + does not announce support of certain features, calling of the corresponding |
| + ioctls is undefined.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_SEND_MODE</term> |
| + <listitem> |
| + <para>Get supported transmit mode. Only LIRC_MODE_PULSE is supported by lircd.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_REC_MODE</term> |
| + <listitem> |
| + <para>Get supported receive modes. Only LIRC_MODE_MODE2 and LIRC_MODE_LIRCCODE |
| + are supported by lircd.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_SEND_CARRIER</term> |
| + <listitem> |
| + <para>Get carrier frequency (in Hz) currently used for transmit.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_REC_CARRIER</term> |
| + <listitem> |
| + <para>Get carrier frequency (in Hz) currently used for IR reception.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_{G,S}ET_{SEND,REC}_DUTY_CYCLE</term> |
| + <listitem> |
| + <para>Get/set the duty cycle (from 0 to 100) of the carrier signal. Currently, |
| + no special meaning is defined for 0 or 100, but this could be used to switch |
| + off carrier generation in the future, so these values should be reserved.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_REC_RESOLUTION</term> |
| + <listitem> |
| + <para>Some receiver have maximum resolution which is defined by internal |
| + sample rate or data format limitations. E.g. it's common that signals can |
| + only be reported in 50 microsecond steps. This integer value is used by |
| + lircd to automatically adjust the aeps tolerance value in the lircd |
| + config file.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_M{IN,AX}_TIMEOUT</term> |
| + <listitem> |
| + <para>Some devices have internal timers that can be used to detect when |
| + there's no IR activity for a long time. This can help lircd in detecting |
| + that a IR signal is finished and can speed up the decoding process. |
| + Returns an integer value with the minimum/maximum timeout that can be |
| + set. Some devices have a fixed timeout, in that case both ioctls will |
| + return the same value even though the timeout cannot be changed.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_M{IN,AX}_FILTER_{PULSE,SPACE}</term> |
| + <listitem> |
| + <para>Some devices are able to filter out spikes in the incoming signal |
| + using given filter rules. These ioctls return the hardware capabilities |
| + that describe the bounds of the possible filters. Filter settings depend |
| + on the IR protocols that are expected. lircd derives the settings from |
| + all protocols definitions found in its config file.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_GET_LENGTH</term> |
| + <listitem> |
| + <para>Retrieves the code length in bits (only for LIRC_MODE_LIRCCODE). |
| + Reads on the device must be done in blocks matching the bit count. |
| + The bit could should be rounded up so that it matches full bytes.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_{SEND,REC}_MODE</term> |
| + <listitem> |
| + <para>Set send/receive mode. Largely obsolete for send, as only |
| + LIRC_MODE_PULSE is supported.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_{SEND,REC}_CARRIER</term> |
| + <listitem> |
| + <para>Set send/receive carrier (in Hz).</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_TRANSMITTER_MASK</term> |
| + <listitem> |
| + <para>This enables the given set of transmitters. The first transmitter |
| + is encoded by the least significant bit, etc. When an invalid bit mask |
| + is given, i.e. a bit is set, even though the device does not have so many |
| + transitters, then this ioctl returns the number of available transitters |
| + and does nothing otherwise.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_REC_TIMEOUT</term> |
| + <listitem> |
| + <para>Sets the integer value for IR inactivity timeout (cf. |
| + LIRC_GET_MIN_TIMEOUT and LIRC_GET_MAX_TIMEOUT). A value of 0 (if |
| + supported by the hardware) disables all hardware timeouts and data should |
| + be reported as soon as possible. If the exact value cannot be set, then |
| + the next possible value _greater_ than the given value should be set.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_REC_TIMEOUT_REPORTS</term> |
| + <listitem> |
| + <para>Enable (1) or disable (0) timeout reports in LIRC_MODE_MODE2. By |
| + default, timeout reports should be turned off.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_REC_FILTER_{,PULSE,SPACE}</term> |
| + <listitem> |
| + <para>Pulses/spaces shorter than this are filtered out by hardware. If |
| + filters cannot be set independently for pulse/space, the corresponding |
| + ioctls must return an error and LIRC_SET_REC_FILTER shall be used instead.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_MEASURE_CARRIER_MODE</term> |
| + <listitem> |
| + <para>Enable (1)/disable (0) measure mode. If enabled, from the next key |
| + press on, the driver will send LIRC_MODE2_FREQUENCY packets. By default |
| + this should be turned off.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SET_REC_{DUTY_CYCLE,CARRIER}_RANGE</term> |
| + <listitem> |
| + <para>To set a range use LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE |
| + with the lower bound first and later LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER |
| + with the upper bound.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_NOTIFY_DECODE</term> |
| + <listitem> |
| + <para>This ioctl is called by lircd whenever a successful decoding of an |
| + incoming IR signal could be done. This can be used by supporting hardware |
| + to give visual feedback to the user e.g. by flashing a LED.</para> |
| + </listitem> |
| + </varlistentry> |
| + <varlistentry> |
| + <term>LIRC_SETUP_{START,END}</term> |
| + <listitem> |
| + <para>Setting of several driver parameters can be optimized by encapsulating |
| + the according ioctl calls with LIRC_SETUP_START/LIRC_SETUP_END. When a |
| + driver receives a LIRC_SETUP_START ioctl it can choose to not commit |
| + further setting changes to the hardware until a LIRC_SETUP_END is received. |
| + But this is open to the driver implementation and every driver must also |
| + handle parameter changes which are not encapsulated by LIRC_SETUP_START |
| + and LIRC_SETUP_END. Drivers can also choose to ignore these ioctls.</para> |
| + </listitem> |
| + </varlistentry> |
| +</variablelist> |
| + |
| +</section> |
| +</section> |
| diff --git a/Documentation/DocBook/v4l/remote_controllers.xml b/Documentation/DocBook/v4l/remote_controllers.xml |
| index 73f5eab..3c3b667 100644 |
| |
| |
| @@ -173,3 +173,5 @@ keymapping.</para> |
| <para>This program demonstrates how to replace the keymap tables.</para> |
| &sub-keytable-c; |
| </section> |
| + |
| +&sub-lirc_device_interface; |
| diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware |
| index 239cbdb..9ea94dc 100644 |
| |
| |
| @@ -26,7 +26,7 @@ use IO::Handle; |
| "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", |
| "or51211", "or51132_qam", "or51132_vsb", "bluebird", |
| "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", |
| - "af9015", "ngene"); |
| + "af9015", "ngene", "az6027"); |
| |
| # Check args |
| syntax() if (scalar(@ARGV) != 1); |
| @@ -567,6 +567,23 @@ sub ngene { |
| "$file1, $file2"; |
| } |
| |
| +sub az6027{ |
| + my $file = "AZ6027_Linux_Driver.tar.gz"; |
| + my $url = "http://linux.terratec.de/files/$file"; |
| + my $firmware = "dvb-usb-az6027-03.fw"; |
| + |
| + wgetfile($file, $url); |
| + |
| + #untar |
| + if( system("tar xzvf $file $firmware")){ |
| + die "failed to untar firmware"; |
| + } |
| + if( system("rm $file")){ |
| + die ("unable to remove unnecessary files"); |
| + } |
| + |
| + $firmware; |
| +} |
| # --------------------------------------------------------------- |
| # Utilities |
| |
| diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 |
| index 16ca030..87c4634 100644 |
| |
| |
| @@ -17,9 +17,9 @@ |
| 16 -> DVBWorld DVB-S2 2005 [0001:2005] |
| 17 -> NetUP Dual DVB-S2 CI [1b55:2a2c] |
| 18 -> Hauppauge WinTV-HVR1270 [0070:2211] |
| - 19 -> Hauppauge WinTV-HVR1275 [0070:2215] |
| - 20 -> Hauppauge WinTV-HVR1255 [0070:2251] |
| - 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295] |
| + 19 -> Hauppauge WinTV-HVR1275 [0070:2215,0070:221d,0070:22f2] |
| + 20 -> Hauppauge WinTV-HVR1255 [0070:2251,0070:2259,0070:22f1] |
| + 21 -> Hauppauge WinTV-HVR1210 [0070:2291,0070:2295,0070:2299,0070:229d,0070:22f0,0070:22f3,0070:22f4,0070:22f5] |
| 22 -> Mygica X8506 DMB-TH [14f1:8651] |
| 23 -> Magic-Pro ProHDTV Extreme 2 [14f1:8657] |
| 24 -> Hauppauge WinTV-HVR1850 [0070:8541] |
| diff --git a/Documentation/video4linux/extract_xc3028.pl b/Documentation/video4linux/extract_xc3028.pl |
| old mode 100644 |
| new mode 100755 |
| diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c |
| index 2ee6c7a..b8a5673 100644 |
| |
| |
| @@ -515,6 +515,8 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, |
| struct input_absinfo abs; |
| struct ff_effect effect; |
| int __user *ip = (int __user *)p; |
| + struct keycode_table_entry kt, *kt_p = p; |
| + char scancode[16]; |
| unsigned int i, t, u, v; |
| int error; |
| |
| @@ -569,6 +571,43 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, |
| |
| return input_set_keycode(dev, t, v); |
| |
| + case EVIOCGKEYCODEBIG: |
| + if (copy_from_user(&kt, kt_p, sizeof(kt))) |
| + return -EFAULT; |
| + |
| + if (kt.len > sizeof(scancode)) |
| + return -EINVAL; |
| + |
| + kt.scancode = scancode; |
| + |
| + error = input_get_keycode_big(dev, &kt); |
| + if (error) |
| + return error; |
| + |
| + if (copy_to_user(kt_p, &kt, sizeof(kt))) |
| + return -EFAULT; |
| + |
| + /* FIXME: probably need some compat32 code */ |
| + if (copy_to_user(kt_p->scancode, kt.scancode, kt.len)) |
| + return -EFAULT; |
| + |
| + return 0; |
| + |
| + case EVIOCSKEYCODEBIG: |
| + if (copy_from_user(&kt, kt_p, sizeof(kt))) |
| + return -EFAULT; |
| + |
| + if (kt.len > sizeof(scancode)) |
| + return -EINVAL; |
| + |
| + kt.scancode = scancode; |
| + |
| + /* FIXME: probably need some compat32 code */ |
| + if (copy_from_user(kt.scancode, kt_p->scancode, kt.len)) |
| + return -EFAULT; |
| + |
| + return input_set_keycode_big(dev, &kt); |
| + |
| case EVIOCRMFF: |
| return input_ff_erase(dev, (int)(unsigned long) p, file); |
| |
| diff --git a/drivers/input/input.c b/drivers/input/input.c |
| index 9c79bd5..43aeb71 100644 |
| |
| |
| @@ -568,6 +568,11 @@ static void input_disconnect_device(struct input_dev *dev) |
| spin_unlock_irq(&dev->event_lock); |
| } |
| |
| +/* |
| + * Those routines handle the default case where no [gs]etkeycode() is |
| + * defined. In this case, an array indexed by the scancode is used. |
| + */ |
| + |
| static int input_fetch_keycode(struct input_dev *dev, int scancode) |
| { |
| switch (dev->keycodesize) { |
| @@ -582,27 +587,74 @@ static int input_fetch_keycode(struct input_dev *dev, int scancode) |
| } |
| } |
| |
| -static int input_default_getkeycode(struct input_dev *dev, |
| - unsigned int scancode, |
| - unsigned int *keycode) |
| +/* |
| + * Supports only 8, 16 and 32 bit scancodes. It wouldn't be that |
| + * hard to write some machine-endian logic to support 24 bit scancodes, |
| + * but it seemed overkill. It should also be noticed that, since there |
| + * are, in general, less than 256 scancodes sparsed into the scancode |
| + * space, even with 16 bits, the codespace is sparsed, with leads into |
| + * memory and code ineficiency, when retrieving the entire scancode |
| + * space. |
| + * So, it is highly recommended to implement getkeycodebig/setkeycodebig |
| + * instead of using a normal table approach, when more than 8 bits is |
| + * needed for the scancode. |
| + */ |
| +static int input_fetch_scancode(struct keycode_table_entry *kt_entry, |
| + u32 *scancode) |
| { |
| + switch (kt_entry->len) { |
| + case 1: |
| + *scancode = *((u8 *)kt_entry->scancode); |
| + break; |
| + case 2: |
| + *scancode = *((u16 *)kt_entry->scancode); |
| + break; |
| + case 4: |
| + *scancode = *((u32 *)kt_entry->scancode); |
| + break; |
| + default: |
| + return -EINVAL; |
| + } |
| + return 0; |
| +} |
| + |
| + |
| +static int input_default_getkeycode_from_index(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry) |
| +{ |
| + u32 scancode = kt_entry->index; |
| + |
| if (!dev->keycodesize) |
| return -EINVAL; |
| |
| if (scancode >= dev->keycodemax) |
| return -EINVAL; |
| |
| - *keycode = input_fetch_keycode(dev, scancode); |
| + kt_entry->keycode = input_fetch_keycode(dev, scancode); |
| + memcpy(kt_entry->scancode, &scancode, 4); |
| |
| return 0; |
| } |
| |
| +static int input_default_getkeycode_from_scancode(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry) |
| +{ |
| + if (input_fetch_scancode(kt_entry, &kt_entry->index)) |
| + return -EINVAL; |
| + |
| + return input_default_getkeycode_from_index(dev, kt_entry); |
| +} |
| + |
| + |
| static int input_default_setkeycode(struct input_dev *dev, |
| - unsigned int scancode, |
| - unsigned int keycode) |
| + struct keycode_table_entry *kt_entry) |
| { |
| - int old_keycode; |
| + u32 old_keycode; |
| int i; |
| + u32 scancode; |
| + |
| + if (input_fetch_scancode(kt_entry, &scancode)) |
| + return -EINVAL; |
| |
| if (scancode >= dev->keycodemax) |
| return -EINVAL; |
| @@ -610,32 +662,33 @@ static int input_default_setkeycode(struct input_dev *dev, |
| if (!dev->keycodesize) |
| return -EINVAL; |
| |
| - if (dev->keycodesize < sizeof(keycode) && (keycode >> (dev->keycodesize * 8))) |
| + if (dev->keycodesize < sizeof(dev->keycode) && |
| + (kt_entry->keycode >> (dev->keycodesize * 8))) |
| return -EINVAL; |
| |
| switch (dev->keycodesize) { |
| case 1: { |
| u8 *k = (u8 *)dev->keycode; |
| old_keycode = k[scancode]; |
| - k[scancode] = keycode; |
| + k[scancode] = kt_entry->keycode; |
| break; |
| } |
| case 2: { |
| u16 *k = (u16 *)dev->keycode; |
| old_keycode = k[scancode]; |
| - k[scancode] = keycode; |
| + k[scancode] = kt_entry->keycode; |
| break; |
| } |
| default: { |
| u32 *k = (u32 *)dev->keycode; |
| old_keycode = k[scancode]; |
| - k[scancode] = keycode; |
| + k[scancode] = kt_entry->keycode; |
| break; |
| } |
| } |
| |
| __clear_bit(old_keycode, dev->keybit); |
| - __set_bit(keycode, dev->keybit); |
| + __set_bit(kt_entry->keycode, dev->keybit); |
| |
| for (i = 0; i < dev->keycodemax; i++) { |
| if (input_fetch_keycode(dev, i) == old_keycode) { |
| @@ -648,6 +701,110 @@ static int input_default_setkeycode(struct input_dev *dev, |
| } |
| |
| /** |
| + * input_get_keycode_big - retrieve keycode currently mapped to a given scancode |
| + * @dev: input device which keymap is being queried |
| + * @kt_entry: keytable entry |
| + * |
| + * This function should be called by anyone interested in retrieving current |
| + * keymap. Presently evdev handlers use it. |
| + */ |
| +int input_get_keycode_big(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry) |
| +{ |
| + if (dev->getkeycode) { |
| + u32 scancode = kt_entry->index; |
| + |
| + /* |
| + * Support for legacy drivers, that don't implement the new |
| + * ioctls |
| + */ |
| + memcpy(kt_entry->scancode, &scancode, 4); |
| + return dev->getkeycode(dev, scancode, |
| + &kt_entry->keycode); |
| + } else |
| + return dev->getkeycodebig_from_index(dev, kt_entry); |
| +} |
| +EXPORT_SYMBOL(input_get_keycode_big); |
| + |
| +/** |
| + * input_set_keycode_big - attribute a keycode to a given scancode |
| + * @dev: input device which keymap is being queried |
| + * @kt_entry: keytable entry |
| + * |
| + * This function should be called by anyone needing to update current |
| + * keymap. Presently keyboard and evdev handlers use it. |
| + */ |
| +int input_set_keycode_big(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry) |
| +{ |
| + unsigned long flags; |
| + int old_keycode; |
| + int retval = -EINVAL; |
| + u32 uninitialized_var(scancode); |
| + |
| + if (kt_entry->keycode < 0 || kt_entry->keycode > KEY_MAX) |
| + return -EINVAL; |
| + |
| + spin_lock_irqsave(&dev->event_lock, flags); |
| + |
| + /* |
| + * We need to know the old scancode, in order to generate a |
| + * keyup effect, if the set operation happens successfully |
| + */ |
| + if (dev->getkeycode) { |
| + /* |
| + * Support for legacy drivers, that don't implement the new |
| + * ioctls |
| + */ |
| + if (!dev->setkeycode) |
| + goto out; |
| + |
| + retval = input_fetch_scancode(kt_entry, &scancode); |
| + if (retval) |
| + goto out; |
| + |
| + retval = dev->getkeycode(dev, scancode, |
| + &old_keycode); |
| + } else { |
| + int new_keycode = kt_entry->keycode; |
| + |
| + retval = dev->getkeycodebig_from_scancode(dev, kt_entry); |
| + old_keycode = kt_entry->keycode; |
| + kt_entry->keycode = new_keycode; |
| + } |
| + |
| + if (retval) |
| + goto out; |
| + |
| + if (dev->getkeycode) |
| + retval = dev->setkeycode(dev, scancode, |
| + kt_entry->keycode); |
| + else |
| + retval = dev->setkeycodebig(dev, kt_entry); |
| + if (retval) |
| + goto out; |
| + |
| + /* |
| + * Simulate keyup event if keycode is not present |
| + * in the keymap anymore |
| + */ |
| + if (test_bit(EV_KEY, dev->evbit) && |
| + !is_event_supported(old_keycode, dev->keybit, KEY_MAX) && |
| + __test_and_clear_bit(old_keycode, dev->key)) { |
| + |
| + input_pass_event(dev, EV_KEY, old_keycode, 0); |
| + if (dev->sync) |
| + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); |
| + } |
| + |
| + out: |
| + spin_unlock_irqrestore(&dev->event_lock, flags); |
| + |
| + return retval; |
| +} |
| +EXPORT_SYMBOL(input_set_keycode_big); |
| + |
| +/** |
| * input_get_keycode - retrieve keycode currently mapped to a given scancode |
| * @dev: input device which keymap is being queried |
| * @scancode: scancode (or its equivalent for device in question) for which |
| @@ -661,13 +818,35 @@ int input_get_keycode(struct input_dev *dev, |
| unsigned int scancode, unsigned int *keycode) |
| { |
| unsigned long flags; |
| - int retval; |
| |
| - spin_lock_irqsave(&dev->event_lock, flags); |
| - retval = dev->getkeycode(dev, scancode, keycode); |
| - spin_unlock_irqrestore(&dev->event_lock, flags); |
| + if (dev->getkeycode) { |
| + /* |
| + * Use the legacy calls |
| + */ |
| + return dev->getkeycode(dev, scancode, keycode); |
| + } else { |
| + int retval; |
| + struct keycode_table_entry kt_entry; |
| |
| - return retval; |
| + /* |
| + * Userspace is using a legacy call with a driver ported |
| + * to the new way. This is a bad idea with long sparsed |
| + * tables, since lots of the retrieved values will be in |
| + * blank. Also, it makes sense only if the table size is |
| + * lower than 2^32. |
| + */ |
| + memset(&kt_entry, 0, sizeof(kt_entry)); |
| + kt_entry.len = 4; |
| + kt_entry.index = scancode; |
| + kt_entry.scancode = (char *)&scancode; |
| + |
| + spin_lock_irqsave(&dev->event_lock, flags); |
| + retval = dev->getkeycodebig_from_index(dev, &kt_entry); |
| + spin_unlock_irqrestore(&dev->event_lock, flags); |
| + |
| + *keycode = kt_entry.keycode; |
| + return retval; |
| + } |
| } |
| EXPORT_SYMBOL(input_get_keycode); |
| |
| @@ -692,13 +871,42 @@ int input_set_keycode(struct input_dev *dev, |
| |
| spin_lock_irqsave(&dev->event_lock, flags); |
| |
| - retval = dev->getkeycode(dev, scancode, &old_keycode); |
| - if (retval) |
| - goto out; |
| + if (dev->getkeycode) { |
| + /* |
| + * Use the legacy calls |
| + */ |
| + retval = dev->getkeycode(dev, scancode, &old_keycode); |
| + if (retval) |
| + goto out; |
| |
| - retval = dev->setkeycode(dev, scancode, keycode); |
| - if (retval) |
| - goto out; |
| + retval = dev->setkeycode(dev, scancode, keycode); |
| + if (retval) |
| + goto out; |
| + } else { |
| + struct keycode_table_entry kt_entry; |
| + |
| + /* |
| + * Userspace is using a legacy call with a driver ported |
| + * to the new way. This is a bad idea with long sparsed |
| + * tables, since lots of the retrieved values will be in |
| + * blank. Also, it makes sense only if the table size is |
| + * lower than 2^32. |
| + */ |
| + memset(&kt_entry, 0, sizeof(kt_entry)); |
| + kt_entry.len = 4; |
| + kt_entry.scancode = (char *)&scancode; |
| + |
| + retval = dev->getkeycodebig_from_scancode(dev, &kt_entry); |
| + if (retval) |
| + goto out; |
| + |
| + old_keycode = kt_entry.keycode; |
| + kt_entry.keycode = keycode; |
| + |
| + retval = dev->setkeycodebig(dev, &kt_entry); |
| + if (retval) |
| + goto out; |
| + } |
| |
| /* Make sure KEY_RESERVED did not get enabled. */ |
| __clear_bit(KEY_RESERVED, dev->keybit); |
| @@ -1636,11 +1843,17 @@ int input_register_device(struct input_dev *dev) |
| dev->rep[REP_PERIOD] = 33; |
| } |
| |
| - if (!dev->getkeycode) |
| - dev->getkeycode = input_default_getkeycode; |
| + if (!dev->getkeycode) { |
| + if (!dev->getkeycodebig_from_index) |
| + dev->getkeycodebig_from_index = input_default_getkeycode_from_index; |
| + if (!dev->getkeycodebig_from_scancode) |
| + dev->getkeycodebig_from_scancode = input_default_getkeycode_from_scancode; |
| + } |
| |
| - if (!dev->setkeycode) |
| - dev->setkeycode = input_default_setkeycode; |
| + if (!dev->setkeycode) { |
| + if (!dev->setkeycodebig) |
| + dev->setkeycodebig = input_default_setkeycode; |
| + } |
| |
| dev_set_name(&dev->dev, "input%ld", |
| (unsigned long) atomic_inc_return(&input_no) - 1); |
| diff --git a/drivers/media/IR/Kconfig b/drivers/media/IR/Kconfig |
| index d22a8ec..e557ae0 100644 |
| |
| |
| @@ -8,6 +8,17 @@ config VIDEO_IR |
| depends on IR_CORE |
| default IR_CORE |
| |
| +config LIRC |
| + tristate |
| + default y |
| + |
| + ---help--- |
| + Enable this option to build the Linux Infrared Remote |
| + Control (LIRC) core device interface driver. The LIRC |
| + interface passes raw IR to and from userspace, where the |
| + LIRC daemon handles protocol decoding for IR reception ann |
| + encoding for IR transmitting (aka "blasting"). |
| + |
| source "drivers/media/IR/keymaps/Kconfig" |
| |
| config IR_NEC_DECODER |
| @@ -42,6 +53,7 @@ config IR_RC6_DECODER |
| config IR_JVC_DECODER |
| tristate "Enable IR raw decoder for the JVC protocol" |
| depends on IR_CORE |
| + select BITREVERSE |
| default y |
| |
| ---help--- |
| @@ -57,6 +69,16 @@ config IR_SONY_DECODER |
| Enable this option if you have an infrared remote control which |
| uses the Sony protocol, and you need software decoding support. |
| |
| +config IR_LIRC_CODEC |
| + tristate "Enable IR to LIRC bridge" |
| + depends on IR_CORE |
| + depends on LIRC |
| + default y |
| + |
| + ---help--- |
| + Enable this option to pass raw IR to and from userspace via |
| + the LIRC interface. |
| + |
| config IR_IMON |
| tristate "SoundGraph iMON Receiver and Display" |
| depends on USB_ARCH_HAS_HCD |
| @@ -68,3 +90,15 @@ config IR_IMON |
| |
| To compile this driver as a module, choose M here: the |
| module will be called imon. |
| + |
| +config IR_MCEUSB |
| + tristate "Windows Media Center Ed. eHome Infrared Transceiver" |
| + depends on USB_ARCH_HAS_HCD |
| + depends on IR_CORE |
| + select USB |
| + ---help--- |
| + Say Y here if you want to use a Windows Media Center Edition |
| + eHome Infrared Transceiver. |
| + |
| + To compile this driver as a module, choose M here: the |
| + module will be called mceusb. |
| diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile |
| index b998fcc..2ae4f3a 100644 |
| |
| |
| @@ -5,11 +5,14 @@ obj-y += keymaps/ |
| |
| obj-$(CONFIG_IR_CORE) += ir-core.o |
| obj-$(CONFIG_VIDEO_IR) += ir-common.o |
| +obj-$(CONFIG_LIRC) += lirc_dev.o |
| obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o |
| obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o |
| obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o |
| obj-$(CONFIG_IR_JVC_DECODER) += ir-jvc-decoder.o |
| obj-$(CONFIG_IR_SONY_DECODER) += ir-sony-decoder.o |
| +obj-$(CONFIG_IR_LIRC_CODEC) += ir-lirc-codec.o |
| |
| # stand-alone IR receivers/transmitters |
| obj-$(CONFIG_IR_IMON) += imon.o |
| +obj-$(CONFIG_IR_MCEUSB) += mceusb.o |
| diff --git a/drivers/media/IR/imon.c b/drivers/media/IR/imon.c |
| index 4bbd45f..0195dd5 100644 |
| |
| |
| @@ -1943,7 +1943,7 @@ static struct imon_context *imon_init_intf0(struct usb_interface *intf) |
| return ictx; |
| |
| urb_submit_failed: |
| - input_unregister_device(ictx->idev); |
| + ir_input_unregister(ictx->idev); |
| input_free_device(ictx->idev); |
| idev_setup_failed: |
| find_endpoint_failed: |
| @@ -2067,6 +2067,7 @@ static void imon_get_ffdc_type(struct imon_context *ictx) |
| detected_display_type = IMON_DISPLAY_TYPE_VFD; |
| break; |
| /* iMON LCD, MCE IR */ |
| + case 0x9e: |
| case 0x9f: |
| dev_info(ictx->dev, "0xffdc iMON LCD, MCE IR"); |
| detected_display_type = IMON_DISPLAY_TYPE_LCD; |
| @@ -2306,7 +2307,7 @@ static void __devexit imon_disconnect(struct usb_interface *interface) |
| if (ifnum == 0) { |
| ictx->dev_present_intf0 = false; |
| usb_kill_urb(ictx->rx_urb_intf0); |
| - input_unregister_device(ictx->idev); |
| + ir_input_unregister(ictx->idev); |
| if (ictx->display_supported) { |
| if (ictx->display_type == IMON_DISPLAY_TYPE_LCD) |
| usb_deregister_dev(interface, &imon_lcd_class); |
| diff --git a/drivers/media/IR/ir-core-priv.h b/drivers/media/IR/ir-core-priv.h |
| index 9a5e65a..babd520 100644 |
| |
| |
| @@ -22,17 +22,62 @@ |
| struct ir_raw_handler { |
| struct list_head list; |
| |
| + u64 protocols; /* which are handled by this handler */ |
| int (*decode)(struct input_dev *input_dev, struct ir_raw_event event); |
| + |
| + /* These two should only be used by the lirc decoder */ |
| int (*raw_register)(struct input_dev *input_dev); |
| int (*raw_unregister)(struct input_dev *input_dev); |
| }; |
| |
| struct ir_raw_event_ctrl { |
| + struct list_head list; /* to keep track of raw clients */ |
| struct work_struct rx_work; /* for the rx decoding workqueue */ |
| struct kfifo kfifo; /* fifo for the pulse/space durations */ |
| ktime_t last_event; /* when last event occurred */ |
| enum raw_event_type last_type; /* last event type */ |
| struct input_dev *input_dev; /* pointer to the parent input_dev */ |
| + u64 enabled_protocols; /* enabled raw protocol decoders */ |
| + |
| + /* raw decoder state follows */ |
| + struct ir_raw_event prev_ev; |
| + struct nec_dec { |
| + int state; |
| + unsigned count; |
| + u32 bits; |
| + } nec; |
| + struct rc5_dec { |
| + int state; |
| + u32 bits; |
| + unsigned count; |
| + unsigned wanted_bits; |
| + } rc5; |
| + struct rc6_dec { |
| + int state; |
| + u8 header; |
| + u32 body; |
| + bool toggle; |
| + unsigned count; |
| + unsigned wanted_bits; |
| + } rc6; |
| + struct sony_dec { |
| + int state; |
| + u32 bits; |
| + unsigned count; |
| + } sony; |
| + struct jvc_dec { |
| + int state; |
| + u16 bits; |
| + u16 old_bits; |
| + unsigned count; |
| + bool first; |
| + bool toggle; |
| + } jvc; |
| + struct lirc_codec { |
| + struct ir_input_dev *ir_dev; |
| + struct lirc_driver *drv; |
| + int lircdata; |
| + } lirc; |
| }; |
| |
| /* macros for IR decoders */ |
| @@ -74,6 +119,7 @@ void ir_unregister_class(struct input_dev *input_dev); |
| /* |
| * Routines from ir-raw-event.c to be used internally and by decoders |
| */ |
| +u64 ir_raw_get_allowed_protocols(void); |
| int ir_raw_event_register(struct input_dev *input_dev); |
| void ir_raw_event_unregister(struct input_dev *input_dev); |
| int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler); |
| @@ -123,4 +169,12 @@ void ir_raw_init(void); |
| #define load_sony_decode() 0 |
| #endif |
| |
| +/* from ir-lirc-codec.c */ |
| +#ifdef CONFIG_IR_LIRC_CODEC_MODULE |
| +#define load_lirc_codec() request_module("ir-lirc-codec") |
| +#else |
| +#define load_lirc_codec() 0 |
| +#endif |
| + |
| + |
| #endif /* _IR_RAW_EVENT */ |
| diff --git a/drivers/media/IR/ir-jvc-decoder.c b/drivers/media/IR/ir-jvc-decoder.c |
| index 0b80494..8894d8b 100644 |
| |
| |
| @@ -25,10 +25,6 @@ |
| #define JVC_TRAILER_PULSE (1 * JVC_UNIT) |
| #define JVC_TRAILER_SPACE (35 * JVC_UNIT) |
| |
| -/* Used to register jvc_decoder clients */ |
| -static LIST_HEAD(decoder_list); |
| -DEFINE_SPINLOCK(decoder_lock); |
| - |
| enum jvc_state { |
| STATE_INACTIVE, |
| STATE_HEADER_SPACE, |
| @@ -38,87 +34,6 @@ enum jvc_state { |
| STATE_TRAILER_SPACE, |
| }; |
| |
| -struct decoder_data { |
| - struct list_head list; |
| - struct ir_input_dev *ir_dev; |
| - int enabled:1; |
| - |
| - /* State machine control */ |
| - enum jvc_state state; |
| - u16 jvc_bits; |
| - u16 jvc_old_bits; |
| - unsigned count; |
| - bool first; |
| - bool toggle; |
| -}; |
| - |
| - |
| -/** |
| - * get_decoder_data() - gets decoder data |
| - * @input_dev: input device |
| - * |
| - * Returns the struct decoder_data that corresponds to a device |
| - */ |
| -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) |
| -{ |
| - struct decoder_data *data = NULL; |
| - |
| - spin_lock(&decoder_lock); |
| - list_for_each_entry(data, &decoder_list, list) { |
| - if (data->ir_dev == ir_dev) |
| - break; |
| - } |
| - spin_unlock(&decoder_lock); |
| - return data; |
| -} |
| - |
| -static ssize_t store_enabled(struct device *d, |
| - struct device_attribute *mattr, |
| - const char *buf, |
| - size_t len) |
| -{ |
| - unsigned long value; |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (strict_strtoul(buf, 10, &value) || value > 1) |
| - return -EINVAL; |
| - |
| - data->enabled = value; |
| - |
| - return len; |
| -} |
| - |
| -static ssize_t show_enabled(struct device *d, |
| - struct device_attribute *mattr, char *buf) |
| -{ |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (data->enabled) |
| - return sprintf(buf, "1\n"); |
| - else |
| - return sprintf(buf, "0\n"); |
| -} |
| - |
| -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); |
| - |
| -static struct attribute *decoder_attributes[] = { |
| - &dev_attr_enabled.attr, |
| - NULL |
| -}; |
| - |
| -static struct attribute_group decoder_attribute_group = { |
| - .name = "jvc_decoder", |
| - .attrs = decoder_attributes, |
| -}; |
| - |
| /** |
| * ir_jvc_decode() - Decode one JVC pulse or space |
| * @input_dev: the struct input_dev descriptor of the device |
| @@ -128,14 +43,10 @@ static struct attribute_group decoder_attribute_group = { |
| */ |
| static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| { |
| - struct decoder_data *data; |
| struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + struct jvc_dec *data = &ir_dev->raw->jvc; |
| |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (!data->enabled) |
| + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_JVC)) |
| return 0; |
| |
| if (IS_RESET(ev)) { |
| @@ -188,9 +99,9 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| if (ev.pulse) |
| break; |
| |
| - data->jvc_bits <<= 1; |
| + data->bits <<= 1; |
| if (eq_margin(ev.duration, JVC_BIT_1_SPACE, JVC_UNIT / 2)) { |
| - data->jvc_bits |= 1; |
| + data->bits |= 1; |
| decrease_duration(&ev, JVC_BIT_1_SPACE); |
| } else if (eq_margin(ev.duration, JVC_BIT_0_SPACE, JVC_UNIT / 2)) |
| decrease_duration(&ev, JVC_BIT_0_SPACE); |
| @@ -223,13 +134,13 @@ static int ir_jvc_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| |
| if (data->first) { |
| u32 scancode; |
| - scancode = (bitrev8((data->jvc_bits >> 8) & 0xff) << 8) | |
| - (bitrev8((data->jvc_bits >> 0) & 0xff) << 0); |
| + scancode = (bitrev8((data->bits >> 8) & 0xff) << 8) | |
| + (bitrev8((data->bits >> 0) & 0xff) << 0); |
| IR_dprintk(1, "JVC scancode 0x%04x\n", scancode); |
| ir_keydown(input_dev, scancode, data->toggle); |
| data->first = false; |
| - data->jvc_old_bits = data->jvc_bits; |
| - } else if (data->jvc_bits == data->jvc_old_bits) { |
| + data->old_bits = data->bits; |
| + } else if (data->bits == data->old_bits) { |
| IR_dprintk(1, "JVC repeat\n"); |
| ir_repeat(input_dev); |
| } else { |
| @@ -249,54 +160,9 @@ out: |
| return -EINVAL; |
| } |
| |
| -static int ir_jvc_register(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - struct decoder_data *data; |
| - int rc; |
| - |
| - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - if (rc < 0) |
| - return rc; |
| - |
| - data = kzalloc(sizeof(*data), GFP_KERNEL); |
| - if (!data) { |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - return -ENOMEM; |
| - } |
| - |
| - data->ir_dev = ir_dev; |
| - data->enabled = 1; |
| - |
| - spin_lock(&decoder_lock); |
| - list_add_tail(&data->list, &decoder_list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| -static int ir_jvc_unregister(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - static struct decoder_data *data; |
| - |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return 0; |
| - |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - |
| - spin_lock(&decoder_lock); |
| - list_del(&data->list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| static struct ir_raw_handler jvc_handler = { |
| + .protocols = IR_TYPE_JVC, |
| .decode = ir_jvc_decode, |
| - .raw_register = ir_jvc_register, |
| - .raw_unregister = ir_jvc_unregister, |
| }; |
| |
| static int __init ir_jvc_decode_init(void) |
| diff --git a/drivers/media/IR/ir-lirc-codec.c b/drivers/media/IR/ir-lirc-codec.c |
| new file mode 100644 |
| index 0000000..afb1ada |
| |
| |
| @@ -0,0 +1,283 @@ |
| +/* ir-lirc-codec.c - ir-core to classic lirc interface bridge |
| + * |
| + * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com> |
| + * |
| + * 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 version 2 of the License. |
| + * |
| + * 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. |
| + */ |
| + |
| +#include <linux/sched.h> |
| +#include <linux/wait.h> |
| +#include <media/lirc.h> |
| +#include <media/lirc_dev.h> |
| +#include <media/ir-core.h> |
| +#include "ir-core-priv.h" |
| + |
| +#define LIRCBUF_SIZE 256 |
| + |
| +/** |
| + * ir_lirc_decode() - Send raw IR data to lirc_dev to be relayed to the |
| + * lircd userspace daemon for decoding. |
| + * @input_dev: the struct input_dev descriptor of the device |
| + * @duration: the struct ir_raw_event descriptor of the pulse/space |
| + * |
| + * This function returns -EINVAL if the lirc interfaces aren't wired up. |
| + */ |
| +static int ir_lirc_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| +{ |
| + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + |
| + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_LIRC)) |
| + return 0; |
| + |
| + if (!ir_dev->raw->lirc.drv || !ir_dev->raw->lirc.drv->rbuf) |
| + return -EINVAL; |
| + |
| + IR_dprintk(2, "LIRC data transfer started (%uus %s)\n", |
| + TO_US(ev.duration), TO_STR(ev.pulse)); |
| + |
| + ir_dev->raw->lirc.lircdata += ev.duration / 1000; |
| + if (ev.pulse) |
| + ir_dev->raw->lirc.lircdata |= PULSE_BIT; |
| + |
| + lirc_buffer_write(ir_dev->raw->lirc.drv->rbuf, |
| + (unsigned char *) &ir_dev->raw->lirc.lircdata); |
| + wake_up(&ir_dev->raw->lirc.drv->rbuf->wait_poll); |
| + |
| + ir_dev->raw->lirc.lircdata = 0; |
| + |
| + return 0; |
| +} |
| + |
| +static ssize_t ir_lirc_transmit_ir(struct file *file, const char *buf, |
| + size_t n, loff_t *ppos) |
| +{ |
| + struct lirc_codec *lirc; |
| + struct ir_input_dev *ir_dev; |
| + int *txbuf; /* buffer with values to transmit */ |
| + int ret = 0, count; |
| + |
| + lirc = lirc_get_pdata(file); |
| + if (!lirc) |
| + return -EFAULT; |
| + |
| + if (n % sizeof(int)) |
| + return -EINVAL; |
| + |
| + count = n / sizeof(int); |
| + if (count > LIRCBUF_SIZE || count % 2 == 0) |
| + return -EINVAL; |
| + |
| + txbuf = kzalloc(sizeof(int) * LIRCBUF_SIZE, GFP_KERNEL); |
| + if (!txbuf) |
| + return -ENOMEM; |
| + |
| + if (copy_from_user(txbuf, buf, n)) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + |
| + ir_dev = lirc->ir_dev; |
| + if (!ir_dev) { |
| + ret = -EFAULT; |
| + goto out; |
| + } |
| + |
| + if (ir_dev->props && ir_dev->props->tx_ir) |
| + ret = ir_dev->props->tx_ir(ir_dev->props->priv, txbuf, (u32)n); |
| + |
| +out: |
| + kfree(txbuf); |
| + return ret; |
| +} |
| + |
| +static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) |
| +{ |
| + struct lirc_codec *lirc; |
| + struct ir_input_dev *ir_dev; |
| + int ret = 0; |
| + void *drv_data; |
| + unsigned long val; |
| + |
| + lirc = lirc_get_pdata(filep); |
| + if (!lirc) |
| + return -EFAULT; |
| + |
| + ir_dev = lirc->ir_dev; |
| + if (!ir_dev || !ir_dev->props || !ir_dev->props->priv) |
| + return -EFAULT; |
| + |
| + drv_data = ir_dev->props->priv; |
| + |
| + switch (cmd) { |
| + case LIRC_SET_TRANSMITTER_MASK: |
| + ret = get_user(val, (unsigned long *)arg); |
| + if (ret) |
| + return ret; |
| + |
| + if (ir_dev->props && ir_dev->props->s_tx_mask) |
| + ret = ir_dev->props->s_tx_mask(drv_data, (u32)val); |
| + else |
| + return -EINVAL; |
| + break; |
| + |
| + case LIRC_SET_SEND_CARRIER: |
| + ret = get_user(val, (unsigned long *)arg); |
| + if (ret) |
| + return ret; |
| + |
| + if (ir_dev->props && ir_dev->props->s_tx_carrier) |
| + ir_dev->props->s_tx_carrier(drv_data, (u32)val); |
| + else |
| + return -EINVAL; |
| + break; |
| + |
| + case LIRC_GET_SEND_MODE: |
| + val = LIRC_CAN_SEND_PULSE & LIRC_CAN_SEND_MASK; |
| + ret = put_user(val, (unsigned long *)arg); |
| + break; |
| + |
| + case LIRC_SET_SEND_MODE: |
| + ret = get_user(val, (unsigned long *)arg); |
| + if (ret) |
| + return ret; |
| + |
| + if (val != (LIRC_MODE_PULSE & LIRC_CAN_SEND_MASK)) |
| + return -EINVAL; |
| + break; |
| + |
| + default: |
| + return lirc_dev_fop_ioctl(filep, cmd, arg); |
| + } |
| + |
| + return ret; |
| +} |
| + |
| +static int ir_lirc_open(void *data) |
| +{ |
| + return 0; |
| +} |
| + |
| +static void ir_lirc_close(void *data) |
| +{ |
| + return; |
| +} |
| + |
| +static struct file_operations lirc_fops = { |
| + .owner = THIS_MODULE, |
| + .write = ir_lirc_transmit_ir, |
| + .unlocked_ioctl = ir_lirc_ioctl, |
| + .read = lirc_dev_fop_read, |
| + .poll = lirc_dev_fop_poll, |
| + .open = lirc_dev_fop_open, |
| + .release = lirc_dev_fop_close, |
| +}; |
| + |
| +static int ir_lirc_register(struct input_dev *input_dev) |
| +{ |
| + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + struct lirc_driver *drv; |
| + struct lirc_buffer *rbuf; |
| + int rc = -ENOMEM; |
| + unsigned long features; |
| + |
| + drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); |
| + if (!drv) |
| + return rc; |
| + |
| + rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL); |
| + if (!drv) |
| + goto rbuf_alloc_failed; |
| + |
| + rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE); |
| + if (rc) |
| + goto rbuf_init_failed; |
| + |
| + features = LIRC_CAN_REC_MODE2; |
| + if (ir_dev->props->tx_ir) { |
| + features |= LIRC_CAN_SEND_PULSE; |
| + if (ir_dev->props->s_tx_mask) |
| + features |= LIRC_CAN_SET_TRANSMITTER_MASK; |
| + if (ir_dev->props->s_tx_carrier) |
| + features |= LIRC_CAN_SET_SEND_CARRIER; |
| + } |
| + |
| + snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)", |
| + ir_dev->driver_name); |
| + drv->minor = -1; |
| + drv->features = features; |
| + drv->data = &ir_dev->raw->lirc; |
| + drv->rbuf = rbuf; |
| + drv->set_use_inc = &ir_lirc_open; |
| + drv->set_use_dec = &ir_lirc_close; |
| + drv->code_length = sizeof(struct ir_raw_event) * 8; |
| + drv->fops = &lirc_fops; |
| + drv->dev = &ir_dev->dev; |
| + drv->owner = THIS_MODULE; |
| + |
| + drv->minor = lirc_register_driver(drv); |
| + if (drv->minor < 0) { |
| + rc = -ENODEV; |
| + goto lirc_register_failed; |
| + } |
| + |
| + ir_dev->raw->lirc.drv = drv; |
| + ir_dev->raw->lirc.ir_dev = ir_dev; |
| + ir_dev->raw->lirc.lircdata = PULSE_MASK; |
| + |
| + return 0; |
| + |
| +lirc_register_failed: |
| +rbuf_init_failed: |
| + kfree(rbuf); |
| +rbuf_alloc_failed: |
| + kfree(drv); |
| + |
| + return rc; |
| +} |
| + |
| +static int ir_lirc_unregister(struct input_dev *input_dev) |
| +{ |
| + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + struct lirc_codec *lirc = &ir_dev->raw->lirc; |
| + |
| + lirc_unregister_driver(lirc->drv->minor); |
| + lirc_buffer_free(lirc->drv->rbuf); |
| + kfree(lirc->drv); |
| + |
| + return 0; |
| +} |
| + |
| +static struct ir_raw_handler lirc_handler = { |
| + .protocols = IR_TYPE_LIRC, |
| + .decode = ir_lirc_decode, |
| + .raw_register = ir_lirc_register, |
| + .raw_unregister = ir_lirc_unregister, |
| +}; |
| + |
| +static int __init ir_lirc_codec_init(void) |
| +{ |
| + ir_raw_handler_register(&lirc_handler); |
| + |
| + printk(KERN_INFO "IR LIRC bridge handler initialized\n"); |
| + return 0; |
| +} |
| + |
| +static void __exit ir_lirc_codec_exit(void) |
| +{ |
| + ir_raw_handler_unregister(&lirc_handler); |
| +} |
| + |
| +module_init(ir_lirc_codec_init); |
| +module_exit(ir_lirc_codec_exit); |
| + |
| +MODULE_LICENSE("GPL"); |
| +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); |
| +MODULE_AUTHOR("Red Hat Inc. (http://www.redhat.com)"); |
| +MODULE_DESCRIPTION("LIRC IR handler bridge"); |
| diff --git a/drivers/media/IR/ir-nec-decoder.c b/drivers/media/IR/ir-nec-decoder.c |
| index ba79233..52e0f37 100644 |
| |
| |
| @@ -27,10 +27,6 @@ |
| #define NEC_TRAILER_PULSE (1 * NEC_UNIT) |
| #define NEC_TRAILER_SPACE (10 * NEC_UNIT) /* even longer in reality */ |
| |
| -/* Used to register nec_decoder clients */ |
| -static LIST_HEAD(decoder_list); |
| -static DEFINE_SPINLOCK(decoder_lock); |
| - |
| enum nec_state { |
| STATE_INACTIVE, |
| STATE_HEADER_SPACE, |
| @@ -40,84 +36,6 @@ enum nec_state { |
| STATE_TRAILER_SPACE, |
| }; |
| |
| -struct decoder_data { |
| - struct list_head list; |
| - struct ir_input_dev *ir_dev; |
| - int enabled:1; |
| - |
| - /* State machine control */ |
| - enum nec_state state; |
| - u32 nec_bits; |
| - unsigned count; |
| -}; |
| - |
| - |
| -/** |
| - * get_decoder_data() - gets decoder data |
| - * @input_dev: input device |
| - * |
| - * Returns the struct decoder_data that corresponds to a device |
| - */ |
| -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) |
| -{ |
| - struct decoder_data *data = NULL; |
| - |
| - spin_lock(&decoder_lock); |
| - list_for_each_entry(data, &decoder_list, list) { |
| - if (data->ir_dev == ir_dev) |
| - break; |
| - } |
| - spin_unlock(&decoder_lock); |
| - return data; |
| -} |
| - |
| -static ssize_t store_enabled(struct device *d, |
| - struct device_attribute *mattr, |
| - const char *buf, |
| - size_t len) |
| -{ |
| - unsigned long value; |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (strict_strtoul(buf, 10, &value) || value > 1) |
| - return -EINVAL; |
| - |
| - data->enabled = value; |
| - |
| - return len; |
| -} |
| - |
| -static ssize_t show_enabled(struct device *d, |
| - struct device_attribute *mattr, char *buf) |
| -{ |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (data->enabled) |
| - return sprintf(buf, "1\n"); |
| - else |
| - return sprintf(buf, "0\n"); |
| -} |
| - |
| -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); |
| - |
| -static struct attribute *decoder_attributes[] = { |
| - &dev_attr_enabled.attr, |
| - NULL |
| -}; |
| - |
| -static struct attribute_group decoder_attribute_group = { |
| - .name = "nec_decoder", |
| - .attrs = decoder_attributes, |
| -}; |
| - |
| /** |
| * ir_nec_decode() - Decode one NEC pulse or space |
| * @input_dev: the struct input_dev descriptor of the device |
| @@ -127,16 +45,12 @@ static struct attribute_group decoder_attribute_group = { |
| */ |
| static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| { |
| - struct decoder_data *data; |
| struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + struct nec_dec *data = &ir_dev->raw->nec; |
| u32 scancode; |
| u8 address, not_address, command, not_command; |
| |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (!data->enabled) |
| + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_NEC)) |
| return 0; |
| |
| if (IS_RESET(ev)) { |
| @@ -191,9 +105,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| if (ev.pulse) |
| break; |
| |
| - data->nec_bits <<= 1; |
| + data->bits <<= 1; |
| if (eq_margin(ev.duration, NEC_BIT_1_SPACE, NEC_UNIT / 2)) |
| - data->nec_bits |= 1; |
| + data->bits |= 1; |
| else if (!eq_margin(ev.duration, NEC_BIT_0_SPACE, NEC_UNIT / 2)) |
| break; |
| data->count++; |
| @@ -222,14 +136,14 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| if (!geq_margin(ev.duration, NEC_TRAILER_SPACE, NEC_UNIT / 2)) |
| break; |
| |
| - address = bitrev8((data->nec_bits >> 24) & 0xff); |
| - not_address = bitrev8((data->nec_bits >> 16) & 0xff); |
| - command = bitrev8((data->nec_bits >> 8) & 0xff); |
| - not_command = bitrev8((data->nec_bits >> 0) & 0xff); |
| + address = bitrev8((data->bits >> 24) & 0xff); |
| + not_address = bitrev8((data->bits >> 16) & 0xff); |
| + command = bitrev8((data->bits >> 8) & 0xff); |
| + not_command = bitrev8((data->bits >> 0) & 0xff); |
| |
| if ((command ^ not_command) != 0xff) { |
| IR_dprintk(1, "NEC checksum error: received 0x%08x\n", |
| - data->nec_bits); |
| + data->bits); |
| break; |
| } |
| |
| @@ -256,54 +170,9 @@ static int ir_nec_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| return -EINVAL; |
| } |
| |
| -static int ir_nec_register(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - struct decoder_data *data; |
| - int rc; |
| - |
| - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - if (rc < 0) |
| - return rc; |
| - |
| - data = kzalloc(sizeof(*data), GFP_KERNEL); |
| - if (!data) { |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - return -ENOMEM; |
| - } |
| - |
| - data->ir_dev = ir_dev; |
| - data->enabled = 1; |
| - |
| - spin_lock(&decoder_lock); |
| - list_add_tail(&data->list, &decoder_list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| -static int ir_nec_unregister(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - static struct decoder_data *data; |
| - |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return 0; |
| - |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - |
| - spin_lock(&decoder_lock); |
| - list_del(&data->list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| static struct ir_raw_handler nec_handler = { |
| + .protocols = IR_TYPE_NEC, |
| .decode = ir_nec_decode, |
| - .raw_register = ir_nec_register, |
| - .raw_unregister = ir_nec_unregister, |
| }; |
| |
| static int __init ir_nec_decode_init(void) |
| diff --git a/drivers/media/IR/ir-raw-event.c b/drivers/media/IR/ir-raw-event.c |
| index ea68a3f..6f192ef 100644 |
| |
| |
| @@ -20,35 +20,13 @@ |
| /* Define the max number of pulse/space transitions to buffer */ |
| #define MAX_IR_EVENT_SIZE 512 |
| |
| +/* Used to keep track of IR raw clients, protected by ir_raw_handler_lock */ |
| +static LIST_HEAD(ir_raw_client_list); |
| + |
| /* Used to handle IR raw handler extensions */ |
| -static LIST_HEAD(ir_raw_handler_list); |
| static DEFINE_SPINLOCK(ir_raw_handler_lock); |
| - |
| -/** |
| - * RUN_DECODER() - runs an operation on all IR decoders |
| - * @ops: IR raw handler operation to be called |
| - * @arg: arguments to be passed to the callback |
| - * |
| - * Calls ir_raw_handler::ops for all registered IR handlers. It prevents |
| - * new decode addition/removal while running, by locking ir_raw_handler_lock |
| - * mutex. If an error occurs, it stops the ops. Otherwise, it returns a sum |
| - * of the return codes. |
| - */ |
| -#define RUN_DECODER(ops, ...) ({ \ |
| - struct ir_raw_handler *_ir_raw_handler; \ |
| - int _sumrc = 0, _rc; \ |
| - spin_lock(&ir_raw_handler_lock); \ |
| - list_for_each_entry(_ir_raw_handler, &ir_raw_handler_list, list) { \ |
| - if (_ir_raw_handler->ops) { \ |
| - _rc = _ir_raw_handler->ops(__VA_ARGS__); \ |
| - if (_rc < 0) \ |
| - break; \ |
| - _sumrc += _rc; \ |
| - } \ |
| - } \ |
| - spin_unlock(&ir_raw_handler_lock); \ |
| - _sumrc; \ |
| -}) |
| +static LIST_HEAD(ir_raw_handler_list); |
| +static u64 available_protocols; |
| |
| #ifdef MODULE |
| /* Used to load the decoders */ |
| @@ -58,57 +36,17 @@ static struct work_struct wq_load; |
| static void ir_raw_event_work(struct work_struct *work) |
| { |
| struct ir_raw_event ev; |
| + struct ir_raw_handler *handler; |
| struct ir_raw_event_ctrl *raw = |
| container_of(work, struct ir_raw_event_ctrl, rx_work); |
| |
| - while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) |
| - RUN_DECODER(decode, raw->input_dev, ev); |
| -} |
| - |
| -int ir_raw_event_register(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir = input_get_drvdata(input_dev); |
| - int rc; |
| - |
| - ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL); |
| - if (!ir->raw) |
| - return -ENOMEM; |
| - |
| - ir->raw->input_dev = input_dev; |
| - INIT_WORK(&ir->raw->rx_work, ir_raw_event_work); |
| - |
| - rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE, |
| - GFP_KERNEL); |
| - if (rc < 0) { |
| - kfree(ir->raw); |
| - ir->raw = NULL; |
| - return rc; |
| - } |
| - |
| - rc = RUN_DECODER(raw_register, input_dev); |
| - if (rc < 0) { |
| - kfifo_free(&ir->raw->kfifo); |
| - kfree(ir->raw); |
| - ir->raw = NULL; |
| - return rc; |
| + while (kfifo_out(&raw->kfifo, &ev, sizeof(ev)) == sizeof(ev)) { |
| + spin_lock(&ir_raw_handler_lock); |
| + list_for_each_entry(handler, &ir_raw_handler_list, list) |
| + handler->decode(raw->input_dev, ev); |
| + spin_unlock(&ir_raw_handler_lock); |
| + raw->prev_ev = ev; |
| } |
| - |
| - return rc; |
| -} |
| - |
| -void ir_raw_event_unregister(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir = input_get_drvdata(input_dev); |
| - |
| - if (!ir->raw) |
| - return; |
| - |
| - cancel_work_sync(&ir->raw->rx_work); |
| - RUN_DECODER(raw_unregister, input_dev); |
| - |
| - kfifo_free(&ir->raw->kfifo); |
| - kfree(ir->raw); |
| - ir->raw = NULL; |
| } |
| |
| /** |
| @@ -204,23 +142,103 @@ void ir_raw_event_handle(struct input_dev *input_dev) |
| } |
| EXPORT_SYMBOL_GPL(ir_raw_event_handle); |
| |
| +/* used internally by the sysfs interface */ |
| +u64 |
| +ir_raw_get_allowed_protocols() |
| +{ |
| + u64 protocols; |
| + spin_lock(&ir_raw_handler_lock); |
| + protocols = available_protocols; |
| + spin_unlock(&ir_raw_handler_lock); |
| + return protocols; |
| +} |
| + |
| +/* |
| + * Used to (un)register raw event clients |
| + */ |
| +int ir_raw_event_register(struct input_dev *input_dev) |
| +{ |
| + struct ir_input_dev *ir = input_get_drvdata(input_dev); |
| + int rc; |
| + struct ir_raw_handler *handler; |
| + |
| + ir->raw = kzalloc(sizeof(*ir->raw), GFP_KERNEL); |
| + if (!ir->raw) |
| + return -ENOMEM; |
| + |
| + ir->raw->input_dev = input_dev; |
| + INIT_WORK(&ir->raw->rx_work, ir_raw_event_work); |
| + ir->raw->enabled_protocols = ~0; |
| + rc = kfifo_alloc(&ir->raw->kfifo, sizeof(s64) * MAX_IR_EVENT_SIZE, |
| + GFP_KERNEL); |
| + if (rc < 0) { |
| + kfree(ir->raw); |
| + ir->raw = NULL; |
| + return rc; |
| + } |
| + |
| + spin_lock(&ir_raw_handler_lock); |
| + list_add_tail(&ir->raw->list, &ir_raw_client_list); |
| + list_for_each_entry(handler, &ir_raw_handler_list, list) |
| + if (handler->raw_register) |
| + handler->raw_register(ir->raw->input_dev); |
| + spin_unlock(&ir_raw_handler_lock); |
| + |
| + return 0; |
| +} |
| + |
| +void ir_raw_event_unregister(struct input_dev *input_dev) |
| +{ |
| + struct ir_input_dev *ir = input_get_drvdata(input_dev); |
| + struct ir_raw_handler *handler; |
| + |
| + if (!ir->raw) |
| + return; |
| + |
| + cancel_work_sync(&ir->raw->rx_work); |
| + |
| + spin_lock(&ir_raw_handler_lock); |
| + list_del(&ir->raw->list); |
| + list_for_each_entry(handler, &ir_raw_handler_list, list) |
| + if (handler->raw_unregister) |
| + handler->raw_unregister(ir->raw->input_dev); |
| + spin_unlock(&ir_raw_handler_lock); |
| + |
| + kfifo_free(&ir->raw->kfifo); |
| + kfree(ir->raw); |
| + ir->raw = NULL; |
| +} |
| + |
| /* |
| * Extension interface - used to register the IR decoders |
| */ |
| |
| int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) |
| { |
| + struct ir_raw_event_ctrl *raw; |
| + |
| spin_lock(&ir_raw_handler_lock); |
| list_add_tail(&ir_raw_handler->list, &ir_raw_handler_list); |
| + if (ir_raw_handler->raw_register) |
| + list_for_each_entry(raw, &ir_raw_client_list, list) |
| + ir_raw_handler->raw_register(raw->input_dev); |
| + available_protocols |= ir_raw_handler->protocols; |
| spin_unlock(&ir_raw_handler_lock); |
| + |
| return 0; |
| } |
| EXPORT_SYMBOL(ir_raw_handler_register); |
| |
| void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) |
| { |
| + struct ir_raw_event_ctrl *raw; |
| + |
| spin_lock(&ir_raw_handler_lock); |
| list_del(&ir_raw_handler->list); |
| + if (ir_raw_handler->raw_unregister) |
| + list_for_each_entry(raw, &ir_raw_client_list, list) |
| + ir_raw_handler->raw_unregister(raw->input_dev); |
| + available_protocols &= ~ir_raw_handler->protocols; |
| spin_unlock(&ir_raw_handler_lock); |
| } |
| EXPORT_SYMBOL(ir_raw_handler_unregister); |
| @@ -235,6 +253,7 @@ static void init_decoders(struct work_struct *work) |
| load_rc6_decode(); |
| load_jvc_decode(); |
| load_sony_decode(); |
| + load_lirc_codec(); |
| |
| /* If needed, we may later add some init code. In this case, |
| it is needed to change the CONFIG_MODULE test at ir-core.h |
| diff --git a/drivers/media/IR/ir-rc5-decoder.c b/drivers/media/IR/ir-rc5-decoder.c |
| index 23cdb1b..df4770d 100644 |
| |
| |
| @@ -30,10 +30,6 @@ |
| #define RC5_BIT_END (1 * RC5_UNIT) |
| #define RC5X_SPACE (4 * RC5_UNIT) |
| |
| -/* Used to register rc5_decoder clients */ |
| -static LIST_HEAD(decoder_list); |
| -static DEFINE_SPINLOCK(decoder_lock); |
| - |
| enum rc5_state { |
| STATE_INACTIVE, |
| STATE_BIT_START, |
| @@ -42,87 +38,6 @@ enum rc5_state { |
| STATE_FINISHED, |
| }; |
| |
| -struct decoder_data { |
| - struct list_head list; |
| - struct ir_input_dev *ir_dev; |
| - int enabled:1; |
| - |
| - /* State machine control */ |
| - enum rc5_state state; |
| - u32 rc5_bits; |
| - struct ir_raw_event prev_ev; |
| - unsigned count; |
| - unsigned wanted_bits; |
| -}; |
| - |
| - |
| -/** |
| - * get_decoder_data() - gets decoder data |
| - * @input_dev: input device |
| - * |
| - * Returns the struct decoder_data that corresponds to a device |
| - */ |
| - |
| -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) |
| -{ |
| - struct decoder_data *data = NULL; |
| - |
| - spin_lock(&decoder_lock); |
| - list_for_each_entry(data, &decoder_list, list) { |
| - if (data->ir_dev == ir_dev) |
| - break; |
| - } |
| - spin_unlock(&decoder_lock); |
| - return data; |
| -} |
| - |
| -static ssize_t store_enabled(struct device *d, |
| - struct device_attribute *mattr, |
| - const char *buf, |
| - size_t len) |
| -{ |
| - unsigned long value; |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (strict_strtoul(buf, 10, &value) || value > 1) |
| - return -EINVAL; |
| - |
| - data->enabled = value; |
| - |
| - return len; |
| -} |
| - |
| -static ssize_t show_enabled(struct device *d, |
| - struct device_attribute *mattr, char *buf) |
| -{ |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (data->enabled) |
| - return sprintf(buf, "1\n"); |
| - else |
| - return sprintf(buf, "0\n"); |
| -} |
| - |
| -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); |
| - |
| -static struct attribute *decoder_attributes[] = { |
| - &dev_attr_enabled.attr, |
| - NULL |
| -}; |
| - |
| -static struct attribute_group decoder_attribute_group = { |
| - .name = "rc5_decoder", |
| - .attrs = decoder_attributes, |
| -}; |
| - |
| /** |
| * ir_rc5_decode() - Decode one RC-5 pulse or space |
| * @input_dev: the struct input_dev descriptor of the device |
| @@ -132,17 +47,13 @@ static struct attribute_group decoder_attribute_group = { |
| */ |
| static int ir_rc5_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| { |
| - struct decoder_data *data; |
| struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + struct rc5_dec *data = &ir_dev->raw->rc5; |
| u8 toggle; |
| u32 scancode; |
| |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (!data->enabled) |
| - return 0; |
| + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC5)) |
| + return 0; |
| |
| if (IS_RESET(ev)) { |
| data->state = STATE_INACTIVE; |
| @@ -176,16 +87,15 @@ again: |
| if (!eq_margin(ev.duration, RC5_BIT_START, RC5_UNIT / 2)) |
| break; |
| |
| - data->rc5_bits <<= 1; |
| + data->bits <<= 1; |
| if (!ev.pulse) |
| - data->rc5_bits |= 1; |
| + data->bits |= 1; |
| data->count++; |
| - data->prev_ev = ev; |
| data->state = STATE_BIT_END; |
| return 0; |
| |
| case STATE_BIT_END: |
| - if (!is_transition(&ev, &data->prev_ev)) |
| + if (!is_transition(&ev, &ir_dev->raw->prev_ev)) |
| break; |
| |
| if (data->count == data->wanted_bits) |
| @@ -217,11 +127,11 @@ again: |
| if (data->wanted_bits == RC5X_NBITS) { |
| /* RC5X */ |
| u8 xdata, command, system; |
| - xdata = (data->rc5_bits & 0x0003F) >> 0; |
| - command = (data->rc5_bits & 0x00FC0) >> 6; |
| - system = (data->rc5_bits & 0x1F000) >> 12; |
| - toggle = (data->rc5_bits & 0x20000) ? 1 : 0; |
| - command += (data->rc5_bits & 0x01000) ? 0 : 0x40; |
| + xdata = (data->bits & 0x0003F) >> 0; |
| + command = (data->bits & 0x00FC0) >> 6; |
| + system = (data->bits & 0x1F000) >> 12; |
| + toggle = (data->bits & 0x20000) ? 1 : 0; |
| + command += (data->bits & 0x01000) ? 0 : 0x40; |
| scancode = system << 16 | command << 8 | xdata; |
| |
| IR_dprintk(1, "RC5X scancode 0x%06x (toggle: %u)\n", |
| @@ -230,10 +140,10 @@ again: |
| } else { |
| /* RC5 */ |
| u8 command, system; |
| - command = (data->rc5_bits & 0x0003F) >> 0; |
| - system = (data->rc5_bits & 0x007C0) >> 6; |
| - toggle = (data->rc5_bits & 0x00800) ? 1 : 0; |
| - command += (data->rc5_bits & 0x01000) ? 0 : 0x40; |
| + command = (data->bits & 0x0003F) >> 0; |
| + system = (data->bits & 0x007C0) >> 6; |
| + toggle = (data->bits & 0x00800) ? 1 : 0; |
| + command += (data->bits & 0x01000) ? 0 : 0x40; |
| scancode = system << 8 | command; |
| |
| IR_dprintk(1, "RC5 scancode 0x%04x (toggle: %u)\n", |
| @@ -252,54 +162,9 @@ out: |
| return -EINVAL; |
| } |
| |
| -static int ir_rc5_register(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - struct decoder_data *data; |
| - int rc; |
| - |
| - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - if (rc < 0) |
| - return rc; |
| - |
| - data = kzalloc(sizeof(*data), GFP_KERNEL); |
| - if (!data) { |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - return -ENOMEM; |
| - } |
| - |
| - data->ir_dev = ir_dev; |
| - data->enabled = 1; |
| - |
| - spin_lock(&decoder_lock); |
| - list_add_tail(&data->list, &decoder_list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| -static int ir_rc5_unregister(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - static struct decoder_data *data; |
| - |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return 0; |
| - |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - |
| - spin_lock(&decoder_lock); |
| - list_del(&data->list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| static struct ir_raw_handler rc5_handler = { |
| + .protocols = IR_TYPE_RC5, |
| .decode = ir_rc5_decode, |
| - .raw_register = ir_rc5_register, |
| - .raw_unregister = ir_rc5_unregister, |
| }; |
| |
| static int __init ir_rc5_decode_init(void) |
| diff --git a/drivers/media/IR/ir-rc6-decoder.c b/drivers/media/IR/ir-rc6-decoder.c |
| index 2bf479f..f1624b8 100644 |
| |
| |
| @@ -36,10 +36,6 @@ |
| #define RC6_STARTBIT_MASK 0x08 /* for the header bits */ |
| #define RC6_6A_MCE_TOGGLE_MASK 0x8000 /* for the body bits */ |
| |
| -/* Used to register rc6_decoder clients */ |
| -static LIST_HEAD(decoder_list); |
| -static DEFINE_SPINLOCK(decoder_lock); |
| - |
| enum rc6_mode { |
| RC6_MODE_0, |
| RC6_MODE_6A, |
| @@ -58,89 +54,8 @@ enum rc6_state { |
| STATE_FINISHED, |
| }; |
| |
| -struct decoder_data { |
| - struct list_head list; |
| - struct ir_input_dev *ir_dev; |
| - int enabled:1; |
| - |
| - /* State machine control */ |
| - enum rc6_state state; |
| - u8 header; |
| - u32 body; |
| - struct ir_raw_event prev_ev; |
| - bool toggle; |
| - unsigned count; |
| - unsigned wanted_bits; |
| -}; |
| - |
| - |
| -/** |
| - * get_decoder_data() - gets decoder data |
| - * @input_dev: input device |
| - * |
| - * Returns the struct decoder_data that corresponds to a device |
| - */ |
| -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) |
| -{ |
| - struct decoder_data *data = NULL; |
| - |
| - spin_lock(&decoder_lock); |
| - list_for_each_entry(data, &decoder_list, list) { |
| - if (data->ir_dev == ir_dev) |
| - break; |
| - } |
| - spin_unlock(&decoder_lock); |
| - return data; |
| -} |
| - |
| -static ssize_t store_enabled(struct device *d, |
| - struct device_attribute *mattr, |
| - const char *buf, |
| - size_t len) |
| +static enum rc6_mode rc6_mode(struct rc6_dec *data) |
| { |
| - unsigned long value; |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (strict_strtoul(buf, 10, &value) || value > 1) |
| - return -EINVAL; |
| - |
| - data->enabled = value; |
| - |
| - return len; |
| -} |
| - |
| -static ssize_t show_enabled(struct device *d, |
| - struct device_attribute *mattr, char *buf) |
| -{ |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (data->enabled) |
| - return sprintf(buf, "1\n"); |
| - else |
| - return sprintf(buf, "0\n"); |
| -} |
| - |
| -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); |
| - |
| -static struct attribute *decoder_attributes[] = { |
| - &dev_attr_enabled.attr, |
| - NULL |
| -}; |
| - |
| -static struct attribute_group decoder_attribute_group = { |
| - .name = "rc6_decoder", |
| - .attrs = decoder_attributes, |
| -}; |
| - |
| -static enum rc6_mode rc6_mode(struct decoder_data *data) { |
| switch (data->header & RC6_MODE_MASK) { |
| case 0: |
| return RC6_MODE_0; |
| @@ -162,16 +77,12 @@ static enum rc6_mode rc6_mode(struct decoder_data *data) { |
| */ |
| static int ir_rc6_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| { |
| - struct decoder_data *data; |
| struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + struct rc6_dec *data = &ir_dev->raw->rc6; |
| u32 scancode; |
| u8 toggle; |
| |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (!data->enabled) |
| + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_RC6)) |
| return 0; |
| |
| if (IS_RESET(ev)) { |
| @@ -223,12 +134,11 @@ again: |
| if (ev.pulse) |
| data->header |= 1; |
| data->count++; |
| - data->prev_ev = ev; |
| data->state = STATE_HEADER_BIT_END; |
| return 0; |
| |
| case STATE_HEADER_BIT_END: |
| - if (!is_transition(&ev, &data->prev_ev)) |
| + if (!is_transition(&ev, &ir_dev->raw->prev_ev)) |
| break; |
| |
| if (data->count == RC6_HEADER_NBITS) |
| @@ -244,12 +154,11 @@ again: |
| break; |
| |
| data->toggle = ev.pulse; |
| - data->prev_ev = ev; |
| data->state = STATE_TOGGLE_END; |
| return 0; |
| |
| case STATE_TOGGLE_END: |
| - if (!is_transition(&ev, &data->prev_ev) || |
| + if (!is_transition(&ev, &ir_dev->raw->prev_ev) || |
| !geq_margin(ev.duration, RC6_TOGGLE_END, RC6_UNIT / 2)) |
| break; |
| |
| @@ -259,7 +168,6 @@ again: |
| } |
| |
| data->state = STATE_BODY_BIT_START; |
| - data->prev_ev = ev; |
| decrease_duration(&ev, RC6_TOGGLE_END); |
| data->count = 0; |
| |
| @@ -291,13 +199,11 @@ again: |
| if (ev.pulse) |
| data->body |= 1; |
| data->count++; |
| - data->prev_ev = ev; |
| - |
| data->state = STATE_BODY_BIT_END; |
| return 0; |
| |
| case STATE_BODY_BIT_END: |
| - if (!is_transition(&ev, &data->prev_ev)) |
| + if (!is_transition(&ev, &ir_dev->raw->prev_ev)) |
| break; |
| |
| if (data->count == data->wanted_bits) |
| @@ -348,54 +254,9 @@ out: |
| return -EINVAL; |
| } |
| |
| -static int ir_rc6_register(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - struct decoder_data *data; |
| - int rc; |
| - |
| - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - if (rc < 0) |
| - return rc; |
| - |
| - data = kzalloc(sizeof(*data), GFP_KERNEL); |
| - if (!data) { |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - return -ENOMEM; |
| - } |
| - |
| - data->ir_dev = ir_dev; |
| - data->enabled = 1; |
| - |
| - spin_lock(&decoder_lock); |
| - list_add_tail(&data->list, &decoder_list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| -static int ir_rc6_unregister(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - static struct decoder_data *data; |
| - |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return 0; |
| - |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - |
| - spin_lock(&decoder_lock); |
| - list_del(&data->list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| static struct ir_raw_handler rc6_handler = { |
| + .protocols = IR_TYPE_RC6, |
| .decode = ir_rc6_decode, |
| - .raw_register = ir_rc6_register, |
| - .raw_unregister = ir_rc6_unregister, |
| }; |
| |
| static int __init ir_rc6_decode_init(void) |
| diff --git a/drivers/media/IR/ir-sony-decoder.c b/drivers/media/IR/ir-sony-decoder.c |
| index 9f440c5..b9074f0 100644 |
| |
| |
| @@ -23,10 +23,6 @@ |
| #define SONY_BIT_SPACE (1 * SONY_UNIT) |
| #define SONY_TRAILER_SPACE (10 * SONY_UNIT) /* minimum */ |
| |
| -/* Used to register sony_decoder clients */ |
| -static LIST_HEAD(decoder_list); |
| -static DEFINE_SPINLOCK(decoder_lock); |
| - |
| enum sony_state { |
| STATE_INACTIVE, |
| STATE_HEADER_SPACE, |
| @@ -35,84 +31,6 @@ enum sony_state { |
| STATE_FINISHED, |
| }; |
| |
| -struct decoder_data { |
| - struct list_head list; |
| - struct ir_input_dev *ir_dev; |
| - int enabled:1; |
| - |
| - /* State machine control */ |
| - enum sony_state state; |
| - u32 sony_bits; |
| - unsigned count; |
| -}; |
| - |
| - |
| -/** |
| - * get_decoder_data() - gets decoder data |
| - * @input_dev: input device |
| - * |
| - * Returns the struct decoder_data that corresponds to a device |
| - */ |
| -static struct decoder_data *get_decoder_data(struct ir_input_dev *ir_dev) |
| -{ |
| - struct decoder_data *data = NULL; |
| - |
| - spin_lock(&decoder_lock); |
| - list_for_each_entry(data, &decoder_list, list) { |
| - if (data->ir_dev == ir_dev) |
| - break; |
| - } |
| - spin_unlock(&decoder_lock); |
| - return data; |
| -} |
| - |
| -static ssize_t store_enabled(struct device *d, |
| - struct device_attribute *mattr, |
| - const char *buf, |
| - size_t len) |
| -{ |
| - unsigned long value; |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (strict_strtoul(buf, 10, &value) || value > 1) |
| - return -EINVAL; |
| - |
| - data->enabled = value; |
| - |
| - return len; |
| -} |
| - |
| -static ssize_t show_enabled(struct device *d, |
| - struct device_attribute *mattr, char *buf) |
| -{ |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - struct decoder_data *data = get_decoder_data(ir_dev); |
| - |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (data->enabled) |
| - return sprintf(buf, "1\n"); |
| - else |
| - return sprintf(buf, "0\n"); |
| -} |
| - |
| -static DEVICE_ATTR(enabled, S_IRUGO | S_IWUSR, show_enabled, store_enabled); |
| - |
| -static struct attribute *decoder_attributes[] = { |
| - &dev_attr_enabled.attr, |
| - NULL |
| -}; |
| - |
| -static struct attribute_group decoder_attribute_group = { |
| - .name = "sony_decoder", |
| - .attrs = decoder_attributes, |
| -}; |
| - |
| /** |
| * ir_sony_decode() - Decode one Sony pulse or space |
| * @input_dev: the struct input_dev descriptor of the device |
| @@ -122,16 +40,12 @@ static struct attribute_group decoder_attribute_group = { |
| */ |
| static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| { |
| - struct decoder_data *data; |
| struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| + struct sony_dec *data = &ir_dev->raw->sony; |
| u32 scancode; |
| u8 device, subdevice, function; |
| |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return -EINVAL; |
| - |
| - if (!data->enabled) |
| + if (!(ir_dev->raw->enabled_protocols & IR_TYPE_SONY)) |
| return 0; |
| |
| if (IS_RESET(ev)) { |
| @@ -172,9 +86,9 @@ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| if (!ev.pulse) |
| break; |
| |
| - data->sony_bits <<= 1; |
| + data->bits <<= 1; |
| if (eq_margin(ev.duration, SONY_BIT_1_PULSE, SONY_UNIT / 2)) |
| - data->sony_bits |= 1; |
| + data->bits |= 1; |
| else if (!eq_margin(ev.duration, SONY_BIT_0_PULSE, SONY_UNIT / 2)) |
| break; |
| |
| @@ -208,19 +122,19 @@ static int ir_sony_decode(struct input_dev *input_dev, struct ir_raw_event ev) |
| |
| switch (data->count) { |
| case 12: |
| - device = bitrev8((data->sony_bits << 3) & 0xF8); |
| + device = bitrev8((data->bits << 3) & 0xF8); |
| subdevice = 0; |
| - function = bitrev8((data->sony_bits >> 4) & 0xFE); |
| + function = bitrev8((data->bits >> 4) & 0xFE); |
| break; |
| case 15: |
| - device = bitrev8((data->sony_bits >> 0) & 0xFF); |
| + device = bitrev8((data->bits >> 0) & 0xFF); |
| subdevice = 0; |
| - function = bitrev8((data->sony_bits >> 7) & 0xFD); |
| + function = bitrev8((data->bits >> 7) & 0xFD); |
| break; |
| case 20: |
| - device = bitrev8((data->sony_bits >> 5) & 0xF8); |
| - subdevice = bitrev8((data->sony_bits >> 0) & 0xFF); |
| - function = bitrev8((data->sony_bits >> 12) & 0xFE); |
| + device = bitrev8((data->bits >> 5) & 0xF8); |
| + subdevice = bitrev8((data->bits >> 0) & 0xFF); |
| + function = bitrev8((data->bits >> 12) & 0xFE); |
| break; |
| default: |
| IR_dprintk(1, "Sony invalid bitcount %u\n", data->count); |
| @@ -241,54 +155,9 @@ out: |
| return -EINVAL; |
| } |
| |
| -static int ir_sony_register(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - struct decoder_data *data; |
| - int rc; |
| - |
| - rc = sysfs_create_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - if (rc < 0) |
| - return rc; |
| - |
| - data = kzalloc(sizeof(*data), GFP_KERNEL); |
| - if (!data) { |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - return -ENOMEM; |
| - } |
| - |
| - data->ir_dev = ir_dev; |
| - data->enabled = 1; |
| - |
| - spin_lock(&decoder_lock); |
| - list_add_tail(&data->list, &decoder_list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| -static int ir_sony_unregister(struct input_dev *input_dev) |
| -{ |
| - struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); |
| - static struct decoder_data *data; |
| - |
| - data = get_decoder_data(ir_dev); |
| - if (!data) |
| - return 0; |
| - |
| - sysfs_remove_group(&ir_dev->dev.kobj, &decoder_attribute_group); |
| - |
| - spin_lock(&decoder_lock); |
| - list_del(&data->list); |
| - spin_unlock(&decoder_lock); |
| - |
| - return 0; |
| -} |
| - |
| static struct ir_raw_handler sony_handler = { |
| + .protocols = IR_TYPE_SONY, |
| .decode = ir_sony_decode, |
| - .raw_register = ir_sony_register, |
| - .raw_unregister = ir_sony_unregister, |
| }; |
| |
| static int __init ir_sony_decode_init(void) |
| diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c |
| index 2098dd1..a841e51 100644 |
| |
| |
| @@ -34,122 +34,186 @@ static struct class ir_input_class = { |
| }; |
| |
| /** |
| - * show_protocol() - shows the current IR protocol |
| + * show_protocols() - shows the current IR protocol(s) |
| * @d: the device descriptor |
| * @mattr: the device attribute struct (unused) |
| * @buf: a pointer to the output buffer |
| * |
| - * This routine is a callback routine for input read the IR protocol type. |
| - * it is trigged by reading /sys/class/rc/rc?/current_protocol. |
| - * It returns the protocol name, as understood by the driver. |
| + * This routine is a callback routine for input read the IR protocol type(s). |
| + * it is trigged by reading /sys/class/rc/rc?/protocols. |
| + * It returns the protocol names of supported protocols. |
| + * Enabled protocols are printed in brackets. |
| */ |
| -static ssize_t show_protocol(struct device *d, |
| - struct device_attribute *mattr, char *buf) |
| +static ssize_t show_protocols(struct device *d, |
| + struct device_attribute *mattr, char *buf) |
| { |
| - char *s; |
| struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - u64 ir_type = ir_dev->rc_tab.ir_type; |
| - |
| - IR_dprintk(1, "Current protocol is %lld\n", (long long)ir_type); |
| - |
| - /* FIXME: doesn't support multiple protocols at the same time */ |
| - if (ir_type == IR_TYPE_UNKNOWN) |
| - s = "Unknown"; |
| - else if (ir_type == IR_TYPE_RC5) |
| - s = "rc-5"; |
| - else if (ir_type == IR_TYPE_NEC) |
| - s = "nec"; |
| - else if (ir_type == IR_TYPE_RC6) |
| - s = "rc6"; |
| - else if (ir_type == IR_TYPE_JVC) |
| - s = "jvc"; |
| - else if (ir_type == IR_TYPE_SONY) |
| - s = "sony"; |
| - else |
| - s = "other"; |
| + u64 allowed, enabled; |
| + char *tmp = buf; |
| + |
| + if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) { |
| + enabled = ir_dev->rc_tab.ir_type; |
| + allowed = ir_dev->props->allowed_protos; |
| + } else { |
| + enabled = ir_dev->raw->enabled_protocols; |
| + allowed = ir_raw_get_allowed_protocols(); |
| + } |
| |
| - return sprintf(buf, "%s\n", s); |
| + IR_dprintk(1, "allowed - 0x%llx, enabled - 0x%llx\n", |
| + (long long)allowed, |
| + (long long)enabled); |
| + |
| + if (allowed & enabled & IR_TYPE_UNKNOWN) |
| + tmp += sprintf(tmp, "[unknown] "); |
| + else if (allowed & IR_TYPE_UNKNOWN) |
| + tmp += sprintf(tmp, "unknown "); |
| + |
| + if (allowed & enabled & IR_TYPE_RC5) |
| + tmp += sprintf(tmp, "[rc5] "); |
| + else if (allowed & IR_TYPE_RC5) |
| + tmp += sprintf(tmp, "rc5 "); |
| + |
| + if (allowed & enabled & IR_TYPE_NEC) |
| + tmp += sprintf(tmp, "[nec] "); |
| + else if (allowed & IR_TYPE_NEC) |
| + tmp += sprintf(tmp, "nec "); |
| + |
| + if (allowed & enabled & IR_TYPE_RC6) |
| + tmp += sprintf(tmp, "[rc6] "); |
| + else if (allowed & IR_TYPE_RC6) |
| + tmp += sprintf(tmp, "rc6 "); |
| + |
| + if (allowed & enabled & IR_TYPE_JVC) |
| + tmp += sprintf(tmp, "[jvc] "); |
| + else if (allowed & IR_TYPE_JVC) |
| + tmp += sprintf(tmp, "jvc "); |
| + |
| + if (allowed & enabled & IR_TYPE_SONY) |
| + tmp += sprintf(tmp, "[sony] "); |
| + else if (allowed & IR_TYPE_SONY) |
| + tmp += sprintf(tmp, "sony "); |
| + |
| + if (allowed & enabled & IR_TYPE_LIRC) |
| + tmp += sprintf(tmp, "[lirc] "); |
| + else if (allowed & IR_TYPE_LIRC) |
| + tmp += sprintf(tmp, "lirc "); |
| + |
| + if (tmp != buf) |
| + tmp--; |
| + *tmp = '\n'; |
| + return tmp + 1 - buf; |
| } |
| |
| /** |
| - * store_protocol() - shows the current IR protocol |
| + * store_protocols() - changes the current IR protocol(s) |
| * @d: the device descriptor |
| * @mattr: the device attribute struct (unused) |
| * @buf: a pointer to the input buffer |
| * @len: length of the input buffer |
| * |
| * This routine is a callback routine for changing the IR protocol type. |
| - * it is trigged by reading /sys/class/rc/rc?/current_protocol. |
| - * It changes the IR the protocol name, if the IR type is recognized |
| - * by the driver. |
| - * If an unknown protocol name is used, returns -EINVAL. |
| + * It is trigged by writing to /sys/class/rc/rc?/protocols. |
| + * Writing "+proto" will add a protocol to the list of enabled protocols. |
| + * Writing "-proto" will remove a protocol from the list of enabled protocols. |
| + * Writing "proto" will enable only "proto". |
| + * Returns -EINVAL if an invalid protocol combination or unknown protocol name |
| + * is used, otherwise @len. |
| */ |
| -static ssize_t store_protocol(struct device *d, |
| - struct device_attribute *mattr, |
| - const char *data, |
| - size_t len) |
| +static ssize_t store_protocols(struct device *d, |
| + struct device_attribute *mattr, |
| + const char *data, |
| + size_t len) |
| { |
| struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| - u64 ir_type = 0; |
| - int rc = -EINVAL; |
| + bool enable, disable; |
| + const char *tmp; |
| + u64 type; |
| + u64 mask; |
| + int rc; |
| unsigned long flags; |
| - char *buf; |
| - |
| - while ((buf = strsep((char **) &data, " \n")) != NULL) { |
| - if (!strcasecmp(buf, "rc-5") || !strcasecmp(buf, "rc5")) |
| - ir_type |= IR_TYPE_RC5; |
| - if (!strcasecmp(buf, "nec")) |
| - ir_type |= IR_TYPE_NEC; |
| - if (!strcasecmp(buf, "jvc")) |
| - ir_type |= IR_TYPE_JVC; |
| - if (!strcasecmp(buf, "sony")) |
| - ir_type |= IR_TYPE_SONY; |
| + |
| + tmp = skip_spaces(data); |
| + |
| + if (*tmp == '+') { |
| + enable = true; |
| + disable = false; |
| + tmp++; |
| + } else if (*tmp == '-') { |
| + enable = false; |
| + disable = true; |
| + tmp++; |
| + } else { |
| + enable = false; |
| + disable = false; |
| } |
| |
| - if (!ir_type) { |
| + if (!strncasecmp(tmp, "unknown", 7)) { |
| + tmp += 7; |
| + mask = IR_TYPE_UNKNOWN; |
| + } else if (!strncasecmp(tmp, "rc5", 3)) { |
| + tmp += 3; |
| + mask = IR_TYPE_RC5; |
| + } else if (!strncasecmp(tmp, "nec", 3)) { |
| + tmp += 3; |
| + mask = IR_TYPE_NEC; |
| + } else if (!strncasecmp(tmp, "rc6", 3)) { |
| + tmp += 3; |
| + mask = IR_TYPE_RC6; |
| + } else if (!strncasecmp(tmp, "jvc", 3)) { |
| + tmp += 3; |
| + mask = IR_TYPE_JVC; |
| + } else if (!strncasecmp(tmp, "sony", 4)) { |
| + tmp += 4; |
| + mask = IR_TYPE_SONY; |
| + } else if (!strncasecmp(tmp, "lirc", 4)) { |
| + tmp += 4; |
| + mask = IR_TYPE_LIRC; |
| + } else { |
| IR_dprintk(1, "Unknown protocol\n"); |
| return -EINVAL; |
| } |
| |
| - if (ir_dev->props && ir_dev->props->change_protocol) |
| - rc = ir_dev->props->change_protocol(ir_dev->props->priv, |
| - ir_type); |
| - |
| - if (rc < 0) { |
| - IR_dprintk(1, "Error setting protocol to %lld\n", |
| - (long long)ir_type); |
| + tmp = skip_spaces(tmp); |
| + if (*tmp != '\0') { |
| + IR_dprintk(1, "Invalid trailing characters\n"); |
| return -EINVAL; |
| } |
| |
| - spin_lock_irqsave(&ir_dev->rc_tab.lock, flags); |
| - ir_dev->rc_tab.ir_type = ir_type; |
| - spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); |
| + if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) |
| + type = ir_dev->rc_tab.ir_type; |
| + else |
| + type = ir_dev->raw->enabled_protocols; |
| |
| - IR_dprintk(1, "Current protocol(s) is(are) %lld\n", |
| - (long long)ir_type); |
| + if (enable) |
| + type |= mask; |
| + else if (disable) |
| + type &= ~mask; |
| + else |
| + type = mask; |
| |
| - return len; |
| -} |
| + if (ir_dev->props && ir_dev->props->change_protocol) { |
| + rc = ir_dev->props->change_protocol(ir_dev->props->priv, |
| + type); |
| + if (rc < 0) { |
| + IR_dprintk(1, "Error setting protocols to 0x%llx\n", |
| + (long long)type); |
| + return -EINVAL; |
| + } |
| + } |
| |
| -static ssize_t show_supported_protocols(struct device *d, |
| - struct device_attribute *mattr, char *buf) |
| -{ |
| - char *orgbuf = buf; |
| - struct ir_input_dev *ir_dev = dev_get_drvdata(d); |
| + if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) { |
| + spin_lock_irqsave(&ir_dev->rc_tab.lock, flags); |
| + ir_dev->rc_tab.ir_type = type; |
| + spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); |
| + } else { |
| + ir_dev->raw->enabled_protocols = type; |
| + } |
| |
| - /* FIXME: doesn't support multiple protocols at the same time */ |
| - if (ir_dev->props->allowed_protos == IR_TYPE_UNKNOWN) |
| - buf += sprintf(buf, "unknown "); |
| - if (ir_dev->props->allowed_protos & IR_TYPE_RC5) |
| - buf += sprintf(buf, "rc-5 "); |
| - if (ir_dev->props->allowed_protos & IR_TYPE_NEC) |
| - buf += sprintf(buf, "nec "); |
| - if (buf == orgbuf) |
| - buf += sprintf(buf, "other "); |
| |
| - buf += sprintf(buf - 1, "\n"); |
| + IR_dprintk(1, "Current protocol(s): 0x%llx\n", |
| + (long long)type); |
| |
| - return buf - orgbuf; |
| + return len; |
| } |
| |
| #define ADD_HOTPLUG_VAR(fmt, val...) \ |
| @@ -159,7 +223,7 @@ static ssize_t show_supported_protocols(struct device *d, |
| return err; \ |
| } while (0) |
| |
| -static int ir_dev_uevent(struct device *device, struct kobj_uevent_env *env) |
| +static int rc_dev_uevent(struct device *device, struct kobj_uevent_env *env) |
| { |
| struct ir_input_dev *ir_dev = dev_get_drvdata(device); |
| |
| @@ -174,34 +238,26 @@ static int ir_dev_uevent(struct device *device, struct kobj_uevent_env *env) |
| /* |
| * Static device attribute struct with the sysfs attributes for IR's |
| */ |
| -static DEVICE_ATTR(protocol, S_IRUGO | S_IWUSR, |
| - show_protocol, store_protocol); |
| - |
| -static DEVICE_ATTR(supported_protocols, S_IRUGO | S_IWUSR, |
| - show_supported_protocols, NULL); |
| +static DEVICE_ATTR(protocols, S_IRUGO | S_IWUSR, |
| + show_protocols, store_protocols); |
| |
| -static struct attribute *ir_hw_dev_attrs[] = { |
| - &dev_attr_protocol.attr, |
| - &dev_attr_supported_protocols.attr, |
| +static struct attribute *rc_dev_attrs[] = { |
| + &dev_attr_protocols.attr, |
| NULL, |
| }; |
| |
| -static struct attribute_group ir_hw_dev_attr_grp = { |
| - .attrs = ir_hw_dev_attrs, |
| +static struct attribute_group rc_dev_attr_grp = { |
| + .attrs = rc_dev_attrs, |
| }; |
| |
| -static const struct attribute_group *ir_hw_dev_attr_groups[] = { |
| - &ir_hw_dev_attr_grp, |
| +static const struct attribute_group *rc_dev_attr_groups[] = { |
| + &rc_dev_attr_grp, |
| NULL |
| }; |
| |
| static struct device_type rc_dev_type = { |
| - .groups = ir_hw_dev_attr_groups, |
| - .uevent = ir_dev_uevent, |
| -}; |
| - |
| -static struct device_type ir_raw_dev_type = { |
| - .uevent = ir_dev_uevent, |
| + .groups = rc_dev_attr_groups, |
| + .uevent = rc_dev_uevent, |
| }; |
| |
| /** |
| @@ -221,12 +277,7 @@ int ir_register_class(struct input_dev *input_dev) |
| if (unlikely(devno < 0)) |
| return devno; |
| |
| - if (ir_dev->props) { |
| - if (ir_dev->props->driver_type == RC_DRIVER_SCANCODE) |
| - ir_dev->dev.type = &rc_dev_type; |
| - } else |
| - ir_dev->dev.type = &ir_raw_dev_type; |
| - |
| + ir_dev->dev.type = &rc_dev_type; |
| ir_dev->dev.class = &ir_input_class; |
| ir_dev->dev.parent = input_dev->dev.parent; |
| dev_set_name(&ir_dev->dev, "rc%d", devno); |
| diff --git a/drivers/media/IR/keymaps/Makefile b/drivers/media/IR/keymaps/Makefile |
| index aea649f..86d3d1f 100644 |
| |
| |
| @@ -37,6 +37,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ |
| rc-kaiomy.o \ |
| rc-kworld-315u.o \ |
| rc-kworld-plus-tv-analog.o \ |
| + rc-lirc.o \ |
| rc-manli.o \ |
| rc-msi-tvanywhere.o \ |
| rc-msi-tvanywhere-plus.o \ |
| @@ -57,6 +58,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ |
| rc-pv951.o \ |
| rc-rc5-hauppauge-new.o \ |
| rc-rc5-tv.o \ |
| + rc-rc6-mce.o \ |
| rc-real-audio-220-32-keys.o \ |
| rc-tbs-nec.o \ |
| rc-terratec-cinergy-xs.o \ |
| diff --git a/drivers/media/IR/keymaps/rc-lirc.c b/drivers/media/IR/keymaps/rc-lirc.c |
| new file mode 100644 |
| index 0000000..43fcf90 |
| |
| |
| @@ -0,0 +1,41 @@ |
| +/* rc-lirc.c - Empty dummy keytable, for use when its preferred to pass |
| + * all raw IR data to the lirc userspace decoder. |
| + * |
| + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com> |
| + * |
| + * 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. |
| + */ |
| + |
| +#include <media/ir-core.h> |
| + |
| +static struct ir_scancode lirc[] = { |
| + { }, |
| +}; |
| + |
| +static struct rc_keymap lirc_map = { |
| + .map = { |
| + .scan = lirc, |
| + .size = ARRAY_SIZE(lirc), |
| + .ir_type = IR_TYPE_LIRC, |
| + .name = RC_MAP_LIRC, |
| + } |
| +}; |
| + |
| +static int __init init_rc_map_lirc(void) |
| +{ |
| + return ir_register_map(&lirc_map); |
| +} |
| + |
| +static void __exit exit_rc_map_lirc(void) |
| +{ |
| + ir_unregister_map(&lirc_map); |
| +} |
| + |
| +module_init(init_rc_map_lirc) |
| +module_exit(exit_rc_map_lirc) |
| + |
| +MODULE_LICENSE("GPL"); |
| +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); |
| diff --git a/drivers/media/IR/keymaps/rc-rc6-mce.c b/drivers/media/IR/keymaps/rc-rc6-mce.c |
| new file mode 100644 |
| index 0000000..c6726a8 |
| |
| |
| @@ -0,0 +1,105 @@ |
| +/* rc-rc6-mce.c - Keytable for Windows Media Center RC-6 remotes for use |
| + * with the Media Center Edition eHome Infrared Transceiver. |
| + * |
| + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com> |
| + * |
| + * 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. |
| + */ |
| + |
| +#include <media/rc-map.h> |
| + |
| +static struct ir_scancode rc6_mce[] = { |
| + { 0x800f0415, KEY_REWIND }, |
| + { 0x800f0414, KEY_FASTFORWARD }, |
| + { 0x800f041b, KEY_PREVIOUS }, |
| + { 0x800f041a, KEY_NEXT }, |
| + |
| + { 0x800f0416, KEY_PLAY }, |
| + { 0x800f0418, KEY_PAUSE }, |
| + { 0x800f0419, KEY_STOP }, |
| + { 0x800f0417, KEY_RECORD }, |
| + |
| + { 0x800f041e, KEY_UP }, |
| + { 0x800f041f, KEY_DOWN }, |
| + { 0x800f0420, KEY_LEFT }, |
| + { 0x800f0421, KEY_RIGHT }, |
| + |
| + { 0x800f040b, KEY_ENTER }, |
| + { 0x800f0422, KEY_OK }, |
| + { 0x800f0423, KEY_EXIT }, |
| + { 0x800f040a, KEY_DELETE }, |
| + |
| + { 0x800f040e, KEY_MUTE }, |
| + { 0x800f0410, KEY_VOLUMEUP }, |
| + { 0x800f0411, KEY_VOLUMEDOWN }, |
| + { 0x800f0412, KEY_CHANNELUP }, |
| + { 0x800f0413, KEY_CHANNELDOWN }, |
| + |
| + { 0x800f0401, KEY_NUMERIC_1 }, |
| + { 0x800f0402, KEY_NUMERIC_2 }, |
| + { 0x800f0403, KEY_NUMERIC_3 }, |
| + { 0x800f0404, KEY_NUMERIC_4 }, |
| + { 0x800f0405, KEY_NUMERIC_5 }, |
| + { 0x800f0406, KEY_NUMERIC_6 }, |
| + { 0x800f0407, KEY_NUMERIC_7 }, |
| + { 0x800f0408, KEY_NUMERIC_8 }, |
| + { 0x800f0409, KEY_NUMERIC_9 }, |
| + { 0x800f0400, KEY_NUMERIC_0 }, |
| + |
| + { 0x800f041d, KEY_NUMERIC_STAR }, |
| + { 0x800f041c, KEY_NUMERIC_POUND }, |
| + |
| + { 0x800f0446, KEY_TV }, |
| + { 0x800f0447, KEY_AUDIO }, /* My Music */ |
| + { 0x800f0448, KEY_PVR }, /* RecordedTV */ |
| + { 0x800f0449, KEY_CAMERA }, |
| + { 0x800f044a, KEY_VIDEO }, |
| + { 0x800f0424, KEY_DVD }, |
| + { 0x800f0425, KEY_TUNER }, /* LiveTV */ |
| + { 0x800f0450, KEY_RADIO }, |
| + |
| + { 0x800f044c, KEY_LANGUAGE }, |
| + { 0x800f0427, KEY_ZOOM }, /* Aspect */ |
| + |
| + { 0x800f045b, KEY_RED }, |
| + { 0x800f045c, KEY_GREEN }, |
| + { 0x800f045d, KEY_YELLOW }, |
| + { 0x800f045e, KEY_BLUE }, |
| + |
| + { 0x800f040f, KEY_INFO }, |
| + { 0x800f0426, KEY_EPG }, /* Guide */ |
| + { 0x800f045a, KEY_SUBTITLE }, /* Caption/Teletext */ |
| + { 0x800f044d, KEY_TITLE }, |
| + |
| + { 0x800f040c, KEY_POWER }, |
| + { 0x800f040d, KEY_PROG1 }, /* Windows MCE button */ |
| + |
| +}; |
| + |
| +static struct rc_keymap rc6_mce_map = { |
| + .map = { |
| + .scan = rc6_mce, |
| + .size = ARRAY_SIZE(rc6_mce), |
| + .ir_type = IR_TYPE_RC6, |
| + .name = RC_MAP_RC6_MCE, |
| + } |
| +}; |
| + |
| +static int __init init_rc_map_rc6_mce(void) |
| +{ |
| + return ir_register_map(&rc6_mce_map); |
| +} |
| + |
| +static void __exit exit_rc_map_rc6_mce(void) |
| +{ |
| + ir_unregister_map(&rc6_mce_map); |
| +} |
| + |
| +module_init(init_rc_map_rc6_mce) |
| +module_exit(exit_rc_map_rc6_mce) |
| + |
| +MODULE_LICENSE("GPL"); |
| +MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>"); |
| diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c |
| new file mode 100644 |
| index 0000000..899891b |
| |
| |
| @@ -0,0 +1,764 @@ |
| +/* |
| + * LIRC base driver |
| + * |
| + * by Artur Lipowski <alipowski@interia.pl> |
| + * |
| + * 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 <linux/module.h> |
| +#include <linux/kernel.h> |
| +#include <linux/sched.h> |
| +#include <linux/errno.h> |
| +#include <linux/ioctl.h> |
| +#include <linux/fs.h> |
| +#include <linux/poll.h> |
| +#include <linux/completion.h> |
| +#include <linux/errno.h> |
| +#include <linux/mutex.h> |
| +#include <linux/wait.h> |
| +#include <linux/unistd.h> |
| +#include <linux/kthread.h> |
| +#include <linux/bitops.h> |
| +#include <linux/device.h> |
| +#include <linux/cdev.h> |
| + |
| +#include <media/lirc.h> |
| +#include <media/lirc_dev.h> |
| + |
| +static int debug; |
| + |
| +#define IRCTL_DEV_NAME "BaseRemoteCtl" |
| +#define NOPLUG -1 |
| +#define LOGHEAD "lirc_dev (%s[%d]): " |
| + |
| +static dev_t lirc_base_dev; |
| + |
| +struct irctl { |
| + struct lirc_driver d; |
| + int attached; |
| + int open; |
| + |
| + struct mutex irctl_lock; |
| + struct lirc_buffer *buf; |
| + unsigned int chunk_size; |
| + |
| + struct task_struct *task; |
| + long jiffies_to_wait; |
| + |
| + struct cdev cdev; |
| +}; |
| + |
| +static DEFINE_MUTEX(lirc_dev_lock); |
| + |
| +static struct irctl *irctls[MAX_IRCTL_DEVICES]; |
| + |
| +/* Only used for sysfs but defined to void otherwise */ |
| +static struct class *lirc_class; |
| + |
| +/* helper function |
| + * initializes the irctl structure |
| + */ |
| +static void init_irctl(struct irctl *ir) |
| +{ |
| + dev_dbg(ir->d.dev, LOGHEAD "initializing irctl\n", |
| + ir->d.name, ir->d.minor); |
| + mutex_init(&ir->irctl_lock); |
| + ir->d.minor = NOPLUG; |
| +} |
| + |
| +static void cleanup(struct irctl *ir) |
| +{ |
| + dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor); |
| + |
| + device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); |
| + |
| + if (ir->buf != ir->d.rbuf) { |
| + lirc_buffer_free(ir->buf); |
| + kfree(ir->buf); |
| + } |
| + ir->buf = NULL; |
| +} |
| + |
| +/* helper function |
| + * reads key codes from driver and puts them into buffer |
| + * returns 0 on success |
| + */ |
| +static int add_to_buf(struct irctl *ir) |
| +{ |
| + if (ir->d.add_to_buf) { |
| + int res = -ENODATA; |
| + int got_data = 0; |
| + |
| + /* |
| + * service the device as long as it is returning |
| + * data and we have space |
| + */ |
| +get_data: |
| + res = ir->d.add_to_buf(ir->d.data, ir->buf); |
| + if (res == 0) { |
| + got_data++; |
| + goto get_data; |
| + } |
| + |
| + if (res == -ENODEV) |
| + kthread_stop(ir->task); |
| + |
| + return got_data ? 0 : res; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +/* main function of the polling thread |
| + */ |
| +static int lirc_thread(void *irctl) |
| +{ |
| + struct irctl *ir = irctl; |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n", |
| + ir->d.name, ir->d.minor); |
| + |
| + do { |
| + if (ir->open) { |
| + if (ir->jiffies_to_wait) { |
| + set_current_state(TASK_INTERRUPTIBLE); |
| + schedule_timeout(ir->jiffies_to_wait); |
| + } |
| + if (kthread_should_stop()) |
| + break; |
| + if (!add_to_buf(ir)) |
| + wake_up_interruptible(&ir->buf->wait_poll); |
| + } else { |
| + set_current_state(TASK_INTERRUPTIBLE); |
| + schedule(); |
| + } |
| + } while (!kthread_should_stop()); |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n", |
| + ir->d.name, ir->d.minor); |
| + |
| + return 0; |
| +} |
| + |
| + |
| +static struct file_operations fops = { |
| + .owner = THIS_MODULE, |
| + .read = lirc_dev_fop_read, |
| + .write = lirc_dev_fop_write, |
| + .poll = lirc_dev_fop_poll, |
| + .unlocked_ioctl = lirc_dev_fop_ioctl, |
| + .open = lirc_dev_fop_open, |
| + .release = lirc_dev_fop_close, |
| +}; |
| + |
| +static int lirc_cdev_add(struct irctl *ir) |
| +{ |
| + int retval; |
| + struct lirc_driver *d = &ir->d; |
| + |
| + if (d->fops) { |
| + cdev_init(&ir->cdev, d->fops); |
| + ir->cdev.owner = d->owner; |
| + } else { |
| + cdev_init(&ir->cdev, &fops); |
| + ir->cdev.owner = THIS_MODULE; |
| + } |
| + kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor); |
| + |
| + retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); |
| + if (retval) |
| + kobject_put(&ir->cdev.kobj); |
| + |
| + return retval; |
| +} |
| + |
| +int lirc_register_driver(struct lirc_driver *d) |
| +{ |
| + struct irctl *ir; |
| + int minor; |
| + int bytes_in_key; |
| + unsigned int chunk_size; |
| + unsigned int buffer_size; |
| + int err; |
| + |
| + if (!d) { |
| + printk(KERN_ERR "lirc_dev: lirc_register_driver: " |
| + "driver pointer must be not NULL!\n"); |
| + err = -EBADRQC; |
| + goto out; |
| + } |
| + |
| + if (MAX_IRCTL_DEVICES <= d->minor) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "\"minor\" must be between 0 and %d (%d)!\n", |
| + MAX_IRCTL_DEVICES-1, d->minor); |
| + err = -EBADRQC; |
| + goto out; |
| + } |
| + |
| + if (1 > d->code_length || (BUFLEN * 8) < d->code_length) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "code length in bits for minor (%d) " |
| + "must be less than %d!\n", |
| + d->minor, BUFLEN * 8); |
| + err = -EBADRQC; |
| + goto out; |
| + } |
| + |
| + dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n", |
| + d->sample_rate); |
| + if (d->sample_rate) { |
| + if (2 > d->sample_rate || HZ < d->sample_rate) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "sample_rate must be between 2 and %d!\n", HZ); |
| + err = -EBADRQC; |
| + goto out; |
| + } |
| + if (!d->add_to_buf) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "add_to_buf cannot be NULL when " |
| + "sample_rate is set\n"); |
| + err = -EBADRQC; |
| + goto out; |
| + } |
| + } else if (!(d->fops && d->fops->read) && !d->rbuf) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "fops->read and rbuf cannot all be NULL!\n"); |
| + err = -EBADRQC; |
| + goto out; |
| + } else if (!d->rbuf) { |
| + if (!(d->fops && d->fops->read && d->fops->poll && |
| + d->fops->unlocked_ioctl)) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "neither read, poll nor unlocked_ioctl can be NULL!\n"); |
| + err = -EBADRQC; |
| + goto out; |
| + } |
| + } |
| + |
| + mutex_lock(&lirc_dev_lock); |
| + |
| + minor = d->minor; |
| + |
| + if (minor < 0) { |
| + /* find first free slot for driver */ |
| + for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) |
| + if (!irctls[minor]) |
| + break; |
| + if (MAX_IRCTL_DEVICES == minor) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "no free slots for drivers!\n"); |
| + err = -ENOMEM; |
| + goto out_lock; |
| + } |
| + } else if (irctls[minor]) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "minor (%d) just registered!\n", minor); |
| + err = -EBUSY; |
| + goto out_lock; |
| + } |
| + |
| + ir = kzalloc(sizeof(struct irctl), GFP_KERNEL); |
| + if (!ir) { |
| + err = -ENOMEM; |
| + goto out_lock; |
| + } |
| + init_irctl(ir); |
| + irctls[minor] = ir; |
| + d->minor = minor; |
| + |
| + if (d->sample_rate) { |
| + ir->jiffies_to_wait = HZ / d->sample_rate; |
| + } else { |
| + /* it means - wait for external event in task queue */ |
| + ir->jiffies_to_wait = 0; |
| + } |
| + |
| + /* some safety check 8-) */ |
| + d->name[sizeof(d->name)-1] = '\0'; |
| + |
| + bytes_in_key = BITS_TO_LONGS(d->code_length) + |
| + (d->code_length % 8 ? 1 : 0); |
| + buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; |
| + chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; |
| + |
| + if (d->rbuf) { |
| + ir->buf = d->rbuf; |
| + } else { |
| + ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); |
| + if (!ir->buf) { |
| + err = -ENOMEM; |
| + goto out_lock; |
| + } |
| + err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); |
| + if (err) { |
| + kfree(ir->buf); |
| + goto out_lock; |
| + } |
| + } |
| + ir->chunk_size = ir->buf->chunk_size; |
| + |
| + if (d->features == 0) |
| + d->features = LIRC_CAN_REC_LIRCCODE; |
| + |
| + ir->d = *d; |
| + ir->d.minor = minor; |
| + |
| + device_create(lirc_class, ir->d.dev, |
| + MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, |
| + "lirc%u", ir->d.minor); |
| + |
| + if (d->sample_rate) { |
| + /* try to fire up polling thread */ |
| + ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev"); |
| + if (IS_ERR(ir->task)) { |
| + dev_err(d->dev, "lirc_dev: lirc_register_driver: " |
| + "cannot run poll thread for minor = %d\n", |
| + d->minor); |
| + err = -ECHILD; |
| + goto out_sysfs; |
| + } |
| + } |
| + |
| + err = lirc_cdev_add(ir); |
| + if (err) |
| + goto out_sysfs; |
| + |
| + ir->attached = 1; |
| + mutex_unlock(&lirc_dev_lock); |
| + |
| + dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", |
| + ir->d.name, ir->d.minor); |
| + return minor; |
| + |
| +out_sysfs: |
| + device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); |
| +out_lock: |
| + mutex_unlock(&lirc_dev_lock); |
| +out: |
| + return err; |
| +} |
| +EXPORT_SYMBOL(lirc_register_driver); |
| + |
| +int lirc_unregister_driver(int minor) |
| +{ |
| + struct irctl *ir; |
| + |
| + if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { |
| + printk(KERN_ERR "lirc_dev: lirc_unregister_driver: " |
| + "\"minor (%d)\" must be between 0 and %d!\n", |
| + minor, MAX_IRCTL_DEVICES-1); |
| + return -EBADRQC; |
| + } |
| + |
| + ir = irctls[minor]; |
| + |
| + mutex_lock(&lirc_dev_lock); |
| + |
| + if (ir->d.minor != minor) { |
| + printk(KERN_ERR "lirc_dev: lirc_unregister_driver: " |
| + "minor (%d) device not registered!", minor); |
| + mutex_unlock(&lirc_dev_lock); |
| + return -ENOENT; |
| + } |
| + |
| + /* end up polling thread */ |
| + if (ir->task) |
| + kthread_stop(ir->task); |
| + |
| + dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", |
| + ir->d.name, ir->d.minor); |
| + |
| + ir->attached = 0; |
| + if (ir->open) { |
| + dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", |
| + ir->d.name, ir->d.minor); |
| + wake_up_interruptible(&ir->buf->wait_poll); |
| + mutex_lock(&ir->irctl_lock); |
| + ir->d.set_use_dec(ir->d.data); |
| + module_put(ir->d.owner); |
| + mutex_unlock(&ir->irctl_lock); |
| + cdev_del(&ir->cdev); |
| + } else { |
| + cleanup(ir); |
| + cdev_del(&ir->cdev); |
| + kfree(ir); |
| + irctls[minor] = NULL; |
| + } |
| + |
| + mutex_unlock(&lirc_dev_lock); |
| + |
| + return 0; |
| +} |
| +EXPORT_SYMBOL(lirc_unregister_driver); |
| + |
| +int lirc_dev_fop_open(struct inode *inode, struct file *file) |
| +{ |
| + struct irctl *ir; |
| + int retval = 0; |
| + |
| + if (iminor(inode) >= MAX_IRCTL_DEVICES) { |
| + printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n", |
| + iminor(inode)); |
| + return -ENODEV; |
| + } |
| + |
| + if (mutex_lock_interruptible(&lirc_dev_lock)) |
| + return -ERESTARTSYS; |
| + |
| + ir = irctls[iminor(inode)]; |
| + if (!ir) { |
| + retval = -ENODEV; |
| + goto error; |
| + } |
| + file->private_data = ir; |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); |
| + |
| + if (ir->d.minor == NOPLUG) { |
| + retval = -ENODEV; |
| + goto error; |
| + } |
| + |
| + if (ir->open) { |
| + retval = -EBUSY; |
| + goto error; |
| + } |
| + |
| + if (try_module_get(ir->d.owner)) { |
| + ++ir->open; |
| + retval = ir->d.set_use_inc(ir->d.data); |
| + |
| + if (retval) { |
| + module_put(ir->d.owner); |
| + --ir->open; |
| + } else { |
| + lirc_buffer_clear(ir->buf); |
| + } |
| + if (ir->task) |
| + wake_up_process(ir->task); |
| + } |
| + |
| +error: |
| + if (ir) |
| + dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n", |
| + ir->d.name, ir->d.minor, retval); |
| + |
| + mutex_unlock(&lirc_dev_lock); |
| + |
| + return retval; |
| +} |
| +EXPORT_SYMBOL(lirc_dev_fop_open); |
| + |
| +int lirc_dev_fop_close(struct inode *inode, struct file *file) |
| +{ |
| + struct irctl *ir = irctls[iminor(inode)]; |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); |
| + |
| + WARN_ON(mutex_lock_killable(&lirc_dev_lock)); |
| + |
| + --ir->open; |
| + if (ir->attached) { |
| + ir->d.set_use_dec(ir->d.data); |
| + module_put(ir->d.owner); |
| + } else { |
| + cleanup(ir); |
| + irctls[ir->d.minor] = NULL; |
| + kfree(ir); |
| + } |
| + |
| + mutex_unlock(&lirc_dev_lock); |
| + |
| + return 0; |
| +} |
| +EXPORT_SYMBOL(lirc_dev_fop_close); |
| + |
| +unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) |
| +{ |
| + struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; |
| + unsigned int ret; |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor); |
| + |
| + if (!ir->attached) { |
| + mutex_unlock(&ir->irctl_lock); |
| + return POLLERR; |
| + } |
| + |
| + poll_wait(file, &ir->buf->wait_poll, wait); |
| + |
| + if (ir->buf) |
| + if (lirc_buffer_empty(ir->buf)) |
| + ret = 0; |
| + else |
| + ret = POLLIN | POLLRDNORM; |
| + else |
| + ret = POLLERR; |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", |
| + ir->d.name, ir->d.minor, ret); |
| + |
| + return ret; |
| +} |
| +EXPORT_SYMBOL(lirc_dev_fop_poll); |
| + |
| +long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
| +{ |
| + unsigned long mode; |
| + int result = 0; |
| + struct irctl *ir = file->private_data; |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", |
| + ir->d.name, ir->d.minor, cmd); |
| + |
| + if (ir->d.minor == NOPLUG || !ir->attached) { |
| + dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", |
| + ir->d.name, ir->d.minor); |
| + return -ENODEV; |
| + } |
| + |
| + mutex_lock(&ir->irctl_lock); |
| + |
| + switch (cmd) { |
| + case LIRC_GET_FEATURES: |
| + result = put_user(ir->d.features, (unsigned long *)arg); |
| + break; |
| + case LIRC_GET_REC_MODE: |
| + if (!(ir->d.features & LIRC_CAN_REC_MASK)) { |
| + result = -ENOSYS; |
| + break; |
| + } |
| + |
| + result = put_user(LIRC_REC2MODE |
| + (ir->d.features & LIRC_CAN_REC_MASK), |
| + (unsigned long *)arg); |
| + break; |
| + case LIRC_SET_REC_MODE: |
| + if (!(ir->d.features & LIRC_CAN_REC_MASK)) { |
| + result = -ENOSYS; |
| + break; |
| + } |
| + |
| + result = get_user(mode, (unsigned long *)arg); |
| + if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) |
| + result = -EINVAL; |
| + /* |
| + * FIXME: We should actually set the mode somehow but |
| + * for now, lirc_serial doesn't support mode changing either |
| + */ |
| + break; |
| + case LIRC_GET_LENGTH: |
| + result = put_user(ir->d.code_length, (unsigned long *)arg); |
| + break; |
| + case LIRC_GET_MIN_TIMEOUT: |
| + if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || |
| + ir->d.min_timeout == 0) { |
| + result = -ENOSYS; |
| + break; |
| + } |
| + |
| + result = put_user(ir->d.min_timeout, (unsigned long *)arg); |
| + break; |
| + case LIRC_GET_MAX_TIMEOUT: |
| + if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || |
| + ir->d.max_timeout == 0) { |
| + result = -ENOSYS; |
| + break; |
| + } |
| + |
| + result = put_user(ir->d.max_timeout, (unsigned long *)arg); |
| + break; |
| + default: |
| + result = -EINVAL; |
| + } |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n", |
| + ir->d.name, ir->d.minor, result); |
| + |
| + mutex_unlock(&ir->irctl_lock); |
| + |
| + return result; |
| +} |
| +EXPORT_SYMBOL(lirc_dev_fop_ioctl); |
| + |
| +ssize_t lirc_dev_fop_read(struct file *file, |
| + char *buffer, |
| + size_t length, |
| + loff_t *ppos) |
| +{ |
| + struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; |
| + unsigned char buf[ir->chunk_size]; |
| + int ret = 0, written = 0; |
| + DECLARE_WAITQUEUE(wait, current); |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); |
| + |
| + if (mutex_lock_interruptible(&ir->irctl_lock)) |
| + return -ERESTARTSYS; |
| + if (!ir->attached) { |
| + mutex_unlock(&ir->irctl_lock); |
| + return -ENODEV; |
| + } |
| + |
| + if (length % ir->chunk_size) { |
| + dev_dbg(ir->d.dev, LOGHEAD "read result = -EINVAL\n", |
| + ir->d.name, ir->d.minor); |
| + mutex_unlock(&ir->irctl_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 < length && ret == 0) { |
| + if (lirc_buffer_empty(ir->buf)) { |
| + /* According to the read(2) man page, 'written' can be |
| + * returned as less than 'length', instead of blocking |
| + * again, returning -EWOULDBLOCK, or returning |
| + * -ERESTARTSYS */ |
| + if (written) |
| + break; |
| + if (file->f_flags & O_NONBLOCK) { |
| + ret = -EWOULDBLOCK; |
| + break; |
| + } |
| + if (signal_pending(current)) { |
| + ret = -ERESTARTSYS; |
| + break; |
| + } |
| + |
| + mutex_unlock(&ir->irctl_lock); |
| + schedule(); |
| + set_current_state(TASK_INTERRUPTIBLE); |
| + |
| + if (mutex_lock_interruptible(&ir->irctl_lock)) { |
| + ret = -ERESTARTSYS; |
| + remove_wait_queue(&ir->buf->wait_poll, &wait); |
| + set_current_state(TASK_RUNNING); |
| + goto out_unlocked; |
| + } |
| + |
| + if (!ir->attached) { |
| + ret = -ENODEV; |
| + break; |
| + } |
| + } else { |
| + lirc_buffer_read(ir->buf, buf); |
| + ret = copy_to_user((void *)buffer+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->irctl_lock); |
| + |
| +out_unlocked: |
| + dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n", |
| + ir->d.name, ir->d.minor, ret ? "-EFAULT" : "OK", ret); |
| + |
| + return ret ? ret : written; |
| +} |
| +EXPORT_SYMBOL(lirc_dev_fop_read); |
| + |
| +void *lirc_get_pdata(struct file *file) |
| +{ |
| + void *data = NULL; |
| + |
| + if (file && file->f_dentry && file->f_dentry->d_inode && |
| + file->f_dentry->d_inode->i_rdev) { |
| + struct irctl *ir; |
| + ir = irctls[iminor(file->f_dentry->d_inode)]; |
| + data = ir->d.data; |
| + } |
| + |
| + return data; |
| +} |
| +EXPORT_SYMBOL(lirc_get_pdata); |
| + |
| + |
| +ssize_t lirc_dev_fop_write(struct file *file, const char *buffer, |
| + size_t length, loff_t *ppos) |
| +{ |
| + struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)]; |
| + |
| + dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor); |
| + |
| + if (!ir->attached) |
| + return -ENODEV; |
| + |
| + return -EINVAL; |
| +} |
| +EXPORT_SYMBOL(lirc_dev_fop_write); |
| + |
| + |
| +static int __init lirc_dev_init(void) |
| +{ |
| + int retval; |
| + |
| + lirc_class = class_create(THIS_MODULE, "lirc"); |
| + if (IS_ERR(lirc_class)) { |
| + retval = PTR_ERR(lirc_class); |
| + printk(KERN_ERR "lirc_dev: class_create failed\n"); |
| + goto error; |
| + } |
| + |
| + retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, |
| + IRCTL_DEV_NAME); |
| + if (retval) { |
| + class_destroy(lirc_class); |
| + printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n"); |
| + goto error; |
| + } |
| + |
| + |
| + printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, " |
| + "major %d \n", MAJOR(lirc_base_dev)); |
| + |
| +error: |
| + return retval; |
| +} |
| + |
| + |
| + |
| +static void __exit lirc_dev_exit(void) |
| +{ |
| + class_destroy(lirc_class); |
| + unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); |
| + printk(KERN_INFO "lirc_dev: module unloaded\n"); |
| +} |
| + |
| +module_init(lirc_dev_init); |
| +module_exit(lirc_dev_exit); |
| + |
| +MODULE_DESCRIPTION("LIRC base driver module"); |
| +MODULE_AUTHOR("Artur Lipowski"); |
| +MODULE_LICENSE("GPL"); |
| + |
| +module_param(debug, bool, S_IRUGO | S_IWUSR); |
| +MODULE_PARM_DESC(debug, "Enable debugging messages"); |
| diff --git a/drivers/media/IR/mceusb.c b/drivers/media/IR/mceusb.c |
| new file mode 100644 |
| index 0000000..78bf7f7 |
| |
| |
| @@ -0,0 +1,1143 @@ |
| +/* |
| + * Driver for USB Windows Media Center Ed. eHome Infrared Transceivers |
| + * |
| + * Copyright (c) 2010 by Jarod Wilson <jarod@redhat.com> |
| + * |
| + * Based on the original lirc_mceusb and lirc_mceusb2 drivers, by Dan |
| + * Conti, Martin Blatter and Daniel Melander, the latter of which was |
| + * in turn also based on the lirc_atiusb driver by Paul Miller. The |
| + * two mce drivers were merged into one by Jarod Wilson, with transmit |
| + * support for the 1st-gen device added primarily by Patrick Calhoun, |
| + * with a bit of tweaks by Jarod. Debugging improvements and proper |
| + * support for what appears to be 3rd-gen hardware added by Jarod. |
| + * Initial port from lirc driver to ir-core drivery by Jarod, based |
| + * partially on a port to an earlier proposed IR infrastructure by |
| + * Jon Smirl, which included enhancements and simplifications to the |
| + * incoming IR buffer parsing routines. |
| + * |
| + * |
| + * 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 <linux/device.h> |
| +#include <linux/module.h> |
| +#include <linux/slab.h> |
| +#include <linux/usb.h> |
| +#include <linux/input.h> |
| +#include <media/ir-core.h> |
| +#include <media/ir-common.h> |
| + |
| +#define DRIVER_VERSION "1.91" |
| +#define DRIVER_AUTHOR "Jarod Wilson <jarod@wilsonet.com>" |
| +#define DRIVER_DESC "Windows Media Center Ed. eHome Infrared Transceiver " \ |
| + "device driver" |
| +#define DRIVER_NAME "mceusb" |
| + |
| +#define USB_BUFLEN 32 /* USB reception buffer length */ |
| +#define USB_CTRL_MSG_SZ 2 /* Size of usb ctrl msg on gen1 hw */ |
| +#define MCE_G1_INIT_MSGS 40 /* Init messages on gen1 hw to throw out */ |
| + |
| +/* MCE constants */ |
| +#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */ |
| +#define MCE_TIME_UNIT 50 /* Approx 50us resolution */ |
| +#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */ |
| +#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */ |
| +#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */ |
| +#define MCE_CONTROL_HEADER 0x9F /* MCE status header */ |
| +#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */ |
| +#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */ |
| +#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */ |
| +#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */ |
| +#define MCE_PULSE_MASK 0x7F /* Pulse mask */ |
| +#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */ |
| +#define MCE_PACKET_LENGTH_MASK 0x1F /* Packet length mask */ |
| + |
| + |
| +/* module parameters */ |
| +#ifdef CONFIG_USB_DEBUG |
| +static int debug = 1; |
| +#else |
| +static int debug; |
| +#endif |
| + |
| +/* general constants */ |
| +#define SEND_FLAG_IN_PROGRESS 1 |
| +#define SEND_FLAG_COMPLETE 2 |
| +#define RECV_FLAG_IN_PROGRESS 3 |
| +#define RECV_FLAG_COMPLETE 4 |
| + |
| +#define MCEUSB_RX 1 |
| +#define MCEUSB_TX 2 |
| + |
| +#define VENDOR_PHILIPS 0x0471 |
| +#define VENDOR_SMK 0x0609 |
| +#define VENDOR_TATUNG 0x1460 |
| +#define VENDOR_GATEWAY 0x107b |
| +#define VENDOR_SHUTTLE 0x1308 |
| +#define VENDOR_SHUTTLE2 0x051c |
| +#define VENDOR_MITSUMI 0x03ee |
| +#define VENDOR_TOPSEED 0x1784 |
| +#define VENDOR_RICAVISION 0x179d |
| +#define VENDOR_ITRON 0x195d |
| +#define VENDOR_FIC 0x1509 |
| +#define VENDOR_LG 0x043e |
| +#define VENDOR_MICROSOFT 0x045e |
| +#define VENDOR_FORMOSA 0x147a |
| +#define VENDOR_FINTEK 0x1934 |
| +#define VENDOR_PINNACLE 0x2304 |
| +#define VENDOR_ECS 0x1019 |
| +#define VENDOR_WISTRON 0x0fb8 |
| +#define VENDOR_COMPRO 0x185b |
| +#define VENDOR_NORTHSTAR 0x04eb |
| +#define VENDOR_REALTEK 0x0bda |
| +#define VENDOR_TIVO 0x105a |
| + |
| +static struct usb_device_id mceusb_dev_table[] = { |
| + /* Original Microsoft MCE IR Transceiver (often HP-branded) */ |
| + { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, |
| + /* Philips Infrared Transceiver - Sahara branded */ |
| + { USB_DEVICE(VENDOR_PHILIPS, 0x0608) }, |
| + /* Philips Infrared Transceiver - HP branded */ |
| + { USB_DEVICE(VENDOR_PHILIPS, 0x060c) }, |
| + /* Philips SRM5100 */ |
| + { USB_DEVICE(VENDOR_PHILIPS, 0x060d) }, |
| + /* Philips Infrared Transceiver - Omaura */ |
| + { USB_DEVICE(VENDOR_PHILIPS, 0x060f) }, |
| + /* Philips Infrared Transceiver - Spinel plus */ |
| + { USB_DEVICE(VENDOR_PHILIPS, 0x0613) }, |
| + /* Philips eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_PHILIPS, 0x0815) }, |
| + /* Realtek MCE IR Receiver */ |
| + { USB_DEVICE(VENDOR_REALTEK, 0x0161) }, |
| + /* SMK/Toshiba G83C0004D410 */ |
| + { USB_DEVICE(VENDOR_SMK, 0x031d) }, |
| + /* SMK eHome Infrared Transceiver (Sony VAIO) */ |
| + { USB_DEVICE(VENDOR_SMK, 0x0322) }, |
| + /* bundled with Hauppauge PVR-150 */ |
| + { USB_DEVICE(VENDOR_SMK, 0x0334) }, |
| + /* SMK eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_SMK, 0x0338) }, |
| + /* Tatung eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_TATUNG, 0x9150) }, |
| + /* Shuttle eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) }, |
| + /* Shuttle eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) }, |
| + /* Gateway eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_GATEWAY, 0x3009) }, |
| + /* Mitsumi */ |
| + { USB_DEVICE(VENDOR_MITSUMI, 0x2501) }, |
| + /* Topseed eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0001) }, |
| + /* Topseed HP eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0006) }, |
| + /* Topseed eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0007) }, |
| + /* Topseed eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, |
| + /* Topseed eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x000a) }, |
| + /* Topseed eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0011) }, |
| + /* Ricavision internal Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_RICAVISION, 0x0010) }, |
| + /* Itron ione Libra Q-11 */ |
| + { USB_DEVICE(VENDOR_ITRON, 0x7002) }, |
| + /* FIC eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_FIC, 0x9242) }, |
| + /* LG eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_LG, 0x9803) }, |
| + /* Microsoft MCE Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) }, |
| + /* Formosa eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_FORMOSA, 0xe015) }, |
| + /* Formosa21 / eHome Infrared Receiver */ |
| + { USB_DEVICE(VENDOR_FORMOSA, 0xe016) }, |
| + /* Formosa aim / Trust MCE Infrared Receiver */ |
| + { USB_DEVICE(VENDOR_FORMOSA, 0xe017) }, |
| + /* Formosa Industrial Computing / Beanbag Emulation Device */ |
| + { USB_DEVICE(VENDOR_FORMOSA, 0xe018) }, |
| + /* Formosa21 / eHome Infrared Receiver */ |
| + { USB_DEVICE(VENDOR_FORMOSA, 0xe03a) }, |
| + /* Formosa Industrial Computing AIM IR605/A */ |
| + { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) }, |
| + /* Formosa Industrial Computing */ |
| + { USB_DEVICE(VENDOR_FORMOSA, 0xe03e) }, |
| + /* Fintek eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_FINTEK, 0x0602) }, |
| + /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */ |
| + { USB_DEVICE(VENDOR_FINTEK, 0x0702) }, |
| + /* Pinnacle Remote Kit */ |
| + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, |
| + /* Elitegroup Computer Systems IR */ |
| + { USB_DEVICE(VENDOR_ECS, 0x0f38) }, |
| + /* Wistron Corp. eHome Infrared Receiver */ |
| + { USB_DEVICE(VENDOR_WISTRON, 0x0002) }, |
| + /* Compro K100 */ |
| + { USB_DEVICE(VENDOR_COMPRO, 0x3020) }, |
| + /* Compro K100 v2 */ |
| + { USB_DEVICE(VENDOR_COMPRO, 0x3082) }, |
| + /* Northstar Systems, Inc. eHome Infrared Transceiver */ |
| + { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) }, |
| + /* TiVo PC IR Receiver */ |
| + { USB_DEVICE(VENDOR_TIVO, 0x2000) }, |
| + /* Terminating entry */ |
| + { } |
| +}; |
| + |
| +static struct usb_device_id gen3_list[] = { |
| + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, |
| + {} |
| +}; |
| + |
| +static struct usb_device_id microsoft_gen1_list[] = { |
| + { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, |
| + {} |
| +}; |
| + |
| +static struct usb_device_id std_tx_mask_list[] = { |
| + { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) }, |
| + { USB_DEVICE(VENDOR_PHILIPS, 0x060c) }, |
| + { USB_DEVICE(VENDOR_SMK, 0x031d) }, |
| + { USB_DEVICE(VENDOR_SMK, 0x0322) }, |
| + { USB_DEVICE(VENDOR_SMK, 0x0334) }, |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0001) }, |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0006) }, |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0007) }, |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0008) }, |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x000a) }, |
| + { USB_DEVICE(VENDOR_TOPSEED, 0x0011) }, |
| + { USB_DEVICE(VENDOR_PINNACLE, 0x0225) }, |
| + {} |
| +}; |
| + |
| +/* data structure for each usb transceiver */ |
| +struct mceusb_dev { |
| + /* ir-core bits */ |
| + struct ir_input_dev *irdev; |
| + struct ir_dev_props *props; |
| + struct ir_raw_event rawir; |
| + |
| + /* core device bits */ |
| + struct device *dev; |
| + struct input_dev *idev; |
| + |
| + /* usb */ |
| + struct usb_device *usbdev; |
| + struct urb *urb_in; |
| + struct usb_endpoint_descriptor *usb_ep_in; |
| + struct usb_endpoint_descriptor *usb_ep_out; |
| + |
| + /* buffers and dma */ |
| + unsigned char *buf_in; |
| + unsigned int len_in; |
| + u8 cmd; /* MCE command type */ |
| + u8 rem; /* Remaining IR data bytes in packet */ |
| + dma_addr_t dma_in; |
| + dma_addr_t dma_out; |
| + |
| + struct { |
| + u32 connected:1; |
| + u32 tx_mask_inverted:1; |
| + u32 microsoft_gen1:1; |
| + u32 reserved:29; |
| + } flags; |
| + |
| + /* transmit support */ |
| + int send_flags; |
| + u32 carrier; |
| + unsigned char tx_mask; |
| + |
| + char name[128]; |
| + char phys[64]; |
| +}; |
| + |
| +/* |
| + * MCE Device Command Strings |
| + * Device command responses vary from device to device... |
| + * - DEVICE_RESET resets the hardware to its default state |
| + * - GET_REVISION fetches the hardware/software revision, common |
| + * replies are ff 0b 45 ff 1b 08 and ff 0b 50 ff 1b 42 |
| + * - GET_CARRIER_FREQ gets the carrier mode and frequency of the |
| + * device, with replies in the form of 9f 06 MM FF, where MM is 0-3, |
| + * meaning clk of 10000000, 2500000, 625000 or 156250, and FF is |
| + * ((clk / frequency) - 1) |
| + * - GET_RX_TIMEOUT fetches the receiver timeout in units of 50us, |
| + * response in the form of 9f 0c msb lsb |
| + * - GET_TX_BITMASK fetches the transmitter bitmask, replies in |
| + * the form of 9f 08 bm, where bm is the bitmask |
| + * - GET_RX_SENSOR fetches the RX sensor setting -- long-range |
| + * general use one or short-range learning one, in the form of |
| + * 9f 14 ss, where ss is either 01 for long-range or 02 for short |
| + * - SET_CARRIER_FREQ sets a new carrier mode and frequency |
| + * - SET_TX_BITMASK sets the transmitter bitmask |
| + * - SET_RX_TIMEOUT sets the receiver timeout |
| + * - SET_RX_SENSOR sets which receiver sensor to use |
| + */ |
| +static char DEVICE_RESET[] = {0x00, 0xff, 0xaa}; |
| +static char GET_REVISION[] = {0xff, 0x0b}; |
| +static char GET_UNKNOWN[] = {0xff, 0x18}; |
| +static char GET_UNKNOWN2[] = {0x9f, 0x05}; |
| +static char GET_CARRIER_FREQ[] = {0x9f, 0x07}; |
| +static char GET_RX_TIMEOUT[] = {0x9f, 0x0d}; |
| +static char GET_TX_BITMASK[] = {0x9f, 0x13}; |
| +static char GET_RX_SENSOR[] = {0x9f, 0x15}; |
| +/* sub in desired values in lower byte or bytes for full command */ |
| +/* FIXME: make use of these for transmit. |
| +static char SET_CARRIER_FREQ[] = {0x9f, 0x06, 0x00, 0x00}; |
| +static char SET_TX_BITMASK[] = {0x9f, 0x08, 0x00}; |
| +static char SET_RX_TIMEOUT[] = {0x9f, 0x0c, 0x00, 0x00}; |
| +static char SET_RX_SENSOR[] = {0x9f, 0x14, 0x00}; |
| +*/ |
| + |
| +static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, |
| + int len, bool out) |
| +{ |
| + char codes[USB_BUFLEN * 3 + 1]; |
| + char inout[9]; |
| + int i; |
| + u8 cmd, subcmd, data1, data2; |
| + struct device *dev = ir->dev; |
| + int idx = 0; |
| + |
| + /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ |
| + if (ir->flags.microsoft_gen1 && !out) |
| + idx = 2; |
| + |
| + if (len <= idx) |
| + return; |
| + |
| + for (i = 0; i < len && i < USB_BUFLEN; i++) |
| + snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF); |
| + |
| + dev_info(dev, "%sx data: %s (length=%d)\n", |
| + (out ? "t" : "r"), codes, len); |
| + |
| + if (out) |
| + strcpy(inout, "Request\0"); |
| + else |
| + strcpy(inout, "Got\0"); |
| + |
| + cmd = buf[idx] & 0xff; |
| + subcmd = buf[idx + 1] & 0xff; |
| + data1 = buf[idx + 2] & 0xff; |
| + data2 = buf[idx + 3] & 0xff; |
| + |
| + switch (cmd) { |
| + case 0x00: |
| + if (subcmd == 0xff && data1 == 0xaa) |
| + dev_info(dev, "Device reset requested\n"); |
| + else |
| + dev_info(dev, "Unknown command 0x%02x 0x%02x\n", |
| + cmd, subcmd); |
| + break; |
| + case 0xff: |
| + switch (subcmd) { |
| + case 0x0b: |
| + if (len == 2) |
| + dev_info(dev, "Get hw/sw rev?\n"); |
| + else |
| + dev_info(dev, "hw/sw rev 0x%02x 0x%02x " |
| + "0x%02x 0x%02x\n", data1, data2, |
| + buf[idx + 4], buf[idx + 5]); |
| + break; |
| + case 0xaa: |
| + dev_info(dev, "Device reset requested\n"); |
| + break; |
| + case 0xfe: |
| + dev_info(dev, "Previous command not supported\n"); |
| + break; |
| + case 0x18: |
| + case 0x1b: |
| + default: |
| + dev_info(dev, "Unknown command 0x%02x 0x%02x\n", |
| + cmd, subcmd); |
| + break; |
| + } |
| + break; |
| + case 0x9f: |
| + switch (subcmd) { |
| + case 0x03: |
| + dev_info(dev, "Ping\n"); |
| + break; |
| + case 0x04: |
| + dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n", |
| + data1, data2); |
| + break; |
| + case 0x06: |
| + dev_info(dev, "%s carrier mode and freq of " |
| + "0x%02x 0x%02x\n", inout, data1, data2); |
| + break; |
| + case 0x07: |
| + dev_info(dev, "Get carrier mode and freq\n"); |
| + break; |
| + case 0x08: |
| + dev_info(dev, "%s transmit blaster mask of 0x%02x\n", |
| + inout, data1); |
| + break; |
| + case 0x0c: |
| + /* value is in units of 50us, so x*50/100 or x/2 ms */ |
| + dev_info(dev, "%s receive timeout of %d ms\n", |
| + inout, ((data1 << 8) | data2) / 2); |
| + break; |
| + case 0x0d: |
| + dev_info(dev, "Get receive timeout\n"); |
| + break; |
| + case 0x13: |
| + dev_info(dev, "Get transmit blaster mask\n"); |
| + break; |
| + case 0x14: |
| + dev_info(dev, "%s %s-range receive sensor in use\n", |
| + inout, data1 == 0x02 ? "short" : "long"); |
| + break; |
| + case 0x15: |
| + if (len == 2) |
| + dev_info(dev, "Get receive sensor\n"); |
| + else |
| + dev_info(dev, "Received pulse count is %d\n", |
| + ((data1 << 8) | data2)); |
| + break; |
| + case 0xfe: |
| + dev_info(dev, "Error! Hardware is likely wedged...\n"); |
| + break; |
| + case 0x05: |
| + case 0x09: |
| + case 0x0f: |
| + default: |
| + dev_info(dev, "Unknown command 0x%02x 0x%02x\n", |
| + cmd, subcmd); |
| + break; |
| + } |
| + break; |
| + default: |
| + break; |
| + } |
| +} |
| + |
| +static void usb_async_callback(struct urb *urb, struct pt_regs *regs) |
| +{ |
| + struct mceusb_dev *ir; |
| + int len; |
| + |
| + if (!urb) |
| + return; |
| + |
| + ir = urb->context; |
| + if (ir) { |
| + len = urb->actual_length; |
| + |
| + dev_dbg(ir->dev, "callback called (status=%d len=%d)\n", |
| + urb->status, len); |
| + |
| + if (debug) |
| + mceusb_dev_printdata(ir, urb->transfer_buffer, |
| + len, true); |
| + } |
| + |
| +} |
| + |
| +/* request incoming or send outgoing usb packet - used to initialize remote */ |
| +static void mce_request_packet(struct mceusb_dev *ir, |
| + struct usb_endpoint_descriptor *ep, |
| + unsigned char *data, int size, int urb_type) |
| +{ |
| + int res; |
| + struct urb *async_urb; |
| + struct device *dev = ir->dev; |
| + unsigned char *async_buf; |
| + |
| + if (urb_type == MCEUSB_TX) { |
| + async_urb = usb_alloc_urb(0, GFP_KERNEL); |
| + if (unlikely(!async_urb)) { |
| + dev_err(dev, "Error, couldn't allocate urb!\n"); |
| + return; |
| + } |
| + |
| + async_buf = kzalloc(size, GFP_KERNEL); |
| + if (!async_buf) { |
| + dev_err(dev, "Error, couldn't allocate buf!\n"); |
| + usb_free_urb(async_urb); |
| + return; |
| + } |
| + |
| + /* outbound data */ |
| + usb_fill_int_urb(async_urb, ir->usbdev, |
| + usb_sndintpipe(ir->usbdev, ep->bEndpointAddress), |
| + async_buf, size, (usb_complete_t) usb_async_callback, |
| + ir, ep->bInterval); |
| + memcpy(async_buf, data, size); |
| + |
| + } else if (urb_type == MCEUSB_RX) { |
| + /* standard request */ |
| + async_urb = ir->urb_in; |
| + ir->send_flags = RECV_FLAG_IN_PROGRESS; |
| + |
| + } else { |
| + dev_err(dev, "Error! Unknown urb type %d\n", urb_type); |
| + return; |
| + } |
| + |
| + dev_dbg(dev, "receive request called (size=%#x)\n", size); |
| + |
| + async_urb->transfer_buffer_length = size; |
| + async_urb->dev = ir->usbdev; |
| + |
| + res = usb_submit_urb(async_urb, GFP_ATOMIC); |
| + if (res) { |
| + dev_dbg(dev, "receive request FAILED! (res=%d)\n", res); |
| + return; |
| + } |
| + dev_dbg(dev, "receive request complete (res=%d)\n", res); |
| +} |
| + |
| +static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size) |
| +{ |
| + mce_request_packet(ir, ir->usb_ep_out, data, size, MCEUSB_TX); |
| +} |
| + |
| +static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size) |
| +{ |
| + mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_RX); |
| +} |
| + |
| +/* Send data out the IR blaster port(s) */ |
| +static int mceusb_tx_ir(void *priv, int *txbuf, u32 n) |
| +{ |
| + struct mceusb_dev *ir = priv; |
| + int i, ret = 0; |
| + int count, cmdcount = 0; |
| + unsigned char *cmdbuf; /* MCE command buffer */ |
| + long signal_duration = 0; /* Singnal length in us */ |
| + struct timeval start_time, end_time; |
| + |
| + do_gettimeofday(&start_time); |
| + |
| + count = n / sizeof(int); |
| + |
| + cmdbuf = kzalloc(sizeof(int) * MCE_CMDBUF_SIZE, GFP_KERNEL); |
| + if (!cmdbuf) |
| + return -ENOMEM; |
| + |
| + /* MCE tx init header */ |
| + cmdbuf[cmdcount++] = MCE_CONTROL_HEADER; |
| + cmdbuf[cmdcount++] = 0x08; |
| + cmdbuf[cmdcount++] = ir->tx_mask; |
| + |
| + /* Generate mce packet data */ |
| + for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) { |
| + signal_duration += txbuf[i]; |
| + txbuf[i] = txbuf[i] / MCE_TIME_UNIT; |
| + |
| + do { /* loop to support long pulses/spaces > 127*50us=6.35ms */ |
| + |
| + /* Insert mce packet header every 4th entry */ |
| + if ((cmdcount < MCE_CMDBUF_SIZE) && |
| + (cmdcount - MCE_TX_HEADER_LENGTH) % |
| + MCE_CODE_LENGTH == 0) |
| + cmdbuf[cmdcount++] = MCE_PACKET_HEADER; |
| + |
| + /* Insert mce packet data */ |
| + if (cmdcount < MCE_CMDBUF_SIZE) |
| + cmdbuf[cmdcount++] = |
| + (txbuf[i] < MCE_PULSE_BIT ? |
| + txbuf[i] : MCE_MAX_PULSE_LENGTH) | |
| + (i & 1 ? 0x00 : MCE_PULSE_BIT); |
| + else { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + |
| + } while ((txbuf[i] > MCE_MAX_PULSE_LENGTH) && |
| + (txbuf[i] -= MCE_MAX_PULSE_LENGTH)); |
| + } |
| + |
| + /* Fix packet length in last header */ |
| + cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] = |
| + 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1; |
| + |
| + /* Check if we have room for the empty packet at the end */ |
| + if (cmdcount >= MCE_CMDBUF_SIZE) { |
| + ret = -EINVAL; |
| + goto out; |
| + } |
| + |
| + /* All mce commands end with an empty packet (0x80) */ |
| + cmdbuf[cmdcount++] = 0x80; |
| + |
| + /* Transmit the command to the mce device */ |
| + mce_async_out(ir, cmdbuf, cmdcount); |
| + |
| + /* |
| + * The lircd gap calculation expects the write function to |
| + * wait the time it takes for the ircommand to be sent before |
| + * it returns. |
| + */ |
| + do_gettimeofday(&end_time); |
| + signal_duration -= (end_time.tv_usec - start_time.tv_usec) + |
| + (end_time.tv_sec - start_time.tv_sec) * 1000000; |
| + |
| + /* delay with the closest number of ticks */ |
| + set_current_state(TASK_INTERRUPTIBLE); |
| + schedule_timeout(usecs_to_jiffies(signal_duration)); |
| + |
| +out: |
| + kfree(cmdbuf); |
| + return ret ? ret : n; |
| +} |
| + |
| +/* Sets active IR outputs -- mce devices typically (all?) have two */ |
| +static int mceusb_set_tx_mask(void *priv, u32 mask) |
| +{ |
| + struct mceusb_dev *ir = priv; |
| + |
| + if (ir->flags.tx_mask_inverted) |
| + ir->tx_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1; |
| + else |
| + ir->tx_mask = mask; |
| + |
| + return 0; |
| +} |
| + |
| +/* Sets the send carrier frequency and mode */ |
| +static int mceusb_set_tx_carrier(void *priv, u32 carrier) |
| +{ |
| + struct mceusb_dev *ir = priv; |
| + int clk = 10000000; |
| + int prescaler = 0, divisor = 0; |
| + unsigned char cmdbuf[4] = { 0x9f, 0x06, 0x00, 0x00 }; |
| + |
| + /* Carrier has changed */ |
| + if (ir->carrier != carrier) { |
| + |
| + if (carrier == 0) { |
| + ir->carrier = carrier; |
| + cmdbuf[2] = 0x01; |
| + cmdbuf[3] = 0x80; |
| + dev_dbg(ir->dev, "%s: disabling carrier " |
| + "modulation\n", __func__); |
| + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); |
| + return carrier; |
| + } |
| + |
| + for (prescaler = 0; prescaler < 4; ++prescaler) { |
| + divisor = (clk >> (2 * prescaler)) / carrier; |
| + if (divisor <= 0xFF) { |
| + ir->carrier = carrier; |
| + cmdbuf[2] = prescaler; |
| + cmdbuf[3] = divisor; |
| + dev_dbg(ir->dev, "%s: requesting %u HZ " |
| + "carrier\n", __func__, carrier); |
| + |
| + /* Transmit new carrier to mce device */ |
| + mce_async_out(ir, cmdbuf, sizeof(cmdbuf)); |
| + return carrier; |
| + } |
| + } |
| + |
| + return -EINVAL; |
| + |
| + } |
| + |
| + return carrier; |
| +} |
| + |
| +static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len) |
| +{ |
| + struct ir_raw_event rawir = { .pulse = false, .duration = 0 }; |
| + int i, start_index = 0; |
| + u8 hdr = MCE_CONTROL_HEADER; |
| + |
| + /* skip meaningless 0xb1 0x60 header bytes on orig receiver */ |
| + if (ir->flags.microsoft_gen1) |
| + start_index = 2; |
| + |
| + for (i = start_index; i < buf_len;) { |
| + if (ir->rem == 0) { |
| + /* decode mce packets of the form (84),AA,BB,CC,DD */ |
| + /* IR data packets can span USB messages - rem */ |
| + hdr = ir->buf_in[i]; |
| + ir->rem = (hdr & MCE_PACKET_LENGTH_MASK); |
| + ir->cmd = (hdr & ~MCE_PACKET_LENGTH_MASK); |
| + dev_dbg(ir->dev, "New data. rem: 0x%02x, cmd: 0x%02x\n", |
| + ir->rem, ir->cmd); |
| + i++; |
| + } |
| + |
| + /* don't process MCE commands */ |
| + if (hdr == MCE_CONTROL_HEADER || hdr == 0xff) { |
| + ir->rem = 0; |
| + return; |
| + } |
| + |
| + for (; (ir->rem > 0) && (i < buf_len); i++) { |
| + ir->rem--; |
| + |
| + rawir.pulse = ((ir->buf_in[i] & MCE_PULSE_BIT) != 0); |
| + rawir.duration = (ir->buf_in[i] & MCE_PULSE_MASK) |
| + * MCE_TIME_UNIT * 1000; |
| + |
| + if ((ir->buf_in[i] & MCE_PULSE_MASK) == 0x7f) { |
| + if (ir->rawir.pulse == rawir.pulse) |
| + ir->rawir.duration += rawir.duration; |
| + else { |
| + ir->rawir.duration = rawir.duration; |
| + ir->rawir.pulse = rawir.pulse; |
| + } |
| + continue; |
| + } |
| + rawir.duration += ir->rawir.duration; |
| + ir->rawir.duration = 0; |
| + ir->rawir.pulse = rawir.pulse; |
| + |
| + dev_dbg(ir->dev, "Storing %s with duration %d\n", |
| + rawir.pulse ? "pulse" : "space", |
| + rawir.duration); |
| + |
| + ir_raw_event_store(ir->idev, &rawir); |
| + } |
| + |
| + if (ir->buf_in[i] == 0x80 || ir->buf_in[i] == 0x9f) |
| + ir->rem = 0; |
| + |
| + dev_dbg(ir->dev, "calling ir_raw_event_handle\n"); |
| + ir_raw_event_handle(ir->idev); |
| + } |
| +} |
| + |
| +static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs) |
| +{ |
| + struct mceusb_dev *ir; |
| + int buf_len; |
| + |
| + if (!urb) |
| + return; |
| + |
| + ir = urb->context; |
| + if (!ir) { |
| + usb_unlink_urb(urb); |
| + return; |
| + } |
| + |
| + buf_len = urb->actual_length; |
| + |
| + if (debug) |
| + mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false); |
| + |
| + if (ir->send_flags == RECV_FLAG_IN_PROGRESS) { |
| + ir->send_flags = SEND_FLAG_COMPLETE; |
| + dev_dbg(&ir->irdev->dev, "setup answer received %d bytes\n", |
| + buf_len); |
| + } |
| + |
| + switch (urb->status) { |
| + /* success */ |
| + case 0: |
| + mceusb_process_ir_data(ir, buf_len); |
| + break; |
| + |
| + case -ECONNRESET: |
| + case -ENOENT: |
| + case -ESHUTDOWN: |
| + usb_unlink_urb(urb); |
| + return; |
| + |
| + case -EPIPE: |
| + default: |
| + break; |
| + } |
| + |
| + usb_submit_urb(urb, GFP_ATOMIC); |
| +} |
| + |
| +static void mceusb_gen1_init(struct mceusb_dev *ir) |
| +{ |
| + int ret; |
| + int maxp = ir->len_in; |
| + struct device *dev = ir->dev; |
| + char *data; |
| + |
| + data = kzalloc(USB_CTRL_MSG_SZ, GFP_KERNEL); |
| + if (!data) { |
| + dev_err(dev, "%s: memory allocation failed!\n", __func__); |
| + return; |
| + } |
| + |
| + /* |
| + * This is a strange one. Windows issues a set address to the device |
| + * on the receive control pipe and expect a certain value pair back |
| + */ |
| + ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), |
| + USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0, |
| + data, USB_CTRL_MSG_SZ, HZ * 3); |
| + dev_dbg(dev, "%s - ret = %d\n", __func__, ret); |
| + dev_dbg(dev, "%s - data[0] = %d, data[1] = %d\n", |
| + __func__, data[0], data[1]); |
| + |
| + /* set feature: bit rate 38400 bps */ |
| + ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), |
| + USB_REQ_SET_FEATURE, USB_TYPE_VENDOR, |
| + 0xc04e, 0x0000, NULL, 0, HZ * 3); |
| + |
| + dev_dbg(dev, "%s - ret = %d\n", __func__, ret); |
| + |
| + /* bRequest 4: set char length to 8 bits */ |
| + ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), |
| + 4, USB_TYPE_VENDOR, |
| + 0x0808, 0x0000, NULL, 0, HZ * 3); |
| + dev_dbg(dev, "%s - retB = %d\n", __func__, ret); |
| + |
| + /* bRequest 2: set handshaking to use DTR/DSR */ |
| + ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0), |
| + 2, USB_TYPE_VENDOR, |
| + 0x0000, 0x0100, NULL, 0, HZ * 3); |
| + dev_dbg(dev, "%s - retC = %d\n", __func__, ret); |
| + |
| + /* device reset */ |
| + mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET)); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + /* get hw/sw revision? */ |
| + mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + kfree(data); |
| +}; |
| + |
| +static void mceusb_gen2_init(struct mceusb_dev *ir) |
| +{ |
| + int maxp = ir->len_in; |
| + |
| + /* device reset */ |
| + mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET)); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + /* get hw/sw revision? */ |
| + mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION)); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + /* unknown what the next two actually return... */ |
| + mce_async_out(ir, GET_UNKNOWN, sizeof(GET_UNKNOWN)); |
| + mce_sync_in(ir, NULL, maxp); |
| + mce_async_out(ir, GET_UNKNOWN2, sizeof(GET_UNKNOWN2)); |
| + mce_sync_in(ir, NULL, maxp); |
| +} |
| + |
| +static void mceusb_get_parameters(struct mceusb_dev *ir) |
| +{ |
| + int maxp = ir->len_in; |
| + |
| + /* get the carrier and frequency */ |
| + mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ)); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + /* get the transmitter bitmask */ |
| + mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK)); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + /* get receiver timeout value */ |
| + mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT)); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + /* get receiver sensor setting */ |
| + mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR)); |
| + mce_sync_in(ir, NULL, maxp); |
| +} |
| + |
| +static struct input_dev *mceusb_init_input_dev(struct mceusb_dev *ir) |
| +{ |
| + struct input_dev *idev; |
| + struct ir_dev_props *props; |
| + struct ir_input_dev *irdev; |
| + struct device *dev = ir->dev; |
| + int ret = -ENODEV; |
| + |
| + idev = input_allocate_device(); |
| + if (!idev) { |
| + dev_err(dev, "remote input dev allocation failed\n"); |
| + goto idev_alloc_failed; |
| + } |
| + |
| + ret = -ENOMEM; |
| + props = kzalloc(sizeof(struct ir_dev_props), GFP_KERNEL); |
| + if (!props) { |
| + dev_err(dev, "remote ir dev props allocation failed\n"); |
| + goto props_alloc_failed; |
| + } |
| + |
| + irdev = kzalloc(sizeof(struct ir_input_dev), GFP_KERNEL); |
| + if (!irdev) { |
| + dev_err(dev, "remote ir input dev allocation failed\n"); |
| + goto ir_dev_alloc_failed; |
| + } |
| + |
| + snprintf(ir->name, sizeof(ir->name), "Media Center Ed. eHome " |
| + "Infrared Remote Transceiver (%04x:%04x)", |
| + le16_to_cpu(ir->usbdev->descriptor.idVendor), |
| + le16_to_cpu(ir->usbdev->descriptor.idProduct)); |
| + |
| + idev->name = ir->name; |
| + usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys)); |
| + strlcat(ir->phys, "/input0", sizeof(ir->phys)); |
| + idev->phys = ir->phys; |
| + |
| + props->priv = ir; |
| + props->driver_type = RC_DRIVER_IR_RAW; |
| + props->allowed_protos = IR_TYPE_ALL; |
| + props->s_tx_mask = mceusb_set_tx_mask; |
| + props->s_tx_carrier = mceusb_set_tx_carrier; |
| + props->tx_ir = mceusb_tx_ir; |
| + |
| + ir->props = props; |
| + ir->irdev = irdev; |
| + |
| + input_set_drvdata(idev, irdev); |
| + |
| + ret = ir_input_register(idev, RC_MAP_RC6_MCE, props, DRIVER_NAME); |
| + if (ret < 0) { |
| + dev_err(dev, "remote input device register failed\n"); |
| + goto irdev_failed; |
| + } |
| + |
| + return idev; |
| + |
| +irdev_failed: |
| + kfree(irdev); |
| +ir_dev_alloc_failed: |
| + kfree(props); |
| +props_alloc_failed: |
| + input_free_device(idev); |
| +idev_alloc_failed: |
| + return NULL; |
| +} |
| + |
| +static int __devinit mceusb_dev_probe(struct usb_interface *intf, |
| + const struct usb_device_id *id) |
| +{ |
| + struct usb_device *dev = interface_to_usbdev(intf); |
| + struct usb_host_interface *idesc; |
| + struct usb_endpoint_descriptor *ep = NULL; |
| + struct usb_endpoint_descriptor *ep_in = NULL; |
| + struct usb_endpoint_descriptor *ep_out = NULL; |
| + struct usb_host_config *config; |
| + struct mceusb_dev *ir = NULL; |
| + int pipe, maxp, i; |
| + char buf[63], name[128] = ""; |
| + bool is_gen3; |
| + bool is_microsoft_gen1; |
| + bool tx_mask_inverted; |
| + |
| + dev_dbg(&intf->dev, ": %s called\n", __func__); |
| + |
| + config = dev->actconfig; |
| + idesc = intf->cur_altsetting; |
| + |
| + is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0; |
| + is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0; |
| + tx_mask_inverted = usb_match_id(intf, std_tx_mask_list) ? 0 : 1; |
| + |
| + /* step through the endpoints to find first bulk in and out endpoint */ |
| + for (i = 0; i < idesc->desc.bNumEndpoints; ++i) { |
| + ep = &idesc->endpoint[i].desc; |
| + |
| + if ((ep_in == NULL) |
| + && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) |
| + == USB_DIR_IN) |
| + && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) |
| + == USB_ENDPOINT_XFER_BULK) |
| + || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) |
| + == USB_ENDPOINT_XFER_INT))) { |
| + |
| + ep_in = ep; |
| + ep_in->bmAttributes = USB_ENDPOINT_XFER_INT; |
| + ep_in->bInterval = 1; |
| + dev_dbg(&intf->dev, ": acceptable inbound endpoint " |
| + "found\n"); |
| + } |
| + |
| + if ((ep_out == NULL) |
| + && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) |
| + == USB_DIR_OUT) |
| + && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) |
| + == USB_ENDPOINT_XFER_BULK) |
| + || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) |
| + == USB_ENDPOINT_XFER_INT))) { |
| + |
| + ep_out = ep; |
| + ep_out->bmAttributes = USB_ENDPOINT_XFER_INT; |
| + ep_out->bInterval = 1; |
| + dev_dbg(&intf->dev, ": acceptable outbound endpoint " |
| + "found\n"); |
| + } |
| + } |
| + if (ep_in == NULL) { |
| + dev_dbg(&intf->dev, ": inbound and/or endpoint not found\n"); |
| + return -ENODEV; |
| + } |
| + |
| + pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress); |
| + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); |
| + |
| + ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL); |
| + if (!ir) |
| + goto mem_alloc_fail; |
| + |
| + ir->buf_in = usb_alloc_coherent(dev, maxp, GFP_ATOMIC, &ir->dma_in); |
| + if (!ir->buf_in) |
| + goto buf_in_alloc_fail; |
| + |
| + ir->urb_in = usb_alloc_urb(0, GFP_KERNEL); |
| + if (!ir->urb_in) |
| + goto urb_in_alloc_fail; |
| + |
| + ir->usbdev = dev; |
| + ir->dev = &intf->dev; |
| + ir->len_in = maxp; |
| + ir->flags.microsoft_gen1 = is_microsoft_gen1; |
| + ir->flags.tx_mask_inverted = tx_mask_inverted; |
| + |
| + /* Saving usb interface data for use by the transmitter routine */ |
| + ir->usb_ep_in = ep_in; |
| + ir->usb_ep_out = ep_out; |
| + |
| + if (dev->descriptor.iManufacturer |
| + && usb_string(dev, dev->descriptor.iManufacturer, |
| + buf, sizeof(buf)) > 0) |
| + strlcpy(name, buf, sizeof(name)); |
| + if (dev->descriptor.iProduct |
| + && usb_string(dev, dev->descriptor.iProduct, |
| + buf, sizeof(buf)) > 0) |
| + snprintf(name + strlen(name), sizeof(name) - strlen(name), |
| + " %s", buf); |
| + |
| + ir->idev = mceusb_init_input_dev(ir); |
| + if (!ir->idev) |
| + goto input_dev_fail; |
| + |
| + /* flush buffers on the device */ |
| + mce_sync_in(ir, NULL, maxp); |
| + mce_sync_in(ir, NULL, maxp); |
| + |
| + /* wire up inbound data handler */ |
| + usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in, |
| + maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval); |
| + ir->urb_in->transfer_dma = ir->dma_in; |
| + ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
| + |
| + /* initialize device */ |
| + if (ir->flags.microsoft_gen1) |
| + mceusb_gen1_init(ir); |
| + else if (!is_gen3) |
| + mceusb_gen2_init(ir); |
| + |
| + mceusb_get_parameters(ir); |
| + |
| + mceusb_set_tx_mask(ir, MCE_DEFAULT_TX_MASK); |
| + |
| + usb_set_intfdata(intf, ir); |
| + |
| + dev_info(&intf->dev, "Registered %s on usb%d:%d\n", name, |
| + dev->bus->busnum, dev->devnum); |
| + |
| + return 0; |
| + |
| + /* Error-handling path */ |
| +input_dev_fail: |
| + usb_free_urb(ir->urb_in); |
| +urb_in_alloc_fail: |
| + usb_free_coherent(dev, maxp, ir->buf_in, ir->dma_in); |
| +buf_in_alloc_fail: |
| + kfree(ir); |
| +mem_alloc_fail: |
| + dev_err(&intf->dev, "%s: device setup failed!\n", __func__); |
| + |
| + return -ENOMEM; |
| +} |
| + |
| + |
| +static void __devexit mceusb_dev_disconnect(struct usb_interface *intf) |
| +{ |
| + struct usb_device *dev = interface_to_usbdev(intf); |
| + struct mceusb_dev *ir = usb_get_intfdata(intf); |
| + |
| + usb_set_intfdata(intf, NULL); |
| + |
| + if (!ir) |
| + return; |
| + |
| + ir->usbdev = NULL; |
| + ir_input_unregister(ir->idev); |
| + usb_kill_urb(ir->urb_in); |
| + usb_free_urb(ir->urb_in); |
| + usb_free_coherent(dev, ir->len_in, ir->buf_in, ir->dma_in); |
| + |
| + kfree(ir); |
| +} |
| + |
| +static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message) |
| +{ |
| + struct mceusb_dev *ir = usb_get_intfdata(intf); |
| + dev_info(ir->dev, "suspend\n"); |
| + usb_kill_urb(ir->urb_in); |
| + return 0; |
| +} |
| + |
| +static int mceusb_dev_resume(struct usb_interface *intf) |
| +{ |
| + struct mceusb_dev *ir = usb_get_intfdata(intf); |
| + dev_info(ir->dev, "resume\n"); |
| + if (usb_submit_urb(ir->urb_in, GFP_ATOMIC)) |
| + return -EIO; |
| + return 0; |
| +} |
| + |
| +static struct usb_driver mceusb_dev_driver = { |
| + .name = DRIVER_NAME, |
| + .probe = mceusb_dev_probe, |
| + .disconnect = mceusb_dev_disconnect, |
| + .suspend = mceusb_dev_suspend, |
| + .resume = mceusb_dev_resume, |
| + .reset_resume = mceusb_dev_resume, |
| + .id_table = mceusb_dev_table |
| +}; |
| + |
| +static int __init mceusb_dev_init(void) |
| +{ |
| + int ret; |
| + |
| + ret = usb_register(&mceusb_dev_driver); |
| + if (ret < 0) |
| + printk(KERN_ERR DRIVER_NAME |
| + ": usb register failed, result = %d\n", ret); |
| + |
| + return ret; |
| +} |
| + |
| +static void __exit mceusb_dev_exit(void) |
| +{ |
| + usb_deregister(&mceusb_dev_driver); |
| +} |
| + |
| +module_init(mceusb_dev_init); |
| +module_exit(mceusb_dev_exit); |
| + |
| +MODULE_DESCRIPTION(DRIVER_DESC); |
| +MODULE_AUTHOR(DRIVER_AUTHOR); |
| +MODULE_LICENSE("GPL"); |
| +MODULE_DEVICE_TABLE(usb, mceusb_dev_table); |
| + |
| +module_param(debug, bool, S_IRUGO | S_IWUSR); |
| +MODULE_PARM_DESC(debug, "Debug enabled or not"); |
| diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c |
| index b2e1545..7955e49 100644 |
| |
| |
| @@ -1249,7 +1249,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, |
| struct tda18271_config *cfg) |
| { |
| struct tda18271_priv *priv = NULL; |
| - int instance; |
| + int instance, ret; |
| |
| mutex_lock(&tda18271_list_mutex); |
| |
| @@ -1268,10 +1268,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, |
| priv->cal_initialized = false; |
| mutex_init(&priv->lock); |
| |
| - if (tda_fail(tda18271_get_id(fe))) |
| + ret = tda18271_get_id(fe); |
| + if (tda_fail(ret)) |
| goto fail; |
| |
| - if (tda_fail(tda18271_assign_map_layout(fe))) |
| + ret = tda18271_assign_map_layout(fe); |
| + if (tda_fail(ret)) |
| goto fail; |
| |
| mutex_lock(&priv->lock); |
| diff --git a/drivers/media/dvb/mantis/Kconfig b/drivers/media/dvb/mantis/Kconfig |
| index f7b72a3..decdeda 100644 |
| |
| |
| @@ -10,9 +10,15 @@ config MANTIS_CORE |
| config DVB_MANTIS |
| tristate "MANTIS based cards" |
| depends on MANTIS_CORE && DVB_CORE && PCI && I2C |
| - select DVB_MB86A16 |
| - select DVB_ZL10353 |
| - select DVB_STV0299 |
| + select DVB_MB86A16 if !DVB_FE_CUSTOMISE |
| + select DVB_ZL10353 if !DVB_FE_CUSTOMISE |
| + select DVB_STV0299 if !DVB_FE_CUSTOMISE |
| + select DVB_LNBP21 if !DVB_FE_CUSTOMISE |
| + select DVB_STB0899 if !DVB_FE_CUSTOMISE |
| + select DVB_STB6100 if !DVB_FE_CUSTOMISE |
| + select DVB_TDA665x if !DVB_FE_CUSTOMISE |
| + select DVB_TDA10021 if !DVB_FE_CUSTOMISE |
| + select DVB_TDA10023 if !DVB_FE_CUSTOMISE |
| select DVB_PLL |
| help |
| Support for PCI cards based on the Mantis PCI bridge. |
| @@ -23,7 +29,7 @@ config DVB_MANTIS |
| config DVB_HOPPER |
| tristate "HOPPER based cards" |
| depends on MANTIS_CORE && DVB_CORE && PCI && I2C |
| - select DVB_ZL10353 |
| + select DVB_ZL10353 if !DVB_FE_CUSTOMISE |
| select DVB_PLL |
| help |
| Support for PCI cards based on the Hopper PCI bridge. |
| diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c |
| index 3d4e466..a99489b 100644 |
| |
| |
| @@ -19,7 +19,7 @@ |
| */ |
| |
| #include <linux/input.h> |
| -#include <media/ir-common.h> |
| +#include <media/ir-core.h> |
| #include <linux/pci.h> |
| |
| #include "dmxdev.h" |
| @@ -104,7 +104,6 @@ EXPORT_SYMBOL_GPL(ir_mantis); |
| int mantis_input_init(struct mantis_pci *mantis) |
| { |
| struct input_dev *rc; |
| - struct ir_input_state rc_state; |
| char name[80], dev[80]; |
| int err; |
| |
| @@ -120,8 +119,6 @@ int mantis_input_init(struct mantis_pci *mantis) |
| rc->name = name; |
| rc->phys = dev; |
| |
| - ir_input_init(rc, &rc_state, IR_TYPE_OTHER); |
| - |
| rc->id.bustype = BUS_PCI; |
| rc->id.vendor = mantis->vendor_id; |
| rc->id.product = mantis->device_id; |
| diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c |
| index d639186..2014dae 100644 |
| |
| |
| @@ -408,10 +408,18 @@ struct cx23885_subid cx23885_subids[] = { |
| .card = CX23885_BOARD_HAUPPAUGE_HVR1275, |
| }, { |
| .subvendor = 0x0070, |
| + .subdevice = 0x221d, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1275, |
| + }, { |
| + .subvendor = 0x0070, |
| .subdevice = 0x2251, |
| .card = CX23885_BOARD_HAUPPAUGE_HVR1255, |
| }, { |
| .subvendor = 0x0070, |
| + .subdevice = 0x2259, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1255, |
| + }, { |
| + .subvendor = 0x0070, |
| .subdevice = 0x2291, |
| .card = CX23885_BOARD_HAUPPAUGE_HVR1210, |
| }, { |
| @@ -419,6 +427,38 @@ struct cx23885_subid cx23885_subids[] = { |
| .subdevice = 0x2295, |
| .card = CX23885_BOARD_HAUPPAUGE_HVR1210, |
| }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x2299, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, |
| + }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x229d, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ |
| + }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x22f0, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, |
| + }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x22f1, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1255, |
| + }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x22f2, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1275, |
| + }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x22f3, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ |
| + }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x22f4, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, |
| + }, { |
| + .subvendor = 0x0070, |
| + .subdevice = 0x22f5, |
| + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, /* HVR1215 */ |
| + }, { |
| .subvendor = 0x14f1, |
| .subdevice = 0x8651, |
| .card = CX23885_BOARD_MYGICA_X8506, |
| diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c |
| index 0dde57e..ff76f64 100644 |
| |
| |
| @@ -1142,7 +1142,7 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf) |
| |
| BUG_ON(in_interrupt()); |
| videobuf_waiton(&buf->vb, 0, 0); |
| - videobuf_dma_unmap(q, dma); |
| + videobuf_dma_unmap(q->dev, dma); |
| videobuf_dma_free(dma); |
| btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); |
| buf->vb.state = VIDEOBUF_NEEDS_INIT; |
| @@ -1953,8 +1953,12 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, |
| goto fail_irq; |
| } |
| |
| - err = request_irq(pci_dev->irq, cx23885_irq, |
| - IRQF_SHARED | IRQF_DISABLED, dev->name, dev); |
| + if (!pci_enable_msi(pci_dev)) |
| + err = request_irq(pci_dev->irq, cx23885_irq, |
| + IRQF_DISABLED, dev->name, dev); |
| + else |
| + err = request_irq(pci_dev->irq, cx23885_irq, |
| + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); |
| if (err < 0) { |
| printk(KERN_ERR "%s: can't get IRQ %d\n", |
| dev->name, pci_dev->irq); |
| @@ -2000,6 +2004,7 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) |
| |
| /* unregister stuff */ |
| free_irq(pci_dev->irq, dev); |
| + pci_disable_msi(pci_dev); |
| |
| cx23885_dev_unregister(dev); |
| v4l2_device_unregister(v4l2_dev); |
| diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c |
| index 0a199d7..3d70af2 100644 |
| |
| |
| @@ -991,7 +991,7 @@ static int dvb_register(struct cx23885_tsport *port) |
| ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, |
| &dev->pci->dev, adapter_nr, 0, |
| cx23885_dvb_fe_ioctl_override); |
| - if (!ret) |
| + if (ret) |
| return ret; |
| |
| /* init CI & MAC */ |
| diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c |
| index 5de6ba9..d0b1613 100644 |
| |
| |
| @@ -37,161 +37,55 @@ |
| |
| #include <linux/input.h> |
| #include <linux/slab.h> |
| -#include <media/ir-common.h> |
| +#include <media/ir-core.h> |
| #include <media/v4l2-subdev.h> |
| |
| #include "cx23885.h" |
| |
| -#define RC5_BITS 14 |
| -#define RC5_HALF_BITS (2*RC5_BITS) |
| -#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1) |
| - |
| -#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */ |
| -#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */ |
| - |
| -#define RC5_EXTENDED_COMMAND_OFFSET 64 |
| - |
| #define MODULE_NAME "cx23885" |
| |
| -static inline unsigned int rc5_command(u32 rc5_baseband) |
| +static void convert_measurement(u32 x, struct ir_raw_event *y) |
| { |
| - return RC5_INSTR(rc5_baseband) + |
| - ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED) |
| - ? RC5_EXTENDED_COMMAND_OFFSET : 0); |
| -} |
| - |
| -static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev) |
| -{ |
| - struct card_ir *ir_input = dev->ir_input; |
| - unsigned int code, command; |
| - u32 rc5; |
| - |
| - /* Ignore codes that are too short to be valid RC-5 */ |
| - if (ir_input->last_bit < (RC5_HALF_BITS - 1)) |
| - return; |
| - |
| - /* The library has the manchester coding backwards; XOR to adapt. */ |
| - code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK; |
| - rc5 = ir_rc5_decode(code); |
| - |
| - switch (RC5_START(rc5)) { |
| - case RC5_START_BITS_NORMAL: |
| - break; |
| - case RC5_START_BITS_EXTENDED: |
| - /* Don't allow if the remote only emits standard commands */ |
| - if (ir_input->start == RC5_START_BITS_NORMAL) |
| - return; |
| - break; |
| - default: |
| + if (x == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { |
| + y->pulse = false; |
| + y->duration = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; |
| return; |
| } |
| |
| - if (ir_input->addr != RC5_ADDR(rc5)) |
| - return; |
| - |
| - /* Don't generate a keypress for RC-5 auto-repeated keypresses */ |
| - command = rc5_command(rc5); |
| - if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) || |
| - command != rc5_command(ir_input->last_rc5) || |
| - /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */ |
| - RC5_START(ir_input->last_rc5) == 0) { |
| - /* This keypress is differnet: not an auto repeat */ |
| - ir_input_nokey(ir_input->dev, &ir_input->ir); |
| - ir_input_keydown(ir_input->dev, &ir_input->ir, command); |
| - } |
| - ir_input->last_rc5 = rc5; |
| - |
| - /* Schedule when we should do the key up event: ir_input_nokey() */ |
| - mod_timer(&ir_input->timer_keyup, |
| - jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout)); |
| + y->pulse = (x & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? true : false; |
| + y->duration = x & V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; |
| } |
| |
| -static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev, |
| - u32 ns_pulse) |
| +static void cx23885_input_process_measurements(struct cx23885_dev *dev, |
| + bool overrun) |
| { |
| - const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */ |
| - struct card_ir *ir_input = dev->ir_input; |
| - int i, level, quarterbits, halfbits; |
| - |
| - if (!ir_input->active) { |
| - ir_input->active = 1; |
| - /* assume an initial space that we may not detect or measure */ |
| - ir_input->code = 0; |
| - ir_input->last_bit = 0; |
| - } |
| + struct cx23885_kernel_ir *kernel_ir = dev->kernel_ir; |
| + struct ir_raw_event kernel_ir_event; |
| |
| - if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { |
| - ir_input->last_bit++; /* Account for the final space */ |
| - ir_input->active = 0; |
| - cx23885_input_process_raw_rc5(dev); |
| - return; |
| - } |
| - |
| - level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0; |
| - |
| - /* Skip any leading space to sync to the start bit */ |
| - if (ir_input->last_bit == 0 && level == 0) |
| - return; |
| - |
| - /* |
| - * With valid RC-5 we can get up to two consecutive half-bits in a |
| - * single pulse measurment. Experiments have shown that the duration |
| - * of a half-bit can vary. Make sure we always end up with an even |
| - * number of quarter bits at the same level (mark or space). |
| - */ |
| - ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; |
| - quarterbits = ns_pulse / rc5_quarterbit_ns; |
| - if (quarterbits & 1) |
| - quarterbits++; |
| - halfbits = quarterbits / 2; |
| - |
| - for (i = 0; i < halfbits; i++) { |
| - ir_input->last_bit++; |
| - ir_input->code |= (level << ir_input->last_bit); |
| - |
| - if (ir_input->last_bit >= RC5_HALF_BITS-1) { |
| - ir_input->active = 0; |
| - cx23885_input_process_raw_rc5(dev); |
| - /* |
| - * If level is 1, a leading mark is invalid for RC5. |
| - * If level is 0, we scan past extra intial space. |
| - * Either way we don't want to reactivate collecting |
| - * marks or spaces here with any left over half-bits. |
| - */ |
| - break; |
| - } |
| - } |
| -} |
| - |
| -static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev, |
| - bool add_eom) |
| -{ |
| - struct card_ir *ir_input = dev->ir_input; |
| - struct ir_input_state *ir_input_state = &ir_input->ir; |
| - |
| - u32 ns_pulse[RC5_HALF_BITS+1]; |
| - ssize_t num = 0; |
| + u32 sd_ir_data[64]; |
| + ssize_t num; |
| int count, i; |
| + bool handle = false; |
| |
| do { |
| - v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse, |
| - sizeof(ns_pulse), &num); |
| + num = 0; |
| + v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) sd_ir_data, |
| + sizeof(sd_ir_data), &num); |
| |
| count = num / sizeof(u32); |
| |
| - /* Append an end of Rx seq, if the caller requested */ |
| - if (add_eom && count < ARRAY_SIZE(ns_pulse)) { |
| - ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; |
| - count++; |
| + for (i = 0; i < count; i++) { |
| + convert_measurement(sd_ir_data[i], &kernel_ir_event); |
| + ir_raw_event_store(kernel_ir->inp_dev, |
| + &kernel_ir_event); |
| + handle = true; |
| } |
| - |
| - /* Just drain the Rx FIFO, if we're called, but not RC-5 */ |
| - if (ir_input_state->ir_type != IR_TYPE_RC5) |
| - continue; |
| - |
| - for (i = 0; i < count; i++) |
| - cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]); |
| } while (num != 0); |
| + |
| + if (overrun) |
| + ir_raw_event_reset(kernel_ir->inp_dev); |
| + else if (handle) |
| + ir_raw_event_handle(kernel_ir->inp_dev); |
| } |
| |
| void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) |
| @@ -230,7 +124,7 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) |
| } |
| |
| if (data_available) |
| - cx23885_input_process_pulse_widths_rc5(dev, overrun); |
| + cx23885_input_process_measurements(dev, overrun); |
| |
| if (overrun) { |
| /* If there was a FIFO overrun, clear & restart the device */ |
| @@ -241,34 +135,15 @@ void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) |
| } |
| } |
| |
| -static void cx23885_input_ir_start(struct cx23885_dev *dev) |
| +static int cx23885_input_ir_start(struct cx23885_dev *dev) |
| { |
| - struct card_ir *ir_input = dev->ir_input; |
| - struct ir_input_state *ir_input_state = &ir_input->ir; |
| struct v4l2_subdev_ir_parameters params; |
| |
| if (dev->sd_ir == NULL) |
| - return; |
| + return -ENODEV; |
| |
| atomic_set(&dev->ir_input_stopping, 0); |
| |
| - /* keyup timer set up, if needed */ |
| - switch (dev->board) { |
| - case CX23885_BOARD_HAUPPAUGE_HVR1850: |
| - case CX23885_BOARD_HAUPPAUGE_HVR1290: |
| - setup_timer(&ir_input->timer_keyup, |
| - ir_rc5_timer_keyup, /* Not actually RC-5 specific */ |
| - (unsigned long) ir_input); |
| - if (ir_input_state->ir_type == IR_TYPE_RC5) { |
| - /* |
| - * RC-5 repeats a held key every |
| - * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms |
| - */ |
| - ir_input->rc5_key_timeout = 115; |
| - } |
| - break; |
| - } |
| - |
| v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); |
| switch (dev->board) { |
| case CX23885_BOARD_HAUPPAUGE_HVR1850: |
| @@ -299,11 +174,21 @@ static void cx23885_input_ir_start(struct cx23885_dev *dev) |
| break; |
| } |
| v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); |
| + return 0; |
| +} |
| + |
| +static int cx23885_input_ir_open(void *priv) |
| +{ |
| + struct cx23885_kernel_ir *kernel_ir = priv; |
| + |
| + if (kernel_ir->cx == NULL) |
| + return -ENODEV; |
| + |
| + return cx23885_input_ir_start(kernel_ir->cx); |
| } |
| |
| static void cx23885_input_ir_stop(struct cx23885_dev *dev) |
| { |
| - struct card_ir *ir_input = dev->ir_input; |
| struct v4l2_subdev_ir_parameters params; |
| |
| if (dev->sd_ir == NULL) |
| @@ -327,21 +212,26 @@ static void cx23885_input_ir_stop(struct cx23885_dev *dev) |
| } |
| |
| flush_scheduled_work(); |
| +} |
| |
| - switch (dev->board) { |
| - case CX23885_BOARD_HAUPPAUGE_HVR1850: |
| - case CX23885_BOARD_HAUPPAUGE_HVR1290: |
| - del_timer_sync(&ir_input->timer_keyup); |
| - break; |
| - } |
| +static void cx23885_input_ir_close(void *priv) |
| +{ |
| + struct cx23885_kernel_ir *kernel_ir = priv; |
| + |
| + if (kernel_ir->cx != NULL) |
| + cx23885_input_ir_stop(kernel_ir->cx); |
| } |
| |
| int cx23885_input_init(struct cx23885_dev *dev) |
| { |
| - struct card_ir *ir; |
| - struct input_dev *input_dev; |
| - char *ir_codes = NULL; |
| - int ir_type, ir_addr, ir_start; |
| + struct cx23885_kernel_ir *kernel_ir; |
| + struct input_dev *inp_dev; |
| + struct ir_dev_props *props; |
| + |
| + char *rc_map; |
| + enum rc_driver_type driver_type; |
| + unsigned long allowed_protos; |
| + |
| int ret; |
| |
| /* |
| @@ -354,53 +244,59 @@ int cx23885_input_init(struct cx23885_dev *dev) |
| switch (dev->board) { |
| case CX23885_BOARD_HAUPPAUGE_HVR1850: |
| case CX23885_BOARD_HAUPPAUGE_HVR1290: |
| - /* Parameters for the grey Hauppauge remote for the HVR-1850 */ |
| - ir_codes = RC_MAP_HAUPPAUGE_NEW; |
| - ir_type = IR_TYPE_RC5; |
| - ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */ |
| - ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */ |
| + /* Integrated CX23888 IR controller */ |
| + driver_type = RC_DRIVER_IR_RAW; |
| + allowed_protos = IR_TYPE_ALL; |
| + /* The grey Hauppauge RC-5 remote */ |
| + rc_map = RC_MAP_RC5_HAUPPAUGE_NEW; |
| break; |
| - } |
| - if (ir_codes == NULL) |
| + default: |
| return -ENODEV; |
| - |
| - ir = kzalloc(sizeof(*ir), GFP_KERNEL); |
| - input_dev = input_allocate_device(); |
| - if (!ir || !input_dev) { |
| - ret = -ENOMEM; |
| - goto err_out_free; |
| } |
| |
| - ir->dev = input_dev; |
| - ir->addr = ir_addr; |
| - ir->start = ir_start; |
| + /* cx23885 board instance kernel IR state */ |
| + kernel_ir = kzalloc(sizeof(struct cx23885_kernel_ir), GFP_KERNEL); |
| + if (kernel_ir == NULL) |
| + return -ENOMEM; |
| |
| - /* init input device */ |
| - snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)", |
| - cx23885_boards[dev->board].name); |
| - snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); |
| + kernel_ir->cx = dev; |
| + kernel_ir->name = kasprintf(GFP_KERNEL, "cx23885 IR (%s)", |
| + cx23885_boards[dev->board].name); |
| + kernel_ir->phys = kasprintf(GFP_KERNEL, "pci-%s/ir0", |
| + pci_name(dev->pci)); |
| |
| - ret = ir_input_init(input_dev, &ir->ir, ir_type); |
| - if (ret < 0) |
| + /* input device */ |
| + inp_dev = input_allocate_device(); |
| + if (inp_dev == NULL) { |
| + ret = -ENOMEM; |
| goto err_out_free; |
| + } |
| |
| - input_dev->name = ir->name; |
| - input_dev->phys = ir->phys; |
| - input_dev->id.bustype = BUS_PCI; |
| - input_dev->id.version = 1; |
| + kernel_ir->inp_dev = inp_dev; |
| + inp_dev->name = kernel_ir->name; |
| + inp_dev->phys = kernel_ir->phys; |
| + inp_dev->id.bustype = BUS_PCI; |
| + inp_dev->id.version = 1; |
| if (dev->pci->subsystem_vendor) { |
| - input_dev->id.vendor = dev->pci->subsystem_vendor; |
| - input_dev->id.product = dev->pci->subsystem_device; |
| + inp_dev->id.vendor = dev->pci->subsystem_vendor; |
| + inp_dev->id.product = dev->pci->subsystem_device; |
| } else { |
| - input_dev->id.vendor = dev->pci->vendor; |
| - input_dev->id.product = dev->pci->device; |
| + inp_dev->id.vendor = dev->pci->vendor; |
| + inp_dev->id.product = dev->pci->device; |
| } |
| - input_dev->dev.parent = &dev->pci->dev; |
| - |
| - dev->ir_input = ir; |
| - cx23885_input_ir_start(dev); |
| - |
| - ret = ir_input_register(ir->dev, ir_codes, NULL, MODULE_NAME); |
| + inp_dev->dev.parent = &dev->pci->dev; |
| + |
| + /* kernel ir device properties */ |
| + props = &kernel_ir->props; |
| + props->driver_type = driver_type; |
| + props->allowed_protos = allowed_protos; |
| + props->priv = kernel_ir; |
| + props->open = cx23885_input_ir_open; |
| + props->close = cx23885_input_ir_close; |
| + |
| + /* Go */ |
| + dev->kernel_ir = kernel_ir; |
| + ret = ir_input_register(inp_dev, rc_map, props, MODULE_NAME); |
| if (ret) |
| goto err_out_stop; |
| |
| @@ -408,9 +304,12 @@ int cx23885_input_init(struct cx23885_dev *dev) |
| |
| err_out_stop: |
| cx23885_input_ir_stop(dev); |
| - dev->ir_input = NULL; |
| + dev->kernel_ir = NULL; |
| + /* TODO: double check clean-up of kernel_ir->inp_dev */ |
| err_out_free: |
| - kfree(ir); |
| + kfree(kernel_ir->phys); |
| + kfree(kernel_ir->name); |
| + kfree(kernel_ir); |
| return ret; |
| } |
| |
| @@ -419,9 +318,11 @@ void cx23885_input_fini(struct cx23885_dev *dev) |
| /* Always stop the IR hardware from generating interrupts */ |
| cx23885_input_ir_stop(dev); |
| |
| - if (dev->ir_input == NULL) |
| + if (dev->kernel_ir == NULL) |
| return; |
| - ir_input_unregister(dev->ir_input->dev); |
| - kfree(dev->ir_input); |
| - dev->ir_input = NULL; |
| + ir_input_unregister(dev->kernel_ir->inp_dev); |
| + kfree(dev->kernel_ir->phys); |
| + kfree(dev->kernel_ir->name); |
| + kfree(dev->kernel_ir); |
| + dev->kernel_ir = NULL; |
| } |
| diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c |
| index 9a677eb..6ceabd4 100644 |
| |
| |
| @@ -53,7 +53,7 @@ void cx23885_ir_rx_work_handler(struct work_struct *work) |
| if (events == 0) |
| return; |
| |
| - if (dev->ir_input) |
| + if (dev->kernel_ir) |
| cx23885_input_rx_work_handler(dev, events); |
| } |
| |
| diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h |
| index 8d6a55e..a33f2b7 100644 |
| |
| |
| @@ -30,6 +30,7 @@ |
| #include <media/tveeprom.h> |
| #include <media/videobuf-dma-sg.h> |
| #include <media/videobuf-dvb.h> |
| +#include <media/ir-core.h> |
| |
| #include "btcx-risc.h" |
| #include "cx23885-reg.h" |
| @@ -304,6 +305,15 @@ struct cx23885_tsport { |
| void *port_priv; |
| }; |
| |
| +struct cx23885_kernel_ir { |
| + struct cx23885_dev *cx; |
| + char *name; |
| + char *phys; |
| + |
| + struct input_dev *inp_dev; |
| + struct ir_dev_props props; |
| +}; |
| + |
| struct cx23885_dev { |
| atomic_t refcount; |
| struct v4l2_device v4l2_dev; |
| @@ -363,7 +373,7 @@ struct cx23885_dev { |
| struct work_struct ir_tx_work; |
| unsigned long ir_tx_notifications; |
| |
| - struct card_ir *ir_input; |
| + struct cx23885_kernel_ir *kernel_ir; |
| atomic_t ir_input_stopping; |
| |
| /* V4l */ |
| diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c |
| index 2918a6e..e8416b7 100644 |
| |
| |
| @@ -45,6 +45,10 @@ static unsigned int latency = UNSET; |
| module_param(latency,int,0444); |
| MODULE_PARM_DESC(latency,"pci latency timer"); |
| |
| +static int disable_ir; |
| +module_param(disable_ir, int, 0444); |
| +MODULE_PARM_DESC(latency, "Disable IR support"); |
| + |
| #define info_printk(core, fmt, arg...) \ |
| printk(KERN_INFO "%s: " fmt, core->name , ## arg) |
| |
| @@ -3498,7 +3502,10 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) |
| } |
| |
| cx88_card_setup(core); |
| - cx88_ir_init(core, pci); |
| + if (!disable_ir) { |
| + cx88_i2c_init_ir(core); |
| + cx88_ir_init(core, pci); |
| + } |
| |
| return core; |
| } |
| diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c |
| index fb39f11..375ad53 100644 |
| |
| |
| @@ -181,6 +181,11 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) |
| } else |
| printk("%s: i2c register FAILED\n", core->name); |
| |
| + return core->i2c_rc; |
| +} |
| + |
| +void cx88_i2c_init_ir(struct cx88_core *core) |
| +{ |
| /* Instantiate the IR receiver device, if present */ |
| if (0 == core->i2c_rc) { |
| struct i2c_board_info info; |
| @@ -207,7 +212,6 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) |
| } |
| } |
| } |
| - return core->i2c_rc; |
| } |
| |
| /* ----------------------------------------------------------------------- */ |
| diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c |
| index e185289..eccc5e4 100644 |
| |
| |
| @@ -30,6 +30,7 @@ |
| #include <linux/module.h> |
| |
| #include "cx88.h" |
| +#include <media/ir-core.h> |
| #include <media/ir-common.h> |
| |
| #define MODULE_NAME "cx88xx" |
| @@ -39,8 +40,8 @@ |
| struct cx88_IR { |
| struct cx88_core *core; |
| struct input_dev *input; |
| - struct ir_input_state ir; |
| struct ir_dev_props props; |
| + u64 ir_type; |
| |
| int users; |
| |
| @@ -51,7 +52,6 @@ struct cx88_IR { |
| u32 sampling; |
| u32 samples[16]; |
| int scount; |
| - unsigned long release; |
| |
| /* poll external decoder */ |
| int polling; |
| @@ -125,29 +125,21 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) |
| |
| data = (data << 4) | ((gpio_key & 0xf0) >> 4); |
| |
| - ir_input_keydown(ir->input, &ir->ir, data); |
| - ir_input_nokey(ir->input, &ir->ir); |
| + ir_keydown(ir->input, data, 0); |
| |
| } else if (ir->mask_keydown) { |
| /* bit set on keydown */ |
| - if (gpio & ir->mask_keydown) { |
| - ir_input_keydown(ir->input, &ir->ir, data); |
| - } else { |
| - ir_input_nokey(ir->input, &ir->ir); |
| - } |
| + if (gpio & ir->mask_keydown) |
| + ir_keydown(ir->input, data, 0); |
| |
| } else if (ir->mask_keyup) { |
| /* bit cleared on keydown */ |
| - if (0 == (gpio & ir->mask_keyup)) { |
| - ir_input_keydown(ir->input, &ir->ir, data); |
| - } else { |
| - ir_input_nokey(ir->input, &ir->ir); |
| - } |
| + if (0 == (gpio & ir->mask_keyup)) |
| + ir_keydown(ir->input, data, 0); |
| |
| } else { |
| /* can't distinguish keydown/up :-/ */ |
| - ir_input_keydown(ir->input, &ir->ir, data); |
| - ir_input_nokey(ir->input, &ir->ir); |
| + ir_keydown(ir->input, data, 0); |
| } |
| } |
| |
| @@ -439,9 +431,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) |
| snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); |
| snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); |
| |
| - err = ir_input_init(input_dev, &ir->ir, ir_type); |
| - if (err < 0) |
| - goto err_out_free; |
| + ir->ir_type = ir_type; |
| |
| input_dev->name = ir->name; |
| input_dev->phys = ir->phys; |
| @@ -516,8 +506,6 @@ void cx88_ir_irq(struct cx88_core *core) |
| } |
| if (!ir->scount) { |
| /* nothing to sample */ |
| - if (ir->ir.keypressed && time_after(jiffies, ir->release)) |
| - ir_input_nokey(ir->input, &ir->ir); |
| return; |
| } |
| |
| @@ -553,7 +541,7 @@ void cx88_ir_irq(struct cx88_core *core) |
| |
| if (ircode == 0) { /* key still pressed */ |
| ir_dprintk("pulse distance decoded repeat code\n"); |
| - ir->release = jiffies + msecs_to_jiffies(120); |
| + ir_repeat(ir->input); |
| break; |
| } |
| |
| @@ -567,10 +555,8 @@ void cx88_ir_irq(struct cx88_core *core) |
| break; |
| } |
| |
| - ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f); |
| - |
| - ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f); |
| - ir->release = jiffies + msecs_to_jiffies(120); |
| + ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0xff); |
| + ir_keydown(ir->input, (ircode >> 16) & 0xff, 0); |
| break; |
| case CX88_BOARD_HAUPPAUGE: |
| case CX88_BOARD_HAUPPAUGE_DVB_T1: |
| @@ -606,16 +592,16 @@ void cx88_ir_irq(struct cx88_core *core) |
| if ( dev != 0x1e && dev != 0x1f ) |
| /* not a hauppauge remote */ |
| break; |
| - ir_input_keydown(ir->input, &ir->ir, code); |
| - ir->release = jiffies + msecs_to_jiffies(120); |
| + ir_keydown(ir->input, code, toggle); |
| break; |
| case CX88_BOARD_PINNACLE_PCTV_HD_800i: |
| ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); |
| ir_dprintk("biphase decoded: %x\n", ircode); |
| if ((ircode & 0xfffff000) != 0x3000) |
| break; |
| - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f); |
| - ir->release = jiffies + msecs_to_jiffies(120); |
| + /* Note: bit 0x800 being the toggle is assumed, not checked |
| + with real hardware */ |
| + ir_keydown(ir->input, ircode & 0x3f, ircode & 0x0800 ? 1 : 0); |
| break; |
| } |
| |
| diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h |
| index bdb03d3..33d161a 100644 |
| |
| |
| @@ -636,6 +636,7 @@ extern struct videobuf_queue_ops cx8800_vbi_qops; |
| /* cx88-i2c.c */ |
| |
| extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); |
| +extern void cx88_i2c_init_ir(struct cx88_core *core); |
| |
| |
| /* ----------------------------------------------------------- */ |
| diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c |
| index 5c3fd94..6759cd5 100644 |
| |
| |
| @@ -65,17 +65,14 @@ struct em28xx_ir_poll_result { |
| struct em28xx_IR { |
| struct em28xx *dev; |
| struct input_dev *input; |
| - struct ir_input_state ir; |
| char name[32]; |
| char phys[32]; |
| |
| /* poll external decoder */ |
| int polling; |
| struct delayed_work work; |
| - unsigned int last_toggle:1; |
| unsigned int full_code:1; |
| unsigned int last_readcount; |
| - unsigned int repeat_interval; |
| |
| int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); |
| |
| @@ -291,67 +288,39 @@ static int em2874_polling_getkey(struct em28xx_IR *ir, |
| static void em28xx_ir_handle_key(struct em28xx_IR *ir) |
| { |
| int result; |
| - int do_sendkey = 0; |
| struct em28xx_ir_poll_result poll_result; |
| |
| /* read the registers containing the IR status */ |
| result = ir->get_key(ir, &poll_result); |
| - if (result < 0) { |
| + if (unlikely(result < 0)) { |
| dprintk("ir->get_key() failed %d\n", result); |
| return; |
| } |
| |
| - dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n", |
| - poll_result.toggle_bit, poll_result.read_count, |
| - ir->last_readcount, poll_result.rc_address, |
| - poll_result.rc_data[0]); |
| - |
| - if (ir->dev->chip_id == CHIP_ID_EM2874) { |
| - /* The em2874 clears the readcount field every time the |
| - register is read. The em2860/2880 datasheet says that it |
| - is supposed to clear the readcount, but it doesn't. So with |
| - the em2874, we are looking for a non-zero read count as |
| - opposed to a readcount that is incrementing */ |
| - ir->last_readcount = 0; |
| - } |
| - |
| - if (poll_result.read_count == 0) { |
| - /* The button has not been pressed since the last read */ |
| - } else if (ir->last_toggle != poll_result.toggle_bit) { |
| - /* A button has been pressed */ |
| - dprintk("button has been pressed\n"); |
| - ir->last_toggle = poll_result.toggle_bit; |
| - ir->repeat_interval = 0; |
| - do_sendkey = 1; |
| - } else if (poll_result.toggle_bit == ir->last_toggle && |
| - poll_result.read_count > 0 && |
| - poll_result.read_count != ir->last_readcount) { |
| - /* The button is still being held down */ |
| - dprintk("button being held down\n"); |
| - |
| - /* Debouncer for first keypress */ |
| - if (ir->repeat_interval++ > 9) { |
| - /* Start repeating after 1 second */ |
| - do_sendkey = 1; |
| - } |
| - } |
| - |
| - if (do_sendkey) { |
| - dprintk("sending keypress\n"); |
| - |
| + if (unlikely(poll_result.read_count != ir->last_readcount)) { |
| + dprintk("%s: toggle: %d, count: %d, key 0x%02x%02x\n", __func__, |
| + poll_result.toggle_bit, poll_result.read_count, |
| + poll_result.rc_address, poll_result.rc_data[0]); |
| if (ir->full_code) |
| - ir_input_keydown(ir->input, &ir->ir, |
| - poll_result.rc_address << 8 | |
| - poll_result.rc_data[0]); |
| + ir_keydown(ir->input, |
| + poll_result.rc_address << 8 | |
| + poll_result.rc_data[0], |
| + poll_result.toggle_bit); |
| else |
| - ir_input_keydown(ir->input, &ir->ir, |
| - poll_result.rc_data[0]); |
| - |
| - ir_input_nokey(ir->input, &ir->ir); |
| + ir_keydown(ir->input, |
| + poll_result.rc_data[0], |
| + poll_result.toggle_bit); |
| + |
| + if (ir->dev->chip_id == CHIP_ID_EM2874) |
| + /* The em2874 clears the readcount field every time the |
| + register is read. The em2860/2880 datasheet says that it |
| + is supposed to clear the readcount, but it doesn't. So with |
| + the em2874, we are looking for a non-zero read count as |
| + opposed to a readcount that is incrementing */ |
| + ir->last_readcount = 0; |
| + else |
| + ir->last_readcount = poll_result.read_count; |
| } |
| - |
| - ir->last_readcount = poll_result.read_count; |
| - return; |
| } |
| |
| static void em28xx_ir_work(struct work_struct *work) |
| @@ -466,11 +435,6 @@ int em28xx_ir_init(struct em28xx *dev) |
| usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); |
| strlcat(ir->phys, "/input0", sizeof(ir->phys)); |
| |
| - /* Set IR protocol */ |
| - err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER); |
| - if (err < 0) |
| - goto err_out_free; |
| - |
| input_dev->name = ir->name; |
| input_dev->phys = ir->phys; |
| input_dev->id.bustype = BUS_USB; |
| diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c |
| index 20090e3..7b9ec6e 100644 |
| |
| |
| @@ -654,12 +654,12 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) |
| } |
| |
| if (buf != NULL && dev->capture_type == 2) { |
| - if (len > 4 && p[0] == 0x88 && p[1] == 0x88 && |
| + if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && |
| p[2] == 0x88 && p[3] == 0x88) { |
| p += 4; |
| len -= 4; |
| } |
| - if (len > 4 && p[0] == 0x22 && p[1] == 0x5a) { |
| + if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { |
| em28xx_isocdbg("Video frame %d, len=%i, %s\n", |
| p[2], len, (p[2] & 1) ? |
| "odd" : "even"); |
| diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h |
| index b252d1b..6216786 100644 |
| |
| |
| @@ -32,6 +32,7 @@ |
| #include <linux/i2c.h> |
| #include <linux/mutex.h> |
| #include <media/ir-kbd-i2c.h> |
| +#include <media/ir-core.h> |
| #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) |
| #include <media/videobuf-dvb.h> |
| #endif |
| diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c |
| index 830d47b..0cae5b8 100644 |
| |
| |
| @@ -286,6 +286,8 @@ static int hdpvr_probe(struct usb_interface *interface, |
| goto error; |
| } |
| |
| + dev->workqueue = 0; |
| + |
| /* register v4l2_device early so it can be used for printks */ |
| if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { |
| err("v4l2_device_register failed"); |
| @@ -380,6 +382,9 @@ static int hdpvr_probe(struct usb_interface *interface, |
| |
| error: |
| if (dev) { |
| + /* Destroy single thread */ |
| + if (dev->workqueue) |
| + destroy_workqueue(dev->workqueue); |
| /* this frees allocated memory */ |
| hdpvr_delete(dev); |
| } |
| diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c |
| index 29d4397..27ae8bb 100644 |
| |
| |
| @@ -47,7 +47,7 @@ |
| #include <linux/i2c-id.h> |
| #include <linux/workqueue.h> |
| |
| -#include <media/ir-common.h> |
| +#include <media/ir-core.h> |
| #include <media/ir-kbd-i2c.h> |
| |
| /* ----------------------------------------------------------------------- */ |
| @@ -272,11 +272,8 @@ static void ir_key_poll(struct IR_i2c *ir) |
| return; |
| } |
| |
| - if (0 == rc) { |
| - ir_input_nokey(ir->input, &ir->ir); |
| - } else { |
| - ir_input_keydown(ir->input, &ir->ir, ir_key); |
| - } |
| + if (rc) |
| + ir_keydown(ir->input, ir_key, 0); |
| } |
| |
| static void ir_work(struct work_struct *work) |
| @@ -439,10 +436,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) |
| dev_name(&client->dev)); |
| |
| /* init + register input device */ |
| - err = ir_input_init(input_dev, &ir->ir, ir_type); |
| - if (err < 0) |
| - goto err_out_free; |
| - |
| + ir->ir_type = ir_type; |
| input_dev->id.bustype = BUS_I2C; |
| input_dev->name = ir->name; |
| input_dev->phys = ir->phys; |
| diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c |
| index b482478..bba6115 100644 |
| |
| |
| @@ -223,7 +223,10 @@ int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp) |
| " pvr2_ioread_setup (setup) id=%p",cp); |
| pvr2_stream_kill(sp); |
| ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT); |
| - if (ret < 0) return ret; |
| + if (ret < 0) { |
| + mutex_unlock(&cp->mutex); |
| + return ret; |
| + } |
| for (idx = 0; idx < BUFFER_COUNT; idx++) { |
| bp = pvr2_stream_get_buffer(sp,idx); |
| pvr2_buffer_set_buffer(bp, |
| diff --git a/include/linux/input.h b/include/linux/input.h |
| index 6fcc910..fe2633c 100644 |
| |
| |
| @@ -34,7 +34,7 @@ struct input_event { |
| * Protocol version. |
| */ |
| |
| -#define EV_VERSION 0x010000 |
| +#define EV_VERSION 0x010001 |
| |
| /* |
| * IOCTLs (0x00 - 0x7f) |
| @@ -56,12 +56,22 @@ struct input_absinfo { |
| __s32 resolution; |
| }; |
| |
| +struct keycode_table_entry { |
| + __u32 keycode; /* e.g. KEY_A */ |
| + __u32 index; /* Index for the given scan/key table, on EVIOCGKEYCODEBIG */ |
| + __u32 len; /* Length of the scancode */ |
| + __u32 reserved[2]; /* Reserved for future usage */ |
| + char *scancode; /* scancode, in machine-endian */ |
| +}; |
| + |
| #define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ |
| #define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ |
| #define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */ |
| #define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) /* set repeat settings */ |
| #define EVIOCGKEYCODE _IOR('E', 0x04, unsigned int[2]) /* get keycode */ |
| #define EVIOCSKEYCODE _IOW('E', 0x04, unsigned int[2]) /* set keycode */ |
| +#define EVIOCGKEYCODEBIG _IOR('E', 0x04, struct keycode_table_entry) /* get keycode */ |
| +#define EVIOCSKEYCODEBIG _IOW('E', 0x04, struct keycode_table_entry) /* set keycode */ |
| |
| #define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ |
| #define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) /* get physical location */ |
| @@ -1066,13 +1076,22 @@ struct ff_effect { |
| * @keycodemax: size of keycode table |
| * @keycodesize: size of elements in keycode table |
| * @keycode: map of scancodes to keycodes for this device |
| - * @setkeycode: optional method to alter current keymap, used to implement |
| + * @setkeycode: optional legacy method to alter current keymap, used to |
| + * implement sparse keymaps. Shouldn't be used on new drivers |
| + * @getkeycode: optional legacy method to retrieve current keymap. |
| + * Shouldn't be used on new drivers. |
| + * @setkeycodebig: optional method to alter current keymap, used to implement |
| * sparse keymaps. If not supplied default mechanism will be used. |
| * The method is being called while holding event_lock and thus must |
| * not sleep |
| - * @getkeycode: optional method to retrieve current keymap. If not supplied |
| - * default mechanism will be used. The method is being called while |
| - * holding event_lock and thus must not sleep |
| + * @getkeycodebig_from_index: optional method to retrieve current keymap from |
| + * an array index. If not supplied default mechanism will be used. |
| + * The method is being called while holding event_lock and thus must |
| + * not sleep |
| + * @getkeycodebig_from_scancode: optional method to retrieve current keymap |
| + * from an scancode. If not supplied default mechanism will be used. |
| + * The method is being called while holding event_lock and thus must |
| + * not sleep |
| * @ff: force feedback structure associated with the device if device |
| * supports force feedback effects |
| * @repeat_key: stores key code of the last key pressed; used to implement |
| @@ -1147,6 +1166,12 @@ struct input_dev { |
| unsigned int scancode, unsigned int keycode); |
| int (*getkeycode)(struct input_dev *dev, |
| unsigned int scancode, unsigned int *keycode); |
| + int (*setkeycodebig)(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry); |
| + int (*getkeycodebig_from_index)(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry); |
| + int (*getkeycodebig_from_scancode)(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry); |
| |
| struct ff_device *ff; |
| |
| @@ -1422,6 +1447,10 @@ int input_get_keycode(struct input_dev *dev, |
| unsigned int scancode, unsigned int *keycode); |
| int input_set_keycode(struct input_dev *dev, |
| unsigned int scancode, unsigned int keycode); |
| +int input_get_keycode_big(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry); |
| +int input_set_keycode_big(struct input_dev *dev, |
| + struct keycode_table_entry *kt_entry); |
| |
| extern struct class input_class; |
| |
| diff --git a/include/media/ir-core.h b/include/media/ir-core.h |
| index ad1303f..513e60d 100644 |
| |
| |
| @@ -47,15 +47,21 @@ enum rc_driver_type { |
| * is opened. |
| * @close: callback to allow drivers to disable polling/irq when IR input device |
| * is opened. |
| + * @s_tx_mask: set transmitter mask (for devices with multiple tx outputs) |
| + * @s_tx_carrier: set transmit carrier frequency |
| + * @tx_ir: transmit IR |
| */ |
| struct ir_dev_props { |
| enum rc_driver_type driver_type; |
| unsigned long allowed_protos; |
| u32 scanmask; |
| - void *priv; |
| + void *priv; |
| int (*change_protocol)(void *priv, u64 ir_type); |
| int (*open)(void *priv); |
| void (*close)(void *priv); |
| + int (*s_tx_mask)(void *priv, u32 mask); |
| + int (*s_tx_carrier)(void *priv, u32 carrier); |
| + int (*tx_ir)(void *priv, int *txbuf, u32 n); |
| }; |
| |
| struct ir_input_dev { |
| diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h |
| index 0506e45..5e96d7a 100644 |
| |
| |
| @@ -11,7 +11,7 @@ struct IR_i2c { |
| struct i2c_client *c; |
| struct input_dev *input; |
| struct ir_input_state ir; |
| - |
| + u64 ir_type; |
| /* Used to avoid fast repeating */ |
| unsigned char old; |
| |
| diff --git a/include/media/lirc.h b/include/media/lirc.h |
| new file mode 100644 |
| index 0000000..42c467c |
| |
| |
| @@ -0,0 +1,165 @@ |
| +/* |
| + * lirc.h - linux infrared remote control header file |
| + * last modified 2010/07/13 by Jarod Wilson |
| + */ |
| + |
| +#ifndef _LINUX_LIRC_H |
| +#define _LINUX_LIRC_H |
| + |
| +#include <linux/types.h> |
| +#include <linux/ioctl.h> |
| + |
| +#define PULSE_BIT 0x01000000 |
| +#define PULSE_MASK 0x00FFFFFF |
| + |
| +#define LIRC_MODE2_SPACE 0x00000000 |
| +#define LIRC_MODE2_PULSE 0x01000000 |
| +#define LIRC_MODE2_FREQUENCY 0x02000000 |
| +#define LIRC_MODE2_TIMEOUT 0x03000000 |
| + |
| +#define LIRC_VALUE_MASK 0x00FFFFFF |
| +#define LIRC_MODE2_MASK 0xFF000000 |
| + |
| +#define LIRC_SPACE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_SPACE) |
| +#define LIRC_PULSE(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_PULSE) |
| +#define LIRC_FREQUENCY(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_FREQUENCY) |
| +#define LIRC_TIMEOUT(val) (((val)&LIRC_VALUE_MASK) | LIRC_MODE2_TIMEOUT) |
| + |
| +#define LIRC_VALUE(val) ((val)&LIRC_VALUE_MASK) |
| +#define LIRC_MODE2(val) ((val)&LIRC_MODE2_MASK) |
| + |
| +#define LIRC_IS_SPACE(val) (LIRC_MODE2(val) == LIRC_MODE2_SPACE) |
| +#define LIRC_IS_PULSE(val) (LIRC_MODE2(val) == LIRC_MODE2_PULSE) |
| +#define LIRC_IS_FREQUENCY(val) (LIRC_MODE2(val) == LIRC_MODE2_FREQUENCY) |
| +#define LIRC_IS_TIMEOUT(val) (LIRC_MODE2(val) == LIRC_MODE2_TIMEOUT) |
| + |
| +/* used heavily by lirc userspace */ |
| +#define lirc_t int |
| + |
| +/*** lirc compatible hardware features ***/ |
| + |
| +#define LIRC_MODE2SEND(x) (x) |
| +#define LIRC_SEND2MODE(x) (x) |
| +#define LIRC_MODE2REC(x) ((x) << 16) |
| +#define LIRC_REC2MODE(x) ((x) >> 16) |
| + |
| +#define LIRC_MODE_RAW 0x00000001 |
| +#define LIRC_MODE_PULSE 0x00000002 |
| +#define LIRC_MODE_MODE2 0x00000004 |
| +#define LIRC_MODE_LIRCCODE 0x00000010 |
| + |
| + |
| +#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW) |
| +#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE) |
| +#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2) |
| +#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE) |
| + |
| +#define LIRC_CAN_SEND_MASK 0x0000003f |
| + |
| +#define LIRC_CAN_SET_SEND_CARRIER 0x00000100 |
| +#define LIRC_CAN_SET_SEND_DUTY_CYCLE 0x00000200 |
| +#define LIRC_CAN_SET_TRANSMITTER_MASK 0x00000400 |
| + |
| +#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW) |
| +#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE) |
| +#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2) |
| +#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE) |
| + |
| +#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK) |
| + |
| +#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16) |
| +#define LIRC_CAN_SET_REC_DUTY_CYCLE (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16) |
| + |
| +#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000 |
| +#define LIRC_CAN_SET_REC_CARRIER_RANGE 0x80000000 |
| +#define LIRC_CAN_GET_REC_RESOLUTION 0x20000000 |
| +#define LIRC_CAN_SET_REC_TIMEOUT 0x10000000 |
| +#define LIRC_CAN_SET_REC_FILTER 0x08000000 |
| + |
| +#define LIRC_CAN_MEASURE_CARRIER 0x02000000 |
| + |
| +#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK) |
| +#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK) |
| + |
| +#define LIRC_CAN_NOTIFY_DECODE 0x01000000 |
| + |
| +/*** IOCTL commands for lirc driver ***/ |
| + |
| +#define LIRC_GET_FEATURES _IOR('i', 0x00000000, __u32) |
| + |
| +#define LIRC_GET_SEND_MODE _IOR('i', 0x00000001, __u32) |
| +#define LIRC_GET_REC_MODE _IOR('i', 0x00000002, __u32) |
| +#define LIRC_GET_SEND_CARRIER _IOR('i', 0x00000003, __u32) |
| +#define LIRC_GET_REC_CARRIER _IOR('i', 0x00000004, __u32) |
| +#define LIRC_GET_SEND_DUTY_CYCLE _IOR('i', 0x00000005, __u32) |
| +#define LIRC_GET_REC_DUTY_CYCLE _IOR('i', 0x00000006, __u32) |
| +#define LIRC_GET_REC_RESOLUTION _IOR('i', 0x00000007, __u32) |
| + |
| +#define LIRC_GET_MIN_TIMEOUT _IOR('i', 0x00000008, __u32) |
| +#define LIRC_GET_MAX_TIMEOUT _IOR('i', 0x00000009, __u32) |
| + |
| +#define LIRC_GET_MIN_FILTER_PULSE _IOR('i', 0x0000000a, __u32) |
| +#define LIRC_GET_MAX_FILTER_PULSE _IOR('i', 0x0000000b, __u32) |
| +#define LIRC_GET_MIN_FILTER_SPACE _IOR('i', 0x0000000c, __u32) |
| +#define LIRC_GET_MAX_FILTER_SPACE _IOR('i', 0x0000000d, __u32) |
| + |
| +/* code length in bits, currently only for LIRC_MODE_LIRCCODE */ |
| +#define LIRC_GET_LENGTH _IOR('i', 0x0000000f, __u32) |
| + |
| +#define LIRC_SET_SEND_MODE _IOW('i', 0x00000011, __u32) |
| +#define LIRC_SET_REC_MODE _IOW('i', 0x00000012, __u32) |
| +/* Note: these can reset the according pulse_width */ |
| +#define LIRC_SET_SEND_CARRIER _IOW('i', 0x00000013, __u32) |
| +#define LIRC_SET_REC_CARRIER _IOW('i', 0x00000014, __u32) |
| +#define LIRC_SET_SEND_DUTY_CYCLE _IOW('i', 0x00000015, __u32) |
| +#define LIRC_SET_REC_DUTY_CYCLE _IOW('i', 0x00000016, __u32) |
| +#define LIRC_SET_TRANSMITTER_MASK _IOW('i', 0x00000017, __u32) |
| + |
| +/* |
| + * when a timeout != 0 is set the driver will send a |
| + * LIRC_MODE2_TIMEOUT data packet, otherwise LIRC_MODE2_TIMEOUT is |
| + * never sent, timeout is disabled by default |
| + */ |
| +#define LIRC_SET_REC_TIMEOUT _IOW('i', 0x00000018, __u32) |
| + |
| +/* 1 enables, 0 disables timeout reports in MODE2 */ |
| +#define LIRC_SET_REC_TIMEOUT_REPORTS _IOW('i', 0x00000019, __u32) |
| + |
| +/* |
| + * pulses shorter than this are filtered out by hardware (software |
| + * emulation in lirc_dev?) |
| + */ |
| +#define LIRC_SET_REC_FILTER_PULSE _IOW('i', 0x0000001a, __u32) |
| +/* |
| + * spaces shorter than this are filtered out by hardware (software |
| + * emulation in lirc_dev?) |
| + */ |
| +#define LIRC_SET_REC_FILTER_SPACE _IOW('i', 0x0000001b, __u32) |
| +/* |
| + * if filter cannot be set independantly for pulse/space, this should |
| + * be used |
| + */ |
| +#define LIRC_SET_REC_FILTER _IOW('i', 0x0000001c, __u32) |
| + |
| +/* |
| + * if enabled from the next key press on the driver will send |
| + * LIRC_MODE2_FREQUENCY packets |
| + */ |
| +#define LIRC_SET_MEASURE_CARRIER_MODE _IOW('i', 0x0000001d, __u32) |
| + |
| +/* |
| + * to set a range use |
| + * LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the |
| + * lower bound first and later |
| + * LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound |
| + */ |
| + |
| +#define LIRC_SET_REC_DUTY_CYCLE_RANGE _IOW('i', 0x0000001e, __u32) |
| +#define LIRC_SET_REC_CARRIER_RANGE _IOW('i', 0x0000001f, __u32) |
| + |
| +#define LIRC_NOTIFY_DECODE _IO('i', 0x00000020) |
| + |
| +#define LIRC_SETUP_START _IO('i', 0x00000021) |
| +#define LIRC_SETUP_END _IO('i', 0x00000022) |
| + |
| +#endif |
| diff --git a/include/media/lirc_dev.h b/include/media/lirc_dev.h |
| new file mode 100644 |
| index 0000000..b1f6066 |
| |
| |
| @@ -0,0 +1,225 @@ |
| +/* |
| + * LIRC base driver |
| + * |
| + * by Artur Lipowski <alipowski@interia.pl> |
| + * This code is licensed under GNU GPL |
| + * |
| + */ |
| + |
| +#ifndef _LINUX_LIRC_DEV_H |
| +#define _LINUX_LIRC_DEV_H |
| + |
| +#define MAX_IRCTL_DEVICES 4 |
| +#define BUFLEN 16 |
| + |
| +#define mod(n, div) ((n) % (div)) |
| + |
| +#include <linux/slab.h> |
| +#include <linux/fs.h> |
| +#include <linux/ioctl.h> |
| +#include <linux/poll.h> |
| +#include <linux/kfifo.h> |
| +#include <media/lirc.h> |
| + |
| +struct lirc_buffer { |
| + wait_queue_head_t wait_poll; |
| + spinlock_t fifo_lock; |
| + unsigned int chunk_size; |
| + unsigned int size; /* in chunks */ |
| + /* Using chunks instead of bytes pretends to simplify boundary checking |
| + * And should allow for some performance fine tunning later */ |
| + struct kfifo fifo; |
| + u8 fifo_initialized; |
| +}; |
| + |
| +static inline void lirc_buffer_clear(struct lirc_buffer *buf) |
| +{ |
| + unsigned long flags; |
| + |
| + if (buf->fifo_initialized) { |
| + spin_lock_irqsave(&buf->fifo_lock, flags); |
| + kfifo_reset(&buf->fifo); |
| + spin_unlock_irqrestore(&buf->fifo_lock, flags); |
| + } else |
| + WARN(1, "calling %s on an uninitialized lirc_buffer\n", |
| + __func__); |
| +} |
| + |
| +static inline int lirc_buffer_init(struct lirc_buffer *buf, |
| + unsigned int chunk_size, |
| + unsigned int size) |
| +{ |
| + int ret; |
| + |
| + init_waitqueue_head(&buf->wait_poll); |
| + spin_lock_init(&buf->fifo_lock); |
| + buf->chunk_size = chunk_size; |
| + buf->size = size; |
| + ret = kfifo_alloc(&buf->fifo, size * chunk_size, GFP_KERNEL); |
| + if (ret == 0) |
| + buf->fifo_initialized = 1; |
| + |
| + return ret; |
| +} |
| + |
| +static inline void lirc_buffer_free(struct lirc_buffer *buf) |
| +{ |
| + if (buf->fifo_initialized) { |
| + kfifo_free(&buf->fifo); |
| + buf->fifo_initialized = 0; |
| + } else |
| + WARN(1, "calling %s on an uninitialized lirc_buffer\n", |
| + __func__); |
| +} |
| + |
| +static inline int lirc_buffer_len(struct lirc_buffer *buf) |
| +{ |
| + int len; |
| + unsigned long flags; |
| + |
| + spin_lock_irqsave(&buf->fifo_lock, flags); |
| + len = kfifo_len(&buf->fifo); |
| + spin_unlock_irqrestore(&buf->fifo_lock, flags); |
| + |
| + return len; |
| +} |
| + |
| +static inline int lirc_buffer_full(struct lirc_buffer *buf) |
| +{ |
| + return lirc_buffer_len(buf) == buf->size * buf->chunk_size; |
| +} |
| + |
| +static inline int lirc_buffer_empty(struct lirc_buffer *buf) |
| +{ |
| + return !lirc_buffer_len(buf); |
| +} |
| + |
| +static inline int lirc_buffer_available(struct lirc_buffer *buf) |
| +{ |
| + return buf->size - (lirc_buffer_len(buf) / buf->chunk_size); |
| +} |
| + |
| +static inline unsigned int lirc_buffer_read(struct lirc_buffer *buf, |
| + unsigned char *dest) |
| +{ |
| + unsigned int ret = 0; |
| + |
| + if (lirc_buffer_len(buf) >= buf->chunk_size) |
| + ret = kfifo_out_locked(&buf->fifo, dest, buf->chunk_size, |
| + &buf->fifo_lock); |
| + return ret; |
| + |
| +} |
| + |
| +static inline unsigned int lirc_buffer_write(struct lirc_buffer *buf, |
| + unsigned char *orig) |
| +{ |
| + unsigned int ret; |
| + |
| + ret = kfifo_in_locked(&buf->fifo, orig, buf->chunk_size, |
| + &buf->fifo_lock); |
| + |
| + return ret; |
| +} |
| + |
| +struct lirc_driver { |
| + char name[40]; |
| + int minor; |
| + unsigned long code_length; |
| + unsigned int buffer_size; /* in chunks holding one code each */ |
| + int sample_rate; |
| + unsigned long features; |
| + |
| + unsigned int chunk_size; |
| + |
| + void *data; |
| + int min_timeout; |
| + int max_timeout; |
| + int (*add_to_buf) (void *data, struct lirc_buffer *buf); |
| + struct lirc_buffer *rbuf; |
| + int (*set_use_inc) (void *data); |
| + void (*set_use_dec) (void *data); |
| + struct file_operations *fops; |
| + struct device *dev; |
| + struct module *owner; |
| +}; |
| + |
| +/* name: |
| + * this string will be used for logs |
| + * |
| + * minor: |
| + * indicates minor device (/dev/lirc) number for registered driver |
| + * if caller fills it with negative value, then the first free minor |
| + * number will be used (if available) |
| + * |
| + * code_length: |
| + * length of the remote control key code expressed in bits |
| + * |
| + * sample_rate: |
| + * |
| + * data: |
| + * it may point to any driver data and this pointer will be passed to |
| + * all callback functions |
| + * |
| + * add_to_buf: |
| + * add_to_buf will be called after specified period of the time or |
| + * triggered by the external event, this behavior depends on value of |
| + * the sample_rate this function will be called in user context. This |
| + * routine should return 0 if data was added to the buffer and |
| + * -ENODATA if none was available. This should add some number of bits |
| + * evenly divisible by code_length to the buffer |
| + * |
| + * rbuf: |
| + * if not NULL, it will be used as a read buffer, you will have to |
| + * write to the buffer by other means, like irq's (see also |
| + * lirc_serial.c). |
| + * |
| + * set_use_inc: |
| + * set_use_inc will be called after device is opened |
| + * |
| + * set_use_dec: |
| + * set_use_dec will be called after device is closed |
| + * |
| + * fops: |
| + * file_operations for drivers which don't fit the current driver model. |
| + * |
| + * Some ioctl's can be directly handled by lirc_dev if the driver's |
| + * ioctl function is NULL or if it returns -ENOIOCTLCMD (see also |
| + * lirc_serial.c). |
| + * |
| + * owner: |
| + * the module owning this struct |
| + * |
| + */ |
| + |
| + |
| +/* following functions can be called ONLY from user context |
| + * |
| + * returns negative value on error or minor number |
| + * of the registered device if success |
| + * contents of the structure pointed by p is copied |
| + */ |
| +extern int lirc_register_driver(struct lirc_driver *d); |
| + |
| +/* returns negative value on error or 0 if success |
| +*/ |
| +extern int lirc_unregister_driver(int minor); |
| + |
| +/* Returns the private data stored in the lirc_driver |
| + * associated with the given device file pointer. |
| + */ |
| +void *lirc_get_pdata(struct file *file); |
| + |
| +/* default file operations |
| + * used by drivers if they override only some operations |
| + */ |
| +int lirc_dev_fop_open(struct inode *inode, struct file *file); |
| +int lirc_dev_fop_close(struct inode *inode, struct file *file); |
| +unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait); |
| +long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg); |
| +ssize_t lirc_dev_fop_read(struct file *file, char *buffer, size_t length, |
| + loff_t *ppos); |
| +ssize_t lirc_dev_fop_write(struct file *file, const char *buffer, size_t length, |
| + loff_t *ppos); |
| + |
| +#endif |
| diff --git a/include/media/rc-map.h b/include/media/rc-map.h |
| index c78e99a..a329858 100644 |
| |
| |
| @@ -17,8 +17,13 @@ |
| #define IR_TYPE_RC6 (1 << 2) /* Philips RC6 protocol */ |
| #define IR_TYPE_JVC (1 << 3) /* JVC protocol */ |
| #define IR_TYPE_SONY (1 << 4) /* Sony12/15/20 protocol */ |
| +#define IR_TYPE_LIRC (1 << 30) /* Pass raw IR to lirc userspace */ |
| #define IR_TYPE_OTHER (1u << 31) |
| |
| +#define IR_TYPE_ALL (IR_TYPE_RC5 | IR_TYPE_NEC | IR_TYPE_RC6 | \ |
| + IR_TYPE_JVC | IR_TYPE_SONY | IR_TYPE_LIRC | \ |
| + IR_TYPE_OTHER) |
| + |
| struct ir_scancode { |
| u32 scancode; |
| u32 keycode; |
| @@ -87,6 +92,7 @@ void rc_map_init(void); |
| #define RC_MAP_KAIOMY "rc-kaiomy" |
| #define RC_MAP_KWORLD_315U "rc-kworld-315u" |
| #define RC_MAP_KWORLD_PLUS_TV_ANALOG "rc-kworld-plus-tv-analog" |
| +#define RC_MAP_LIRC "rc-lirc" |
| #define RC_MAP_MANLI "rc-manli" |
| #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" |
| #define RC_MAP_MSI_TVANYWHERE "rc-msi-tvanywhere" |
| @@ -107,6 +113,7 @@ void rc_map_init(void); |
| #define RC_MAP_PV951 "rc-pv951" |
| #define RC_MAP_RC5_HAUPPAUGE_NEW "rc-rc5-hauppauge-new" |
| #define RC_MAP_RC5_TV "rc-rc5-tv" |
| +#define RC_MAP_RC6_MCE "rc-rc6-mce" |
| #define RC_MAP_REAL_AUDIO_220_32_KEYS "rc-real-audio-220-32-keys" |
| #define RC_MAP_TBS_NEC "rc-tbs-nec" |
| #define RC_MAP_TERRATEC_CINERGY_XS "rc-terratec-cinergy-xs" |