Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
43e5e7c6 | 2 | /* |
92eda7e4 DH |
3 | * Debug support for HID Nintendo Wii / Wii U peripherals |
4 | * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> | |
43e5e7c6 DH |
5 | */ |
6 | ||
7 | /* | |
43e5e7c6 DH |
8 | */ |
9 | ||
1d3452c6 | 10 | #include <linux/debugfs.h> |
43e5e7c6 | 11 | #include <linux/module.h> |
43d782ae | 12 | #include <linux/seq_file.h> |
43e5e7c6 | 13 | #include <linux/spinlock.h> |
1d3452c6 | 14 | #include <linux/uaccess.h> |
43e5e7c6 DH |
15 | #include "hid-wiimote.h" |
16 | ||
17 | struct wiimote_debug { | |
18 | struct wiimote_data *wdata; | |
1d3452c6 | 19 | struct dentry *eeprom; |
43d782ae | 20 | struct dentry *drm; |
1d3452c6 DH |
21 | }; |
22 | ||
1d3452c6 DH |
23 | static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, |
24 | loff_t *off) | |
25 | { | |
26 | struct wiimote_debug *dbg = f->private_data; | |
27 | struct wiimote_data *wdata = dbg->wdata; | |
28 | unsigned long flags; | |
29 | ssize_t ret; | |
30 | char buf[16]; | |
b77a989a | 31 | __u16 size = 0; |
1d3452c6 DH |
32 | |
33 | if (s == 0) | |
34 | return -EINVAL; | |
35 | if (*off > 0xffffff) | |
36 | return 0; | |
37 | if (s > 16) | |
38 | s = 16; | |
39 | ||
40 | ret = wiimote_cmd_acquire(wdata); | |
41 | if (ret) | |
42 | return ret; | |
43 | ||
44 | spin_lock_irqsave(&wdata->state.lock, flags); | |
45 | wdata->state.cmd_read_size = s; | |
46 | wdata->state.cmd_read_buf = buf; | |
47 | wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff); | |
48 | wiiproto_req_reeprom(wdata, *off, s); | |
49 | spin_unlock_irqrestore(&wdata->state.lock, flags); | |
50 | ||
51 | ret = wiimote_cmd_wait(wdata); | |
52 | if (!ret) | |
53 | size = wdata->state.cmd_read_size; | |
54 | ||
55 | spin_lock_irqsave(&wdata->state.lock, flags); | |
56 | wdata->state.cmd_read_buf = NULL; | |
57 | spin_unlock_irqrestore(&wdata->state.lock, flags); | |
58 | ||
59 | wiimote_cmd_release(wdata); | |
60 | ||
61 | if (ret) | |
62 | return ret; | |
63 | else if (size == 0) | |
64 | return -EIO; | |
65 | ||
66 | if (copy_to_user(u, buf, size)) | |
67 | return -EFAULT; | |
68 | ||
69 | *off += size; | |
70 | ret = size; | |
71 | ||
72 | return ret; | |
73 | } | |
74 | ||
75 | static const struct file_operations wiidebug_eeprom_fops = { | |
76 | .owner = THIS_MODULE, | |
234e3405 | 77 | .open = simple_open, |
1d3452c6 DH |
78 | .read = wiidebug_eeprom_read, |
79 | .llseek = generic_file_llseek, | |
43e5e7c6 DH |
80 | }; |
81 | ||
43d782ae DH |
82 | static const char *wiidebug_drmmap[] = { |
83 | [WIIPROTO_REQ_NULL] = "NULL", | |
84 | [WIIPROTO_REQ_DRM_K] = "K", | |
85 | [WIIPROTO_REQ_DRM_KA] = "KA", | |
86 | [WIIPROTO_REQ_DRM_KE] = "KE", | |
87 | [WIIPROTO_REQ_DRM_KAI] = "KAI", | |
88 | [WIIPROTO_REQ_DRM_KEE] = "KEE", | |
89 | [WIIPROTO_REQ_DRM_KAE] = "KAE", | |
90 | [WIIPROTO_REQ_DRM_KIE] = "KIE", | |
91 | [WIIPROTO_REQ_DRM_KAIE] = "KAIE", | |
92 | [WIIPROTO_REQ_DRM_E] = "E", | |
93 | [WIIPROTO_REQ_DRM_SKAI1] = "SKAI1", | |
94 | [WIIPROTO_REQ_DRM_SKAI2] = "SKAI2", | |
95 | [WIIPROTO_REQ_MAX] = NULL | |
96 | }; | |
97 | ||
98 | static int wiidebug_drm_show(struct seq_file *f, void *p) | |
99 | { | |
100 | struct wiimote_debug *dbg = f->private; | |
101 | const char *str = NULL; | |
102 | unsigned long flags; | |
103 | __u8 drm; | |
104 | ||
105 | spin_lock_irqsave(&dbg->wdata->state.lock, flags); | |
106 | drm = dbg->wdata->state.drm; | |
107 | spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); | |
108 | ||
109 | if (drm < WIIPROTO_REQ_MAX) | |
110 | str = wiidebug_drmmap[drm]; | |
111 | if (!str) | |
112 | str = "unknown"; | |
113 | ||
114 | seq_printf(f, "%s\n", str); | |
115 | ||
116 | return 0; | |
117 | } | |
118 | ||
119 | static int wiidebug_drm_open(struct inode *i, struct file *f) | |
120 | { | |
121 | return single_open(f, wiidebug_drm_show, i->i_private); | |
122 | } | |
123 | ||
124 | static ssize_t wiidebug_drm_write(struct file *f, const char __user *u, | |
125 | size_t s, loff_t *off) | |
126 | { | |
51103c70 DH |
127 | struct seq_file *sf = f->private_data; |
128 | struct wiimote_debug *dbg = sf->private; | |
43d782ae DH |
129 | unsigned long flags; |
130 | char buf[16]; | |
131 | ssize_t len; | |
132 | int i; | |
133 | ||
134 | if (s == 0) | |
135 | return -EINVAL; | |
136 | ||
137 | len = min((size_t) 15, s); | |
138 | if (copy_from_user(buf, u, len)) | |
139 | return -EFAULT; | |
140 | ||
0d57eb87 | 141 | buf[len] = 0; |
43d782ae DH |
142 | |
143 | for (i = 0; i < WIIPROTO_REQ_MAX; ++i) { | |
144 | if (!wiidebug_drmmap[i]) | |
145 | continue; | |
146 | if (!strcasecmp(buf, wiidebug_drmmap[i])) | |
147 | break; | |
148 | } | |
149 | ||
150 | if (i == WIIPROTO_REQ_MAX) | |
0d57eb87 | 151 | i = simple_strtoul(buf, NULL, 16); |
43d782ae DH |
152 | |
153 | spin_lock_irqsave(&dbg->wdata->state.lock, flags); | |
d76f89e1 | 154 | dbg->wdata->state.flags &= ~WIIPROTO_FLAG_DRM_LOCKED; |
43d782ae | 155 | wiiproto_req_drm(dbg->wdata, (__u8) i); |
d76f89e1 DH |
156 | if (i != WIIPROTO_REQ_NULL) |
157 | dbg->wdata->state.flags |= WIIPROTO_FLAG_DRM_LOCKED; | |
43d782ae DH |
158 | spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); |
159 | ||
160 | return len; | |
161 | } | |
162 | ||
163 | static const struct file_operations wiidebug_drm_fops = { | |
164 | .owner = THIS_MODULE, | |
165 | .open = wiidebug_drm_open, | |
166 | .read = seq_read, | |
167 | .llseek = seq_lseek, | |
168 | .write = wiidebug_drm_write, | |
169 | .release = single_release, | |
170 | }; | |
171 | ||
43e5e7c6 DH |
172 | int wiidebug_init(struct wiimote_data *wdata) |
173 | { | |
174 | struct wiimote_debug *dbg; | |
175 | unsigned long flags; | |
43d782ae | 176 | int ret = -ENOMEM; |
43e5e7c6 DH |
177 | |
178 | dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); | |
179 | if (!dbg) | |
180 | return -ENOMEM; | |
181 | ||
182 | dbg->wdata = wdata; | |
183 | ||
1d3452c6 DH |
184 | dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, |
185 | dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); | |
43d782ae DH |
186 | if (!dbg->eeprom) |
187 | goto err; | |
188 | ||
189 | dbg->drm = debugfs_create_file("drm", S_IRUSR, | |
190 | dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops); | |
191 | if (!dbg->drm) | |
192 | goto err_drm; | |
1d3452c6 | 193 | |
43e5e7c6 DH |
194 | spin_lock_irqsave(&wdata->state.lock, flags); |
195 | wdata->debug = dbg; | |
196 | spin_unlock_irqrestore(&wdata->state.lock, flags); | |
197 | ||
198 | return 0; | |
43d782ae DH |
199 | |
200 | err_drm: | |
201 | debugfs_remove(dbg->eeprom); | |
202 | err: | |
203 | kfree(dbg); | |
204 | return ret; | |
43e5e7c6 DH |
205 | } |
206 | ||
207 | void wiidebug_deinit(struct wiimote_data *wdata) | |
208 | { | |
209 | struct wiimote_debug *dbg = wdata->debug; | |
210 | unsigned long flags; | |
211 | ||
212 | if (!dbg) | |
213 | return; | |
214 | ||
215 | spin_lock_irqsave(&wdata->state.lock, flags); | |
216 | wdata->debug = NULL; | |
217 | spin_unlock_irqrestore(&wdata->state.lock, flags); | |
218 | ||
43d782ae | 219 | debugfs_remove(dbg->drm); |
1d3452c6 | 220 | debugfs_remove(dbg->eeprom); |
43e5e7c6 DH |
221 | kfree(dbg); |
222 | } |