1 // SPDX-License-Identifier: GPL-2.0
3 * FS-iA6B iBus RC receiver driver
5 * This driver provides all 14 channels of the FlySky FS-ia6B RC receiver
8 * Additionally, the channels can be converted to discrete switch values.
9 * By default, it is configured for the offical FS-i6 remote control.
10 * If you use a different hardware configuration, you can configure it
11 * using the `switch_config` parameter.
14 #include <linux/device.h>
15 #include <linux/input.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/serio.h>
19 #include <linux/slab.h>
20 #include <linux/types.h>
22 #define DRIVER_DESC "FS-iA6B iBus RC receiver"
24 MODULE_AUTHOR("Markus Koch <markus@notsyncing.net>");
25 MODULE_DESCRIPTION(DRIVER_DESC
);
26 MODULE_LICENSE("GPL");
28 #define IBUS_SERVO_COUNT 14
30 static char *switch_config
= "00000022320000";
31 module_param(switch_config
, charp
, 0444);
32 MODULE_PARM_DESC(switch_config
,
33 "Amount of switch positions per channel (14 characters, 0-3)");
35 static int fsia6b_axes
[IBUS_SERVO_COUNT
] = {
45 enum ibus_state
{ SYNC
, COLLECT
, PROCESS
};
48 enum ibus_state state
;
52 u16 channel
[IBUS_SERVO_COUNT
];
56 struct input_dev
*dev
;
57 struct ibus_packet packet
;
62 static irqreturn_t
fsia6b_serio_irq(struct serio
*serio
,
63 unsigned char data
, unsigned int flags
)
65 struct fsia6b
*fsia6b
= serio_get_drvdata(serio
);
70 fsia6b
->packet
.ibuf
= (data
<< 8) | ((fsia6b
->packet
.ibuf
>> 8) & 0xFF);
72 switch (fsia6b
->packet
.state
) {
74 if (fsia6b
->packet
.ibuf
== 0x4020)
75 fsia6b
->packet
.state
= COLLECT
;
79 fsia6b
->packet
.state
= PROCESS
;
83 fsia6b
->packet
.channel
[fsia6b
->packet
.offset
] =
85 fsia6b
->packet
.offset
++;
87 if (fsia6b
->packet
.offset
== IBUS_SERVO_COUNT
) {
88 fsia6b
->packet
.offset
= 0;
89 fsia6b
->packet
.state
= SYNC
;
90 for (i
= 0; i
< IBUS_SERVO_COUNT
; ++i
) {
91 input_report_abs(fsia6b
->dev
, fsia6b_axes
[i
],
92 fsia6b
->packet
.channel
[i
]);
95 if (fsia6b
->packet
.channel
[i
] > 1900)
97 else if (fsia6b
->packet
.channel
[i
] < 1100)
100 switch (switch_config
[i
]) {
102 input_report_key(fsia6b
->dev
,
107 input_report_key(fsia6b
->dev
,
112 input_report_key(fsia6b
->dev
,
117 input_sync(fsia6b
->dev
);
119 fsia6b
->packet
.state
= COLLECT
;
127 static int fsia6b_serio_connect(struct serio
*serio
, struct serio_driver
*drv
)
129 struct fsia6b
*fsia6b
;
130 struct input_dev
*input_dev
;
135 fsia6b
= kzalloc(sizeof(*fsia6b
), GFP_KERNEL
);
139 fsia6b
->packet
.ibuf
= 0;
140 fsia6b
->packet
.offset
= 0;
141 fsia6b
->packet
.state
= SYNC
;
143 serio_set_drvdata(serio
, fsia6b
);
145 input_dev
= input_allocate_device();
150 fsia6b
->dev
= input_dev
;
152 snprintf(fsia6b
->phys
, sizeof(fsia6b
->phys
), "%s/input0", serio
->phys
);
154 input_dev
->name
= DRIVER_DESC
;
155 input_dev
->phys
= fsia6b
->phys
;
156 input_dev
->id
.bustype
= BUS_RS232
;
157 input_dev
->id
.vendor
= SERIO_FSIA6B
;
158 input_dev
->id
.product
= serio
->id
.id
;
159 input_dev
->id
.version
= 0x0100;
160 input_dev
->dev
.parent
= &serio
->dev
;
162 for (i
= 0; i
< IBUS_SERVO_COUNT
; i
++)
163 input_set_abs_params(input_dev
, fsia6b_axes
[i
],
166 /* Register switch configuration */
167 for (i
= 0; i
< IBUS_SERVO_COUNT
; i
++) {
168 if (switch_config
[i
] < '0' || switch_config
[i
] > '3') {
169 dev_err(&fsia6b
->dev
->dev
,
170 "Invalid switch configuration supplied for fsia6b.\n");
175 for (j
= '1'; j
<= switch_config
[i
]; j
++) {
176 input_set_capability(input_dev
, EV_KEY
, BTN_0
+ sw_id
);
181 err
= serio_open(serio
, drv
);
185 err
= input_register_device(fsia6b
->dev
);
191 fail3
: serio_close(serio
);
192 fail2
: input_free_device(input_dev
);
193 fail1
: serio_set_drvdata(serio
, NULL
);
198 static void fsia6b_serio_disconnect(struct serio
*serio
)
200 struct fsia6b
*fsia6b
= serio_get_drvdata(serio
);
203 serio_set_drvdata(serio
, NULL
);
204 input_unregister_device(fsia6b
->dev
);
208 static const struct serio_device_id fsia6b_serio_ids
[] = {
211 .proto
= SERIO_FSIA6B
,
218 MODULE_DEVICE_TABLE(serio
, fsia6b_serio_ids
);
220 static struct serio_driver fsia6b_serio_drv
= {
224 .description
= DRIVER_DESC
,
225 .id_table
= fsia6b_serio_ids
,
226 .interrupt
= fsia6b_serio_irq
,
227 .connect
= fsia6b_serio_connect
,
228 .disconnect
= fsia6b_serio_disconnect
231 module_serio_driver(fsia6b_serio_drv
)