serial167.c
来自「linux 内核源代码」· C语言 代码 · 共 2,458 行 · 第 1/5 页
C
2,458 行
} 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; return IRQ_HANDLED;} /* cy_modem_interrupt */static irqreturn_t cd2401_tx_interrupt(int irq, void *dev_id){ 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 IRQ_HANDLED; } 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 IRQ_HANDLED; } /* 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; return IRQ_HANDLED;} /* cy_tx_interrupt */static irqreturn_t cd2401_rx_interrupt(int irq, void *dev_id){ 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; int len; /* 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 len = tty_buffer_request_room(tty, char_count); while (len--) { data = base_addr[CyRDR]; tty_insert_flip_char(tty, data, TTY_NORMAL);#ifdef CYCLOM_16Y_HACK udelay(10L);#endif } tty_schedule_flip(tty); } /* end of service */ base_addr[CyREOIR] = save_cnt ? 0 : CyNOTRANS; return IRQ_HANDLED;} /* 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 keventd work queue and sets a request flag * 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 void do_softint(struct work_struct *ugly_api){ struct cyclades_port *info = container_of(ugly_api, struct cyclades_port, tqueue); 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; } 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)) { tty_wakeup(tty); }} /* do_softint *//* This is called whenever a port becomes active; interrupts are enabled and DTR & RTS are turned on. */static int startup(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_zeroed_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 local_irq_save(flags); 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; local_irq_restore(flags);#ifdef SERIAL_DEBUG_OPEN printk(" done\n");#endif return 0;} /* startup */void start_xmit(struct cyclades_port *info){ unsigned long flags; volatile unsigned char *base_addr = (u_char *) BASE_ADDR; int channel; channel = info->line; local_irq_save(flags); base_addr[CyCAR] = channel; base_addr[CyIER] |= CyTxMpty; local_irq_restore(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 void shutdown(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. */ local_irq_save(flags); if (info->xmit_buf) { free_page((unsigned long)info->xmit_buf); info->xmit_buf = NULL; } 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; local_irq_restore(flags);#ifdef SERIAL_DEBUG_OPEN printk(" done\n");#endif} /* shutdown *//* * This routine finds or computes the various line characteristics. */static void config_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 mapping is slightly different from serial.c because there is still the possibility of supporting 75 kbit/sec with the Cyclades board.) */ if (i & CBAUDEX) { if (i == B57600) i = 16; else if (i == B115200) i = 18;#ifdef B78600 else if (i == B78600) i = 17;#endif else info->tty->termios->c_cflag &= ~CBAUDEX; }#endif if (i == 15) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) i += 1; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) i += 3; } /* Don't ever change the speed of the console port. It will * run at the speed specified in bootinfo, or at 19.2K */ /* Actually, it should run at whatever speed 166Bug was using */ /* Note info->timeout isn't used at present */ if (info != serial_console_info) { info->tbpr = baud_bpr[i]; /* Tx BPR */ info->tco = baud_co[i]; /* Tx CO */ info->rbpr = baud_bpr[i]; /* Rx BPR */ info->rco = baud_co[i] >> 5; /* Rx CO */ if (baud_table[i] == 134) { info->timeout = (info->xmit_fifo_size * HZ * 30 / 269) + 2; /* get it right for 134.5 baud */ } else if (baud_table[i]) { info->timeout = (info->xmit_fifo_size * HZ * 15 / baud_table[i]) + 2; /* this needs to be propagated into the card info */ } else { info->timeout = 0; } } /* By tradition (is it a standard?) a baud rate of zero implies the line should be/has been closed. A bit later in this routine such a test is performed. */ /* byte size and parity */ info->cor7 = 0; info->cor6 = 0; info->cor5 = 0; info->cor4 = (info->default_threshold ? info->default_threshold : baud_cor4[i]); /* receive threshold */ /* Following two lines added 101295, RGH. */ /* It is obviously wrong to access CyCORx, and not info->corx here, * try and remember to fix it later! */ channel = info->line; base_addr[CyCAR] = (u_char) channel; if (C_CLOCAL(info->tty)) { if (base_addr[CyIER] & CyMdmCh) base_addr[CyIER] &= ~CyMdmCh; /* without modem intr */ /* ignore 1->0 modem transitions */ if (base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) base_addr[CyCOR4] &= ~(CyDSR | CyCTS | CyDCD); /* ignore 0->1 modem transitions */ if (base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) base_addr[CyCOR5] &= ~(CyDSR | CyCTS | CyDCD); } else { if ((base_addr[CyIER] & CyMdmCh) != CyMdmCh) base_addr[CyIER] |= CyMdmCh; /* with modem intr */ /* act on 1->0 modem transitions */ if ((base_addr[CyCOR4] & (CyDSR | CyCTS | CyDCD)) != (CyDSR | CyCTS | CyDCD)) base_addr[CyCOR4] |= CyDSR | CyCTS | CyDCD; /* act on 0->1 modem transitions */ if ((base_addr[CyCOR5] & (CyDSR | CyCTS | CyDCD)) != (CyDSR | CyCTS | CyDCD)) base_addr[CyCOR5] |= CyDSR | CyCTS | CyDCD; } info->cor3 = (cflag & CSTOPB) ? Cy_2_STOP : Cy_1_STOP; info->cor2 = CyETC; switch (cflag & CSIZE) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?