static enum hrtimer_restart hrtimer_event()

in generators/pps_gen_parport.c [57:139]


static enum hrtimer_restart hrtimer_event(struct hrtimer *timer)
{
	struct timespec64 expire_time, ts1, ts2, ts3, dts;
	struct pps_generator_pp *dev;
	struct parport *port;
	long lim, delta;
	unsigned long flags;

	/* We have to disable interrupts here. The idea is to prevent
	 * other interrupts on the same processor to introduce random
	 * lags while polling the clock. ktime_get_real_ts64() takes <1us on
	 * most machines while other interrupt handlers can take much
	 * more potentially.
	 *
	 * NB: approx time with blocked interrupts =
	 * send_delay + 3 * SAFETY_INTERVAL
	 */
	local_irq_save(flags);

	/* first of all we get the time stamp... */
	ktime_get_real_ts64(&ts1);
	expire_time = ktime_to_timespec64(hrtimer_get_softexpires(timer));
	dev = container_of(timer, struct pps_generator_pp, timer);
	lim = NSEC_PER_SEC - send_delay - dev->port_write_time;

	/* check if we are late */
	if (expire_time.tv_sec != ts1.tv_sec || ts1.tv_nsec > lim) {
		local_irq_restore(flags);
		pr_err("we are late this time %lld.%09ld\n",
				(s64)ts1.tv_sec, ts1.tv_nsec);
		goto done;
	}

	/* busy loop until the time is right for an assert edge */
	do {
		ktime_get_real_ts64(&ts2);
	} while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);

	/* set the signal */
	port = dev->pardev->port;
	port->ops->write_control(port, SIGNAL);

	/* busy loop until the time is right for a clear edge */
	lim = NSEC_PER_SEC - dev->port_write_time;
	do {
		ktime_get_real_ts64(&ts2);
	} while (expire_time.tv_sec == ts2.tv_sec && ts2.tv_nsec < lim);

	/* unset the signal */
	port->ops->write_control(port, NO_SIGNAL);

	ktime_get_real_ts64(&ts3);

	local_irq_restore(flags);

	/* update calibrated port write time */
	dts = timespec64_sub(ts3, ts2);
	dev->port_write_time =
		(dev->port_write_time + timespec64_to_ns(&dts)) >> 1;

done:
	/* update calibrated hrtimer error */
	dts = timespec64_sub(ts1, expire_time);
	delta = timespec64_to_ns(&dts);
	/* If the new error value is bigger then the old, use the new
	 * value, if not then slowly move towards the new value. This
	 * way it should be safe in bad conditions and efficient in
	 * good conditions.
	 */
	if (delta >= hrtimer_error)
		hrtimer_error = delta;
	else
		hrtimer_error = (3 * hrtimer_error + delta) >> 2;

	/* update the hrtimer expire time */
	hrtimer_set_expires(timer,
			ktime_set(expire_time.tv_sec + 1,
				NSEC_PER_SEC - (send_delay +
				dev->port_write_time + SAFETY_INTERVAL +
				2 * hrtimer_error)));

	return HRTIMER_RESTART;
}