12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 243e5e7c6SDavid Herrmann /* 392eda7e4SDavid Herrmann * Debug support for HID Nintendo Wii / Wii U peripherals 492eda7e4SDavid Herrmann * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> 543e5e7c6SDavid Herrmann */ 643e5e7c6SDavid Herrmann 743e5e7c6SDavid Herrmann /* 843e5e7c6SDavid Herrmann */ 943e5e7c6SDavid Herrmann 101d3452c6SDavid Herrmann #include <linux/debugfs.h> 1143e5e7c6SDavid Herrmann #include <linux/module.h> 1243d782aeSDavid Herrmann #include <linux/seq_file.h> 1343e5e7c6SDavid Herrmann #include <linux/spinlock.h> 141d3452c6SDavid Herrmann #include <linux/uaccess.h> 1543e5e7c6SDavid Herrmann #include "hid-wiimote.h" 1643e5e7c6SDavid Herrmann 1743e5e7c6SDavid Herrmann struct wiimote_debug { 1843e5e7c6SDavid Herrmann struct wiimote_data *wdata; 191d3452c6SDavid Herrmann struct dentry *eeprom; 2043d782aeSDavid Herrmann struct dentry *drm; 211d3452c6SDavid Herrmann }; 221d3452c6SDavid Herrmann 231d3452c6SDavid Herrmann static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, 241d3452c6SDavid Herrmann loff_t *off) 251d3452c6SDavid Herrmann { 261d3452c6SDavid Herrmann struct wiimote_debug *dbg = f->private_data; 271d3452c6SDavid Herrmann struct wiimote_data *wdata = dbg->wdata; 281d3452c6SDavid Herrmann unsigned long flags; 291d3452c6SDavid Herrmann ssize_t ret; 301d3452c6SDavid Herrmann char buf[16]; 31b77a989aSSimon Que __u16 size = 0; 321d3452c6SDavid Herrmann 331d3452c6SDavid Herrmann if (s == 0) 341d3452c6SDavid Herrmann return -EINVAL; 351d3452c6SDavid Herrmann if (*off > 0xffffff) 361d3452c6SDavid Herrmann return 0; 371d3452c6SDavid Herrmann if (s > 16) 381d3452c6SDavid Herrmann s = 16; 391d3452c6SDavid Herrmann 401d3452c6SDavid Herrmann ret = wiimote_cmd_acquire(wdata); 411d3452c6SDavid Herrmann if (ret) 421d3452c6SDavid Herrmann return ret; 431d3452c6SDavid Herrmann 441d3452c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 451d3452c6SDavid Herrmann wdata->state.cmd_read_size = s; 461d3452c6SDavid Herrmann wdata->state.cmd_read_buf = buf; 471d3452c6SDavid Herrmann wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff); 481d3452c6SDavid Herrmann wiiproto_req_reeprom(wdata, *off, s); 491d3452c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 501d3452c6SDavid Herrmann 511d3452c6SDavid Herrmann ret = wiimote_cmd_wait(wdata); 521d3452c6SDavid Herrmann if (!ret) 531d3452c6SDavid Herrmann size = wdata->state.cmd_read_size; 541d3452c6SDavid Herrmann 551d3452c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 561d3452c6SDavid Herrmann wdata->state.cmd_read_buf = NULL; 571d3452c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 581d3452c6SDavid Herrmann 591d3452c6SDavid Herrmann wiimote_cmd_release(wdata); 601d3452c6SDavid Herrmann 611d3452c6SDavid Herrmann if (ret) 621d3452c6SDavid Herrmann return ret; 631d3452c6SDavid Herrmann else if (size == 0) 641d3452c6SDavid Herrmann return -EIO; 651d3452c6SDavid Herrmann 661d3452c6SDavid Herrmann if (copy_to_user(u, buf, size)) 671d3452c6SDavid Herrmann return -EFAULT; 681d3452c6SDavid Herrmann 691d3452c6SDavid Herrmann *off += size; 701d3452c6SDavid Herrmann ret = size; 711d3452c6SDavid Herrmann 721d3452c6SDavid Herrmann return ret; 731d3452c6SDavid Herrmann } 741d3452c6SDavid Herrmann 751d3452c6SDavid Herrmann static const struct file_operations wiidebug_eeprom_fops = { 761d3452c6SDavid Herrmann .owner = THIS_MODULE, 77234e3405SStephen Boyd .open = simple_open, 781d3452c6SDavid Herrmann .read = wiidebug_eeprom_read, 791d3452c6SDavid Herrmann .llseek = generic_file_llseek, 8043e5e7c6SDavid Herrmann }; 8143e5e7c6SDavid Herrmann 8243d782aeSDavid Herrmann static const char *wiidebug_drmmap[] = { 8343d782aeSDavid Herrmann [WIIPROTO_REQ_NULL] = "NULL", 8443d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_K] = "K", 8543d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KA] = "KA", 8643d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KE] = "KE", 8743d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KAI] = "KAI", 8843d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KEE] = "KEE", 8943d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KAE] = "KAE", 9043d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KIE] = "KIE", 9143d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KAIE] = "KAIE", 9243d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_E] = "E", 9343d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_SKAI1] = "SKAI1", 9443d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_SKAI2] = "SKAI2", 9543d782aeSDavid Herrmann [WIIPROTO_REQ_MAX] = NULL 9643d782aeSDavid Herrmann }; 9743d782aeSDavid Herrmann 9843d782aeSDavid Herrmann static int wiidebug_drm_show(struct seq_file *f, void *p) 9943d782aeSDavid Herrmann { 10043d782aeSDavid Herrmann struct wiimote_debug *dbg = f->private; 10143d782aeSDavid Herrmann const char *str = NULL; 10243d782aeSDavid Herrmann unsigned long flags; 10343d782aeSDavid Herrmann __u8 drm; 10443d782aeSDavid Herrmann 10543d782aeSDavid Herrmann spin_lock_irqsave(&dbg->wdata->state.lock, flags); 10643d782aeSDavid Herrmann drm = dbg->wdata->state.drm; 10743d782aeSDavid Herrmann spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 10843d782aeSDavid Herrmann 10943d782aeSDavid Herrmann if (drm < WIIPROTO_REQ_MAX) 11043d782aeSDavid Herrmann str = wiidebug_drmmap[drm]; 11143d782aeSDavid Herrmann if (!str) 11243d782aeSDavid Herrmann str = "unknown"; 11343d782aeSDavid Herrmann 11443d782aeSDavid Herrmann seq_printf(f, "%s\n", str); 11543d782aeSDavid Herrmann 11643d782aeSDavid Herrmann return 0; 11743d782aeSDavid Herrmann } 11843d782aeSDavid Herrmann 11943d782aeSDavid Herrmann static int wiidebug_drm_open(struct inode *i, struct file *f) 12043d782aeSDavid Herrmann { 12143d782aeSDavid Herrmann return single_open(f, wiidebug_drm_show, i->i_private); 12243d782aeSDavid Herrmann } 12343d782aeSDavid Herrmann 12443d782aeSDavid Herrmann static ssize_t wiidebug_drm_write(struct file *f, const char __user *u, 12543d782aeSDavid Herrmann size_t s, loff_t *off) 12643d782aeSDavid Herrmann { 12751103c70SDavid Herrmann struct seq_file *sf = f->private_data; 12851103c70SDavid Herrmann struct wiimote_debug *dbg = sf->private; 12943d782aeSDavid Herrmann unsigned long flags; 13043d782aeSDavid Herrmann char buf[16]; 13143d782aeSDavid Herrmann ssize_t len; 13243d782aeSDavid Herrmann int i; 13343d782aeSDavid Herrmann 13443d782aeSDavid Herrmann if (s == 0) 13543d782aeSDavid Herrmann return -EINVAL; 13643d782aeSDavid Herrmann 13743d782aeSDavid Herrmann len = min((size_t) 15, s); 13843d782aeSDavid Herrmann if (copy_from_user(buf, u, len)) 13943d782aeSDavid Herrmann return -EFAULT; 14043d782aeSDavid Herrmann 1410d57eb87SDavid Herrmann buf[len] = 0; 14243d782aeSDavid Herrmann 14343d782aeSDavid Herrmann for (i = 0; i < WIIPROTO_REQ_MAX; ++i) { 14443d782aeSDavid Herrmann if (!wiidebug_drmmap[i]) 14543d782aeSDavid Herrmann continue; 14643d782aeSDavid Herrmann if (!strcasecmp(buf, wiidebug_drmmap[i])) 14743d782aeSDavid Herrmann break; 14843d782aeSDavid Herrmann } 14943d782aeSDavid Herrmann 15043d782aeSDavid Herrmann if (i == WIIPROTO_REQ_MAX) 1510d57eb87SDavid Herrmann i = simple_strtoul(buf, NULL, 16); 15243d782aeSDavid Herrmann 15343d782aeSDavid Herrmann spin_lock_irqsave(&dbg->wdata->state.lock, flags); 154d76f89e1SDavid Herrmann dbg->wdata->state.flags &= ~WIIPROTO_FLAG_DRM_LOCKED; 15543d782aeSDavid Herrmann wiiproto_req_drm(dbg->wdata, (__u8) i); 156d76f89e1SDavid Herrmann if (i != WIIPROTO_REQ_NULL) 157d76f89e1SDavid Herrmann dbg->wdata->state.flags |= WIIPROTO_FLAG_DRM_LOCKED; 15843d782aeSDavid Herrmann spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 15943d782aeSDavid Herrmann 16043d782aeSDavid Herrmann return len; 16143d782aeSDavid Herrmann } 16243d782aeSDavid Herrmann 16343d782aeSDavid Herrmann static const struct file_operations wiidebug_drm_fops = { 16443d782aeSDavid Herrmann .owner = THIS_MODULE, 16543d782aeSDavid Herrmann .open = wiidebug_drm_open, 16643d782aeSDavid Herrmann .read = seq_read, 16743d782aeSDavid Herrmann .llseek = seq_lseek, 16843d782aeSDavid Herrmann .write = wiidebug_drm_write, 16943d782aeSDavid Herrmann .release = single_release, 17043d782aeSDavid Herrmann }; 17143d782aeSDavid Herrmann 17243e5e7c6SDavid Herrmann int wiidebug_init(struct wiimote_data *wdata) 17343e5e7c6SDavid Herrmann { 17443e5e7c6SDavid Herrmann struct wiimote_debug *dbg; 17543e5e7c6SDavid Herrmann unsigned long flags; 17643d782aeSDavid Herrmann int ret = -ENOMEM; 17743e5e7c6SDavid Herrmann 17843e5e7c6SDavid Herrmann dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); 17943e5e7c6SDavid Herrmann if (!dbg) 18043e5e7c6SDavid Herrmann return -ENOMEM; 18143e5e7c6SDavid Herrmann 18243e5e7c6SDavid Herrmann dbg->wdata = wdata; 18343e5e7c6SDavid Herrmann 1841d3452c6SDavid Herrmann dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, 1851d3452c6SDavid Herrmann dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); 18643d782aeSDavid Herrmann if (!dbg->eeprom) 18743d782aeSDavid Herrmann goto err; 18843d782aeSDavid Herrmann 18943d782aeSDavid Herrmann dbg->drm = debugfs_create_file("drm", S_IRUSR, 19043d782aeSDavid Herrmann dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops); 19143d782aeSDavid Herrmann if (!dbg->drm) 19243d782aeSDavid Herrmann goto err_drm; 1931d3452c6SDavid Herrmann 19443e5e7c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 19543e5e7c6SDavid Herrmann wdata->debug = dbg; 19643e5e7c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 19743e5e7c6SDavid Herrmann 19843e5e7c6SDavid Herrmann return 0; 19943d782aeSDavid Herrmann 20043d782aeSDavid Herrmann err_drm: 20143d782aeSDavid Herrmann debugfs_remove(dbg->eeprom); 20243d782aeSDavid Herrmann err: 20343d782aeSDavid Herrmann kfree(dbg); 20443d782aeSDavid Herrmann return ret; 20543e5e7c6SDavid Herrmann } 20643e5e7c6SDavid Herrmann 20743e5e7c6SDavid Herrmann void wiidebug_deinit(struct wiimote_data *wdata) 20843e5e7c6SDavid Herrmann { 20943e5e7c6SDavid Herrmann struct wiimote_debug *dbg = wdata->debug; 21043e5e7c6SDavid Herrmann unsigned long flags; 21143e5e7c6SDavid Herrmann 21243e5e7c6SDavid Herrmann if (!dbg) 21343e5e7c6SDavid Herrmann return; 21443e5e7c6SDavid Herrmann 21543e5e7c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 21643e5e7c6SDavid Herrmann wdata->debug = NULL; 21743e5e7c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 21843e5e7c6SDavid Herrmann 21943d782aeSDavid Herrmann debugfs_remove(dbg->drm); 2201d3452c6SDavid Herrmann debugfs_remove(dbg->eeprom); 22143e5e7c6SDavid Herrmann kfree(dbg); 22243e5e7c6SDavid Herrmann } 223