.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * Driver for the remote control of SAA7146 based AV7110 cards |
---|
3 | 4 | * |
---|
4 | 5 | * Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de> |
---|
5 | 6 | * Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.de> |
---|
6 | | - * |
---|
7 | | - * This program is free software; you can redistribute it and/or |
---|
8 | | - * modify it under the terms of the GNU General Public License |
---|
9 | | - * as published by the Free Software Foundation; either version 2 |
---|
10 | | - * of the License, or (at your option) any later version. |
---|
11 | | - * |
---|
12 | | - * This program is distributed in the hope that it will be useful, |
---|
13 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
14 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
15 | | - * GNU General Public License for more details. |
---|
16 | | - * To obtain the license, point your browser to |
---|
17 | | - * http://www.gnu.org/copyleft/gpl.html |
---|
18 | | - * |
---|
| 7 | + * Copyright (C) 2019 Sean Young <sean@mess.org> |
---|
19 | 8 | */ |
---|
20 | 9 | |
---|
21 | | - |
---|
22 | | -#include <linux/types.h> |
---|
23 | | -#include <linux/init.h> |
---|
24 | | -#include <linux/module.h> |
---|
25 | | -#include <linux/proc_fs.h> |
---|
26 | 10 | #include <linux/kernel.h> |
---|
27 | | -#include <linux/bitops.h> |
---|
| 11 | +#include <media/rc-core.h> |
---|
28 | 12 | |
---|
29 | 13 | #include "av7110.h" |
---|
30 | 14 | #include "av7110_hw.h" |
---|
31 | | - |
---|
32 | | - |
---|
33 | | -#define AV_CNT 4 |
---|
34 | 15 | |
---|
35 | 16 | #define IR_RC5 0 |
---|
36 | 17 | #define IR_RCMM 1 |
---|
37 | 18 | #define IR_RC5_EXT 2 /* internal only */ |
---|
38 | 19 | |
---|
39 | | -#define IR_ALL 0xffffffff |
---|
40 | | - |
---|
41 | | -#define UP_TIMEOUT (HZ*7/25) |
---|
42 | | - |
---|
43 | | - |
---|
44 | | -/* Note: enable ir debugging by or'ing debug with 16 */ |
---|
45 | | - |
---|
46 | | -static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; |
---|
47 | | -module_param_array(ir_protocol, int, NULL, 0644); |
---|
48 | | -MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); |
---|
49 | | - |
---|
50 | | -static int ir_inversion[AV_CNT]; |
---|
51 | | -module_param_array(ir_inversion, int, NULL, 0644); |
---|
52 | | -MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); |
---|
53 | | - |
---|
54 | | -static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; |
---|
55 | | -module_param_array(ir_device_mask, uint, NULL, 0644); |
---|
56 | | -MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); |
---|
57 | | - |
---|
58 | | - |
---|
59 | | -static int av_cnt; |
---|
60 | | -static struct av7110 *av_list[AV_CNT]; |
---|
61 | | - |
---|
62 | | -static u16 default_key_map [256] = { |
---|
63 | | - KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, |
---|
64 | | - KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, |
---|
65 | | - KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
66 | | - KEY_CHANNELUP, KEY_CHANNELDOWN, 0, 0, 0, 0, 0, 0, |
---|
67 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
68 | | - 0, 0, 0, 0, KEY_TEXT, 0, 0, KEY_TV, 0, 0, 0, 0, 0, KEY_SETUP, 0, 0, |
---|
69 | | - 0, 0, 0, KEY_SUBTITLE, 0, 0, KEY_LANGUAGE, 0, |
---|
70 | | - KEY_RADIO, 0, 0, 0, 0, KEY_EXIT, 0, 0, |
---|
71 | | - KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_OK, 0, 0, 0, |
---|
72 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RED, KEY_GREEN, KEY_YELLOW, |
---|
73 | | - KEY_BLUE, 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_LIST, 0, 0, 0, 0, 0, 0, |
---|
74 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
75 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
76 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
77 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
78 | | - 0, 0, 0, 0, KEY_UP, KEY_UP, KEY_DOWN, KEY_DOWN, |
---|
79 | | - 0, 0, 0, 0, KEY_EPG, 0, 0, 0, |
---|
80 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
81 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
---|
82 | | - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_VCR |
---|
83 | | -}; |
---|
84 | | - |
---|
85 | | - |
---|
86 | | -/* key-up timer */ |
---|
87 | | -static void av7110_emit_keyup(struct timer_list *t) |
---|
| 20 | +/* interrupt handler */ |
---|
| 21 | +void av7110_ir_handler(struct av7110 *av7110, u32 ircom) |
---|
88 | 22 | { |
---|
89 | | - struct infrared *ir = from_timer(ir, t, keyup_timer); |
---|
| 23 | + struct rc_dev *rcdev = av7110->ir.rcdev; |
---|
| 24 | + enum rc_proto proto; |
---|
| 25 | + u32 command, addr, scancode; |
---|
| 26 | + u32 toggle; |
---|
90 | 27 | |
---|
91 | | - if (!ir || !ir->keypressed) |
---|
92 | | - return; |
---|
| 28 | + dprintk(4, "ir command = %08x\n", ircom); |
---|
93 | 29 | |
---|
94 | | - input_report_key(ir->input_dev, ir->last_key, 0); |
---|
95 | | - input_sync(ir->input_dev); |
---|
96 | | - ir->keypressed = false; |
---|
97 | | -} |
---|
98 | | - |
---|
99 | | - |
---|
100 | | -/* tasklet */ |
---|
101 | | -static void av7110_emit_key(unsigned long parm) |
---|
102 | | -{ |
---|
103 | | - struct infrared *ir = (struct infrared *) parm; |
---|
104 | | - u32 ircom = ir->ir_command; |
---|
105 | | - u8 data; |
---|
106 | | - u8 addr; |
---|
107 | | - u16 toggle; |
---|
108 | | - u16 keycode; |
---|
109 | | - |
---|
110 | | - /* extract device address and data */ |
---|
111 | | - switch (ir->protocol) { |
---|
112 | | - case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ |
---|
113 | | - data = ircom & 0x3f; |
---|
114 | | - addr = (ircom >> 6) & 0x1f; |
---|
115 | | - toggle = ircom & 0x0800; |
---|
116 | | - break; |
---|
117 | | - |
---|
118 | | - case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ |
---|
119 | | - data = ircom & 0xff; |
---|
120 | | - addr = (ircom >> 8) & 0x1f; |
---|
121 | | - toggle = ircom & 0x8000; |
---|
122 | | - break; |
---|
123 | | - |
---|
124 | | - case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ |
---|
125 | | - data = ircom & 0x3f; |
---|
126 | | - addr = (ircom >> 6) & 0x1f; |
---|
127 | | - /* invert 7th data bit for backward compatibility with RC5 keymaps */ |
---|
128 | | - if (!(ircom & 0x1000)) |
---|
129 | | - data |= 0x40; |
---|
130 | | - toggle = ircom & 0x0800; |
---|
131 | | - break; |
---|
132 | | - |
---|
133 | | - default: |
---|
134 | | - printk("%s invalid protocol %x\n", __func__, ir->protocol); |
---|
135 | | - return; |
---|
136 | | - } |
---|
137 | | - |
---|
138 | | - input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); |
---|
139 | | - input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); |
---|
140 | | - |
---|
141 | | - keycode = ir->key_map[data]; |
---|
142 | | - |
---|
143 | | - dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", |
---|
144 | | - __func__, ircom, addr, data, keycode); |
---|
145 | | - |
---|
146 | | - /* check device address */ |
---|
147 | | - if (!(ir->device_mask & (1 << addr))) |
---|
148 | | - return; |
---|
149 | | - |
---|
150 | | - if (!keycode) { |
---|
151 | | - printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", |
---|
152 | | - __func__, ircom, addr, data); |
---|
153 | | - return; |
---|
154 | | - } |
---|
155 | | - |
---|
156 | | - if (ir->keypressed && |
---|
157 | | - (ir->last_key != keycode || toggle != ir->last_toggle)) |
---|
158 | | - input_event(ir->input_dev, EV_KEY, ir->last_key, 0); |
---|
159 | | - |
---|
160 | | - input_event(ir->input_dev, EV_KEY, keycode, 1); |
---|
161 | | - input_sync(ir->input_dev); |
---|
162 | | - |
---|
163 | | - ir->keypressed = true; |
---|
164 | | - ir->last_key = keycode; |
---|
165 | | - ir->last_toggle = toggle; |
---|
166 | | - |
---|
167 | | - mod_timer(&ir->keyup_timer, jiffies + UP_TIMEOUT); |
---|
168 | | -} |
---|
169 | | - |
---|
170 | | - |
---|
171 | | -/* register with input layer */ |
---|
172 | | -static void input_register_keys(struct infrared *ir) |
---|
173 | | -{ |
---|
174 | | - int i; |
---|
175 | | - |
---|
176 | | - set_bit(EV_KEY, ir->input_dev->evbit); |
---|
177 | | - set_bit(EV_REP, ir->input_dev->evbit); |
---|
178 | | - set_bit(EV_MSC, ir->input_dev->evbit); |
---|
179 | | - |
---|
180 | | - set_bit(MSC_RAW, ir->input_dev->mscbit); |
---|
181 | | - set_bit(MSC_SCAN, ir->input_dev->mscbit); |
---|
182 | | - |
---|
183 | | - memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); |
---|
184 | | - |
---|
185 | | - for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { |
---|
186 | | - if (ir->key_map[i] > KEY_MAX) |
---|
187 | | - ir->key_map[i] = 0; |
---|
188 | | - else if (ir->key_map[i] > KEY_RESERVED) |
---|
189 | | - set_bit(ir->key_map[i], ir->input_dev->keybit); |
---|
190 | | - } |
---|
191 | | - |
---|
192 | | - ir->input_dev->keycode = ir->key_map; |
---|
193 | | - ir->input_dev->keycodesize = sizeof(ir->key_map[0]); |
---|
194 | | - ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); |
---|
195 | | -} |
---|
196 | | - |
---|
197 | | -/* check for configuration changes */ |
---|
198 | | -int av7110_check_ir_config(struct av7110 *av7110, int force) |
---|
199 | | -{ |
---|
200 | | - int i; |
---|
201 | | - int modified = force; |
---|
202 | | - int ret = -ENODEV; |
---|
203 | | - |
---|
204 | | - for (i = 0; i < av_cnt; i++) |
---|
205 | | - if (av7110 == av_list[i]) |
---|
| 30 | + if (rcdev) { |
---|
| 31 | + switch (av7110->ir.ir_config) { |
---|
| 32 | + case IR_RC5: /* RC5: 5 bits device address, 6 bits command */ |
---|
| 33 | + command = ircom & 0x3f; |
---|
| 34 | + addr = (ircom >> 6) & 0x1f; |
---|
| 35 | + scancode = RC_SCANCODE_RC5(addr, command); |
---|
| 36 | + toggle = ircom & 0x0800; |
---|
| 37 | + proto = RC_PROTO_RC5; |
---|
206 | 38 | break; |
---|
207 | 39 | |
---|
208 | | - if (i < av_cnt && av7110) { |
---|
209 | | - if ((av7110->ir.protocol & 1) != ir_protocol[i] || |
---|
210 | | - av7110->ir.inversion != ir_inversion[i]) |
---|
211 | | - modified = true; |
---|
| 40 | + case IR_RCMM: /* RCMM: 32 bits scancode */ |
---|
| 41 | + scancode = ircom & ~0x8000; |
---|
| 42 | + toggle = ircom & 0x8000; |
---|
| 43 | + proto = RC_PROTO_RCMM32; |
---|
| 44 | + break; |
---|
212 | 45 | |
---|
213 | | - if (modified) { |
---|
214 | | - /* protocol */ |
---|
215 | | - if (ir_protocol[i]) { |
---|
216 | | - ir_protocol[i] = 1; |
---|
217 | | - av7110->ir.protocol = IR_RCMM; |
---|
218 | | - av7110->ir.ir_config = 0x0001; |
---|
219 | | - } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { |
---|
220 | | - av7110->ir.protocol = IR_RC5_EXT; |
---|
221 | | - av7110->ir.ir_config = 0x0002; |
---|
222 | | - } else { |
---|
223 | | - av7110->ir.protocol = IR_RC5; |
---|
224 | | - av7110->ir.ir_config = 0x0000; |
---|
225 | | - } |
---|
226 | | - /* inversion */ |
---|
227 | | - if (ir_inversion[i]) { |
---|
228 | | - ir_inversion[i] = 1; |
---|
229 | | - av7110->ir.ir_config |= 0x8000; |
---|
230 | | - } |
---|
231 | | - av7110->ir.inversion = ir_inversion[i]; |
---|
232 | | - /* update ARM */ |
---|
233 | | - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, |
---|
234 | | - av7110->ir.ir_config); |
---|
235 | | - } else |
---|
236 | | - ret = 0; |
---|
| 46 | + case IR_RC5_EXT: |
---|
| 47 | + /* |
---|
| 48 | + * extended RC5: 5 bits device address, 7 bits command |
---|
| 49 | + * |
---|
| 50 | + * Extended RC5 uses only one start bit. The second |
---|
| 51 | + * start bit is re-assigned bit 6 of the command bit. |
---|
| 52 | + */ |
---|
| 53 | + command = ircom & 0x3f; |
---|
| 54 | + addr = (ircom >> 6) & 0x1f; |
---|
| 55 | + if (!(ircom & 0x1000)) |
---|
| 56 | + command |= 0x40; |
---|
| 57 | + scancode = RC_SCANCODE_RC5(addr, command); |
---|
| 58 | + toggle = ircom & 0x0800; |
---|
| 59 | + proto = RC_PROTO_RC5; |
---|
| 60 | + break; |
---|
| 61 | + default: |
---|
| 62 | + dprintk(2, "unknown ir config %d\n", |
---|
| 63 | + av7110->ir.ir_config); |
---|
| 64 | + return; |
---|
| 65 | + } |
---|
237 | 66 | |
---|
238 | | - /* address */ |
---|
239 | | - if (av7110->ir.device_mask != ir_device_mask[i]) |
---|
240 | | - av7110->ir.device_mask = ir_device_mask[i]; |
---|
| 67 | + rc_keydown(rcdev, proto, scancode, toggle != 0); |
---|
| 68 | + } |
---|
| 69 | +} |
---|
| 70 | + |
---|
| 71 | +int av7110_set_ir_config(struct av7110 *av7110) |
---|
| 72 | +{ |
---|
| 73 | + dprintk(4, "ir config = %08x\n", av7110->ir.ir_config); |
---|
| 74 | + |
---|
| 75 | + return av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, |
---|
| 76 | + av7110->ir.ir_config); |
---|
| 77 | +} |
---|
| 78 | + |
---|
| 79 | +static int change_protocol(struct rc_dev *rcdev, u64 *rc_type) |
---|
| 80 | +{ |
---|
| 81 | + struct av7110 *av7110 = rcdev->priv; |
---|
| 82 | + u32 ir_config; |
---|
| 83 | + |
---|
| 84 | + if (*rc_type & RC_PROTO_BIT_RCMM32) { |
---|
| 85 | + ir_config = IR_RCMM; |
---|
| 86 | + *rc_type = RC_PROTO_BIT_RCMM32; |
---|
| 87 | + } else if (*rc_type & RC_PROTO_BIT_RC5) { |
---|
| 88 | + if (FW_VERSION(av7110->arm_app) >= 0x2620) |
---|
| 89 | + ir_config = IR_RC5_EXT; |
---|
| 90 | + else |
---|
| 91 | + ir_config = IR_RC5; |
---|
| 92 | + *rc_type = RC_PROTO_BIT_RC5; |
---|
| 93 | + } else { |
---|
| 94 | + return -EINVAL; |
---|
| 95 | + } |
---|
| 96 | + |
---|
| 97 | + if (ir_config == av7110->ir.ir_config) |
---|
| 98 | + return 0; |
---|
| 99 | + |
---|
| 100 | + av7110->ir.ir_config = ir_config; |
---|
| 101 | + |
---|
| 102 | + return av7110_set_ir_config(av7110); |
---|
| 103 | +} |
---|
| 104 | + |
---|
| 105 | +int av7110_ir_init(struct av7110 *av7110) |
---|
| 106 | +{ |
---|
| 107 | + struct rc_dev *rcdev; |
---|
| 108 | + struct pci_dev *pci; |
---|
| 109 | + int ret; |
---|
| 110 | + |
---|
| 111 | + rcdev = rc_allocate_device(RC_DRIVER_SCANCODE); |
---|
| 112 | + if (!rcdev) |
---|
| 113 | + return -ENOMEM; |
---|
| 114 | + |
---|
| 115 | + pci = av7110->dev->pci; |
---|
| 116 | + |
---|
| 117 | + snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), |
---|
| 118 | + "pci-%s/ir0", pci_name(pci)); |
---|
| 119 | + |
---|
| 120 | + rcdev->device_name = av7110->card_name; |
---|
| 121 | + rcdev->driver_name = KBUILD_MODNAME; |
---|
| 122 | + rcdev->input_phys = av7110->ir.input_phys; |
---|
| 123 | + rcdev->input_id.bustype = BUS_PCI; |
---|
| 124 | + rcdev->input_id.version = 2; |
---|
| 125 | + if (pci->subsystem_vendor) { |
---|
| 126 | + rcdev->input_id.vendor = pci->subsystem_vendor; |
---|
| 127 | + rcdev->input_id.product = pci->subsystem_device; |
---|
| 128 | + } else { |
---|
| 129 | + rcdev->input_id.vendor = pci->vendor; |
---|
| 130 | + rcdev->input_id.product = pci->device; |
---|
| 131 | + } |
---|
| 132 | + |
---|
| 133 | + rcdev->dev.parent = &pci->dev; |
---|
| 134 | + rcdev->allowed_protocols = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RCMM32; |
---|
| 135 | + rcdev->change_protocol = change_protocol; |
---|
| 136 | + rcdev->map_name = RC_MAP_HAUPPAUGE; |
---|
| 137 | + rcdev->priv = av7110; |
---|
| 138 | + |
---|
| 139 | + av7110->ir.rcdev = rcdev; |
---|
| 140 | + av7110->ir.ir_config = IR_RC5; |
---|
| 141 | + av7110_set_ir_config(av7110); |
---|
| 142 | + |
---|
| 143 | + ret = rc_register_device(rcdev); |
---|
| 144 | + if (ret) { |
---|
| 145 | + av7110->ir.rcdev = NULL; |
---|
| 146 | + rc_free_device(rcdev); |
---|
241 | 147 | } |
---|
242 | 148 | |
---|
243 | 149 | return ret; |
---|
244 | 150 | } |
---|
245 | 151 | |
---|
246 | | - |
---|
247 | | -/* /proc/av7110_ir interface */ |
---|
248 | | -static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer, |
---|
249 | | - size_t count, loff_t *pos) |
---|
250 | | -{ |
---|
251 | | - char *page; |
---|
252 | | - u32 ir_config; |
---|
253 | | - int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; |
---|
254 | | - int i; |
---|
255 | | - |
---|
256 | | - if (count < size) |
---|
257 | | - return -EINVAL; |
---|
258 | | - |
---|
259 | | - page = vmalloc(size); |
---|
260 | | - if (!page) |
---|
261 | | - return -ENOMEM; |
---|
262 | | - |
---|
263 | | - if (copy_from_user(page, buffer, size)) { |
---|
264 | | - vfree(page); |
---|
265 | | - return -EFAULT; |
---|
266 | | - } |
---|
267 | | - |
---|
268 | | - memcpy(&ir_config, page, sizeof ir_config); |
---|
269 | | - |
---|
270 | | - for (i = 0; i < av_cnt; i++) { |
---|
271 | | - /* keymap */ |
---|
272 | | - memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, |
---|
273 | | - sizeof(av_list[i]->ir.key_map)); |
---|
274 | | - /* protocol, inversion, address */ |
---|
275 | | - ir_protocol[i] = ir_config & 0x0001; |
---|
276 | | - ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; |
---|
277 | | - if (ir_config & 0x4000) |
---|
278 | | - ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); |
---|
279 | | - else |
---|
280 | | - ir_device_mask[i] = IR_ALL; |
---|
281 | | - /* update configuration */ |
---|
282 | | - av7110_check_ir_config(av_list[i], false); |
---|
283 | | - input_register_keys(&av_list[i]->ir); |
---|
284 | | - } |
---|
285 | | - vfree(page); |
---|
286 | | - return count; |
---|
287 | | -} |
---|
288 | | - |
---|
289 | | -static const struct file_operations av7110_ir_proc_fops = { |
---|
290 | | - .owner = THIS_MODULE, |
---|
291 | | - .write = av7110_ir_proc_write, |
---|
292 | | - .llseek = noop_llseek, |
---|
293 | | -}; |
---|
294 | | - |
---|
295 | | -/* interrupt handler */ |
---|
296 | | -static void ir_handler(struct av7110 *av7110, u32 ircom) |
---|
297 | | -{ |
---|
298 | | - dprintk(4, "ir command = %08x\n", ircom); |
---|
299 | | - av7110->ir.ir_command = ircom; |
---|
300 | | - tasklet_schedule(&av7110->ir.ir_tasklet); |
---|
301 | | -} |
---|
302 | | - |
---|
303 | | - |
---|
304 | | -int av7110_ir_init(struct av7110 *av7110) |
---|
305 | | -{ |
---|
306 | | - struct input_dev *input_dev; |
---|
307 | | - static struct proc_dir_entry *e; |
---|
308 | | - int err; |
---|
309 | | - |
---|
310 | | - if (av_cnt >= ARRAY_SIZE(av_list)) |
---|
311 | | - return -ENOSPC; |
---|
312 | | - |
---|
313 | | - av_list[av_cnt++] = av7110; |
---|
314 | | - av7110_check_ir_config(av7110, true); |
---|
315 | | - |
---|
316 | | - timer_setup(&av7110->ir.keyup_timer, av7110_emit_keyup, 0); |
---|
317 | | - |
---|
318 | | - input_dev = input_allocate_device(); |
---|
319 | | - if (!input_dev) |
---|
320 | | - return -ENOMEM; |
---|
321 | | - |
---|
322 | | - av7110->ir.input_dev = input_dev; |
---|
323 | | - snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), |
---|
324 | | - "pci-%s/ir0", pci_name(av7110->dev->pci)); |
---|
325 | | - |
---|
326 | | - input_dev->name = "DVB on-card IR receiver"; |
---|
327 | | - |
---|
328 | | - input_dev->phys = av7110->ir.input_phys; |
---|
329 | | - input_dev->id.bustype = BUS_PCI; |
---|
330 | | - input_dev->id.version = 2; |
---|
331 | | - if (av7110->dev->pci->subsystem_vendor) { |
---|
332 | | - input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; |
---|
333 | | - input_dev->id.product = av7110->dev->pci->subsystem_device; |
---|
334 | | - } else { |
---|
335 | | - input_dev->id.vendor = av7110->dev->pci->vendor; |
---|
336 | | - input_dev->id.product = av7110->dev->pci->device; |
---|
337 | | - } |
---|
338 | | - input_dev->dev.parent = &av7110->dev->pci->dev; |
---|
339 | | - /* initial keymap */ |
---|
340 | | - memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); |
---|
341 | | - input_register_keys(&av7110->ir); |
---|
342 | | - err = input_register_device(input_dev); |
---|
343 | | - if (err) { |
---|
344 | | - input_free_device(input_dev); |
---|
345 | | - return err; |
---|
346 | | - } |
---|
347 | | - |
---|
348 | | - /* |
---|
349 | | - * Input core's default autorepeat is 33 cps with 250 msec |
---|
350 | | - * delay, let's adjust to numbers more suitable for remote |
---|
351 | | - * control. |
---|
352 | | - */ |
---|
353 | | - input_enable_softrepeat(input_dev, 250, 125); |
---|
354 | | - |
---|
355 | | - if (av_cnt == 1) { |
---|
356 | | - e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops); |
---|
357 | | - if (e) |
---|
358 | | - proc_set_size(e, 4 + 256 * sizeof(u16)); |
---|
359 | | - } |
---|
360 | | - |
---|
361 | | - tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); |
---|
362 | | - av7110->ir.ir_handler = ir_handler; |
---|
363 | | - |
---|
364 | | - return 0; |
---|
365 | | -} |
---|
366 | | - |
---|
367 | | - |
---|
368 | 152 | void av7110_ir_exit(struct av7110 *av7110) |
---|
369 | 153 | { |
---|
370 | | - int i; |
---|
371 | | - |
---|
372 | | - if (av_cnt == 0) |
---|
373 | | - return; |
---|
374 | | - |
---|
375 | | - del_timer_sync(&av7110->ir.keyup_timer); |
---|
376 | | - av7110->ir.ir_handler = NULL; |
---|
377 | | - tasklet_kill(&av7110->ir.ir_tasklet); |
---|
378 | | - |
---|
379 | | - for (i = 0; i < av_cnt; i++) |
---|
380 | | - if (av_list[i] == av7110) { |
---|
381 | | - av_list[i] = av_list[av_cnt-1]; |
---|
382 | | - av_list[av_cnt-1] = NULL; |
---|
383 | | - break; |
---|
384 | | - } |
---|
385 | | - |
---|
386 | | - if (av_cnt == 1) |
---|
387 | | - remove_proc_entry("av7110_ir", NULL); |
---|
388 | | - |
---|
389 | | - input_unregister_device(av7110->ir.input_dev); |
---|
390 | | - |
---|
391 | | - av_cnt--; |
---|
| 154 | + rc_unregister_device(av7110->ir.rcdev); |
---|
392 | 155 | } |
---|
393 | 156 | |
---|
394 | 157 | //MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>"); |
---|