18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Debug support for HID Nintendo Wii / Wii U peripherals 48c2ecf20Sopenharmony_ci * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci/* 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 138c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 148c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 158c2ecf20Sopenharmony_ci#include "hid-wiimote.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistruct wiimote_debug { 188c2ecf20Sopenharmony_ci struct wiimote_data *wdata; 198c2ecf20Sopenharmony_ci struct dentry *eeprom; 208c2ecf20Sopenharmony_ci struct dentry *drm; 218c2ecf20Sopenharmony_ci}; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, 248c2ecf20Sopenharmony_ci loff_t *off) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci struct wiimote_debug *dbg = f->private_data; 278c2ecf20Sopenharmony_ci struct wiimote_data *wdata = dbg->wdata; 288c2ecf20Sopenharmony_ci unsigned long flags; 298c2ecf20Sopenharmony_ci ssize_t ret; 308c2ecf20Sopenharmony_ci char buf[16]; 318c2ecf20Sopenharmony_ci __u16 size = 0; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci if (s == 0) 348c2ecf20Sopenharmony_ci return -EINVAL; 358c2ecf20Sopenharmony_ci if (*off > 0xffffff) 368c2ecf20Sopenharmony_ci return 0; 378c2ecf20Sopenharmony_ci if (s > 16) 388c2ecf20Sopenharmony_ci s = 16; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ret = wiimote_cmd_acquire(wdata); 418c2ecf20Sopenharmony_ci if (ret) 428c2ecf20Sopenharmony_ci return ret; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 458c2ecf20Sopenharmony_ci wdata->state.cmd_read_size = s; 468c2ecf20Sopenharmony_ci wdata->state.cmd_read_buf = buf; 478c2ecf20Sopenharmony_ci wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff); 488c2ecf20Sopenharmony_ci wiiproto_req_reeprom(wdata, *off, s); 498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ret = wiimote_cmd_wait(wdata); 528c2ecf20Sopenharmony_ci if (!ret) 538c2ecf20Sopenharmony_ci size = wdata->state.cmd_read_size; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 568c2ecf20Sopenharmony_ci wdata->state.cmd_read_buf = NULL; 578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci wiimote_cmd_release(wdata); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (ret) 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci else if (size == 0) 648c2ecf20Sopenharmony_ci return -EIO; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci if (copy_to_user(u, buf, size)) 678c2ecf20Sopenharmony_ci return -EFAULT; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci *off += size; 708c2ecf20Sopenharmony_ci ret = size; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci return ret; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic const struct file_operations wiidebug_eeprom_fops = { 768c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 778c2ecf20Sopenharmony_ci .open = simple_open, 788c2ecf20Sopenharmony_ci .read = wiidebug_eeprom_read, 798c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 808c2ecf20Sopenharmony_ci}; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic const char *wiidebug_drmmap[] = { 838c2ecf20Sopenharmony_ci [WIIPROTO_REQ_NULL] = "NULL", 848c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_K] = "K", 858c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_KA] = "KA", 868c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_KE] = "KE", 878c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_KAI] = "KAI", 888c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_KEE] = "KEE", 898c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_KAE] = "KAE", 908c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_KIE] = "KIE", 918c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_KAIE] = "KAIE", 928c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_E] = "E", 938c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_SKAI1] = "SKAI1", 948c2ecf20Sopenharmony_ci [WIIPROTO_REQ_DRM_SKAI2] = "SKAI2", 958c2ecf20Sopenharmony_ci [WIIPROTO_REQ_MAX] = NULL 968c2ecf20Sopenharmony_ci}; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic int wiidebug_drm_show(struct seq_file *f, void *p) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct wiimote_debug *dbg = f->private; 1018c2ecf20Sopenharmony_ci const char *str = NULL; 1028c2ecf20Sopenharmony_ci unsigned long flags; 1038c2ecf20Sopenharmony_ci __u8 drm; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci spin_lock_irqsave(&dbg->wdata->state.lock, flags); 1068c2ecf20Sopenharmony_ci drm = dbg->wdata->state.drm; 1078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (drm < WIIPROTO_REQ_MAX) 1108c2ecf20Sopenharmony_ci str = wiidebug_drmmap[drm]; 1118c2ecf20Sopenharmony_ci if (!str) 1128c2ecf20Sopenharmony_ci str = "unknown"; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci seq_printf(f, "%s\n", str); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int wiidebug_drm_open(struct inode *i, struct file *f) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci return single_open(f, wiidebug_drm_show, i->i_private); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic ssize_t wiidebug_drm_write(struct file *f, const char __user *u, 1258c2ecf20Sopenharmony_ci size_t s, loff_t *off) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct seq_file *sf = f->private_data; 1288c2ecf20Sopenharmony_ci struct wiimote_debug *dbg = sf->private; 1298c2ecf20Sopenharmony_ci unsigned long flags; 1308c2ecf20Sopenharmony_ci char buf[16]; 1318c2ecf20Sopenharmony_ci ssize_t len; 1328c2ecf20Sopenharmony_ci int i; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (s == 0) 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci len = min((size_t) 15, s); 1388c2ecf20Sopenharmony_ci if (copy_from_user(buf, u, len)) 1398c2ecf20Sopenharmony_ci return -EFAULT; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci buf[len] = 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci for (i = 0; i < WIIPROTO_REQ_MAX; ++i) { 1448c2ecf20Sopenharmony_ci if (!wiidebug_drmmap[i]) 1458c2ecf20Sopenharmony_ci continue; 1468c2ecf20Sopenharmony_ci if (!strcasecmp(buf, wiidebug_drmmap[i])) 1478c2ecf20Sopenharmony_ci break; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (i == WIIPROTO_REQ_MAX) 1518c2ecf20Sopenharmony_ci i = simple_strtoul(buf, NULL, 16); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci spin_lock_irqsave(&dbg->wdata->state.lock, flags); 1548c2ecf20Sopenharmony_ci dbg->wdata->state.flags &= ~WIIPROTO_FLAG_DRM_LOCKED; 1558c2ecf20Sopenharmony_ci wiiproto_req_drm(dbg->wdata, (__u8) i); 1568c2ecf20Sopenharmony_ci if (i != WIIPROTO_REQ_NULL) 1578c2ecf20Sopenharmony_ci dbg->wdata->state.flags |= WIIPROTO_FLAG_DRM_LOCKED; 1588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return len; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic const struct file_operations wiidebug_drm_fops = { 1648c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 1658c2ecf20Sopenharmony_ci .open = wiidebug_drm_open, 1668c2ecf20Sopenharmony_ci .read = seq_read, 1678c2ecf20Sopenharmony_ci .llseek = seq_lseek, 1688c2ecf20Sopenharmony_ci .write = wiidebug_drm_write, 1698c2ecf20Sopenharmony_ci .release = single_release, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciint wiidebug_init(struct wiimote_data *wdata) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct wiimote_debug *dbg; 1758c2ecf20Sopenharmony_ci unsigned long flags; 1768c2ecf20Sopenharmony_ci int ret = -ENOMEM; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); 1798c2ecf20Sopenharmony_ci if (!dbg) 1808c2ecf20Sopenharmony_ci return -ENOMEM; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci dbg->wdata = wdata; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, 1858c2ecf20Sopenharmony_ci dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); 1868c2ecf20Sopenharmony_ci if (!dbg->eeprom) 1878c2ecf20Sopenharmony_ci goto err; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci dbg->drm = debugfs_create_file("drm", S_IRUSR, 1908c2ecf20Sopenharmony_ci dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops); 1918c2ecf20Sopenharmony_ci if (!dbg->drm) 1928c2ecf20Sopenharmony_ci goto err_drm; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 1958c2ecf20Sopenharmony_ci wdata->debug = dbg; 1968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci return 0; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cierr_drm: 2018c2ecf20Sopenharmony_ci debugfs_remove(dbg->eeprom); 2028c2ecf20Sopenharmony_cierr: 2038c2ecf20Sopenharmony_ci kfree(dbg); 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_civoid wiidebug_deinit(struct wiimote_data *wdata) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct wiimote_debug *dbg = wdata->debug; 2108c2ecf20Sopenharmony_ci unsigned long flags; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (!dbg) 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 2168c2ecf20Sopenharmony_ci wdata->debug = NULL; 2178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci debugfs_remove(dbg->drm); 2208c2ecf20Sopenharmony_ci debugfs_remove(dbg->eeprom); 2218c2ecf20Sopenharmony_ci kfree(dbg); 2228c2ecf20Sopenharmony_ci} 223