⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dz.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * dz.c: Serial port driver for DECStations equiped  *       with the DZ chipset. * * Copyright (C) 1998 Olivier A. D. Lebaillif  *              * Email: olivier.lebaillif@ifrsys.com * * [31-AUG-98] triemer * Changed IRQ to use Harald's dec internals interrupts.h * removed base_addr code - moving address assignment to setup.c * Changed name of dz_init to rs_init to be consistent with tc code * [13-NOV-98] triemer fixed code to receive characters *    after patches by harald to irq code.   * [09-JAN-99] triemer minor fix for schedule - due to removal of timeout *            field from "current" - somewhere between 2.1.121 and 2.1.131 *   * Parts (C) 1999 David Airlie, airlied@linux.ie  * [07-SEP-99] Bugfixes  */#define DEBUG_DZ 1#ifdef MODULE#include <linux/module.h>#include <linux/version.h>#else#define MOD_INC_USE_COUNT#define MOD_DEC_USE_COUNT#endif#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/init.h> #include <linux/malloc.h>#include <linux/mm.h>#include <linux/major.h>#include <linux/param.h>#include <linux/tqueue.h>#include <linux/interrupt.h>#include <asm-mips/wbflush.h>/* for definition of SERIAL */#include <asm/dec/interrupts.h>/* for definition of struct console */#ifdef CONFIG_SERIAL_CONSOLE#define CONSOLE_LINE (3)#endif /* ifdef CONFIG_SERIAL_CONSOLE */#if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ)#include <linux/console.h>#endif /* if defined(CONFIG_SERIAL_CONSOLE) || defined(DEBUG_DZ) */#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/serial.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/dec/machtype.h>#include <asm/dec/kn01.h>#include <asm/dec/kn02.h>#ifdef DEBUG_DZ#include <linux/ptrace.h>#include <linux/fs.h>#include <asm/bootinfo.h>extern int (*prom_printf) (char *,...);#endif#include "dz.h"#define DZ_INTR_DEBUG 1DECLARE_TASK_QUEUE(tq_serial);extern wait_queue_head_t keypress_wait; static struct dz_serial *lines[4];static unsigned char tmp_buffer[256];#ifdef DEBUG_DZ/* * debugging code to send out chars via prom  */static void debug_console( const char *s,int count){    unsigned i;    for (i = 0; i < count; i++) {       if (*s == 10)           prom_printf("%c", 13);       prom_printf("%c", *s++);    }}#endif/* * ------------------------------------------------------------ * dz_in () and dz_out () * * These routines are used to access the registers of the DZ  * chip, hiding relocation differences between implementation. * ------------------------------------------------------------ */static inline unsigned short dz_in (struct dz_serial *info, unsigned offset){  volatile unsigned short *addr = (volatile unsigned short *)(info->port + offset);  return *addr;}static inline void dz_out (struct dz_serial *info, unsigned offset, unsigned short value){    volatile unsigned short *addr = (volatile unsigned short *)(info->port + offset);  *addr = value;}/* * ------------------------------------------------------------ * rs_stop () and rs_start () * * These routines are called before setting or resetting  * tty->stopped. They enable or disable transmitter interrupts,  * as necessary. * ------------------------------------------------------------ */static void dz_stop (struct tty_struct *tty){  struct dz_serial *info;   unsigned short mask, tmp;  if (tty==0)     return;    info = (struct dz_serial *)tty->driver_data;            mask = 1 << info->line;  tmp = dz_in (info, DZ_TCR);       /* read the TX flag */    tmp &= ~mask;                   /* clear the TX flag */    dz_out (info, DZ_TCR, tmp);}  static void dz_start (struct tty_struct *tty){  struct dz_serial *info = (struct dz_serial *)tty->driver_data;  unsigned short mask, tmp;  mask = 1 << info->line;  tmp = dz_in (info, DZ_TCR);      /* read the TX flag */    tmp |= mask;                   /* set the TX flag */    dz_out (info, DZ_TCR, tmp);}  /* * ------------------------------------------------------------ * Here starts the interrupt handling routines.  All of the  * following subroutines are declared as inline and are folded  * into dz_interrupt.  They were separated out for readability's  * sake.  * * Note: rs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off.  People who may want to modify * rs_interrupt() should try to keep the interrupt handler as fast as * possible.  After you are done making modifications, it is not a bad * idea to do: *  * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer dz.c * * and look at the resulting assemble code in serial.s. * * ------------------------------------------------------------ *//* * ------------------------------------------------------------ * dz_sched_event () * * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. * ------------------------------------------------------------ */static inline void dz_sched_event (struct dz_serial *info, int event){  info->event |= 1 << event;  queue_task (&info->tqueue, &tq_serial);  mark_bh (SERIAL_BH);}/* * ------------------------------------------------------------ * receive_char () * * This routine deals with inputs from any lines. * ------------------------------------------------------------ */static inline void receive_chars (struct dz_serial *info_in){  struct dz_serial *info;  struct tty_struct *tty = 0;  struct async_icount *icount;  int ignore = 0;  unsigned short status, tmp;  unsigned char ch;  /* this code is going to be a problem...     the call to tty_flip_buffer is going to need     to be rethought...   */  do    {      status = dz_in (info_in, DZ_RBUF);      info = lines[LINE(status)];      /* punt so we don't get duplicate characters */      if (!(status & DZ_DVAL))        goto ignore_char;    ch = UCHAR(status);                /* grab the char */#ifdef 0    if (info->is_console) {      if (ch == 0) return;            /* it's a break ... */      wake_up (&keypress_wait);       /* It is a 'keyboard interrupt' ;-) */    }#endif    tty = info->tty;                  /* now tty points to the proper dev */    icount = &info->icount;    if (!tty) break;    if (tty->flip.count >= TTY_FLIPBUF_SIZE) break;    *tty->flip.char_buf_ptr = ch;    *tty->flip.flag_buf_ptr = 0;    icount->rx++;    /* keep track of the statistics */    if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) {      if (status & DZ_PERR)                /* parity error */        icount->parity++;      else if (status & DZ_FERR)           /* frame error */        icount->frame++;      if (status & DZ_OERR)                /* overrun error */        icount->overrun++;      /*  check to see if we should ignore the character	 and mask off conditions that should be ignored      */      if (status & info->ignore_status_mask) {        if (++ignore > 100 ) break;        goto ignore_char;      }      /* mask off the error conditions we want to ignore */      tmp = status & info->read_status_mask;      if (tmp & DZ_PERR)	{        *tty->flip.flag_buf_ptr = TTY_PARITY;	  debug_console("PERR\n",5);	}      else if (tmp & DZ_FERR)	{        *tty->flip.flag_buf_ptr = TTY_FRAME;	  debug_console("FERR\n",5);	}      if (tmp & DZ_OERR) 	{ 	  debug_console("OERR\n",5);	if (tty->flip.count < TTY_FLIPBUF_SIZE) {          tty->flip.count++;          tty->flip.flag_buf_ptr++;          tty->flip.char_buf_ptr++;          *tty->flip.flag_buf_ptr = TTY_OVERRUN;        }      }    }    tty->flip.flag_buf_ptr++;    tty->flip.char_buf_ptr++;    tty->flip.count++;  ignore_char:  } while (status & DZ_DVAL);  if (tty)    tty_flip_buffer_push(tty);}/* * ------------------------------------------------------------ * transmit_char () * * This routine deals with outputs to any lines. * ------------------------------------------------------------ */static inline void transmit_chars (struct dz_serial *info){  unsigned char tmp;  if (info->x_char) {           /* XON/XOFF chars */    dz_out (info, DZ_TDR, info->x_char);    info->icount.tx++;    info->x_char = 0;    return;  }  /* if nothing to do or stopped or hardware stopped */  if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped) {    dz_stop (info->tty);    return;  }  /* if something to do ... (rember the dz has no output fifo so we go one char at a time :-< */  tmp = (unsigned short)info->xmit_buf[info->xmit_tail++];  dz_out (info, DZ_TDR, tmp);  info->xmit_tail = info->xmit_tail & (DZ_XMIT_SIZE - 1);  info->icount.tx++;  if (--info->xmit_cnt < WAKEUP_CHARS)    dz_sched_event (info, DZ_EVENT_WRITE_WAKEUP);  /* Are we done */  if (info->xmit_cnt <= 0) dz_stop (info->tty);}/* * ------------------------------------------------------------ * check_modem_status () * * Only valid for the MODEM line duh ! * ------------------------------------------------------------ */static inline void check_modem_status (struct dz_serial *info){  unsigned short status;  /* if not ne modem line just return */  if (info->line != DZ_MODEM) return;  status = dz_in (info, DZ_MSR);    /* it's easy, since DSR2 is the only bit in the register */  if (status) info->icount.dsr++;}/* * ------------------------------------------------------------ * dz_interrupt () * * this is the main interrupt routine for the DZ chip. * It deals with the multiple ports. * ------------------------------------------------------------ */static void dz_interrupt (int irq, void *dev, struct pt_regs *regs){  struct dz_serial *info;  unsigned short status;  status = dz_in ((struct dz_serial *)dev, DZ_CSR); /* get the reason why we just got an irq */  info = lines[LINE(status)];     /* re-arrange info the proper port */  if (status & DZ_RDONE)       receive_chars (info);          /* the receive function */  if (status & DZ_TRDY)     transmit_chars (info);}/* * ------------------------------------------------------------------- * Here ends the DZ interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh (void){        run_task_queue (&tq_serial);}static void do_softint (void *private_data){  struct dz_serial *info = (struct dz_serial *)private_data;  struct tty_struct *tty = info->tty;  if (!tty) return;  if (test_and_clear_bit (DZ_EVENT_WRITE_WAKEUP, &info->event)) {    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup)      (tty->ldisc.write_wakeup) (tty);    wake_up_interruptible (&tty->write_wait);  }}/* * ------------------------------------------------------------------- * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred.  The path of * hangup processing is: * *      serial interrupt routine -> (scheduler tqueue) -> *      do_serial_hangup() -> tty->hangup() -> rs_hangup() * -------------------------------------------------------------------  */static void do_serial_hangup (void *private_data){  struct dz_serial *info = (struct dz_serial *)private_data;  struct tty_struct *tty = info->tty;;          if (!tty) return;  tty_hangup (tty);}/* * ------------------------------------------------------------------- * startup () * * various initialization tasks * -------------------------------------------------------------------  */static int startup (struct dz_serial *info){  unsigned long page, flags;  unsigned short tmp;  if (info->is_initialized) return 0;    save_flags (flags);  cli ();  if (!info->port) {    if (info->tty) set_bit (TTY_IO_ERROR, &info->tty->flags);    restore_flags (flags);    return -ENODEV;  }  if (!info->xmit_buf) {    page = get_free_page (GFP_KERNEL);    if (!page) {      restore_flags (flags);      return -ENOMEM;    }    info->xmit_buf = (unsigned char *)page;  }  if (info->tty) clear_bit (TTY_IO_ERROR, &info->tty->flags);  /* enable the interrupt and the scanning */  tmp = dz_in (info, DZ_CSR);  tmp |= (DZ_RIE | DZ_TIE | DZ_MSE);  dz_out (info, DZ_CSR, tmp);  info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;  /* set up the speed */  change_speed (info);  /* clear the line transmitter buffer      I can't figure out why I need to do this - but     its necessary - in order for the console portion     and the interrupt portion to live happily side by side.  */  /* clear the line transmitter buffer      I can't figure out why I need to do this - but     its necessary - in order for the console portion     and the interrupt portion to live happily side by side.  */  info->is_initialized = 1;  restore_flags (flags);  return 0;}/*  * ------------------------------------------------------------------- * shutdown () * * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. * -------------------------------------------------------------------  */static void shutdown (struct dz_serial *info){  unsigned long flags;  unsigned short tmp;  if (!info->is_initialized) return;  save_flags (flags);  cli ();  dz_stop (info->tty);  info->cflags &= ~DZ_CREAD;          /* turn off receive enable flag */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -