summaryrefslogtreecommitdiff
path: root/drivers/dahdi/dahdi-sysfs.c
diff options
context:
space:
mode:
authorTzafrir Cohen <tzafrir.cohen@xorcom.com>2011-10-26 18:59:20 +0000
committerTzafrir Cohen <tzafrir.cohen@xorcom.com>2011-10-26 18:59:20 +0000
commit581df51b0dfcf3148dad593cb13705fd6896d5ed (patch)
treecf3300a2cf6fc26f88d304aaf8d655c2433a1fa8 /drivers/dahdi/dahdi-sysfs.c
parent83af0a21bf7b0f11b142d56948f0a5c4ea19f313 (diff)
dahdi: Expose dahdi devices in sysfs.
This exposes dahdi devices in sysfs and also exposes attributes that will allow user space to control the registration order in spans. This facilitates loading drivers out of order yet keeping consistent span/channel numbering, which in turn will eventually allow the blacklist for DAHDI drivers to be removed. The default behavior, controlled with the auto_register module parameter on dahdi is to number the spans / channels in order like is currently done. So this change does not introduce any new behavior by default. * Writing (anything) to this attribute returns the span to its unassigned state * Fix dahdi_chan_unreg() echocan refcount * Add safeguard against duplicate unassignment to _dahdi_unregister_span() * Remove the span from device_node list, only in dahdi_unregister_device() and not in dahdi_unregister_span() * Free allocated span->span_device in span_sysfs_remove() [is it safe?, didn't cause problem so far...] Signed-off-by: Shaun Ruffell <sruffell@digium.com> Signed-off-by: Oron Peled <oron.peled@xorcom.com> Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com> dahdi: Add "hardware_id" dahdi_device attribute. - The "hardware_id" does not change with device location (e.g: when a PCI card is moved from one slot to another). - Not all devices have this attribute. It is legal for it to be NULL (that is the default for all low-level drivers that do not set it explicitly). - When "hardware_id" is NULL, the sysfs attribute value is "\n" Signed-off-by: Oron Peled <oron.peled@xorcom.com> Acked-by: Shaun Ruffell <sruffell@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@10275 a0bf4364-ded3-4de4-8d8a-66a801d63aff
Diffstat (limited to 'drivers/dahdi/dahdi-sysfs.c')
-rw-r--r--drivers/dahdi/dahdi-sysfs.c240
1 files changed, 230 insertions, 10 deletions
diff --git a/drivers/dahdi/dahdi-sysfs.c b/drivers/dahdi/dahdi-sysfs.c
index 4bc1cab..16d19ef 100644
--- a/drivers/dahdi/dahdi-sysfs.c
+++ b/drivers/dahdi/dahdi-sysfs.c
@@ -218,13 +218,20 @@ static BUS_ATTR_READER(field##_show, dev, buf) \
span_attr(name, "%s\n");
span_attr(desc, "%s\n");
span_attr(spantype, "%s\n");
-span_attr(offset, "%d\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 BUS_ATTR_READER(local_spanno_show, dev, buf)
+{
+ struct dahdi_span *span;
+
+ span = dev_to_span(dev);
+ return sprintf(buf, "%d\n", local_spanno(span));
+}
+
static BUS_ATTR_READER(is_digital_show, dev, buf)
{
struct dahdi_span *span;
@@ -245,7 +252,7 @@ static struct device_attribute span_dev_attrs[] = {
__ATTR_RO(name),
__ATTR_RO(desc),
__ATTR_RO(spantype),
- __ATTR_RO(offset),
+ __ATTR_RO(local_spanno),
__ATTR_RO(alarms),
__ATTR_RO(irq),
__ATTR_RO(irqmisses),
@@ -256,7 +263,6 @@ static struct device_attribute span_dev_attrs[] = {
__ATTR_NULL,
};
-
static struct driver_attribute dahdi_attrs[] = {
__ATTR_NULL,
};
@@ -335,7 +341,6 @@ 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)
{
static const char *DAHDI_STRING = "dahdi!";
@@ -370,10 +375,8 @@ void span_sysfs_remove(struct dahdi_span *span)
span_dbg(DEVICES, span, "\n");
span_device = span->span_device;
- if (!span_device) {
- WARN_ON(!span_device);
+ if (!span_device)
return;
- }
for (x = 0; x < span->channels; x++) {
struct dahdi_chan *chan = span->chans[x];
@@ -396,6 +399,9 @@ void span_sysfs_remove(struct dahdi_span *span)
dev_set_drvdata(span_device, NULL);
span_device->parent = NULL;
put_device(span_device);
+ memset(&span->span_device, 0, sizeof(span->span_device));
+ kfree(span->span_device);
+ span->span_device = NULL;
}
int span_sysfs_create(struct dahdi_span *span)
@@ -404,7 +410,10 @@ int span_sysfs_create(struct dahdi_span *span)
int res = 0;
int x;
- BUG_ON(span->span_device);
+ if (span->span_device) {
+ WARN_ON(1);
+ return -EEXIST;
+ }
span->span_device = kzalloc(sizeof(*span->span_device), GFP_KERNEL);
if (!span->span_device)
@@ -414,8 +423,8 @@ int span_sysfs_create(struct dahdi_span *span)
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);
+ span_device->parent = &span->parent->dev;
+ dev_set_name(span_device, "span-%d", span->spanno);
dev_set_drvdata(span_device, span);
span_device->release = span_release;
res = device_register(span_device);
@@ -471,8 +480,170 @@ static struct {
unsigned int pseudo:1;
unsigned int sysfs_driver_registered:1;
unsigned int sysfs_spans_bus_type:1;
+ unsigned int dahdi_device_bus_registered:1;
} dummy_dev;
+static inline struct dahdi_device *to_ddev(struct device *dev)
+{
+ return container_of(dev, struct dahdi_device, dev);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static ssize_t dahdi_device_manufacturer_show(struct device *dev, char *buf)
+#else
+static ssize_t
+dahdi_device_manufacturer_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+#endif
+{
+ struct dahdi_device *ddev = to_ddev(dev);
+ return sprintf(buf, "%s\n", ddev->manufacturer);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static ssize_t dahdi_device_type_show(struct device *dev, char *buf)
+#else
+static ssize_t
+dahdi_device_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+#endif
+{
+ struct dahdi_device *ddev = to_ddev(dev);
+ return sprintf(buf, "%s\n", ddev->devicetype);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static ssize_t dahdi_device_span_count_show(struct device *dev, char *buf)
+#else
+static ssize_t
+dahdi_device_span_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+#endif
+{
+ struct dahdi_device *ddev = to_ddev(dev);
+ unsigned int count = 0;
+ struct list_head *pos;
+
+ list_for_each(pos, &ddev->spans)
+ ++count;
+
+ return sprintf(buf, "%d\n", count);
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static ssize_t dahdi_device_hardware_id_show(struct device *dev, char *buf)
+#else
+static ssize_t
+dahdi_device_hardware_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+#endif
+{
+ struct dahdi_device *ddev = to_ddev(dev);
+
+ return sprintf(buf, "%s\n",
+ (ddev->hardware_id) ? ddev->hardware_id : "");
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static ssize_t
+dahdi_device_auto_assign(struct device *dev, const char *buf, size_t count)
+#else
+static ssize_t
+dahdi_device_auto_assign(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+#endif
+{
+ struct dahdi_device *ddev = to_ddev(dev);
+ dahdi_assign_device_spans(ddev);
+ return count;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static ssize_t
+dahdi_device_assign_span(struct device *dev, const char *buf, size_t count)
+#else
+static ssize_t
+dahdi_device_assign_span(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+#endif
+{
+ int ret;
+ struct dahdi_span *span;
+ unsigned int local_span_number;
+ unsigned int desired_spanno;
+ unsigned int desired_basechanno;
+ struct dahdi_device *const ddev = to_ddev(dev);
+
+ ret = sscanf(buf, "%u:%u:%u", &local_span_number, &desired_spanno,
+ &desired_basechanno);
+ if (ret != 3)
+ return -EINVAL;
+
+ if (desired_spanno && !desired_basechanno) {
+ dev_notice(dev, "Must set span number AND base chan number\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(span, &ddev->spans, device_node) {
+ if (local_span_number == local_spanno(span)) {
+ ret = dahdi_assign_span(span, desired_spanno,
+ desired_basechanno, 1);
+ return (ret) ? ret : count;
+ }
+ }
+ dev_notice(dev, "no match for local span number %d\n", local_span_number);
+ return -EINVAL;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13)
+static ssize_t
+dahdi_device_unassign_span(struct device *dev, const char *buf, size_t count)
+#else
+static ssize_t
+dahdi_device_unassign_span(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+#endif
+{
+ int ret;
+ unsigned int local_span_number;
+ struct dahdi_span *span;
+ struct dahdi_device *const ddev = to_ddev(dev);
+
+ ret = sscanf(buf, "%u", &local_span_number);
+ if (ret != 1)
+ return -EINVAL;
+
+ ret = -ENODEV;
+ list_for_each_entry(span, &ddev->spans, device_node) {
+ if (local_span_number == local_spanno(span))
+ ret = dahdi_unassign_span(span);
+ }
+ if (-ENODEV == ret) {
+ if (printk_ratelimit()) {
+ dev_info(dev, "'%d' is an invalid local span number.\n",
+ local_span_number);
+ }
+ return -EINVAL;
+ }
+ return (ret < 0) ? ret : count;
+}
+
+static struct device_attribute dahdi_device_attrs[] = {
+ __ATTR(manufacturer, S_IRUGO, dahdi_device_manufacturer_show, NULL),
+ __ATTR(type, S_IRUGO, dahdi_device_type_show, NULL),
+ __ATTR(span_count, S_IRUGO, dahdi_device_span_count_show, NULL),
+ __ATTR(hardware_id, S_IRUGO, dahdi_device_hardware_id_show, NULL),
+ __ATTR(auto_assign, S_IWUSR, NULL, dahdi_device_auto_assign),
+ __ATTR(assign_span, S_IWUSR, NULL, dahdi_device_assign_span),
+ __ATTR(unassign_span, S_IWUSR, NULL, dahdi_device_unassign_span),
+ __ATTR_NULL,
+};
+
+static struct bus_type dahdi_device_bus = {
+ .name = "dahdi_devices",
+ .dev_attrs = dahdi_device_attrs,
+};
+
void dahdi_sysfs_exit(void)
{
dahdi_dbg(DEVICES, "SYSFS\n");
@@ -512,6 +683,49 @@ void dahdi_sysfs_exit(void)
dummy_dev.sysfs_spans_bus_type = 0;
}
unregister_chrdev(DAHDI_MAJOR, "dahdi");
+
+ if (dummy_dev.dahdi_device_bus_registered) {
+ bus_unregister(&dahdi_device_bus);
+ dummy_dev.dahdi_device_bus_registered = 0;
+ }
+}
+
+static void dahdi_device_release(struct device *dev)
+{
+ struct dahdi_device *ddev = container_of(dev, struct dahdi_device, dev);
+ kfree(ddev);
+}
+
+/**
+ * dahdi_sysfs_add_device - Add the dahdi_device into the sysfs hierarchy.
+ * @ddev: The device to add.
+ * @parent: The physical device that is implementing this device.
+ *
+ * By adding the dahdi_device to the sysfs hierarchy user space can control
+ * how spans are numbered.
+ *
+ */
+int dahdi_sysfs_add_device(struct dahdi_device *ddev, struct device *parent)
+{
+ int ret;
+ struct device *const dev = &ddev->dev;
+
+ dev->parent = parent;
+ dev->bus = &dahdi_device_bus;
+ dev_set_name(dev, "%s:%s", parent->bus->name, dev_name(parent));
+ ret = device_add(dev);
+ return ret;
+}
+
+void dahdi_sysfs_init_device(struct dahdi_device *ddev)
+{
+ device_initialize(&ddev->dev);
+ ddev->dev.release = dahdi_device_release;
+}
+
+void dahdi_sysfs_unregister_device(struct dahdi_device *ddev)
+{
+ device_del(&ddev->dev);
}
int __init dahdi_sysfs_init(const struct file_operations *dahdi_fops)
@@ -519,6 +733,12 @@ int __init dahdi_sysfs_init(const struct file_operations *dahdi_fops)
int res = 0;
void *dev;
+ res = bus_register(&dahdi_device_bus);
+ if (res)
+ return res;
+
+ dummy_dev.dahdi_device_bus_registered = 1;
+
res = register_chrdev(DAHDI_MAJOR, "dahdi", dahdi_fops);
if (res) {
module_printk(KERN_ERR, "Unable to register DAHDI character device handler on %d\n", DAHDI_MAJOR);