143e5e7c6SDavid Herrmann /* 243e5e7c6SDavid Herrmann * Debug support for HID Nintendo Wiimote devices 343e5e7c6SDavid Herrmann * Copyright (c) 2011 David Herrmann 443e5e7c6SDavid Herrmann */ 543e5e7c6SDavid Herrmann 643e5e7c6SDavid Herrmann /* 743e5e7c6SDavid Herrmann * This program is free software; you can redistribute it and/or modify it 843e5e7c6SDavid Herrmann * under the terms of the GNU General Public License as published by the Free 943e5e7c6SDavid Herrmann * Software Foundation; either version 2 of the License, or (at your option) 1043e5e7c6SDavid Herrmann * any later version. 1143e5e7c6SDavid Herrmann */ 1243e5e7c6SDavid Herrmann 131d3452c6SDavid Herrmann #include <linux/debugfs.h> 1443e5e7c6SDavid Herrmann #include <linux/module.h> 1543d782aeSDavid Herrmann #include <linux/seq_file.h> 1643e5e7c6SDavid Herrmann #include <linux/spinlock.h> 171d3452c6SDavid Herrmann #include <linux/uaccess.h> 1843e5e7c6SDavid Herrmann #include "hid-wiimote.h" 1943e5e7c6SDavid Herrmann 2043e5e7c6SDavid Herrmann struct wiimote_debug { 2143e5e7c6SDavid Herrmann struct wiimote_data *wdata; 221d3452c6SDavid Herrmann struct dentry *eeprom; 2343d782aeSDavid Herrmann struct dentry *drm; 241d3452c6SDavid Herrmann }; 251d3452c6SDavid Herrmann 261d3452c6SDavid Herrmann static ssize_t wiidebug_eeprom_read(struct file *f, char __user *u, size_t s, 271d3452c6SDavid Herrmann loff_t *off) 281d3452c6SDavid Herrmann { 291d3452c6SDavid Herrmann struct wiimote_debug *dbg = f->private_data; 301d3452c6SDavid Herrmann struct wiimote_data *wdata = dbg->wdata; 311d3452c6SDavid Herrmann unsigned long flags; 321d3452c6SDavid Herrmann ssize_t ret; 331d3452c6SDavid Herrmann char buf[16]; 34b77a989aSSimon Que __u16 size = 0; 351d3452c6SDavid Herrmann 361d3452c6SDavid Herrmann if (s == 0) 371d3452c6SDavid Herrmann return -EINVAL; 381d3452c6SDavid Herrmann if (*off > 0xffffff) 391d3452c6SDavid Herrmann return 0; 401d3452c6SDavid Herrmann if (s > 16) 411d3452c6SDavid Herrmann s = 16; 421d3452c6SDavid Herrmann 431d3452c6SDavid Herrmann ret = wiimote_cmd_acquire(wdata); 441d3452c6SDavid Herrmann if (ret) 451d3452c6SDavid Herrmann return ret; 461d3452c6SDavid Herrmann 471d3452c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 481d3452c6SDavid Herrmann wdata->state.cmd_read_size = s; 491d3452c6SDavid Herrmann wdata->state.cmd_read_buf = buf; 501d3452c6SDavid Herrmann wiimote_cmd_set(wdata, WIIPROTO_REQ_RMEM, *off & 0xffff); 511d3452c6SDavid Herrmann wiiproto_req_reeprom(wdata, *off, s); 521d3452c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 531d3452c6SDavid Herrmann 541d3452c6SDavid Herrmann ret = wiimote_cmd_wait(wdata); 551d3452c6SDavid Herrmann if (!ret) 561d3452c6SDavid Herrmann size = wdata->state.cmd_read_size; 571d3452c6SDavid Herrmann 581d3452c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 591d3452c6SDavid Herrmann wdata->state.cmd_read_buf = NULL; 601d3452c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 611d3452c6SDavid Herrmann 621d3452c6SDavid Herrmann wiimote_cmd_release(wdata); 631d3452c6SDavid Herrmann 641d3452c6SDavid Herrmann if (ret) 651d3452c6SDavid Herrmann return ret; 661d3452c6SDavid Herrmann else if (size == 0) 671d3452c6SDavid Herrmann return -EIO; 681d3452c6SDavid Herrmann 691d3452c6SDavid Herrmann if (copy_to_user(u, buf, size)) 701d3452c6SDavid Herrmann return -EFAULT; 711d3452c6SDavid Herrmann 721d3452c6SDavid Herrmann *off += size; 731d3452c6SDavid Herrmann ret = size; 741d3452c6SDavid Herrmann 751d3452c6SDavid Herrmann return ret; 761d3452c6SDavid Herrmann } 771d3452c6SDavid Herrmann 781d3452c6SDavid Herrmann static const struct file_operations wiidebug_eeprom_fops = { 791d3452c6SDavid Herrmann .owner = THIS_MODULE, 80234e3405SStephen Boyd .open = simple_open, 811d3452c6SDavid Herrmann .read = wiidebug_eeprom_read, 821d3452c6SDavid Herrmann .llseek = generic_file_llseek, 8343e5e7c6SDavid Herrmann }; 8443e5e7c6SDavid Herrmann 8543d782aeSDavid Herrmann static const char *wiidebug_drmmap[] = { 8643d782aeSDavid Herrmann [WIIPROTO_REQ_NULL] = "NULL", 8743d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_K] = "K", 8843d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KA] = "KA", 8943d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KE] = "KE", 9043d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KAI] = "KAI", 9143d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KEE] = "KEE", 9243d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KAE] = "KAE", 9343d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KIE] = "KIE", 9443d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_KAIE] = "KAIE", 9543d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_E] = "E", 9643d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_SKAI1] = "SKAI1", 9743d782aeSDavid Herrmann [WIIPROTO_REQ_DRM_SKAI2] = "SKAI2", 9843d782aeSDavid Herrmann [WIIPROTO_REQ_MAX] = NULL 9943d782aeSDavid Herrmann }; 10043d782aeSDavid Herrmann 10143d782aeSDavid Herrmann static int wiidebug_drm_show(struct seq_file *f, void *p) 10243d782aeSDavid Herrmann { 10343d782aeSDavid Herrmann struct wiimote_debug *dbg = f->private; 10443d782aeSDavid Herrmann const char *str = NULL; 10543d782aeSDavid Herrmann unsigned long flags; 10643d782aeSDavid Herrmann __u8 drm; 10743d782aeSDavid Herrmann 10843d782aeSDavid Herrmann spin_lock_irqsave(&dbg->wdata->state.lock, flags); 10943d782aeSDavid Herrmann drm = dbg->wdata->state.drm; 11043d782aeSDavid Herrmann spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 11143d782aeSDavid Herrmann 11243d782aeSDavid Herrmann if (drm < WIIPROTO_REQ_MAX) 11343d782aeSDavid Herrmann str = wiidebug_drmmap[drm]; 11443d782aeSDavid Herrmann if (!str) 11543d782aeSDavid Herrmann str = "unknown"; 11643d782aeSDavid Herrmann 11743d782aeSDavid Herrmann seq_printf(f, "%s\n", str); 11843d782aeSDavid Herrmann 11943d782aeSDavid Herrmann return 0; 12043d782aeSDavid Herrmann } 12143d782aeSDavid Herrmann 12243d782aeSDavid Herrmann static int wiidebug_drm_open(struct inode *i, struct file *f) 12343d782aeSDavid Herrmann { 12443d782aeSDavid Herrmann return single_open(f, wiidebug_drm_show, i->i_private); 12543d782aeSDavid Herrmann } 12643d782aeSDavid Herrmann 12743d782aeSDavid Herrmann static ssize_t wiidebug_drm_write(struct file *f, const char __user *u, 12843d782aeSDavid Herrmann size_t s, loff_t *off) 12943d782aeSDavid Herrmann { 13043d782aeSDavid Herrmann struct wiimote_debug *dbg = f->private_data; 13143d782aeSDavid Herrmann unsigned long flags; 13243d782aeSDavid Herrmann char buf[16]; 13343d782aeSDavid Herrmann ssize_t len; 13443d782aeSDavid Herrmann int i; 13543d782aeSDavid Herrmann 13643d782aeSDavid Herrmann if (s == 0) 13743d782aeSDavid Herrmann return -EINVAL; 13843d782aeSDavid Herrmann 13943d782aeSDavid Herrmann len = min((size_t) 15, s); 14043d782aeSDavid Herrmann if (copy_from_user(buf, u, len)) 14143d782aeSDavid Herrmann return -EFAULT; 14243d782aeSDavid Herrmann 14343d782aeSDavid Herrmann buf[15] = 0; 14443d782aeSDavid Herrmann 14543d782aeSDavid Herrmann for (i = 0; i < WIIPROTO_REQ_MAX; ++i) { 14643d782aeSDavid Herrmann if (!wiidebug_drmmap[i]) 14743d782aeSDavid Herrmann continue; 14843d782aeSDavid Herrmann if (!strcasecmp(buf, wiidebug_drmmap[i])) 14943d782aeSDavid Herrmann break; 15043d782aeSDavid Herrmann } 15143d782aeSDavid Herrmann 15243d782aeSDavid Herrmann if (i == WIIPROTO_REQ_MAX) 15343d782aeSDavid Herrmann i = simple_strtoul(buf, NULL, 10); 15443d782aeSDavid Herrmann 15543d782aeSDavid Herrmann spin_lock_irqsave(&dbg->wdata->state.lock, flags); 15643d782aeSDavid Herrmann wiiproto_req_drm(dbg->wdata, (__u8) i); 15743d782aeSDavid Herrmann spin_unlock_irqrestore(&dbg->wdata->state.lock, flags); 15843d782aeSDavid Herrmann 15943d782aeSDavid Herrmann return len; 16043d782aeSDavid Herrmann } 16143d782aeSDavid Herrmann 16243d782aeSDavid Herrmann static const struct file_operations wiidebug_drm_fops = { 16343d782aeSDavid Herrmann .owner = THIS_MODULE, 16443d782aeSDavid Herrmann .open = wiidebug_drm_open, 16543d782aeSDavid Herrmann .read = seq_read, 16643d782aeSDavid Herrmann .llseek = seq_lseek, 16743d782aeSDavid Herrmann .write = wiidebug_drm_write, 16843d782aeSDavid Herrmann .release = single_release, 16943d782aeSDavid Herrmann }; 17043d782aeSDavid Herrmann 17143e5e7c6SDavid Herrmann int wiidebug_init(struct wiimote_data *wdata) 17243e5e7c6SDavid Herrmann { 17343e5e7c6SDavid Herrmann struct wiimote_debug *dbg; 17443e5e7c6SDavid Herrmann unsigned long flags; 17543d782aeSDavid Herrmann int ret = -ENOMEM; 17643e5e7c6SDavid Herrmann 17743e5e7c6SDavid Herrmann dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); 17843e5e7c6SDavid Herrmann if (!dbg) 17943e5e7c6SDavid Herrmann return -ENOMEM; 18043e5e7c6SDavid Herrmann 18143e5e7c6SDavid Herrmann dbg->wdata = wdata; 18243e5e7c6SDavid Herrmann 1831d3452c6SDavid Herrmann dbg->eeprom = debugfs_create_file("eeprom", S_IRUSR, 1841d3452c6SDavid Herrmann dbg->wdata->hdev->debug_dir, dbg, &wiidebug_eeprom_fops); 18543d782aeSDavid Herrmann if (!dbg->eeprom) 18643d782aeSDavid Herrmann goto err; 18743d782aeSDavid Herrmann 18843d782aeSDavid Herrmann dbg->drm = debugfs_create_file("drm", S_IRUSR, 18943d782aeSDavid Herrmann dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops); 19043d782aeSDavid Herrmann if (!dbg->drm) 19143d782aeSDavid Herrmann goto err_drm; 1921d3452c6SDavid Herrmann 19343e5e7c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 19443e5e7c6SDavid Herrmann wdata->debug = dbg; 19543e5e7c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 19643e5e7c6SDavid Herrmann 19743e5e7c6SDavid Herrmann return 0; 19843d782aeSDavid Herrmann 19943d782aeSDavid Herrmann err_drm: 20043d782aeSDavid Herrmann debugfs_remove(dbg->eeprom); 20143d782aeSDavid Herrmann err: 20243d782aeSDavid Herrmann kfree(dbg); 20343d782aeSDavid Herrmann return ret; 20443e5e7c6SDavid Herrmann } 20543e5e7c6SDavid Herrmann 20643e5e7c6SDavid Herrmann void wiidebug_deinit(struct wiimote_data *wdata) 20743e5e7c6SDavid Herrmann { 20843e5e7c6SDavid Herrmann struct wiimote_debug *dbg = wdata->debug; 20943e5e7c6SDavid Herrmann unsigned long flags; 21043e5e7c6SDavid Herrmann 21143e5e7c6SDavid Herrmann if (!dbg) 21243e5e7c6SDavid Herrmann return; 21343e5e7c6SDavid Herrmann 21443e5e7c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags); 21543e5e7c6SDavid Herrmann wdata->debug = NULL; 21643e5e7c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags); 21743e5e7c6SDavid Herrmann 21843d782aeSDavid Herrmann debugfs_remove(dbg->drm); 2191d3452c6SDavid Herrmann debugfs_remove(dbg->eeprom); 22043e5e7c6SDavid Herrmann kfree(dbg); 22143e5e7c6SDavid Herrmann } 222