Makefile:
# Comment/uncomment the following line to disable/enable debugging
#DEBUG = y
# Add your debugging flag (or not) to CFLAGS
ifeq ($(DEBUG),y)
DEBFLAGS = -O -g -DSCULL_DEBUG # "-O" is needed to expand inlines
else
DEBFLAGS = -O2
endif
EXTRA_CFLAGS += $(DEBFLAGS)
EXTRA_CFLAGS += -I..
ifneq ($(KERNELRELEASE),)
# call from kernel build system
obj-m := tiny_tty.o tiny_serial.o
else
#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
KERNELDIR ?= /root/Tiny4412_android_4_1_2/linux-3.0.31
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
clean:
rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
depend .depend dep:
$(CC) $(EXTRA_CFLAGS) -M *.c > .depend
ifeq (.depend,$(wildcard .depend))
include .depend
endif
tiny_tty.c
/*
* Tiny TTY driver
*
* Copyright (C) 2002-2004 Greg Kroah-Hartman (greg@kroah.com)
*
* 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, version 2 of the License.
*
* This driver shows how to create a minimal tty driver. It does not rely on
* any backing hardware, but creates a timer that emulates data being received
* from some kind of hardware.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_VERSION "v2.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman
#define DRIVER_DESC "Tiny TTY driver"
/* Module information */
MODULE_AUTHOR( DRIVER_AUTHOR );
MODULE_DESCRIPTION( DRIVER_DESC );
MODULE_LICENSE("GPL");
#define DELAY_TIME HZ * 2 /* 2 seconds per character */
#define TINY_DATA_CHARACTER 't'
#define TINY_TTY_MAJOR 240 /* experimental range */
#define TINY_TTY_MINORS 4 /* only have 4 devices */
struct tiny_serial {
struct tty_struct *tty; /* pointer to the tty for this device */
int open_count; /* number of times this port has been opened */
struct semaphore sem; /* locks this structure */
struct timer_list *timer;
/* for tiocmget and tiocmset functions */
int msr; /* MSR shadow */
int mcr; /* MCR shadow */
/* for ioctl fun */
struct serial_struct serial;
wait_queue_head_t wait;
struct async_icount icount;
};
static struct tiny_serial *tiny_table[TINY_TTY_MINORS]; /* initially all NULL */
static void tiny_timer(unsigned long timer_data)
{
struct tiny_serial *tiny = (struct tiny_serial *)timer_data;
struct tty_struct *tty;
int i;
char data[1] = {TINY_DATA_CHARACTER};
int data_size = 1;
if (!tiny)
return;
tty = tiny->tty;
/* send the data to the tty layer for users to read. This doesn't
* actually push the data through unless tty->low_latency is set */
for (i = 0; i < data_size; ++i) {
if (!tty_buffer_request_room(tty, 1))
tty_flip_buffer_push(tty);
tty_insert_flip_char(tty, data[i], TTY_NORMAL);
}
tty_flip_buffer_push(tty);
/* resubmit the timer again */
tiny->timer->expires = jiffies + DELAY_TIME;
add_timer(tiny->timer);
}
static int tiny_open(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny;
struct timer_list *timer;
int index;
/* initialize the pointer in case something fails */
tty->driver_data = NULL;
/* get the serial object associated with this tty pointer */
index = tty->index;
tiny = tiny_table[index];
if (tiny == NULL) {
/* first time accessing this device, let's create it */
tiny = kmalloc(sizeof(*tiny), GFP_KERNEL);
if (!tiny)
return -ENOMEM;
sema_init(&tiny->sem, 1);
tiny->open_count = 0;
tiny->timer = NULL;
tiny_table[index] = tiny;
}
down(&tiny->sem);
/* save our structure within the tty structure */
tty->driver_data = tiny;
tiny->tty = tty;
++tiny->open_count;
if (tiny->open_count == 1) {
/* this is the first time this port is opened */
/* do any hardware initialization needed here */
/* create our timer and submit it */
if (!tiny->timer) {
timer = kmalloc(sizeof(*timer), GFP_KERNEL);
if (!timer) {
up(&tiny->sem);
return -ENOMEM;
}
tiny->timer = timer;
}
init_timer(tiny->timer);
tiny->timer->data = (unsigned long )tiny;
tiny->timer->expires = jiffies + DELAY_TIME;
tiny->timer->function = tiny_timer;
add_timer(tiny->timer);
}
up(&tiny->sem);
return 0;
}
static void do_close(struct tiny_serial *tiny)
{
down(&tiny->sem);
if (!tiny->open_count) {
/* port was never opened */
goto exit;
}
--tiny->open_count;
if (tiny->open_count <= 0) {
/* The port is being closed by the last user. */
/* Do any hardware specific stuff here */
/* shut down our timer */
del_timer(tiny->timer);
}
exit:
up(&tiny->sem);
}
static void tiny_close(struct tty_struct *tty, struct file *file)
{
struct tiny_serial *tiny = tty->driver_data;
if (tiny)
do_close(tiny);
}
static int tiny_write(struct tty_struct *tty,
const unsigned char *buffer, int count)
{
struct tiny_serial *tiny = tty->driver_data;
int i;
int retval = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count)
/* port was not opened */
goto exit;
/* fake sending the data out a hardware port by
* writing it to the kernel debug log.
*/
printk(KERN_DEBUG "%s - ", __FUNCTION__);
for (i = 0; i < count; ++i)
printk("%02x ", buffer[i]);
printk("n");
exit:
up(&tiny->sem);
return retval;
}
static int tiny_write_room(struct tty_struct *tty)
{
struct tiny_serial *tiny = tty->driver_data;
int room = -EINVAL;
if (!tiny)
return -ENODEV;
down(&tiny->sem);
if (!tiny->open_count) {
/* port was not opened */
goto exit;
}
/* calculate how much room is left in the device */
room = 255;
exit:
up(&tiny->sem);
return room;
}
#define RELEVANT_IFLAG(iflag) ((iflag) & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
static void tiny_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
{
unsigned int cflag;
cflag = tty->termios->c_cflag;
/* check that they really want us to change something */
if (old_termios) {
if ((cflag == old_termios->c_cflag) &&
(RELEVANT_IFLAG(tty->termios->c_iflag) ==
RELEVANT_IFLAG(old_termios->c_iflag))) {
printk(KERN_DEBUG " - nothing to change...n");
return;
}
}
/* get the byte size */
switch (cflag & CSIZE) {
case CS5:
printk(KERN_DEBUG " - data bits = 5n");