1801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman/*
2801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * Loopback driver for rc-core,
3801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman *
4801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * Copyright (c) 2010 David Härdeman <david@hardeman.nu>
5801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman *
6801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * This driver receives TX data and passes it back as RX data,
7801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * which is useful for (scripted) debugging of rc-core without
8801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * having to use actual hardware.
9801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman *
10801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * This program is free software; you can redistribute it and/or modify
11801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * it under the terms of the GNU General Public License as published by
12801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * the Free Software Foundation; either version 2 of the License, or
13801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * (at your option) any later version.
14801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman *
15801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * This program is distributed in the hope that it will be useful,
16801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * but WITHOUT ANY WARRANTY; without even the implied warranty of
17801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * GNU General Public License for more details.
19801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman *
20801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * You should have received a copy of the GNU General Public License
21801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * along with this program; if not, write to the Free Software
22801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman *
24801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman */
25801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
26801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#include <linux/device.h>
27801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#include <linux/module.h>
28801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#include <linux/sched.h>
29801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#include <media/rc-core.h>
30801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
31801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#define DRIVER_NAME	"rc-loopback"
32801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#define dprintk(x...)	if (debug) printk(KERN_INFO DRIVER_NAME ": " x)
33801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#define RXMASK_REGULAR	0x1
34801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman#define RXMASK_LEARNING	0x2
35801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
36801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic bool debug;
37801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
38801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstruct loopback_dev {
39801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct rc_dev *dev;
40801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	u32 txmask;
41801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	u32 txcarrier;
42801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	u32 txduty;
43801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	bool idle;
44801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	bool learning;
45801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	bool carrierreport;
46801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	u32 rxcarriermin;
47801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	u32 rxcarriermax;
48801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman};
49801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
50801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic struct loopback_dev loopdev;
51801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
52801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic int loop_set_tx_mask(struct rc_dev *dev, u32 mask)
53801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
54801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
55801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
56801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if ((mask & (RXMASK_REGULAR | RXMASK_LEARNING)) != mask) {
57801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("invalid tx mask: %u\n", mask);
58801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		return -EINVAL;
59801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
60801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
61801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	dprintk("setting tx mask: %u\n", mask);
62801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	lodev->txmask = mask;
63801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	return 0;
64801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
65801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
66801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic int loop_set_tx_carrier(struct rc_dev *dev, u32 carrier)
67801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
68801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
69801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
70801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	dprintk("setting tx carrier: %u\n", carrier);
71801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	lodev->txcarrier = carrier;
72801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	return 0;
73801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
74801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
75801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic int loop_set_tx_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
76801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
77801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
78801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
79801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (duty_cycle < 1 || duty_cycle > 99) {
80801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("invalid duty cycle: %u\n", duty_cycle);
81801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		return -EINVAL;
82801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
83801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
84801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	dprintk("setting duty cycle: %u\n", duty_cycle);
85801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	lodev->txduty = duty_cycle;
86801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	return 0;
87801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
88801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
89801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic int loop_set_rx_carrier_range(struct rc_dev *dev, u32 min, u32 max)
90801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
91801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
92801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
93801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (min < 1 || min > max) {
94801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("invalid rx carrier range %u to %u\n", min, max);
95801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		return -EINVAL;
96801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
97801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
98801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	dprintk("setting rx carrier range %u to %u\n", min, max);
99801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	lodev->rxcarriermin = min;
100801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	lodev->rxcarriermax = max;
101801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	return 0;
102801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
103801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
1045588dc2b025fd8b2188142b8d59efe562bd57d80David Härdemanstatic int loop_tx_ir(struct rc_dev *dev, unsigned *txbuf, unsigned count)
105801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
106801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
107801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	u32 rxmask;
108801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	unsigned total_duration = 0;
109801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	unsigned i;
110801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	DEFINE_IR_RAW_EVENT(rawir);
111801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
112801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	for (i = 0; i < count; i++)
113801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		total_duration += abs(txbuf[i]);
114801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
115801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (total_duration == 0) {
116801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("invalid tx data, total duration zero\n");
117801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		return -EINVAL;
118801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
119801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
120801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (lodev->txcarrier < lodev->rxcarriermin ||
121801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	    lodev->txcarrier > lodev->rxcarriermax) {
122801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("ignoring tx, carrier out of range\n");
123801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		goto out;
124801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
125801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
126801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (lodev->learning)
127801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		rxmask = RXMASK_LEARNING;
128801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	else
129801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		rxmask = RXMASK_REGULAR;
130801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
131801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (!(rxmask & lodev->txmask)) {
132801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("ignoring tx, rx mask mismatch\n");
133801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		goto out;
134801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
135801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
136801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	for (i = 0; i < count; i++) {
137801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		rawir.pulse = i % 2 ? false : true;
1385588dc2b025fd8b2188142b8d59efe562bd57d80David Härdeman		rawir.duration = txbuf[i] * 1000;
139801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		if (rawir.duration)
140801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman			ir_raw_event_store_with_filter(dev, &rawir);
141801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
14208ffff9fa410916f1847aff831206465cefa924fDavid Härdeman
14308ffff9fa410916f1847aff831206465cefa924fDavid Härdeman	/* Fake a silence long enough to cause us to go idle */
14408ffff9fa410916f1847aff831206465cefa924fDavid Härdeman	rawir.pulse = false;
14508ffff9fa410916f1847aff831206465cefa924fDavid Härdeman	rawir.duration = dev->timeout;
14608ffff9fa410916f1847aff831206465cefa924fDavid Härdeman	ir_raw_event_store_with_filter(dev, &rawir);
14708ffff9fa410916f1847aff831206465cefa924fDavid Härdeman
148801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	ir_raw_event_handle(dev);
149801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
150801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanout:
151801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	/* Lirc expects this function to take as long as the total duration */
152801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	set_current_state(TASK_INTERRUPTIBLE);
153801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	schedule_timeout(usecs_to_jiffies(total_duration));
1545588dc2b025fd8b2188142b8d59efe562bd57d80David Härdeman	return count;
155801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
156801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
157801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic void loop_set_idle(struct rc_dev *dev, bool enable)
158801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
159801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
160801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
161801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (lodev->idle != enable) {
162801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("%sing idle mode\n", enable ? "enter" : "exit");
163801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		lodev->idle = enable;
164801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
165801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
166801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
167801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic int loop_set_learning_mode(struct rc_dev *dev, int enable)
168801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
169801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
170801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
171801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (lodev->learning != enable) {
172801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("%sing learning mode\n", enable ? "enter" : "exit");
173801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		lodev->learning = !!enable;
174801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
175801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
176801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	return 0;
177801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
178801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
179801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic int loop_set_carrier_report(struct rc_dev *dev, int enable)
180801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
181801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct loopback_dev *lodev = dev->priv;
182801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
183801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (lodev->carrierreport != enable) {
184801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		dprintk("%sabling carrier reports\n", enable ? "en" : "dis");
185801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		lodev->carrierreport = !!enable;
186801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
187801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
188801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	return 0;
189801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
190801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
191801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic int __init loop_init(void)
192801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
193801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	struct rc_dev *rc;
194801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	int ret;
195801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
196801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc = rc_allocate_device();
197801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (!rc) {
198801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		printk(KERN_ERR DRIVER_NAME ": rc_dev allocation failed\n");
199801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		return -ENOMEM;
200801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
201801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
202801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->input_name		= "rc-core loopback device";
203801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->input_phys		= "rc-core/virtual";
204801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->input_id.bustype	= BUS_VIRTUAL;
205801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->input_id.version	= 1;
206801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->driver_name		= DRIVER_NAME;
207801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->map_name		= RC_MAP_EMPTY;
208801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->priv		= &loopdev;
209801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->driver_type		= RC_DRIVER_IR_RAW;
210801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->allowed_protos	= RC_TYPE_ALL;
211801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->timeout		= 100 * 1000 * 1000; /* 100 ms */
212801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->min_timeout		= 1;
213801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->max_timeout		= UINT_MAX;
214801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->rx_resolution	= 1000;
215801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->tx_resolution	= 1000;
216801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->s_tx_mask		= loop_set_tx_mask;
217801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->s_tx_carrier	= loop_set_tx_carrier;
218801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->s_tx_duty_cycle	= loop_set_tx_duty_cycle;
219801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->s_rx_carrier_range	= loop_set_rx_carrier_range;
220801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->tx_ir		= loop_tx_ir;
221801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->s_idle		= loop_set_idle;
222801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->s_learning_mode	= loop_set_learning_mode;
223801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->s_carrier_report	= loop_set_carrier_report;
224801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc->priv		= &loopdev;
225801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
226801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.txmask		= RXMASK_REGULAR;
227801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.txcarrier	= 36000;
228801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.txduty		= 50;
229801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.rxcarriermin	= 1;
230801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.rxcarriermax	= ~0;
231801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.idle		= true;
232801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.learning	= false;
233801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.carrierreport	= false;
234801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
235801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	ret = rc_register_device(rc);
236801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	if (ret < 0) {
237801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		printk(KERN_ERR DRIVER_NAME ": rc_dev registration failed\n");
238801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		rc_free_device(rc);
239801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman		return ret;
240801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	}
241801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
242801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	loopdev.dev = rc;
243801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	return 0;
244801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
245801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
246801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanstatic void __exit loop_exit(void)
247801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman{
248801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman	rc_unregister_device(loopdev.dev);
249801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman}
250801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
251801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanmodule_init(loop_init);
252801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanmodule_exit(loop_exit);
253801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
254801c73c04ad57689dc9b47baf62cbb23f954d987David Härdemanmodule_param(debug, bool, S_IRUGO | S_IWUSR);
255801c73c04ad57689dc9b47baf62cbb23f954d987David HärdemanMODULE_PARM_DESC(debug, "Enable debug messages");
256801c73c04ad57689dc9b47baf62cbb23f954d987David Härdeman
257801c73c04ad57689dc9b47baf62cbb23f954d987David HärdemanMODULE_DESCRIPTION("Loopback device for rc-core debugging");
258801c73c04ad57689dc9b47baf62cbb23f954d987David HärdemanMODULE_AUTHOR("David Härdeman <david@hardeman.nu>");
259801c73c04ad57689dc9b47baf62cbb23f954d987David HärdemanMODULE_LICENSE("GPL");
260