1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * SEGA Dreamcast controller driver |
4 | * Based on drivers/usb/iforce.c |
5 | * |
6 | * Copyright Yaegashi Takeshi, 2001 |
7 | * Adrian McMenamin, 2008 - 2009 |
8 | */ |
9 | |
10 | #include <linux/kernel.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/input.h> |
13 | #include <linux/module.h> |
14 | #include <linux/init.h> |
15 | #include <linux/timer.h> |
16 | #include <linux/maple.h> |
17 | |
18 | MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>" ); |
19 | MODULE_DESCRIPTION("SEGA Dreamcast controller driver" ); |
20 | MODULE_LICENSE("GPL" ); |
21 | |
22 | struct dc_pad { |
23 | struct input_dev *dev; |
24 | struct maple_device *mdev; |
25 | }; |
26 | |
27 | static void dc_pad_callback(struct mapleq *mq) |
28 | { |
29 | unsigned short buttons; |
30 | struct maple_device *mapledev = mq->dev; |
31 | struct dc_pad *pad = maple_get_drvdata(mapledev); |
32 | struct input_dev *dev = pad->dev; |
33 | unsigned char *res = mq->recvbuf->buf; |
34 | |
35 | buttons = ~le16_to_cpup(p: (__le16 *)(res + 8)); |
36 | |
37 | input_report_abs(dev, ABS_HAT0Y, |
38 | value: (buttons & 0x0010 ? -1 : 0) + (buttons & 0x0020 ? 1 : 0)); |
39 | input_report_abs(dev, ABS_HAT0X, |
40 | value: (buttons & 0x0040 ? -1 : 0) + (buttons & 0x0080 ? 1 : 0)); |
41 | input_report_abs(dev, ABS_HAT1Y, |
42 | value: (buttons & 0x1000 ? -1 : 0) + (buttons & 0x2000 ? 1 : 0)); |
43 | input_report_abs(dev, ABS_HAT1X, |
44 | value: (buttons & 0x4000 ? -1 : 0) + (buttons & 0x8000 ? 1 : 0)); |
45 | |
46 | input_report_key(dev, BTN_C, value: buttons & 0x0001); |
47 | input_report_key(dev, BTN_B, value: buttons & 0x0002); |
48 | input_report_key(dev, BTN_A, value: buttons & 0x0004); |
49 | input_report_key(dev, BTN_START, value: buttons & 0x0008); |
50 | input_report_key(dev, BTN_Z, value: buttons & 0x0100); |
51 | input_report_key(dev, BTN_Y, value: buttons & 0x0200); |
52 | input_report_key(dev, BTN_X, value: buttons & 0x0400); |
53 | input_report_key(dev, BTN_SELECT, value: buttons & 0x0800); |
54 | |
55 | input_report_abs(dev, ABS_GAS, value: res[10]); |
56 | input_report_abs(dev, ABS_BRAKE, value: res[11]); |
57 | input_report_abs(dev, ABS_X, value: res[12]); |
58 | input_report_abs(dev, ABS_Y, value: res[13]); |
59 | input_report_abs(dev, ABS_RX, value: res[14]); |
60 | input_report_abs(dev, ABS_RY, value: res[15]); |
61 | } |
62 | |
63 | static int dc_pad_open(struct input_dev *dev) |
64 | { |
65 | struct dc_pad *pad = dev_get_platdata(dev: &dev->dev); |
66 | |
67 | maple_getcond_callback(dev: pad->mdev, callback: dc_pad_callback, HZ/20, |
68 | function: MAPLE_FUNC_CONTROLLER); |
69 | |
70 | return 0; |
71 | } |
72 | |
73 | static void dc_pad_close(struct input_dev *dev) |
74 | { |
75 | struct dc_pad *pad = dev_get_platdata(dev: &dev->dev); |
76 | |
77 | maple_getcond_callback(dev: pad->mdev, callback: dc_pad_callback, interval: 0, |
78 | function: MAPLE_FUNC_CONTROLLER); |
79 | } |
80 | |
81 | /* allow the controller to be used */ |
82 | static int probe_maple_controller(struct device *dev) |
83 | { |
84 | static const short btn_bit[32] = { |
85 | BTN_C, BTN_B, BTN_A, BTN_START, -1, -1, -1, -1, |
86 | BTN_Z, BTN_Y, BTN_X, BTN_SELECT, -1, -1, -1, -1, |
87 | -1, -1, -1, -1, -1, -1, -1, -1, |
88 | -1, -1, -1, -1, -1, -1, -1, -1, |
89 | }; |
90 | |
91 | static const short abs_bit[32] = { |
92 | -1, -1, -1, -1, ABS_HAT0Y, ABS_HAT0Y, ABS_HAT0X, ABS_HAT0X, |
93 | -1, -1, -1, -1, ABS_HAT1Y, ABS_HAT1Y, ABS_HAT1X, ABS_HAT1X, |
94 | ABS_GAS, ABS_BRAKE, ABS_X, ABS_Y, ABS_RX, ABS_RY, -1, -1, |
95 | -1, -1, -1, -1, -1, -1, -1, -1, |
96 | }; |
97 | |
98 | struct maple_device *mdev = to_maple_dev(dev); |
99 | struct maple_driver *mdrv = to_maple_driver(dev->driver); |
100 | int i, error; |
101 | struct dc_pad *pad; |
102 | struct input_dev *idev; |
103 | unsigned long data = be32_to_cpu(mdev->devinfo.function_data[0]); |
104 | |
105 | pad = kzalloc(size: sizeof(struct dc_pad), GFP_KERNEL); |
106 | idev = input_allocate_device(); |
107 | if (!pad || !idev) { |
108 | error = -ENOMEM; |
109 | goto fail; |
110 | } |
111 | |
112 | pad->dev = idev; |
113 | pad->mdev = mdev; |
114 | |
115 | idev->open = dc_pad_open; |
116 | idev->close = dc_pad_close; |
117 | |
118 | for (i = 0; i < 32; i++) { |
119 | if (data & (1 << i)) { |
120 | if (btn_bit[i] >= 0) |
121 | __set_bit(btn_bit[i], idev->keybit); |
122 | else if (abs_bit[i] >= 0) |
123 | __set_bit(abs_bit[i], idev->absbit); |
124 | } |
125 | } |
126 | |
127 | if (idev->keybit[BIT_WORD(BTN_JOYSTICK)]) |
128 | idev->evbit[0] |= BIT_MASK(EV_KEY); |
129 | |
130 | if (idev->absbit[0]) |
131 | idev->evbit[0] |= BIT_MASK(EV_ABS); |
132 | |
133 | for (i = ABS_X; i <= ABS_BRAKE; i++) |
134 | input_set_abs_params(dev: idev, axis: i, min: 0, max: 255, fuzz: 0, flat: 0); |
135 | |
136 | for (i = ABS_HAT0X; i <= ABS_HAT3Y; i++) |
137 | input_set_abs_params(dev: idev, axis: i, min: 1, max: -1, fuzz: 0, flat: 0); |
138 | |
139 | idev->dev.platform_data = pad; |
140 | idev->dev.parent = &mdev->dev; |
141 | idev->name = mdev->product_name; |
142 | idev->id.bustype = BUS_HOST; |
143 | |
144 | error = input_register_device(idev); |
145 | if (error) |
146 | goto fail; |
147 | |
148 | mdev->driver = mdrv; |
149 | maple_set_drvdata(mdev, pad); |
150 | |
151 | return 0; |
152 | |
153 | fail: |
154 | input_free_device(dev: idev); |
155 | kfree(objp: pad); |
156 | maple_set_drvdata(mdev, NULL); |
157 | return error; |
158 | } |
159 | |
160 | static int remove_maple_controller(struct device *dev) |
161 | { |
162 | struct maple_device *mdev = to_maple_dev(dev); |
163 | struct dc_pad *pad = maple_get_drvdata(mdev); |
164 | |
165 | mdev->callback = NULL; |
166 | input_unregister_device(pad->dev); |
167 | maple_set_drvdata(mdev, NULL); |
168 | kfree(objp: pad); |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | static struct maple_driver dc_pad_driver = { |
174 | .function = MAPLE_FUNC_CONTROLLER, |
175 | .drv = { |
176 | .name = "Dreamcast_controller" , |
177 | .probe = probe_maple_controller, |
178 | .remove = remove_maple_controller, |
179 | }, |
180 | }; |
181 | |
182 | static int __init dc_pad_init(void) |
183 | { |
184 | return maple_driver_register(&dc_pad_driver); |
185 | } |
186 | |
187 | static void __exit dc_pad_exit(void) |
188 | { |
189 | maple_driver_unregister(&dc_pad_driver); |
190 | } |
191 | |
192 | module_init(dc_pad_init); |
193 | module_exit(dc_pad_exit); |
194 | |