mcfserial.c
来自「这是一个SIGMA方案的PMP播放器的UCLINUX程序,可播放DVD,VCD,」· C语言 代码 · 共 1,983 行 · 第 1/4 页
C
1,983 行
/* * mcfserial.c -- serial driver for ColdFire internal UARTS. * * Copyright (c) 1999-2002 Greg Ungerer <gerg@snapgear.com> * Copyright (c) 2000-2001 Lineo, Inc. <www.lineo.com> * Copyright (C) 2001-2002 SnapGear Inc. <www.snapgear.com> * * Based on code from 68332serial.c which was: * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1998 TSHG * Copyright (c) 1999 Rt-Control Inc. <jeff@uclinux.org> */ #include <linux/module.h>#include <linux/errno.h>#include <linux/signal.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/wait.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/config.h>#include <linux/major.h>#include <linux/string.h>#include <linux/fcntl.h>#include <linux/mm.h>#include <linux/kernel.h>#include <linux/serial.h>#include <linux/serialP.h>#ifdef CONFIG_LEDMAN#include <linux/ledman.h>#endif#include <linux/console.h>#include <linux/version.h>#include <linux/init.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/segment.h>#include <asm/semaphore.h>#include <asm/bitops.h>#include <asm/delay.h>#include <asm/coldfire.h>#include <asm/mcfsim.h>#include <asm/mcfuart.h>#include <asm/nettel.h>#if LINUX_VERSION_CODE < 0x020100#define queue_task_irq_off queue_task#define copy_from_user(a,b,c) memcpy_fromfs(a,b,c)#define copy_to_user(a,b,c) memcpy_tofs(a,b,c)#else#include <asm/uaccess.h>#endif#include "mcfserial.h"/* * the only event we use */#undef RS_EVENT_WRITE_WAKEUP#define RS_EVENT_WRITE_WAKEUP 0#if LINUX_VERSION_CODE >= 0x020100struct timer_list mcfrs_timer_struct;#endif/* * Default console baud rate, we use this as the default * for all ports so init can just open /dev/console and * keep going. Perhaps one day the cflag settings for the * console can be used instead. */#if defined(CONFIG_ARNEWSH) || defined(CONFIG_MOTOROLA)#define CONSOLE_BAUD_RATE 19200#define DEFAULT_CBAUD B19200#endif#ifndef CONSOLE_BAUD_RATE#define CONSOLE_BAUD_RATE 9600#define DEFAULT_CBAUD B9600#endifint mcfrs_console_inited = 0;int mcfrs_console_port = -1;int mcfrs_console_baud = CONSOLE_BAUD_RATE;int mcfrs_console_cbaud = DEFAULT_CBAUD;DECLARE_TASK_QUEUE(mcf_tq_serial);/* * Driver data structures. */struct tty_driver mcfrs_serial_driver, mcfrs_callout_driver;static int mcfrs_serial_refcount;/* serial subtype definitions */#define SERIAL_TYPE_NORMAL 1#define SERIAL_TYPE_CALLOUT 2 /* number of characters left in xmit buffer before we ask for more */#define WAKEUP_CHARS 256/* Debugging... */#undef SERIAL_DEBUG_OPEN#undef SERIAL_DEBUG_FLOW#define _INLINE_ inline#define IRQBASE 73/* * Configuration table, UARTs to look for at startup. */static struct mcf_serial mcfrs_table[] = { { 0, (MCF_MBAR+MCFUART_BASE1), IRQBASE, ASYNC_BOOT_AUTOCONF }, /* ttyS0 */ { 0, (MCF_MBAR+MCFUART_BASE2), IRQBASE+1, ASYNC_BOOT_AUTOCONF }, /* ttyS1 */};#define NR_PORTS (sizeof(mcfrs_table) / sizeof(struct mcf_serial))static struct tty_struct *mcfrs_serial_table[NR_PORTS];static struct termios *mcfrs_serial_termios[NR_PORTS];static struct termios *mcfrs_serial_termios_locked[NR_PORTS];/* * This is used to figure out the divisor speeds and the timeouts. */static int mcfrs_baud_table[] = { 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 0};#define MCFRS_BAUD_TABLE_SIZE \ (sizeof(mcfrs_baud_table)/sizeof(mcfrs_baud_table[0]))#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endif#ifdef CONFIG_MAGIC_SYSRQ/* * Magic system request keys. Used for debugging... */extern int magic_sysrq_key(int ch);#endif/* * tmp_buf is used as a temporary buffer by serial_write. We need to * lock it in case the copy_from_user blocks while swapping in a page, * and some other program tries to do a serial write at the same time. * Since the lock will only come under contention when the system is * swapping and available memory is low, it makes sense to share one * buffer across all the serial ports, since it significantly saves * memory if large numbers of serial ports are open. */static unsigned char mcfrs_tmp_buf[4096]; /* This is cheating */#if LINUX_VERSION_CODE < 0x020100static struct semaphore mcfrs_tmp_buf_sem = MUTEX;#elsestatic DECLARE_MUTEX(mcfrs_tmp_buf_sem);#endif/* * Forware declarations... */static void mcfrs_change_speed(struct mcf_serial *info);static inline int serial_paranoia_check(struct mcf_serial *info, dev_t device, const char *routine){#ifdef SERIAL_PARANOIA_CHECK static const char *badmagic = "Warning: bad magic number for serial struct (%d, %d) in %s\n"; static const char *badinfo = "Warning: null mcf_serial for (%d, %d) in %s\n"; if (!info) { printk(badinfo, MAJOR(device), MINOR(device), routine); return 1; } if (info->magic != SERIAL_MAGIC) { printk(badmagic, MAJOR(device), MINOR(device), routine); return 1; }#endif return 0;}/* * Sets or clears DTR and RTS on the requested line. */static void mcfrs_setsignals(struct mcf_serial *info, int dtr, int rts){ volatile unsigned char *uartp; unsigned long flags; #if 0 printk("%s(%d): mcfrs_setsignals(info=%x,dtr=%d,rts=%d)\n", __FILE__, __LINE__, info, dtr, rts);#endif save_flags(flags); cli(); if (dtr >= 0) {#ifdef MCFPP_DTR0 if (info->line) mcf_setppdata(MCFPP_DTR1, (dtr ? 0 : MCFPP_DTR1)); else mcf_setppdata(MCFPP_DTR0, (dtr ? 0 : MCFPP_DTR0));#endif } if (rts >= 0) { uartp = (volatile unsigned char *) info->addr; if (rts) { info->sigs |= TIOCM_RTS; uartp[MCFUART_UOP1] = MCFUART_UOP_RTS; } else { info->sigs &= ~TIOCM_RTS; uartp[MCFUART_UOP0] = MCFUART_UOP_RTS; } } restore_flags(flags); return;}/* * Gets values of serial signals. */static int mcfrs_getsignals(struct mcf_serial *info){ volatile unsigned char *uartp; unsigned long flags; int sigs;#if defined(CONFIG_NETtel) && defined(CONFIG_M5307) unsigned short ppdata;#endif#if 0 printk("%s(%d): mcfrs_getsignals(info=%x)\n", __FILE__, __LINE__);#endif save_flags(flags); cli(); uartp = (volatile unsigned char *) info->addr; sigs = (uartp[MCFUART_UIPR] & MCFUART_UIPR_CTS) ? 0 : TIOCM_CTS; sigs |= (info->sigs & TIOCM_RTS);#ifdef MCFPP_DCD0{ unsigned int ppdata; ppdata = mcf_getppdata(); if (info->line == 0) { sigs |= (ppdata & MCFPP_DCD0) ? 0 : TIOCM_CD; sigs |= (ppdata & MCFPP_DTR0) ? 0 : TIOCM_DTR; } else if (info->line == 1) { sigs |= (ppdata & MCFPP_DCD1) ? 0 : TIOCM_CD; sigs |= (ppdata & MCFPP_DTR1) ? 0 : TIOCM_DTR; }}#endif restore_flags(flags); return(sigs);}/* * ------------------------------------------------------------ * mcfrs_stop() and mcfrs_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void mcfrs_stop(struct tty_struct *tty){ volatile unsigned char *uartp; struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "mcfrs_stop")) return; save_flags(flags); cli(); uartp = (volatile unsigned char *) info->addr; info->imr &= ~MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; restore_flags(flags);}static void mcfrs_start(struct tty_struct *tty){ volatile unsigned char *uartp; struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "mcfrs_start")) return; save_flags(flags); cli(); if (info->xmit_cnt && info->xmit_buf) { uartp = (volatile unsigned char *) info->addr; info->imr |= MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; } restore_flags(flags);}/* * ---------------------------------------------------------------------- * * Here starts the interrupt handling routines. All of the following * subroutines are declared as inline and are folded into * mcfrs_interrupt(). They were separated out for readability's sake. * * Note: mcfrs_interrupt() is a "fast" interrupt, which means that it * runs with interrupts turned off. People who may want to modify * mcfrs_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 serial.c * * and look at the resulting assemble code in serial.s. * * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 * ----------------------------------------------------------------------- *//* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. */static _INLINE_ void mcfrs_sched_event(struct mcf_serial *info, int event){ info->event |= 1 << event; queue_task(&info->tqueue, &mcf_tq_serial); mark_bh(CM206_BH);}static _INLINE_ void receive_chars(struct mcf_serial *info, struct pt_regs *regs, unsigned short rx){ volatile unsigned char *uartp; struct tty_struct *tty = info->tty; unsigned char status, ch; if (!tty) return;#if defined(CONFIG_LEDMAN) ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_RX : LEDMAN_COM1_RX);#endif uartp = (volatile unsigned char *) info->addr; while ((status = uartp[MCFUART_USR]) & MCFUART_USR_RXREADY) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; ch = uartp[MCFUART_URB]; info->stats.rx++;#ifdef CONFIG_MAGIC_SYSRQ if (mcfrs_console_inited && (info->line == mcfrs_console_port)) { if (magic_sysrq_key(ch)) continue; }#endif tty->flip.count++; if (status & MCFUART_USR_RXERR) uartp[MCFUART_UCR] = MCFUART_UCR_CMDRESETERR; if (status & MCFUART_USR_RXBREAK) { info->stats.rxbreak++; *tty->flip.flag_buf_ptr++ = TTY_BREAK; } else if (status & MCFUART_USR_RXPARITY) { info->stats.rxparity++; *tty->flip.flag_buf_ptr++ = TTY_PARITY; } else if (status & MCFUART_USR_RXOVERRUN) { info->stats.rxoverrun++; *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; } else if (status & MCFUART_USR_RXFRAMING) { info->stats.rxframing++; *tty->flip.flag_buf_ptr++ = TTY_FRAME; } else { *tty->flip.flag_buf_ptr++ = 0; } *tty->flip.char_buf_ptr++ = ch; } queue_task(&tty->flip.tqueue, &tq_timer); return;}static _INLINE_ void transmit_chars(struct mcf_serial *info){ volatile unsigned char *uartp;#if defined(CONFIG_LEDMAN) ledman_cmd(LEDMAN_CMD_SET, info->line ? LEDMAN_COM2_TX : LEDMAN_COM1_TX);#endif uartp = (volatile unsigned char *) info->addr; if (info->x_char) { /* Send special char - probably flow control */ uartp[MCFUART_UTB] = info->x_char; info->x_char = 0; info->stats.tx++; } if ((info->xmit_cnt <= 0) || info->tty->stopped) { info->imr &= ~MCFUART_UIR_TXREADY; uartp[MCFUART_UIMR] = info->imr; return; } while (uartp[MCFUART_USR] & MCFUART_USR_TXREADY) { uartp[MCFUART_UTB] = info->xmit_buf[info->xmit_tail++]; info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->stats.tx++; if (--info->xmit_cnt <= 0) break; } if (info->xmit_cnt < WAKEUP_CHARS) mcfrs_sched_event(info, RS_EVENT_WRITE_WAKEUP); return;}/* * This is the serial driver's generic interrupt routine */void mcfrs_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct mcf_serial *info; unsigned char isr; info = &mcfrs_table[(irq - IRQBASE)]; isr = (((volatile unsigned char *)info->addr)[MCFUART_UISR]) & info->imr; if (isr & MCFUART_UIR_RXREADY) receive_chars(info, regs, isr); if (isr & MCFUART_UIR_TXREADY) transmit_chars(info);#if 0 if (isr & MCFUART_UIR_DELTABREAK) { printk("%s(%d): delta break!\n", __FILE__, __LINE__); receive_chars(info, regs, isr); }#endif return;}/* * ------------------------------------------------------------------- * Here ends the serial 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 * mcfrs_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 mcfrs_sched_event(), and they get done here. */static void do_serial_bh(void){ run_task_queue(&mcf_tq_serial);}static void do_softint(void *private_){ struct mcf_serial *info = (struct mcf_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(RS_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); }}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?