void uart_handle_dcd_change(struct uart_port *uport, unsigned int status) { struct uart_state *state = uport->state; struct tty_port *port = &state->port; struct tty_ldisc *ld = tty_ldisc_ref(port->tty); struct pps_event_time ts; if (ld && ld->ops->dcd_change) pps_get_ts(&ts); uport->icount.dcd++; #ifdef CONFIG_HARD_PPS if ((uport->flags & UPF_HARDPPS_CD) && status) hardpps(); #endif if (port->flags & ASYNC_CHECK_CD) { if (status) wake_up_interruptible(&port->open_wait); else if (port->tty) tty_hangup(port->tty); } if (ld && ld->ops->dcd_change) ld->ops->dcd_change(port->tty, status, &ts); if (ld) tty_ldisc_deref(ld); }
static void uart00_modem_status(struct uart_port *port) { unsigned int status; struct uart_icount *icount = &port->icount; struct uart_info *info = port->info; status = UART_GET_MSR(port); if (!status & (UART_MSR_DCTS_MSK | UART_MSR_DDSR_MSK | UART_MSR_TERI_MSK | UART_MSR_DDCD_MSK)) return; if (status & UART_MSR_DDCD_MSK) { icount->dcd++; #ifdef CONFIG_HARD_PPS if ((port->flags & ASYNC_HARDPPS_CD) && (status & UART_MSR_DCD_MSK)) hardpps(); #endif if (info->flags & ASYNC_CHECK_CD) { if (status & UART_MSR_DCD_MSK) wake_up_interruptible(&info->open_wait); else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) && (port->flags & ASYNC_CALLOUT_NOHUP))) { if (info->tty) tty_hangup(info->tty); } } } if (status & UART_MSR_DDSR_MSK) icount->dsr++; if (status & UART_MSR_DCTS_MSK) { icount->cts++; if (info->flags & ASYNC_CTS_FLOW) { status &= UART_MSR_CTS_MSK; if (info->tty->hw_stopped) { if (status) { info->tty->hw_stopped = 0; port->ops->start_tx(port, 0); uart_write_wakeup(port); } } else { if (!status) { info->tty->hw_stopped = 1; port->ops->stop_tx(port, 0); } } } } wake_up_interruptible(&info->delta_msr_wait); }
/* pps_kc_event - call hardpps() on PPS event * @pps: the PPS source * @ts: PPS event timestamp * @event: PPS event edge * * This function calls hardpps() when an event from bound PPS source occurs. */ void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts, int event) { unsigned long flags; /* Pass some events to kernel consumer if activated */ spin_lock_irqsave(&pps_kc_hardpps_lock, flags); if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) hardpps(&ts->ts_real, &ts->ts_raw); spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags); }
void pps_kc_event(struct pps_device *pps, struct pps_event_time *ts, int event) { unsigned long flags; spin_lock_irqsave(&pps_kc_hardpps_lock, flags); if (pps == pps_kc_hardpps_dev && event & pps_kc_hardpps_mode) hardpps(&ts->ts_real, &ts->ts_raw); spin_unlock_irqrestore(&pps_kc_hardpps_lock, flags); }
/* * take external status interrupt (only CD interests us) */ static int zs_xsisr( struct zscom *zs ) { register struct zsaline *za = (struct zsaline *)(void *)zs->zs_priv; register struct zscc_device *zsaddr = zs->zs_addr; register queue_t *q; register unsigned char zsstatus; register int loopcheck; register char *dname; #ifdef PPS_SYNC register unsigned int s; register long usec; #endif /* * pick up current state */ zsstatus = zsaddr->zscc_control; if ((za->za_rr0 ^ zsstatus) & (cdmask)) { timestamp_t cdevent; register int status; za->za_rr0 = (za->za_rr0 & ~(cdmask)) | (zsstatus & (cdmask)); #ifdef PPS_SYNC s = splclock(); #ifdef PPS_NEW usec = timestamp.tv_usec; #else usec = pps_time.tv_usec; #endif #endif /* * time stamp */ uniqtime(&cdevent.tv); #ifdef PPS_SYNC (void)splx(s); #endif /* * logical state */ status = cd_invert ? (zsstatus & cdmask) == 0 : (zsstatus & cdmask) != 0; #ifdef PPS_SYNC if (status) { usec = cdevent.tv.tv_usec - usec; if (usec < 0) usec += 1000000; hardpps(&cdevent.tv, usec); } #endif q = za->za_ttycommon.t_readq; /* * ok - now the hard part - find ourself */ loopcheck = MAXDEPTH; while (q) { if (q->q_qinfo && q->q_qinfo->qi_minfo) { dname = q->q_qinfo->qi_minfo->mi_idname; if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) { /* * back home - phew (hopping along stream queues might * prove dangerous to your health) */ if ((((parsestream_t *)(void *)q->q_ptr)->parse_status & PARSE_ENABLE) && parse_iopps(&((parsestream_t *)(void *)q->q_ptr)->parse_io, (int)(status ? SYNC_ONE : SYNC_ZERO), &cdevent)) { /* * XXX - currently we do not pass up the message, as * we should. * for a correct behaviour wee need to block out * processing until parse_iodone has been posted via * a softcall-ed routine which does the message pass-up * right now PPS information relies on input being * received */ parse_iodone(&((parsestream_t *)(void *)q->q_ptr)->parse_io); } if (status) { ((parsestream_t *)(void *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv; ++(((parsestream_t *)(void *)q->q_ptr)->parse_ppsclockev.serial); } parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname)); break; } } q = q->q_next; if (!loopcheck--) { panic("zs_xsisr: STREAMS Queue corrupted - CD event"); } } /* * only pretend that CD has been handled */ ZSDELAY(2); if (!((za->za_rr0 ^ zsstatus) & ~(cdmask))) { /* * all done - kill status indication and return */ zsaddr->zscc_control = ZSWR0_RESET_STATUS; /* might kill other conditions here */ return 0; } } if (zsstatus & cdmask) /* fake CARRIER status */ za->za_flags |= ZAS_CARR_ON; else za->za_flags &= ~ZAS_CARR_ON; /* * we are now gathered here to process some unusual external status * interrupts. * any CD events have also been handled and shouldn't be processed * by the original routine (unless we have a VERY busy port pin) * some initializations are done here, which could have been done before for * both code paths but have been avoided for minimum path length to * the uniq_time routine */ dname = (char *) 0; q = za->za_ttycommon.t_readq; loopcheck = MAXDEPTH; /* * the real thing for everything else ... */ while (q) { if (q->q_qinfo && q->q_qinfo->qi_minfo) { dname = q->q_qinfo->qi_minfo->mi_idname; if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname)) { register int (*zsisr) (struct zscom *); /* * back home - phew (hopping along stream queues might * prove dangerous to your health) */ if ((zsisr = ((struct savedzsops *)((parsestream_t *)(void *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint)) return zsisr(zs); else panic("zs_xsisr: unable to locate original ISR"); parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname)); /* * now back to our program ... */ return 0; } } q = q->q_next; if (!loopcheck--) { panic("zs_xsisr: STREAMS Queue corrupted - non CD event"); } } /* * last resort - shouldn't even come here as it indicates * corrupted TTY structures */ printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-"); if (emergencyzs && emergencyzs->zsop_xsint) emergencyzs->zsop_xsint(zs); else panic("zs_xsisr: no emergency ISR handler"); return 0; }
static void check_modem_status(struct serial_state *info) { struct tty_port *port = &info->tport; unsigned char status = ciab.pra & (SER_DCD | SER_CTS | SER_DSR); unsigned char dstatus; struct async_icount *icount; /* Determine bits that have changed */ dstatus = status ^ current_ctl_bits; current_ctl_bits = status; if (dstatus) { icount = &info->icount; /* update input line counters */ if (dstatus & SER_DSR) icount->dsr++; if (dstatus & SER_DCD) { icount->dcd++; #ifdef CONFIG_HARD_PPS if ((port->flags & ASYNC_HARDPPS_CD) && !(status & SER_DCD)) hardpps(); #endif } if (dstatus & SER_CTS) icount->cts++; wake_up_interruptible(&port->delta_msr_wait); } if ((port->flags & ASYNC_CHECK_CD) && (dstatus & SER_DCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttyS%d CD now %s...", info->line, (!(status & SER_DCD)) ? "on" : "off"); #endif if (!(status & SER_DCD)) wake_up_interruptible(&port->open_wait); else { #ifdef SERIAL_DEBUG_OPEN printk("doing serial hangup..."); #endif if (port->tty) tty_hangup(port->tty); } } if (port->flags & ASYNC_CTS_FLOW) { if (port->tty->hw_stopped) { if (!(status & SER_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start..."); #endif port->tty->hw_stopped = 0; info->IER |= UART_IER_THRI; custom.intena = IF_SETCLR | IF_TBE; mb(); /* set a pending Tx Interrupt, transmitter should restart now */ custom.intreq = IF_SETCLR | IF_TBE; mb(); tty_wakeup(port->tty); return; } } else { if ((status & SER_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop..."); #endif port->tty->hw_stopped = 1; info->IER &= ~UART_IER_THRI; /* disable Tx interrupt and remove any pending interrupts */ custom.intena = IF_TBE; mb(); custom.intreq = IF_TBE; mb(); } } } }
/* I need to do this for the SCCs, so it is left as a reminder. */ static _INLINE_ void check_modem_status(struct async_struct *info) { int status; /* struct async_icount *icount; */ struct async_icount_24 *icount; status = serial_in(info, UART_MSR); if (status & UART_MSR_ANY_DELTA) { icount = &info->state->icount; /* update input line counters */ if (status & UART_MSR_TERI) icount->rng++; if (status & UART_MSR_DDSR) icount->dsr++; if (status & UART_MSR_DDCD) { icount->dcd++; #ifdef CONFIG_HARD_PPS if ((info->flags & ASYNC_HARDPPS_CD) && (status & UART_MSR_DCD)) hardpps(); #endif } if (status & UART_MSR_DCTS) icount->cts++; wake_up_interruptible(&info->delta_msr_wait); } if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) { #if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR)) printk("ttys%d CD now %s...", info->line, (status & UART_MSR_DCD) ? "on" : "off"); #endif if (status & UART_MSR_DCD) wake_up_interruptible(&info->open_wait); else { #ifdef SERIAL_DEBUG_OPEN printk("scheduling hangup..."); #endif queue_task(&info->tqueue_hangup, &tq_scheduler); } } if (info->flags & ASYNC_CTS_FLOW) { if (info->port.tty->hw_stopped) { if (status & UART_MSR_CTS) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx start..."); #endif info->port.tty->hw_stopped = 0; info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); return; } } else { if (!(status & UART_MSR_CTS)) { #if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW)) printk("CTS tx stop..."); #endif info->port.tty->hw_stopped = 1; info->IER &= ~UART_IER_THRI; serial_out(info, UART_IER, info->IER); } } } }
int sscomrxintr(void *arg) { struct sscom_softc *sc = arg; bus_space_tag_t iot = sc->sc_iot; bus_space_handle_t ioh = sc->sc_ioh; u_char *put, *end; u_int cc; if (SSCOM_ISALIVE(sc) == 0) return 0; SSCOM_LOCK(sc); end = sc->sc_ebuf; put = sc->sc_rbput; cc = sc->sc_rbavail; do { u_char msts, delta; u_char uerstat; uint32_t ufstat; ufstat = bus_space_read_4(iot, ioh, SSCOM_UFSTAT); /* XXX: break interrupt with no character? */ if ( (ufstat & (UFSTAT_RXCOUNT|UFSTAT_RXFULL)) && !ISSET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED)) { while (cc > 0) { int cn_trapped = 0; /* get status and received character. read status register first */ uerstat = sscom_geterr(iot, ioh); put[0] = sscom_getc(iot, ioh); if (ISSET(uerstat, UERSTAT_BREAK)) { int con_trapped = 0; cn_check_magic(sc->sc_tty->t_dev, CNC_BREAK, sscom_cnm_state); if (con_trapped) continue; #if defined(KGDB) if (ISSET(sc->sc_hwflags, SSCOM_HW_KGDB)) { kgdb_connect(1); continue; } #endif } put[1] = uerstat; cn_check_magic(sc->sc_tty->t_dev, put[0], sscom_cnm_state); if (!cn_trapped) { put += 2; if (put >= end) put = sc->sc_rbuf; cc--; } ufstat = bus_space_read_4(iot, ioh, SSCOM_UFSTAT); if ( (ufstat & (UFSTAT_RXFULL|UFSTAT_RXCOUNT)) == 0 ) break; } /* * Current string of incoming characters ended because * no more data was available or we ran out of space. * Schedule a receive event if any data was received. * If we're out of space, turn off receive interrupts. */ sc->sc_rbput = put; sc->sc_rbavail = cc; if (!ISSET(sc->sc_rx_flags, RX_TTY_OVERFLOWED)) sc->sc_rx_ready = 1; /* * See if we are in danger of overflowing a buffer. If * so, use hardware flow control to ease the pressure. */ if (!ISSET(sc->sc_rx_flags, RX_IBUF_BLOCKED) && cc < sc->sc_r_hiwat) { SET(sc->sc_rx_flags, RX_IBUF_BLOCKED); sscom_hwiflow(sc); } /* * If we're out of space, disable receive interrupts * until the queue has drained a bit. */ if (!cc) { SET(sc->sc_rx_flags, RX_IBUF_OVERFLOWED); sscom_disable_rxint(sc); sc->sc_ucon &= ~UCON_ERRINT; bus_space_write_4(iot, ioh, SSCOM_UCON, sc->sc_ucon); } } msts = sc->sc_read_modem_status(sc); delta = msts ^ sc->sc_msts; sc->sc_msts = msts; #ifdef notyet /* * Pulse-per-second (PSS) signals on edge of DCD? * Process these even if line discipline is ignoring DCD. */ if (delta & sc->sc_ppsmask) { struct timeval tv; if ((msr & sc->sc_ppsmask) == sc->sc_ppsassert) { /* XXX nanotime() */ microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &sc->ppsinfo.assert_timestamp); if (sc->ppsparam.mode & PPS_OFFSETASSERT) { timespecadd(&sc->ppsinfo.assert_timestamp, &sc->ppsparam.assert_offset, &sc->ppsinfo.assert_timestamp); } #ifdef PPS_SYNC if (sc->ppsparam.mode & PPS_HARDPPSONASSERT) hardpps(&tv, tv.tv_usec); #endif sc->ppsinfo.assert_sequence++; sc->ppsinfo.current_mode = sc->ppsparam.mode; } else if ((msr & sc->sc_ppsmask) == sc->sc_ppsclear) { /* XXX nanotime() */ microtime(&tv); TIMEVAL_TO_TIMESPEC(&tv, &sc->ppsinfo.clear_timestamp); if (sc->ppsparam.mode & PPS_OFFSETCLEAR) { timespecadd(&sc->ppsinfo.clear_timestamp, &sc->ppsparam.clear_offset, &sc->ppsinfo.clear_timestamp); } #ifdef PPS_SYNC if (sc->ppsparam.mode & PPS_HARDPPSONCLEAR) hardpps(&tv, tv.tv_usec); #endif sc->ppsinfo.clear_sequence++; sc->ppsinfo.current_mode = sc->ppsparam.mode; } } #endif /* * Process normal status changes */ if (ISSET(delta, sc->sc_msr_mask)) { SET(sc->sc_msr_delta, delta); /* * Stop output immediately if we lose the output * flow control signal or carrier detect. */ if (ISSET(~msts, sc->sc_msr_mask)) { sc->sc_tbc = 0; sc->sc_heldtbc = 0; #ifdef SSCOM_DEBUG if (sscom_debug) sscomstatus(sc, "sscomintr "); #endif } sc->sc_st_check = 1; } /* * Done handling any receive interrupts. */ /* * If we've delayed a parameter change, do it * now, and restart * output. */ if ((ufstat & UFSTAT_TXCOUNT) == 0) { /* XXX: we should check transmitter empty also */ if (sc->sc_heldchange) { sscom_loadchannelregs(sc); sc->sc_heldchange = 0; sc->sc_tbc = sc->sc_heldtbc; sc->sc_heldtbc = 0; } } } while (0); SSCOM_UNLOCK(sc); /* Wake up the poller. */ softint_schedule(sc->sc_si); #ifdef RND_COM rnd_add_uint32(&sc->rnd_source, iir | rsr); #endif return 1; }
void pps_event(struct pps_state *pps, int event) { struct bintime bt; struct timespec ts, *tsp, *osp; u_int tcount, *pcount; int foff, fhard; pps_seq_t *pseq; KASSERT(pps != NULL, ("NULL pps pointer in pps_event")); /* If the timecounter was wound up underneath us, bail out. */ if (pps->capgen == 0 || pps->capgen != pps->capth->th_generation) return; /* Things would be easier with arrays. */ if (event == PPS_CAPTUREASSERT) { tsp = &pps->ppsinfo.assert_timestamp; osp = &pps->ppsparam.assert_offset; foff = pps->ppsparam.mode & PPS_OFFSETASSERT; fhard = pps->kcmode & PPS_CAPTUREASSERT; pcount = &pps->ppscount[0]; pseq = &pps->ppsinfo.assert_sequence; } else { tsp = &pps->ppsinfo.clear_timestamp; osp = &pps->ppsparam.clear_offset; foff = pps->ppsparam.mode & PPS_OFFSETCLEAR; fhard = pps->kcmode & PPS_CAPTURECLEAR; pcount = &pps->ppscount[1]; pseq = &pps->ppsinfo.clear_sequence; } /* * If the timecounter changed, we cannot compare the count values, so * we have to drop the rest of the PPS-stuff until the next event. */ if (pps->ppstc != pps->capth->th_counter) { pps->ppstc = pps->capth->th_counter; *pcount = pps->capcount; pps->ppscount[2] = pps->capcount; return; } /* Convert the count to a timespec. */ tcount = pps->capcount - pps->capth->th_offset_count; tcount &= pps->capth->th_counter->tc_counter_mask; bt = pps->capth->th_offset; bintime_addx(&bt, pps->capth->th_scale * tcount); bintime_add(&bt, &boottimebin); bintime2timespec(&bt, &ts); /* If the timecounter was wound up underneath us, bail out. */ if (pps->capgen != pps->capth->th_generation) return; *pcount = pps->capcount; (*pseq)++; *tsp = ts; if (foff) { timespecadd(tsp, osp); if (tsp->tv_nsec < 0) { tsp->tv_nsec += 1000000000; tsp->tv_sec -= 1; } } #ifdef PPS_SYNC if (fhard) { uint64_t scale; /* * Feed the NTP PLL/FLL. * The FLL wants to know how many (hardware) nanoseconds * elapsed since the previous event. */ tcount = pps->capcount - pps->ppscount[2]; pps->ppscount[2] = pps->capcount; tcount &= pps->capth->th_counter->tc_counter_mask; scale = (uint64_t)1 << 63; scale /= pps->capth->th_counter->tc_frequency; scale *= 2; bt.sec = 0; bt.frac = 0; bintime_addx(&bt, scale * tcount); bintime2timespec(&bt, &ts); hardpps(tsp, ts.tv_nsec + 1000000000 * ts.tv_sec); } #endif }