kernel-ark/drivers/media/rc/serial_ir.c

845 lines
20 KiB
C
Raw Permalink Normal View History

treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157 Based on 3 normalized pattern(s): 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 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 [author] [kishon] [vijay] [abraham] [i] [kishon]@[ti] [com] 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 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 [author] [graeme] [gregory] [gg]@[slimlogic] [co] [uk] [author] [kishon] [vijay] [abraham] [i] [kishon]@[ti] [com] [based] [on] [twl6030]_[usb] [c] [author] [hema] [hk] [hemahk]@[ti] [com] 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 1105 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Richard Fontana <rfontana@redhat.com> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070033.202006027@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-27 06:55:06 +00:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* serial_ir.c
*
* serial_ir - Device driver that records pulse- and pause-lengths
* (space-lengths) between DDCD event on a serial port.
*
* Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
* Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
* Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
* Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
* Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
* Copyright (C) 2016 Sean Young <sean@mess.org> (port to rc-core)
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/serial_reg.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <media/rc-core.h>
struct serial_ir_hw {
int signal_pin;
int signal_pin_change;
u8 on;
u8 off;
unsigned set_send_carrier:1;
unsigned set_duty_cycle:1;
void (*send_pulse)(unsigned int length, ktime_t edge);
void (*send_space)(void);
spinlock_t lock;
};
#define IR_HOMEBREW 0
#define IR_IRDEO 1
#define IR_IRDEO_REMOTE 2
#define IR_ANIMAX 3
#define IR_IGOR 4
/* module parameters */
static int type;
static int io;
static int irq;
static ulong iommap;
static int ioshift;
static bool softcarrier = true;
static bool share_irq;
static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */
static bool txsense; /* 0 = active high, 1 = active low */
/* forward declarations */
static void send_pulse_irdeo(unsigned int length, ktime_t edge);
static void send_space_irdeo(void);
#ifdef CONFIG_IR_SERIAL_TRANSMITTER
static void send_pulse_homebrew(unsigned int length, ktime_t edge);
static void send_space_homebrew(void);
#endif
static struct serial_ir_hw hardware[] = {
[IR_HOMEBREW] = {
.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_HOMEBREW].lock),
.signal_pin = UART_MSR_DCD,
.signal_pin_change = UART_MSR_DDCD,
.on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
.off = (UART_MCR_RTS | UART_MCR_OUT2),
#ifdef CONFIG_IR_SERIAL_TRANSMITTER
.send_pulse = send_pulse_homebrew,
.send_space = send_space_homebrew,
.set_send_carrier = true,
.set_duty_cycle = true,
#endif
},
[IR_IRDEO] = {
.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IRDEO].lock),
.signal_pin = UART_MSR_DSR,
.signal_pin_change = UART_MSR_DDSR,
.on = UART_MCR_OUT2,
.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
.send_pulse = send_pulse_irdeo,
.send_space = send_space_irdeo,
.set_duty_cycle = true,
},
[IR_IRDEO_REMOTE] = {
.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IRDEO_REMOTE].lock),
.signal_pin = UART_MSR_DSR,
.signal_pin_change = UART_MSR_DDSR,
.on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
.send_pulse = send_pulse_irdeo,
.send_space = send_space_irdeo,
.set_duty_cycle = true,
},
[IR_ANIMAX] = {
.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_ANIMAX].lock),
.signal_pin = UART_MSR_DCD,
.signal_pin_change = UART_MSR_DDCD,
.on = 0,
.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
},
[IR_IGOR] = {
.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IGOR].lock),
.signal_pin = UART_MSR_DSR,
.signal_pin_change = UART_MSR_DDSR,
.on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
.off = (UART_MCR_RTS | UART_MCR_OUT2),
#ifdef CONFIG_IR_SERIAL_TRANSMITTER
.send_pulse = send_pulse_homebrew,
.send_space = send_space_homebrew,
.set_send_carrier = true,
.set_duty_cycle = true,
#endif
},
};
#define RS_ISR_PASS_LIMIT 256
struct serial_ir {
ktime_t lastkt;
struct rc_dev *rcdev;
struct platform_device *pdev;
struct timer_list timeout_timer;
unsigned int carrier;
unsigned int duty_cycle;
};
static struct serial_ir serial_ir;
/* fetch serial input packet (1 byte) from register offset */
static u8 sinp(int offset)
{
if (iommap)
/* the register is memory-mapped */
offset <<= ioshift;
return inb(io + offset);
}
/* write serial output packet (1 byte) of value to register offset */
static void soutp(int offset, u8 value)
{
if (iommap)
/* the register is memory-mapped */
offset <<= ioshift;
outb(value, io + offset);
}
static void on(void)
{
if (txsense)
soutp(UART_MCR, hardware[type].off);
else
soutp(UART_MCR, hardware[type].on);
}
static void off(void)
{
if (txsense)
soutp(UART_MCR, hardware[type].on);
else
soutp(UART_MCR, hardware[type].off);
}
static void send_pulse_irdeo(unsigned int length, ktime_t target)
{
long rawbits;
int i;
unsigned char output;
unsigned char chunk, shifted;
/* how many bits have to be sent ? */
rawbits = length * 1152 / 10000;
if (serial_ir.duty_cycle > 50)
chunk = 3;
else
chunk = 1;
for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
shifted = chunk << (i * 3);
shifted >>= 1;
output &= (~shifted);
i++;
if (i == 3) {
soutp(UART_TX, output);
while (!(sinp(UART_LSR) & UART_LSR_THRE))
;
output = 0x7f;
i = 0;
}
}
if (i != 0) {
soutp(UART_TX, output);
while (!(sinp(UART_LSR) & UART_LSR_TEMT))
;
}
}
static void send_space_irdeo(void)
{
}
#ifdef CONFIG_IR_SERIAL_TRANSMITTER
static void send_pulse_homebrew_softcarrier(unsigned int length, ktime_t edge)
{
ktime_t now, target = ktime_add_us(edge, length);
/*
* delta should never exceed 4 seconds and on m68k
* ndelay(s64) does not compile; so use s32 rather than s64.
*/
s32 delta;
unsigned int pulse, space;
/* Ensure the dividend fits into 32 bit */
pulse = DIV_ROUND_CLOSEST(serial_ir.duty_cycle * (NSEC_PER_SEC / 100),
serial_ir.carrier);
space = DIV_ROUND_CLOSEST((100 - serial_ir.duty_cycle) *
(NSEC_PER_SEC / 100), serial_ir.carrier);
for (;;) {
now = ktime_get();
if (ktime_compare(now, target) >= 0)
break;
on();
edge = ktime_add_ns(edge, pulse);
delta = ktime_to_ns(ktime_sub(edge, now));
if (delta > 0)
ndelay(delta);
now = ktime_get();
off();
if (ktime_compare(now, target) >= 0)
break;
edge = ktime_add_ns(edge, space);
delta = ktime_to_ns(ktime_sub(edge, now));
if (delta > 0)
ndelay(delta);
}
}
static void send_pulse_homebrew(unsigned int length, ktime_t edge)
{
if (softcarrier)
send_pulse_homebrew_softcarrier(length, edge);
else
on();
}
static void send_space_homebrew(void)
{
off();
}
#endif
static void frbwrite(unsigned int l, bool is_pulse)
{
/* simple noise filter */
static unsigned int ptr, pulse, space;
struct ir_raw_event ev = {};
if (ptr > 0 && is_pulse) {
pulse += l;
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
if (pulse > 250) {
ev.duration = space;
ev.pulse = false;
ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
ev.duration = pulse;
ev.pulse = true;
ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
ptr = 0;
pulse = 0;
}
return;
}
if (!is_pulse) {
if (ptr == 0) {
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
if (l > 20000) {
space = l;
ptr++;
return;
}
} else {
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
if (l > 20000) {
space += pulse;
if (space > IR_MAX_DURATION)
space = IR_MAX_DURATION;
space += l;
if (space > IR_MAX_DURATION)
space = IR_MAX_DURATION;
pulse = 0;
return;
}
ev.duration = space;
ev.pulse = false;
ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
ev.duration = pulse;
ev.pulse = true;
ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
ptr = 0;
pulse = 0;
}
}
ev.duration = l;
ev.pulse = is_pulse;
ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
}
static irqreturn_t serial_ir_irq_handler(int i, void *blah)
{
ktime_t kt;
int counter, dcd;
u8 status;
ktime_t delkt;
unsigned int data;
static int last_dcd = -1;
if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
/* not our interrupt */
return IRQ_NONE;
}
counter = 0;
do {
counter++;
status = sinp(UART_MSR);
if (counter > RS_ISR_PASS_LIMIT) {
dev_err(&serial_ir.pdev->dev, "Trapped in interrupt");
break;
}
if ((status & hardware[type].signal_pin_change) &&
sense != -1) {
/* get current time */
kt = ktime_get();
/*
* The driver needs to know if your receiver is
* active high or active low, or the space/pulse
* sense could be inverted.
*/
/* calc time since last interrupt in nanoseconds */
dcd = (status & hardware[type].signal_pin) ? 1 : 0;
if (dcd == last_dcd) {
dev_dbg(&serial_ir.pdev->dev,
"ignoring spike: %d %d %lldns %lldns\n",
dcd, sense, ktime_to_ns(kt),
ktime_to_ns(serial_ir.lastkt));
continue;
}
delkt = ktime_sub(kt, serial_ir.lastkt);
if (ktime_compare(delkt, ktime_set(15, 0)) > 0) {
data = IR_MAX_DURATION; /* really long time */
if (!(dcd ^ sense)) {
/* sanity check */
dev_err(&serial_ir.pdev->dev,
"dcd unexpected: %d %d %lldns %lldns\n",
dcd, sense, ktime_to_ns(kt),
ktime_to_ns(serial_ir.lastkt));
/*
* detecting pulse while this
* MUST be a space!
*/
sense = sense ? 0 : 1;
}
} else {
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
data = ktime_to_us(delkt);
}
frbwrite(data, !(dcd ^ sense));
serial_ir.lastkt = kt;
last_dcd = dcd;
}
} while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
mod_timer(&serial_ir.timeout_timer,
jiffies + usecs_to_jiffies(serial_ir.rcdev->timeout));
ir_raw_event_handle(serial_ir.rcdev);
return IRQ_HANDLED;
}
static int hardware_init_port(void)
{
u8 scratch, scratch2, scratch3;
/*
* This is a simple port existence test, borrowed from the autoconfig
* function in drivers/tty/serial/8250/8250_port.c
*/
scratch = sinp(UART_IER);
soutp(UART_IER, 0);
#ifdef __i386__
outb(0xff, 0x080);
#endif
scratch2 = sinp(UART_IER) & 0x0f;
soutp(UART_IER, 0x0f);
#ifdef __i386__
outb(0x00, 0x080);
#endif
scratch3 = sinp(UART_IER) & 0x0f;
soutp(UART_IER, scratch);
if (scratch2 != 0 || scratch3 != 0x0f) {
/* we fail, there's nothing here */
pr_err("port existence test failed, cannot continue\n");
return -ENODEV;
}
/* Set DLAB 0. */
soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
/* First of all, disable all interrupts */
soutp(UART_IER, sinp(UART_IER) &
(~(UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI)));
/* Clear registers. */
sinp(UART_LSR);
sinp(UART_RX);
sinp(UART_IIR);
sinp(UART_MSR);
/* Set line for power source */
off();
/* Clear registers again to be sure. */
sinp(UART_LSR);
sinp(UART_RX);
sinp(UART_IIR);
sinp(UART_MSR);
switch (type) {
case IR_IRDEO:
case IR_IRDEO_REMOTE:
/* setup port to 7N1 @ 115200 Baud */
/* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
/* Set DLAB 1. */
soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
/* Set divisor to 1 => 115200 Baud */
soutp(UART_DLM, 0);
soutp(UART_DLL, 1);
/* Set DLAB 0 + 7N1 */
soutp(UART_LCR, UART_LCR_WLEN7);
/* THR interrupt already disabled at this point */
break;
default:
break;
}
return 0;
}
static void serial_ir_timeout(struct timer_list *unused)
{
struct ir_raw_event ev = {
.timeout = true,
.duration = serial_ir.rcdev->timeout
};
ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
ir_raw_event_handle(serial_ir.rcdev);
}
/* Needed by serial_ir_probe() */
static int serial_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
unsigned int count);
static int serial_ir_tx_duty_cycle(struct rc_dev *dev, u32 cycle);
static int serial_ir_tx_carrier(struct rc_dev *dev, u32 carrier);
static int serial_ir_open(struct rc_dev *rcdev);
static void serial_ir_close(struct rc_dev *rcdev);
static int serial_ir_probe(struct platform_device *dev)
{
struct rc_dev *rcdev;
int i, nlow, nhigh, result;
rcdev = devm_rc_allocate_device(&dev->dev, RC_DRIVER_IR_RAW);
if (!rcdev)
return -ENOMEM;
if (hardware[type].send_pulse && hardware[type].send_space)
rcdev->tx_ir = serial_ir_tx;
if (hardware[type].set_send_carrier)
rcdev->s_tx_carrier = serial_ir_tx_carrier;
if (hardware[type].set_duty_cycle)
rcdev->s_tx_duty_cycle = serial_ir_tx_duty_cycle;
switch (type) {
case IR_HOMEBREW:
rcdev->device_name = "Serial IR type home-brew";
break;
case IR_IRDEO:
rcdev->device_name = "Serial IR type IRdeo";
break;
case IR_IRDEO_REMOTE:
rcdev->device_name = "Serial IR type IRdeo remote";
break;
case IR_ANIMAX:
rcdev->device_name = "Serial IR type AnimaX";
break;
case IR_IGOR:
rcdev->device_name = "Serial IR type IgorPlug";
break;
}
rcdev->input_phys = KBUILD_MODNAME "/input0";
rcdev->input_id.bustype = BUS_HOST;
rcdev->input_id.vendor = 0x0001;
rcdev->input_id.product = 0x0001;
rcdev->input_id.version = 0x0100;
rcdev->open = serial_ir_open;
rcdev->close = serial_ir_close;
rcdev->dev.parent = &serial_ir.pdev->dev;
rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
rcdev->driver_name = KBUILD_MODNAME;
rcdev->map_name = RC_MAP_RC6_MCE;
rcdev->min_timeout = 1;
rcdev->timeout = IR_DEFAULT_TIMEOUT;
rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
media: rc: harmonize infrared durations to microseconds rc-core kapi uses nanoseconds for infrared durations for receiving, and microseconds for sending. The uapi already uses microseconds for both, so this patch does not change the uapi. Infrared durations do not need nanosecond resolution. IR protocols do not have durations shorter than about 100 microseconds. Some IR hardware offers 250 microseconds resolution, which is sufficient for most protocols. Better hardware has 50 microsecond resolution and is enough for every protocol I am aware off. Unify on microseconds everywhere. This simplifies the code since less conversion between microseconds and nanoseconds needs to be done. This affects: - rx_resolution member of struct rc_dev - timeout member of struct rc_dev - duration member in struct ir_raw_event Cc: "Bruno Prémont" <bonbons@linux-vserver.org> Cc: Hans Verkuil <hverkuil-cisco@xs4all.nl> Cc: Maxim Levitsky <maximlevitsky@gmail.com> Cc: Patrick Lerda <patrick9876@free.fr> Cc: Kevin Hilman <khilman@baylibre.com> Cc: Neil Armstrong <narmstrong@baylibre.com> Cc: Jerome Brunet <jbrunet@baylibre.com> Cc: Martin Blumenstingl <martin.blumenstingl@googlemail.com> Cc: Sean Wang <sean.wang@mediatek.com> Cc: Matthias Brugger <matthias.bgg@gmail.com> Cc: Patrice Chotard <patrice.chotard@st.com> Cc: Maxime Ripard <mripard@kernel.org> Cc: Chen-Yu Tsai <wens@csie.org> Cc: "David Härdeman" <david@hardeman.nu> Cc: Benjamin Valentin <benpicco@googlemail.com> Cc: Antti Palosaari <crope@iki.fi> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
2020-08-23 17:23:05 +00:00
rcdev->rx_resolution = 250;
serial_ir.rcdev = rcdev;
timer_setup(&serial_ir.timeout_timer, serial_ir_timeout, 0);
result = devm_request_irq(&dev->dev, irq, serial_ir_irq_handler,
share_irq ? IRQF_SHARED : 0,
KBUILD_MODNAME, &hardware);
if (result < 0) {
if (result == -EBUSY)
dev_err(&dev->dev, "IRQ %d busy\n", irq);
else if (result == -EINVAL)
dev_err(&dev->dev, "Bad irq number or handler\n");
return result;
}
/* Reserve io region. */
if ((iommap &&
(devm_request_mem_region(&dev->dev, iommap, 8UL << ioshift,
KBUILD_MODNAME) == NULL)) ||
(!iommap && (devm_request_region(&dev->dev, io, 8,
KBUILD_MODNAME) == NULL))) {
dev_err(&dev->dev, "port %04x already in use\n", io);
dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n");
dev_warn(&dev->dev,
"or compile the serial port driver as module and\n");
dev_warn(&dev->dev, "make sure this module is loaded first\n");
return -EBUSY;
}
result = hardware_init_port();
if (result < 0)
return result;
/* Initialize pulse/space widths */
serial_ir.duty_cycle = 50;
serial_ir.carrier = 38000;
/* If pin is high, then this must be an active low receiver. */
if (sense == -1) {
/* wait 1/2 sec for the power supply */
msleep(500);
/*
* probe 9 times every 0.04s, collect "votes" for
* active high/low
*/
nlow = 0;
nhigh = 0;
for (i = 0; i < 9; i++) {
if (sinp(UART_MSR) & hardware[type].signal_pin)
nlow++;
else
nhigh++;
msleep(40);
}
sense = nlow >= nhigh ? 1 : 0;
dev_info(&dev->dev, "auto-detected active %s receiver\n",
sense ? "low" : "high");
} else
dev_info(&dev->dev, "Manually using active %s receiver\n",
sense ? "low" : "high");
dev_dbg(&dev->dev, "Interrupt %d, port %04x obtained\n", irq, io);
return devm_rc_register_device(&dev->dev, rcdev);
}
static int serial_ir_open(struct rc_dev *rcdev)
{
unsigned long flags;
/* initialize timestamp */
serial_ir.lastkt = ktime_get();
spin_lock_irqsave(&hardware[type].lock, flags);
/* Set DLAB 0. */
soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
soutp(UART_IER, sinp(UART_IER) | UART_IER_MSI);
spin_unlock_irqrestore(&hardware[type].lock, flags);
return 0;
}
static void serial_ir_close(struct rc_dev *rcdev)
{
unsigned long flags;
spin_lock_irqsave(&hardware[type].lock, flags);
/* Set DLAB 0. */
soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
/* First of all, disable all interrupts */
soutp(UART_IER, sinp(UART_IER) &
(~(UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI)));
spin_unlock_irqrestore(&hardware[type].lock, flags);
}
static int serial_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
unsigned int count)
{
unsigned long flags;
ktime_t edge;
s64 delta;
int i;
spin_lock_irqsave(&hardware[type].lock, flags);
if (type == IR_IRDEO) {
/* DTR, RTS down */
on();
}
edge = ktime_get();
for (i = 0; i < count; i++) {
if (i % 2)
hardware[type].send_space();
else
hardware[type].send_pulse(txbuf[i], edge);
edge = ktime_add_us(edge, txbuf[i]);
delta = ktime_us_delta(edge, ktime_get());
if (delta > 25) {
spin_unlock_irqrestore(&hardware[type].lock, flags);
usleep_range(delta - 25, delta + 25);
spin_lock_irqsave(&hardware[type].lock, flags);
} else if (delta > 0) {
udelay(delta);
}
}
off();
spin_unlock_irqrestore(&hardware[type].lock, flags);
return count;
}
static int serial_ir_tx_duty_cycle(struct rc_dev *dev, u32 cycle)
{
serial_ir.duty_cycle = cycle;
return 0;
}
static int serial_ir_tx_carrier(struct rc_dev *dev, u32 carrier)
{
if (carrier > 500000 || carrier < 20000)
return -EINVAL;
serial_ir.carrier = carrier;
return 0;
}
static int serial_ir_suspend(struct platform_device *dev,
pm_message_t state)
{
/* Set DLAB 0. */
soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
/* Disable all interrupts */
soutp(UART_IER, sinp(UART_IER) &
(~(UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI)));
/* Clear registers. */
sinp(UART_LSR);
sinp(UART_RX);
sinp(UART_IIR);
sinp(UART_MSR);
return 0;
}
static int serial_ir_resume(struct platform_device *dev)
{
unsigned long flags;
int result;
result = hardware_init_port();
if (result < 0)
return result;
spin_lock_irqsave(&hardware[type].lock, flags);
/* Enable Interrupt */
serial_ir.lastkt = ktime_get();
soutp(UART_IER, sinp(UART_IER) | UART_IER_MSI);
off();
spin_unlock_irqrestore(&hardware[type].lock, flags);
return 0;
}
static struct platform_driver serial_ir_driver = {
.probe = serial_ir_probe,
.suspend = serial_ir_suspend,
.resume = serial_ir_resume,
.driver = {
.name = "serial_ir",
},
};
static int __init serial_ir_init(void)
{
int result;
result = platform_driver_register(&serial_ir_driver);
if (result)
return result;
serial_ir.pdev = platform_device_alloc("serial_ir", 0);
if (!serial_ir.pdev) {
result = -ENOMEM;
goto exit_driver_unregister;
}
result = platform_device_add(serial_ir.pdev);
if (result)
goto exit_device_put;
return 0;
exit_device_put:
platform_device_put(serial_ir.pdev);
exit_driver_unregister:
platform_driver_unregister(&serial_ir_driver);
return result;
}
static void serial_ir_exit(void)
{
platform_device_unregister(serial_ir.pdev);
platform_driver_unregister(&serial_ir_driver);
}
static int __init serial_ir_init_module(void)
{
switch (type) {
case IR_HOMEBREW:
case IR_IRDEO:
case IR_IRDEO_REMOTE:
case IR_ANIMAX:
case IR_IGOR:
/* if nothing specified, use ttyS0/com1 and irq 4 */
io = io ? io : 0x3f8;
irq = irq ? irq : 4;
break;
default:
return -EINVAL;
}
if (!softcarrier) {
switch (type) {
case IR_HOMEBREW:
case IR_IGOR:
hardware[type].set_send_carrier = false;
hardware[type].set_duty_cycle = false;
break;
}
}
/* make sure sense is either -1, 0, or 1 */
if (sense != -1)
sense = !!sense;
media: serial_ir: Fix use-after-free in serial_ir_init_module Syzkaller report this: BUG: KASAN: use-after-free in sysfs_remove_file_ns+0x5f/0x70 fs/sysfs/file.c:468 Read of size 8 at addr ffff8881dc7ae030 by task syz-executor.0/6249 CPU: 1 PID: 6249 Comm: syz-executor.0 Not tainted 5.0.0-rc8+ #3 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.2-1ubuntu1 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xfa/0x1ce lib/dump_stack.c:113 print_address_description+0x65/0x270 mm/kasan/report.c:187 kasan_report+0x149/0x18d mm/kasan/report.c:317 ? 0xffffffffc1728000 sysfs_remove_file_ns+0x5f/0x70 fs/sysfs/file.c:468 sysfs_remove_file include/linux/sysfs.h:519 [inline] driver_remove_file+0x40/0x50 drivers/base/driver.c:122 remove_bind_files drivers/base/bus.c:585 [inline] bus_remove_driver+0x186/0x220 drivers/base/bus.c:725 driver_unregister+0x6c/0xa0 drivers/base/driver.c:197 serial_ir_init_module+0x169/0x1000 [serial_ir] do_one_initcall+0xfa/0x5ca init/main.c:887 do_init_module+0x204/0x5f6 kernel/module.c:3460 load_module+0x66b2/0x8570 kernel/module.c:3808 __do_sys_finit_module+0x238/0x2a0 kernel/module.c:3902 do_syscall_64+0x147/0x600 arch/x86/entry/common.c:290 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x462e99 Code: f7 d8 64 89 02 b8 ff ff ff ff c3 66 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 bc ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007f9450132c58 EFLAGS: 00000246 ORIG_RAX: 0000000000000139 RAX: ffffffffffffffda RBX: 000000000073bf00 RCX: 0000000000462e99 RDX: 0000000000000000 RSI: 0000000020000100 RDI: 0000000000000003 RBP: 00007f9450132c70 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00007f94501336bc R13: 00000000004bcefa R14: 00000000006f6fb0 R15: 0000000000000004 Allocated by task 6249: set_track mm/kasan/common.c:85 [inline] __kasan_kmalloc.constprop.3+0xa0/0xd0 mm/kasan/common.c:495 kmalloc include/linux/slab.h:545 [inline] kzalloc include/linux/slab.h:740 [inline] bus_add_driver+0xc0/0x610 drivers/base/bus.c:651 driver_register+0x1bb/0x3f0 drivers/base/driver.c:170 serial_ir_init_module+0xe8/0x1000 [serial_ir] do_one_initcall+0xfa/0x5ca init/main.c:887 do_init_module+0x204/0x5f6 kernel/module.c:3460 load_module+0x66b2/0x8570 kernel/module.c:3808 __do_sys_finit_module+0x238/0x2a0 kernel/module.c:3902 do_syscall_64+0x147/0x600 arch/x86/entry/common.c:290 entry_SYSCALL_64_after_hwframe+0x49/0xbe Freed by task 6249: set_track mm/kasan/common.c:85 [inline] __kasan_slab_free+0x130/0x180 mm/kasan/common.c:457 slab_free_hook mm/slub.c:1430 [inline] slab_free_freelist_hook mm/slub.c:1457 [inline] slab_free mm/slub.c:3005 [inline] kfree+0xe1/0x270 mm/slub.c:3957 kobject_cleanup lib/kobject.c:662 [inline] kobject_release lib/kobject.c:691 [inline] kref_put include/linux/kref.h:67 [inline] kobject_put+0x146/0x240 lib/kobject.c:708 bus_remove_driver+0x10e/0x220 drivers/base/bus.c:732 driver_unregister+0x6c/0xa0 drivers/base/driver.c:197 serial_ir_init_module+0x14c/0x1000 [serial_ir] do_one_initcall+0xfa/0x5ca init/main.c:887 do_init_module+0x204/0x5f6 kernel/module.c:3460 load_module+0x66b2/0x8570 kernel/module.c:3808 __do_sys_finit_module+0x238/0x2a0 kernel/module.c:3902 do_syscall_64+0x147/0x600 arch/x86/entry/common.c:290 entry_SYSCALL_64_after_hwframe+0x49/0xbe The buggy address belongs to the object at ffff8881dc7ae000 which belongs to the cache kmalloc-256 of size 256 The buggy address is located 48 bytes inside of 256-byte region [ffff8881dc7ae000, ffff8881dc7ae100) The buggy address belongs to the page: page:ffffea000771eb80 count:1 mapcount:0 mapping:ffff8881f6c02e00 index:0x0 flags: 0x2fffc0000000200(slab) raw: 02fffc0000000200 ffffea0007d14800 0000000400000002 ffff8881f6c02e00 raw: 0000000000000000 00000000800c000c 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8881dc7adf00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff8881dc7adf80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff8881dc7ae000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff8881dc7ae080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff8881dc7ae100: fc fc fc fc fc fc fc fc 00 00 00 00 00 00 00 00 There are already cleanup handlings in serial_ir_init error path, no need to call serial_ir_exit do it again in serial_ir_init_module, otherwise will trigger a use-after-free issue. Fixes: fa5dc29c1fcc ("[media] lirc_serial: move out of staging and rename to serial_ir") Reported-by: Hulk Robot <hulkci@huawei.com> Signed-off-by: YueHaibing <yuehaibing@huawei.com> Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
2019-03-05 05:40:26 +00:00
return serial_ir_init();
}
static void __exit serial_ir_exit_module(void)
{
del_timer_sync(&serial_ir.timeout_timer);
serial_ir_exit();
}
module_init(serial_ir_init_module);
module_exit(serial_ir_exit_module);
MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, Christoph Bartelmus, Andrei Tanas");
MODULE_LICENSE("GPL");
module_param(type, int, 0444);
MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo, 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug");
module_param_hw(io, int, ioport, 0444);
MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
/* some architectures (e.g. intel xscale) have memory mapped registers */
Annotation of module parameters that specify device settings -----BEGIN PGP SIGNATURE----- iQIVAwUAWPiW6vSw1s6N8H32AQLOrw/+NTqGf7bjq+64YKS6NfR0XDgE+wNJltGO ck7zJW3NHIg76RNu8s0I9xg5aVmwizz3Z5DGROZquaolnezux4tQihZ3AFyxIzLc +Y3WHYagcML7yFfjl/WznCLRD5EW3yPln4lCvQO0nW/xICRYeRI057JaIbi2Dtek BhcXt3c4AjXDLdYJkgtHV3p2R2mt8hcdFdWqqx6s7JaIThZNRGNzxAgtbcB9k5IW HVG9ZEIL73VBYWHrYivzjHYF5rBnNCPt87eOwDQeTOSkhv8te+u9k+bH8vxZw1T0 XUtDrLBndKiuVo2GUfLkkF8LItx3Q9eLCJYy0joaIliyPqTEsPx9KjQ+Af0cxS9s ZPCZ5SYf96stKmDeL5xaMfrAmeyVHJ4lc4JTOqdzbIT8blsOSfYO/03p0ALShSDv /RQLaKGlf8Bjoy8PwKFcXb4sIDufcd/U1Av/EMFXxOfgN/u2JUkGKq6EaIM5B68L fHPje+aR9VNELPmPjwNOWtmN4I79EH3EItQf7zv0KG+UeKhcHLx/EAcSJ3ZRKEkH Lathg7pPOEJGArPiVO79TZzBG01ADn1aiwv65XObMzNZ+54xI/mN/Y1DNF/kL5jU XzvNzEjFt8mwMIZGVNdAt4+pDyMfIZGZSyUkSRKFnaQZMIvQrfQIU9RLBYLX5eOx +/p0VkIwDpg= =lbS7 -----END PGP SIGNATURE----- Merge tag 'hwparam-20170420' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs Pull hw lockdown support from David Howells: "Annotation of module parameters that configure hardware resources including ioports, iomem addresses, irq lines and dma channels. This allows a future patch to prohibit the use of such module parameters to prevent that hardware from being abused to gain access to the running kernel image as part of locking the kernel down under UEFI secure boot conditions. Annotations are made by changing: module_param(n, t, p) module_param_named(n, v, t, p) module_param_array(n, t, m, p) to: module_param_hw(n, t, hwtype, p) module_param_hw_named(n, v, t, hwtype, p) module_param_hw_array(n, t, hwtype, m, p) where the module parameter refers to a hardware setting hwtype specifies the type of the resource being configured. This can be one of: ioport Module parameter configures an I/O port iomem Module parameter configures an I/O mem address ioport_or_iomem Module parameter could be either (runtime set) irq Module parameter configures an I/O port dma Module parameter configures a DMA channel dma_addr Module parameter configures a DMA buffer address other Module parameter configures some other value Note that the hwtype is compile checked, but not currently stored (the lockdown code probably won't require it). It is, however, there for future use. A bonus is that the hwtype can also be used for grepping. The intention is for the kernel to ignore or reject attempts to set annotated module parameters if lockdown is enabled. This applies to options passed on the boot command line, passed to insmod/modprobe or direct twiddling in /sys/module/ parameter files. The module initialisation then needs to handle the parameter not being set, by (1) giving an error, (2) probing for a value or (3) using a reasonable default. What I can't do is just reject a module out of hand because it may take a hardware setting in the module parameters. Some important modules, some ipmi stuff for instance, both probe for hardware and allow hardware to be manually specified; if the driver is aborts with any error, you don't get any ipmi hardware. Further, trying to do this entirely in the module initialisation code doesn't protect against sysfs twiddling. [!] Note that in and of itself, this series of patches should have no effect on the the size of the kernel or code execution - that is left to a patch in the next series to effect. It does mark annotated kernel parameters with a KERNEL_PARAM_FL_HWPARAM flag in an already existing field" * tag 'hwparam-20170420' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: (38 commits) Annotate hardware config module parameters in sound/pci/ Annotate hardware config module parameters in sound/oss/ Annotate hardware config module parameters in sound/isa/ Annotate hardware config module parameters in sound/drivers/ Annotate hardware config module parameters in fs/pstore/ Annotate hardware config module parameters in drivers/watchdog/ Annotate hardware config module parameters in drivers/video/ Annotate hardware config module parameters in drivers/tty/ Annotate hardware config module parameters in drivers/staging/vme/ Annotate hardware config module parameters in drivers/staging/speakup/ Annotate hardware config module parameters in drivers/staging/media/ Annotate hardware config module parameters in drivers/scsi/ Annotate hardware config module parameters in drivers/pcmcia/ Annotate hardware config module parameters in drivers/pci/hotplug/ Annotate hardware config module parameters in drivers/parport/ Annotate hardware config module parameters in drivers/net/wireless/ Annotate hardware config module parameters in drivers/net/wan/ Annotate hardware config module parameters in drivers/net/irda/ Annotate hardware config module parameters in drivers/net/hamradio/ Annotate hardware config module parameters in drivers/net/ethernet/ ...
2017-05-11 02:13:03 +00:00
module_param_hw(iommap, ulong, other, 0444);
MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O (0 = no memory mapped io)");
/*
* some architectures (e.g. intel xscale) align the 8bit serial registers
* on 32bit word boundaries.
* See linux-kernel/drivers/tty/serial/8250/8250.c serial_in()/out()
*/
module_param_hw(ioshift, int, other, 0444);
MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
module_param_hw(share_irq, bool, other, 0444);
MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
module_param(sense, int, 0444);
MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit (0 = active high, 1 = active low )");
#ifdef CONFIG_IR_SERIAL_TRANSMITTER
module_param(txsense, bool, 0444);
MODULE_PARM_DESC(txsense, "Sense of transmitter circuit (0 = active high, 1 = active low )");
#endif
module_param(softcarrier, bool, 0444);
MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");