cyclades.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,734 行 · 第 1/5 页
C
1,734 行
cy_writeb(address + (CyCAR<<index), 0); cy_writeb(address + (CySRER<<index), cy_readb(address + (CySRER<<index)) | CyTxRdy); local_irq_restore(flags); /* Wait ... */ udelay(5000L); /* Check which interrupt is in use */ irq = probe_irq_off(irqs); /* Clean up */ save_xir = (u_char) cy_readb(address + (CyTIR<<index)); save_car = cy_readb(address + (CyCAR<<index)); cy_writeb(address + (CyCAR<<index), (save_xir & 0x3)); cy_writeb(address + (CySRER<<index), cy_readb(address + (CySRER<<index)) & ~CyTxRdy); cy_writeb(address + (CyTIR<<index), (save_xir & 0x3f)); cy_writeb(address + (CyCAR<<index), (save_car)); cy_writeb(address + (Cy_ClrIntr<<index), 0); /* Cy_ClrIntr is 0x1800 */ return (irq > 0)? irq : 0;}#endif /* CONFIG_ISA *//* The real interrupt service routine is called whenever the card wants its hand held--chars received, out buffer empty, modem change, etc. */static irqreturn_tcyy_interrupt(int irq, void *dev_id, struct pt_regs *regs){ struct tty_struct *tty; int status; struct cyclades_card *cinfo; struct cyclades_port *info; void __iomem *base_addr, *card_base_addr; int chip; int save_xir, channel, save_car; char data; volatile int char_count; int outch; int i,j,index; int too_many; int had_work; int mdm_change; int mdm_status; if((cinfo = (struct cyclades_card *)dev_id) == 0){#ifdef CY_DEBUG_INTERRUPTS printk("cyy_interrupt: spurious interrupt %d\n\r", irq);#endif return IRQ_NONE; /* spurious interrupt */ } card_base_addr = cinfo->base_addr; index = cinfo->bus_index; /* This loop checks all chips in the card. Make a note whenever _any_ chip had some work to do, as this is considered an indication that there will be more to do. Only when no chip has any work does this outermost loop exit. */ do{ had_work = 0; for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) { base_addr = cinfo->base_addr + (cy_chip_offset[chip]<<index); too_many = 0; while ( (status = cy_readb(base_addr+(CySVRR<<index))) != 0x00) { had_work++; /* The purpose of the following test is to ensure that no chip can monopolize the driver. This forces the chips to be checked in a round-robin fashion (after draining each of a bunch (1000) of characters). */ if(1000<too_many++){ break; } if (status & CySRReceive) { /* reception interrupt */#ifdef CY_DEBUG_INTERRUPTS printk("cyy_interrupt: rcvd intr, chip %d\n\r", chip);#endif /* determine the channel & change to that context */ spin_lock(&cinfo->card_lock); save_xir = (u_char) cy_readb(base_addr+(CyRIR<<index)); channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->first_line; info = &cy_port[i]; info->last_active = jiffies; save_car = cy_readb(base_addr+(CyCAR<<index)); cy_writeb(base_addr+(CyCAR<<index), save_xir); /* if there is nowhere to put the data, discard it */ if(info->tty == 0){ j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask); if ( j == CyIVRRxEx ) { /* exception */ data = cy_readb(base_addr+(CyRDSR<<index)); } else { /* normal character reception */ char_count = cy_readb(base_addr+(CyRDCR<<index)); while(char_count--){ data = cy_readb(base_addr+(CyRDSR<<index)); } } }else{ /* there is an open port for this data */ tty = info->tty; j = (cy_readb(base_addr+(CyRIVR<<index)) & CyIVRMask); if ( j == CyIVRRxEx ) { /* exception */ data = cy_readb(base_addr+(CyRDSR<<index)); /* For statistics only */ if (data & CyBREAK) info->icount.brk++; else if(data & CyFRAME) info->icount.frame++; else if(data & CyPARITY) info->icount.parity++; else if(data & CyOVERRUN) info->icount.overrun++; if(data & info->ignore_status_mask){ info->icount.rx++; continue; } if (tty->flip.count < TTY_FLIPBUF_SIZE){ tty->flip.count++; if (data & info->read_status_mask){ if(data & CyBREAK){ *tty->flip.flag_buf_ptr++ = TTY_BREAK; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); info->icount.rx++; if (info->flags & ASYNC_SAK){ do_SAK(tty); } }else if(data & CyFRAME){ *tty->flip.flag_buf_ptr++ = TTY_FRAME; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); info->icount.rx++; info->idle_stats.frame_errs++; }else if(data & CyPARITY){ *tty->flip.flag_buf_ptr++ = TTY_PARITY; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); info->icount.rx++; info->idle_stats.parity_errs++; }else if(data & CyOVERRUN){ *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; *tty->flip.char_buf_ptr++ = 0; info->icount.rx++; /* If the flip buffer itself is overflowing, we still lose the next incoming character. */ if(tty->flip.count < TTY_FLIPBUF_SIZE){ tty->flip.count++; *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = cy_readb(base_addr+(CyRDSR<<index)); info->icount.rx++; } info->idle_stats.overruns++; /* These two conditions may imply */ /* a normal read should be done. */ /* }else if(data & CyTIMEOUT){ */ /* }else if(data & CySPECHAR){ */ }else{ *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = 0; info->icount.rx++; } }else{ *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = 0; info->icount.rx++; } }else{ /* there was a software buffer overrun and nothing could be done about it!!! */ info->icount.buf_overrun++; info->idle_stats.overruns++; } } else { /* normal character reception */ /* load # chars available from the chip */ char_count = cy_readb(base_addr+(CyRDCR<<index));#ifdef CY_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--){ if (tty->flip.count >= TTY_FLIPBUF_SIZE){ break; } tty->flip.count++; data = cy_readb(base_addr+(CyRDSR<<index)); *tty->flip.flag_buf_ptr++ = TTY_NORMAL; *tty->flip.char_buf_ptr++ = data; info->idle_stats.recv_bytes++; info->icount.rx++;#ifdef CY_16Y_HACK udelay(10L);#endif } info->idle_stats.recv_idle = jiffies; } schedule_delayed_work(&tty->flip.work, 1); } /* end of service */ cy_writeb(base_addr+(CyRIR<<index), (save_xir & 0x3f)); cy_writeb(base_addr+(CyCAR<<index), (save_car)); spin_unlock(&cinfo->card_lock); } if (status & CySRTransmit) { /* transmission interrupt */ /* Since we only get here when the transmit buffer is empty, we know we can always stuff a dozen characters. */#ifdef CY_DEBUG_INTERRUPTS printk("cyy_interrupt: xmit intr, chip %d\n\r", chip);#endif /* determine the channel & change to that context */ spin_lock(&cinfo->card_lock); save_xir = (u_char) cy_readb(base_addr+(CyTIR<<index)); channel = (u_short ) (save_xir & CyIRChannel); i = channel + chip * 4 + cinfo->first_line; save_car = cy_readb(base_addr+(CyCAR<<index)); cy_writeb(base_addr+(CyCAR<<index), save_xir); /* validate the port# (as configured and open) */ if( (i < 0) || (NR_PORTS <= i) ){ cy_writeb(base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxRdy); goto txend; } info = &cy_port[i]; info->last_active = jiffies; if(info->tty == 0){ cy_writeb(base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxRdy); goto txdone; } /* load the on-chip space for outbound data */ char_count = info->xmit_fifo_size; if(info->x_char) { /* send special char */ outch = info->x_char; cy_writeb(base_addr+(CyTDR<<index), outch); char_count--; info->icount.tx++; info->x_char = 0; } if (info->breakon || info->breakoff) { if (info->breakon) { cy_writeb(base_addr + (CyTDR<<index), 0); cy_writeb(base_addr + (CyTDR<<index), 0x81); info->breakon = 0; char_count -= 2; } if (info->breakoff) { cy_writeb(base_addr + (CyTDR<<index), 0); cy_writeb(base_addr + (CyTDR<<index), 0x83); info->breakoff = 0; char_count -= 2; } } while (char_count-- > 0){ if (!info->xmit_cnt){ if (cy_readb(base_addr+(CySRER<<index))&CyTxMpty) { cy_writeb(base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxMpty); } else { cy_writeb(base_addr+(CySRER<<index), ((cy_readb(base_addr+(CySRER<<index)) & ~CyTxRdy) | CyTxMpty)); } goto txdone; } if (info->xmit_buf == 0){ cy_writeb(base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxRdy); goto txdone; } if (info->tty->stopped || info->tty->hw_stopped){ cy_writeb(base_addr+(CySRER<<index), cy_readb(base_addr+(CySRER<<index)) & ~CyTxRdy); goto txdone; } /* 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) & (SERIAL_XMIT_SIZE - 1); cy_writeb(base_addr+(CyTDR<<index), outch); info->icount.tx++; }else{ if(char_count > 1){ info->xmit_cnt--; info->xmit_tail = (info->xmit_tail + 1) & (SERIAL_XMIT_SIZE - 1); cy_writeb(base_addr+(CyTDR<<index), outch); cy_writeb(base_addr+(CyTDR<<index), 0); info->icount.tx++; char_count--; }else{ } } } txdone: if (info->xmit_cnt < WAKEUP_CHARS) { cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP); } txend:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?