diff options
author | Oron Peled <oron.peled@xorcom.com> | 2011-02-20 17:08:26 +0200 |
---|---|---|
committer | Shaun Ruffell <sruffell@digium.com> | 2011-04-15 14:21:13 -0500 |
commit | a899994d442a262f084fc47ec80013ac5787296c (patch) | |
tree | cff8a5258d0d57cd365f88552995107167123f7e | |
parent | b3c86b81ae638d90ee27a821e962e297ab85cbe4 (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.c | 3 | ||||
-rw-r--r-- | drivers/dahdi/dahdi-sysfs.c | 329 | ||||
-rw-r--r-- | include/dahdi/kernel.h | 4 |
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 { |