1
2
3
4
5
6
7
8
9
10
11
12#include <linux/device.h>
13#include <linux/module.h>
14#include <linux/sched.h>
15#include <linux/slab.h>
16#include <media/rc-core.h>
17
18#define DRIVER_NAME "rc-loopback"
19#define RXMASK_NARROWBAND 0x1
20#define RXMASK_WIDEBAND 0x2
21
22struct loopback_dev {
23 struct rc_dev *dev;
24 u32 txmask;
25 u32 txcarrier;
26 u32 txduty;
27 bool idle;
28 bool wideband;
29 bool carrierreport;
30 u32 rxcarriermin;
31 u32 rxcarriermax;
32};
33
34static struct loopback_dev loopdev;
35
36static int loop_set_tx_mask(struct rc_dev *dev, u32 mask)
37{
38 struct loopback_dev *lodev = dev->priv;
39
40 if ((mask & (RXMASK_NARROWBAND | RXMASK_WIDEBAND)) != mask) {
41 dev_dbg(&dev->dev, "invalid tx mask: %u\n", mask);
42 return 2;
43 }
44
45 dev_dbg(&dev->dev, "setting tx mask: %u\n", mask);
46 lodev->txmask = mask;
47 return 0;
48}
49
50static int loop_set_tx_carrier(struct rc_dev *dev, u32 carrier)
51{
52 struct loopback_dev *lodev = dev->priv;
53
54 dev_dbg(&dev->dev, "setting tx carrier: %u\n", carrier);
55 lodev->txcarrier = carrier;
56 return 0;
57}
58
59static int loop_set_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
60{
61 struct loopback_dev *lodev = dev->priv;
62
63 if (duty_cycle < 1 || duty_cycle > 99) {
64 dev_dbg(&dev->dev, "invalid duty cycle: %u\n", duty_cycle);
65 return -EINVAL;
66 }
67
68 dev_dbg(&dev->dev, "setting duty cycle: %u\n", duty_cycle);
69 lodev->txduty = duty_cycle;
70 return 0;
71}
72
73static int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max)
74{
75 struct loopback_dev *lodev = dev->priv;
76
77 if (min < 1 || min > max) {
78 dev_dbg(&dev->dev, "invalid rx carrier range %u to %u\n", min, max);
79 return -EINVAL;
80 }
81
82 dev_dbg(&dev->dev, "setting rx carrier range %u to %u\n", min, max);
83 lodev->rxcarriermin = min;
84 lodev->rxcarriermax = max;
85 return 0;
86}
87
88static int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
89{
90 struct loopback_dev *lodev = dev->priv;
91 u32 rxmask;
92 unsigned i;
93 struct ir_raw_event rawir = {};
94
95 if (lodev->txcarrier < lodev->rxcarriermin ||
96 lodev->txcarrier > lodev->rxcarriermax) {
97 dev_dbg(&dev->dev, "ignoring tx, carrier out of range\n");
98 goto out;
99 }
100
101 if (lodev->wideband)
102 rxmask = RXMASK_WIDEBAND;
103 else
104 rxmask = RXMASK_NARROWBAND;
105
106 if (!(rxmask & lodev->txmask)) {
107 dev_dbg(&dev->dev, "ignoring tx, rx mask mismatch\n");
108 goto out;
109 }
110
111 for (i = 0; i < count; i++) {
112 rawir.pulse = i % 2 ? false : true;
113 rawir.duration = txbuf[i];
114
115 ir_raw_event_store_with_filter(dev, &rawir);
116 }
117
118 if (lodev->carrierreport) {
119 rawir.pulse = false;
120 rawir.carrier_report = true;
121 rawir.carrier = lodev->txcarrier;
122
123 ir_raw_event_store(dev, &rawir);
124 }
125
126
127 rawir.pulse = false;
128 rawir.duration = dev->timeout;
129 ir_raw_event_store_with_filter(dev, &rawir);
130
131 ir_raw_event_handle(dev);
132
133out:
134 return count;
135}
136
137static void loop_set_idle(struct rc_dev *dev, bool enable)
138{
139 struct loopback_dev *lodev = dev->priv;
140
141 if (lodev->idle != enable) {
142 dev_dbg(&dev->dev, "%sing idle mode\n", enable ? "enter" : "exit");
143 lodev->idle = enable;
144 }
145}
146
147static int loop_set_wideband_receiver(struct rc_dev *dev, int enable)
148{
149 struct loopback_dev *lodev = dev->priv;
150
151 if (lodev->wideband != enable) {
152 dev_dbg(&dev->dev, "using %sband receiver\n", enable ? "wide" : "narrow");
153 lodev->wideband = !!enable;
154 }
155
156 return 0;
157}
158
159static int loop_set_carrier_report(struct rc_dev *dev, int enable)
160{
161 struct loopback_dev *lodev = dev->priv;
162
163 if (lodev->carrierreport != enable) {
164 dev_dbg(&dev->dev, "%sabling carrier reports\n", enable ? "en" : "dis");
165 lodev->carrierreport = !!enable;
166 }
167
168 return 0;
169}
170
171static int loop_set_wakeup_filter(struct rc_dev *dev,
172 struct rc_scancode_filter *sc)
173{
174 static const unsigned int max = 512;
175 struct ir_raw_event *raw;
176 int ret;
177 int i;
178
179
180 if (!sc->mask)
181 return 0;
182
183
184 raw = kmalloc_array(max, sizeof(*raw), GFP_KERNEL);
185 if (!raw)
186 return -ENOMEM;
187
188 ret = ir_raw_encode_scancode(dev->wakeup_protocol, sc->data, raw, max);
189
190 if (ret == -ENOBUFS)
191 ret = max;
192 if (ret >= 0) {
193
194 for (i = 0; i < ret; ++i)
195 ir_raw_event_store(dev, &raw[i]);
196 ir_raw_event_handle(dev);
197
198 ret = 0;
199 }
200
201 kfree(raw);
202
203 return ret;
204}
205
206static int __init loop_init(void)
207{
208 struct rc_dev *rc;
209 int ret;
210
211 rc = rc_allocate_device(RC_DRIVER_IR_RAW);
212 if (!rc)
213 return -ENOMEM;
214
215 rc->device_name = "rc-core loopback device";
216 rc->input_phys = "rc-core/virtual";
217 rc->input_id.bustype = BUS_VIRTUAL;
218 rc->input_id.version = 1;
219 rc->driver_name = DRIVER_NAME;
220 rc->map_name = RC_MAP_EMPTY;
221 rc->priv = &loopdev;
222 rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
223 rc->allowed_wakeup_protocols = RC_PROTO_BIT_ALL_IR_ENCODER;
224 rc->encode_wakeup = true;
225 rc->timeout = IR_DEFAULT_TIMEOUT;
226 rc->min_timeout = 1;
227 rc->max_timeout = IR_MAX_TIMEOUT;
228 rc->rx_resolution = 1;
229 rc->tx_resolution = 1;
230 rc->s_tx_mask = loop_set_tx_mask;
231 rc->s_tx_carrier = loop_set_tx_carrier;
232 rc->s_tx_duty_cycle = loop_set_tx_duty_cycle;
233 rc->s_rx_carrier_range = loop_set_rx_carrier_range;
234 rc->tx_ir = loop_tx_ir;
235 rc->s_idle = loop_set_idle;
236 rc->s_wideband_receiver = loop_set_wideband_receiver;
237 rc->s_carrier_report = loop_set_carrier_report;
238 rc->s_wakeup_filter = loop_set_wakeup_filter;
239
240 loopdev.txmask = RXMASK_NARROWBAND;
241 loopdev.txcarrier = 36000;
242 loopdev.txduty = 50;
243 loopdev.rxcarriermin = 1;
244 loopdev.rxcarriermax = ~0;
245 loopdev.idle = true;
246 loopdev.wideband = false;
247 loopdev.carrierreport = false;
248
249 ret = rc_register_device(rc);
250 if (ret < 0) {
251 dev_err(&rc->dev, "rc_dev registration failed\n");
252 rc_free_device(rc);
253 return ret;
254 }
255
256 loopdev.dev = rc;
257 return 0;
258}
259
260static void __exit loop_exit(void)
261{
262 rc_unregister_device(loopdev.dev);
263}
264
265module_init(loop_init);
266module_exit(loop_exit);
267
268MODULE_DESCRIPTION("Loopback device for rc-core debugging");
269MODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
270MODULE_LICENSE("GPL");
271