/*
 * disable_mmc_usb.c
 *
 * Here's a sample kernel module showing how to disable mmc and still
 * enable SD.
 *
 * usage: insmod disable_mmc_usb.ko func=<func_name> func2=<func2_name>
 *
 * For more information on theory of operation of kretprobes, see
 * Documentation/kprobes.txt
 *
 * Build and insert the kernel module as done in the kprobe example.
 * You will see the trace data in /var/log/messages and on the console
 * whenever the probed function returns. (Some messages may be suppressed
 * if syslogd is configured to eliminate duplicate messages.)
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/ktime.h>
#include <linux/limits.h>
#include <linux/sched.h>
#include <linux/usb.h>

/*
 *  * The table of devices
 *   */
#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
		                    vendorName, productName, useProtocol, useTransport, \
		                    initFunction, flags) \
{\
	        USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
	        .driver_info = (flags) \
}

static const struct usb_device_id disable_realtek_cr_ids[] = {
#       include "unusual_realtek.h"
	        {}                      /* Terminating entry */
};

#undef UNUSUAL_DEV

static int set_mmc_flag;
static char func_name[NAME_MAX] = "storage_probe";
static char func2_name[NAME_MAX] = "usb_usual_ignore_device";
module_param_string(func, func_name, NAME_MAX, S_IRUGO);
MODULE_PARM_DESC(func, "Function to kretprobe; this module will disable the"
			"mmc card and still enable the sd one");
module_param_string(func2, func2_name, NAME_MAX, S_IRUGO);
MODULE_PARM_DESC(func, "Function to kretprobe; this module will disable the"
			"mmc card and still enable the sd one");

/* The main probe routine for standard devices */
static int jstorage_probe(struct usb_interface *intf,
			 const struct usb_device_id *id)
{
	struct usb_device *udev;
	unsigned vid, pid;
	int iter = 0;

	udev = interface_to_usbdev(intf);
	vid = le16_to_cpu(udev->descriptor.idVendor);
	pid = le16_to_cpu(udev->descriptor.idProduct);

	while(disable_realtek_cr_ids[iter].idVendor) {
		if(disable_realtek_cr_ids[iter].idVendor == vid && disable_realtek_cr_ids[iter].idProduct == pid) {
			printk("disable_mmc_usb: vid = 0x%x, pid = 0x%x\n", vid, pid);
			set_mmc_flag = 1;
			break;
		}
		iter++;
	}

	/* Always end with a call to jprobe_return(). */
	jprobe_return();
	return 0;
}

static int entry2_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
	return 0;
}

/*
 * Return-probe handler: Log the return value and duration. Duration may turn
 * out to be zero consistently, depending upon the granularity of time
 * accounting on the platform.
 */
static int ret2_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
	if (set_mmc_flag == 1) {
		regs->ax = -ENXIO;
		set_mmc_flag = 0;
	}

	return 0;
}

static struct jprobe my_jprobe = {
	.entry			= jstorage_probe,
};

static struct kretprobe my2_kretprobe = {
	.handler		= ret2_handler,
	.entry_handler		= entry2_handler,
	/* Probe up to 20 instances concurrently. */
	.maxactive		= 20,
};

static int __init kretprobe_init(void)
{
	int ret;

	my_jprobe.kp.symbol_name = func_name;
	my2_kretprobe.kp.symbol_name = func2_name;

	ret = register_jprobe(&my_jprobe);
	if (ret < 0) {
		printk(KERN_INFO "register_jprobe failed, returned %d\n", ret);
		return -1;
	}

	ret = register_kretprobe(&my2_kretprobe);
	if (ret < 0) {
		printk(KERN_INFO "register_kretprobe failed, returned %d\n",
				ret);
		return -1;
	}

	printk(KERN_INFO "Planted jprobe at %p, handler addr %p\n",
	       my_jprobe.kp.addr, my_jprobe.entry);
	printk(KERN_INFO "Planted return probe at %s: %p\n",
			my2_kretprobe.kp.symbol_name, my2_kretprobe.kp.addr);
	return 0;
}

static void __exit kretprobe_exit(void)
{
	unregister_kretprobe(&my2_kretprobe);

	unregister_jprobe(&my_jprobe);
	printk(KERN_INFO "jprobe at %p unregistered\n", my_jprobe.kp.addr);

	printk(KERN_INFO "kretprobe at %p unregistered\n",
			my2_kretprobe.kp.addr);

	/* nmissed > 0 suggests that maxactive was set too low. */
	printk(KERN_INFO "Missed probing %d instances of %s\n",
		my2_kretprobe.nmissed, my2_kretprobe.kp.symbol_name);
}

module_init(kretprobe_init)
module_exit(kretprobe_exit)
MODULE_LICENSE("GPL");
