summaryrefslogtreecommitdiff
path: root/drivers/media/rc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/rc')
-rw-r--r--drivers/media/rc/Kconfig11
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/imon.c36
-rw-r--r--drivers/media/rc/ite-cir.c60
-rw-r--r--drivers/media/rc/keymaps/Makefile1
-rw-r--r--drivers/media/rc/keymaps/rc-avermedia-cardbus.c2
-rw-r--r--drivers/media/rc/keymaps/rc-imon-mce.c2
-rw-r--r--drivers/media/rc/keymaps/rc-imon-pad.c6
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c2
-rw-r--r--drivers/media/rc/keymaps/rc-rc6-mce.c4
-rw-r--r--drivers/media/rc/keymaps/rc-tivo.c98
-rw-r--r--drivers/media/rc/keymaps/rc-winfast.c4
-rw-r--r--drivers/media/rc/mceusb.c34
-rw-r--r--drivers/media/rc/nuvoton-cir.c75
-rw-r--r--drivers/media/rc/nuvoton-cir.h17
-rw-r--r--drivers/media/rc/rc-loopback.c6
-rw-r--r--drivers/media/rc/rc-main.c54
-rw-r--r--drivers/media/rc/redrat3.c1344
-rw-r--r--drivers/media/rc/winbond-cir.c447
19 files changed, 1998 insertions, 206 deletions
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 7f03142a329f..154c337f00fd 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -161,6 +161,17 @@ config IR_NUVOTON
To compile this driver as a module, choose M here: the
module will be called nuvoton-cir.
+config IR_REDRAT3
+ tristate "RedRat3 IR Transceiver"
+ depends on USB_ARCH_HAS_HCD
+ depends on RC_CORE
+ select USB
+ ---help---
+ Say Y here if you want to use a RedRat3 Infrared Transceiver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called redrat3.
+
config IR_STREAMZAP
tristate "Streamzap PC Remote IR Receiver"
depends on USB_ARCH_HAS_HCD
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index c6cfe70d862f..1f90a219a162 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_IR_ITE_CIR) += ite-cir.o
obj-$(CONFIG_IR_MCEUSB) += mceusb.o
obj-$(CONFIG_IR_NUVOTON) += nuvoton-cir.o
obj-$(CONFIG_IR_ENE) += ene_ir.o
+obj-$(CONFIG_IR_REDRAT3) += redrat3.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c
index 8fc0f081b470..3f3c70716268 100644
--- a/drivers/media/rc/imon.c
+++ b/drivers/media/rc/imon.c
@@ -443,16 +443,6 @@ static int display_close(struct inode *inode, struct file *file)
} else {
ictx->display_isopen = false;
dev_dbg(ictx->dev, "display port closed\n");
- if (!ictx->dev_present_intf0) {
- /*
- * Device disconnected before close and IR port is not
- * open. If IR port is open, context will be deleted by
- * ir_close.
- */
- mutex_unlock(&ictx->lock);
- free_imon_context(ictx);
- return retval;
- }
}
mutex_unlock(&ictx->lock);
@@ -1492,7 +1482,6 @@ static void imon_incoming_packet(struct imon_context *ictx,
struct device *dev = ictx->dev;
unsigned long flags;
u32 kc;
- bool norelease = false;
int i;
u64 scancode;
int press_type = 0;
@@ -1560,7 +1549,6 @@ static void imon_incoming_packet(struct imon_context *ictx,
!(buf[1] & 0x1 || buf[1] >> 2 & 0x1))) {
len = 8;
imon_pad_to_keys(ictx, buf);
- norelease = true;
}
if (debug) {
@@ -1982,7 +1970,7 @@ static struct input_dev *imon_init_touch(struct imon_context *ictx)
return touch;
touch_register_failed:
- input_free_device(ictx->touch);
+ input_free_device(touch);
touch_alloc_failed:
return NULL;
@@ -2274,14 +2262,12 @@ static int __devinit imon_probe(struct usb_interface *interface,
struct usb_host_interface *iface_desc = NULL;
struct usb_interface *first_if;
struct device *dev = &interface->dev;
- int ifnum, code_length, sysfs_err;
+ int ifnum, sysfs_err;
int ret = 0;
struct imon_context *ictx = NULL;
struct imon_context *first_if_ctx = NULL;
u16 vendor, product;
- code_length = BUF_CHUNK_SIZE * 8;
-
usbdev = usb_get_dev(interface_to_usbdev(interface));
iface_desc = interface->cur_altsetting;
ifnum = iface_desc->desc.bInterfaceNumber;
@@ -2366,8 +2352,6 @@ static void __devexit imon_disconnect(struct usb_interface *interface)
dev = ictx->dev;
ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
- mutex_lock(&ictx->lock);
-
/*
* sysfs_remove_group is safe to call even if sysfs_create_group
* hasn't been called
@@ -2391,24 +2375,20 @@ static void __devexit imon_disconnect(struct usb_interface *interface)
if (ictx->display_supported) {
if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
usb_deregister_dev(interface, &imon_lcd_class);
- else
+ else if (ictx->display_type == IMON_DISPLAY_TYPE_VFD)
usb_deregister_dev(interface, &imon_vfd_class);
}
} else {
ictx->dev_present_intf1 = false;
usb_kill_urb(ictx->rx_urb_intf1);
- if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
+ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
input_unregister_device(ictx->touch);
+ del_timer_sync(&ictx->ttimer);
+ }
}
- if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1) {
- if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
- del_timer_sync(&ictx->ttimer);
- mutex_unlock(&ictx->lock);
- if (!ictx->display_isopen)
- free_imon_context(ictx);
- } else
- mutex_unlock(&ictx->lock);
+ if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1)
+ free_imon_context(ictx);
mutex_unlock(&driver_lock);
diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c
index 43908a70bd8b..e716b931cf7e 100644
--- a/drivers/media/rc/ite-cir.c
+++ b/drivers/media/rc/ite-cir.c
@@ -1250,11 +1250,9 @@ static void it8709_disable(struct ite_dev *dev)
ite_dbg("%s called", __func__);
/* clear out all interrupt enable flags */
- it8709_wr(dev,
- it8709_rr(dev,
- IT85_C0IER) & ~(IT85_IEC | IT85_RFOIE |
- IT85_RDAIE |
- IT85_TLDLIE), IT85_C0IER);
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+ ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+ IT85_C0IER);
/* disable the receiver */
it8709_disable_rx(dev);
@@ -1270,11 +1268,9 @@ static void it8709_init_hardware(struct ite_dev *dev)
ite_dbg("%s called", __func__);
/* disable all the interrupts */
- it8709_wr(dev,
- it8709_rr(dev,
- IT85_C0IER) & ~(IT85_IEC | IT85_RFOIE |
- IT85_RDAIE |
- IT85_TLDLIE), IT85_C0IER);
+ it8709_wr(dev, it8709_rr(dev, IT85_C0IER) &
+ ~(IT85_IEC | IT85_RFOIE | IT85_RDAIE | IT85_TLDLIE),
+ IT85_C0IER);
/* program the baud rate divisor */
it8709_wr(dev, ITE_BAUDRATE_DIVISOR & 0xff, IT85_C0BDLR);
@@ -1282,28 +1278,22 @@ static void it8709_init_hardware(struct ite_dev *dev)
IT85_C0BDHR);
/* program the C0MSTCR register defaults */
- it8709_wr(dev, (it8709_rr(dev, IT85_C0MSTCR) & ~(IT85_ILSEL |
- IT85_ILE
- | IT85_FIFOTL
- |
- IT85_FIFOCLR
- |
- IT85_RESET))
- | IT85_FIFOTL_DEFAULT, IT85_C0MSTCR);
+ it8709_wr(dev, (it8709_rr(dev, IT85_C0MSTCR) &
+ ~(IT85_ILSEL | IT85_ILE | IT85_FIFOTL
+ | IT85_FIFOCLR | IT85_RESET)) | IT85_FIFOTL_DEFAULT,
+ IT85_C0MSTCR);
/* program the C0RCR register defaults */
- it8709_wr(dev,
- (it8709_rr(dev, IT85_C0RCR) &
- ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND
- | IT85_RXACT | IT85_RXDCR)) |
- ITE_RXDCR_DEFAULT, IT85_C0RCR);
+ it8709_wr(dev, (it8709_rr(dev, IT85_C0RCR) &
+ ~(IT85_RXEN | IT85_RDWOS | IT85_RXEND | IT85_RXACT
+ | IT85_RXDCR)) | ITE_RXDCR_DEFAULT,
+ IT85_C0RCR);
/* program the C0TCR register defaults */
- it8709_wr(dev, (it8709_rr(dev, IT85_C0TCR)
- &~(IT85_TXMPM | IT85_TXMPW))
- |IT85_TXRLE | IT85_TXENDF |
- IT85_TXMPM_DEFAULT |
- IT85_TXMPW_DEFAULT, IT85_C0TCR);
+ it8709_wr(dev, (it8709_rr(dev, IT85_C0TCR) & ~(IT85_TXMPM | IT85_TXMPW))
+ | IT85_TXRLE | IT85_TXENDF | IT85_TXMPM_DEFAULT
+ | IT85_TXMPW_DEFAULT,
+ IT85_C0TCR);
/* program the carrier parameters */
ite_set_carrier_params(dev);
@@ -1660,6 +1650,9 @@ static int ite_suspend(struct pnp_dev *pdev, pm_message_t state)
ite_dbg("%s called", __func__);
+ /* wait for any transmission to end */
+ wait_event_interruptible(dev->tx_ended, !dev->transmitting);
+
spin_lock_irqsave(&dev->lock, flags);
/* disable all interrupts */
@@ -1680,13 +1673,10 @@ static int ite_resume(struct pnp_dev *pdev)
spin_lock_irqsave(&dev->lock, flags);
- if (dev->transmitting) {
- /* wake up the transmitter */
- wake_up_interruptible(&dev->tx_queue);
- } else {
- /* enable the receiver */
- dev->params.enable_rx(dev);
- }
+ /* reinitialize hardware config registers */
+ dev->params.init_hardware(dev);
+ /* enable the receiver */
+ dev->params.enable_rx(dev);
spin_unlock_irqrestore(&dev->lock, flags);
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 85cac7ddbcec..b57fc83fb4d2 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-terratec-slim.o \
rc-terratec-slim-2.o \
rc-tevii-nec.o \
+ rc-tivo.o \
rc-total-media-in-hand.o \
rc-trekstor.o \
rc-tt-1500.o \
diff --git a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
index bdf97b74cf90..22f54d413a35 100644
--- a/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
+++ b/drivers/media/rc/keymaps/rc-avermedia-cardbus.c
@@ -52,7 +52,7 @@ static struct rc_map_table avermedia_cardbus[] = {
{ 0x28, KEY_SELECT }, /* Select */
{ 0x29, KEY_BLUE }, /* Blue/Picture */
{ 0x2a, KEY_BACKSPACE }, /* Back */
- { 0x2b, KEY_MEDIA }, /* PIP (Picture-in-picture) */
+ { 0x2b, KEY_VIDEO }, /* PIP (Picture-in-picture) */
{ 0x2c, KEY_DOWN },
{ 0x2e, KEY_DOT },
{ 0x2f, KEY_TV }, /* Live TV */
diff --git a/drivers/media/rc/keymaps/rc-imon-mce.c b/drivers/media/rc/keymaps/rc-imon-mce.c
index 937a81989f00..0ea2aa190d81 100644
--- a/drivers/media/rc/keymaps/rc-imon-mce.c
+++ b/drivers/media/rc/keymaps/rc-imon-mce.c
@@ -111,7 +111,7 @@ static struct rc_map_table imon_mce[] = {
{ 0x800ff44d, KEY_TITLE },
{ 0x800ff40c, KEY_POWER },
- { 0x800ff40d, KEY_LEFTMETA }, /* Windows MCE button */
+ { 0x800ff40d, KEY_MEDIA }, /* Windows MCE button */
};
diff --git a/drivers/media/rc/keymaps/rc-imon-pad.c b/drivers/media/rc/keymaps/rc-imon-pad.c
index 63d42bd24c9e..75d3843fdc30 100644
--- a/drivers/media/rc/keymaps/rc-imon-pad.c
+++ b/drivers/media/rc/keymaps/rc-imon-pad.c
@@ -87,7 +87,7 @@ static struct rc_map_table imon_pad[] = {
{ 0x2b8515b7, KEY_VIDEO },
{ 0x299195b7, KEY_AUDIO },
- { 0x2ba115b7, KEY_CAMERA },
+ { 0x2ba115b7, KEY_IMAGES },
{ 0x28a515b7, KEY_TV },
{ 0x29a395b7, KEY_DVD },
{ 0x29a295b7, KEY_DVD },
@@ -97,7 +97,7 @@ static struct rc_map_table imon_pad[] = {
{ 0x2ba395b7, KEY_MENU },
{ 0x288515b7, KEY_BOOKMARKS },
- { 0x2ab715b7, KEY_MEDIA }, /* Thumbnail */
+ { 0x2ab715b7, KEY_CAMERA }, /* Thumbnail */
{ 0x298595b7, KEY_SUBTITLE },
{ 0x2b8595b7, KEY_LANGUAGE },
@@ -125,7 +125,7 @@ static struct rc_map_table imon_pad[] = {
{ 0x2b8195b7, KEY_CONTEXT_MENU }, /* Left Menu*/
{ 0x02000065, KEY_COMPOSE }, /* RightMenu */
{ 0x28b715b7, KEY_COMPOSE }, /* RightMenu */
- { 0x2ab195b7, KEY_LEFTMETA }, /* Go or MultiMon */
+ { 0x2ab195b7, KEY_MEDIA }, /* Go or MultiMon */
{ 0x29b715b7, KEY_DASHBOARD }, /* AppLauncher */
};
diff --git a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
index 08d183120e41..7fa17a369f2d 100644
--- a/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
+++ b/drivers/media/rc/keymaps/rc-kworld-plus-tv-analog.c
@@ -17,7 +17,7 @@
*/
static struct rc_map_table kworld_plus_tv_analog[] = {
- { 0x0c, KEY_LEFTMETA }, /* Kworld key */
+ { 0x0c, KEY_MEDIA }, /* Kworld key */
{ 0x16, KEY_CLOSECD }, /* -> ) */
{ 0x1d, KEY_POWER2 },
diff --git a/drivers/media/rc/keymaps/rc-rc6-mce.c b/drivers/media/rc/keymaps/rc-rc6-mce.c
index 8dd519ecc58e..01b69bcc8666 100644
--- a/drivers/media/rc/keymaps/rc-rc6-mce.c
+++ b/drivers/media/rc/keymaps/rc-rc6-mce.c
@@ -30,7 +30,7 @@ static struct rc_map_table rc6_mce[] = {
{ 0x800f040a, KEY_DELETE },
{ 0x800f040b, KEY_ENTER },
{ 0x800f040c, KEY_POWER }, /* PC Power */
- { 0x800f040d, KEY_LEFTMETA }, /* Windows MCE button */
+ { 0x800f040d, KEY_MEDIA }, /* Windows MCE button */
{ 0x800f040e, KEY_MUTE },
{ 0x800f040f, KEY_INFO },
@@ -87,7 +87,7 @@ static struct rc_map_table rc6_mce[] = {
{ 0x800f0465, KEY_POWER2 }, /* TV Power */
{ 0x800f046e, KEY_PLAYPAUSE },
- { 0x800f046f, KEY_MEDIA }, /* Start media application (NEW) */
+ { 0x800f046f, KEY_PLAYER }, /* Start media application (NEW) */
{ 0x800f0480, KEY_BRIGHTNESSDOWN },
{ 0x800f0481, KEY_PLAYPAUSE },
diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c
new file mode 100644
index 000000000000..98ad085531fd
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-tivo.c
@@ -0,0 +1,98 @@
+/* rc-tivo.c - Keytable for TiVo remotes
+ *
+ * Copyright (c) 2011 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>
+
+/*
+ * Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle,
+ * which also ships with a TiVo-branded IR transceiver, supported by the mceusb
+ * driver. Note that the remote uses an NEC-ish protocol, but instead of having
+ * a command/not_command pair, it has a vendor ID of 0xa10c, but some keys, the
+ * NEC extended checksums do pass, so the table presently has the intended
+ * values and the checksum-passed versions for those keys.
+ */
+static struct rc_map_table tivo[] = {
+ { 0xa10c900f, KEY_MEDIA }, /* TiVo Button */
+ { 0xa10c0807, KEY_POWER2 }, /* TV Power */
+ { 0xa10c8807, KEY_TV }, /* Live TV/Swap */
+ { 0xa10c2c03, KEY_VIDEO_NEXT }, /* TV Input */
+ { 0xa10cc807, KEY_INFO },
+ { 0xa10cfa05, KEY_CYCLEWINDOWS }, /* Window */
+ { 0x0085305f, KEY_CYCLEWINDOWS },
+ { 0xa10c6c03, KEY_EPG }, /* Guide */
+
+ { 0xa10c2807, KEY_UP },
+ { 0xa10c6807, KEY_DOWN },
+ { 0xa10ce807, KEY_LEFT },
+ { 0xa10ca807, KEY_RIGHT },
+
+ { 0xa10c1807, KEY_SCROLLDOWN }, /* Red Thumbs Down */
+ { 0xa10c9807, KEY_SELECT },
+ { 0xa10c5807, KEY_SCROLLUP }, /* Green Thumbs Up */
+
+ { 0xa10c3807, KEY_VOLUMEUP },
+ { 0xa10cb807, KEY_VOLUMEDOWN },
+ { 0xa10cd807, KEY_MUTE },
+ { 0xa10c040b, KEY_RECORD },
+ { 0xa10c7807, KEY_CHANNELUP },
+ { 0xa10cf807, KEY_CHANNELDOWN },
+ { 0x0085301f, KEY_CHANNELDOWN },
+
+ { 0xa10c840b, KEY_PLAY },
+ { 0xa10cc40b, KEY_PAUSE },
+ { 0xa10ca40b, KEY_SLOW },
+ { 0xa10c440b, KEY_REWIND },
+ { 0xa10c240b, KEY_FASTFORWARD },
+ { 0xa10c640b, KEY_PREVIOUS },
+ { 0xa10ce40b, KEY_NEXT }, /* ->| */
+
+ { 0xa10c220d, KEY_ZOOM }, /* Aspect */
+ { 0xa10c120d, KEY_STOP },
+ { 0xa10c520d, KEY_DVD }, /* DVD Menu */
+
+ { 0xa10c140b, KEY_NUMERIC_1 },
+ { 0xa10c940b, KEY_NUMERIC_2 },
+ { 0xa10c540b, KEY_NUMERIC_3 },
+ { 0xa10cd40b, KEY_NUMERIC_4 },
+ { 0xa10c340b, KEY_NUMERIC_5 },
+ { 0xa10cb40b, KEY_NUMERIC_6 },
+ { 0xa10c740b, KEY_NUMERIC_7 },
+ { 0xa10cf40b, KEY_NUMERIC_8 },
+ { 0x0085302f, KEY_NUMERIC_8 },
+ { 0xa10c0c03, KEY_NUMERIC_9 },
+ { 0xa10c8c03, KEY_NUMERIC_0 },
+ { 0xa10ccc03, KEY_ENTER },
+ { 0xa10c4c03, KEY_CLEAR },
+};
+
+static struct rc_map_list tivo_map = {
+ .map = {
+ .scan = tivo,
+ .size = ARRAY_SIZE(tivo),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_TIVO,
+ }
+};
+
+static int __init init_rc_map_tivo(void)
+{
+ return rc_map_register(&tivo_map);
+}
+
+static void __exit exit_rc_map_tivo(void)
+{
+ rc_map_unregister(&tivo_map);
+}
+
+module_init(init_rc_map_tivo)
+module_exit(exit_rc_map_tivo)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
diff --git a/drivers/media/rc/keymaps/rc-winfast.c b/drivers/media/rc/keymaps/rc-winfast.c
index 0062ca291959..d8a34c14676a 100644
--- a/drivers/media/rc/keymaps/rc-winfast.c
+++ b/drivers/media/rc/keymaps/rc-winfast.c
@@ -32,8 +32,8 @@ static struct rc_map_table winfast[] = {
{ 0x02, KEY_TUNER }, /* TV/FM, not on Y0400052 */
{ 0x1e, KEY_VIDEO }, /* Video Source */
{ 0x16, KEY_INFO }, /* Display information */
- { 0x04, KEY_LEFT },
- { 0x08, KEY_RIGHT },
+ { 0x04, KEY_RIGHT },
+ { 0x08, KEY_LEFT },
{ 0x0c, KEY_UP },
{ 0x10, KEY_DOWN },
{ 0x03, KEY_ZOOM }, /* fullscreen */
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 0c273ec465c9..ad927fcaa020 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -149,6 +149,8 @@ enum mceusb_model_type {
POLARIS_EVK,
CX_HYBRID_TV,
MULTIFUNCTION,
+ TIVO_KIT,
+ MCE_GEN2_NO_TX,
};
struct mceusb_model {
@@ -172,6 +174,10 @@ static const struct mceusb_model mceusb_model[] = {
[MCE_GEN2] = {
.mce_gen2 = 1,
},
+ [MCE_GEN2_NO_TX] = {
+ .mce_gen2 = 1,
+ .no_tx = 1,
+ },
[MCE_GEN2_TX_INV] = {
.mce_gen2 = 1,
.tx_mask_normal = 1,
@@ -197,6 +203,10 @@ static const struct mceusb_model mceusb_model[] = {
.mce_gen2 = 1,
.ir_intfnum = 2,
},
+ [TIVO_KIT] = {
+ .mce_gen2 = 1,
+ .rc_map = RC_MAP_TIVO,
+ },
};
static struct usb_device_id mceusb_dev_table[] = {
@@ -279,7 +289,8 @@ static struct usb_device_id mceusb_dev_table[] = {
/* Formosa21 / eHome Infrared Receiver */
{ USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
/* Formosa aim / Trust MCE Infrared Receiver */
- { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe017),
+ .driver_info = MCE_GEN2_NO_TX },
/* Formosa Industrial Computing / Beanbag Emulation Device */
{ USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
/* Formosa21 / eHome Infrared Receiver */
@@ -308,7 +319,8 @@ static struct usb_device_id mceusb_dev_table[] = {
/* Northstar Systems, Inc. eHome Infrared Transceiver */
{ USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
/* TiVo PC IR Receiver */
- { USB_DEVICE(VENDOR_TIVO, 0x2000) },
+ { USB_DEVICE(VENDOR_TIVO, 0x2000),
+ .driver_info = TIVO_KIT },
/* Conexant Hybrid TV "Shelby" Polaris SDK */
{ USB_DEVICE(VENDOR_CONEXANT, 0x58a1),
.driver_info = POLARIS_EVK },
@@ -603,11 +615,10 @@ static void mce_async_callback(struct urb *urb, struct pt_regs *regs)
}
/* 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)
+static void mce_request_packet(struct mceusb_dev *ir, unsigned char *data,
+ int size, int urb_type)
{
- int res;
+ int res, pipe;
struct urb *async_urb;
struct device *dev = ir->dev;
unsigned char *async_buf;
@@ -627,10 +638,11 @@ static void mce_request_packet(struct mceusb_dev *ir,
}
/* outbound data */
- usb_fill_int_urb(async_urb, ir->usbdev,
- usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
+ pipe = usb_sndintpipe(ir->usbdev,
+ ir->usb_ep_out->bEndpointAddress);
+ usb_fill_int_urb(async_urb, ir->usbdev, pipe,
async_buf, size, (usb_complete_t)mce_async_callback,
- ir, ep->bInterval);
+ ir, ir->usb_ep_out->bInterval);
memcpy(async_buf, data, size);
} else if (urb_type == MCEUSB_RX) {
@@ -658,12 +670,12 @@ static void mce_request_packet(struct mceusb_dev *ir,
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);
+ mce_request_packet(ir, 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);
+ mce_request_packet(ir, data, size, MCEUSB_RX);
}
/* Send data out the IR blaster port(s) */
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c
index d4d64492a057..bf3060ea6107 100644
--- a/drivers/media/rc/nuvoton-cir.c
+++ b/drivers/media/rc/nuvoton-cir.c
@@ -37,8 +37,6 @@
#include "nuvoton-cir.h"
-static char *chip_id = "w836x7hg";
-
/* write val to config reg */
static inline void nvt_cr_write(struct nvt_dev *nvt, u8 val, u8 reg)
{
@@ -233,6 +231,8 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
unsigned long flags;
u8 chip_major, chip_minor;
int ret = 0;
+ char chip_id[12];
+ bool chip_unknown = false;
nvt_efm_enable(nvt);
@@ -246,15 +246,39 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
}
chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
- nvt_dbg("%s: chip id: 0x%02x 0x%02x", chip_id, chip_major, chip_minor);
- if (chip_major != CHIP_ID_HIGH ||
- (chip_minor != CHIP_ID_LOW && chip_minor != CHIP_ID_LOW2)) {
- nvt_pr(KERN_ERR, "%s: unsupported chip, id: 0x%02x 0x%02x",
- chip_id, chip_major, chip_minor);
- ret = -ENODEV;
+ /* these are the known working chip revisions... */
+ switch (chip_major) {
+ case CHIP_ID_HIGH_667:
+ strcpy(chip_id, "w83667hg\0");
+ if (chip_minor != CHIP_ID_LOW_667)
+ chip_unknown = true;
+ break;
+ case CHIP_ID_HIGH_677B:
+ strcpy(chip_id, "w83677hg\0");
+ if (chip_minor != CHIP_ID_LOW_677B2 &&
+ chip_minor != CHIP_ID_LOW_677B3)
+ chip_unknown = true;
+ break;
+ case CHIP_ID_HIGH_677C:
+ strcpy(chip_id, "w83677hg-c\0");
+ if (chip_minor != CHIP_ID_LOW_677C)
+ chip_unknown = true;
+ break;
+ default:
+ strcpy(chip_id, "w836x7hg\0");
+ chip_unknown = true;
+ break;
}
+ /* warn, but still let the driver load, if we don't know this chip */
+ if (chip_unknown)
+ nvt_pr(KERN_WARNING, "%s: unknown chip, id: 0x%02x 0x%02x, "
+ "it may not work...", chip_id, chip_major, chip_minor);
+ else
+ nvt_dbg("%s: chip id: 0x%02x 0x%02x",
+ chip_id, chip_major, chip_minor);
+
nvt_efm_disable(nvt);
spin_lock_irqsave(&nvt->nvt_lock, flags);
@@ -267,13 +291,23 @@ static int nvt_hw_detect(struct nvt_dev *nvt)
static void nvt_cir_ldev_init(struct nvt_dev *nvt)
{
- u8 val;
+ u8 val, psreg, psmask, psval;
+
+ if (nvt->chip_major == CHIP_ID_HIGH_667) {
+ psreg = CR_MULTIFUNC_PIN_SEL;
+ psmask = MULTIFUNC_PIN_SEL_MASK;
+ psval = MULTIFUNC_ENABLE_CIR | MULTIFUNC_ENABLE_CIRWB;
+ } else {
+ psreg = CR_OUTPUT_PIN_SEL;
+ psmask = OUTPUT_PIN_SEL_MASK;
+ psval = OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB;
+ }
- /* output pin selection (Pin95=CIRRX, Pin96=CIRTX1, WB enabled */
- val = nvt_cr_read(nvt, CR_OUTPUT_PIN_SEL);
- val &= OUTPUT_PIN_SEL_MASK;
- val |= (OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB);
- nvt_cr_write(nvt, val, CR_OUTPUT_PIN_SEL);
+ /* output pin selection: enable CIR, with WB sensor enabled */
+ val = nvt_cr_read(nvt, psreg);
+ val &= psmask;
+ val |= psval;
+ nvt_cr_write(nvt, val, psreg);
/* Select CIR logical device and enable */
nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
@@ -640,7 +674,7 @@ static void nvt_process_rx_ir_data(struct nvt_dev *nvt)
rawir.pulse ? "pulse" : "space",
rawir.duration);
- ir_raw_event_store(nvt->rdev, &rawir);
+ ir_raw_event_store_with_filter(nvt->rdev, &rawir);
}
/*
@@ -1070,18 +1104,20 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
rdev->tx_ir = nvt_tx_ir;
rdev->s_tx_carrier = nvt_set_tx_carrier;
rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
+ rdev->input_phys = "nuvoton/cir0";
rdev->input_id.bustype = BUS_HOST;
rdev->input_id.vendor = PCI_VENDOR_ID_WINBOND2;
rdev->input_id.product = nvt->chip_major;
rdev->input_id.version = nvt->chip_minor;
+ rdev->dev.parent = &pdev->dev;
rdev->driver_name = NVT_DRIVER_NAME;
rdev->map_name = RC_MAP_RC6_MCE;
+ rdev->timeout = US_TO_NS(1000);
+ /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
+ rdev->rx_resolution = US_TO_NS(CIR_SAMPLE_PERIOD);
#if 0
rdev->min_timeout = XYZ;
rdev->max_timeout = XYZ;
- rdev->timeout = XYZ;
- /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
- rdev->rx_resolution = XYZ;
/* tx bits */
rdev->tx_resolution = XYZ;
#endif
@@ -1090,8 +1126,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
if (ret)
goto failure;
- device_set_wakeup_capable(&pdev->dev, 1);
- device_set_wakeup_enable(&pdev->dev, 1);
+ device_init_wakeup(&pdev->dev, true);
nvt->rdev = rdev;
nvt_pr(KERN_NOTICE, "driver has been successfully loaded\n");
if (debug) {
diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h
index 048135eea702..379795d61ea7 100644
--- a/drivers/media/rc/nuvoton-cir.h
+++ b/drivers/media/rc/nuvoton-cir.h
@@ -330,9 +330,13 @@ struct nvt_dev {
#define EFER_EFM_DISABLE 0xaa
/* Chip IDs found in CR_CHIP_ID_{HI,LO} */
-#define CHIP_ID_HIGH 0xb4
-#define CHIP_ID_LOW 0x72
-#define CHIP_ID_LOW2 0x73
+#define CHIP_ID_HIGH_667 0xa5
+#define CHIP_ID_HIGH_677B 0xb4
+#define CHIP_ID_HIGH_677C 0xc3
+#define CHIP_ID_LOW_667 0x13
+#define CHIP_ID_LOW_677B2 0x72
+#define CHIP_ID_LOW_677B3 0x73
+#define CHIP_ID_LOW_677C 0x33
/* Config regs we need to care about */
#define CR_SOFTWARE_RESET 0x02
@@ -341,6 +345,7 @@ struct nvt_dev {
#define CR_CHIP_ID_LO 0x21
#define CR_DEV_POWER_DOWN 0x22 /* bit 2 is CIR power, default power on */
#define CR_OUTPUT_PIN_SEL 0x27
+#define CR_MULTIFUNC_PIN_SEL 0x2c
#define CR_LOGICAL_DEV_EN 0x30 /* valid for all logical devices */
/* next three regs valid for both the CIR and CIR_WAKE logical devices */
#define CR_CIR_BASE_ADDR_HI 0x60
@@ -364,10 +369,16 @@ struct nvt_dev {
#define CIR_INTR_MOUSE_IRQ_BIT 0x80
#define PME_INTR_CIR_PASS_BIT 0x08
+/* w83677hg CIR pin config */
#define OUTPUT_PIN_SEL_MASK 0xbc
#define OUTPUT_ENABLE_CIR 0x01 /* Pin95=CIRRX, Pin96=CIRTX1 */
#define OUTPUT_ENABLE_CIRWB 0x40 /* enable wide-band sensor */
+/* w83667hg CIR pin config */
+#define MULTIFUNC_PIN_SEL_MASK 0x1f
+#define MULTIFUNC_ENABLE_CIR 0x80 /* Pin75=CIRRX, Pin76=CIRTX1 */
+#define MULTIFUNC_ENABLE_CIRWB 0x20 /* enable wide-band sensor */
+
/* MCE CIR signal length, related on sample period */
/* MCE CIR controller signal length: about 43ms
diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c
index 49cee61d79c6..cc846b2619cf 100644
--- a/drivers/media/rc/rc-loopback.c
+++ b/drivers/media/rc/rc-loopback.c
@@ -146,6 +146,12 @@ static int loop_tx_ir(struct rc_dev *dev, int *txbuf, u32 n)
if (rawir.duration)
ir_raw_event_store_with_filter(dev, &rawir);
}
+
+ /* Fake a silence long enough to cause us to go idle */
+ rawir.pulse = false;
+ rawir.duration = dev->timeout;
+ ir_raw_event_store_with_filter(dev, &rawir);
+
ir_raw_event_handle(dev);
out:
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index a2706648e365..f57cd5677ac2 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -749,6 +749,9 @@ static struct {
* it is trigged by reading /sys/class/rc/rc?/protocols.
* It returns the protocol names of supported protocols.
* Enabled protocols are printed in brackets.
+ *
+ * dev->lock is taken to guard against races between device
+ * registration, store_protocols and show_protocols.
*/
static ssize_t show_protocols(struct device *device,
struct device_attribute *mattr, char *buf)
@@ -762,6 +765,8 @@ static ssize_t show_protocols(struct device *device,
if (!dev)
return -EINVAL;
+ mutex_lock(&dev->lock);
+
if (dev->driver_type == RC_DRIVER_SCANCODE) {
enabled = dev->rc_map.rc_type;
allowed = dev->allowed_protos;
@@ -784,6 +789,9 @@ static ssize_t show_protocols(struct device *device,
if (tmp != buf)
tmp--;
*tmp = '\n';
+
+ mutex_unlock(&dev->lock);
+
return tmp + 1 - buf;
}
@@ -802,6 +810,9 @@ static ssize_t show_protocols(struct device *device,
* Writing "none" will disable all protocols.
* Returns -EINVAL if an invalid protocol combination or unknown protocol name
* is used, otherwise @len.
+ *
+ * dev->lock is taken to guard against races between device
+ * registration, store_protocols and show_protocols.
*/
static ssize_t store_protocols(struct device *device,
struct device_attribute *mattr,
@@ -815,18 +826,22 @@ static ssize_t store_protocols(struct device *device,
u64 mask;
int rc, i, count = 0;
unsigned long flags;
+ ssize_t ret;
/* Device is being removed */
if (!dev)
return -EINVAL;
+ mutex_lock(&dev->lock);
+
if (dev->driver_type == RC_DRIVER_SCANCODE)
type = dev->rc_map.rc_type;
else if (dev->raw)
type = dev->raw->enabled_protocols;
else {
IR_dprintk(1, "Protocol switching not supported\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
while ((tmp = strsep((char **) &data, " \n")) != NULL) {
@@ -860,7 +875,8 @@ static ssize_t store_protocols(struct device *device,
}
if (i == ARRAY_SIZE(proto_names)) {
IR_dprintk(1, "Unknown protocol: '%s'\n", tmp);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
count++;
}
@@ -875,7 +891,8 @@ static ssize_t store_protocols(struct device *device,
if (!count) {
IR_dprintk(1, "Protocol not specified\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
if (dev->change_protocol) {
@@ -883,7 +900,8 @@ static ssize_t store_protocols(struct device *device,
if (rc < 0) {
IR_dprintk(1, "Error setting protocols to 0x%llx\n",
(long long)type);
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
}
@@ -898,7 +916,11 @@ static ssize_t store_protocols(struct device *device,
IR_dprintk(1, "Current protocol(s): 0x%llx\n",
(long long)type);
- return len;
+ ret = len;
+
+out:
+ mutex_unlock(&dev->lock);
+ return ret;
}
static void rc_dev_release(struct device *device)
@@ -974,6 +996,7 @@ struct rc_dev *rc_allocate_device(void)
spin_lock_init(&dev->rc_map.lock);
spin_lock_init(&dev->keylock);
+ mutex_init(&dev->lock);
setup_timer(&dev->timer_keyup, ir_timer_keyup, (unsigned long)dev);
dev->dev.type = &rc_dev_type;
@@ -1019,12 +1042,21 @@ int rc_register_device(struct rc_dev *dev)
if (dev->close)
dev->input_dev->close = ir_close;
+ /*
+ * Take the lock here, as the device sysfs node will appear
+ * when device_add() is called, which may trigger an ir-keytable udev
+ * rule, which will in turn call show_protocols and access either
+ * dev->rc_map.rc_type or dev->raw->enabled_protocols before it has
+ * been initialized.
+ */
+ mutex_lock(&dev->lock);
+
dev->devno = (unsigned long)(atomic_inc_return(&devno) - 1);
dev_set_name(&dev->dev, "rc%ld", dev->devno);
dev_set_drvdata(&dev->dev, dev);
rc = device_add(&dev->dev);
if (rc)
- return rc;
+ goto out_unlock;
rc = ir_setkeytable(dev, rc_map);
if (rc)
@@ -1046,6 +1078,13 @@ int rc_register_device(struct rc_dev *dev)
*/
dev->input_dev->rep[REP_DELAY] = 500;
+ /*
+ * As a repeat event on protocols like RC-5 and NEC take as long as
+ * 110/114ms, using 33ms as a repeat period is not the right thing
+ * to do.
+ */
+ dev->input_dev->rep[REP_PERIOD] = 125;
+
path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
printk(KERN_INFO "%s: %s as %s\n",
dev_name(&dev->dev),
@@ -1058,6 +1097,7 @@ int rc_register_device(struct rc_dev *dev)
if (rc < 0)
goto out_input;
}
+ mutex_unlock(&dev->lock);
if (dev->change_protocol) {
rc = dev->change_protocol(dev, rc_map->rc_type);
@@ -1083,6 +1123,8 @@ out_table:
ir_free_table(&dev->rc_map);
out_dev:
device_del(&dev->dev);
+out_unlock:
+ mutex_unlock(&dev->lock);
return rc;
}
EXPORT_SYMBOL_GPL(rc_register_device);
diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c
new file mode 100644
index 000000000000..5147767ccb78
--- /dev/null
+++ b/drivers/media/rc/redrat3.c
@@ -0,0 +1,1344 @@
+/*
+ * USB RedRat3 IR Transceiver rc-core driver
+ *
+ * Copyright (c) 2011 by Jarod Wilson <jarod@redhat.com>
+ * based heavily on the work of Stephen Cox, with additional
+ * help from RedRat Ltd.
+ *
+ * This driver began life based an an old version of the first-generation
+ * lirc_mceusb driver from the lirc 0.7.2 distribution. It was then
+ * significantly rewritten by Stephen Cox with the aid of RedRat Ltd's
+ * Chris Dodge.
+ *
+ * The driver was then ported to rc-core and significantly rewritten again,
+ * by Jarod, using the in-kernel mceusb driver as a guide, after an initial
+ * port effort was started by Stephen.
+ *
+ * TODO LIST:
+ * - fix lirc not showing repeats properly
+ * --
+ *
+ * The RedRat3 is a USB transceiver with both send & receive,
+ * with 2 separate sensors available for receive to enable
+ * both good long range reception for general use, and good
+ * short range reception when required for learning a signal.
+ *
+ * http://www.redrat.co.uk/
+ *
+ * It uses its own little protocol to communicate, the required
+ * parts of which are embedded within this driver.
+ * --
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/input.h>
+#include <media/rc-core.h>
+
+/* Driver Information */
+#define DRIVER_VERSION "0.70"
+#define DRIVER_AUTHOR "Jarod Wilson <jarod@redhat.com>"
+#define DRIVER_AUTHOR2 "The Dweller, Stephen Cox"
+#define DRIVER_DESC "RedRat3 USB IR Transceiver Driver"
+#define DRIVER_NAME "redrat3"
+
+/* module parameters */
+#ifdef CONFIG_USB_DEBUG
+static int debug = 1;
+#else
+static int debug;
+#endif
+
+#define RR3_DEBUG_STANDARD 0x1
+#define RR3_DEBUG_FUNCTION_TRACE 0x2
+
+#define rr3_dbg(dev, fmt, ...) \
+ do { \
+ if (debug & RR3_DEBUG_STANDARD) \
+ dev_info(dev, fmt, ## __VA_ARGS__); \
+ } while (0)
+
+#define rr3_ftr(dev, fmt, ...) \
+ do { \
+ if (debug & RR3_DEBUG_FUNCTION_TRACE) \
+ dev_info(dev, fmt, ## __VA_ARGS__); \
+ } while (0)
+
+/* bulk data transfer types */
+#define RR3_ERROR 0x01
+#define RR3_MOD_SIGNAL_IN 0x20
+#define RR3_MOD_SIGNAL_OUT 0x21
+
+/* Get the RR firmware version */
+#define RR3_FW_VERSION 0xb1
+#define RR3_FW_VERSION_LEN 64
+/* Send encoded signal bulk-sent earlier*/
+#define RR3_TX_SEND_SIGNAL 0xb3
+#define RR3_SET_IR_PARAM 0xb7
+#define RR3_GET_IR_PARAM 0xb8
+/* Blink the red LED on the device */
+#define RR3_BLINK_LED 0xb9
+/* Read serial number of device */
+#define RR3_READ_SER_NO 0xba
+#define RR3_SER_NO_LEN 4
+/* Start capture with the RC receiver */
+#define RR3_RC_DET_ENABLE 0xbb
+/* Stop capture with the RC receiver */
+#define RR3_RC_DET_DISABLE 0xbc
+/* Return the status of RC detector capture */
+#define RR3_RC_DET_STATUS 0xbd
+/* Reset redrat */
+#define RR3_RESET 0xa0
+
+/* Max number of lengths in the signal. */
+#define RR3_IR_IO_MAX_LENGTHS 0x01
+/* Periods to measure mod. freq. */
+#define RR3_IR_IO_PERIODS_MF 0x02
+/* Size of memory for main signal data */
+#define RR3_IR_IO_SIG_MEM_SIZE 0x03
+/* Delta value when measuring lengths */
+#define RR3_IR_IO_LENGTH_FUZZ 0x04
+/* Timeout for end of signal detection */
+#define RR3_IR_IO_SIG_TIMEOUT 0x05
+/* Minumum value for pause recognition. */
+#define RR3_IR_IO_MIN_PAUSE 0x06
+
+/* Clock freq. of EZ-USB chip */
+#define RR3_CLK 24000000
+/* Clock periods per timer count */
+#define RR3_CLK_PER_COUNT 12
+/* (RR3_CLK / RR3_CLK_PER_COUNT) */
+#define RR3_CLK_CONV_FACTOR 2000000
+/* USB bulk-in IR data endpoint address */
+#define RR3_BULK_IN_EP_ADDR 0x82
+
+/* Raw Modulated signal data value offsets */
+#define RR3_PAUSE_OFFSET 0
+#define RR3_FREQ_COUNT_OFFSET 4
+#define RR3_NUM_PERIOD_OFFSET 6
+#define RR3_MAX_LENGTHS_OFFSET 8
+#define RR3_NUM_LENGTHS_OFFSET 9
+#define RR3_MAX_SIGS_OFFSET 10
+#define RR3_NUM_SIGS_OFFSET 12
+#define RR3_REPEATS_OFFSET 14
+
+/* Size of the fixed-length portion of the signal */
+#define RR3_HEADER_LENGTH 15
+#define RR3_DRIVER_MAXLENS 128
+#define RR3_MAX_SIG_SIZE 512
+#define RR3_MAX_BUF_SIZE \
+ ((2 * RR3_HEADER_LENGTH) + RR3_DRIVER_MAXLENS + RR3_MAX_SIG_SIZE)
+#define RR3_TIME_UNIT 50
+#define RR3_END_OF_SIGNAL 0x7f
+#define RR3_TX_HEADER_OFFSET 4
+#define RR3_TX_TRAILER_LEN 2
+#define RR3_RX_MIN_TIMEOUT 5
+#define RR3_RX_MAX_TIMEOUT 2000
+
+/* The 8051's CPUCS Register address */
+#define RR3_CPUCS_REG_ADDR 0x7f92
+
+#define USB_RR3USB_VENDOR_ID 0x112a
+#define USB_RR3USB_PRODUCT_ID 0x0001
+#define USB_RR3IIUSB_PRODUCT_ID 0x0005
+
+/* table of devices that work with this driver */
+static struct usb_device_id redrat3_dev_table[] = {
+ /* Original version of the RedRat3 */
+ {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3USB_PRODUCT_ID)},
+ /* Second Version/release of the RedRat3 - RetRat3-II */
+ {USB_DEVICE(USB_RR3USB_VENDOR_ID, USB_RR3IIUSB_PRODUCT_ID)},
+ {} /* Terminating entry */
+};
+
+/* Structure to hold all of our device specific stuff */
+struct redrat3_dev {
+ /* core device bits */
+ struct rc_dev *rc;
+ struct device *dev;
+
+ /* save off the usb device pointer */
+ struct usb_device *udev;
+
+ /* the receive endpoint */
+ struct usb_endpoint_descriptor *ep_in;
+ /* the buffer to receive data */
+ unsigned char *bulk_in_buf;
+ /* urb used to read ir data */
+ struct urb *read_urb;
+
+ /* the send endpoint */
+ struct usb_endpoint_descriptor *ep_out;
+ /* the buffer to send data */
+ unsigned char *bulk_out_buf;
+ /* the urb used to send data */
+ struct urb *write_urb;
+
+ /* usb dma */
+ dma_addr_t dma_in;
+ dma_addr_t dma_out;
+
+ /* true if write urb is busy */
+ bool write_busy;
+ /* wait for the write to finish */
+ struct completion write_finished;
+
+ /* locks this structure */
+ struct mutex lock;
+
+ /* rx signal timeout timer */
+ struct timer_list rx_timeout;
+
+ /* Is the device currently receiving? */
+ bool recv_in_progress;
+ /* is the detector enabled*/
+ bool det_enabled;
+ /* Is the device currently transmitting?*/
+ bool transmitting;
+
+ /* store for current packet */
+ char pbuf[RR3_MAX_BUF_SIZE];
+ u16 pktlen;
+ u16 pkttype;
+ u16 bytes_read;
+ /* indicate whether we are going to reprocess
+ * the USB callback with a bigger buffer */
+ int buftoosmall;
+ char *datap;
+
+ u32 carrier;
+
+ char name[128];
+ char phys[64];
+};
+
+/* All incoming data buffers adhere to a very specific data format */
+struct redrat3_signal_header {
+ u16 length; /* Length of data being transferred */
+ u16 transfer_type; /* Type of data transferred */
+ u32 pause; /* Pause between main and repeat signals */
+ u16 mod_freq_count; /* Value of timer on mod. freq. measurement */
+ u16 no_periods; /* No. of periods over which mod. freq. is measured */
+ u8 max_lengths; /* Max no. of lengths (i.e. size of array) */
+ u8 no_lengths; /* Actual no. of elements in lengths array */
+ u16 max_sig_size; /* Max no. of values in signal data array */
+ u16 sig_size; /* Acuto no. of values in signal data array */
+ u8 no_repeats; /* No. of repeats of repeat signal section */
+ /* Here forward is the lengths and signal data */
+};
+
+static void redrat3_dump_signal_header(struct redrat3_signal_header *header)
+{
+ pr_info("%s:\n", __func__);
+ pr_info(" * length: %u, transfer_type: 0x%02x\n",
+ header->length, header->transfer_type);
+ pr_info(" * pause: %u, freq_count: %u, no_periods: %u\n",
+ header->pause, header->mod_freq_count, header->no_periods);
+ pr_info(" * lengths: %u (max: %u)\n",
+ header->no_lengths, header->max_lengths);
+ pr_info(" * sig_size: %u (max: %u)\n",
+ header->sig_size, header->max_sig_size);
+ pr_info(" * repeats: %u\n", header->no_repeats);
+}
+
+static void redrat3_dump_signal_data(char *buffer, u16 len)
+{
+ int offset, i;
+ char *data_vals;
+
+ pr_info("%s:", __func__);
+
+ offset = RR3_TX_HEADER_OFFSET + RR3_HEADER_LENGTH
+ + (RR3_DRIVER_MAXLENS * sizeof(u16));
+
+ /* read RR3_DRIVER_MAXLENS from ctrl msg */
+ data_vals = buffer + offset;
+
+ for (i = 0; i < len; i++) {
+ if (i % 10 == 0)
+ pr_cont("\n * ");
+ pr_cont("%02x ", *data_vals++);
+ }
+
+ pr_cont("\n");
+}
+
+/*
+ * redrat3_issue_async
+ *
+ * Issues an async read to the ir data in port..
+ * sets the callback to be redrat3_handle_async
+ */
+static void redrat3_issue_async(struct redrat3_dev *rr3)
+{
+ int res;
+
+ rr3_ftr(rr3->dev, "Entering %s\n", __func__);
+
+ if (!rr3->det_enabled) {
+ dev_warn(rr3->dev, "not issuing async read, "
+ "detector not enabled\n");
+ return;
+ }
+
+ memset(rr3->bulk_in_buf, 0, rr3->ep_in->wMaxPacketSize);
+ res = usb_submit_urb(rr3->read_urb, GFP_ATOMIC);
+ if (res)
+ rr3_dbg(rr3->dev, "%s: receive request FAILED! "
+ "(res %d, len %d)\n", __func__, res,
+ rr3->read_urb->transfer_buffer_length);
+}
+
+static void redrat3_dump_fw_error(struct redrat3_dev *rr3, int code)
+{
+ if (!rr3->transmitting && (code != 0x40))
+ dev_info(rr3->dev, "fw error code 0x%02x: ", code);
+
+ switch (code) {
+ case 0x00:
+ pr_cont("No Error\n");
+ break;
+
+ /* Codes 0x20 through 0x2f are IR Firmware Errors */
+ case 0x20:
+ pr_cont("Initial signal pulse not long enough "
+ "to measure carrier frequency\n");
+ break;
+ case 0x21:
+ pr_cont("Not enough length values allocated for signal\n");
+ break;
+ case 0x22:
+ pr_cont("Not enough memory allocated for signal data\n");
+ break;
+ case 0x23:
+ pr_cont("Too many signal repeats\n");
+ break;
+ case 0x28:
+ pr_cont("Insufficient memory available for IR signal "
+ "data memory allocation\n");
+ break;
+ case 0x29:
+ pr_cont("Insufficient memory available "
+ "for IrDa signal data memory allocation\n");
+ break;
+
+ /* Codes 0x30 through 0x3f are USB Firmware Errors */
+ case 0x30:
+ pr_cont("Insufficient memory available for bulk "
+ "transfer structure\n");
+ break;
+
+ /*
+ * Other error codes... These are primarily errors that can occur in
+ * the control messages sent to the redrat
+ */
+ case 0x40:
+ if (!rr3->transmitting)
+ pr_cont("Signal capture has been terminated\n");
+ break;
+ case 0x41:
+ pr_cont("Attempt to set/get and unknown signal I/O "
+ "algorithm parameter\n");
+ break;
+ case 0x42:
+ pr_cont("Signal capture already started\n");
+ break;
+
+ default:
+ pr_cont("Unknown Error\n");
+ break;
+ }
+}
+
+static u32 redrat3_val_to_mod_freq(struct redrat3_signal_header *ph)
+{
+ u32 mod_freq = 0;
+
+ if (ph->mod_freq_count != 0)
+ mod_freq = (RR3_CLK * ph->no_periods) /
+ (ph->mod_freq_count * RR3_CLK_PER_COUNT);
+
+ return mod_freq;
+}
+
+/* this function scales down the figures for the same result... */
+static u32 redrat3_len_to_us(u32 length)
+{
+ u32 biglen = length * 1000;
+ u32 divisor = (RR3_CLK_CONV_FACTOR) / 1000;
+ u32 result = (u32) (biglen / divisor);
+
+ /* don't allow zero lengths to go back, breaks lirc */
+ return result ? result : 1;
+}
+
+/*
+ * convert us back into redrat3 lengths
+ *
+ * length * 1000 length * 1000000
+ * ------------- = ---------------- = micro
+ * rr3clk / 1000 rr3clk
+
+ * 6 * 2 4 * 3 micro * rr3clk micro * rr3clk / 1000
+ * ----- = 4 ----- = 6 -------------- = len ---------------------
+ * 3 2 1000000 1000
+ */
+static u32 redrat3_us_to_len(u32 microsec)
+{
+ u32 result;
+ u32 divisor;
+
+ microsec &= IR_MAX_DURATION;
+ divisor = (RR3_CLK_CONV_FACTOR / 1000);
+ result = (u32)(microsec * divisor) / 1000;
+
+ /* don't allow zero lengths to go back, breaks lirc */
+ return result ? result : 1;
+
+}
+
+/* timer callback to send long trailing space on receive timeout */
+static void redrat3_rx_timeout(unsigned long data)
+{
+ struct redrat3_dev *rr3 = (struct redrat3_dev *)data;
+ DEFINE_IR_RAW_EVENT(rawir);
+
+ rawir.pulse = false;
+ rawir.duration = rr3->rc->timeout;
+ rr3_dbg(rr3->dev, "storing trailing space with duration %d\n",
+ rawir.duration);
+ ir_raw_event_store_with_filter(rr3->rc, &rawir);
+
+ rr3_dbg(rr3->dev, "calling ir_raw_event_handle\n");
+ ir_raw_event_handle(rr3->rc);
+
+ rr3_dbg(rr3->dev, "calling ir_raw_event_reset\n");
+ ir_raw_event_reset(rr3->rc);
+}
+
+static void redrat3_process_ir_data(struct redrat3_dev *rr3)
+{
+ DEFINE_IR_RAW_EVENT(rawir);
+ struct redrat3_signal_header header;
+ struct device *dev;
+ int i;
+ unsigned long delay;
+ u32 mod_freq, single_len;
+ u16 *len_vals;
+ u8 *data_vals;
+ u32 tmp32;
+ u16 tmp16;
+ char *sig_data;
+
+ if (!rr3) {
+ pr_err("%s called with no context!\n", __func__);
+ return;
+ }
+
+ rr3_ftr(rr3->dev, "Entered %s\n", __func__);
+
+ dev = rr3->dev;
+ sig_data = rr3->pbuf;
+
+ header.length = rr3->pktlen;
+ header.transfer_type = rr3->pkttype;
+
+ /* Sanity check */
+ if (!(header.length >= RR3_HEADER_LENGTH))
+ dev_warn(dev, "read returned less than rr3 header len\n");
+
+ delay = usecs_to_jiffies(rr3->rc->timeout / 1000);
+ mod_timer(&rr3->rx_timeout, jiffies + delay);
+
+ memcpy(&tmp32, sig_data + RR3_PAUSE_OFFSET, sizeof(tmp32));
+ header.pause = be32_to_cpu(tmp32);
+
+ memcpy(&tmp16, sig_data + RR3_FREQ_COUNT_OFFSET, sizeof(tmp16));
+ header.mod_freq_count = be16_to_cpu(tmp16);
+
+ memcpy(&tmp16, sig_data + RR3_NUM_PERIOD_OFFSET, sizeof(tmp16));
+ header.no_periods = be16_to_cpu(tmp16);
+
+ header.max_lengths = sig_data[RR3_MAX_LENGTHS_OFFSET];
+ header.no_lengths = sig_data[RR3_NUM_LENGTHS_OFFSET];
+
+ memcpy(&tmp16, sig_data + RR3_MAX_SIGS_OFFSET, sizeof(tmp16));
+ header.max_sig_size = be16_to_cpu(tmp16);
+
+ memcpy(&tmp16, sig_data + RR3_NUM_SIGS_OFFSET, sizeof(tmp16));
+ header.sig_size = be16_to_cpu(tmp16);
+
+ header.no_repeats= sig_data[RR3_REPEATS_OFFSET];
+
+ if (debug) {
+ redrat3_dump_signal_header(&header);
+ redrat3_dump_signal_data(sig_data, header.sig_size);
+ }
+
+ mod_freq = redrat3_val_to_mod_freq(&header);
+ rr3_dbg(dev, "Got mod_freq of %u\n", mod_freq);
+
+ /* Here we pull out the 'length' values from the signal */
+ len_vals = (u16 *)(sig_data + RR3_HEADER_LENGTH);
+
+ data_vals = sig_data + RR3_HEADER_LENGTH +
+ (header.max_lengths * sizeof(u16));
+
+ /* process each rr3 encoded byte into an int */
+ for (i = 0; i < header.sig_size; i++) {
+ u16 val = len_vals[data_vals[i]];
+ single_len = redrat3_len_to_us((u32)be16_to_cpu(val));
+
+ /* cap the value to IR_MAX_DURATION */
+ single_len &= IR_MAX_DURATION;
+
+ /* we should always get pulse/space/pulse/space samples */
+ if (i % 2)
+ rawir.pulse = false;
+ else
+ rawir.pulse = true;
+
+ rawir.duration = US_TO_NS(single_len);
+ rr3_dbg(dev, "storing %s with duration %d (i: %d)\n",
+ rawir.pulse ? "pulse" : "space", rawir.duration, i);
+ ir_raw_event_store_with_filter(rr3->rc, &rawir);
+ }
+
+ /* add a trailing space, if need be */
+ if (i % 2) {
+ rawir.pulse = false;
+ /* this duration is made up, and may not be ideal... */
+ rawir.duration = rr3->rc->timeout / 2;
+ rr3_dbg(dev, "storing trailing space with duration %d\n",
+ rawir.duration);
+ ir_raw_event_store_with_filter(rr3->rc, &rawir);
+ }
+
+ rr3_dbg(dev, "calling ir_raw_event_handle\n");
+ ir_raw_event_handle(rr3->rc);
+
+ return;
+}
+
+/* Util fn to send rr3 cmds */
+static u8 redrat3_send_cmd(int cmd, struct redrat3_dev *rr3)
+{
+ struct usb_device *udev;
+ u8 *data;
+ int res;
+
+ data = kzalloc(sizeof(u8), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ udev = rr3->udev;
+ res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), cmd,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x0000, 0x0000, data, sizeof(u8), HZ * 10);
+
+ if (res < 0) {
+ dev_err(rr3->dev, "%s: Error sending rr3 cmd res %d, data %d",
+ __func__, res, *data);
+ res = -EIO;
+ } else
+ res = (u8)data[0];
+
+ kfree(data);
+
+ return res;
+}
+
+/* Enables the long range detector and starts async receive */
+static int redrat3_enable_detector(struct redrat3_dev *rr3)
+{
+ struct device *dev = rr3->dev;
+ u8 ret;
+
+ rr3_ftr(dev, "Entering %s\n", __func__);
+
+ ret = redrat3_send_cmd(RR3_RC_DET_ENABLE, rr3);
+ if (ret != 0)
+ dev_dbg(dev, "%s: unexpected ret of %d\n",
+ __func__, ret);
+
+ ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3);
+ if (ret != 1) {
+ dev_err(dev, "%s: detector status: %d, should be 1\n",
+ __func__, ret);
+ return -EIO;
+ }
+
+ rr3->det_enabled = true;
+ redrat3_issue_async(rr3);
+
+ return 0;
+}
+
+/* Disables the rr3 long range detector */
+static void redrat3_disable_detector(struct redrat3_dev *rr3)
+{
+ struct device *dev = rr3->dev;
+ u8 ret;
+
+ rr3_ftr(dev, "Entering %s\n", __func__);
+
+ ret = redrat3_send_cmd(RR3_RC_DET_DISABLE, rr3);
+ if (ret != 0)
+ dev_err(dev, "%s: failure!\n", __func__);
+
+ ret = redrat3_send_cmd(RR3_RC_DET_STATUS, rr3);
+ if (ret != 0)
+ dev_warn(dev, "%s: detector status: %d, should be 0\n",
+ __func__, ret);
+
+ rr3->det_enabled = false;
+}
+
+static inline void redrat3_delete(struct redrat3_dev *rr3,
+ struct usb_device *udev)
+{
+ rr3_ftr(rr3->dev, "%s cleaning up\n", __func__);
+ usb_kill_urb(rr3->read_urb);
+ usb_kill_urb(rr3->write_urb);
+
+ usb_free_urb(rr3->read_urb);
+ usb_free_urb(rr3->write_urb);
+
+ usb_free_coherent(udev, rr3->ep_in->wMaxPacketSize,
+ rr3->bulk_in_buf, rr3->dma_in);
+ usb_free_coherent(udev, rr3->ep_out->wMaxPacketSize,
+ rr3->bulk_out_buf, rr3->dma_out);
+
+ kfree(rr3);
+}
+
+static u32 redrat3_get_timeout(struct device *dev,
+ struct rc_dev *rc, struct usb_device *udev)
+{
+ u32 *tmp;
+ u32 timeout = MS_TO_NS(150); /* a sane default, if things go haywire */
+ int len, ret, pipe;
+
+ len = sizeof(*tmp);
+ tmp = kzalloc(len, GFP_KERNEL);
+ if (!tmp) {
+ dev_warn(dev, "Memory allocation faillure\n");
+ return timeout;
+ }
+
+ pipe = usb_rcvctrlpipe(udev, 0);
+ ret = usb_control_msg(udev, pipe, RR3_GET_IR_PARAM,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ RR3_IR_IO_SIG_TIMEOUT, 0, tmp, len, HZ * 5);
+ if (ret != len) {
+ dev_warn(dev, "Failed to read timeout from hardware\n");
+ return timeout;
+ }
+
+ timeout = US_TO_NS(redrat3_len_to_us(be32_to_cpu(*tmp)));
+ if (timeout < rc->min_timeout)
+ timeout = rc->min_timeout;
+ else if (timeout > rc->max_timeout)
+ timeout = rc->max_timeout;
+
+ rr3_dbg(dev, "Got timeout of %d ms\n", timeout / (1000 * 1000));
+ return timeout;
+}
+
+static void redrat3_reset(struct redrat3_dev *rr3)
+{
+ struct usb_device *udev = rr3->udev;
+ struct device *dev = rr3->dev;
+ int rc, rxpipe, txpipe;
+ u8 *val;
+ int len = sizeof(u8);
+
+ rr3_ftr(dev, "Entering %s\n", __func__);
+
+ rxpipe = usb_rcvctrlpipe(udev, 0);
+ txpipe = usb_sndctrlpipe(udev, 0);
+
+ val = kzalloc(len, GFP_KERNEL);
+ if (!val) {
+ dev_err(dev, "Memory allocation failure\n");
+ return;
+ }
+
+ *val = 0x01;
+ rc = usb_control_msg(udev, rxpipe, RR3_RESET,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ RR3_CPUCS_REG_ADDR, 0, val, len, HZ * 25);
+ rr3_dbg(dev, "reset returned 0x%02x\n", rc);
+
+ *val = 5;
+ rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ RR3_IR_IO_LENGTH_FUZZ, 0, val, len, HZ * 25);
+ rr3_dbg(dev, "set ir parm len fuzz %d rc 0x%02x\n", *val, rc);
+
+ *val = RR3_DRIVER_MAXLENS;
+ rc = usb_control_msg(udev, txpipe, RR3_SET_IR_PARAM,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ RR3_IR_IO_MAX_LENGTHS, 0, val, len, HZ * 25);
+ rr3_dbg(dev, "set ir parm max lens %d rc 0x%02x\n", *val, rc);
+
+ kfree(val);
+}
+
+static void redrat3_get_firmware_rev(struct redrat3_dev *rr3)
+{
+ int rc = 0;
+ char *buffer;
+
+ rr3_ftr(rr3->dev, "Entering %s\n", __func__);
+
+ buffer = kzalloc(sizeof(char) * (RR3_FW_VERSION_LEN + 1), GFP_KERNEL);
+ if (!buffer) {
+ dev_err(rr3->dev, "Memory allocation failure\n");
+ return;
+ }
+
+ rc = usb_control_msg(rr3->udev, usb_rcvctrlpipe(rr3->udev, 0),
+ RR3_FW_VERSION,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0, 0, buffer, RR3_FW_VERSION_LEN, HZ * 5);
+
+ if (rc >= 0)
+ dev_info(rr3->dev, "Firmware rev: %s", buffer);
+ else
+ dev_err(rr3->dev, "Problem fetching firmware ID\n");
+
+ kfree(buffer);
+ rr3_ftr(rr3->dev, "Exiting %s\n", __func__);
+}
+
+static void redrat3_read_packet_start(struct redrat3_dev *rr3, int len)
+{
+ u16 tx_error;
+ u16 hdrlen;
+
+ rr3_ftr(rr3->dev, "Entering %s\n", __func__);
+
+ /* grab the Length and type of transfer */
+ memcpy(&(rr3->pktlen), (unsigned char *) rr3->bulk_in_buf,
+ sizeof(rr3->pktlen));
+ memcpy(&(rr3->pkttype), ((unsigned char *) rr3->bulk_in_buf +
+ sizeof(rr3->pktlen)),
+ sizeof(rr3->pkttype));
+
+ /*data needs conversion to know what its real values are*/
+ rr3->pktlen = be16_to_cpu(rr3->pktlen);
+ rr3->pkttype = be16_to_cpu(rr3->pkttype);
+
+ switch (rr3->pkttype) {
+ case RR3_ERROR:
+ memcpy(&tx_error, ((unsigned char *)rr3->bulk_in_buf
+ + (sizeof(rr3->pktlen) + sizeof(rr3->pkttype))),
+ sizeof(tx_error));
+ tx_error = be16_to_cpu(tx_error);
+ redrat3_dump_fw_error(rr3, tx_error);
+ break;
+
+ case RR3_MOD_SIGNAL_IN:
+ hdrlen = sizeof(rr3->pktlen) + sizeof(rr3->pkttype);
+ rr3->bytes_read = len;
+ rr3->bytes_read -= hdrlen;
+ rr3->datap = &(rr3->pbuf[0]);
+
+ memcpy(rr3->datap, ((unsigned char *)rr3->bulk_in_buf + hdrlen),
+ rr3->bytes_read);
+ rr3->datap += rr3->bytes_read;
+ rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n",
+ rr3->bytes_read, rr3->pktlen);
+ break;
+
+ default:
+ rr3_dbg(rr3->dev, "ignoring packet with type 0x%02x, "
+ "len of %d, 0x%02x\n", rr3->pkttype, len, rr3->pktlen);
+ break;
+ }
+}
+
+static void redrat3_read_packet_continue(struct redrat3_dev *rr3, int len)
+{
+
+ rr3_ftr(rr3->dev, "Entering %s\n", __func__);
+
+ memcpy(rr3->datap, (unsigned char *)rr3->bulk_in_buf, len);
+ rr3->datap += len;
+
+ rr3->bytes_read += len;
+ rr3_dbg(rr3->dev, "bytes_read %d, pktlen %d\n",
+ rr3->bytes_read, rr3->pktlen);
+}
+
+/* gather IR data from incoming urb, process it when we have enough */
+static int redrat3_get_ir_data(struct redrat3_dev *rr3, int len)
+{
+ struct device *dev = rr3->dev;
+ int ret = 0;
+
+ rr3_ftr(dev, "Entering %s\n", __func__);
+
+ if (rr3->pktlen > RR3_MAX_BUF_SIZE) {
+ dev_err(rr3->dev, "error: packet larger than buffer\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if ((rr3->bytes_read == 0) &&
+ (len >= (sizeof(rr3->pkttype) + sizeof(rr3->pktlen)))) {
+ redrat3_read_packet_start(rr3, len);
+ } else if (rr3->bytes_read != 0) {
+ redrat3_read_packet_continue(rr3, len);
+ } else if (rr3->bytes_read == 0) {
+ dev_err(dev, "error: no packet data read\n");
+ ret = -ENODATA;
+ goto out;
+ }
+
+ if (rr3->bytes_read > rr3->pktlen) {
+ dev_err(dev, "bytes_read (%d) greater than pktlen (%d)\n",
+ rr3->bytes_read, rr3->pktlen);
+ ret = -EINVAL;
+ goto out;
+ } else if (rr3->bytes_read < rr3->pktlen)
+ /* we're still accumulating data */
+ return 0;
+
+ /* if we get here, we've got IR data to decode */
+ if (rr3->pkttype == RR3_MOD_SIGNAL_IN)
+ redrat3_process_ir_data(rr3);
+ else
+ rr3_dbg(dev, "discarding non-signal data packet "
+ "(type 0x%02x)\n", rr3->pkttype);
+
+out:
+ rr3->bytes_read = 0;
+ rr3->pktlen = 0;
+ rr3->pkttype = 0;
+ return ret;
+}
+
+/* callback function from USB when async USB request has completed */
+static void redrat3_handle_async(struct urb *urb, struct pt_regs *regs)
+{
+ struct redrat3_dev *rr3;
+
+ if (!urb)
+ return;
+
+ rr3 = urb->context;
+ if (!rr3) {
+ pr_err("%s called with invalid context!\n", __func__);
+ usb_unlink_urb(urb);
+ return;
+ }
+
+ rr3_ftr(rr3->dev, "Entering %s\n", __func__);
+
+ if (!rr3->det_enabled) {
+ rr3_dbg(rr3->dev, "received a read callback but detector "
+ "disabled - ignoring\n");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ redrat3_get_ir_data(rr3, urb->actual_length);
+ break;
+
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ usb_unlink_urb(urb);
+ return;
+
+ case -EPIPE:
+ default:
+ dev_warn(rr3->dev, "Error: urb status = %d\n", urb->status);
+ rr3->bytes_read = 0;
+ rr3->pktlen = 0;
+ rr3->pkttype = 0;
+ break;
+ }
+
+ if (!rr3->transmitting)
+ redrat3_issue_async(rr3);
+ else
+ rr3_dbg(rr3->dev, "IR transmit in progress\n");
+}
+
+static void redrat3_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct redrat3_dev *rr3;
+ int len;
+
+ if (!urb)
+ return;
+
+ rr3 = urb->context;
+ if (rr3) {
+ len = urb->actual_length;
+ rr3_ftr(rr3->dev, "%s: called (status=%d len=%d)\n",
+ __func__, urb->status, len);
+ }
+}
+
+static u16 mod_freq_to_val(unsigned int mod_freq)
+{
+ int mult = 6000000;
+
+ /* Clk used in mod. freq. generation is CLK24/4. */
+ return (u16)(65536 - (mult / mod_freq));
+}
+
+static int redrat3_set_tx_carrier(struct rc_dev *dev, u32 carrier)
+{
+ struct redrat3_dev *rr3 = dev->priv;
+
+ rr3->carrier = carrier;
+
+ return carrier;
+}
+
+static int redrat3_transmit_ir(struct rc_dev *rcdev, int *txbuf, u32 n)
+{
+ struct redrat3_dev *rr3 = rcdev->priv;
+ struct device *dev = rr3->dev;
+ struct redrat3_signal_header header;
+ int i, j, count, ret, ret_len, offset;
+ int lencheck, cur_sample_len, pipe;
+ char *buffer = NULL, *sigdata = NULL;
+ int *sample_lens = NULL;
+ u32 tmpi;
+ u16 tmps;
+ u8 *datap;
+ u8 curlencheck = 0;
+ u16 *lengths_ptr;
+ int sendbuf_len;
+
+ rr3_ftr(dev, "Entering %s\n", __func__);
+
+ if (rr3->transmitting) {
+ dev_warn(dev, "%s: transmitter already in use\n", __func__);
+ return -EAGAIN;
+ }
+
+ count = n / sizeof(int);
+ if (count > (RR3_DRIVER_MAXLENS * 2))
+ return -EINVAL;
+
+ rr3->transmitting = true;
+
+ redrat3_disable_detector(rr3);
+
+ if (rr3->det_enabled) {
+ dev_err(dev, "%s: cannot tx while rx is enabled\n", __func__);
+ ret = -EIO;
+ goto out;
+ }
+
+ sample_lens = kzalloc(sizeof(int) * RR3_DRIVER_MAXLENS, GFP_KERNEL);
+ if (!sample_lens) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < count; i++) {
+ for (lencheck = 0; lencheck < curlencheck; lencheck++) {
+ cur_sample_len = redrat3_us_to_len(txbuf[i]);
+ if (sample_lens[lencheck] == cur_sample_len)
+ break;
+ }
+ if (lencheck == curlencheck) {
+ cur_sample_len = redrat3_us_to_len(txbuf[i]);
+ rr3_dbg(dev, "txbuf[%d]=%u, pos %d, enc %u\n",
+ i, txbuf[i], curlencheck, cur_sample_len);
+ if (curlencheck < 255) {
+ /* now convert the value to a proper
+ * rr3 value.. */
+ sample_lens[curlencheck] = cur_sample_len;
+ curlencheck++;
+ } else {
+ dev_err(dev, "signal too long\n");
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+ }
+
+ sigdata = kzalloc((count + RR3_TX_TRAILER_LEN), GFP_KERNEL);
+ if (!sigdata) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ sigdata[count] = RR3_END_OF_SIGNAL;
+ sigdata[count + 1] = RR3_END_OF_SIGNAL;
+ for (i = 0; i < count; i++) {
+ for (j = 0; j < curlencheck; j++) {
+ if (sample_lens[j] == redrat3_us_to_len(txbuf[i]))
+ sigdata[i] = j;
+ }
+ }
+
+ offset = RR3_TX_HEADER_OFFSET;
+ sendbuf_len = RR3_HEADER_LENGTH + (sizeof(u16) * RR3_DRIVER_MAXLENS)
+ + count + RR3_TX_TRAILER_LEN + offset;
+
+ buffer = kzalloc(sendbuf_len, GFP_KERNEL);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* fill in our packet header */
+ header.length = sendbuf_len - offset;
+ header.transfer_type = RR3_MOD_SIGNAL_OUT;
+ header.pause = redrat3_len_to_us(100);
+ header.mod_freq_count = mod_freq_to_val(rr3->carrier);
+ header.no_periods = 0; /* n/a to transmit */
+ header.max_lengths = RR3_DRIVER_MAXLENS;
+ header.no_lengths = curlencheck;
+ header.max_sig_size = RR3_MAX_SIG_SIZE;
+ header.sig_size = count + RR3_TX_TRAILER_LEN;
+ /* we currently rely on repeat handling in the IR encoding source */
+ header.no_repeats = 0;
+
+ tmps = cpu_to_be16(header.length);
+ memcpy(buffer, &tmps, 2);
+
+ tmps = cpu_to_be16(header.transfer_type);
+ memcpy(buffer + 2, &tmps, 2);
+
+ tmpi = cpu_to_be32(header.pause);
+ memcpy(buffer + offset, &tmpi, sizeof(tmpi));
+
+ tmps = cpu_to_be16(header.mod_freq_count);
+ memcpy(buffer + offset + RR3_FREQ_COUNT_OFFSET, &tmps, 2);
+
+ buffer[offset + RR3_NUM_LENGTHS_OFFSET] = header.no_lengths;
+
+ tmps = cpu_to_be16(header.sig_size);
+ memcpy(buffer + offset + RR3_NUM_SIGS_OFFSET, &tmps, 2);
+
+ buffer[offset + RR3_REPEATS_OFFSET] = header.no_repeats;
+
+ lengths_ptr = (u16 *)(buffer + offset + RR3_HEADER_LENGTH);
+ for (i = 0; i < curlencheck; ++i)
+ lengths_ptr[i] = cpu_to_be16(sample_lens[i]);
+
+ datap = (u8 *)(buffer + offset + RR3_HEADER_LENGTH +
+ (sizeof(u16) * RR3_DRIVER_MAXLENS));
+ memcpy(datap, sigdata, (count + RR3_TX_TRAILER_LEN));
+
+ if (debug) {
+ redrat3_dump_signal_header(&header);
+ redrat3_dump_signal_data(buffer, header.sig_size);
+ }
+
+ pipe = usb_sndbulkpipe(rr3->udev, rr3->ep_out->bEndpointAddress);
+ tmps = usb_bulk_msg(rr3->udev, pipe, buffer,
+ sendbuf_len, &ret_len, 10 * HZ);
+ rr3_dbg(dev, "sent %d bytes, (ret %d)\n", ret_len, tmps);
+
+ /* now tell the hardware to transmit what we sent it */
+ pipe = usb_rcvctrlpipe(rr3->udev, 0);
+ ret = usb_control_msg(rr3->udev, pipe, RR3_TX_SEND_SIGNAL,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0, 0, buffer, 2, HZ * 10);
+
+ if (ret < 0)
+ dev_err(dev, "Error: control msg send failed, rc %d\n", ret);
+ else
+ ret = n;
+
+out:
+ kfree(sample_lens);
+ kfree(buffer);
+ kfree(sigdata);
+
+ rr3->transmitting = false;
+
+ redrat3_enable_detector(rr3);
+
+ return ret;
+}
+
+static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
+{
+ struct device *dev = rr3->dev;
+ struct rc_dev *rc;
+ int ret = -ENODEV;
+ u16 prod = le16_to_cpu(rr3->udev->descriptor.idProduct);
+
+ rc = rc_allocate_device();
+ if (!rc) {
+ dev_err(dev, "remote input dev allocation failed\n");
+ goto out;
+ }
+
+ snprintf(rr3->name, sizeof(rr3->name), "RedRat3%s "
+ "Infrared Remote Transceiver (%04x:%04x)",
+ prod == USB_RR3IIUSB_PRODUCT_ID ? "-II" : "",
+ le16_to_cpu(rr3->udev->descriptor.idVendor), prod);
+
+ usb_make_path(rr3->udev, rr3->phys, sizeof(rr3->phys));
+
+ rc->input_name = rr3->name;
+ rc->input_phys = rr3->phys;
+ usb_to_input_id(rr3->udev, &rc->input_id);
+ rc->dev.parent = dev;
+ rc->priv = rr3;
+ rc->driver_type = RC_DRIVER_IR_RAW;
+ rc->allowed_protos = RC_TYPE_ALL;
+ rc->min_timeout = MS_TO_NS(RR3_RX_MIN_TIMEOUT);
+ rc->max_timeout = MS_TO_NS(RR3_RX_MAX_TIMEOUT);
+ rc->timeout = redrat3_get_timeout(dev, rc, rr3->udev);
+ rc->tx_ir = redrat3_transmit_ir;
+ rc->s_tx_carrier = redrat3_set_tx_carrier;
+ rc->driver_name = DRIVER_NAME;
+ rc->map_name = RC_MAP_HAUPPAUGE;
+
+ ret = rc_register_device(rc);
+ if (ret < 0) {
+ dev_err(dev, "remote dev registration failed\n");
+ goto out;
+ }
+
+ return rc;
+
+out:
+ rc_free_device(rc);
+ return NULL;
+}
+
+static int __devinit redrat3_dev_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct device *dev = &intf->dev;
+ struct usb_host_interface *uhi;
+ struct redrat3_dev *rr3;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_endpoint_descriptor *ep_in = NULL;
+ struct usb_endpoint_descriptor *ep_out = NULL;
+ u8 addr, attrs;
+ int pipe, i;
+ int retval = -ENOMEM;
+
+ rr3_ftr(dev, "%s called\n", __func__);
+
+ uhi = intf->cur_altsetting;
+
+ /* find our bulk-in and bulk-out endpoints */
+ for (i = 0; i < uhi->desc.bNumEndpoints; ++i) {
+ ep = &uhi->endpoint[i].desc;
+ addr = ep->bEndpointAddress;
+ attrs = ep->bmAttributes;
+
+ if ((ep_in == NULL) &&
+ ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
+ ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ rr3_dbg(dev, "found bulk-in endpoint at 0x%02x\n",
+ ep->bEndpointAddress);
+ /* data comes in on 0x82, 0x81 is for other data... */
+ if (ep->bEndpointAddress == RR3_BULK_IN_EP_ADDR)
+ ep_in = ep;
+ }
+
+ if ((ep_out == NULL) &&
+ ((addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
+ ((attrs & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK)) {
+ rr3_dbg(dev, "found bulk-out endpoint at 0x%02x\n",
+ ep->bEndpointAddress);
+ ep_out = ep;
+ }
+ }
+
+ if (!ep_in || !ep_out) {
+ dev_err(dev, "Couldn't find both in and out endpoints\n");
+ retval = -ENODEV;
+ goto no_endpoints;
+ }
+
+ /* allocate memory for our device state and initialize it */
+ rr3 = kzalloc(sizeof(*rr3), GFP_KERNEL);
+ if (rr3 == NULL) {
+ dev_err(dev, "Memory allocation failure\n");
+ goto error;
+ }
+
+ rr3->dev = &intf->dev;
+
+ /* set up bulk-in endpoint */
+ rr3->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rr3->read_urb) {
+ dev_err(dev, "Read urb allocation failure\n");
+ goto error;
+ }
+
+ rr3->ep_in = ep_in;
+ rr3->bulk_in_buf = usb_alloc_coherent(udev, ep_in->wMaxPacketSize,
+ GFP_ATOMIC, &rr3->dma_in);
+ if (!rr3->bulk_in_buf) {
+ dev_err(dev, "Read buffer allocation failure\n");
+ goto error;
+ }
+
+ pipe = usb_rcvbulkpipe(udev, ep_in->bEndpointAddress);
+ usb_fill_bulk_urb(rr3->read_urb, udev, pipe,
+ rr3->bulk_in_buf, ep_in->wMaxPacketSize,
+ (usb_complete_t)redrat3_handle_async, rr3);
+
+ /* set up bulk-out endpoint*/
+ rr3->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rr3->write_urb) {
+ dev_err(dev, "Write urb allocation failure\n");
+ goto error;
+ }
+
+ rr3->ep_out = ep_out;
+ rr3->bulk_out_buf = usb_alloc_coherent(udev, ep_out->wMaxPacketSize,
+ GFP_ATOMIC, &rr3->dma_out);
+ if (!rr3->bulk_out_buf) {
+ dev_err(dev, "Write buffer allocation failure\n");
+ goto error;
+ }
+
+ pipe = usb_sndbulkpipe(udev, ep_out->bEndpointAddress);
+ usb_fill_bulk_urb(rr3->write_urb, udev, pipe,
+ rr3->bulk_out_buf, ep_out->wMaxPacketSize,
+ (usb_complete_t)redrat3_write_bulk_callback, rr3);
+
+ mutex_init(&rr3->lock);
+ rr3->udev = udev;
+
+ redrat3_reset(rr3);
+ redrat3_get_firmware_rev(rr3);
+
+ /* might be all we need to do? */
+ retval = redrat3_enable_detector(rr3);
+ if (retval < 0)
+ goto error;
+
+ /* default.. will get overridden by any sends with a freq defined */
+ rr3->carrier = 38000;
+
+ rr3->rc = redrat3_init_rc_dev(rr3);
+ if (!rr3->rc)
+ goto error;
+
+ setup_timer(&rr3->rx_timeout, redrat3_rx_timeout, (unsigned long)rr3);
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(intf, rr3);
+
+ rr3_ftr(dev, "Exiting %s\n", __func__);
+ return 0;
+
+error:
+ redrat3_delete(rr3, rr3->udev);
+
+no_endpoints:
+ dev_err(dev, "%s: retval = %x", __func__, retval);
+
+ return retval;
+}
+
+static void __devexit redrat3_dev_disconnect(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+
+ rr3_ftr(&intf->dev, "Entering %s\n", __func__);
+
+ if (!rr3)
+ return;
+
+ redrat3_disable_detector(rr3);
+
+ usb_set_intfdata(intf, NULL);
+ rc_unregister_device(rr3->rc);
+ redrat3_delete(rr3, udev);
+
+ rr3_ftr(&intf->dev, "RedRat3 IR Transceiver now disconnected\n");
+}
+
+static int redrat3_dev_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+ rr3_ftr(rr3->dev, "suspend\n");
+ usb_kill_urb(rr3->read_urb);
+ return 0;
+}
+
+static int redrat3_dev_resume(struct usb_interface *intf)
+{
+ struct redrat3_dev *rr3 = usb_get_intfdata(intf);
+ rr3_ftr(rr3->dev, "resume\n");
+ if (usb_submit_urb(rr3->read_urb, GFP_ATOMIC))
+ return -EIO;
+ return 0;
+}
+
+static struct usb_driver redrat3_dev_driver = {
+ .name = DRIVER_NAME,
+ .probe = redrat3_dev_probe,
+ .disconnect = redrat3_dev_disconnect,
+ .suspend = redrat3_dev_suspend,
+ .resume = redrat3_dev_resume,
+ .reset_resume = redrat3_dev_resume,
+ .id_table = redrat3_dev_table
+};
+
+static int __init redrat3_dev_init(void)
+{
+ int ret;
+
+ ret = usb_register(&redrat3_dev_driver);
+ if (ret < 0)
+ pr_err(DRIVER_NAME
+ ": usb register failed, result = %d\n", ret);
+
+ return ret;
+}
+
+static void __exit redrat3_dev_exit(void)
+{
+ usb_deregister(&redrat3_dev_driver);
+}
+
+module_init(redrat3_dev_init);
+module_exit(redrat3_dev_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_AUTHOR(DRIVER_AUTHOR2);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, redrat3_dev_table);
+
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Enable module debug spew. 0 = no debugging (default) "
+ "0x1 = standard debug messages, 0x2 = function tracing debug. "
+ "Flag bits are addative (i.e., 0x3 for both debug types).");
diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c
index 186de5522001..5d06b899e859 100644
--- a/drivers/media/rc/winbond-cir.c
+++ b/drivers/media/rc/winbond-cir.c
@@ -19,11 +19,12 @@
* o DSDT dumps
*
* Supported features:
+ * o IR Receive
+ * o IR Transmit
* o Wake-On-CIR functionality
*
* To do:
* o Learning
- * o IR Transmit
*
* 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
@@ -50,6 +51,8 @@
#include <linux/io.h>
#include <linux/bitrev.h>
#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
#include <media/rc-core.h>
#define DRVNAME "winbond-cir"
@@ -118,14 +121,24 @@
#define WBCIR_IRQ_NONE 0x00
/* RX data bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_RX 0x01
+/* TX data low bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_TX_LOW 0x02
/* Over/Under-flow bit for WBCIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
#define WBCIR_IRQ_ERR 0x04
+/* TX data empty bit for WBCEIR_REG_SP3_IER and WBCIR_REG_SP3_EIR */
+#define WBCIR_IRQ_TX_EMPTY 0x20
/* Led enable/disable bit for WBCIR_REG_ECEIR_CTS */
#define WBCIR_LED_ENABLE 0x80
/* RX data available bit for WBCIR_REG_SP3_LSR */
#define WBCIR_RX_AVAIL 0x01
+/* RX data overrun error bit for WBCIR_REG_SP3_LSR */
+#define WBCIR_RX_OVERRUN 0x02
+/* TX End-Of-Transmission bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_TX_EOT 0x04
/* RX disable bit for WBCIR_REG_SP3_ASCR */
#define WBCIR_RX_DISABLE 0x20
+/* TX data underrun error bit for WBCIR_REG_SP3_ASCR */
+#define WBCIR_TX_UNDERRUN 0x40
/* Extended mode enable bit for WBCIR_REG_SP3_EXCR1 */
#define WBCIR_EXT_ENABLE 0x01
/* Select compare register in WBCIR_REG_WCEIR_INDEX (bits 5 & 6) */
@@ -154,6 +167,21 @@ enum wbcir_protocol {
IR_PROTOCOL_RC6 = 0x2,
};
+/* Possible states for IR reception */
+enum wbcir_rxstate {
+ WBCIR_RXSTATE_INACTIVE = 0,
+ WBCIR_RXSTATE_ACTIVE,
+ WBCIR_RXSTATE_ERROR
+};
+
+/* Possible states for IR transmission */
+enum wbcir_txstate {
+ WBCIR_TXSTATE_INACTIVE = 0,
+ WBCIR_TXSTATE_ACTIVE,
+ WBCIR_TXSTATE_DONE,
+ WBCIR_TXSTATE_ERROR
+};
+
/* Misc */
#define WBCIR_NAME "Winbond CIR"
#define WBCIR_ID_FAMILY 0xF1 /* Family ID for the WPCD376I */
@@ -166,22 +194,29 @@ enum wbcir_protocol {
/* Per-device data */
struct wbcir_data {
spinlock_t spinlock;
+ struct rc_dev *dev;
+ struct led_classdev led;
unsigned long wbase; /* Wake-Up Baseaddr */
unsigned long ebase; /* Enhanced Func. Baseaddr */
unsigned long sbase; /* Serial Port Baseaddr */
unsigned int irq; /* Serial Port IRQ */
+ u8 irqmask;
- struct rc_dev *dev;
-
+ /* RX state */
+ enum wbcir_rxstate rxstate;
struct led_trigger *rxtrigger;
- struct led_trigger *txtrigger;
- struct led_classdev led;
+ struct ir_raw_event rxev;
- /* RX irdata state */
- bool irdata_active;
- bool irdata_error;
- struct ir_raw_event ev;
+ /* TX state */
+ enum wbcir_txstate txstate;
+ struct led_trigger *txtrigger;
+ u32 txlen;
+ u32 txoff;
+ u32 *txbuf;
+ wait_queue_head_t txwaitq;
+ u8 txmask;
+ u32 txcarrier;
};
static enum wbcir_protocol protocol = IR_PROTOCOL_RC6;
@@ -193,6 +228,10 @@ static int invert; /* default = 0 */
module_param(invert, bool, 0444);
MODULE_PARM_DESC(invert, "Invert the signal from the IR receiver");
+static int txandrx; /* default = 0 */
+module_param(txandrx, bool, 0444);
+MODULE_PARM_DESC(invert, "Allow simultaneous TX and RX");
+
static unsigned int wake_sc = 0x800F040C;
module_param(wake_sc, uint, 0644);
MODULE_PARM_DESC(wake_sc, "Scancode of the power-on IR command");
@@ -228,6 +267,17 @@ wbcir_select_bank(struct wbcir_data *data, enum wbcir_bank bank)
outb(bank, data->sbase + WBCIR_REG_SP3_BSR);
}
+static inline void
+wbcir_set_irqmask(struct wbcir_data *data, u8 irqmask)
+{
+ if (data->irqmask == irqmask)
+ return;
+
+ wbcir_select_bank(data, WBCIR_BANK_0);
+ outb(irqmask, data->sbase + WBCIR_REG_SP3_IER);
+ data->irqmask = irqmask;
+}
+
static enum led_brightness
wbcir_led_brightness_get(struct led_classdev *led_cdev)
{
@@ -279,97 +329,297 @@ wbcir_to_rc6cells(u8 val)
*
*****************************************************************************/
+static void
+wbcir_idle_rx(struct rc_dev *dev, bool idle)
+{
+ struct wbcir_data *data = dev->priv;
+
+ if (!idle && data->rxstate == WBCIR_RXSTATE_INACTIVE) {
+ data->rxstate = WBCIR_RXSTATE_ACTIVE;
+ led_trigger_event(data->rxtrigger, LED_FULL);
+ }
+
+ if (idle && data->rxstate != WBCIR_RXSTATE_INACTIVE)
+ /* Tell hardware to go idle by setting RXINACTIVE */
+ outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+}
+
+static void
+wbcir_irq_rx(struct wbcir_data *data, struct pnp_dev *device)
+{
+ u8 irdata;
+ DEFINE_IR_RAW_EVENT(rawir);
+
+ /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */
+ while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL) {
+ irdata = inb(data->sbase + WBCIR_REG_SP3_RXDATA);
+ if (data->rxstate == WBCIR_RXSTATE_ERROR)
+ continue;
+ rawir.pulse = irdata & 0x80 ? false : true;
+ rawir.duration = US_TO_NS((irdata & 0x7F) * 10);
+ ir_raw_event_store_with_filter(data->dev, &rawir);
+ }
+
+ /* Check if we should go idle */
+ if (data->dev->idle) {
+ led_trigger_event(data->rxtrigger, LED_OFF);
+ data->rxstate = WBCIR_RXSTATE_INACTIVE;
+ }
+
+ ir_raw_event_handle(data->dev);
+}
+
+static void
+wbcir_irq_tx(struct wbcir_data *data)
+{
+ unsigned int space;
+ unsigned int used;
+ u8 bytes[16];
+ u8 byte;
+
+ if (!data->txbuf)
+ return;
+
+ switch (data->txstate) {
+ case WBCIR_TXSTATE_INACTIVE:
+ /* TX FIFO empty */
+ space = 16;
+ led_trigger_event(data->txtrigger, LED_FULL);
+ break;
+ case WBCIR_TXSTATE_ACTIVE:
+ /* TX FIFO low (3 bytes or less) */
+ space = 13;
+ break;
+ case WBCIR_TXSTATE_ERROR:
+ space = 0;
+ break;
+ default:
+ return;
+ }
+
+ /*
+ * TX data is run-length coded in bytes: YXXXXXXX
+ * Y = space (1) or pulse (0)
+ * X = duration, encoded as (X + 1) * 10us (i.e 10 to 1280 us)
+ */
+ for (used = 0; used < space && data->txoff != data->txlen; used++) {
+ if (data->txbuf[data->txoff] == 0) {
+ data->txoff++;
+ continue;
+ }
+ byte = min((u32)0x80, data->txbuf[data->txoff]);
+ data->txbuf[data->txoff] -= byte;
+ byte--;
+ byte |= (data->txoff % 2 ? 0x80 : 0x00); /* pulse/space */
+ bytes[used] = byte;
+ }
+
+ while (data->txbuf[data->txoff] == 0 && data->txoff != data->txlen)
+ data->txoff++;
+
+ if (used == 0) {
+ /* Finished */
+ if (data->txstate == WBCIR_TXSTATE_ERROR)
+ /* Clear TX underrun bit */
+ outb(WBCIR_TX_UNDERRUN, data->sbase + WBCIR_REG_SP3_ASCR);
+ else
+ data->txstate = WBCIR_TXSTATE_DONE;
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
+ led_trigger_event(data->txtrigger, LED_OFF);
+ wake_up(&data->txwaitq);
+ } else if (data->txoff == data->txlen) {
+ /* At the end of transmission, tell the hw before last byte */
+ outsb(data->sbase + WBCIR_REG_SP3_TXDATA, bytes, used - 1);
+ outb(WBCIR_TX_EOT, data->sbase + WBCIR_REG_SP3_ASCR);
+ outb(bytes[used - 1], data->sbase + WBCIR_REG_SP3_TXDATA);
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR |
+ WBCIR_IRQ_TX_EMPTY);
+ } else {
+ /* More data to follow... */
+ outsb(data->sbase + WBCIR_REG_SP3_RXDATA, bytes, used);
+ if (data->txstate == WBCIR_TXSTATE_INACTIVE) {
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR |
+ WBCIR_IRQ_TX_LOW);
+ data->txstate = WBCIR_TXSTATE_ACTIVE;
+ }
+ }
+}
+
static irqreturn_t
wbcir_irq_handler(int irqno, void *cookie)
{
struct pnp_dev *device = cookie;
struct wbcir_data *data = pnp_get_drvdata(device);
unsigned long flags;
- u8 irdata[8];
- u8 disable = true;
u8 status;
- int i;
spin_lock_irqsave(&data->spinlock, flags);
-
wbcir_select_bank(data, WBCIR_BANK_0);
-
status = inb(data->sbase + WBCIR_REG_SP3_EIR);
+ status &= data->irqmask;
- if (!(status & (WBCIR_IRQ_RX | WBCIR_IRQ_ERR))) {
+ if (!status) {
spin_unlock_irqrestore(&data->spinlock, flags);
return IRQ_NONE;
}
- /* Check for e.g. buffer overflow */
if (status & WBCIR_IRQ_ERR) {
- data->irdata_error = true;
- ir_raw_event_reset(data->dev);
- }
-
- if (!(status & WBCIR_IRQ_RX))
- goto out;
+ /* RX overflow? (read clears bit) */
+ if (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_OVERRUN) {
+ data->rxstate = WBCIR_RXSTATE_ERROR;
+ ir_raw_event_reset(data->dev);
+ }
- if (!data->irdata_active) {
- data->irdata_active = true;
- led_trigger_event(data->rxtrigger, LED_FULL);
+ /* TX underflow? */
+ if (inb(data->sbase + WBCIR_REG_SP3_ASCR) & WBCIR_TX_UNDERRUN)
+ data->txstate = WBCIR_TXSTATE_ERROR;
}
- /* Since RXHDLEV is set, at least 8 bytes are in the FIFO */
- insb(data->sbase + WBCIR_REG_SP3_RXDATA, &irdata[0], 8);
+ if (status & WBCIR_IRQ_RX)
+ wbcir_irq_rx(data, device);
- for (i = 0; i < 8; i++) {
- u8 pulse;
- u32 duration;
+ if (status & (WBCIR_IRQ_TX_LOW | WBCIR_IRQ_TX_EMPTY))
+ wbcir_irq_tx(data);
- if (irdata[i] != 0xFF && irdata[i] != 0x00)
- disable = false;
-
- if (data->irdata_error)
- continue;
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return IRQ_HANDLED;
+}
- pulse = irdata[i] & 0x80 ? false : true;
- duration = (irdata[i] & 0x7F) * 10000; /* ns */
+/*****************************************************************************
+ *
+ * RC-CORE INTERFACE FUNCTIONS
+ *
+ *****************************************************************************/
- if (data->ev.pulse != pulse) {
- if (data->ev.duration != 0) {
- ir_raw_event_store(data->dev, &data->ev);
- data->ev.duration = 0;
- }
+static int
+wbcir_txcarrier(struct rc_dev *dev, u32 carrier)
+{
+ struct wbcir_data *data = dev->priv;
+ unsigned long flags;
+ u8 val;
+ u32 freq;
+
+ freq = DIV_ROUND_CLOSEST(carrier, 1000);
+ if (freq < 30 || freq > 60)
+ return -EINVAL;
+
+ switch (freq) {
+ case 58:
+ case 59:
+ case 60:
+ val = freq - 58;
+ freq *= 1000;
+ break;
+ case 57:
+ val = freq - 27;
+ freq = 56900;
+ break;
+ default:
+ val = freq - 27;
+ freq *= 1000;
+ break;
+ }
- data->ev.pulse = pulse;
- }
+ spin_lock_irqsave(&data->spinlock, flags);
+ if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return -EBUSY;
+ }
- data->ev.duration += duration;
+ if (data->txcarrier != freq) {
+ wbcir_select_bank(data, WBCIR_BANK_7);
+ wbcir_set_bits(data->sbase + WBCIR_REG_SP3_IRTXMC, val, 0x1F);
+ data->txcarrier = freq;
}
- if (disable) {
- if (data->ev.duration != 0 && !data->irdata_error) {
- ir_raw_event_store(data->dev, &data->ev);
- data->ev.duration = 0;
- }
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return 0;
+}
- /* Set RXINACTIVE */
- outb(WBCIR_RX_DISABLE, data->sbase + WBCIR_REG_SP3_ASCR);
+static int
+wbcir_txmask(struct rc_dev *dev, u32 mask)
+{
+ struct wbcir_data *data = dev->priv;
+ unsigned long flags;
+ u8 val;
- /* Drain the FIFO */
- while (inb(data->sbase + WBCIR_REG_SP3_LSR) & WBCIR_RX_AVAIL)
- inb(data->sbase + WBCIR_REG_SP3_RXDATA);
+ /* Four outputs, only one output can be enabled at a time */
+ switch (mask) {
+ case 0x1:
+ val = 0x0;
+ break;
+ case 0x2:
+ val = 0x1;
+ break;
+ case 0x4:
+ val = 0x2;
+ break;
+ case 0x8:
+ val = 0x3;
+ break;
+ default:
+ return -EINVAL;
+ }
- ir_raw_event_reset(data->dev);
- data->irdata_error = false;
- data->irdata_active = false;
- led_trigger_event(data->rxtrigger, LED_OFF);
+ spin_lock_irqsave(&data->spinlock, flags);
+ if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return -EBUSY;
}
- ir_raw_event_handle(data->dev);
+ if (data->txmask != mask) {
+ wbcir_set_bits(data->ebase + WBCIR_REG_ECEIR_CTS, val, 0x0c);
+ data->txmask = mask;
+ }
-out:
spin_unlock_irqrestore(&data->spinlock, flags);
- return IRQ_HANDLED;
+ return 0;
}
+static int
+wbcir_tx(struct rc_dev *dev, int *buf, u32 bufsize)
+{
+ struct wbcir_data *data = dev->priv;
+ u32 count;
+ unsigned i;
+ unsigned long flags;
+
+ /* bufsize has been sanity checked by the caller */
+ count = bufsize / sizeof(int);
+ /* Not sure if this is possible, but better safe than sorry */
+ spin_lock_irqsave(&data->spinlock, flags);
+ if (data->txstate != WBCIR_TXSTATE_INACTIVE) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ return -EBUSY;
+ }
+
+ /* Convert values to multiples of 10us */
+ for (i = 0; i < count; i++)
+ buf[i] = DIV_ROUND_CLOSEST(buf[i], 10);
+
+ /* Fill the TX fifo once, the irq handler will do the rest */
+ data->txbuf = buf;
+ data->txlen = count;
+ data->txoff = 0;
+ wbcir_irq_tx(data);
+
+ /* Wait for the TX to complete */
+ while (data->txstate == WBCIR_TXSTATE_ACTIVE) {
+ spin_unlock_irqrestore(&data->spinlock, flags);
+ wait_event(data->txwaitq, data->txstate != WBCIR_TXSTATE_ACTIVE);
+ spin_lock_irqsave(&data->spinlock, flags);
+ }
+
+ /* We're done */
+ if (data->txstate == WBCIR_TXSTATE_ERROR)
+ count = -EAGAIN;
+ data->txstate = WBCIR_TXSTATE_INACTIVE;
+ data->txbuf = NULL;
+ spin_unlock_irqrestore(&data->spinlock, flags);
+
+ return count;
+}
/*****************************************************************************
*
@@ -382,7 +632,7 @@ wbcir_shutdown(struct pnp_dev *device)
{
struct device *dev = &device->dev;
struct wbcir_data *data = pnp_get_drvdata(device);
- int do_wake = 1;
+ bool do_wake = true;
u8 match[11];
u8 mask[11];
u8 rc6_csl = 0;
@@ -392,14 +642,14 @@ wbcir_shutdown(struct pnp_dev *device)
memset(mask, 0, sizeof(mask));
if (wake_sc == INVALID_SCANCODE || !device_may_wakeup(dev)) {
- do_wake = 0;
+ do_wake = false;
goto finish;
}
switch (protocol) {
case IR_PROTOCOL_RC5:
if (wake_sc > 0xFFF) {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC5 - Invalid wake scancode\n");
break;
}
@@ -418,7 +668,7 @@ wbcir_shutdown(struct pnp_dev *device)
case IR_PROTOCOL_NEC:
if (wake_sc > 0xFFFFFF) {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "NEC - Invalid wake scancode\n");
break;
}
@@ -440,7 +690,7 @@ wbcir_shutdown(struct pnp_dev *device)
if (wake_rc6mode == 0) {
if (wake_sc > 0xFFFF) {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC6 - Invalid wake scancode\n");
break;
}
@@ -496,7 +746,7 @@ wbcir_shutdown(struct pnp_dev *device)
} else if (wake_sc <= 0x007FFFFF) {
rc6_csl = 60;
} else {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC6 - Invalid wake scancode\n");
break;
}
@@ -508,14 +758,14 @@ wbcir_shutdown(struct pnp_dev *device)
mask[i++] = 0x0F;
} else {
- do_wake = 0;
+ do_wake = false;
dev_err(dev, "RC6 - Invalid wake mode\n");
}
break;
default:
- do_wake = 0;
+ do_wake = false;
break;
}
@@ -551,21 +801,18 @@ finish:
wbcir_set_bits(data->wbase + WBCIR_REG_WCEIR_CTL, 0x00, 0x01);
}
- /* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
- /* Disable LED */
- data->irdata_active = false;
- led_trigger_event(data->rxtrigger, LED_OFF);
-
/*
* ACPI will set the HW disable bit for SP3 which means that the
* output signals are left in an undefined state which may cause
* spurious interrupts which we need to ignore until the hardware
* is reinitialized.
*/
+ wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
disable_irq(data->irq);
+
+ /* Disable LED */
+ led_trigger_event(data->rxtrigger, LED_OFF);
+ led_trigger_event(data->txtrigger, LED_OFF);
}
static int
@@ -581,8 +828,7 @@ wbcir_init_hw(struct wbcir_data *data)
u8 tmp;
/* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
+ wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
/* Set PROT_SEL, RX_INV, Clear CEIR_EN (needed for the led) */
tmp = protocol << 4;
@@ -606,10 +852,11 @@ wbcir_init_hw(struct wbcir_data *data)
outb(0x00, data->ebase + WBCIR_REG_ECEIR_CCTL);
/*
- * Clear IR LED, set SP3 clock to 24Mhz
+ * Clear IR LED, set SP3 clock to 24Mhz, set TX mask to IRTX1,
* set SP3_IRRX_SW to binary 01, helpfully not documented
*/
outb(0x10, data->ebase + WBCIR_REG_ECEIR_CTS);
+ data->txmask = 0x1;
/* Enable extended mode */
wbcir_select_bank(data, WBCIR_BANK_2);
@@ -657,18 +904,21 @@ wbcir_init_hw(struct wbcir_data *data)
wbcir_select_bank(data, WBCIR_BANK_4);
outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR1);
- /* Enable MSR interrupt, Clear AUX_IRX */
+ /* Disable MSR interrupt, clear AUX_IRX, mask RX during TX? */
wbcir_select_bank(data, WBCIR_BANK_5);
- outb(0x00, data->sbase + WBCIR_REG_SP3_IRCR2);
+ outb(txandrx ? 0x03 : 0x02, data->sbase + WBCIR_REG_SP3_IRCR2);
/* Disable CRC */
wbcir_select_bank(data, WBCIR_BANK_6);
outb(0x20, data->sbase + WBCIR_REG_SP3_IRCR3);
- /* Set RX/TX (de)modulation freq, not really used */
+ /* Set RX demodulation freq, not really used */
wbcir_select_bank(data, WBCIR_BANK_7);
outb(0xF2, data->sbase + WBCIR_REG_SP3_IRRXDC);
+
+ /* Set TX modulation, 36kHz, 7us pulse width */
outb(0x69, data->sbase + WBCIR_REG_SP3_IRTXMC);
+ data->txcarrier = 36000;
/* Set invert and pin direction */
if (invert)
@@ -683,16 +933,23 @@ wbcir_init_hw(struct wbcir_data *data)
/* Clear AUX status bits */
outb(0xE0, data->sbase + WBCIR_REG_SP3_ASCR);
- /* Clear IR decoding state */
- data->irdata_active = false;
- led_trigger_event(data->rxtrigger, LED_OFF);
- data->irdata_error = false;
- data->ev.duration = 0;
+ /* Clear RX state */
+ data->rxstate = WBCIR_RXSTATE_INACTIVE;
+ data->rxev.duration = 0;
ir_raw_event_reset(data->dev);
ir_raw_event_handle(data->dev);
+ /*
+ * Check TX state, if we did a suspend/resume cycle while TX was
+ * active, we will have a process waiting in txwaitq.
+ */
+ if (data->txstate == WBCIR_TXSTATE_ACTIVE) {
+ data->txstate = WBCIR_TXSTATE_ERROR;
+ wake_up(&data->txwaitq);
+ }
+
/* Enable interrupts */
- outb(WBCIR_IRQ_RX | WBCIR_IRQ_ERR, data->sbase + WBCIR_REG_SP3_IER);
+ wbcir_set_irqmask(data, WBCIR_IRQ_RX | WBCIR_IRQ_ERR);
}
static int
@@ -729,6 +986,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
pnp_set_drvdata(device, data);
spin_lock_init(&data->spinlock);
+ init_waitqueue_head(&data->txwaitq);
data->ebase = pnp_port_start(device, 0);
data->wbase = pnp_port_start(device, 1);
data->sbase = pnp_port_start(device, 2);
@@ -807,6 +1065,11 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND;
data->dev->input_id.product = WBCIR_ID_FAMILY;
data->dev->input_id.version = WBCIR_ID_CHIP;
+ data->dev->map_name = RC_MAP_RC6_MCE;
+ data->dev->s_idle = wbcir_idle_rx;
+ data->dev->s_tx_mask = wbcir_txmask;
+ data->dev->s_tx_carrier = wbcir_txcarrier;
+ data->dev->tx_ir = wbcir_tx;
data->dev->priv = data;
data->dev->dev.parent = &device->dev;
@@ -849,9 +1112,7 @@ wbcir_remove(struct pnp_dev *device)
struct wbcir_data *data = pnp_get_drvdata(device);
/* Disable interrupts */
- wbcir_select_bank(data, WBCIR_BANK_0);
- outb(WBCIR_IRQ_NONE, data->sbase + WBCIR_REG_SP3_IER);
-
+ wbcir_set_irqmask(data, WBCIR_IRQ_NONE);
free_irq(data->irq, device);
/* Clear status bits NEC_REP, BUFF, MSG_END, MATCH */