lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Tue, 13 Nov 2012 20:46:37 +0200
From:	Constantine Shulyupin <const@...eLinux.com>
To:	linux-kernel@...r.kernel.org, celinux-dev@...ts.celinuxforum.org,
	gregkh@...uxfoundation.org
CC:	Tim Bird <tim.bird@...sony.com>, Baruch Siach <baruch@...s.co.il>,
	Thomas Petazzoni <thomas.petazzoni@...e-electrons.com>,
	Peter Korsgaard <jacmet@...site.dk>,
	Ezequiel Garcia <elezegarcia@...il.com>,
	Selim TEMUR <selimtemur@...il.com>,
	Jean-Christophe PLAGNIOL-VILLARD <plagnioj@...osoft.com>
Subject: [PATCH] LDT - Linux Driver Template

From: Constantine Shulyupin <const@...eLinux.com>

LDT is useful for Linux driver development beginners,
hackers and as starting point for a new drivers.
The driver uses following Linux facilities: module, platform driver,
file operations (read/write, mmap, ioctl, blocking and nonblocking mode, polling),
kfifo, completion, interrupt, tasklet, work, kthread, timer, simple misc device,
multiple char devices, Device Model, configfs, UART 0x3f8,
HW loopback, SW loopback, ftracer.

Signed-off-by: Constantine Shulyupin <const@...eLinux.com>
---
 samples/Kconfig             |   14 +
 samples/Makefile            |    5 +-
 samples/ltd/Makefile        |    1 +
 samples/ltd/ldt.c           |  764 +++++++++++++++++++++++++++++++++++++++++++
 samples/ltd/ldt_plat_dev.c  |   68 ++++
 tools/testing/ldt/Makefile  |    6 +
 tools/testing/ldt/ctracer.h |  380 +++++++++++++++++++++
 tools/testing/ldt/dio.c     |  362 ++++++++++++++++++++
 tools/testing/ldt/ldt-test  |  142 ++++++++
 9 files changed, 1740 insertions(+), 2 deletions(-)
 create mode 100644 samples/ltd/Makefile
 create mode 100644 samples/ltd/ldt.c
 create mode 100644 samples/ltd/ldt_plat_dev.c
 create mode 100644 tools/testing/ldt/Makefile
 create mode 100644 tools/testing/ldt/ctracer.h
 create mode 100644 tools/testing/ldt/dio.c
 create mode 100755 tools/testing/ldt/ldt-test

diff --git a/samples/Kconfig b/samples/Kconfig
index 7b6792a..2b93fd0 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -69,4 +69,18 @@ config SAMPLE_RPMSG_CLIENT
 	  to communicate with an AMP-configured remote processor over
 	  the rpmsg bus.
 
+config SAMPLE_DRIVER_TEMPLATE
+	tristate "LDT - Linux driver template"
+	help
+	  Template of Linux device driver. Useful for Linux driver
+	  development beginners, hackers and as starting point for a new drivers.
+	  The driver uses following Linux facilities: module, platform driver,
+	  file operations (read/write, mmap, ioctl, blocking and nonblocking mode, polling),
+	  kfifo, completion, interrupt, tasklet, work, kthread, timer, simple misc device,
+	  multiple char devices, Device Model, configfs, UART 0x3f8, HW loopback,
+	  SW loopback, ftracer.
+	  Usermode test script and utility are located in tools/testing/ldt/
+
+	  List of more samples and skeletons can be found at http://elinux.org/Device_drivers
+
 endif # SAMPLES
diff --git a/samples/Makefile b/samples/Makefile
index 5ef08bb..d4b1818 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -1,4 +1,5 @@
 # Makefile for Linux samples code
 
-obj-$(CONFIG_SAMPLES)	+= kobject/ kprobes/ tracepoints/ trace_events/ \
-			   hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/
+obj-$(CONFIG_SAMPLES)	+= kobject/ kprobes/ tracepoints/ trace_events/
+obj-$(CONFIG_SAMPLES)	+= hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/
+obj-$(CONFIG_SAMPLES)	+= ldt/
diff --git a/samples/ltd/Makefile b/samples/ltd/Makefile
new file mode 100644
index 0000000..efd691f
--- /dev/null
+++ b/samples/ltd/Makefile
@@ -0,0 +1 @@
+obj-$(SAMPLE_DRIVER_TEMPLATE)+= ldt.o ldt_plat_dev.o
diff --git a/samples/ltd/ldt.c b/samples/ltd/ldt.c
new file mode 100644
index 0000000..a4d2b3b
--- /dev/null
+++ b/samples/ltd/ldt.c
@@ -0,0 +1,764 @@
+/*
+ *	LDT - Linux Driver Template
+ *
+ *	Copyright (C) 2012 Constantine Shulyupin http://www.makelinux.net/
+ *
+ *	Dual BSD/GPL License
+ *
+ *
+ *	The driver demonstrates usage of following Linux facilities:
+ *
+ *	Linux kernel module
+ *	file_operations
+ *		read and write (UART)
+ *		blocking read
+ *		polling
+ *		mmap
+ *		ioctl
+ *	kfifo
+ *	completion
+ *	interrupt
+ *	tasklet
+ *	timer
+ *	work
+ *	kthread
+ *	simple single misc device file (miscdevice, misc_register)
+ *	multiple char device files (alloc_chrdev_region)
+ *	debugfs
+ *	platform_driver and platform_device in another module
+ *	simple UART driver on port 0x3f8 with IRQ 4
+ *	Device Model (class, device)
+ *	Power Management (dev_pm_ops)
+ *	Device Tree (of_device_id)
+ *
+ *	TODO:
+ *	linked list
+ *	private instance state struct
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/kfifo.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/serial_reg.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/mod_devicetable.h>
+
+#define ctracer_cut_path(fn) (fn[0] != '/' ? fn : (strrchr(fn, '/') + 1))
+#define __file__	ctracer_cut_path(__FILE__)
+
+/*
+ *	print_context prints execution context:
+ *	hard interrupt, soft interrupt or scheduled task
+ */
+
+#define print_context()	\
+	pr_debug("%s:%d %s %s 0x%x\n", __file__, __LINE__, __func__, \
+			(in_irq() ? "harirq" : current->comm), preempt_count());
+
+#define once(exp) do { \
+	static int _passed; if (!_passed) { exp; }; _passed = 1; } while (0)
+
+#define check(a) \
+	(ret = a, ((ret < 0) ? pr_warning("%s:%i %s FAIL\n\t%i=%s\n", \
+	__file__, __LINE__, __func__, ret, #a) : 0), ret)
+
+#define pr_debug_hex(h)	pr_debug("%s:%d %s %s = 0x%lX\n", \
+	__file__, __LINE__, __func__, #h, (long int)h)
+#define pr_debug_dec(d)	pr_debug("%s:%d %s %s = %ld\n", \
+	__file__, __LINE__, __func__, #d, (long int)d)
+
+#define pr_err_msg(m)	pr_err("%s:%d %s %s\n", __file__, __LINE__, __func__, m)
+
+
+static char ldt_name[] = KBUILD_MODNAME;
+static int bufsize = PFN_ALIGN(16 * 1024);
+static void *in_buf;
+static void *out_buf;
+static int uart_detected;
+void *port_ptr;
+
+static int port;
+module_param(port, int, 0);
+static int port_size;
+module_param(port_size, int, 0);
+
+static int irq;
+module_param(irq, int, 0);
+
+static int loopback;
+module_param(loopback, int, 0);
+
+static int isr_counter;
+static int ldt_work_counter;
+
+#define FIFO_SIZE 128		/* should be power of two */
+static DEFINE_KFIFO(in_fifo, char, FIFO_SIZE);
+static DEFINE_KFIFO(out_fifo, char, FIFO_SIZE);
+
+static DECLARE_WAIT_QUEUE_HEAD(ldt_readable);
+
+static spinlock_t fifo_lock;
+
+
+/*
+ *	ldt_received - called with data received from HW port
+ *	Called from tasklet, which is fired from ISR or timer
+ */
+
+static void ldt_received(void *data, int size)
+{
+	kfifo_in_spinlocked(&in_fifo, data, size, &fifo_lock);
+	wake_up_interruptible(&ldt_readable);
+}
+
+/*
+ *	ldt_send - send data to HW port or emulated SW loopback
+ */
+
+static void ldt_send(void *data, int size)
+{
+	if (uart_detected) {
+		if (ioread8(port_ptr + UART_LSR) & UART_LSR_THRE)
+			iowrite8(*(char *)data, port_ptr + UART_TX);
+		else
+			pr_err_msg("overflow");
+	} else
+		/* emulate loopback  */
+	if (loopback)
+		ldt_received(data, size);
+}
+
+/*
+ *	work
+ */
+
+static void ldt_work_func(struct work_struct *work)
+{
+	once(print_context());
+	ldt_work_counter++;
+}
+
+DECLARE_WORK(ldt_work, ldt_work_func);
+
+/*
+ *	tasklet
+ */
+
+static DECLARE_COMPLETION(ldt_complete);
+
+#define tx_ready()	(ioread8(port_ptr + UART_LSR) & UART_LSR_THRE)
+#define rx_ready()	(ioread8(port_ptr + UART_LSR) & UART_LSR_DR)
+
+static void ldt_tasklet_func(unsigned long d)
+{
+	char data_out, data_in;
+	once(print_context());
+	if (uart_detected) {
+		while (tx_ready() && kfifo_out_spinlocked(&out_fifo, &data_out, sizeof(data_out), &fifo_lock)) {
+			pr_debug_hex(ioread8(port_ptr + UART_LSR));
+			pr_debug_dec(data_out);
+			if (data_out >= 32)
+				pr_debug("data_out = '%c' ", data_out);
+			ldt_send(&data_out, sizeof(data_out));
+		}
+		while (rx_ready()) {
+			pr_debug_hex(ioread8(port_ptr + UART_LSR));
+			data_in = ioread8(port_ptr + UART_RX);
+			pr_debug_dec(data_in);
+			if (data_in >= 32)
+				pr_debug("data_out = '%c' ", data_in);
+			ldt_received(&data_in, sizeof(data_in));
+		}
+	} else {
+		while (kfifo_out_spinlocked(&out_fifo, &data_out, sizeof(data_out), &fifo_lock)) {
+			pr_debug_dec(data_out);
+			ldt_send(&data_out, sizeof(data_out));
+		}
+	}
+	schedule_work(&ldt_work);
+	complete(&ldt_complete);
+}
+
+static DECLARE_TASKLET(ldt_tasklet, ldt_tasklet_func, 0);
+
+/*
+ *	interrupt
+ */
+
+static irqreturn_t ldt_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/*
+	 *      UART interrupt is not fired in loopback mode,
+	 *      therefore fire ldt_tasklet from timer too
+	 */
+	once(print_context());
+	isr_counter++;
+	pr_debug_hex(ioread8(port_ptr + UART_FCR));
+	pr_debug_hex(ioread8(port_ptr + UART_IIR));
+	tasklet_schedule(&ldt_tasklet);
+	return IRQ_HANDLED;	/* our IRQ */
+}
+
+/*
+ *	timer
+ */
+
+static struct timer_list ldt_timer;
+static void ldt_timer_func(unsigned long data)
+{
+	/*
+	 *      this timer is used just to fire ldt_tasklet,
+	 *      when there is no interrupt in loopback mode
+	 */
+	if (loopback)
+		tasklet_schedule(&ldt_tasklet);
+	mod_timer(&ldt_timer, jiffies + HZ / 100);
+}
+
+static DEFINE_TIMER(ldt_timer, ldt_timer_func, 0, 0);
+
+/*
+ *	file_operations
+ */
+
+static int ldt_open(struct inode *inode, struct file *file)
+{
+	print_context();
+	pr_debug_dec(imajor(inode));
+	pr_debug_dec(iminor(inode));
+	pr_debug_hex(file->f_flags & O_NONBLOCK);
+	return 0;
+}
+
+static int ldt_release(struct inode *inode, struct file *file)
+{
+	print_context();
+	pr_debug_dec(imajor(inode));
+	pr_debug_dec(iminor(inode));
+	pr_debug_dec(isr_counter);
+	pr_debug_dec(ldt_work_counter);
+	return 0;
+}
+
+/*
+ *	read
+ */
+
+static DEFINE_MUTEX(read_lock);
+
+static ssize_t ldt_read(struct file *file, char __user * buf, size_t count, loff_t * ppos)
+{
+	int ret;
+	unsigned int copied;
+	if (kfifo_is_empty(&in_fifo)) {
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			goto exit;
+		} else {
+			pr_err_msg("waiting");
+			ret = wait_event_interruptible(ldt_readable, !kfifo_is_empty(&in_fifo));
+			if (ret == -ERESTARTSYS) {
+				pr_err_msg("interrupted");
+				ret = -EINTR;
+				goto exit;
+			}
+		}
+	}
+	if (mutex_lock_interruptible(&read_lock)) {
+		pr_err_msg("interrupted");
+		return -EINTR;
+	}
+	ret = kfifo_to_user(&in_fifo, buf, count, &copied);
+	mutex_unlock(&read_lock);
+exit:
+	return ret ? ret : copied;
+}
+
+/*
+ *	write
+ */
+
+static DEFINE_MUTEX(write_lock);
+
+static ssize_t ldt_write(struct file *file, const char __user * buf, size_t count, loff_t * ppos)
+{
+	int ret;
+	unsigned int copied;
+	/* TODO: wait_event_interruptible ... ldt_writeable */
+	if (mutex_lock_interruptible(&write_lock))
+		return -EINTR;
+	ret = kfifo_from_user(&out_fifo, buf, count, &copied);
+	mutex_unlock(&write_lock);
+	tasklet_schedule(&ldt_tasklet);
+	return ret ? ret : copied;
+}
+
+/*
+ *	polling
+ */
+
+static unsigned int ldt_poll(struct file *file, poll_table * pt)
+{
+	unsigned int mask = 0;
+	poll_wait(file, &ldt_readable, pt);
+	/*poll_wait(file, ldt_writeable, pt); TODO */
+
+	if (!kfifo_is_empty(&in_fifo))
+		mask |= POLLIN | POLLRDNORM;
+	mask |= POLLOUT | POLLWRNORM;
+#if 0
+	mask |= POLLHUP;	/* on output eof */
+	mask |= POLLERR;	/* on output error */
+#endif
+	pr_debug_hex(mask);
+	return mask;
+}
+
+/*
+ *	pages_flag - set or clear a flag for sequence of pages
+ *
+ *	more generic solution instead SetPageReserved, ClearPageReserved etc
+ */
+
+void pages_flag(struct page *page, int pages, int mask, int value)
+{
+	for (; pages; pages--, page++)
+		if (value)
+			__set_bit(mask, &page->flags);
+		else
+			__clear_bit(mask, &page->flags);
+}
+
+/*
+ *	mmap
+ */
+static int ldt_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	void *buf = NULL;
+	if (vma->vm_flags & VM_WRITE)
+		buf = in_buf;
+	else if (vma->vm_flags & VM_READ)
+		buf = out_buf;
+	if (!buf)
+		return -EINVAL;
+	if (remap_pfn_range(vma, vma->vm_start, virt_to_phys(buf) >> PAGE_SHIFT,
+			    vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
+		pr_err_msg("remap_pfn_range failed");
+		return -EAGAIN;
+	}
+	return 0;
+}
+
+/*
+ *	ioctl
+ */
+
+#define trace_ioctl(nr) printk("ioctl=(%c%c %c #%i %i)\n", \
+	(_IOC_READ & _IOC_DIR(nr)) ? 'r' : ' ', (_IOC_WRITE & _IOC_DIR(nr)) ? 'w' : ' ', \
+	_IOC_TYPE(nr), _IOC_NR(nr), _IOC_SIZE(nr))
+
+static long ldt_ioctl(struct file *f, unsigned int cmnd, unsigned long arg)
+{
+	void __user *user = (void *)arg;
+	pr_debug_hex(cmnd);
+	pr_debug_hex(arg);
+	trace_ioctl(cmnd);
+	if (_IOC_DIR(cmnd) == _IOC_WRITE) {
+		copy_from_user(in_buf, user, _IOC_SIZE(cmnd));
+		memcpy(out_buf, in_buf, bufsize);
+		memset(in_buf, 0, bufsize);
+	}
+	if (_IOC_DIR(cmnd) == _IOC_READ) {
+		copy_to_user(user, out_buf, _IOC_SIZE(cmnd));
+		memset(out_buf, 0, bufsize);
+	}
+	switch (_IOC_TYPE(cmnd)) {
+	case 'A':
+		switch (_IOC_NR(cmnd)) {
+		case 0:
+			break;
+		}
+		break;
+	}
+	return 0;
+}
+
+static const struct file_operations ldt_fops = {
+	.owner = THIS_MODULE,
+	.open = ldt_open,
+	.release = ldt_release,
+	.read = ldt_read,
+	.write = ldt_write,
+	.poll = ldt_poll,
+	.mmap = ldt_mmap,
+	.unlocked_ioctl = ldt_ioctl,
+};
+
+#ifdef USE_MISCDEV
+/*
+ *	use miscdevice for single instance device
+ */
+static struct miscdevice ldt_miscdev = {
+	MISC_DYNAMIC_MINOR,
+	ldt_name,
+	&ldt_fops,
+};
+#else
+/*
+ *	used cdev and device for multiple instances device
+ */
+
+static int devs = 8;
+module_param(devs, int, 0);
+
+static struct cdev ldt_cdev;
+static struct class *ldt_class;
+static struct device *ldt_dev;
+#if 0
+static char *ldt_devnode(struct device *dev, umode_t * mode)
+{
+	if (mode)
+		*mode = S_IRUGO | S_IWUGO;
+	/* *mode = 0666; */
+	return NULL;
+}
+#endif
+#endif
+
+/*
+ *	kthread
+ */
+
+static int ldt_thread_sub(void *data)
+{
+	int ret = 0;
+	/*
+	   perform here a useful work in task context
+	 */
+	return ret;
+}
+
+static int ldt_thread(void *data)
+{
+	int ret = 0;
+	print_context();
+	allow_signal(SIGINT);
+	while (!kthread_should_stop()) {
+		ret = wait_for_completion_interruptible(&ldt_complete);
+		if (ret == -ERESTARTSYS) {
+			pr_err_msg("interrupted");
+			ret = -EINTR;
+			break;
+		}
+		ret = ldt_thread_sub(data);
+	}
+	return ret;
+}
+
+/*
+ *	UART
+ */
+
+static struct resource *port_r;
+
+static int uart_probe(void)
+{
+	int ret = 0;
+	if (port) {
+		port_ptr = ioport_map(port, port_size);
+		pr_debug_hex(port_ptr);
+		port_r = request_region(port, port_size, ldt_name);
+		pr_debug_hex(port_r);
+		/* ignore error */
+	}
+	if (irq) {
+		ret = check(request_irq(irq, (void *)ldt_isr, IRQF_SHARED, ldt_name, THIS_MODULE));
+		iowrite8(UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_LOOP, port_ptr + UART_MCR);
+		uart_detected = (ioread8(port_ptr + UART_MSR) & 0xF0) == (UART_MSR_DCD | UART_MSR_CTS);
+		pr_debug_hex(ioread8(port_ptr + UART_MSR));
+
+		if (uart_detected) {
+			/*iowrite8(UART_IER_MSI | UART_IER_THRI |  UART_IER_RDI | UART_IER_RLSI, port_ptr + UART_IER); */
+			iowrite8(UART_IER_RDI | UART_IER_RLSI | UART_IER_THRI, port_ptr + UART_IER);
+			iowrite8(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, port_ptr + UART_MCR);
+			iowrite8(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT, port_ptr + UART_FCR);
+			pr_debug_dec(loopback);
+			if (loopback)
+				iowrite8(ioread8(port_ptr + UART_MCR) | UART_MCR_LOOP, port_ptr + UART_MCR);
+		}
+		if (!uart_detected && loopback) {
+			pr_warn("Emulating loopback in software\n");
+			ret = -ENODEV;
+		}
+	}
+	pr_debug_hex(uart_detected);
+	pr_debug_hex(ioread8(port_ptr + UART_IER));
+	pr_debug_hex(ioread8(port_ptr + UART_IIR));
+	pr_debug_hex(ioread8(port_ptr + UART_FCR));
+	pr_debug_hex(ioread8(port_ptr + UART_LCR));
+	pr_debug_hex(ioread8(port_ptr + UART_MCR));
+	pr_debug_hex(ioread8(port_ptr + UART_LSR));
+	pr_debug_hex(ioread8(port_ptr + UART_MSR));
+	return ret;
+}
+
+static struct task_struct *thread;
+static struct dentry *debugfs;
+static int major;
+
+int chrdev_region_init(char *dev_name)
+{
+	int ret;
+	int d;
+	dev_t devid;
+	devid = MKDEV(major, 0);
+	ret = check(alloc_chrdev_region(&devid, 0, devs, dev_name));
+	major = MAJOR(devid);
+	pr_debug_dec(major);
+	cdev_init(&ldt_cdev, &ldt_fops);
+	check(cdev_add(&ldt_cdev, MKDEV(major, 0), devs));
+	ldt_class = class_create(THIS_MODULE, dev_name);
+	/* ldt_class->devnode = ldt_devnode; */
+	ldt_dev = device_create(ldt_class, NULL, devid, NULL, "%s", dev_name);
+	for (d = 1; d < devs; d++)
+		device_create(ldt_class, NULL, MKDEV(major, d), NULL, "%s%d", dev_name, d);
+	pr_debug_dec(IS_ERR(ldt_dev));
+	pr_debug_hex(ldt_dev);
+	return major;
+}
+
+/*
+ *	ldt_probe - main initialization function
+ */
+
+static __devinit int ldt_probe(struct platform_device *pdev)
+{
+	int ret;
+	char *data = NULL;
+	struct resource *r;
+	print_context();
+	printk(KERN_DEBUG"%s %s %s", ldt_name, __DATE__, __TIME__);
+	printk(KERN_DEBUG"pdev = %p ", pdev);
+	pr_debug_dec(irq);
+	pr_debug_dec(bufsize);
+	in_buf = alloc_pages_exact(bufsize, GFP_KERNEL | __GFP_ZERO);
+	if (!in_buf) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+	pages_flag(virt_to_page(in_buf), PFN_UP(bufsize), PG_reserved, 1);
+	out_buf = alloc_pages_exact(bufsize, GFP_KERNEL | __GFP_ZERO);
+	if (!out_buf) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+	pages_flag(virt_to_page(out_buf), PFN_UP(bufsize), PG_reserved, 1);
+	if (pdev) {
+		dev_dbg(&pdev->dev, "%s:%d %s attaching driver\n", __file__, __LINE__, __func__);
+		pr_debug_hex(pdev->dev.of_node);
+#ifdef CONFIG_OF_DEVICE
+		check(of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev));
+#endif
+		data = pdev->dev.platform_data;
+		printk("%p %s\n", data, data);
+		if (!irq)
+			irq = platform_get_irq(pdev, 0);
+		r = platform_get_resource(pdev, IORESOURCE_IO, 0);
+		if (r && !port)
+			port = r->start;
+
+		if (r && !port_size)
+			port_size = resource_size(r);
+	}
+	isr_counter = 0;
+	uart_probe();
+	/* proc_create(ldt_name, 0, NULL, &ldt_fops); depricated */
+	mod_timer(&ldt_timer, jiffies + HZ / 10);
+	thread = kthread_run(ldt_thread, NULL, "%s", ldt_name);
+	if (IS_ERR(thread)) {
+		ret = PTR_ERR(thread);
+		if (ret)
+			goto exit;
+	}
+	debugfs = debugfs_create_file(ldt_name, S_IRUGO, NULL, NULL, &ldt_fops);
+#ifdef USE_MISCDEV
+	ret = check(misc_register(&ldt_miscdev));
+	if (ret < 0)
+		goto exit;
+	pr_debug_dec(ldt_miscdev.minor);
+#else
+	chrdev_region_init(ldt_name);
+#endif
+exit:
+	pr_debug_dec(ret);
+	return ret;
+}
+
+/*
+ *	ldt_remove - main clean up function
+ */
+
+static int __devexit ldt_remove(struct platform_device *pdev)
+{
+	int d;
+	if (pdev)
+		dev_dbg(&pdev->dev, "%s:%d %s detaching driver\n", __file__, __LINE__, __func__);
+	/* remove_proc_entry(ldt_name, NULL); depricated */
+	if (debugfs)
+		debugfs_remove(debugfs);
+#ifdef USE_MISCDEV
+	misc_deregister(&ldt_miscdev);
+#else
+	for (d = 0; d < devs; d++)
+		device_destroy(ldt_class, MKDEV(major, d));
+	class_destroy(ldt_class);
+	cdev_del(&ldt_cdev);
+	unregister_chrdev_region(MKDEV(major, 0), devs);
+#endif
+	if (!IS_ERR_OR_NULL(thread)) {
+		send_sig(SIGINT, thread, 1);
+		kthread_stop(thread);
+	}
+	del_timer(&ldt_timer);
+	if (port_r)
+		release_region(port, port_size);
+	if (irq) {
+		if (uart_detected) {
+			iowrite8(0, port_ptr + UART_IER);
+			iowrite8(0, port_ptr + UART_FCR);
+			iowrite8(0, port_ptr + UART_MCR);
+			ioread8(port_ptr + UART_RX);
+		}
+		free_irq(irq, THIS_MODULE);
+	}
+	tasklet_kill(&ldt_tasklet);
+	if (in_buf) {
+		pages_flag(virt_to_page(in_buf), PFN_UP(bufsize), PG_reserved, 0);
+		free_pages_exact(in_buf, bufsize);
+	}
+	if (out_buf) {
+		pages_flag(virt_to_page(out_buf), PFN_UP(bufsize), PG_reserved, 0);
+		free_pages_exact(out_buf, bufsize);
+	}
+	pr_debug_dec(isr_counter);
+	pr_debug_dec(ldt_work_counter);
+	if (port_ptr)
+		ioport_unmap(port_ptr);
+	return 0;
+}
+
+#ifdef USE_PLATFORM_DEVICE
+
+/*
+ * Following code requires platform_device (ldt_plat_dev.*) to work
+ */
+
+#ifdef CONFIG_PM
+
+static int ldt_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int ldt_resume(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops ldt_pm = {
+	.suspend = ldt_suspend,
+	.resume = ldt_resume,
+};
+
+#define ldt_pm_ops (&ldt_pm)
+#else
+#define ldt_pm_ops NULL
+#endif
+
+static const struct of_device_id ldt_of_match[] = {
+	{.compatible = "linux-driver-template",},
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, ldt_of_match);
+
+static struct platform_driver ldt_driver = {
+	.driver = {
+		   .name = "ldt_device_name",
+		   .owner = THIS_MODULE,
+		   .pm = ldt_pm_ops,
+		   .of_match_table = of_match_ptr(ldt_of_match),
+		   },
+	.probe = ldt_probe,
+	.remove = __devexit_p(ldt_remove),
+};
+
+#ifdef module_platform_driver
+module_platform_driver(ldt_driver);
+#else
+
+/*
+ *	for Linux kernel releases before v3.1-12
+ *	without macro module_platform_driver
+ */
+
+static int ldt_init(void)
+{
+	int ret = 0;
+	ret = platform_driver_register(&ldt_driver);
+	return ret;
+}
+
+static void ldt_exit(void)
+{
+	platform_driver_unregister(&ldt_driver);
+}
+
+module_init(ldt_init);
+module_exit(ldt_exit);
+#endif /* module_platform_driver */
+
+#else /* !USE_PLATFORM_DEVICE */
+
+/*
+ *	Standalone module initialization to run without platform_device
+ */
+
+static int ldt_init(void)
+{
+	int ret = 0;
+	/*
+	 *      Call probe function directly,
+	 *      bypassing platform_device infrastructure
+	 */
+	ret = ldt_probe(NULL);
+	return ret;
+}
+
+static void ldt_exit(void)
+{
+	int res;
+	res = ldt_remove(NULL);
+}
+
+module_init(ldt_init);
+module_exit(ldt_exit);
+#endif
+
+MODULE_DESCRIPTION("LDT - Linux Driver Template");
+MODULE_AUTHOR("Constantine Shulyupin <const@...elinux.net>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/samples/ltd/ldt_plat_dev.c b/samples/ltd/ldt_plat_dev.c
new file mode 100644
index 0000000..cd45057
--- /dev/null
+++ b/samples/ltd/ldt_plat_dev.c
@@ -0,0 +1,68 @@
+/*
+ *	LDT - Linux Driver Template
+ *
+ *	Copyright (C) 2012 Constantine Shulyupin  http://www.makelinux.net/
+ *
+ *	Dual BSD/GPL License
+ *
+ *	platform_device template driver
+ *
+ *	uses
+ *
+ *	platform_data
+ *	resources
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include "tracing.h"
+
+static struct resource ldt_resource[] = {
+	{
+	 .flags = IORESOURCE_IO,
+	 .start = 0x3f8,
+	 .end = 0x3ff,
+	 },
+	{
+	 .flags = IORESOURCE_IRQ,
+	 .start = 4,
+	 .end = 4,
+	 },
+	{
+	 .flags = IORESOURCE_MEM,
+	 .start = 0,
+	 .end = 0,
+	 },
+};
+
+void ldt_dev_release(struct device *dev)
+{
+_entry:;
+}
+
+static struct platform_device ldt_platform_device = {
+	.name = "ldt_device_name",
+	.id = 0,
+	.num_resources = ARRAY_SIZE(ldt_resource),
+	.resource = ldt_resource,
+	.dev.platform_data = "test data",
+	.dev.release = ldt_dev_release,
+};
+
+static int ldt_plat_dev_init(void)
+{
+	return platform_device_register(&ldt_platform_device);
+}
+
+static void ldt_plat_dev_exit(void)
+{
+	platform_device_unregister(&ldt_platform_device);
+}
+
+module_init(ldt_plat_dev_init);
+module_exit(ldt_plat_dev_exit);
+
+MODULE_DESCRIPTION("LDT - Linux Driver Template: platform_device");
+MODULE_AUTHOR("Constantine Shulyupin <const@...elinux.net>");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/tools/testing/ldt/Makefile b/tools/testing/ldt/Makefile
new file mode 100644
index 0000000..0bfefd6
--- /dev/null
+++ b/tools/testing/ldt/Makefile
@@ -0,0 +1,6 @@
+all: dio
+
+dio: CPPFLAGS+=-g -D CTRACER_ON
+
+clean:
+	rm -rf dio *.o  dio *.tmp *.log
diff --git a/tools/testing/ldt/ctracer.h b/tools/testing/ldt/ctracer.h
new file mode 100644
index 0000000..45c5d48
--- /dev/null
+++ b/tools/testing/ldt/ctracer.h
@@ -0,0 +1,380 @@
+/*
+	Tracing utility for C
+
+	implemented in single h-file
+
+	Copyright (C) 2012 Constantine Shulyupin  http://www.makelinux.net/
+
+	Dual BSD/GPL License
+*/
+
+#if 0
+/* Optional configuration flags: */
+#define TRACE_TIME
+#define TRACE_MALLOC
+#define TRACE_LINUX_MEMORY_ON
+#endif
+/*
+	VI command to include label _entry to each function start for tracing
+	:%s/) *\n{ *$/)\r{\t_entry:;/
+ */
+
+#ifndef __ASSEMBLY__
+
+#ifndef CTRACER_H_INCLUDED
+#define CTRACER_H_INCLUDED
+extern __thread int ret;
+
+#define multistatement(ms)	ms	/* trick to bypass checkpatch.pl error */
+/*
+#define _entry multistatement(trllog(); goto _entry; _entry)
+*/
+#define _entry multistatement(_trace_enter_exit_(); trln(); goto _entry_second; _entry_second)
+/*
+#define _entry once(trl()); goto _entry; _entry
+#define return trlm("} "); return
+*/
+
+#define do_statement(a)	do { a } while (0)
+
+/*
+	trace variables: integer, hex, string, pointer, float, time value, with and w/o new line
+	macro with '_' doesn't prints new line
+	notation:
+	tr = trace
+	v<letter> = printf Variable in specified format (d, x, f, s, etc)
+*/
+
+#define trla(fmt, args...) tracef("%s:%i %s "fmt, __file__, __LINE__, __func__, ## args)
+#define trv(t, v) tracef(#v" = %"t EOL, v)
+#define trv_(t, v) tracef(#v" = %"t" ", v)
+#define trvd(d) tracef(#d" = %ld"EOL, (long int)d)
+#define trvd_(d) tracef(#d" = %ld ", (long int)d)
+#define trvx_(x) tracef(#x" = 0x%x ", (int)x)
+#define trvx(x) tracef(#x" = 0x%x"EOL, (int)x)
+#define trvlx(x) tracef(#x" = %#llx"EOL, (int)x)
+#define trvX(x) tracef(#x" = %#X"EOL, (int)x)
+#define trvf(f) tracef(#f" = %f"EOL, f)
+#define trvf_(f) tracef(#f" = %f ", f)
+#define trvtv_(tv) tracef(#tv" = %u.%06u ", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec)
+#define trvtv(tv) tracef(#tv" = %u.%06u"EOL, (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec)
+#define trvs(s) tracef(#s" = \"%s\""EOL, s)
+#define trvs_(s) tracef(#s" = \"%s\" ", s)
+#define trvp(p) tracef(#p" = %08x"EOL, (unsigned)p)
+#define trvp_(p) tracef(#p" = %08x ", (unsigned)p)
+#define trvdn(d, n) {int i; tracef("%s", #d"[]="); for (i = 0; i < n; i++) tracef("%d:%d,", i, (*((int *)d+i))); tracef(EOL); }
+#define trvxn(d, n) {int i; tracef("%s", #d"[]="); for (i = 0; i < n; i++) tracef("%04x,", (*((int *)d+i))); tracef(EOL); }
+#define trvdr(record) trvdn(&record, sizeof(record)/sizeof(int));
+#define trvxr(record) trvxn(&record, sizeof(record)/sizeof(int));
+
+/* trvdnz - TRace Digital Variable, if Not Zero */
+#define trvdnz(d) { if (d) tracef(#d" = %d"EOL, (int)d); }
+#define trace_call(a) do { trla("calling %s {\n", #a); a; tracef("} done\n"); } while (0)
+
+/* trlm - TRace Location, with Message */
+#define trlm(m) tracef(SOL"%s:%i %s %s"EOL, __file__, __LINE__, __func__, m)
+#define trlm_(m) tracef(SOL"%s:%i %s %s ", __file__, __LINE__, __func__, m)
+#define trl() do { trace_time(); trlm(""); } while (0)
+#define trl_() tracef(SOL"%s:%i %s ", __file__, __LINE__, __func__)
+#define trln() tracef(EOL)
+
+#define trl_in() do_statement(trace_time(); trlm("{");)
+#define trl_out() do_statement(trace_time(); trlm("}");)
+#define empty_statement() do { } while (0)
+
+#define trace_mem(P, N) \
+	 IFTRACE({ int i = 0; tracef("%s=", #P); for (; i < (int)(N) ; i++) \
+{ if (i && (!(i % 16))) tracef("%i:", i); \
+tracef("%02x ", 0xFF & *((char *)((void *)(P))+i)); \
+if (!((i+1) % 4)) \
+	tracef(" "); \
+if (!((i+1) % 16)) \
+	tracef(EOL); \
+}; tracef(EOL); })
+
+#define trace_mem_int_list(P, N) \
+IFTRACE({ int i = 0; for (; i < (int)(N); i += sizeof(int)) \
+{ tracef("%i, ", *(int *)((void *)(P)+i)); \
+}; })
+
+#define trace_mem_int(P, N) \
+IFTRACE({ int i = 0; for (; i < (int)(N) ; i += sizeof(int)) \
+{ if (i && (!(i % 16))) tracef("%i:", i); \
+tracef("%x ", *(int *)((void *)(P)+i)); \
+if (!((i+1) % 64)) \
+	tracef(EOL); \
+}; tracef(EOL); })
+
+#define trace_ioctl(nr) tracef("ioctl=(%c%c %c #%i %i)\n", \
+	(_IOC_READ & _IOC_DIR(nr)) ? 'r' : ' ', (_IOC_WRITE & _IOC_DIR(nr)) ? 'w' : ' ', \
+	_IOC_TYPE(nr), _IOC_NR(nr), _IOC_SIZE(nr))
+
+#define trace_ioctl_(nr) tracef("ioctl=(%i %i %i %i)", _IOC_DIR(nr), _IOC_TYPE(nr), _IOC_NR(nr), _IOC_SIZE(nr))
+
+#define chkz(a) \
+(p = a,\
+	((!p) ? tracef("%s %i %s FAIL %i = %s\n", __FILE__, __LINE__, __func__, p, #a) : 0),\
+	p)
+
+#define chkn(a) \
+(ret = a,\
+	((ret < 0) ? tracef("%s:%i %s FAIL\n\t%i=%s\n", __FILE__, __LINE__, __func__, ret, #a)\
+	 : 0), ret)
+
+#define chkne(a) \
+(/* tracef("calling  %s\n",#a), */ \
+	ret = a,\
+	((ret < 0) ? tracef("%s:%i %s FAIL errno = %i \"%s\" %i = %s\n", __FILE__, __LINE__, __func__, errno, strerror(errno), ret, #a)\
+	 : 0), ret)
+
+#define chkn2(a) \
+(ret = a,\
+	((ret < 0) ? tracef("%s %i %s FAIL %i = %s\n", __FILE__, __LINE__, __func__, ret, #a)\
+	 : tracef("%s %i %s %i = %s\n", __FILE__, __LINE__, __func__, ret, #a)),\
+	ret)
+
+#define once(exp) do_statement( \
+	static int _passed; if (!_passed) {exp; }; _passed = 1;)
+
+
+#ifdef CTRACER_OFF		/* force no tracing */
+#undef CTRACER_ON
+#endif
+
+#ifdef CTRACER_ON
+#define IFTRACE(x) x
+
+#ifdef __KERNEL__
+#undef TRACE_TIME
+#include <linux/kernel.h>
+#include <linux/printk.h>
+
+#ifdef TRACE_LINUX_MEMORY_ON
+#include <linux/mmzone.h>
+
+extern int free_pages_prev;
+#define trace_linux_mem() do { \
+extern zone_t *zone_table[MAX_NR_ZONES*MAX_NR_NODES]; \
+int mem_change = zone_table[0]->free_pages - free_pages_prev; \
+if (mem_change) { \
+	trl_(); trvi_(mem_change); trvi(zone_table[0]->free_pages); } \
+	free_pages_prev = zone_table[0]->free_pages; \
+} while (0)
+#endif
+
+#define SOL KERN_DEBUG
+#define tracef(fmt, args...) printk(fmt, ##args)
+
+#else /* !__KERNEL__ */
+/* CTRACER_ON and not __KERNEL__ */
+#include <stdio.h>
+
+#define tracef(args...) fprintf(stderr, ##args)
+
+#if 0
+#include <signal.h>
+#define BP {trl(); kill(0, SIGTRAP); }
+#define BP kill(0, SIGTRAP)
+#endif
+
+#ifndef tracef
+#define tracef printf
+#endif
+#endif /* !__KERNEL__ */
+
+#ifndef _hweight32
+static inline unsigned int _hweight32(unsigned int w)
+{	/* from kernel */
+	w -= (w >> 1) & 0x55555555;
+	w = (w & 0x33333333) + ((w >> 2) & 0x33333333);
+	w = (w + (w >> 4)) & 0x0f0f0f0f;
+	return (w * 0x01010101) >> 24;
+}
+
+#define _hweight32 _hweight32
+#endif
+#define trllog(args ...) \
+do {  \
+	static int num;			\
+	if (_hweight32(num) < 2) {		\
+		trla("#%d\n", (int)num);	\
+	}	num++;				\
+} while (0)
+
+#define trlnum(n, args ...) \
+do {  \
+	static int num;			\
+	if (num < n) {		\
+		trl_();				\
+		tracef("#0x%x", (int)num);	\
+		args;				\
+		trln();			\
+	}	num++;				\
+} while (0)
+
+#define trleach(n, args ...) \
+do {  \
+	static int num;			\
+	if (!(num % n)) {	\
+		trl_();				\
+		trvi_(num);		\
+		args;				\
+		trln();			\
+	}	num++;				\
+} while (0)
+
+#else /* !CTRACER_ON */
+#define trllog(args ...)
+
+static inline int empty_function(void)
+{
+	return 0;
+}
+
+#define IFTRACE(x) empty_statement()
+#define trace_linux_mem() empty_statement()
+#define tracef(fmt, args...) empty_function()
+#define stack_trace() empty_statement()
+
+#endif /* _TARCE */
+
+#ifndef SOL
+#define SOL ""
+#endif
+#define EOL "\n" /* for console */
+
+#ifdef MODULE
+/* omit full absolute path for modules */
+extern char *strrchr(const char *s, int c);
+#define ctracer_cut_path(fn) (fn[0] != '/' ? fn : (strrchr(fn, '/') + 1))
+#define __file__	ctracer_cut_path(__FILE__)
+#else
+#define __file__	__FILE__
+#endif
+
+#ifdef TRACE_MALLOC
+static int malloc_count;
+static void *malloc_trace;
+#endif
+#ifdef TRACE_MALLOC
+
+#define malloc(s) \
+	(trla("malloc #%i %p %i\n", ++malloc_count, malloc_trace = malloc(s), s),\
+	malloc_trace)
+
+#define free(p) { free(p); trla("free   #%i %p\n", malloc_count--, (void *)p); }
+
+#define strdup(s) \
+	(trla("strdup #%i %p\n", ++malloc_count, malloc_trace = (void *)strdup(s)),\
+	(char *)malloc_trace)
+
+#endif
+
+#ifdef TRACE_TIME
+
+#include <time.h>
+#include <sys/time.h>
+
+#ifndef trace_time_defined
+#define trace_time_defined
+
+void trace_time();
+/*
+extern double time_prev_f;
+void static inline trace_time()
+{
+	time_t time_cur;
+	double time_cur_f;
+	time(&time_cur);
+	struct timeval tv;
+	struct timezone tz;
+	struct tm* time_tm;
+	gettimeofday(&tv, &tz);
+	time_tm = localtime(&time_cur);
+	time_cur = tv.tv_sec;
+	time_cur_f = 0.000001 * tv.tv_usec + time_cur;
+	double passed = time_cur_f - time_prev_f;
+	if (passed > 0.001)
+	{
+		tracef("time=%04d-%02d-%02d %02d:%02d:%02d %02d +%1.4f s\n",
+				time_tm->tm_year+1900, time_tm->tm_mon+1, time_tm->tm_mday,
+				time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec, (int)tv.tv_usec,
+				passed);
+		time_prev_f = time_cur_f;
+	}
+}
+*/
+#endif
+
+#else
+#define trace_time() empty_statement()
+#endif
+
+#ifdef __GLIBC__XX
+#include <execinfo.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef stack_trace
+#undef stack_trace
+#endif
+#ifndef stack_trace_difined
+#define stack_trace_difined
+/* only once */
+static inline void stack_trace(void)
+{
+	void *array[5];
+	size_t size;
+	char **strings;
+	size_t i;
+	size = backtrace(array, sizeof(array) / sizeof(array[0]));
+	strings = backtrace_symbols(array, size);
+	tracef("Stack:\n");
+
+	for (i = 0; i < size; i++) {
+		if (!array[i])
+			break;
+		tracef("%i %p %s\n", i, array[i], strings[i]);
+	}
+	free(strings);
+}
+#endif
+#endif /* __GLIBC__ */
+
+/* see also nr_free_pages */
+#define freeram() { \
+	static unsigned int last; struct sysinfo i; si_meminfo(&i); trl_(); \
+	int d = last-i.freeram; int used = i.totalram-i.freeram; \
+	trvi_(i.freeram); trvi_(used);  trvi(d); \
+	last = i.freeram; }
+
+extern int sprint_symbol_no_offset(char *buffer, unsigned long address);
+
+static inline void __on_cleanup(char *s[])
+{
+#ifdef __KERNEL__
+	printk(KERN_DEBUG"%s", *s);
+#else
+	fputs(*s, stderr);
+#endif
+}
+
+#if !defined(__KERNEL__) || defined(MODULE)
+static inline int lookup_symbol_name(unsigned long addr, char *symbol)
+{
+	return sprintf(symbol, "%lx", addr);
+}
+#else
+int lookup_symbol_name(unsigned long addr, char *symname);
+#endif
+
+#define _trace_enter_exit_() char _caller[200]; \
+	lookup_symbol_name((unsigned long)__builtin_return_address(0), _caller); \
+	char __attribute__((cleanup(__on_cleanup))) *_s; \
+	char _ret_msg[100]; _s = _ret_msg; \
+	snprintf(_ret_msg, sizeof(_ret_msg), "%s < %s }\n", _caller, __func__); \
+	tracef(SOL"%s > %s { @ %s:%d", _caller, __func__, __file__, __LINE__);
+
+/*__END_DECLS */
+#endif /* CTRACER_H_INCLUDED */
+#endif /* __ASSEMBLY__ */
diff --git a/tools/testing/ldt/dio.c b/tools/testing/ldt/dio.c
new file mode 100644
index 0000000..79f3886
--- /dev/null
+++ b/tools/testing/ldt/dio.c
@@ -0,0 +1,362 @@
+/*
+ *	DIO - Device Input/Output utility for testing device drivers
+ *
+ *	stdin/stdout <--> dio <--> mmap, ioctl, read/write
+ *
+ *	Copyright (C) 2012 Constantine Shulyupin <const@...elinux.net>
+ *	http://www.makelinux.net/
+ *
+ *	Dual BSD/GPL License
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/poll.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/user.h>
+#include <time.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <linux/ioctl.h>
+#include "ctracer.h"
+
+static enum io_type {
+	file_io,
+	mmap_io,
+	ioctl_io
+} io_type;
+
+static void *inbuf, *outbuf;
+static void *mm;
+static void *mem;
+static int buf_size;
+static int offset;
+static char *dev_name;
+static int ignore_eof;
+static int ioctl_num;
+static int loops;
+static int delay;
+static char ioctl_type = 'A';
+__thread int ret;
+static int ro, wo; /* read only, write only*/
+
+/*
+#define VERBOSE
+*/
+
+int output(int dev, void *buf, int size)
+{
+#ifdef VERBOSE
+_entry:
+	trl_();
+	trvd(size);
+#endif
+	ret = 0;
+	if (dev < 0 || ro)
+		return 0;
+	switch (io_type) {
+	case mmap_io:
+		memcpy(mem, buf, size);
+		ret = size;
+		break;
+	case ioctl_io:
+		ioctl(dev, _IOC(_IOC_WRITE, ioctl_type, ioctl_num, size & _IOC_SIZEMASK), buf);
+		break;
+	case file_io:
+	default:
+		ret = write(dev, buf, size);
+	}
+	return ret;
+}
+
+int input(int dev, void *buf, int size)
+{
+	ret = 0;
+#ifdef VERBOSE
+_entry:
+	trl_();
+	trvd(size);
+#endif
+	if (dev < 0 || wo)
+		return 0;
+	switch (io_type) {
+	case mmap_io:
+		memcpy(buf, mem, size);
+		ret = size;
+		break;
+	case ioctl_io:
+		ioctl(dev, _IOC(_IOC_READ, ioctl_type, ioctl_num, size & _IOC_SIZEMASK), buf);
+		ret = size;
+		break;
+	case file_io:
+	default:
+		ret = read(dev, buf, size);
+	}
+	return ret;
+}
+
+int io_start(int dev)
+{
+	struct pollfd pfd[2];
+	ssize_t data_in_len, data_out_len, len_total = 0;
+	int i = 0;
+
+	/* TODO: wo, ro */
+	pfd[0].fd = fileno(stdin);
+	pfd[0].events = POLLIN;
+	pfd[1].fd = dev;
+	pfd[1].events = POLLIN;
+	while (poll(pfd, sizeof(pfd) / sizeof(pfd[0]), -1) > 0) {
+#ifdef VERBOSE
+		trvd_(i);
+		trvx_(pfd[0].revents);
+		trvx_(pfd[1].revents);
+		trln();
+#endif
+		data_in_len = 0;
+		if (pfd[0].revents & POLLIN) {
+			pfd[0].revents = 0;
+			ret = data_in_len = read(fileno(stdin), inbuf, buf_size);
+			if (data_in_len < 0) {
+				usleep(100000);
+				break;
+			}
+			if (!data_in_len && !ignore_eof) {
+				/* read returns 0 on End Of File */
+				break;
+			}
+#ifdef VERBOSE
+			trvd_(data_in_len);
+			trln();
+#endif
+again:
+			chkne(ret = output(dev, inbuf, data_in_len));
+			if (ret < 0 && errno == EAGAIN) {
+				usleep(100000);
+				goto again;
+			}
+			if (data_in_len > 0)
+				len_total += data_in_len;
+		}
+		data_out_len = 0;
+		if (pfd[1].revents & POLLIN) {
+			pfd[1].revents = 0;
+			chkne(ret = data_out_len = input(dev, outbuf, buf_size));
+			if (data_out_len < 0) {
+				usleep(100000);
+				break;
+			}
+			if (!data_out_len) {
+				/* EOF, don't expect data from the file any more
+				   but wee can continue to write */
+				pfd[1].events = 0;
+			}
+			if (!data_out_len && !ignore_eof) {
+				/* read returns 0 on End Of File */
+				break;
+			}
+			write(fileno(stdout), outbuf, data_out_len);
+			if (data_out_len > 0)
+				len_total += data_out_len;
+		}
+#ifdef VERBOSE
+		trl_();
+		trvd_(i);
+		trvd_(len_total);
+		trvd_(data_in_len);
+		trvd_(data_out_len);
+		trln();
+#endif
+		if ((!ignore_eof && pfd[0].revents & POLLHUP) || pfd[1].revents & POLLHUP)
+			break;
+		i++;
+		if (loops && i >= loops)
+			break;
+		usleep(1000 * delay);
+	}
+#ifdef VERBOSE
+	trl_();
+	trvd_(i);
+	trvd_(len_total);
+	trvd_(data_in_len);
+	trvd_(data_out_len);
+	trln();
+#endif
+	return ret;
+}
+
+#define add_literal_option(o)  do { options[optnum].name = #o; \
+	options[optnum].flag = (void *)&o; options[optnum].has_arg = 1; \
+	options[optnum].val = -1; optnum++; } while (0)
+
+#define add_flag_option(n, p, v) do { options[optnum].name = n; \
+	options[optnum].flag = (void *)p; options[optnum].has_arg = 0; \
+	options[optnum].val = v; optnum++; } while (0)
+
+static struct option options[100];
+int optnum;
+static int verbose;
+
+int options_init()
+{
+	optnum = 0;
+	/* on gcc 64, pointer to variable can be used only on run-time
+	 */
+	memset(options, 0, sizeof(options));
+	add_literal_option(io_type);
+	add_literal_option(buf_size);
+	add_literal_option(ioctl_num);
+	add_literal_option(ioctl_type);
+	add_literal_option(loops);
+	add_literal_option(delay);
+	add_literal_option(offset);
+	add_flag_option("ioctl", &io_type, ioctl_io);
+	add_flag_option("mmap", &io_type, mmap_io);
+	add_flag_option("file", &io_type, file_io);
+	add_flag_option("ignore_eof", &ignore_eof, 1);
+	add_flag_option("verbose", &verbose, 1);
+	add_flag_option("ro", &ro, 1);
+	add_flag_option("wo", &wo, 1);
+	options[optnum].name = strdup("help");
+	options[optnum].has_arg = 0;
+	options[optnum].val = 'h';
+	optnum++;
+	return optnum;
+}
+
+/*
+ * expand_arg, return_if_arg_is_equal - utility functions
+ * to translate command line parameters
+ * from string to numeric values using predefined preprocessor defines
+ */
+
+#define return_if_arg_is_equal(entry) do { if (0 == strcmp(arg, #entry)) return entry; } while (0)
+
+int expand_arg(char *arg)
+{
+	if (!arg)
+		return 0;
+/*
+	return_if_arg_is_equal(SOCK_STREAM);
+*/
+	return strtol(arg, NULL, 0);
+}
+
+char *usage = "dio - Device Input/Output utility\n\
+Usage:\n\
+	dio <options> <device file>\n\
+\n\
+options:\n\
+\n\
+default values are marked with '*'\n\
+\n\
+	-h | --help\n\
+		show this help\n\
+\n\
+	--buf_size <n> \n\
+		I/O buffer size\n\
+\n\
+Samples:\n\
+\n\
+TBD\n\
+\n\
+";
+
+int init(int argc, char *argv[])
+{
+	int opt = 0;
+	int longindex = 0;
+	options_init();
+	opterr = 0;
+	while ((opt = getopt_long(argc, argv, "h", options, &longindex)) != -1) {
+		switch (opt) {
+		case 0:
+			if (options[longindex].val == -1)
+				*options[longindex].flag = expand_arg(optarg);
+			break;
+		case 'h':
+			printf("%s", usage);
+			exit(0);
+			break;
+		default:	/* '?' */
+			printf("Error in arguments\n");
+			trvx(opt);
+			exit(EXIT_FAILURE);
+		}
+	}
+	if (optind < argc)
+		dev_name = argv[optind];
+	if (io_type == ioctl_io && buf_size >= 1 << _IOC_SIZEBITS)
+		fprintf(stderr, "WARNING: size of ioctl data it too big\n");
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	int dev;
+
+	buf_size = sysconf(_SC_PAGESIZE);
+	init(argc, argv);
+	verbose && fprintf(stderr, "%s compiled " __DATE__ " " __TIME__ "\n", argv[0]);
+	if (io_type == ioctl_io && buf_size >= 1 << _IOC_SIZEBITS)
+		buf_size = (1 << _IOC_SIZEBITS) - 1;
+	inbuf = malloc(buf_size);
+	outbuf = malloc(buf_size);
+	chkne(dev = open(dev_name, O_CREAT | O_RDWR, 0666));
+	if (io_type == mmap_io) {
+		mm = mmap(NULL, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dev, offset & ~(sysconf(_SC_PAGESIZE)-1));
+		if (mm == MAP_FAILED) {
+			warn("mmap() failed");
+			goto exit;
+		}
+		mem = mm + (offset & (sysconf(_SC_PAGESIZE)-1));
+	}
+	if (verbose) {
+		trvs_(dev_name);
+		trvd_(io_type);
+		trvd_(buf_size);
+		trvd_(ignore_eof);
+		trvd_(verbose);
+		trvp_(mm);
+		trvp_(mem);
+		trln();
+	}
+	switch (io_type) {
+	case mmap_io:
+	case ioctl_io:
+		if (!ro) {
+			chkne(ret = read(fileno(stdin), inbuf, buf_size));
+			if (ret < 0)
+				goto exit;
+			chkne(ret = output(dev, inbuf, ret));
+		}
+		if (!wo) {
+			chkne(ret = input(dev, outbuf, buf_size));
+			if (ret < 0)
+				goto exit;
+			write(fileno(stdout), outbuf, ret);
+		}
+		break;
+	case file_io:
+	default:
+		io_start(dev);
+	}
+exit:
+	if (mm && mm != MAP_FAILED)
+		munmap(mm, buf_size);
+	free(outbuf);
+	free(inbuf);
+	close(dev);
+	exit(EXIT_SUCCESS);
+}
diff --git a/tools/testing/ldt/ldt-test b/tools/testing/ldt/ldt-test
new file mode 100755
index 0000000..9e84a6f
--- /dev/null
+++ b/tools/testing/ldt/ldt-test
@@ -0,0 +1,142 @@
+#!/bin/bash
+
+#
+#	LDT - Linux Driver Template
+#
+#	Test script
+#
+#	Copyright (C) 2012 Constantine Shulyupin  http://www.makelinux.net/
+#
+#	Dual BSD/GPL License
+#
+
+RED="\\033[0;31m"
+NOCOLOR="\\033[0;39m"
+GREEN="\\033[0;32m"
+GRAY="\\033[0;37m"
+
+set -o errtrace
+debugfs=`grep debugfs /proc/mounts | awk '{ print $2; }'`
+tracing=$debugfs/tracing
+
+tracing()
+{
+	sudo sh -c "cd $tracing; $1" || true
+}
+
+tracing_start()
+{
+	tracing "echo :mod:ldt > set_ftrace_filter"
+	tracing "echo function > current_tracer" # need for draw_functrace.py
+	#tracing "echo function_graph > current_tracer"
+	tracing "echo 1 > function_profile_enabled"
+	# useful optional command:
+	#tracing "echo XXX > set_ftrace_notrace"
+	#sudo cat $tracing/current_tracer
+	#sudo cat $tracing/set_ftrace_filter
+	#sudo cat $tracing/function_profile_enabled
+	# available_filter_functions
+	# echo $$ > set_ftrace_pid
+}
+
+tracing_stop()
+{
+	( echo Profiling data per CPU
+	tracing "cat trace_stat/function*" )> trace_stat.log && echo trace_stat.log saved
+	tracing "echo 0 > function_profile_enabled"
+	sudo cp $tracing/trace ftrace.log && echo ftrace.log saved
+	sudo dd iflag=nonblock if=$tracing/trace_pipe 2> /dev/null > trace_pipe.log || true && echo trace_pipe.log saved
+	tracing "echo nop > current_tracer"
+	#export PYTHONPATH=/usr/src/linux-headers-$(uname -r)/scripts/tracing/
+	# draw_functrace.py needs function tracer
+	python /usr/src/linux-headers-$(uname -r)/scripts/tracing/draw_functrace.py \
+		< trace_pipe.log > functrace.log && echo functrace.log saved || true
+}
+
+# sudo rmmod parport_pc parport  ppdev lp
+sudo dmesg -n 7
+sudo rmmod ldt ldt_plat_dev 2> /dev/null
+sudo dmesg -c > /dev/null
+stty -F /dev/ttyS0 115200
+make -s
+set -o errexit
+
+#
+# Check for presence looback on /dev/ttyS0.
+# If loopback is not present, switch loopback on in the driver
+#
+
+data='loopback?'
+received=`echo $data | ./dio  --ignore_eof --loops 2 --delay 10 /dev/ttyS0 2> /dev/null`
+if [ "$data" == "$received" ]; then
+echo -e "Loopback on /dev/ttyS0 detected"
+loopback=0
+else
+echo -e "No loopback behind /dev/ttyS0 detected, running ldt driver with UART in loopback mode"
+loopback=1
+fi
+
+# clean data
+echo | ./dio  --ignore_eof --loops 10 --delay 10 /dev/ttyS0 2> /dev/null > /dev/null
+
+sudo modprobe ldt loopback=$loopback
+sudo modprobe ldt_plat_dev
+
+tracing_start || true
+sudo sh -c "chmod go+rw /dev/ldt*"
+data=123rw
+echo $data > /dev/ldt
+sleep 0.5
+received=`dd iflag=nonblock if=/dev/ldt 2> /dev/null || true`
+if [ "$data" == "$received" ]; then
+echo -e "${GREEN}LDT nonblocking read/write test passed$NOCOLOR"
+else
+echo -e "${RED}LDT nonblock read/write test failed$NOCOLOR"
+echo expected $data
+echo received $received
+fi
+
+data=123bl
+cat /dev/ldt > R.tmp &
+sleep 0.5; echo $data > /dev/ldt;
+sleep 0.5
+kill %1; wait %1 2> /dev/null || true
+received=`cat R.tmp`
+rm -f R.tmp
+
+if [ "$data" == "$received" ]; then
+echo -e "${GREEN}LDT blocking read/write test passed$NOCOLOR"
+else
+echo -e "${RED}LDT blocking read/write test failed$NOCOLOR"
+echo expected $data
+echo received $received
+fi
+
+data=123mmap
+received=`sudo echo $data | ./dio --mmap /dev/ldt`
+if [ "$data" == "$received" ]; then
+echo -e "${GREEN}LDT mmap test passed$NOCOLOR"
+else
+echo -e "${RED}LDT mmap test failed$NOCOLOR"
+echo expected $data
+echo received $received
+fi
+
+data=123ioctl
+received=`sudo echo $data | ./dio --ioctl /dev/ldt`
+if [ "$data" == "$received" ]; then
+echo -e "${GREEN}LDT ioctl test passed$NOCOLOR"
+else
+echo -e "${RED}LDT ioctl test failed$NOCOLOR"
+echo expected $data
+echo received $received
+fi
+
+sudo ls -l /sys/kernel/debug/ldt
+#grep ldt /proc/interrupts || true
+
+#sudo rmmod ldt ldt_plat_dev 2> /dev/null
+
+tracing_stop || true
+sudo dmesg --notime --show-delta --read-clear 2>/dev/null > kernel.log || \
+sudo dmesg -c > kernel.log && echo kernel.log saved
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ