summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOron Peled <oron.peled@xorcom.com>2011-02-20 17:08:26 +0200
committerShaun Ruffell <sruffell@digium.com>2011-04-15 14:21:13 -0500
commita899994d442a262f084fc47ec80013ac5787296c (patch)
treecff8a5258d0d57cd365f88552995107167123f7e
parentb3c86b81ae638d90ee27a821e962e297ab85cbe4 (diff)
dahdi: Expose spans in sysfs.
This change will facilitate creating rules that will allow spans and channels to be accessed by named device files instead of by numbers. Signed-off-by: Oron Peled <oron.peled@xorcom.com> Signed-off-by: Shaun Ruffell <sruffell@digium.com>
-rw-r--r--drivers/dahdi/dahdi-base.c3
-rw-r--r--drivers/dahdi/dahdi-sysfs.c329
-rw-r--r--include/dahdi/kernel.h4
3 files changed, 336 insertions, 0 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c
index 789b2af..afd8797 100644
--- a/drivers/dahdi/dahdi-base.c
+++ b/drivers/dahdi/dahdi-base.c
@@ -6472,6 +6472,7 @@ struct dahdi_device *dahdi_create_device(void)
ddev = kzalloc(sizeof(*ddev), GFP_KERNEL);
if (!ddev)
return NULL;
+ device_initialize(&ddev->dev);
return ddev;
}
EXPORT_SYMBOL(dahdi_create_device);
@@ -6613,6 +6614,8 @@ static int _dahdi_register_device(struct dahdi_device *ddev,
ddev->location = (ddev->location) ?: UNKNOWN;
ddev->devicetype = (ddev->devicetype) ?: UNKNOWN;
+ ddev->dev.parent = parent;
+
list_for_each_entry(s, &ddev->spans, device_node) {
s->parent = ddev;
ret = _dahdi_register_span(s, 1);
diff --git a/drivers/dahdi/dahdi-sysfs.c b/drivers/dahdi/dahdi-sysfs.c
index 5de4bcf..2ed4c06 100644
--- a/drivers/dahdi/dahdi-sysfs.c
+++ b/drivers/dahdi/dahdi-sysfs.c
@@ -49,6 +49,270 @@ static struct class_simple *dahdi_class = NULL;
#define class_destroy class_simple_destroy
#endif
+/*
+ * Very old hotplug support
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 9)
+#define OLD_HOTPLUG_SUPPORT /* for older kernels */
+#define OLD_HOTPLUG_SUPPORT_269
+#endif
+
+#ifdef OLD_HOTPLUG_SUPPORT_269
+/* Copy from new kernels lib/kobject_uevent.c */
+enum kobject_action {
+ KOBJ_ADD,
+ KOBJ_REMOVE,
+ KOBJ_CHANGE,
+ KOBJ_MOUNT,
+ KOBJ_UMOUNT,
+ KOBJ_OFFLINE,
+ KOBJ_ONLINE,
+};
+#endif
+
+/*
+ * Hotplug replaced with uevent in 2.6.16
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+#define OLD_HOTPLUG_SUPPORT /* for older kernels */
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14)
+#define DEVICE_ATTR_READER(name, dev, buf) \
+ ssize_t name(struct device *dev, struct device_attribute *attr,\
+ char *buf)
+#define DEVICE_ATTR_WRITER(name, dev, buf, count) \
+ ssize_t name(struct device *dev, struct device_attribute *attr,\
+ const char *buf, size_t count)
+#define BUS_ATTR_READER(name, dev, buf) \
+ ssize_t name(struct device *dev, struct device_attribute *attr, \
+ char *buf)
+#define BUS_ATTR_WRITER(name, dev, buf, count) \
+ ssize_t name(struct device *dev, struct device_attribute *attr,\
+ const char *buf, size_t count)
+#else
+#define DEVICE_ATTR_READER(name, dev, buf) \
+ ssize_t name(struct device *dev, char *buf)
+#define DEVICE_ATTR_WRITER(name, dev, buf, count) \
+ ssize_t name(struct device *dev, const char *buf, size_t count)
+#define BUS_ATTR_READER(name, dev, buf) \
+ ssize_t name(struct device *dev, char *buf)
+#define BUS_ATTR_WRITER(name, dev, buf, count) \
+ ssize_t name(struct device *dev, const char *buf, size_t count)
+#endif
+
+#define DRIVER_ATTR_READER(name, drv, buf) \
+ ssize_t name(struct device_driver *drv, char * buf)
+
+
+static char *initdir = "/usr/share/dahdi";
+module_param(initdir, charp, 0644);
+
+static int span_match(struct device *dev, struct device_driver *driver)
+{
+ return 1;
+}
+
+static inline struct dahdi_span *dev_to_span(const struct device *const dev)
+{
+ return dev_get_drvdata(dev);
+}
+
+#ifdef OLD_HOTPLUG_SUPPORT
+static int span_hotplug(struct device *dev, char **envp, int envnum,
+ char *buff, int bufsize)
+{
+ struct dahdi_span *span;
+
+ if (!dev)
+ return -ENODEV;
+ span = dev_to_span(dev);
+ envp[0] = buff;
+ if (snprintf(buff, bufsize, "SPAN_NAME=%s", span->name) >= bufsize)
+ return -ENOMEM;
+ envp[1] = NULL;
+ return 0;
+}
+#else
+
+#define SPAN_VAR_BLOCK \
+ do { \
+ DAHDI_ADD_UEVENT_VAR("DAHDI_INIT_DIR=%s", initdir); \
+ DAHDI_ADD_UEVENT_VAR("SPAN_NUM=%d", span->spanno); \
+ DAHDI_ADD_UEVENT_VAR("SPAN_NAME=%s", span->name); \
+ } while (0)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+#define DAHDI_ADD_UEVENT_VAR(fmt, val...) \
+ do { \
+ int err = add_uevent_var(envp, num_envp, &i, \
+ buffer, buffer_size, &len, \
+ fmt, val); \
+ if (err) \
+ return err; \
+ } while (0)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
+#define dev_name(dev) ((dev)->bus_id)
+#define dev_set_name(dev, format, ...) \
+ snprintf((dev)->bus_id, BUS_ID_SIZE, format, ## __VA_ARGS__);
+#endif
+
+static int span_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct dahdi_span *span;
+ int i = 0;
+ int len = 0;
+
+ if (!dev)
+ return -ENODEV;
+
+ span = dev_to_span(dev);
+ if (!span)
+ return -ENODEV;
+
+ dahdi_dbg(GENERAL, "SYFS dev_name=%s span=%s\n",
+ dev_name(dev), span->name);
+ SPAN_VAR_BLOCK;
+ envp[i] = NULL;
+ return 0;
+}
+
+#else
+#define DAHDI_ADD_UEVENT_VAR(fmt, val...) \
+ do { \
+ int err = add_uevent_var(kenv, fmt, val); \
+ if (err) \
+ return err; \
+ } while (0)
+
+static int span_uevent(struct device *dev, struct kobj_uevent_env *kenv)
+{
+ struct dahdi_span *span;
+
+ if (!dev)
+ return -ENODEV;
+ span = dev_to_span(dev);
+ if (!span)
+ return -ENODEV;
+ dahdi_dbg(GENERAL, "SYFS dev_name=%s span=%s\n",
+ dev_name(dev), span->name);
+ SPAN_VAR_BLOCK;
+ return 0;
+}
+
+#endif
+
+#endif /* OLD_HOTPLUG_SUPPORT */
+
+#define span_attr(field, format_string) \
+static BUS_ATTR_READER(field##_show, dev, buf) \
+{ \
+ struct dahdi_span *span; \
+ \
+ span = dev_to_span(dev); \
+ return sprintf(buf, format_string, span->field); \
+}
+
+span_attr(name, "%s\n");
+span_attr(desc, "%s\n");
+span_attr(spantype, "%s\n");
+span_attr(alarms, "0x%x\n");
+span_attr(irq, "%d\n");
+span_attr(irqmisses, "%d\n");
+span_attr(lbo, "%d\n");
+span_attr(syncsrc, "%d\n");
+
+static struct device_attribute span_dev_attrs[] = {
+ __ATTR_RO(name),
+ __ATTR_RO(desc),
+ __ATTR_RO(spantype),
+ __ATTR_RO(alarms),
+ __ATTR_RO(irq),
+ __ATTR_RO(irqmisses),
+ __ATTR_RO(lbo),
+ __ATTR_RO(syncsrc),
+ __ATTR_NULL,
+};
+
+
+static struct driver_attribute dahdi_attrs[] = {
+ __ATTR_NULL,
+};
+
+static struct bus_type spans_bus_type = {
+ .name = "dahdi_spans",
+ .match = span_match,
+#ifdef OLD_HOTPLUG_SUPPORT
+ .hotplug = span_hotplug,
+#else
+ .uevent = span_uevent,
+#endif
+ .dev_attrs = span_dev_attrs,
+ .drv_attrs = dahdi_attrs,
+};
+
+static int span_probe(struct device *dev)
+{
+ struct dahdi_span *span;
+
+ span = dev_to_span(dev);
+ return 0;
+}
+
+static int span_remove(struct device *dev)
+{
+ struct dahdi_span *span;
+
+ span = dev_to_span(dev);
+ return 0;
+}
+
+static struct device_driver dahdi_driver = {
+ .name = "generic_lowlevel",
+ .bus = &spans_bus_type,
+ .probe = span_probe,
+ .remove = span_remove,
+#ifndef OLD_HOTPLUG_SUPPORT
+ .owner = THIS_MODULE
+#endif
+};
+
+static void span_uevent_send(struct dahdi_span *span, enum kobject_action act)
+{
+ struct kobject *kobj;
+
+ kobj = &span->span_device->kobj;
+ span_dbg(DEVICES, span, "SYFS dev_name=%s action=%d\n",
+ dev_name(span->span_device), act);
+
+#if defined(OLD_HOTPLUG_SUPPORT_269)
+ {
+ /* Copy from new kernels lib/kobject_uevent.c */
+ static const char *const str[] = {
+ [KOBJ_ADD] "add",
+ [KOBJ_REMOVE] "remove",
+ [KOBJ_CHANGE] "change",
+ [KOBJ_MOUNT] "mount",
+ [KOBJ_UMOUNT] "umount",
+ [KOBJ_OFFLINE] "offline",
+ [KOBJ_ONLINE] "online"
+ };
+ kobject_hotplug(str[act], kobj);
+ }
+#elif defined(OLD_HOTPLUG_SUPPORT)
+ kobject_hotplug(kobj, act);
+#else
+ kobject_uevent(kobj, act);
+#endif
+}
+
+static void span_release(struct device *dev)
+{
+ dahdi_dbg(DEVICES, "%s: %s\n", __func__, dev_name(dev));
+}
+
int dahdi_register_chardev(struct dahdi_chardev *dev)
{
@@ -78,7 +342,17 @@ EXPORT_SYMBOL(dahdi_unregister_chardev);
void span_sysfs_remove(struct dahdi_span *span)
{
+ struct device *span_device;
int x;
+
+ span_dbg(DEVICES, span, "\n");
+ span_device = span->span_device;
+
+ if (!span_device) {
+ WARN_ON(!span_device);
+ return;
+ }
+
for (x = 0; x < span->channels; x++) {
struct dahdi_chan *chan = span->chans[x];
if (!test_bit(DAHDI_FLAGBIT_DEVFILE, &chan->flags))
@@ -88,13 +362,41 @@ void span_sysfs_remove(struct dahdi_span *span)
MKDEV(DAHDI_MAJOR, chan->channo));
clear_bit(DAHDI_FLAGBIT_DEVFILE, &chan->flags);
}
+ if (!dev_get_drvdata(span_device))
+ return;
+ span_uevent_send(span, KOBJ_OFFLINE);
+ dev_set_drvdata(span_device, NULL);
+ span_device->parent = NULL;
+ device_unregister(span->span_device);
}
int span_sysfs_create(struct dahdi_span *span)
{
+ struct device *span_device;
int res = 0;
int x;
+ BUG_ON(span->span_device);
+
+ span->span_device = kzalloc(sizeof(*span->span_device), GFP_KERNEL);
+ if (!span->span_device)
+ return -ENOMEM;
+
+ span_device = span->span_device;
+ span_dbg(DEVICES, span, "\n");
+
+ span_device->bus = &spans_bus_type;
+ span_device->parent = span->parent->dev.parent;
+ dev_set_name(span_device, "span-%03d", span->spanno);
+ dev_set_drvdata(span_device, span);
+ span_device->release = span_release;
+ res = device_register(span_device);
+ if (res) {
+ span_err(span, "%s: device_register failed: %d\n", __func__,
+ res);
+ goto cleanup;
+ }
+
for (x = 0; x < span->channels; x++) {
struct dahdi_chan *chan = span->chans[x];
char chan_name[32];
@@ -137,10 +439,13 @@ static struct {
unsigned int timer:1;
unsigned int channel:1;
unsigned int pseudo:1;
+ unsigned int sysfs_driver_registered:1;
+ unsigned int sysfs_spans_bus_type:1;
} dummy_dev;
void dahdi_sysfs_exit(void)
{
+ dahdi_dbg(DEVICES, "SYSFS\n");
if (dummy_dev.pseudo) {
dahdi_dbg(DEVICES, "Removing /dev/dahdi/pseudo:\n");
DEL_DAHDI_DEV(DAHDI_PSEUDO);
@@ -166,6 +471,16 @@ void dahdi_sysfs_exit(void)
class_destroy(dahdi_class);
dahdi_class = NULL;
}
+ if (dummy_dev.sysfs_driver_registered) {
+ dahdi_dbg(DEVICES, "Unregister driver\n");
+ driver_unregister(&dahdi_driver);
+ dummy_dev.sysfs_driver_registered = 0;
+ }
+ if (dummy_dev.sysfs_spans_bus_type) {
+ dahdi_dbg(DEVICES, "Unregister span bus type\n");
+ bus_unregister(&spans_bus_type);
+ dummy_dev.sysfs_spans_bus_type = 0;
+ }
unregister_chrdev(DAHDI_MAJOR, "dahdi");
}
@@ -220,6 +535,20 @@ int __init dahdi_sysfs_init(const struct file_operations *dahdi_fops)
goto cleanup;
}
dummy_dev.ctl = 1;
+ res = bus_register(&spans_bus_type);
+ if (res != 0) {
+ dahdi_err("%s: bus_register(%s) failed. Error number %d",
+ __func__, spans_bus_type.name, res);
+ goto cleanup;
+ }
+ dummy_dev.sysfs_spans_bus_type = 1;
+ res = driver_register(&dahdi_driver);
+ if (res < 0) {
+ dahdi_err("%s: driver_register(%s) failed. Error number %d",
+ __func__, dahdi_driver.name, res);
+ goto cleanup;
+ }
+ dummy_dev.sysfs_driver_registered = 1;
return 0;
diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h
index 2f23f19..3bf11f1 100644
--- a/include/dahdi/kernel.h
+++ b/include/dahdi/kernel.h
@@ -54,6 +54,8 @@
#include <linux/skbuff.h>
#include <linux/interrupt.h>
#endif
+#include <linux/device.h>
+#include <linux/sysfs.h>
#include <linux/poll.h>
@@ -891,6 +893,7 @@ struct dahdi_device {
const char *manufacturer;
const char *location;
const char *devicetype;
+ struct device dev;
};
struct dahdi_span {
@@ -940,6 +943,7 @@ struct dahdi_span {
struct dahdi_device *parent;
struct list_head device_node;
+ struct device *span_device;
};
struct dahdi_transcoder_channel {