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");