📄 serial167.c
字号:
*tty->flip.char_buf_ptr++ = 0; } }else{ *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = 0; } }else{ /* there was a software buffer overrun and nothing could be done about it!!! */ } } queue_task(&tty->flip.tqueue, &tq_timer); /* end of service */ base_addr[CyREOIR] = rfoc ? 0 : CyNOTRANS;} /* cy_rxerr_interrupt */static voidcd2401_modem_interrupt(int irq, void *dev_id, struct pt_regs *fp){ struct cyclades_port *info; volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; int channel; int mdm_change; int mdm_status; /* determine the channel and change to that context */ channel = (u_short ) (base_addr[CyLICR] >> 2); info = &cy_port[channel]; info->last_active = jiffies; mdm_change = base_addr[CyMISR]; mdm_status = base_addr[CyMSVR1]; if(info->tty == 0){ /* nowhere to put the data, ignore it */ ; }else{ if((mdm_change & CyDCD) && (info->flags & ASYNC_CHECK_CD)){ if(mdm_status & CyDCD){/* CP('!'); */ cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP); }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE) &&(info->flags & ASYNC_CALLOUT_NOHUP))){/* CP('@'); */ cy_sched_event(info, Cy_EVENT_HANGUP); } } if((mdm_change & CyCTS) && (info->flags & ASYNC_CTS_FLOW)){ if(info->tty->stopped){ if(mdm_status & CyCTS){ /* !!! cy_start isn't used because... */ info->tty->stopped = 0; base_addr[CyIER] |= CyTxMpty; cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } }else{ if(!(mdm_status & CyCTS)){ /* !!! cy_stop isn't used because... */ info->tty->stopped = 1; base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); } } } if(mdm_status & CyDSR){ } } base_addr[CyMEOIR] = 0;} /* cy_modem_interrupt */static voidcd2401_tx_interrupt(int irq, void *dev_id, struct pt_regs *fp){ struct cyclades_port *info; volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; int channel; int char_count, saved_cnt; int outch; /* determine the channel and change to that context */ channel = (u_short ) (base_addr[CyLICR] >> 2);#ifdef CONFIG_REMOTE_DEBUG if (channel == DEBUG_PORT) { panic ("TxInt on debug port!!!"); }#endif info = &cy_port[channel]; /* validate the port number (as configured and open) */ if( (channel < 0) || (NR_PORTS <= channel) ){ base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); base_addr[CyTEOIR] = CyNOTRANS; return; } info->last_active = jiffies; if(info->tty == 0){ base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); if (info->xmit_cnt < WAKEUP_CHARS) { cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } base_addr[CyTEOIR] = CyNOTRANS; return; } /* load the on-chip space available for outbound data */ saved_cnt = char_count = base_addr[CyTFTC]; if(info->x_char) { /* send special char */ outch = info->x_char; base_addr[CyTDR] = outch; char_count--; info->x_char = 0; } if (info->x_break){ /* The Cirrus chip requires the "Embedded Transmit Commands" of start break, delay, and end break sequences to be sent. The duration of the break is given in TICs, which runs at HZ (typically 100) and the PPR runs at 200 Hz, so the delay is duration * 200/HZ, and thus a break can run from 1/100 sec to about 5/4 sec. Need to check these values - RGH 141095. */ base_addr[CyTDR] = 0; /* start break */ base_addr[CyTDR] = 0x81; base_addr[CyTDR] = 0; /* delay a bit */ base_addr[CyTDR] = 0x82; base_addr[CyTDR] = info->x_break*200/HZ; base_addr[CyTDR] = 0; /* terminate break */ base_addr[CyTDR] = 0x83; char_count -= 7; info->x_break = 0; } while (char_count > 0){ if (!info->xmit_cnt){ base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); break; } if (info->xmit_buf == 0){ base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); break; } if (info->tty->stopped || info->tty->hw_stopped){ base_addr[CyIER] &= ~(CyTxMpty|CyTxRdy); break; } /* Because the Embedded Transmit Commands have been enabled, we must check to see if the escape character, NULL, is being sent. If it is, we must ensure that there is room for it to be doubled in the output stream. Therefore we no longer advance the pointer when the character is fetched, but rather wait until after the check for a NULL output character. (This is necessary because there may not be room for the two chars needed to send a NULL. */ outch = info->xmit_buf[info->xmit_tail]; if( outch ){ info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (PAGE_SIZE - 1); base_addr[CyTDR] = outch; char_count--; }else{ if(char_count > 1){ info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (PAGE_SIZE - 1); base_addr[CyTDR] = outch; base_addr[CyTDR] = 0; char_count--; char_count--; }else{ break; } } } if (info->xmit_cnt < WAKEUP_CHARS) { cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } base_addr[CyTEOIR] = (char_count != saved_cnt) ? 0 : CyNOTRANS;} /* cy_tx_interrupt */static voidcd2401_rx_interrupt(int irq, void *dev_id, struct pt_regs *fp){ struct tty_struct *tty; struct cyclades_port *info; volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; int channel; char data; int char_count; int save_cnt; /* determine the channel and change to that context */ channel = (u_short ) (base_addr[CyLICR] >> 2); info = &cy_port[channel]; info->last_active = jiffies; save_cnt = char_count = base_addr[CyRFOC];#ifdef CONFIG_REMOTE_DEBUG if (channel == DEBUG_PORT) { while (char_count--) { data = base_addr[CyRDR]; queueDebugChar(data); } } else#endif /* if there is nowhere to put the data, discard it */ if(info->tty == 0){ while(char_count--){ data = base_addr[CyRDR]; } }else{ /* there is an open port for this data */ tty = info->tty; /* load # characters available from the chip */#ifdef CYCLOM_ENABLE_MONITORING ++info->mon.int_count; info->mon.char_count += char_count; if (char_count > info->mon.char_max) info->mon.char_max = char_count; info->mon.char_last = char_count;#endif while(char_count--){ data = base_addr[CyRDR]; if (tty->flip.count >= TTY_FLIPBUF_SIZE){ continue; } tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = data;#ifdef CYCLOM_16Y_HACK udelay(10L);#endif } queue_task(&tty->flip.tqueue, &tq_timer); } /* end of service */ base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS;} /* cy_rx_interrupt *//* * 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 * cy_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 cy_sched_event(), and they get done here. * * This is done through one level of indirection--the task queue. * When a hardware interrupt service routine wants service by the * driver's bottom half, it enqueues the appropriate tq_struct (one * per port) to the tq_cyclades work queue and sets a request flag * via mark_bh for processing that queue. When the time is right, * do_cyclades_bh is called (because of the mark_bh) and it requests * that the work queue be processed. * * Although this may seem unwieldy, it gives the system a way to * pass an argument (in this case the pointer to the cyclades_port * structure) to the bottom half of the driver. Previous kernels * had to poll every port to see if that port needed servicing. */static voiddo_cyclades_bh(void){ run_task_queue(&tq_cyclades);} /* do_cyclades_bh */static voiddo_softint(void *private_){ struct cyclades_port *info = (struct cyclades_port *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(Cy_EVENT_HANGUP, &info->event)) { tty_hangup(info->tty); wake_up_interruptible(&info->open_wait); info->flags &= ~(ASYNC_NORMAL_ACTIVE| ASYNC_CALLOUT_ACTIVE); } if (test_and_clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) { wake_up_interruptible(&info->open_wait); } if (test_and_clear_bit(Cy_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); }} /* do_softint *//* This is called whenever a port becomes active; interrupts are enabled and DTR & RTS are turned on. */static intstartup(struct cyclades_port * info){ unsigned long flags; volatile unsigned char *base_addr = (unsigned char *)BASE_ADDR; int channel; if (info->flags & ASYNC_INITIALIZED){ return 0; } if (!info->type){ if (info->tty){ set_bit(TTY_IO_ERROR, &info->tty->flags); } return 0; } if (!info->xmit_buf){ info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL); if (!info->xmit_buf){ return -ENOMEM; } } config_setup(info); channel = info->line;#ifdef SERIAL_DEBUG_OPEN printk("startup channel %d\n", channel);#endif save_flags(flags); cli(); base_addr[CyCAR] = (u_char)channel; write_cy_cmd(base_addr,CyENB_RCVR|CyENB_XMTR); base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */ base_addr[CyMSVR1] = CyRTS;/* CP('S');CP('1'); */ base_addr[CyMSVR2] = CyDTR;#ifdef SERIAL_DEBUG_DTR printk("cyc: %d: raising DTR\n", __LINE__); printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);#endif base_addr[CyIER] |= CyRxData; info->flags |= ASYNC_INITIALIZED; if (info->tty){ clear_bit(TTY_IO_ERROR, &info->tty->flags); } info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; restore_flags(flags);#ifdef SERIAL_DEBUG_OPEN printk(" done\n");#endif return 0;} /* startup */voidstart_xmit( struct cyclades_port *info ){ unsigned long flags; volatile unsigned char *base_addr = (u_char *)BASE_ADDR; int channel; channel = info->line; save_flags(flags); cli(); base_addr[CyCAR] = channel; base_addr[CyIER] |= CyTxMpty; restore_flags(flags);} /* start_xmit *//* * This routine shuts down a serial port; interrupts are disabled, * and DTR is dropped if the hangup on close termio flag is on. */static voidshutdown(struct cyclades_port * info){ unsigned long flags; volatile unsigned char *base_addr = (u_char *)BASE_ADDR; int channel; if (!(info->flags & ASYNC_INITIALIZED)){/* CP('$'); */ return; } channel = info->line;#ifdef SERIAL_DEBUG_OPEN printk("shutdown channel %d\n", channel);#endif /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE SENT BEFORE DROPPING THE LINE !!! (Perhaps set some flag that is read when XMTY happens.) Other choices are to delay some fixed interval or schedule some later processing. */ save_flags(flags); cli(); if (info->xmit_buf){ free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } base_addr[CyCAR] = (u_char)channel; if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { base_addr[CyMSVR1] = 0;/* CP('C');CP('1'); */ base_addr[CyMSVR2] = 0;#ifdef SERIAL_DEBUG_DTR printk("cyc: %d: dropping DTR\n", __LINE__); printk(" status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);#endif } write_cy_cmd(base_addr,CyDIS_RCVR); /* it may be appropriate to clear _XMIT at some later date (after testing)!!! */ if (info->tty){ set_bit(TTY_IO_ERROR, &info->tty->flags); } info->flags &= ~ASYNC_INITIALIZED; restore_flags(flags);#ifdef SERIAL_DEBUG_OPEN printk(" done\n");#endif return;} /* shutdown *//* * This routine finds or computes the various line characteristics. */static voidconfig_setup(struct cyclades_port * info){ unsigned long flags; volatile unsigned char *base_addr = (u_char *)BASE_ADDR; int channel; unsigned cflag; int i; unsigned char ti, need_init_chan = 0; if (!info->tty || !info->tty->termios){ return; } if (info->line == -1){ return; } cflag = info->tty->termios->c_cflag; /* baud rate */ i = cflag & CBAUD;#ifdef CBAUDEX/* Starting with kernel 1.1.65, there is direct support for higher baud rates. The following code supports those changes. The conditional aspect allows this driver to be used for earlier as well as later kernel versions. (The
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -