Month: February 2010

Emulating a read-only serial

This is a read only serial driver which I developed to emulate a real GPS.
Here you will find just the stub of original driver, anyway it should work correctly.
When you execute “cat /dev/ttytest0” you should see “Just a simple serial test” string.

/****************************************************************************/
/*
 *	test.c
 *
 *	(C) Copyright 2010, Alan Carvalho de Assis 
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

/****************************************************************************/

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/init.h>
#include <linux/serial.h>
#include <linux/workqueue.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
#include <linux/io.h>
//#include 

static int debug = 1;

#define dbg(fmt, arg...)						\
	do {								\
		if (debug)						\
			printk (KERN_DEBUG "%s: %s: " fmt "\n",		\
				"test" , __FUNCTION__ , ## arg);	\
	} while (0)

#define TEST_INTERVAL 100

static void test_callback(struct work_struct *ignored);
static struct delayed_work test_work;

struct uart_port *myport;

/****************************************************************************/

char mystring[] = "Just a simple serial test\n";

/****************************************************************************/

/*
 *	Local per-uart structure.
 */
struct test_uart {
	struct uart_port	port;
	unsigned int		sigs;		/* Local copy of line sigs */
	unsigned char		imr;		/* Local IMR mirror */
};

/****************************************************************************/

static unsigned int test_tx_empty(struct uart_port *port)
{
	dbg();

	return 0;
}

/****************************************************************************/

static unsigned int test_get_mctrl(struct uart_port *port)
{
	dbg();

	return 0;
}

/****************************************************************************/

static void test_set_mctrl(struct uart_port *port, unsigned int sigs)
{
	dbg();
}

/****************************************************************************/

static void test_tx_chars(struct test_uart *pp)
{
	dbg();
}

/****************************************************************************/

static void test_start_tx(struct uart_port *port)
{
	dbg();

	test_tx_chars(NULL);
}

/****************************************************************************/

static void test_stop_tx(struct uart_port *port)
{
	dbg();
}

/****************************************************************************/

static void test_stop_rx(struct uart_port *port)
{
	dbg();
}

/****************************************************************************/

static void test_break_ctl(struct uart_port *port, int break_state)
{
	dbg();
}

/****************************************************************************/

static void test_enable_ms(struct uart_port *port)
{
	dbg();
}

/****************************************************************************/

static int test_startup(struct uart_port *port)
{
	dbg();

	/* schedule a delayed work to start receiving data */
	INIT_DELAYED_WORK(&test_work, test_callback);
	schedule_delayed_work(&test_work, TEST_INTERVAL);

	return 0;
}

/****************************************************************************/

static void test_shutdown(struct uart_port *port)
{
	dbg();

	cancel_delayed_work(&test_work);
}

/****************************************************************************/

static void test_set_termios(struct uart_port *port, struct ktermios *termios,
	struct ktermios *old)
{
	dbg();
}

/****************************************************************************/

static void test_rx_chars(struct test_uart *pp)
{
	unsigned char ch, flag;
	int len, i = 0;

	dbg();

	len = strlen(mystring) + 1;

	while (i < len) {
		ch = mystring[i++];
		flag = TTY_NORMAL;

		tty_insert_flip_char(myport->state->port.tty, ch, flag);
	}

	tty_flip_buffer_push(myport->state->port.tty);
}

/****************************************************************************/

static void test_callback(struct work_struct *ignored)
{
	//struct test_uart *pp = container_of(myport, struct test_uart, port);

	dbg();

	/* receive characters and send it to user */
	test_rx_chars(NULL);

	/* call me again */
	schedule_delayed_work(&test_work, TEST_INTERVAL);
}

/****************************************************************************/

static void test_config_port(struct uart_port *port, int flags)
{
	dbg();
}

/****************************************************************************/

static const char *test_type(struct uart_port *port)
{
	dbg();
	return (port->type == PORT_test) ? "Maxtrack UART" : NULL;
}

/****************************************************************************/

static int test_request_port(struct uart_port *port)
{
	dbg();

	/* UARTs always present */
	return 0;
}

/****************************************************************************/

static void test_release_port(struct uart_port *port)
{
	dbg();

	/* Nothing to release... */
}

/****************************************************************************/

static int test_verify_port(struct uart_port *port, struct serial_struct *ser)
{
	dbg();

	if ((ser->type != PORT_UNKNOWN) && (ser->type != PORT_test))
		return -EINVAL;
	return 0;
}

/****************************************************************************/

/*
 *	Define the basic serial functions we support.
 */
static struct uart_ops test_uart_ops = {
	.tx_empty	= test_tx_empty,
	.get_mctrl	= test_get_mctrl,
	.set_mctrl	= test_set_mctrl,
	.start_tx	= test_start_tx,
	.stop_tx	= test_stop_tx,
	.stop_rx	= test_stop_rx,
	.enable_ms	= test_enable_ms,
	.break_ctl	= test_break_ctl,
	.startup	= test_startup,
	.shutdown	= test_shutdown,
	.set_termios	= test_set_termios,
	.type		= test_type,
	.request_port	= test_request_port,
	.release_port	= test_release_port,
	.config_port	= test_config_port,
	.verify_port	= test_verify_port,
};

static struct test_uart test_ports[1];

/****************************************************************************/

/*
 *	Define the test UART driver structure.
 */
static struct uart_driver test_driver = {
	.owner		= THIS_MODULE,
	.driver_name	= "test",
	.dev_name	= "ttytest",
	.nr		= 1,
};

/****************************************************************************/

static int test_remove(struct platform_device *pdev)
{
	struct uart_port *port;

	port = &test_ports[0].port;
	if (port)
		uart_remove_one_port(&test_driver, port);

	return 0;
}

/****************************************************************************/

static int __init test_init(void)
{
	int rc;

	struct uart_port *port;

	rc = uart_register_driver(&test_driver);
	if (rc)
		return rc;

	port = &test_ports[0].port;

	port->line = 0;
	port->type = PORT_TEST; /*remember to define PORT_TEST at include/linux/serial_core.h */
	port->iotype = SERIAL_IO_MEM;
	port->ops = &test_uart_ops;
	port->flags = ASYNC_BOOT_AUTOCONF;

	uart_add_one_port(&test_driver, port);

	myport = port;

	return 0;
}

/****************************************************************************/

static void __exit test_exit(void)
{
	cancel_delayed_work(&test_work);

	uart_unregister_driver(&test_driver);
}

/****************************************************************************/

module_init(test_init);
module_exit(test_exit);

MODULE_AUTHOR("Alan Carvalho de Assis ");
MODULE_DESCRIPTION("Test UART driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:testuart");
/****************************************************************************/