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