📄 8253xsyn.c
字号:
/* -*- linux-c -*- *//* $Id: 8253xsyn.c,v 1.17 2002/02/10 22:17:25 martillo Exp $ * 8253xsyn.c: SYNC TTY Driver for the SIEMENS SAB8253X DUSCC. * * Implementation, modifications and extensions * Copyright (C) 2001 By Joachim Martillo, Telford Tools, Inc. * * 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; either version * 2 of the License, or (at your option) any later version. *//* Standard in kernel modules */#define DEFINE_VARIABLE#include <linux/module.h> /* Specifically, a module */#include <asm/io.h>#include <linux/timer.h>#include <linux/interrupt.h>#include <linux/tty.h>#include <linux/tty_flip.h>#include <linux/mm.h>#include <linux/version.h>#include <asm/uaccess.h>#include "8253xctl.h"#include "8253x.h"#include <linux/pci.h>#include <linux/fs.h>#ifdef MODULE#undef XCONFIG_SERIAL_CONSOLE#endifstatic void sab8253x_flush_to_ldiscS(void *private_) /* need a separate version for sync there are no flags associated with received sync TTY data*/{ struct tty_struct *tty = (struct tty_struct *) private_; unsigned char *cp; int count; struct sab_port *port; struct sk_buff *skb; if(tty) { port = (struct sab_port *)tty->driver_data; } else { return; } if(port == NULL) { return; } if (test_bit(TTY_DONT_FLIP, &tty->flags)) { queue_task(&tty->flip.tqueue, &tq_timer); return; } /* note that a hangup may have occurred -- perhaps should check for that */ port->DoingInterrupt = 1; while(port->sab8253xc_rcvbuflist && (skb_queue_len(port->sab8253xc_rcvbuflist) > 0)) { skb = skb_dequeue(port->sab8253xc_rcvbuflist); count = skb->data_len; cp = skb->data; (*tty->ldisc.receive_buf)(tty, cp, 0, count); dev_kfree_skb_any(skb); } port->DoingInterrupt = 0;}void sab8253x_flush_charsS(struct tty_struct *tty){ struct sab_port *port = (struct sab_port *)tty->driver_data; if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_flush_chars")) { return; } if ((Sab8253xCountTransmit(port) <= 0) || tty->stopped || tty->hw_stopped) { /* can't flush */ return; } sab8253x_start_txS(port);}/* * ------------------------------------------------------------ * sab8253x_stopS() and sab8253x_startS() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */void sab8253x_stopS(struct tty_struct *tty){ struct sab_port *port = (struct sab_port *)tty->driver_data; /* can't do anything here */ if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_stop")) { return; } /* interrupt handles it all*/ /* turning off XPR is not an option in sync mode */}void sab8253x_startS(struct tty_struct *tty){ struct sab_port *port = (struct sab_port *)tty->driver_data; if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_start")) { return; } sab8253x_start_txS(port);}static void sab8253x_receive_charsS(struct sab_port *port, union sab8253x_irq_status *stat){ struct tty_struct *tty = port->tty; unsigned char buf[32]; int free_fifo = 0; int reset_fifo = 0; int msg_done = 0; int msg_bad = 0; int count = 0; int total_size = 0; int rstatus = 0; struct sk_buff *skb; /* Read number of BYTES (Character + Status) available. */ if((stat->images[ISR1_IDX] & SAB82532_ISR1_RDO) || (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) ) { ++msg_bad; ++free_fifo; ++reset_fifo; } else { if (stat->images[ISR0_IDX] & SAB82532_ISR0_RPF) { count = port->recv_fifo_size; ++free_fifo; } if (stat->images[ISR0_IDX] & SAB82532_ISR0_RME) { count = READB(port, rbcl); count &= (port->recv_fifo_size - 1); ++msg_done; ++free_fifo; total_size = READB(port, rbch); if(total_size & SAB82532_RBCH_OV) /* need to revisit for 4096 byte frames */ { msg_bad++; } rstatus = READB(port, rsta); if((rstatus & SAB82532_RSTA_VFR) == 0) { msg_bad++; } if(rstatus & SAB82532_RSTA_RDO) { msg_bad++; } if((rstatus & SAB82532_RSTA_CRC) == 0) { msg_bad++; } if(rstatus & SAB82532_RSTA_RAB) { msg_bad++; } } } /* Read the FIFO. */ (*port->readfifo)(port, buf, count); /* Issue Receive Message Complete command. */ if (free_fifo) { sab8253x_cec_wait(port); WRITEB(port, cmdr, SAB82532_CMDR_RMC); } if(reset_fifo) { sab8253x_cec_wait(port); WRITEB(port, cmdr, SAB82532_CMDR_RHR); } if(msg_bad) { port->msgbufindex = 0; return; } memcpy(&port->msgbuf[port->msgbufindex], buf, count); port->msgbufindex += count; #ifdef CONSOLE_SUPPORT if (port->is_console) { wake_up(&keypress_wait); }#endif if(msg_done) { if(port->msgbufindex <= 3) /* min is 1 char + 2 CRC + status byte */ { port->msgbufindex = 0; return; } total_size = port->msgbufindex - 3; /* strip off the crc16 and the status byte */ port->msgbufindex = 0; /* ignore the receive buffer waiting -- we know the correct size here */ if (!tty) { return; } if(skb = dev_alloc_skb(total_size), skb) { memcpy(skb->data, &port->msgbuf[0], total_size); skb->tail = (skb->data + total_size); skb->data_len = total_size; skb->len = total_size; skb_queue_tail(port->sab8253xc_rcvbuflist, skb); } queue_task(&tty->flip.tqueue, &tq_timer); /* clear out flip buffer as fast as possible * maybe should not be done unconditionally hear * but should be within the above consequence * clause */ }}static void sab8253x_check_statusS(struct sab_port *port, union sab8253x_irq_status *stat){ struct tty_struct *tty = port->tty; int modem_change = 0; mctlsig_t *sig; if (!tty) { return; } /* check_modem:*/ /* Checking DCD */ sig = &port->dcd; if (stat->images[sig->irq] & sig->irqmask) { sig->val = ISON(port,dcd); port->icount.dcd++; modem_change++; } /* Checking CTS */ sig = &port->cts; if (stat->images[sig->irq] & sig->irqmask) { sig->val = ISON(port,cts); port->icount.cts++; modem_change++; } /* Checking DSR */ sig = &port->dsr; if (stat->images[sig->irq] & sig->irqmask) { sig->val = ISON(port,dsr); port->icount.dsr++; modem_change++; } if (modem_change) { wake_up_interruptible(&port->delta_msr_wait); } sig = &port->dcd; if ((port->flags & FLAG8253X_CHECK_CD) && (stat->images[sig->irq] & sig->irqmask)) { if (sig->val) { wake_up_interruptible(&port->open_wait); } else if (!((port->flags & FLAG8253X_CALLOUT_ACTIVE) && (port->flags & FLAG8253X_CALLOUT_NOHUP))) {#if 0 /* requires more investigation */ MOD_INC_USE_COUNT; if (schedule_task(&port->tqueue_hangup) == 0) { MOD_DEC_USE_COUNT; }#endif } } sig = &port->cts; if (port->flags & FLAG8253X_CTS_FLOW) { /* not setting this yet */ if (port->tty->hw_stopped) { if (sig->val) { port->tty->hw_stopped = 0; sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP); sab8253x_start_txS(port); } } else { if(!(getccr2configS(port) & SAB82532_CCR2_TOE)) { if (!(sig->val)) { port->tty->hw_stopped = 1; } } } }}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void sab8253x_change_speedS(struct sab_port *port){ unsigned long flags,baud; tcflag_t cflag; u8 ccr2=0,ccr4=0,ebrg=0; int i, bits;#ifdef DEBUGGING printk("Change speed! ");#endif if (!port->tty || !port->tty->termios) {#ifdef DEBUGGING printk("NOT!\n");#endif return; } #ifdef DEBUGGING printk(" for real.\n");#endif cflag = port->tty->termios->c_cflag; /* Byte size and parity */ switch (cflag & CSIZE) { case CS5: bits = 7; break; case CS6: bits = 8; break; case CS7: bits = 9; break; default: case CS8: bits = 10; break; } if (cflag & CSTOPB) { bits++; } if (cflag & PARENB) { bits++; } /* Determine EBRG values based on the "encoded"baud rate */ i = cflag & CBAUD; switch(i) { case B0: baud=0; break; case B50: baud=100; break; case B75: baud=150; break; case B110: baud=220; break; case B134: baud=269; break; case B150: baud=300; break; case B200: baud=400; break; case B300: baud=600; break; case B600: baud=1200; break; case B1200: baud=2400; break; case B1800: baud=3600; break; case B2400: baud=4800; break; case B4800: baud=9600; break; case B9600: baud=19200; break; case B19200: baud=38400; break; case B38400: if(port->custspeed) { baud=port->custspeed<<1; } else { baud=76800; } break; case B57600: baud=115200; break;#ifdef SKIPTHIS case B76800: baud=153600; break; case B153600: baud=307200; break;#endif case B230400: baud=460800; break; case B460800: baud=921600; break; case B115200: default: baud=230400; break; } if(!sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud))) { printk("Aurora Warning. baudrate %ld could not be set! Using 115200",baud); baud=230400; sab8253x_baud(port,baud,&ebrg,&ccr2,&ccr4,&(port->baud)); } if (port->baud) port->timeout = (port->xmit_fifo_size * HZ * bits) / port->baud; else port->timeout = 0; port->timeout += HZ / 50; /* Add .02 seconds of slop */ /* CTS flow control flags */ if (cflag & CRTSCTS) port->flags |= FLAG8253X_CTS_FLOW; else port->flags &= ~(FLAG8253X_CTS_FLOW); if (cflag & CLOCAL) port->flags &= ~(FLAG8253X_CHECK_CD); else port->flags |= FLAG8253X_CHECK_CD; if (port->tty) port->tty->hw_stopped = 0; /* * Set up parity check flag * XXX: not implemented, yet. */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) /* * Characters to ignore * XXX: not implemented, yet. */ /* * !!! ignore all characters if CREAD is not set * XXX: not implemented, yet. */ if ((cflag & CREAD) == 0) port->ignore_status_mask |= SAB82532_ISR0_RPF; save_flags(flags); cli(); sab8253x_cec_wait(port); WRITEB(port, bgr, ebrg); WRITEB(port, ccr2, READB(port, ccr2) & ~(0xc0)); /* clear out current baud rage */ WRITEB(port, ccr2, READB(port, ccr2) | ccr2); WRITEB(port, ccr4, (READB(port,ccr4) & ~SAB82532_CCR4_EBRG) | ccr4); if (port->flags & FLAG8253X_CTS_FLOW) { WRITEB(port, mode, READB(port,mode) & ~(SAB82532_MODE_RTS)); port->interrupt_mask1 &= ~(SAB82532_IMR1_CSC); WRITEB(port, imr1, port->interrupt_mask1); } else { WRITEB(port, mode, READB(port,mode) | SAB82532_MODE_RTS); port->interrupt_mask1 |= SAB82532_IMR1_CSC; WRITEB(port, imr1, port->interrupt_mask1); } WRITEB(port, mode, READB(port, mode) | SAB82532_MODE_RAC); restore_flags(flags);}void sab8253x_set_termiosS(struct tty_struct *tty, struct termios *old_termios){ struct sab_port *port = (struct sab_port *)tty->driver_data; if((tty->termios->c_cflag == old_termios->c_cflag) && (RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { return; } if(!port) { return; } sab8253x_change_speedS(port); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { LOWER(port,rts); LOWER(port,dtr); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { RAISE(port,dtr); if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS)) { RAISE(port,rts); } } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; sab8253x_startS(tty); }}static int sab8253x_startupS(struct sab_port *port){ unsigned long flags; int retval = 0; save_flags(flags); cli(); port->msgbufindex = 0; port->xmit_buf = NULL; port->buffergreedy = 0; if (port->flags & FLAG8253X_INITIALIZED) { goto errout; } if (!port->regs) { if (port->tty) { set_bit(TTY_IO_ERROR, &port->tty->flags); } retval = -ENODEV; goto errout; } /* * Initialize the Hardware */ sab8253x_init_lineS(port); #if 0 /* maybe should be conditional */ if (port->tty->termios->c_cflag & CBAUD) {#endif /* Activate RTS */ RAISE(port,rts); /* Activate DTR */ RAISE(port,dtr);#if 0 }#endif /* * Initialize the modem signals values */ port->dcd.val=ISON(port,dcd); port->cts.val=ISON(port,cts); port->dsr.val=ISON(port,dsr); /* * Finally, enable interrupts */ port->interrupt_mask0 = SAB82532_IMR0_RFS | SAB82532_IMR0_PCE | SAB82532_IMR0_PLLA | SAB82532_IMR0_RSC | SAB82532_IMR0_CDSC; /*((port->ccontrol.ccr2 & SAB82532_CCR2_TOE) ? SAB82532_IMR0_CDSC : 0); */ WRITEB(port,imr0,port->interrupt_mask0); port->interrupt_mask1 = SAB82532_IMR1_EOP | SAB82532_IMR1_XMR | SAB82532_IMR1_TIN | SAB82532_IMR1_XPR; WRITEB(port, imr1, port->interrupt_mask1); port->all_sent = 1; if (port->tty) { clear_bit(TTY_IO_ERROR, &port->tty->flags); } port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; /* * and set the speed of the serial port */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -