1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * FS-iA6B iBus RC receiver driver
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * This driver provides all 14 channels of the FlySky FS-ia6B RC receiver
6*4882a593Smuzhiyun * as analog values.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Additionally, the channels can be converted to discrete switch values.
9*4882a593Smuzhiyun * By default, it is configured for the offical FS-i6 remote control.
10*4882a593Smuzhiyun * If you use a different hardware configuration, you can configure it
11*4882a593Smuzhiyun * using the `switch_config` parameter.
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/device.h>
15*4882a593Smuzhiyun #include <linux/input.h>
16*4882a593Smuzhiyun #include <linux/kernel.h>
17*4882a593Smuzhiyun #include <linux/module.h>
18*4882a593Smuzhiyun #include <linux/serio.h>
19*4882a593Smuzhiyun #include <linux/slab.h>
20*4882a593Smuzhiyun #include <linux/types.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define DRIVER_DESC "FS-iA6B iBus RC receiver"
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun MODULE_AUTHOR("Markus Koch <markus@notsyncing.net>");
25*4882a593Smuzhiyun MODULE_DESCRIPTION(DRIVER_DESC);
26*4882a593Smuzhiyun MODULE_LICENSE("GPL");
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun #define IBUS_SERVO_COUNT 14
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun static char *switch_config = "00000022320000";
31*4882a593Smuzhiyun module_param(switch_config, charp, 0444);
32*4882a593Smuzhiyun MODULE_PARM_DESC(switch_config,
33*4882a593Smuzhiyun "Amount of switch positions per channel (14 characters, 0-3)");
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun static int fsia6b_axes[IBUS_SERVO_COUNT] = {
36*4882a593Smuzhiyun ABS_X, ABS_Y,
37*4882a593Smuzhiyun ABS_Z, ABS_RX,
38*4882a593Smuzhiyun ABS_RY, ABS_RZ,
39*4882a593Smuzhiyun ABS_HAT0X, ABS_HAT0Y,
40*4882a593Smuzhiyun ABS_HAT1X, ABS_HAT1Y,
41*4882a593Smuzhiyun ABS_HAT2X, ABS_HAT2Y,
42*4882a593Smuzhiyun ABS_HAT3X, ABS_HAT3Y
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun enum ibus_state { SYNC, COLLECT, PROCESS };
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun struct ibus_packet {
48*4882a593Smuzhiyun enum ibus_state state;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun int offset;
51*4882a593Smuzhiyun u16 ibuf;
52*4882a593Smuzhiyun u16 channel[IBUS_SERVO_COUNT];
53*4882a593Smuzhiyun };
54*4882a593Smuzhiyun
55*4882a593Smuzhiyun struct fsia6b {
56*4882a593Smuzhiyun struct input_dev *dev;
57*4882a593Smuzhiyun struct ibus_packet packet;
58*4882a593Smuzhiyun
59*4882a593Smuzhiyun char phys[32];
60*4882a593Smuzhiyun };
61*4882a593Smuzhiyun
fsia6b_serio_irq(struct serio * serio,unsigned char data,unsigned int flags)62*4882a593Smuzhiyun static irqreturn_t fsia6b_serio_irq(struct serio *serio,
63*4882a593Smuzhiyun unsigned char data, unsigned int flags)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun struct fsia6b *fsia6b = serio_get_drvdata(serio);
66*4882a593Smuzhiyun int i;
67*4882a593Smuzhiyun int sw_state;
68*4882a593Smuzhiyun int sw_id = BTN_0;
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun fsia6b->packet.ibuf = (data << 8) | ((fsia6b->packet.ibuf >> 8) & 0xFF);
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun switch (fsia6b->packet.state) {
73*4882a593Smuzhiyun case SYNC:
74*4882a593Smuzhiyun if (fsia6b->packet.ibuf == 0x4020)
75*4882a593Smuzhiyun fsia6b->packet.state = COLLECT;
76*4882a593Smuzhiyun break;
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun case COLLECT:
79*4882a593Smuzhiyun fsia6b->packet.state = PROCESS;
80*4882a593Smuzhiyun break;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun case PROCESS:
83*4882a593Smuzhiyun fsia6b->packet.channel[fsia6b->packet.offset] =
84*4882a593Smuzhiyun fsia6b->packet.ibuf;
85*4882a593Smuzhiyun fsia6b->packet.offset++;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun if (fsia6b->packet.offset == IBUS_SERVO_COUNT) {
88*4882a593Smuzhiyun fsia6b->packet.offset = 0;
89*4882a593Smuzhiyun fsia6b->packet.state = SYNC;
90*4882a593Smuzhiyun for (i = 0; i < IBUS_SERVO_COUNT; ++i) {
91*4882a593Smuzhiyun input_report_abs(fsia6b->dev, fsia6b_axes[i],
92*4882a593Smuzhiyun fsia6b->packet.channel[i]);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun sw_state = 0;
95*4882a593Smuzhiyun if (fsia6b->packet.channel[i] > 1900)
96*4882a593Smuzhiyun sw_state = 1;
97*4882a593Smuzhiyun else if (fsia6b->packet.channel[i] < 1100)
98*4882a593Smuzhiyun sw_state = 2;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun switch (switch_config[i]) {
101*4882a593Smuzhiyun case '3':
102*4882a593Smuzhiyun input_report_key(fsia6b->dev,
103*4882a593Smuzhiyun sw_id++,
104*4882a593Smuzhiyun sw_state == 0);
105*4882a593Smuzhiyun fallthrough;
106*4882a593Smuzhiyun case '2':
107*4882a593Smuzhiyun input_report_key(fsia6b->dev,
108*4882a593Smuzhiyun sw_id++,
109*4882a593Smuzhiyun sw_state == 1);
110*4882a593Smuzhiyun fallthrough;
111*4882a593Smuzhiyun case '1':
112*4882a593Smuzhiyun input_report_key(fsia6b->dev,
113*4882a593Smuzhiyun sw_id++,
114*4882a593Smuzhiyun sw_state == 2);
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun input_sync(fsia6b->dev);
118*4882a593Smuzhiyun } else {
119*4882a593Smuzhiyun fsia6b->packet.state = COLLECT;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun break;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun return IRQ_HANDLED;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
fsia6b_serio_connect(struct serio * serio,struct serio_driver * drv)127*4882a593Smuzhiyun static int fsia6b_serio_connect(struct serio *serio, struct serio_driver *drv)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun struct fsia6b *fsia6b;
130*4882a593Smuzhiyun struct input_dev *input_dev;
131*4882a593Smuzhiyun int err;
132*4882a593Smuzhiyun int i, j;
133*4882a593Smuzhiyun int sw_id = 0;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun fsia6b = kzalloc(sizeof(*fsia6b), GFP_KERNEL);
136*4882a593Smuzhiyun if (!fsia6b)
137*4882a593Smuzhiyun return -ENOMEM;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun fsia6b->packet.ibuf = 0;
140*4882a593Smuzhiyun fsia6b->packet.offset = 0;
141*4882a593Smuzhiyun fsia6b->packet.state = SYNC;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun serio_set_drvdata(serio, fsia6b);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun input_dev = input_allocate_device();
146*4882a593Smuzhiyun if (!input_dev) {
147*4882a593Smuzhiyun err = -ENOMEM;
148*4882a593Smuzhiyun goto fail1;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun fsia6b->dev = input_dev;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun snprintf(fsia6b->phys, sizeof(fsia6b->phys), "%s/input0", serio->phys);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun input_dev->name = DRIVER_DESC;
155*4882a593Smuzhiyun input_dev->phys = fsia6b->phys;
156*4882a593Smuzhiyun input_dev->id.bustype = BUS_RS232;
157*4882a593Smuzhiyun input_dev->id.vendor = SERIO_FSIA6B;
158*4882a593Smuzhiyun input_dev->id.product = serio->id.id;
159*4882a593Smuzhiyun input_dev->id.version = 0x0100;
160*4882a593Smuzhiyun input_dev->dev.parent = &serio->dev;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun for (i = 0; i < IBUS_SERVO_COUNT; i++)
163*4882a593Smuzhiyun input_set_abs_params(input_dev, fsia6b_axes[i],
164*4882a593Smuzhiyun 1000, 2000, 2, 2);
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun /* Register switch configuration */
167*4882a593Smuzhiyun for (i = 0; i < IBUS_SERVO_COUNT; i++) {
168*4882a593Smuzhiyun if (switch_config[i] < '0' || switch_config[i] > '3') {
169*4882a593Smuzhiyun dev_err(&fsia6b->dev->dev,
170*4882a593Smuzhiyun "Invalid switch configuration supplied for fsia6b.\n");
171*4882a593Smuzhiyun err = -EINVAL;
172*4882a593Smuzhiyun goto fail2;
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun for (j = '1'; j <= switch_config[i]; j++) {
176*4882a593Smuzhiyun input_set_capability(input_dev, EV_KEY, BTN_0 + sw_id);
177*4882a593Smuzhiyun sw_id++;
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun err = serio_open(serio, drv);
182*4882a593Smuzhiyun if (err)
183*4882a593Smuzhiyun goto fail2;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun err = input_register_device(fsia6b->dev);
186*4882a593Smuzhiyun if (err)
187*4882a593Smuzhiyun goto fail3;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun return 0;
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun fail3: serio_close(serio);
192*4882a593Smuzhiyun fail2: input_free_device(input_dev);
193*4882a593Smuzhiyun fail1: serio_set_drvdata(serio, NULL);
194*4882a593Smuzhiyun kfree(fsia6b);
195*4882a593Smuzhiyun return err;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
fsia6b_serio_disconnect(struct serio * serio)198*4882a593Smuzhiyun static void fsia6b_serio_disconnect(struct serio *serio)
199*4882a593Smuzhiyun {
200*4882a593Smuzhiyun struct fsia6b *fsia6b = serio_get_drvdata(serio);
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun serio_close(serio);
203*4882a593Smuzhiyun serio_set_drvdata(serio, NULL);
204*4882a593Smuzhiyun input_unregister_device(fsia6b->dev);
205*4882a593Smuzhiyun kfree(fsia6b);
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun static const struct serio_device_id fsia6b_serio_ids[] = {
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun .type = SERIO_RS232,
211*4882a593Smuzhiyun .proto = SERIO_FSIA6B,
212*4882a593Smuzhiyun .id = SERIO_ANY,
213*4882a593Smuzhiyun .extra = SERIO_ANY,
214*4882a593Smuzhiyun },
215*4882a593Smuzhiyun { 0 }
216*4882a593Smuzhiyun };
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun MODULE_DEVICE_TABLE(serio, fsia6b_serio_ids);
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun static struct serio_driver fsia6b_serio_drv = {
221*4882a593Smuzhiyun .driver = {
222*4882a593Smuzhiyun .name = "fsia6b"
223*4882a593Smuzhiyun },
224*4882a593Smuzhiyun .description = DRIVER_DESC,
225*4882a593Smuzhiyun .id_table = fsia6b_serio_ids,
226*4882a593Smuzhiyun .interrupt = fsia6b_serio_irq,
227*4882a593Smuzhiyun .connect = fsia6b_serio_connect,
228*4882a593Smuzhiyun .disconnect = fsia6b_serio_disconnect
229*4882a593Smuzhiyun };
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun module_serio_driver(fsia6b_serio_drv)
232