diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c index c9089f0..6886195 100644 --- a/drivers/dahdi/dahdi-base.c +++ b/drivers/dahdi/dahdi-base.c @@ -507,17 +507,12 @@ static struct dahdi_span *span_find_and_get(int spanno) mutex_lock(®istration_mutex); found = _find_span(spanno); - if (found && !try_module_get(found->ops->owner)) + if (found && !get_span(found)) found = NULL; mutex_unlock(®istration_mutex); return found; } -static void put_span(struct dahdi_span *span) -{ - module_put(span->ops->owner); -} - static unsigned int span_count(void) { unsigned int count = 0; @@ -1745,7 +1740,12 @@ static void dahdi_set_law(struct dahdi_chan *chan, int law) } } -static void dahdi_chan_reg(struct dahdi_chan *chan) +/** + * __dahdi_init_chan - Initialize the channel data structures. + * @chan: The channel to initialize + * + */ +static void __dahdi_init_chan(struct dahdi_chan *chan) { might_sleep(); @@ -1759,12 +1759,17 @@ static void dahdi_chan_reg(struct dahdi_chan *chan) chan->writechunk = chan->swritechunk; chan->rxgain = NULL; chan->txgain = NULL; - dahdi_set_law(chan, 0); - dahdi_set_law(chan, DAHDI_LAW_DEFAULT); close_channel(chan); +} - /* set this AFTER running close_channel() so that - HDLC channels wont cause hangage */ +/** + * dahdi_chan_reg - Mark the channel registered. + * + * This must be called after close channel during registration, normally + * covered by the call to __dahdi_init_chan, to avoid "HDLC hangage" + */ +static inline void dahdi_chan_reg(struct dahdi_chan *chan) +{ set_bit(DAHDI_FLAGBIT_REGISTERED, &chan->flags); } @@ -2159,7 +2164,10 @@ static void dahdi_chan_unreg(struct dahdi_chan *chan) chan->file->private_data = NULL; } + spin_lock_irqsave(&chan->lock, flags); release_echocan(chan->ec_factory); + chan->ec_factory = NULL; + spin_unlock_irqrestore(&chan->lock, flags); #ifdef CONFIG_DAHDI_NET if (dahdi_have_netdev(chan)) { @@ -3047,8 +3055,6 @@ static int can_open_timer(void) static unsigned int max_pseudo_channels = 512; static unsigned int num_pseudo_channels; -static int pinned_spans = 1; - /** * dahdi_alloc_pseudo() - Returns a new pseudo channel. * @@ -3092,6 +3098,7 @@ static struct dahdi_chan *dahdi_alloc_pseudo(struct file *file) pseudo->chan.channo = channo; pseudo->chan.chanpos = channo - FIRST_PSEUDO_CHANNEL + 1; + __dahdi_init_chan(&pseudo->chan); dahdi_chan_reg(&pseudo->chan); snprintf(pseudo->chan.name, sizeof(pseudo->chan.name)-1, @@ -6619,7 +6626,7 @@ static long dahdi_ioctl_compat(struct file *file, unsigned int cmd, * Must be callled with registration_mutex held. * */ -static int _get_next_channo(const struct dahdi_span *span) +static unsigned int _get_next_channo(const struct dahdi_span *span) { const struct list_head *pos = &span->spans_node; while (pos != &span_list) { @@ -6631,89 +6638,224 @@ static int _get_next_channo(const struct dahdi_span *span) return -1; } +static void +set_spanno_and_basechan(struct dahdi_span *span, u32 spanno, u32 basechan) +{ + int i; + span->spanno = spanno; + for (i = 0; i < span->channels; ++i) + span->chans[i]->channo = basechan + i; +} + /** - * _find_spanno_and_channo - Find the next available span and channel number. + * _assign_spanno_and_basechan - Assign next available span and channel numbers. + * + * This function will set span->spanno and channo for all the member channels. + * It will assign the first available location. * * Must be called with registration_mutex held. * */ -static struct list_head *_find_spanno_and_channo(const struct dahdi_span *span, - int *spanno, int *channo, - struct list_head *loc) +static int _assign_spanno_and_basechan(struct dahdi_span *span) { struct dahdi_span *pos; - int next_channo; - - *spanno = 1; - *channo = 1; + unsigned int next_channo; + unsigned int spanno = 1; + unsigned int basechan = 1; list_for_each_entry(pos, &span_list, spans_node) { - bool skip_span; - loc = &pos->spans_node; + if (pos->spanno <= spanno) { + spanno = pos->spanno + 1; + basechan = pos->chans[0]->channo + pos->channels; + continue; + } + next_channo = _get_next_channo(pos); - - skip_span = (pos->spanno == *spanno) || - ((next_channo > 1) && - ((*channo + span->channels) > next_channo)); - - if (!skip_span) + if ((basechan + span->channels) >= next_channo) break; - *spanno = pos->spanno + 1; + /* We can't fit here, let's look at the next location. */ + spanno = pos->spanno + 1; if (pos->channels) - *channo = next_channo + pos->channels; - + basechan = pos->chans[0]->channo + pos->channels; } - return loc; + set_spanno_and_basechan(span, spanno, basechan); + return 0; } +static inline struct dahdi_span *span_from_node(struct list_head *node) +{ + return container_of(node, struct dahdi_span, spans_node); +} + +/* + * Call with registration_mutex held. Make sure all the spans are on the list + * ordered by span. + * + */ +static void _dahdi_add_span_to_span_list(struct dahdi_span *span) +{ + unsigned long flags; + struct dahdi_span *pos; + + if (list_empty(&span_list)) { + list_add_tail(&span->spans_node, &span_list); + return; + } + + list_for_each_entry(pos, &span_list, spans_node) { + WARN_ON(0 == pos->spanno); + if (pos->spanno > span->spanno) + break; + } + + spin_lock_irqsave(&chan_lock, flags); + list_add(&span->spans_node, pos->spans_node.prev); + spin_unlock_irqrestore(&chan_lock, flags); +} + +/** + * _check_spanno_and_basechan - Check if we can fit the new span in the requested location. + * + * Must be called with registration_mutex held. + * + */ +static int +_check_spanno_and_basechan(struct dahdi_span *span, u32 spanno, u32 basechan) +{ + struct dahdi_span *pos; + unsigned int next_channo; + + list_for_each_entry(pos, &span_list, spans_node) { + + next_channo = _get_next_channo(pos); + + if (pos->spanno <= spanno) { + if (basechan < next_channo + pos->channels) { + /* Requested basechan breaks channel sorting */ + dev_info(span->parent->dev.parent, + "[%d] basechan (%d) is too low for wanted span %d\n", + local_spanno(span), basechan, spanno); + return -EINVAL; + } + continue; + } + + if (next_channo == -1) + break; + + if ((basechan + span->channels) < next_channo) + break; + + /* Cannot fit the span into the requested location. Abort. */ + return -EINVAL; + } + + set_spanno_and_basechan(span, spanno, basechan); + return 0; +} + + struct dahdi_device *dahdi_create_device(void) { struct dahdi_device *ddev; ddev = kzalloc(sizeof(*ddev), GFP_KERNEL); if (!ddev) return NULL; - device_initialize(&ddev->dev); + INIT_LIST_HEAD(&ddev->spans); + dahdi_sysfs_init_device(ddev); return ddev; } EXPORT_SYMBOL(dahdi_create_device); void dahdi_free_device(struct dahdi_device *ddev) { - kfree(ddev); + put_device(&ddev->dev); } EXPORT_SYMBOL(dahdi_free_device); /** - * _dahdi_register_span() - Register a new DAHDI span + * __dahdi_init_span - Setup all the data structures for the span. + * @span: The span of interest. + * + */ +static void __dahdi_init_span(struct dahdi_span *span) +{ + int x; + + INIT_LIST_HEAD(&span->spans_node); + spin_lock_init(&span->lock); + clear_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags); + + if (!span->deflaw) { + module_printk(KERN_NOTICE, "Span %s didn't specify default " + "law. Assuming mulaw, please fix driver!\n", + span->name); + span->deflaw = DAHDI_LAW_MULAW; + } + + for (x = 0; x < span->channels; ++x) { + span->chans[x]->span = span; + __dahdi_init_chan(span->chans[x]); + } +} + +/** + * dahdi_init_span - (Re)Initializes a dahdi span. + * @span: The span to initialize. + * + * Reinitializing a device span might be necessary if a span has been changed + * (channels added / removed) between when the dahdi_device it is on was first + * registered and when the spans are actually assigned. + * + */ +void dahdi_init_span(struct dahdi_span *span) +{ + mutex_lock(®istration_mutex); + __dahdi_init_span(span); + mutex_unlock(®istration_mutex); +} +EXPORT_SYMBOL(dahdi_init_span); + +/** + * _dahdi_assign_span() - Assign a new DAHDI span * @span: the DAHDI span + * @spanno: The span number we would like assigned. If 0, the first + * available spanno/basechan will be used. + * @basechan: The base channel number we would like. Ignored if spanno is 0. * @prefmaster: will the new span be preferred as a master? * - * Registers a span for usage with DAHDI. All the channel numbers in it - * will get the lowest available channel numbers. + * Assigns a span for usage with DAHDI. All the channel numbers in it will + * have their numbering started at basechan. * * If prefmaster is set to anything > 0, span will attempt to become the * master DAHDI span at registration time. If 0: it will only become * master if no other span is currently the master (i.e.: it is the * first one). * - * Must be called with registration_mutex held. + * Must be called with registration_mutex held, and the span must have already + * been initialized ith the __dahdi_init_span call. * */ -static int _dahdi_register_span(struct dahdi_span *span, int prefmaster) +static int _dahdi_assign_span(struct dahdi_span *span, unsigned int spanno, + unsigned int basechan, int prefmaster) { - unsigned int spanno; - unsigned int x; - struct list_head *loc = &span_list; - unsigned long flags; - unsigned int channo; int res = 0; + unsigned int x; if (!span || !span->ops || !span->ops->owner) return -EINVAL; + if (test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) { + dev_info(span->parent->dev.parent, + "local span %d is already assigned span %d " + "with base channel %d\n", local_spanno(span), span->spanno, + span->chans[0]->channo); + return -EINVAL; + } + if (span->ops->enable_hw_preechocan || span->ops->disable_hw_preechocan) { if ((NULL == span->ops->enable_hw_preechocan) || @@ -6727,24 +6869,20 @@ static int _dahdi_register_span(struct dahdi_span *span, int prefmaster) span->deflaw = DAHDI_LAW_MULAW; } - INIT_LIST_HEAD(&span->spans_node); - spin_lock_init(&span->lock); - /* Look through the span list to find the first available span number. * The spans are kept on this list in sorted order. We'll also save * off the next available channel number to use. */ - loc = _find_spanno_and_channo(span, &spanno, &channo, loc); + if (0 == spanno) + res = _assign_spanno_and_basechan(span); + else + res = _check_spanno_and_basechan(span, spanno, basechan); - if (unlikely(channo >= FIRST_PSEUDO_CHANNEL)) - return -EINVAL; + if (res) + return res; - span->spanno = spanno; - for (x = 0; x < span->channels; x++) { - span->chans[x]->span = span; - span->chans[x]->channo = channo + x; + for (x = 0; x < span->channels; x++) dahdi_chan_reg(span->chans[x]); - } #ifdef CONFIG_PROC_FS { @@ -6771,13 +6909,12 @@ static int _dahdi_register_span(struct dahdi_span *span, int prefmaster) "%d channels\n", span->spanno, span->name, span->channels); } - spin_lock_irqsave(&chan_lock, flags); - if (loc == &span_list) - list_add_tail(&span->spans_node, &span_list); - else - list_add(&span->spans_node, loc); - spin_unlock_irqrestore(&chan_lock, flags); + _dahdi_add_span_to_span_list(span); + set_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags); + if (span->ops->assigned) + span->ops->assigned(span); + __dahdi_find_master_span(); return 0; @@ -6800,16 +6937,36 @@ cleanup: return res; } +int dahdi_assign_span(struct dahdi_span *span, unsigned int spanno, + unsigned int basechan, int prefmaster) +{ + int ret; + mutex_lock(®istration_mutex); + ret = _dahdi_assign_span(span, spanno, basechan, prefmaster); + mutex_unlock(®istration_mutex); + return ret; +} + +int dahdi_assign_device_spans(struct dahdi_device *ddev) +{ + struct dahdi_span *span; + mutex_lock(®istration_mutex); + list_for_each_entry(span, &ddev->spans, device_node) + _dahdi_assign_span(span, 0, 0, 1); + mutex_unlock(®istration_mutex); + return 0; +} + +static int auto_assign_spans = 1; static const char *UNKNOWN = ""; /** - * _dahdi_register_device - Registers the spans of a DAHDI device. + * _dahdi_register_device - Registers a DAHDI device and assign its spans. * @ddev: the DAHDI device * - * If pinned_spans is defined add the device to the device list and wait for + * If auto_assign_spans is 0, add the device to the device list and wait for * userspace to finish registration. Otherwise, go ahead and register the - * spans in order as was done historically since the beginning of the zaptel - * days. + * spans in order as was done historically. * * Must hold registration_mutex when this function is called. * @@ -6818,19 +6975,28 @@ static int _dahdi_register_device(struct dahdi_device *ddev, struct device *parent) { struct dahdi_span *s; - int ret = 0; + int ret; ddev->manufacturer = (ddev->manufacturer) ?: UNKNOWN; 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); + s->spanno = 0; + __dahdi_init_span(s); } + ret = dahdi_sysfs_add_device(ddev, parent); + if (ret) + return ret; + + if (!auto_assign_spans) + return 0; + + list_for_each_entry(s, &ddev->spans, device_node) + ret = _dahdi_assign_span(s, 0, 0, 1); + return ret; } @@ -6857,26 +7023,27 @@ int dahdi_register_device(struct dahdi_device *ddev, struct device *parent) EXPORT_SYMBOL(dahdi_register_device); /** - * _dahdi_unregister_span() - unregister a DAHDI span + * _dahdi_unassign_span() - unassign a DAHDI span * @span: the DAHDI span * - * Unregisters a span that has been previously registered with - * dahdi_register_span(). + * Unassigns a span that has been previously assigned with + * dahdi_assign_span(). * * Must be called with the registration_mutex held. * */ -static int _dahdi_unregister_span(struct dahdi_span *span) +static int _dahdi_unassign_span(struct dahdi_span *span) { int x; struct dahdi_span *new_master, *s; unsigned long flags; - if (span != _find_span(span->spanno)) { - module_printk(KERN_ERR, "Span %s does not appear to be registered\n", span->name); - return -1; + if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) { + dev_info(span->parent->dev.parent, + "local span %d is already unassigned\n", + local_spanno(span)); + return -EINVAL; } - spin_lock_irqsave(&chan_lock, flags); list_del_init(&span->spans_node); spin_unlock_irqrestore(&chan_lock, flags); @@ -6888,9 +7055,10 @@ static int _dahdi_unregister_span(struct dahdi_span *span) span->ops->shutdown(span); if (debug & DEBUG_MAIN) - module_printk(KERN_NOTICE, "Unregistering Span '%s' with %d channels\n", span->name, span->channels); + module_printk(KERN_NOTICE, "Unassigning Span '%s' with %d channels\n", span->name, span->channels); #ifdef CONFIG_PROC_FS - remove_proc_entry(span->proc_entry->name, root_proc_entry); + if (span->proc_entry) + remove_proc_entry(span->proc_entry->name, root_proc_entry); #endif /* CONFIG_PROC_FS */ span_sysfs_remove(span); @@ -6920,6 +7088,17 @@ static int _dahdi_unregister_span(struct dahdi_span *span) return 0; } +int dahdi_unassign_span(struct dahdi_span *span) +{ + int ret; + + module_printk(KERN_NOTICE, "%s: %s\n", __func__, span->name); + mutex_lock(®istration_mutex); + ret = _dahdi_unassign_span(span); + mutex_unlock(®istration_mutex); + return ret; +} + /** * dahdi_unregister_device() - unregister a DAHDI device * @span: the DAHDI span @@ -6931,22 +7110,28 @@ static int _dahdi_unregister_span(struct dahdi_span *span) void dahdi_unregister_device(struct dahdi_device *ddev) { struct dahdi_span *s; + struct dahdi_span *next; WARN_ON(!ddev); might_sleep(); if (unlikely(!ddev)) return; mutex_lock(®istration_mutex); - list_for_each_entry(s, &ddev->spans, device_node) - _dahdi_unregister_span(s); + list_for_each_entry_safe(s, next, &ddev->spans, device_node) { + _dahdi_unassign_span(s); + list_del_init(&s->device_node); + } mutex_unlock(®istration_mutex); + dahdi_sysfs_unregister_device(ddev); + if (UNKNOWN == ddev->location) ddev->location = NULL; if (UNKNOWN == ddev->manufacturer) ddev->manufacturer = NULL; if (UNKNOWN == ddev->devicetype) ddev->devicetype = NULL; + } EXPORT_SYMBOL(dahdi_unregister_device); @@ -9547,10 +9732,11 @@ MODULE_PARM_DESC(max_pseudo_channels, "Maximum number of pseudo channels."); module_param(hwec_overrides_swec, int, 0644); MODULE_PARM_DESC(hwec_overrides_swec, "When true, a hardware echo canceller is used instead of configured SWEC."); -module_param(pinned_spans, int, 0644); -MODULE_PARM_DESC(pinned_spans, "If 1, span/channel numbers can be statically " - "defined. If 0, spans/channels are numbered in first come " - "first serve order. Default 1"); +module_param(auto_assign_spans, int, 0644); +MODULE_PARM_DESC(auto_assign_spans, + "If 1 spans will automatically have their children span and " + "channel numbers assigned by the driver. If 0, user space " + "will need to assign them via /sys/bus/dahdi_devices."); static const struct file_operations dahdi_fops = { .owner = THIS_MODULE, 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); diff --git a/drivers/dahdi/dahdi.h b/drivers/dahdi/dahdi.h index 92ddbdc..5baebc1 100644 --- a/drivers/dahdi/dahdi.h +++ b/drivers/dahdi/dahdi.h @@ -34,4 +34,28 @@ void span_sysfs_remove(struct dahdi_span *span); int __init dahdi_sysfs_init(const struct file_operations *dahdi_fops); void dahdi_sysfs_exit(void); +void dahdi_sysfs_init_device(struct dahdi_device *ddev); +int dahdi_sysfs_add_device(struct dahdi_device *ddev, struct device *parent); +void dahdi_sysfs_unregister_device(struct dahdi_device *ddev); + +int dahdi_assign_span(struct dahdi_span *span, unsigned int spanno, + unsigned int basechan, int prefmaster); +int dahdi_unassign_span(struct dahdi_span *span); +int dahdi_assign_device_spans(struct dahdi_device *ddev); + +static inline int get_span(struct dahdi_span *span) +{ + return try_module_get(span->ops->owner); +} + +static inline void put_span(struct dahdi_span *span) +{ + module_put(span->ops->owner); +} + +static inline int local_spanno(struct dahdi_span *span) +{ + return span->offset + 1; +} + #endif /* _DAHDI_H */ diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c index f6b2a48..bfd6190 100644 --- a/drivers/dahdi/wcte12xp/base.c +++ b/drivers/dahdi/wcte12xp/base.c @@ -1874,7 +1874,7 @@ static int t1_software_init(struct t1 *wc, enum linemode type) return res; } - return 0; + return res; } #if 0 diff --git a/drivers/dahdi/xpp/xbus-core.c b/drivers/dahdi/xpp/xbus-core.c index 39fe67c..7fe0493 100644 --- a/drivers/dahdi/xpp/xbus-core.c +++ b/drivers/dahdi/xpp/xbus-core.c @@ -904,6 +904,7 @@ static int xbus_register_dahdi_device(xbus_t *xbus) * So let's also export it via the newfangled "location" field. */ xbus->ddev->location = xbus->connector; + xbus->ddev->hardware_id = xbus->label; /* * Prepare the span list @@ -943,6 +944,7 @@ static void xbus_unregister_dahdi_device(xbus_t *xbus) kfree(xbus->ddev->devicetype); xbus->ddev->devicetype = NULL; xbus->ddev->location = NULL; + xbus->ddev->hardware_id = NULL; dahdi_free_device(xbus->ddev); xbus->ddev = NULL; for(i = 0; i < MAX_XPDS; i++) { diff --git a/include/dahdi/kernel.h b/include/dahdi/kernel.h index 12e8ff2..bb32126 100644 --- a/include/dahdi/kernel.h +++ b/include/dahdi/kernel.h @@ -896,16 +896,21 @@ struct dahdi_span_ops { /** * dahdi_device - Represents a device that can contain one or more spans. * - * @spans: List of child spans. + * @spans: List of child spans. * @manufacturer: Device manufacturer. - * @location: The location of this device - * @devicetype: What type of device this is. + * @location: The location of this device. This should not change if + * the device is replaced (e.g: in the same PCI slot) + * @hardware_id: The hardware_id of this device (NULL for devices without + * a hardware_id). This should not change if the device is + * relocated to a different location (e.g: different PCI slot) + * @devicetype: What type of device this is. * */ struct dahdi_device { struct list_head spans; const char *manufacturer; const char *location; + const char *hardware_id; const char *devicetype; struct device dev; }; @@ -1164,6 +1169,7 @@ struct dahdi_device *dahdi_create_device(void); int dahdi_register_device(struct dahdi_device *ddev, struct device *parent); void dahdi_unregister_device(struct dahdi_device *ddev); void dahdi_free_device(struct dahdi_device *ddev); +void dahdi_init_span(struct dahdi_span *span); /*! Allocate / free memory for a transcoder */ struct dahdi_transcoder *dahdi_transcoder_alloc(int numchans); @@ -1451,6 +1457,10 @@ typedef u32 __bitwise pm_message_t; #endif /* 2.6.26 */ #endif /* 2.6.31 */ +#ifndef CONFIG_TRACING +#define trace_printk printk +#endif + #ifndef DEFINE_SPINLOCK #define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED #endif