linux-sg2042/drivers/media/rc/serial_ir.c

860 lines
21 KiB
C
Raw Normal View History

/*
* 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)
* 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.
*/
#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;
if (pulse > 250000) {
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) {
if (l > 20000000) {
space = l;
ptr++;
return;
}
} else {
if (l > 20000000) {
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_err(&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 {
data = ktime_to_ns(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 + nsecs_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;
rcdev->rx_resolution = 250000;
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, 8 << 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)
{
int result;
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;
result = serial_ir_init();
if (!result)
return 0;
serial_ir_exit();
return result;
}
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 10:13:03 +08: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)");