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