[LinuxPPS] [PATCH] SysFS support

Rodolfo Giometti giometti at enneenne.com
Tue Jan 23 20:01:32 CET 2007


Hello,

here a patch to add sysfs support. By using this patch you get a new
class into the system named "pps":

   $ ls /sys/class/pps/    
   00/  01/  02/

Each number is a PPS source ID and inside that directory you can find
several info about the PPS source (just like procfs support):

   $ ls /sys/class/pps/01/
   assert	clear  echo  mode  name  path  subsystem@  uevent
   $ cat /sys/class/pps/01/name 
   serial1
   $ cat /sys/class/pps/01/path 
   /dev/ttyS1
   $ cat /sys/class/pps/01/assert 
   0.000000000 #0

The patch is not yet into master branch and you can find it into
"dev_sysfs_support" branch. After some test (and feedback :) I'll move
the patch into master.

With this patch I think LinuxPPS is ready to try access into Linux
main tree. :) That's why I decided to start preparing the whole patch
and some documentation to submit to the LKML.

Soggestions from __everyone__ is wellcomed and encouraged! :D

Ciao,

Rodolfo

-- 

GNU/Linux Solutions                  e-mail:    giometti at enneenne.com
Linux Device Driver                             giometti at gnudd.com
Embedded Systems                     		giometti at linux.it
UNIX programming                     phone:     +39 349 2432127
-------------- next part --------------
diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile
index d60480b..cc51203 100644
--- a/drivers/pps/Makefile
+++ b/drivers/pps/Makefile
@@ -2,6 +2,6 @@
 # Makefile for the PPS core.
 #
 
-pps_core-objs			+= pps.o kapi.o procfs.o
+pps_core-objs			+= pps.o kapi.o sysfs.o procfs.o
 obj-$(CONFIG_PPS)		+= pps_core.o
 obj-y				+= clients/
diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c
index 8aa9c32..2487778 100644
--- a/drivers/pps/kapi.c
+++ b/drivers/pps/kapi.c
@@ -90,6 +90,10 @@ static inline int __linuxpps_register_source(struct linuxpps_source_info_s *info
 	linuxpps_source[i].params.mode = default_params;
 	init_waitqueue_head(&linuxpps_source[i].queue);
 
+	ret = linuxpps_sysfs_create_source_entry(info, i);
+	if (ret < 0)
+		err("unable to create sysfs entry for source %d", i);
+
 	ret = linuxpps_procfs_create_source_entry(info, i);
 	if (ret < 0)
 		err("unable to create procfs entry for source %d", i);
@@ -123,6 +127,7 @@ static inline void __linuxpps_unregister_source(struct linuxpps_source_info_s *i
 	}
 
 	/* Deallocate the PPS source */
+	linuxpps_sysfs_remove_source_entry(info, i);
 	linuxpps_procfs_remove_source_entry(info, i);
 	linuxpps_source[i].info = NULL; 
 } 
diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c
index b8e7b0d..c7c754e 100644
--- a/drivers/pps/pps.c
+++ b/drivers/pps/pps.c
@@ -329,6 +329,7 @@ static void linuxpps_nl_data_ready(struct sock *sk, int len)
 
 void __exit linuxpps_exit(void)
 {
+	linuxpps_sysfs_unregister();
 	linuxpps_procfs_unregister();
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
 	sock_release(nl_sk->sk_socket);
@@ -358,6 +359,13 @@ int __init linuxpps_init(void)
 	/* Init the main struct */
 	memset(linuxpps_source, 0, sizeof(struct linuxpps_s)*LINUXPPS_MAX_SOURCES);
 
+	/* Register to sysfs */
+	ret = linuxpps_sysfs_register();
+	if (ret < 0) {
+		err("unable to register sysfs");
+		goto linuxpps_sysfs_register_error;
+	}
+
 	/* Register to procfs */
 	ret = linuxpps_procfs_register();
 	if (ret < 0) {
@@ -372,6 +380,10 @@ int __init linuxpps_init(void)
 
 linuxpps_procfs_register_error :
 
+	linuxpps_sysfs_unregister();
+
+linuxpps_sysfs_register_error :
+
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,0)
 	sock_release(nl_sk->sk_socket);
 #else
diff --git a/drivers/pps/sysfs.c b/drivers/pps/sysfs.c
new file mode 100644
index 0000000..98833cc
--- /dev/null
+++ b/drivers/pps/sysfs.c
@@ -0,0 +1,200 @@
+/*
+ * sysfs.c -- sysfs support
+ *
+ *
+ * Copyright (C) 2007   Rodolfo Giometti <giometti at linux.it>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/string.h>
+
+#include <linux/timepps.h>
+#include <linux/pps.h>
+
+/* ----- Private functions -------------------------------------------- */
+
+static ssize_t linuxpps_show_assert(struct class_device *cdev, char *buf)
+{
+	struct linuxpps_s *dev = class_get_devdata(cdev);
+
+	return sprintf(buf, "%ld.%09ld #%ld\n",
+			dev->assert_tu.tspec.tv_sec,
+			dev->assert_tu.tspec.tv_nsec,
+			dev->assert_sequence);
+}
+
+static ssize_t linuxpps_show_clear(struct class_device *cdev, char *buf)
+{
+	struct linuxpps_s *dev = class_get_devdata(cdev);
+
+	return sprintf(buf, "%ld.%09ld #%ld\n",
+			dev->clear_tu.tspec.tv_sec,
+			dev->clear_tu.tspec.tv_nsec,
+			dev->clear_sequence);
+}
+
+static ssize_t linuxpps_show_mode(struct class_device *cdev, char *buf)
+{
+	struct linuxpps_source_info_s *info = to_pps_info(cdev);
+
+	return sprintf(buf, "%4x\n", info->mode);
+}
+
+static ssize_t linuxpps_show_echo(struct class_device *cdev, char *buf)
+{
+	struct linuxpps_source_info_s *info = to_pps_info(cdev);
+
+	return sprintf(buf, "%d\n", !!info->echo);
+}
+
+static ssize_t linuxpps_show_name(struct class_device *cdev, char *buf)
+{
+	struct linuxpps_source_info_s *info = to_pps_info(cdev);
+
+	return sprintf(buf, "%s\n", info->name);
+}
+
+static ssize_t linuxpps_show_path(struct class_device *cdev, char *buf)
+{
+	struct linuxpps_source_info_s *info = to_pps_info(cdev);
+
+	return sprintf(buf, "%s\n", info->path);
+}
+
+/* ----- Files definitions -------------------------------------------- */
+
+#define DECLARE_INFO_ATTR(_name, _mode, _show, _store)			\
+{									\
+	.attr   = {							\
+		.name = __stringify(_name),				\
+		.mode = _mode, 						\
+		.owner = THIS_MODULE,					\
+	},								\
+	.show   = _show,						\
+	.store  = _store,						\
+}
+
+static struct class_device_attribute linuxpps_class_device_attributes[] = {
+	DECLARE_INFO_ATTR(assert, 0644, linuxpps_show_assert, NULL),
+	DECLARE_INFO_ATTR(clear, 0644, linuxpps_show_clear, NULL),
+	DECLARE_INFO_ATTR(mode, 0644, linuxpps_show_mode, NULL),
+	DECLARE_INFO_ATTR(echo, 0644, linuxpps_show_echo, NULL),
+	DECLARE_INFO_ATTR(name, 0644, linuxpps_show_name, NULL),
+	DECLARE_INFO_ATTR(path, 0644, linuxpps_show_path, NULL),
+};
+
+/* ----- Class definitions -------------------------------------------- */
+
+static void linuxpps_class_release(struct class_device *cdev)
+{
+	/* Nop??? */
+}
+
+static struct class linuxpps_class = {
+	.name = "pps",
+	.release = linuxpps_class_release,
+};
+
+/* ----- Public functions --------------------------------------------- */
+
+void linuxpps_sysfs_remove_source_entry(struct linuxpps_source_info_s *info, int id) {
+	int i;
+
+	/* Sanity checks */
+	if (info == NULL || info->dir == NULL || id >= LINUXPPS_MAX_SOURCES)
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(linuxpps_class_device_attributes); i++)
+		class_device_remove_file(&info->class_dev,
+					&linuxpps_class_device_attributes[i]);
+
+	class_device_unregister(&info->class_dev);
+}
+
+int linuxpps_sysfs_create_source_entry(struct linuxpps_source_info_s *info, int id) {
+	char buf[32];
+	int i, ret;
+
+	/* Sanity checks */
+	if (info == NULL || id >= LINUXPPS_MAX_SOURCES)
+		return -EINVAL;
+
+	/* Create dir class device name */
+	sprintf(buf, "%.02d", id);
+
+	/* Setup the class struct */
+	memset(&info->class_dev, 0, sizeof(struct class_device));
+	info->class_dev.class = &linuxpps_class;
+	strlcpy(info->class_dev.class_id, buf, KOBJ_NAME_LEN);
+	class_set_devdata(&info->class_dev, &linuxpps_source[id]);
+
+	/* Register the new class */
+	ret = class_device_register(&info->class_dev);
+	if (unlikely(ret))
+		goto error_class_device_register;
+
+	/* Create info files */
+
+	/* Create file "assert" and "clear" according to source capability */
+	if (info->mode&PPS_CAPTUREASSERT) {
+		ret = class_device_create_file(&info->class_dev,
+					&linuxpps_class_device_attributes[0]);
+		i = 0;
+		if (unlikely(ret))
+			goto error_class_device_create_file;
+	}
+	if (info->mode&PPS_CAPTURECLEAR) {
+		ret = class_device_create_file(&info->class_dev,
+					&linuxpps_class_device_attributes[1]);
+		i = 1;
+		if (unlikely(ret))
+			goto error_class_device_create_file;
+	}
+
+	for (i = 2; i < ARRAY_SIZE(linuxpps_class_device_attributes); i++) {
+		ret = class_device_create_file(&info->class_dev,
+					&linuxpps_class_device_attributes[i]);
+		if (unlikely(ret))
+			goto error_class_device_create_file;
+	}
+
+	return 0;
+
+error_class_device_create_file :
+	while (--i >= 0)
+		class_device_remove_file(&info->class_dev,
+					&linuxpps_class_device_attributes[i]);
+
+	class_device_unregister(&info->class_dev);
+	/* Here the  release() method was already called */
+
+error_class_device_register :
+
+	return ret;
+}
+
+void linuxpps_sysfs_unregister(void)
+{
+	class_unregister(&linuxpps_class);
+}
+
+int linuxpps_sysfs_register(void)
+{
+	return class_register(&linuxpps_class);
+}
diff --git a/include/linux/pps.h b/include/linux/pps.h
index b1175f7..80080a7 100644
--- a/include/linux/pps.h
+++ b/include/linux/pps.h
@@ -53,6 +53,9 @@ struct linuxpps_source_info_s {
 
 	void (*echo)(int source, int event, void *data);/* the PPS echo function */
 
+	/* sysfs section */
+	struct class_device class_dev;
+
 	/* procfs section */
 	struct proc_dir_entry *dir;
 };
@@ -94,6 +97,9 @@ static inline int linuxpps_is_allocated(int source) {
 	return ret;
 }
 
+#define to_class_dev(obj) container_of((obj), struct class_device, kobj)
+#define to_pps_info(obj) container_of((obj), struct linuxpps_source_info_s, class_dev)
+
 /* --- Exported functions -------------------------------------------------- */
 
 extern int linuxpps_register_source(struct linuxpps_source_info_s *info, int default_params, int try_id);
@@ -105,4 +111,9 @@ extern void linuxpps_procfs_remove_source_entry(struct linuxpps_source_info_s *i
 extern int linuxpps_procfs_register(void);
 extern void linuxpps_procfs_unregister(void);
 
+extern int linuxpps_sysfs_create_source_entry(struct linuxpps_source_info_s *info, int id);
+extern void linuxpps_sysfs_remove_source_entry(struct linuxpps_source_info_s *info, int id);
+extern int linuxpps_sysfs_register(void);
+extern void linuxpps_sysfs_unregister(void);
+
 #endif /* _PPS_H_ */


More information about the LinuxPPS mailing list