1*2874c5fdSThomas 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
wiidebug_eeprom_read(struct file * f,char __user * u,size_t s,loff_t * off)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
wiidebug_drm_show(struct seq_file * f,void * p)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
wiidebug_drm_open(struct inode * i,struct file * f)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
wiidebug_drm_write(struct file * f,const char __user * u,size_t s,loff_t * off)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
wiidebug_init(struct wiimote_data * wdata)17243e5e7c6SDavid Herrmann int wiidebug_init(struct wiimote_data *wdata)
17343e5e7c6SDavid Herrmann {
17443e5e7c6SDavid Herrmann struct wiimote_debug *dbg;
17543e5e7c6SDavid Herrmann unsigned long flags;
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
18643d782aeSDavid Herrmann dbg->drm = debugfs_create_file("drm", S_IRUSR,
18743d782aeSDavid Herrmann dbg->wdata->hdev->debug_dir, dbg, &wiidebug_drm_fops);
1881d3452c6SDavid Herrmann
18943e5e7c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
19043e5e7c6SDavid Herrmann wdata->debug = dbg;
19143e5e7c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
19243e5e7c6SDavid Herrmann
19343e5e7c6SDavid Herrmann return 0;
19443d782aeSDavid Herrmann
19543e5e7c6SDavid Herrmann }
19643e5e7c6SDavid Herrmann
wiidebug_deinit(struct wiimote_data * wdata)19743e5e7c6SDavid Herrmann void wiidebug_deinit(struct wiimote_data *wdata)
19843e5e7c6SDavid Herrmann {
19943e5e7c6SDavid Herrmann struct wiimote_debug *dbg = wdata->debug;
20043e5e7c6SDavid Herrmann unsigned long flags;
20143e5e7c6SDavid Herrmann
20243e5e7c6SDavid Herrmann if (!dbg)
20343e5e7c6SDavid Herrmann return;
20443e5e7c6SDavid Herrmann
20543e5e7c6SDavid Herrmann spin_lock_irqsave(&wdata->state.lock, flags);
20643e5e7c6SDavid Herrmann wdata->debug = NULL;
20743e5e7c6SDavid Herrmann spin_unlock_irqrestore(&wdata->state.lock, flags);
20843e5e7c6SDavid Herrmann
20943d782aeSDavid Herrmann debugfs_remove(dbg->drm);
2101d3452c6SDavid Herrmann debugfs_remove(dbg->eeprom);
21143e5e7c6SDavid Herrmann kfree(dbg);
21243e5e7c6SDavid Herrmann }
213