162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Debug support for HID Nintendo Wii / Wii U peripherals 462306a36Sopenharmony_ci * Copyright (c) 2011-2013 David Herrmann <dh.herrmann@gmail.com> 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci/* 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/debugfs.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/seq_file.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/uaccess.h> 1562306a36Sopenharmony_ci#include "hid-wiimote.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct wiimote_debug { 1862306a36Sopenharmony_ci struct wiimote_data *wdata; 1962306a36Sopenharmony_ci struct dentry *eeprom; 2062306a36Sopenharmony_ci struct dentry *drm; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, 2462306a36Sopenharmony_ci loff_t *off) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci struct wiimote_debug *dbg = f->private_data; 2762306a36Sopenharmony_ci struct wiimote_data *wdata = dbg->wdata; 2862306a36Sopenharmony_ci unsigned long flags; 2962306a36Sopenharmony_ci ssize_t ret; 3062306a36Sopenharmony_ci char buf[16]; 3162306a36Sopenharmony_ci __u16 size = 0; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (s == 0) 3462306a36Sopenharmony_ci return -EINVAL; 3562306a36Sopenharmony_ci if (*off > 0xffffff) 3662306a36Sopenharmony_ci return 0; 3762306a36Sopenharmony_ci if (s > 16) 3862306a36Sopenharmony_ci s = 16; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci ret = wiimote_cmd_acquire(wdata); 4162306a36Sopenharmony_ci if (ret) 4262306a36Sopenharmony_ci return ret; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 4562306a36Sopenharmony_ci wdata->state.cmd_read_size = s; 4662306a36Sopenharmony_ci wdata->state.cmd_read_buf = buf; 4762306a36Sopenharmony_ci wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff); 4862306a36Sopenharmony_ci wiiproto_req_reeprom(wdata, *off, s); 4962306a36Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci ret = wiimote_cmd_wait(wdata); 5262306a36Sopenharmony_ci if (!ret) 5362306a36Sopenharmony_ci size = wdata->state.cmd_read_size; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 5662306a36Sopenharmony_ci wdata->state.cmd_read_buf = NULL; 5762306a36Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci wiimote_cmd_release(wdata); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (ret) 6262306a36Sopenharmony_ci return ret; 6362306a36Sopenharmony_ci else if (size == 0) 6462306a36Sopenharmony_ci return -EIO; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci if (copy_to_user(u, buf, size)) 6762306a36Sopenharmony_ci return -EFAULT; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci *off += size; 7062306a36Sopenharmony_ci ret = size; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci return ret; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const struct file_operations wiidebug_eeprom_fops = { 7662306a36Sopenharmony_ci .owner = THIS_MODULE, 7762306a36Sopenharmony_ci .open = simple_open, 7862306a36Sopenharmony_ci .read = wiidebug_eeprom_read, 7962306a36Sopenharmony_ci .llseek = generic_file_llseek, 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic const char *wiidebug_drmmap[] = { 8362306a36Sopenharmony_ci [WIIPROTO_REQ_NULL] = "NULL", 8462306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_K] = "K", 8562306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_KA] = "KA", 8662306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_KE] = "KE", 8762306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_KAI] = "KAI", 8862306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_KEE] = "KEE", 8962306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_KAE] = "KAE", 9062306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_KIE] = "KIE", 9162306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_KAIE] = "KAIE", 9262306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_E] = "E", 9362306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_SKAI1] = "SKAI1", 9462306a36Sopenharmony_ci [WIIPROTO_REQ_DRM_SKAI2] = "SKAI2", 9562306a36Sopenharmony_ci [WIIPROTO_REQ_MAX] = NULL 9662306a36Sopenharmony_ci}; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic int wiidebug_drm_show(struct seq_file *f, void *p) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct wiimote_debug *dbg = f->private; 10162306a36Sopenharmony_ci const char *str = NULL; 10262306a36Sopenharmony_ci unsigned long flags; 10362306a36Sopenharmony_ci __u8 drm; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci spin_lock_irqsave(&dbg->wdata->state.lock, flags); 10662306a36Sopenharmony_ci drm = dbg->wdata->state.drm; 10762306a36Sopenharmony_ci spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (drm < WIIPROTO_REQ_MAX) 11062306a36Sopenharmony_ci str = wiidebug_drmmap[drm]; 11162306a36Sopenharmony_ci if (!str) 11262306a36Sopenharmony_ci str = "unknown"; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci seq_printf(f, "%s\n", str); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int wiidebug_drm_open(struct inode *i, struct file *f) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci return single_open(f, wiidebug_drm_show, i->i_private); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic ssize_t wiidebug_drm_write(struct file *f, const char __user *u, 12562306a36Sopenharmony_ci size_t s, loff_t *off) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct seq_file *sf = f->private_data; 12862306a36Sopenharmony_ci struct wiimote_debug *dbg = sf->private; 12962306a36Sopenharmony_ci unsigned long flags; 13062306a36Sopenharmony_ci char buf[16]; 13162306a36Sopenharmony_ci ssize_t len; 13262306a36Sopenharmony_ci int i; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (s == 0) 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci len = min((size_t) 15, s); 13862306a36Sopenharmony_ci if (copy_from_user(buf, u, len)) 13962306a36Sopenharmony_ci return -EFAULT; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci buf[len] = 0; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci for (i = 0; i < WIIPROTO_REQ_MAX; ++i) { 14462306a36Sopenharmony_ci if (!wiidebug_drmmap[i]) 14562306a36Sopenharmony_ci continue; 14662306a36Sopenharmony_ci if (!strcasecmp(buf, wiidebug_drmmap[i])) 14762306a36Sopenharmony_ci break; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (i == WIIPROTO_REQ_MAX) 15162306a36Sopenharmony_ci i = simple_strtoul(buf, NULL, 16); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci spin_lock_irqsave(&dbg->wdata->state.lock, flags); 15462306a36Sopenharmony_ci dbg->wdata->state.flags &= ~WIIPROTO_FLAG_DRM_LOCKED; 15562306a36Sopenharmony_ci wiiproto_req_drm(dbg->wdata, (__u8) i); 15662306a36Sopenharmony_ci if (i != WIIPROTO_REQ_NULL) 15762306a36Sopenharmony_ci dbg->wdata->state.flags |= WIIPROTO_FLAG_DRM_LOCKED; 15862306a36Sopenharmony_ci spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return len; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic const struct file_operations wiidebug_drm_fops = { 16462306a36Sopenharmony_ci .owner = THIS_MODULE, 16562306a36Sopenharmony_ci .open = wiidebug_drm_open, 16662306a36Sopenharmony_ci .read = seq_read, 16762306a36Sopenharmony_ci .llseek = seq_lseek, 16862306a36Sopenharmony_ci .write = wiidebug_drm_write, 16962306a36Sopenharmony_ci .release = single_release, 17062306a36Sopenharmony_ci}; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ciint wiidebug_init(struct wiimote_data *wdata) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct wiimote_debug *dbg; 17562306a36Sopenharmony_ci unsigned long flags; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); 17862306a36Sopenharmony_ci if (!dbg) 17962306a36Sopenharmony_ci return -ENOMEM; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci dbg->wdata = wdata; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, 18462306a36Sopenharmony_ci dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci dbg->drm = debugfs_create_file("drm", S_IRUSR, 18762306a36Sopenharmony_ci dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 19062306a36Sopenharmony_ci wdata->debug = dbg; 19162306a36Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_civoid wiidebug_deinit(struct wiimote_data *wdata) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct wiimote_debug *dbg = wdata->debug; 20062306a36Sopenharmony_ci unsigned long flags; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!dbg) 20362306a36Sopenharmony_ci return; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci spin_lock_irqsave(&wdata->state.lock, flags); 20662306a36Sopenharmony_ci wdata->debug = NULL; 20762306a36Sopenharmony_ci spin_unlock_irqrestore(&wdata->state.lock, flags); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci debugfs_remove(dbg->drm); 21062306a36Sopenharmony_ci debugfs_remove(dbg->eeprom); 21162306a36Sopenharmony_ci kfree(dbg); 21262306a36Sopenharmony_ci} 213