📄 8253xtty.c
字号:
/* -*- linux-c -*- *//* $Id: 8253xtty.c,v 1.23 2002/02/10 22:17:25 martillo Exp $ * sab82532.c: ASYNC Driver for the SIEMENS SAB82532 DUSCC. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) * * Modified by Francois Wautier 2000 (fw@auroratech.com) * * Extended extensively by Joachim Martillo 2001 (Telford002@aol.com) * to provide synchronous/asynchronous TTY/Callout/character/network device * capabilities. * * 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 <linux/pci.h>#include "8253xctl.h"#include "sp502.h"DECLARE_TASK_QUEUE(tq_8253x_serial); /* this just initializes a list head called */ /* tq_8253x_serial*/struct tty_driver sab8253x_serial_driver, sab8253x_callout_driver, sync_sab8253x_serial_driver;int sab8253x_refcount;/* Trace things on serial device, useful for console debugging: */#undef SERIAL_LOG_DEVICE#ifdef SERIAL_LOG_DEVICEstatic void dprint_init(int tty);#endifstatic void sab8253x_change_speed(struct sab_port *port);static struct tty_struct **sab8253x_tableASY = 0; /* make dynamic */static struct tty_struct **sab8253x_tableCUA = 0; /* make dynamic */static struct tty_struct **sab8253x_tableSYN = 0; /* make dynamic */static struct termios **sab8253x_termios = 0 ;static struct termios **sab8253x_termios_locked = 0;#ifdef MODULE#undef XCONFIG_SERIAL_CONSOLE /* leaving out CONFIG_SERIAL_CONSOLE for now */#endif#ifdef XCONFIG_SERIAL_CONSOLE /* not really implemented yet */extern int serial_console;struct console sab8253x_console;int sab8253x_console_init(void);#endif#ifndef MIN#define MIN(a,b) ((a) < (b) ? (a) : (b))#endifchar sab8253x_serial_version[16];static void sab8253x_flush_to_ldisc(void *private_){ struct tty_struct *tty = (struct tty_struct *) private_; unsigned char *cp; char *fp; int count; struct sab_port *port; struct sk_buff *skb; if(tty) { port = (struct sab_port *)tty->driver_data; /* probably a silly check */ } else { return; } if(!port) { 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; fp = skb->data + (count/2); (*tty->ldisc.receive_buf)(tty, cp, fp, count/2); dev_kfree_skb_any(skb); } port->DoingInterrupt = 0;}/* only used asynchronously */static void inline sab8253x_tec_wait(struct sab_port *port){ int count = port->tec_timeout; while((READB(port, star) & SAB82532_STAR_TEC) && --count) { udelay(1); }}void sab8253x_start_tx(struct sab_port *port){ unsigned long flags; register int count; register int total; register int offset; char temporary[32]; register unsigned int slopspace; register int sendsize; unsigned int totaltransmit; unsigned fifospace; unsigned loadedcount; struct tty_struct *tty = port->tty; fifospace = port->xmit_fifo_size; loadedcount = 0; if(port->sabnext2.transmit == NULL) { return; } save_flags(flags); cli(); while(count = port->sabnext2.transmit->Count, (count & OWNER) == OWN_SAB) { count &= ~OWN_SAB; /* OWN_SAB is really 0 but cannot guarantee in the future */ if(port->sabnext2.transmit->HostVaddr) { total = (port->sabnext2.transmit->HostVaddr->tail - port->sabnext2.transmit->HostVaddr->data); /* packet size */ } else { total = 0; /* the data is only in the crc/trailer */ } if(tty && (tty->stopped || tty->hw_stopped)) { /* works for frame that only has a trailer (crc) */ port->interrupt_mask1 |= SAB82532_IMR1_XPR; WRITEB(port, imr1, port->interrupt_mask1); restore_flags(flags); /* can't send */ return; } offset = (total - count); /* offset to data still to send */ port->interrupt_mask1 &= ~(SAB82532_IMR1_ALLS); WRITEB(port, imr1, port->interrupt_mask1); port->all_sent = 0; if(READB(port,star) & SAB82532_STAR_XFW) { if(count <= fifospace) { port->xmit_cnt = count; slopspace = 0; sendsize = 0; if(port->sabnext2.transmit->sendcrc) /* obviously should not happen for async but might use for priority transmission */ { slopspace = fifospace - count; } if(slopspace) { if(count) { memcpy(temporary, &port->sabnext2.transmit->HostVaddr->data[offset], count); } sendsize = MIN(slopspace, (4 - port->sabnext2.transmit->crcindex)); /* how many bytes to send */ memcpy(&temporary[count], &((unsigned char*)(&port->sabnext2.transmit->crc)) [port->sabnext2.transmit->crcindex], sendsize); port->sabnext2.transmit->crcindex += sendsize; if(port->sabnext2.transmit->crcindex >= 4) { port->sabnext2.transmit->sendcrc = 0; } port->xmit_buf = temporary; } else { port->xmit_buf = /* set up wrifefifo variables */ &port->sabnext2.transmit->HostVaddr->data[offset]; } port->xmit_cnt += sendsize; count = 0; } else { count -= fifospace; port->xmit_cnt = fifospace; port->xmit_buf = /* set up wrifefifo variables */ &port->sabnext2.transmit->HostVaddr->data[offset]; } port->xmit_tail= 0; loadedcount = port->xmit_cnt; (*port->writefifo)(port); totaltransmit = Sab8253xCountTransmitDescriptors(port); if((sab8253xt_listsize - totaltransmit) > 2) { sab8253x_sched_event(port, SAB8253X_EVENT_WRITE_WAKEUP); } if((sab8253xt_listsize - totaltransmit) > (sab8253xt_listsize/2)) { port->buffergreedy = 0; } else { port->buffergreedy = 1; } port->xmit_buf = NULL; /* this var is used to indicate whether to call kfree */ fifospace -= loadedcount; if ((count <= 0) && (port->sabnext2.transmit->sendcrc == 0)) { port->sabnext2.transmit->Count = OWN_DRIVER;#ifdef FREEININTERRUPT /* treat this routine as if taking place in interrupt */ if(port->sabnext2.transmit->HostVaddr) { skb_unlink(port->sabnext2.transmit->HostVaddr); dev_kfree_skb_any(port->sabnext2.transmit->HostVaddr); port->sabnext2.transmit->HostVaddr = 0; /* no skb */ } port->sabnext2.transmit->crcindex = 0; /* no single byte */#endif port->sabnext2.transmit = port->sabnext2.transmit->VNext; if((port->sabnext2.transmit->Count & OWNER) == OWN_SAB) { if(fifospace > 0) { continue; /* the only place where this code really loops */ } if(fifospace < 0) { printk(KERN_ALERT "sab8253x: bad math in interrupt handler.\n"); } port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR); WRITEB(port, imr1, port->interrupt_mask1); } else { port->interrupt_mask1 |= SAB82532_IMR1_XPR; WRITEB(port, imr1, port->interrupt_mask1); } sab8253x_cec_wait(port); /* Issue a Transmit Frame command. */ WRITEB(port, cmdr, SAB82532_CMDR_XF); /* This could be optimized to load from next skbuff */ /* SAB82532_CMDR_XF is the same as SAB82532_CMDR_XTF */ restore_flags(flags); return; } sab8253x_cec_wait(port); /* Issue a Transmit Frame command. */ WRITEB(port, cmdr, SAB82532_CMDR_XF); /* same as SAB82532_CMDR_XTF */ port->sabnext2.transmit->Count = (count|OWN_SAB); } port->interrupt_mask1 &= ~(SAB82532_IMR1_XPR); WRITEB(port, imr1, port->interrupt_mask1); restore_flags(flags); return; } /* The While loop only exits via return*/ /* we get here by skipping the loop */ port->interrupt_mask1 |= SAB82532_IMR1_XPR; WRITEB(port, imr1, port->interrupt_mask1); restore_flags(flags); return;}/* * ------------------------------------------------------------ * sab8253x_stop() and sab8253x_start() * * This routines are called before setting or resetting tty->stopped. * They enable or disable transmitter interrupts, as necessary. * ------------------------------------------------------------ */static void sab8253x_stop(struct tty_struct *tty){ struct sab_port *port = (struct sab_port *)tty->driver_data; unsigned long flags; if (sab8253x_serial_paranoia_check(port, tty->device, "sab8253x_stop")) { return; } save_flags(flags); cli(); /* maybe should turn off ALLS as well but the stop flags are checked so ALLS is probably harmless and I have seen too much evil associated with that interrupt*/ port->interrupt_mask1 |= SAB82532_IMR1_XPR; WRITEB(port, imr1, port->interrupt_mask1); restore_flags(flags);}static void sab8253x_start(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_tx(port);}/* * This routine is used by the interrupt handler to schedule * processing in the software interrupt portion of the driver. *//* no obvious changes for sync tty */static void sab8253x_receive_chars(struct sab_port *port, union sab8253x_irq_status *stat){ struct tty_struct *tty = port->tty; unsigned char buf[32]; unsigned char reordered[32]; unsigned char status; int free_fifo = 0; int i, count = 0; struct sk_buff *skb; /* Read number of BYTES (Character + Status) available. */ if (stat->images[ISR0_IDX] & SAB82532_ISR0_RPF) { count = port->recv_fifo_size; free_fifo++; } if (stat->images[ISR0_IDX] & SAB82532_ISR0_TCD) { count = READB(port,rbcl) & (port->recv_fifo_size - 1); free_fifo++; } /* Issue a FIFO read command in case we where idle. */ if (stat->sreg.isr0 & SAB82532_ISR0_TIME) { sab8253x_cec_wait(port); WRITEB(port, cmdr, SAB82532_CMDR_RFRD); } if (stat->images[ISR0_IDX] & SAB82532_ISR0_RFO) { /* FIFO overflow */ free_fifo++; } /* 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); } #ifdef CONSOLE_SUPPORT if (port->is_console) { wake_up(&keypress_wait); }#endif if (!tty) { return; } if(!count) { return; } for(i = 0; i < count; i += 2) { reordered[i/2] = buf[i]; status = buf[i+1]; if (status & SAB82532_RSTAT_PE) { status = TTY_PARITY; port->icount.parity++; } else if (status & SAB82532_RSTAT_FE) { status = TTY_FRAME; port->icount.frame++; } else { status = TTY_NORMAL; } reordered[(count+i)/2] = status; } if(port->active2.receive == NULL) { return; } memcpy(port->active2.receive->HostVaddr->tail, reordered, count); port->active2.receive->HostVaddr->tail += count; port->active2.receive->HostVaddr->data_len = count; port->active2.receive->HostVaddr->len = count; if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL) /* use dev_alloc_skb because at int there is header space but so what*/ { port->icount.buf_overrun++; port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB); port->active2.receive->HostVaddr->data_len = 0; port->active2.receive->HostVaddr->len = 0; } else { skb_unlink(port->active2.receive->HostVaddr); skb_queue_tail(port->sab8253xc_rcvbuflist, port->active2.receive->HostVaddr); skb_queue_head(port->sab8253xbuflist, skb); port->active2.receive->HostVaddr = skb; port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB); } queue_task(&tty->flip.tqueue, &tq_timer);}static void sab8253x_transmit_chars(struct sab_port *port, union sab8253x_irq_status *stat){ if (stat->sreg.isr1 & SAB82532_ISR1_ALLS) /* got an all sent int? */ { port->interrupt_mask1 |= SAB82532_IMR1_ALLS; WRITEB(port, imr1, port->interrupt_mask1); port->all_sent = 1; /* not much else to do */ } /* a very weird chip -- this int only indicates this int */ sab8253x_start_tx(port);}static void sab8253x_check_status(struct sab_port *port, union sab8253x_irq_status *stat){ struct tty_struct *tty = port->tty; int modem_change = 0; mctlsig_t *sig; struct sk_buff *skb; if (!tty) { return; } if(port->active2.receive == NULL) { goto check_modem; } if (stat->images[ISR1_IDX] & SAB82532_ISR1_BRK) {#ifdef XCONFIG_SERIAL_CONSOLE if (port->is_console) { batten_down_hatches(info); /* need to add this function */ return; }#endif port->active2.receive->HostVaddr->tail[0] = 0; port->active2.receive->HostVaddr->tail[1] = TTY_PARITY; port->active2.receive->HostVaddr->tail += 2; port->active2.receive->HostVaddr->data_len = 2; port->active2.receive->HostVaddr->len = 2; if(skb = dev_alloc_skb(port->recv_fifo_size), skb == NULL) { port->icount.buf_overrun++; port->active2.receive->HostVaddr->tail = port->active2.receive->HostVaddr->data; /* clear the buffer */ port->active2.receive->Count = (port->recv_fifo_size|OWN_SAB); port->active2.receive->HostVaddr->data_len = 0; port->active2.receive->HostVaddr->len = 0; } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -