1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2017 Red Hat, Inc |
4 | */ |
5 | |
6 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
7 | |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> |
10 | #include <linux/libps2.h> |
11 | #include <linux/i2c.h> |
12 | #include <linux/serio.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/workqueue.h> |
15 | #include "psmouse.h" |
16 | |
17 | struct psmouse_smbus_dev { |
18 | struct i2c_board_info board; |
19 | struct psmouse *psmouse; |
20 | struct i2c_client *client; |
21 | struct list_head node; |
22 | bool dead; |
23 | bool need_deactivate; |
24 | }; |
25 | |
26 | static LIST_HEAD(psmouse_smbus_list); |
27 | static DEFINE_MUTEX(psmouse_smbus_mutex); |
28 | |
29 | static struct workqueue_struct *psmouse_smbus_wq; |
30 | |
31 | static void psmouse_smbus_check_adapter(struct i2c_adapter *adapter) |
32 | { |
33 | struct psmouse_smbus_dev *smbdev; |
34 | |
35 | if (!i2c_check_functionality(adap: adapter, I2C_FUNC_SMBUS_HOST_NOTIFY)) |
36 | return; |
37 | |
38 | mutex_lock(&psmouse_smbus_mutex); |
39 | |
40 | list_for_each_entry(smbdev, &psmouse_smbus_list, node) { |
41 | if (smbdev->dead) |
42 | continue; |
43 | |
44 | if (smbdev->client) |
45 | continue; |
46 | |
47 | /* |
48 | * Here would be a good place to check if device is actually |
49 | * present, but it seems that SMBus will not respond unless we |
50 | * fully reset PS/2 connection. So cross our fingers, and try |
51 | * to switch over, hopefully our system will not have too many |
52 | * "host notify" I2C adapters. |
53 | */ |
54 | psmouse_dbg(smbdev->psmouse, |
55 | "SMBus candidate adapter appeared, triggering rescan\n" ); |
56 | serio_rescan(serio: smbdev->psmouse->ps2dev.serio); |
57 | } |
58 | |
59 | mutex_unlock(lock: &psmouse_smbus_mutex); |
60 | } |
61 | |
62 | static void psmouse_smbus_detach_i2c_client(struct i2c_client *client) |
63 | { |
64 | struct psmouse_smbus_dev *smbdev, *tmp; |
65 | |
66 | mutex_lock(&psmouse_smbus_mutex); |
67 | |
68 | list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { |
69 | if (smbdev->client != client) |
70 | continue; |
71 | |
72 | kfree(objp: client->dev.platform_data); |
73 | client->dev.platform_data = NULL; |
74 | |
75 | if (!smbdev->dead) { |
76 | psmouse_dbg(smbdev->psmouse, |
77 | "Marking SMBus companion %s as gone\n" , |
78 | dev_name(&smbdev->client->dev)); |
79 | smbdev->dead = true; |
80 | device_link_remove(consumer: &smbdev->client->dev, |
81 | supplier: &smbdev->psmouse->ps2dev.serio->dev); |
82 | serio_rescan(serio: smbdev->psmouse->ps2dev.serio); |
83 | } else { |
84 | list_del(entry: &smbdev->node); |
85 | kfree(objp: smbdev); |
86 | } |
87 | } |
88 | |
89 | mutex_unlock(lock: &psmouse_smbus_mutex); |
90 | } |
91 | |
92 | static int psmouse_smbus_notifier_call(struct notifier_block *nb, |
93 | unsigned long action, void *data) |
94 | { |
95 | struct device *dev = data; |
96 | |
97 | switch (action) { |
98 | case BUS_NOTIFY_ADD_DEVICE: |
99 | if (dev->type == &i2c_adapter_type) |
100 | psmouse_smbus_check_adapter(to_i2c_adapter(dev)); |
101 | break; |
102 | |
103 | case BUS_NOTIFY_REMOVED_DEVICE: |
104 | if (dev->type == &i2c_client_type) |
105 | psmouse_smbus_detach_i2c_client(to_i2c_client(dev)); |
106 | break; |
107 | } |
108 | |
109 | return 0; |
110 | } |
111 | |
112 | static struct notifier_block psmouse_smbus_notifier = { |
113 | .notifier_call = psmouse_smbus_notifier_call, |
114 | }; |
115 | |
116 | static psmouse_ret_t psmouse_smbus_process_byte(struct psmouse *psmouse) |
117 | { |
118 | return PSMOUSE_FULL_PACKET; |
119 | } |
120 | |
121 | static int psmouse_smbus_reconnect(struct psmouse *psmouse) |
122 | { |
123 | struct psmouse_smbus_dev *smbdev = psmouse->private; |
124 | |
125 | if (smbdev->need_deactivate) |
126 | psmouse_deactivate(psmouse); |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | struct psmouse_smbus_removal_work { |
132 | struct work_struct work; |
133 | struct i2c_client *client; |
134 | }; |
135 | |
136 | static void psmouse_smbus_remove_i2c_device(struct work_struct *work) |
137 | { |
138 | struct psmouse_smbus_removal_work *rwork = |
139 | container_of(work, struct psmouse_smbus_removal_work, work); |
140 | |
141 | dev_dbg(&rwork->client->dev, "destroying SMBus companion device\n" ); |
142 | i2c_unregister_device(client: rwork->client); |
143 | |
144 | kfree(objp: rwork); |
145 | } |
146 | |
147 | /* |
148 | * This schedules removal of SMBus companion device. We have to do |
149 | * it in a separate tread to avoid deadlocking on psmouse_mutex in |
150 | * case the device has a trackstick (which is also driven by psmouse). |
151 | * |
152 | * Note that this may be racing with i2c adapter removal, but we |
153 | * can't do anything about that: i2c automatically destroys clients |
154 | * attached to an adapter that is being removed. This has to be |
155 | * fixed in i2c core. |
156 | */ |
157 | static void psmouse_smbus_schedule_remove(struct i2c_client *client) |
158 | { |
159 | struct psmouse_smbus_removal_work *rwork; |
160 | |
161 | rwork = kzalloc(size: sizeof(*rwork), GFP_KERNEL); |
162 | if (rwork) { |
163 | INIT_WORK(&rwork->work, psmouse_smbus_remove_i2c_device); |
164 | rwork->client = client; |
165 | |
166 | queue_work(wq: psmouse_smbus_wq, work: &rwork->work); |
167 | } |
168 | } |
169 | |
170 | static void psmouse_smbus_disconnect(struct psmouse *psmouse) |
171 | { |
172 | struct psmouse_smbus_dev *smbdev = psmouse->private; |
173 | |
174 | mutex_lock(&psmouse_smbus_mutex); |
175 | |
176 | if (smbdev->dead) { |
177 | list_del(entry: &smbdev->node); |
178 | kfree(objp: smbdev); |
179 | } else { |
180 | smbdev->dead = true; |
181 | device_link_remove(consumer: &smbdev->client->dev, |
182 | supplier: &psmouse->ps2dev.serio->dev); |
183 | psmouse_dbg(smbdev->psmouse, |
184 | "posting removal request for SMBus companion %s\n" , |
185 | dev_name(&smbdev->client->dev)); |
186 | psmouse_smbus_schedule_remove(client: smbdev->client); |
187 | } |
188 | |
189 | mutex_unlock(lock: &psmouse_smbus_mutex); |
190 | |
191 | psmouse->private = NULL; |
192 | } |
193 | |
194 | static int psmouse_smbus_create_companion(struct device *dev, void *data) |
195 | { |
196 | struct psmouse_smbus_dev *smbdev = data; |
197 | unsigned short addr_list[] = { smbdev->board.addr, I2C_CLIENT_END }; |
198 | struct i2c_adapter *adapter; |
199 | struct i2c_client *client; |
200 | |
201 | adapter = i2c_verify_adapter(dev); |
202 | if (!adapter) |
203 | return 0; |
204 | |
205 | if (!i2c_check_functionality(adap: adapter, I2C_FUNC_SMBUS_HOST_NOTIFY)) |
206 | return 0; |
207 | |
208 | client = i2c_new_scanned_device(adap: adapter, info: &smbdev->board, |
209 | addr_list, NULL); |
210 | if (IS_ERR(ptr: client)) |
211 | return 0; |
212 | |
213 | /* We have our(?) device, stop iterating i2c bus. */ |
214 | smbdev->client = client; |
215 | return 1; |
216 | } |
217 | |
218 | void psmouse_smbus_cleanup(struct psmouse *psmouse) |
219 | { |
220 | struct psmouse_smbus_dev *smbdev, *tmp; |
221 | |
222 | mutex_lock(&psmouse_smbus_mutex); |
223 | |
224 | list_for_each_entry_safe(smbdev, tmp, &psmouse_smbus_list, node) { |
225 | if (psmouse == smbdev->psmouse) { |
226 | list_del(entry: &smbdev->node); |
227 | kfree(objp: smbdev); |
228 | } |
229 | } |
230 | |
231 | mutex_unlock(lock: &psmouse_smbus_mutex); |
232 | } |
233 | |
234 | int psmouse_smbus_init(struct psmouse *psmouse, |
235 | const struct i2c_board_info *board, |
236 | const void *pdata, size_t pdata_size, |
237 | bool need_deactivate, |
238 | bool leave_breadcrumbs) |
239 | { |
240 | struct psmouse_smbus_dev *smbdev; |
241 | int error; |
242 | |
243 | smbdev = kzalloc(size: sizeof(*smbdev), GFP_KERNEL); |
244 | if (!smbdev) |
245 | return -ENOMEM; |
246 | |
247 | smbdev->psmouse = psmouse; |
248 | smbdev->board = *board; |
249 | smbdev->need_deactivate = need_deactivate; |
250 | |
251 | if (pdata) { |
252 | smbdev->board.platform_data = kmemdup(p: pdata, size: pdata_size, |
253 | GFP_KERNEL); |
254 | if (!smbdev->board.platform_data) { |
255 | kfree(objp: smbdev); |
256 | return -ENOMEM; |
257 | } |
258 | } |
259 | |
260 | if (need_deactivate) |
261 | psmouse_deactivate(psmouse); |
262 | |
263 | psmouse->private = smbdev; |
264 | psmouse->protocol_handler = psmouse_smbus_process_byte; |
265 | psmouse->reconnect = psmouse_smbus_reconnect; |
266 | psmouse->fast_reconnect = psmouse_smbus_reconnect; |
267 | psmouse->disconnect = psmouse_smbus_disconnect; |
268 | psmouse->resync_time = 0; |
269 | |
270 | mutex_lock(&psmouse_smbus_mutex); |
271 | list_add_tail(new: &smbdev->node, head: &psmouse_smbus_list); |
272 | mutex_unlock(lock: &psmouse_smbus_mutex); |
273 | |
274 | /* Bind to already existing adapters right away */ |
275 | error = i2c_for_each_dev(data: smbdev, fn: psmouse_smbus_create_companion); |
276 | |
277 | if (smbdev->client) { |
278 | /* We have our companion device */ |
279 | if (!device_link_add(consumer: &smbdev->client->dev, |
280 | supplier: &psmouse->ps2dev.serio->dev, |
281 | DL_FLAG_STATELESS)) |
282 | psmouse_warn(psmouse, |
283 | "failed to set up link with iSMBus companion %s\n" , |
284 | dev_name(&smbdev->client->dev)); |
285 | return 0; |
286 | } |
287 | |
288 | /* |
289 | * If we did not create i2c device we will not need platform |
290 | * data even if we are leaving breadcrumbs. |
291 | */ |
292 | kfree(objp: smbdev->board.platform_data); |
293 | smbdev->board.platform_data = NULL; |
294 | |
295 | if (error < 0 || !leave_breadcrumbs) { |
296 | mutex_lock(&psmouse_smbus_mutex); |
297 | list_del(entry: &smbdev->node); |
298 | mutex_unlock(lock: &psmouse_smbus_mutex); |
299 | |
300 | kfree(objp: smbdev); |
301 | } |
302 | |
303 | return error < 0 ? error : -EAGAIN; |
304 | } |
305 | |
306 | int __init psmouse_smbus_module_init(void) |
307 | { |
308 | int error; |
309 | |
310 | psmouse_smbus_wq = alloc_workqueue(fmt: "psmouse-smbus" , flags: 0, max_active: 0); |
311 | if (!psmouse_smbus_wq) |
312 | return -ENOMEM; |
313 | |
314 | error = bus_register_notifier(bus: &i2c_bus_type, nb: &psmouse_smbus_notifier); |
315 | if (error) { |
316 | pr_err("failed to register i2c bus notifier: %d\n" , error); |
317 | destroy_workqueue(wq: psmouse_smbus_wq); |
318 | return error; |
319 | } |
320 | |
321 | return 0; |
322 | } |
323 | |
324 | void psmouse_smbus_module_exit(void) |
325 | { |
326 | bus_unregister_notifier(bus: &i2c_bus_type, nb: &psmouse_smbus_notifier); |
327 | destroy_workqueue(wq: psmouse_smbus_wq); |
328 | } |
329 | |