[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