![Jarod Wilson](/assets/img/avatar_default.png)
When device_add is called in rc_register_device, the rc sysfs nodes show up, and there's a window in which ir-keytable can be launched via udev and trigger a show_protocols call, which runs without various rc_dev fields filled in yet. Add some locking around registration and store/show_protocols to prevent that from happening. The problem manifests thusly: [64692.957872] BUG: unable to handle kernel NULL pointer dereference at 0000000000000090 [64692.957878] IP: [<ffffffffa036a4c1>] show_protocols+0x47/0xf1 [rc_core] [64692.957890] PGD 19cfc7067 PUD 19cfc6067 PMD 0 [64692.957894] Oops: 0000 [#1] SMP [64692.957897] last sysfs file: /sys/devices/pci0000:00/0000:00:03.1/usb3/3-1/3-1:1.0/rc/rc2/protocols [64692.957902] CPU 3 [64692.957903] Modules linked in: redrat3(+) ir_lirc_codec lirc_dev ir_sony_decoder ir_jvc_decoder ir_rc6_decoder ir_rc5_decoder rc_hauppauge ir_nec _decoder rc_core ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables snd_emu10k1_synth snd_emux_synth snd_seq_virmidi snd_seq_mi di_event snd_seq_midi_emul snd_emu10k1 snd_rawmidi snd_ac97_codec ac97_bus snd_seq snd_pcm snd_seq_device snd_timer snd_page_alloc snd_util_mem pcsp kr tg3 snd_hwdep emu10k1_gp snd amd64_edac_mod gameport edac_core soundcore edac_mce_amd k8temp shpchp i2c_piix4 lm63 e100 mii uinput ipv6 raid0 rai d1 ata_generic firewire_ohci pata_acpi firewire_core crc_itu_t sata_svw pata_serverworks floppy radeon ttm drm_kms_helper drm i2c_algo_bit i2c_core [last unloaded: redrat3] [64692.957949] [64692.957952] Pid: 12265, comm: ir-keytable Tainted: G M W 2.6.39-rc6+ #2 empty empty/TYAN Thunder K8HM S3892 [64692.957957] RIP: 0010:[<ffffffffa036a4c1>] [<ffffffffa036a4c1>] show_protocols+0x47/0xf1 [rc_core] [64692.957962] RSP: 0018:ffff880194509e38 EFLAGS: 00010202 [64692.957964] RAX: 0000000000000000 RBX: ffffffffa036d1e0 RCX: ffffffffa036a47a [64692.957966] RDX: ffff88019a84d000 RSI: ffffffffa036d1e0 RDI: ffff88019cf2f3f0 [64692.957969] RBP: ffff880194509e68 R08: 0000000000000002 R09: 0000000000000000 [64692.957971] R10: 0000000000000002 R11: 0000000000001617 R12: ffff88019a84d000 [64692.957973] R13: 0000000000001000 R14: ffff8801944d2e38 R15: ffff88019ce5f190 [64692.957976] FS: 00007f0a30c9a720(0000) GS:ffff88019fc00000(0000) knlGS:0000000000000000 [64692.957979] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [64692.957981] CR2: 0000000000000090 CR3: 000000019a8e0000 CR4: 00000000000006e0 [64692.957983] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [64692.957986] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [64692.957989] Process ir-keytable (pid: 12265, threadinfo ffff880194508000, task ffff88019a9fc720) [64692.957991] Stack: [64692.957992] 0000000000000002 ffffffffa036d1e0 ffff880194509f58 0000000000001000 [64692.957997] ffff8801944d2e38 ffff88019ce5f190 ffff880194509e98 ffffffff8131484b [64692.958001] ffffffff8118e923 ffffffff810e9b2f ffff880194509e98 ffff8801944d2e18 [64692.958005] Call Trace: [64692.958014] [<ffffffff8131484b>] dev_attr_show+0x27/0x4e [64692.958014] [<ffffffff8118e923>] ? sysfs_read_file+0x94/0x172 [64692.958014] [<ffffffff810e9b2f>] ? __get_free_pages+0x16/0x52 [64692.958014] [<ffffffff8118e94c>] sysfs_read_file+0xbd/0x172 [64692.958014] [<ffffffff8113205e>] vfs_read+0xac/0xf3 [64692.958014] [<ffffffff8113347b>] ? fget_light+0x3a/0xa1 [64692.958014] [<ffffffff811320f2>] sys_read+0x4d/0x74 [64692.958014] [<ffffffff814c19c2>] system_call_fastpath+0x16/0x1b Its a bit difficult to reproduce, but I'm fairly confident this has fixed the problem. Signed-off-by: Jarod Wilson <jarod@redhat.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
227 lines
7.4 KiB
C
227 lines
7.4 KiB
C
/*
|
|
* Remote Controller core header
|
|
*
|
|
* Copyright (C) 2009-2010 by Mauro Carvalho Chehab <mchehab@redhat.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#ifndef _RC_CORE
|
|
#define _RC_CORE
|
|
|
|
#include <linux/spinlock.h>
|
|
#include <linux/kfifo.h>
|
|
#include <linux/time.h>
|
|
#include <linux/timer.h>
|
|
#include <media/rc-map.h>
|
|
|
|
extern int rc_core_debug;
|
|
#define IR_dprintk(level, fmt, arg...) if (rc_core_debug >= level) \
|
|
printk(KERN_DEBUG "%s: " fmt , __func__, ## arg)
|
|
|
|
enum rc_driver_type {
|
|
RC_DRIVER_SCANCODE = 0, /* Driver or hardware generates a scancode */
|
|
RC_DRIVER_IR_RAW, /* Needs a Infra-Red pulse/space decoder */
|
|
};
|
|
|
|
/**
|
|
* struct rc_dev - represents a remote control device
|
|
* @dev: driver model's view of this device
|
|
* @input_name: name of the input child device
|
|
* @input_phys: physical path to the input child device
|
|
* @input_id: id of the input child device (struct input_id)
|
|
* @driver_name: name of the hardware driver which registered this device
|
|
* @map_name: name of the default keymap
|
|
* @rc_map: current scan/key table
|
|
* @lock: used to ensure we've filled in all protocol details before
|
|
* anyone can call show_protocols or store_protocols
|
|
* @devno: unique remote control device number
|
|
* @raw: additional data for raw pulse/space devices
|
|
* @input_dev: the input child device used to communicate events to userspace
|
|
* @driver_type: specifies if protocol decoding is done in hardware or software
|
|
* @idle: used to keep track of RX state
|
|
* @allowed_protos: bitmask with the supported RC_TYPE_* protocols
|
|
* @scanmask: some hardware decoders are not capable of providing the full
|
|
* scancode to the application. As this is a hardware limit, we can't do
|
|
* anything with it. Yet, as the same keycode table can be used with other
|
|
* devices, a mask is provided to allow its usage. Drivers should generally
|
|
* leave this field in blank
|
|
* @priv: driver-specific data
|
|
* @keylock: protects the remaining members of the struct
|
|
* @keypressed: whether a key is currently pressed
|
|
* @keyup_jiffies: time (in jiffies) when the current keypress should be released
|
|
* @timer_keyup: timer for releasing a keypress
|
|
* @last_keycode: keycode of last keypress
|
|
* @last_scancode: scancode of last keypress
|
|
* @last_toggle: toggle value of last command
|
|
* @timeout: optional time after which device stops sending data
|
|
* @min_timeout: minimum timeout supported by device
|
|
* @max_timeout: maximum timeout supported by device
|
|
* @rx_resolution : resolution (in ns) of input sampler
|
|
* @tx_resolution: resolution (in ns) of output sampler
|
|
* @change_protocol: allow changing the protocol used on hardware decoders
|
|
* @open: callback to allow drivers to enable polling/irq when IR input device
|
|
* is opened.
|
|
* @close: callback to allow drivers to disable polling/irq when IR input device
|
|
* is opened.
|
|
* @s_tx_mask: set transmitter mask (for devices with multiple tx outputs)
|
|
* @s_tx_carrier: set transmit carrier frequency
|
|
* @s_tx_duty_cycle: set transmit duty cycle (0% - 100%)
|
|
* @s_rx_carrier: inform driver about carrier it is expected to handle
|
|
* @tx_ir: transmit IR
|
|
* @s_idle: enable/disable hardware idle mode, upon which,
|
|
* device doesn't interrupt host until it sees IR pulses
|
|
* @s_learning_mode: enable wide band receiver used for learning
|
|
* @s_carrier_report: enable carrier reports
|
|
*/
|
|
struct rc_dev {
|
|
struct device dev;
|
|
const char *input_name;
|
|
const char *input_phys;
|
|
struct input_id input_id;
|
|
char *driver_name;
|
|
const char *map_name;
|
|
struct rc_map rc_map;
|
|
struct mutex lock;
|
|
unsigned long devno;
|
|
struct ir_raw_event_ctrl *raw;
|
|
struct input_dev *input_dev;
|
|
enum rc_driver_type driver_type;
|
|
bool idle;
|
|
u64 allowed_protos;
|
|
u32 scanmask;
|
|
void *priv;
|
|
spinlock_t keylock;
|
|
bool keypressed;
|
|
unsigned long keyup_jiffies;
|
|
struct timer_list timer_keyup;
|
|
u32 last_keycode;
|
|
u32 last_scancode;
|
|
u8 last_toggle;
|
|
u32 timeout;
|
|
u32 min_timeout;
|
|
u32 max_timeout;
|
|
u32 rx_resolution;
|
|
u32 tx_resolution;
|
|
int (*change_protocol)(struct rc_dev *dev, u64 rc_type);
|
|
int (*open)(struct rc_dev *dev);
|
|
void (*close)(struct rc_dev *dev);
|
|
int (*s_tx_mask)(struct rc_dev *dev, u32 mask);
|
|
int (*s_tx_carrier)(struct rc_dev *dev, u32 carrier);
|
|
int (*s_tx_duty_cycle)(struct rc_dev *dev, u32 duty_cycle);
|
|
int (*s_rx_carrier_range)(struct rc_dev *dev, u32 min, u32 max);
|
|
int (*tx_ir)(struct rc_dev *dev, int *txbuf, u32 n);
|
|
void (*s_idle)(struct rc_dev *dev, bool enable);
|
|
int (*s_learning_mode)(struct rc_dev *dev, int enable);
|
|
int (*s_carrier_report) (struct rc_dev *dev, int enable);
|
|
};
|
|
|
|
#define to_rc_dev(d) container_of(d, struct rc_dev, dev)
|
|
|
|
/*
|
|
* From rc-main.c
|
|
* Those functions can be used on any type of Remote Controller. They
|
|
* basically creates an input_dev and properly reports the device as a
|
|
* Remote Controller, at sys/class/rc.
|
|
*/
|
|
|
|
struct rc_dev *rc_allocate_device(void);
|
|
void rc_free_device(struct rc_dev *dev);
|
|
int rc_register_device(struct rc_dev *dev);
|
|
void rc_unregister_device(struct rc_dev *dev);
|
|
|
|
void rc_repeat(struct rc_dev *dev);
|
|
void rc_keydown(struct rc_dev *dev, int scancode, u8 toggle);
|
|
void rc_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle);
|
|
void rc_keyup(struct rc_dev *dev);
|
|
u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode);
|
|
|
|
/*
|
|
* From rc-raw.c
|
|
* The Raw interface is specific to InfraRed. It may be a good idea to
|
|
* split it later into a separate header.
|
|
*/
|
|
|
|
enum raw_event_type {
|
|
IR_SPACE = (1 << 0),
|
|
IR_PULSE = (1 << 1),
|
|
IR_START_EVENT = (1 << 2),
|
|
IR_STOP_EVENT = (1 << 3),
|
|
};
|
|
|
|
struct ir_raw_event {
|
|
union {
|
|
u32 duration;
|
|
|
|
struct {
|
|
u32 carrier;
|
|
u8 duty_cycle;
|
|
};
|
|
};
|
|
|
|
unsigned pulse:1;
|
|
unsigned reset:1;
|
|
unsigned timeout:1;
|
|
unsigned carrier_report:1;
|
|
};
|
|
|
|
#define DEFINE_IR_RAW_EVENT(event) \
|
|
struct ir_raw_event event = { \
|
|
{ .duration = 0 } , \
|
|
.pulse = 0, \
|
|
.reset = 0, \
|
|
.timeout = 0, \
|
|
.carrier_report = 0 }
|
|
|
|
static inline void init_ir_raw_event(struct ir_raw_event *ev)
|
|
{
|
|
memset(ev, 0, sizeof(*ev));
|
|
}
|
|
|
|
#define IR_MAX_DURATION 0xFFFFFFFF /* a bit more than 4 seconds */
|
|
#define US_TO_NS(usec) ((usec) * 1000)
|
|
#define MS_TO_US(msec) ((msec) * 1000)
|
|
#define MS_TO_NS(msec) ((msec) * 1000 * 1000)
|
|
|
|
void ir_raw_event_handle(struct rc_dev *dev);
|
|
int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev);
|
|
int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type);
|
|
int ir_raw_event_store_with_filter(struct rc_dev *dev,
|
|
struct ir_raw_event *ev);
|
|
void ir_raw_event_set_idle(struct rc_dev *dev, bool idle);
|
|
|
|
static inline void ir_raw_event_reset(struct rc_dev *dev)
|
|
{
|
|
DEFINE_IR_RAW_EVENT(ev);
|
|
ev.reset = true;
|
|
|
|
ir_raw_event_store(dev, &ev);
|
|
ir_raw_event_handle(dev);
|
|
}
|
|
|
|
/* extract mask bits out of data and pack them into the result */
|
|
static inline u32 ir_extract_bits(u32 data, u32 mask)
|
|
{
|
|
u32 vbit = 1, value = 0;
|
|
|
|
do {
|
|
if (mask & 1) {
|
|
if (data & 1)
|
|
value |= vbit;
|
|
vbit <<= 1;
|
|
}
|
|
data >>= 1;
|
|
} while (mask >>= 1);
|
|
|
|
return value;
|
|
}
|
|
|
|
#endif /* _RC_CORE */
|