原创 tty(1)

2012-6-11 00:15 1502 5 5 分类: MCU/ 嵌入式

/*
 * 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 <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/wait.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <asm/uaccess.h>
#include <linux/timer.h>

#define DRIVER_VERSION "v2.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#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  1 /* 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[14] = "hello,world  ";
 char c_enter = '\n';
 int data_size = 12;

 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->flip.count >= TTY_FLIPBUF_SIZE)
//   tty_flip_buffer_push(tty);
  tty_insert_flip_char(tty, data, TTY_NORMAL);
 }
 tty_insert_flip_char(tty, '\n', TTY_NORMAL);
// printk ("tty push buffer \n");
 tty_flip_buffer_push(tty);
// printk ("tty end buffer \n");

 // resubmit the timer again
 tiny->timer->expires = jiffies + DELAY_TIME;
 add_timer(tiny->timer);
 
/* //test timer
 static int i;
 struct tiny_serial *tiny = (struct tiny_serial *)timer_data;

 ++i;
 printk ("tiny timer i=%d\n",i);
 // 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;
 if (NULL == tty)
 {
  printk ("tty == NULL \n");
  return -1;
 }

 /* initialize the pointer in case something fails */
 tty->driver_data = NULL;

 /* get the serial object associated with this tty pointer */
 index = 0;
 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;

  init_MUTEX(&tiny->sem);
  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 */
  printk ("open 3 \n");
  if (NULL == tiny->timer) {
   timer = kmalloc(sizeof(*timer), GFP_KERNEL);
   if (NULL == 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;
  printk ("open 4 \n");
  add_timer(tiny->timer);
  printk ("open 5 \n");

 }

 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);
 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 termios *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 = 5\n");
   break;
  case CS6:
   printk(KERN_DEBUG " - data bits = 6\n");
   break;
  case CS7:
   printk(KERN_DEBUG " - data bits = 7\n");
   break;
  default:
  case CS8:
   printk(KERN_DEBUG " - data bits = 8\n");
   break;
 }
 
 /* determine the parity */
 if (cflag & PARENB)
  if (cflag & PARODD)
   printk(KERN_DEBUG " - parity = odd\n");
  else
   printk(KERN_DEBUG " - parity = even\n");
 else
  printk(KERN_DEBUG " - parity = none\n");

 /* figure out the stop bits requested */
 if (cflag & CSTOPB)
  printk(KERN_DEBUG " - stop bits = 2\n");
 else
  printk(KERN_DEBUG " - stop bits = 1\n");

 /* figure out the hardware flow control settings */
 if (cflag & CRTSCTS)
  printk(KERN_DEBUG " - RTS/CTS is enabled\n");
 else
  printk(KERN_DEBUG " - RTS/CTS is disabled\n");
 
 /* determine software flow control */
 /* if we are implementing XON/XOFF, set the start and
  * stop character in the device */
 if (I_IXOFF(tty) || I_IXON(tty)) {
  unsigned char stop_char  = STOP_CHAR(tty);
  unsigned char start_char = START_CHAR(tty);

  /* if we are implementing INBOUND XON/XOFF */
  if (I_IXOFF(tty))
   printk(KERN_DEBUG " - INBOUND XON/XOFF is enabled, "
    "XON = %2x, XOFF = %2x", start_char, stop_char);
  else
   printk(KERN_DEBUG" - INBOUND XON/XOFF is disabled");

  /* if we are implementing OUTBOUND XON/XOFF */
  if (I_IXON(tty))
   printk(KERN_DEBUG" - OUTBOUND XON/XOFF is enabled, "
    "XON = %2x, XOFF = %2x", start_char, stop_char);
  else
   printk(KERN_DEBUG" - OUTBOUND XON/XOFF is disabled");
 }

 /* get the baud rate wanted */
 printk(KERN_DEBUG " - baud rate = %d", tty_get_baud_rate(tty));
}

/* Our fake UART values */
#define MCR_DTR  0x01
#define MCR_RTS  0x02
#define MCR_LOOP 0x04
#define MSR_CTS  0x08
#define MSR_CD  0x10
#define MSR_RI  0x20
#define MSR_DSR  0x40

module_exit(tiny_exit);

arm
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
5
关闭 站长推荐上一条 /3 下一条