⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 time.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
		}	}	/*	 * Do not try to get the alternate port aib if the clock	 * is not in sync yet.	 */	if (!eacr.es)		return eacr;	/*	 * If steai is available we can get the information about	 * the other port immediately. If only stetr is available the	 * data-port bit toggle has to be used.	 */	if (test_bit(ETR_FLAG_STEAI, &etr_flags)) {		if (eacr.p0 && !etr_port0_uptodate) {			etr_steai_cv(&etr_port0, ETR_STEAI_PORT_0);			etr_port0_uptodate = 1;		}		if (eacr.p1 && !etr_port1_uptodate) {			etr_steai_cv(&etr_port1, ETR_STEAI_PORT_1);			etr_port1_uptodate = 1;		}	} else {		/*		 * One port was updated above, if the other		 * port is not uptodate toggle dp bit.		 */		if ((eacr.p0 && !etr_port0_uptodate) ||		    (eacr.p1 && !etr_port1_uptodate))			eacr.dp ^= 1;		else			eacr.dp = 0;	}	return eacr;}/* * Write new etr control register if it differs from the current one. * Return 1 if etr_tolec has been updated as well. */static void etr_update_eacr(struct etr_eacr eacr){	int dp_changed;	if (memcmp(&etr_eacr, &eacr, sizeof(eacr)) == 0)		/* No change, return. */		return;	/*	 * The disable of an active port of the change of the data port	 * bit can/will cause a change in the data port.	 */	dp_changed = etr_eacr.e0 > eacr.e0 || etr_eacr.e1 > eacr.e1 ||		(etr_eacr.dp ^ eacr.dp) != 0;	etr_eacr = eacr;	etr_setr(&etr_eacr);	if (dp_changed)		etr_tolec = get_clock();}/* * ETR tasklet. In this function you'll find the main logic. In * particular this is the only function that calls etr_update_eacr(), * it "controls" the etr control register. */static void etr_work_fn(struct work_struct *work){	unsigned long long now;	struct etr_eacr eacr;	struct etr_aib aib;	int sync_port;	/* Create working copy of etr_eacr. */	eacr = etr_eacr;	/* Check for the different events and their immediate effects. */	eacr = etr_handle_events(eacr);	/* Check if ETR is supposed to be active. */	eacr.ea = eacr.p0 || eacr.p1;	if (!eacr.ea) {		/* Both ports offline. Reset everything. */		eacr.dp = eacr.es = eacr.sl = 0;		on_each_cpu(etr_disable_sync_clock, NULL, 0, 1);		del_timer_sync(&etr_timer);		etr_update_eacr(eacr);		set_bit(ETR_FLAG_EACCES, &etr_flags);		return;	}	/* Store aib to get the current ETR status word. */	BUG_ON(etr_stetr(&aib) != 0);	etr_port0.esw = etr_port1.esw = aib.esw;	/* Copy status word. */	now = get_clock();	/*	 * Update the port information if the last stepping port change	 * or data port change is older than 1.6 seconds.	 */	if (now >= etr_tolec + (1600000 << 12))		eacr = etr_handle_update(&aib, eacr);	/*	 * Select ports to enable. The prefered synchronization mode is PPS.	 * If a port can be enabled depends on a number of things:	 * 1) The port needs to be online and uptodate. A port is not	 *    disabled just because it is not uptodate, but it is only	 *    enabled if it is uptodate.	 * 2) The port needs to have the same mode (pps / etr).	 * 3) The port needs to be usable -> etr_port_valid() == 1	 * 4) To enable the second port the clock needs to be in sync.	 * 5) If both ports are useable and are ETR ports, the network id	 *    has to be the same.	 * The eacr.sl bit is used to indicate etr mode vs. pps mode.	 */	if (eacr.p0 && aib.esw.psc0 == etr_lpsc_pps_mode) {		eacr.sl = 0;		eacr.e0 = 1;		if (!etr_mode_is_pps(etr_eacr))			eacr.es = 0;		if (!eacr.es || !eacr.p1 || aib.esw.psc1 != etr_lpsc_pps_mode)			eacr.e1 = 0;		// FIXME: uptodate checks ?		else if (etr_port0_uptodate && etr_port1_uptodate)			eacr.e1 = 1;		sync_port = (etr_port0_uptodate &&			     etr_port_valid(&etr_port0, 0)) ? 0 : -1;		clear_bit(ETR_FLAG_EACCES, &etr_flags);	} else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_pps_mode) {		eacr.sl = 0;		eacr.e0 = 0;		eacr.e1 = 1;		if (!etr_mode_is_pps(etr_eacr))			eacr.es = 0;		sync_port = (etr_port1_uptodate &&			     etr_port_valid(&etr_port1, 1)) ? 1 : -1;		clear_bit(ETR_FLAG_EACCES, &etr_flags);	} else if (eacr.p0 && aib.esw.psc0 == etr_lpsc_operational_step) {		eacr.sl = 1;		eacr.e0 = 1;		if (!etr_mode_is_etr(etr_eacr))			eacr.es = 0;		if (!eacr.es || !eacr.p1 ||		    aib.esw.psc1 != etr_lpsc_operational_alt)			eacr.e1 = 0;		else if (etr_port0_uptodate && etr_port1_uptodate &&			 etr_compare_network(&etr_port0, &etr_port1))			eacr.e1 = 1;		sync_port = (etr_port0_uptodate &&			     etr_port_valid(&etr_port0, 0)) ? 0 : -1;		clear_bit(ETR_FLAG_EACCES, &etr_flags);	} else if (eacr.p1 && aib.esw.psc1 == etr_lpsc_operational_step) {		eacr.sl = 1;		eacr.e0 = 0;		eacr.e1 = 1;		if (!etr_mode_is_etr(etr_eacr))			eacr.es = 0;		sync_port = (etr_port1_uptodate &&			     etr_port_valid(&etr_port1, 1)) ? 1 : -1;		clear_bit(ETR_FLAG_EACCES, &etr_flags);	} else {		/* Both ports not usable. */		eacr.es = eacr.sl = 0;		sync_port = -1;		set_bit(ETR_FLAG_EACCES, &etr_flags);	}	/*	 * If the clock is in sync just update the eacr and return.	 * If there is no valid sync port wait for a port update.	 */	if (eacr.es || sync_port < 0) {		etr_update_eacr(eacr);		etr_set_tolec_timeout(now);		return;	}	/*	 * Prepare control register for clock syncing	 * (reset data port bit, set sync check control.	 */	eacr.dp = 0;	eacr.es = 1;	/*	 * Update eacr and try to synchronize the clock. If the update	 * of eacr caused a stepping port switch (or if we have to	 * assume that a stepping port switch has occured) or the	 * clock syncing failed, reset the sync check control bit	 * and set up a timer to try again after 0.5 seconds	 */	etr_update_eacr(eacr);	if (now < etr_tolec + (1600000 << 12) ||	    etr_sync_clock(&aib, sync_port) != 0) {		/* Sync failed. Try again in 1/2 second. */		eacr.es = 0;		etr_update_eacr(eacr);		etr_set_sync_timeout();	} else		etr_set_tolec_timeout(now);}/* * Sysfs interface functions */static struct sysdev_class etr_sysclass = {	set_kset_name("etr")};static struct sys_device etr_port0_dev = {	.id	= 0,	.cls	= &etr_sysclass,};static struct sys_device etr_port1_dev = {	.id	= 1,	.cls	= &etr_sysclass,};/* * ETR class attributes */static ssize_t etr_stepping_port_show(struct sysdev_class *class, char *buf){	return sprintf(buf, "%i\n", etr_port0.esw.p);}static SYSDEV_CLASS_ATTR(stepping_port, 0400, etr_stepping_port_show, NULL);static ssize_t etr_stepping_mode_show(struct sysdev_class *class, char *buf){	char *mode_str;	if (etr_mode_is_pps(etr_eacr))		mode_str = "pps";	else if (etr_mode_is_etr(etr_eacr))		mode_str = "etr";	else		mode_str = "local";	return sprintf(buf, "%s\n", mode_str);}static SYSDEV_CLASS_ATTR(stepping_mode, 0400, etr_stepping_mode_show, NULL);/* * ETR port attributes */static inline struct etr_aib *etr_aib_from_dev(struct sys_device *dev){	if (dev == &etr_port0_dev)		return etr_port0_online ? &etr_port0 : NULL;	else		return etr_port1_online ? &etr_port1 : NULL;}static ssize_t etr_online_show(struct sys_device *dev, char *buf){	unsigned int online;	online = (dev == &etr_port0_dev) ? etr_port0_online : etr_port1_online;	return sprintf(buf, "%i\n", online);}static ssize_t etr_online_store(struct sys_device *dev,			      const char *buf, size_t count){	unsigned int value;	value = simple_strtoul(buf, NULL, 0);	if (value != 0 && value != 1)		return -EINVAL;	if (test_bit(ETR_FLAG_ENOSYS, &etr_flags))		return -ENOSYS;	if (dev == &etr_port0_dev) {		if (etr_port0_online == value)			return count;	/* Nothing to do. */		etr_port0_online = value;		set_bit(ETR_EVENT_PORT0_CHANGE, &etr_events);		schedule_work(&etr_work);	} else {		if (etr_port1_online == value)			return count;	/* Nothing to do. */		etr_port1_online = value;		set_bit(ETR_EVENT_PORT1_CHANGE, &etr_events);		schedule_work(&etr_work);	}	return count;}static SYSDEV_ATTR(online, 0600, etr_online_show, etr_online_store);static ssize_t etr_stepping_control_show(struct sys_device *dev, char *buf){	return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ?		       etr_eacr.e0 : etr_eacr.e1);}static SYSDEV_ATTR(stepping_control, 0400, etr_stepping_control_show, NULL);static ssize_t etr_mode_code_show(struct sys_device *dev, char *buf){	if (!etr_port0_online && !etr_port1_online)		/* Status word is not uptodate if both ports are offline. */		return -ENODATA;	return sprintf(buf, "%i\n", (dev == &etr_port0_dev) ?		       etr_port0.esw.psc0 : etr_port0.esw.psc1);}static SYSDEV_ATTR(state_code, 0400, etr_mode_code_show, NULL);static ssize_t etr_untuned_show(struct sys_device *dev, char *buf){	struct etr_aib *aib = etr_aib_from_dev(dev);	if (!aib || !aib->slsw.v1)		return -ENODATA;	return sprintf(buf, "%i\n", aib->edf1.u);}static SYSDEV_ATTR(untuned, 0400, etr_untuned_show, NULL);static ssize_t etr_network_id_show(struct sys_device *dev, char *buf){	struct etr_aib *aib = etr_aib_from_dev(dev);	if (!aib || !aib->slsw.v1)		return -ENODATA;	return sprintf(buf, "%i\n", aib->edf1.net_id);}static SYSDEV_ATTR(network, 0400, etr_network_id_show, NULL);static ssize_t etr_id_show(struct sys_device *dev, char *buf){	struct etr_aib *aib = etr_aib_from_dev(dev);	if (!aib || !aib->slsw.v1)		return -ENODATA;	return sprintf(buf, "%i\n", aib->edf1.etr_id);}static SYSDEV_ATTR(id, 0400, etr_id_show, NULL);static ssize_t etr_port_number_show(struct sys_device *dev, char *buf){	struct etr_aib *aib = etr_aib_from_dev(dev);	if (!aib || !aib->slsw.v1)		return -ENODATA;	return sprintf(buf, "%i\n", aib->edf1.etr_pn);}static SYSDEV_ATTR(port, 0400, etr_port_number_show, NULL);static ssize_t etr_coupled_show(struct sys_device *dev, char *buf){	struct etr_aib *aib = etr_aib_from_dev(dev);	if (!aib || !aib->slsw.v3)		return -ENODATA;	return sprintf(buf, "%i\n", aib->edf3.c);}static SYSDEV_ATTR(coupled, 0400, etr_coupled_show, NULL);static ssize_t etr_local_time_show(struct sys_device *dev, char *buf){	struct etr_aib *aib = etr_aib_from_dev(dev);	if (!aib || !aib->slsw.v3)		return -ENODATA;	return sprintf(buf, "%i\n", aib->edf3.blto);}static SYSDEV_ATTR(local_time, 0400, etr_local_time_show, NULL);static ssize_t etr_utc_offset_show(struct sys_device *dev, char *buf){	struct etr_aib *aib = etr_aib_from_dev(dev);	if (!aib || !aib->slsw.v3)		return -ENODATA;	return sprintf(buf, "%i\n", aib->edf3.buo);}static SYSDEV_ATTR(utc_offset, 0400, etr_utc_offset_show, NULL);static struct sysdev_attribute *etr_port_attributes[] = {	&attr_online,	&attr_stepping_control,	&attr_state_code,	&attr_untuned,	&attr_network,	&attr_id,	&attr_port,	&attr_coupled,	&attr_local_time,	&attr_utc_offset,	NULL};static int __init etr_register_port(struct sys_device *dev){	struct sysdev_attribute **attr;	int rc;	rc = sysdev_register(dev);	if (rc)		goto out;	for (attr = etr_port_attributes; *attr; attr++) {		rc = sysdev_create_file(dev, *attr);		if (rc)			goto out_unreg;	}	return 0;out_unreg:	for (; attr >= etr_port_attributes; attr--)		sysdev_remove_file(dev, *attr);	sysdev_unregister(dev);out:	return rc;}static void __init etr_unregister_port(struct sys_device *dev){	struct sysdev_attribute **attr;	for (attr = etr_port_attributes; *attr; attr++)		sysdev_remove_file(dev, *attr);	sysdev_unregister(dev);}static int __init etr_init_sysfs(void){	int rc;	rc = sysdev_class_register(&etr_sysclass);	if (rc)		goto out;	rc = sysdev_class_create_file(&etr_sysclass, &attr_stepping_port);	if (rc)		goto out_unreg_class;	rc = sysdev_class_create_file(&etr_sysclass, &attr_stepping_mode);	if (rc)		goto out_remove_stepping_port;	rc = etr_register_port(&etr_port0_dev);	if (rc)		goto out_remove_stepping_mode;	rc = etr_register_port(&etr_port1_dev);	if (rc)		goto out_remove_port0;	return 0;out_remove_port0:	etr_unregister_port(&etr_port0_dev);out_remove_stepping_mode:	sysdev_class_remove_file(&etr_sysclass, &attr_stepping_mode);out_remove_stepping_port:	sysdev_class_remove_file(&etr_sysclass, &attr_stepping_port);out_unreg_class:	sysdev_class_unregister(&etr_sysclass);out:	return rc;}device_initcall(etr_init_sysfs);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -