Exemple #1
0
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);

}
Exemple #3
0
/* 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);
}
Exemple #5
0
/*
 * 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;
}
Exemple #6
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;
}
Exemple #9
0
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
}