diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/dahdi/dahdi-base.c | 297 | ||||
-rw-r--r-- | drivers/dahdi/dahdi-sysfs.c | 150 | ||||
-rw-r--r-- | drivers/dahdi/dahdi.h | 18 |
3 files changed, 378 insertions, 87 deletions
diff --git a/drivers/dahdi/dahdi-base.c b/drivers/dahdi/dahdi-base.c index afd8797..a70deb3 100644 --- a/drivers/dahdi/dahdi-base.c +++ b/drivers/dahdi/dahdi-base.c @@ -531,17 +531,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; @@ -1663,7 +1658,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(); @@ -1677,12 +1677,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); } @@ -2922,8 +2927,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. * @@ -2969,6 +2972,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, @@ -6416,7 +6420,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) { @@ -6428,64 +6432,173 @@ 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); + if ((basechan + span->channels) >= next_channo) + break; + + /* We can't fit here, let's look at the next location. */ + spanno = pos->spanno + 1; + if (pos->channels) + basechan = pos->chans[0]->channo + pos->channels; + } - skip_span = (pos->spanno == *spanno) || - ((next_channo > 1) && - ((*channo + span->channels) > next_channo)); + set_spanno_and_basechan(span, spanno, basechan); + trace_printk("%s: spanno: %d basechan: %d span->spanno: %d\n", + dev_name(&span->parent->dev), spanno, basechan, + span->spanno); + return 0; +} - if (!skip_span) +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) + continue; + } + + trace_printk("%s: pos->spanno=%d span->spanno: %d\n", + dev_name(&span->parent->dev), pos->spanno, span->spanno); + + spin_lock_irqsave(&chan_lock, flags); + list_add_tail(&span->spans_node, &pos->spans_node); + 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) { + + if (pos->spanno <= spanno) + continue; + + next_channo = _get_next_channo(pos); + if (!next_channo) break; - *spanno = pos->spanno + 1; - if (pos->channels) - *channo = next_channo + pos->channels; + if ((basechan + span->channels) >= next_channo) + break; + /* Cannot fit the span into the requested location. Abort. */ + return -EINVAL; } - return loc; + 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_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_register_span() - Register 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 @@ -6496,45 +6609,43 @@ EXPORT_SYMBOL(dahdi_free_device); * 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_register_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 (!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; + if (test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags)) { + dev_info(span->parent->dev.parent, + "local span %d is already registered as span %d " + "with base channel %d\n", span->offset, span->spanno, + span->chans[0]->channo); + return -EINVAL; } - 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; + trace_printk("span->spanno: %d\n", span->spanno); + + for (x = 0; x < span->channels; x++) dahdi_chan_reg(span->chans[x]); - } #ifdef CONFIG_PROC_FS { @@ -6561,13 +6672,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->registered) + span->ops->registered(span); + __dahdi_find_master_span(); return 0; @@ -6590,16 +6700,36 @@ cleanup: return res; } +int dahdi_register_span(struct dahdi_span *span, unsigned int spanno, + unsigned int basechan, int prefmaster) +{ + int ret; + mutex_lock(®istration_mutex); + ret = _dahdi_register_span(span, spanno, basechan, prefmaster); + mutex_unlock(®istration_mutex); + return ret; +} + +int dahdi_register_device_spans(struct dahdi_device *ddev) +{ + struct dahdi_span *span; + mutex_lock(®istration_mutex); + list_for_each_entry(span, &ddev->spans, device_node) + _dahdi_register_span(span, 0, 0, 1); + mutex_unlock(®istration_mutex); + return 0; +} + +static int auto_register_spans = 1; static const char *UNKNOWN = ""; /** * _dahdi_register_device - Registers the spans of a DAHDI device. * @ddev: the DAHDI device * - * If pinned_spans is defined add the device to the device list and wait for + * If auto_register_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. * @@ -6608,19 +6738,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_register_spans) + return 0; + + list_for_each_entry(s, &ddev->spans, device_node) + ret = _dahdi_register_span(s, 0, 0, 1); + return ret; } @@ -6671,11 +6810,6 @@ static int dahdi_unregister_span(struct dahdi_span *span) 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; - } - spin_lock_irqsave(&chan_lock, flags); list_del_init(&span->spans_node); spin_unlock_irqrestore(&chan_lock, flags); @@ -6689,7 +6823,8 @@ static int dahdi_unregister_span(struct dahdi_span *span) if (debug & DEBUG_MAIN) module_printk(KERN_NOTICE, "Unregistering 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); @@ -6740,12 +6875,15 @@ void dahdi_unregister_device(struct dahdi_device *ddev) dahdi_unregister_span(s); 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); @@ -9340,10 +9478,11 @@ module_param(deftaps, int, 0644); module_param(max_pseudo_channels, int, 0644); MODULE_PARM_DESC(max_pseudo_channels, "Maximum number of pseudo channels."); -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_register_spans, int, 0644); +MODULE_PARM_DESC(auto_register_spans, + "If 1 spans will automatically have their children span and " + "channel numbers assigned by the driver. If 0, user space " + "will need to register 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 2ed4c06..02677dd 100644 --- a/drivers/dahdi/dahdi-sysfs.c +++ b/drivers/dahdi/dahdi-sysfs.c @@ -113,7 +113,7 @@ 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) +static inline struct dahdi_span *dev_to_span(struct device *const dev) { return dev_get_drvdata(dev); } @@ -236,7 +236,6 @@ static struct device_attribute span_dev_attrs[] = { __ATTR_NULL, }; - static struct driver_attribute dahdi_attrs[] = { __ATTR_NULL, }; @@ -313,7 +312,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!"; @@ -348,10 +346,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]; @@ -376,7 +372,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) @@ -386,7 +385,7 @@ 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; + span_device->parent = &span->parent->dev; dev_set_name(span_device, "span-%03d", span->spanno); dev_set_drvdata(span_device, span); span_device->release = span_release; @@ -441,8 +440,94 @@ 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); +} + +static ssize_t +dahdi_device_manufacturer_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dahdi_device *ddev = to_ddev(dev); + return sprintf(buf, "%s\n", ddev->manufacturer); +} + +static ssize_t +dahdi_device_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dahdi_device *ddev = to_ddev(dev); + return sprintf(buf, "%s\n", ddev->devicetype); +} + +static ssize_t +dahdi_device_span_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + 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); +} + +static ssize_t +dahdi_device_auto_register(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dahdi_device *ddev = to_ddev(dev); + dahdi_register_device_spans(ddev); + return count; +} + +static ssize_t +dahdi_device_register_span(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct dahdi_span *span; + unsigned int span_offset; + unsigned int desired_spanno; + unsigned int desired_basechanno; + struct dahdi_device *const ddev = to_ddev(dev); + + ret = sscanf(buf, "%u:%u:%u", &span_offset, &desired_spanno, + &desired_basechanno); + if (ret != 3) + return -EINVAL; + + list_for_each_entry(span, &ddev->spans, device_node) { + if (span_offset == span->offset) { + ret = dahdi_register_span(span, desired_spanno, + desired_basechanno, 1); + return (ret) ? ret : count; + } + } + + return -EINVAL; +} + +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(auto_register, S_IWUSR, NULL, dahdi_device_auto_register), + __ATTR(register_span, S_IWUSR, NULL, dahdi_device_register_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"); @@ -482,6 +567,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->release = dahdi_device_release; + dev_set_name(dev, "dahdi:%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); +} + +void dahdi_sysfs_unregister_device(struct dahdi_device *ddev) +{ + device_del(&ddev->dev); } int __init dahdi_sysfs_init(const struct file_operations *dahdi_fops) @@ -489,6 +617,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..b4fc23e 100644 --- a/drivers/dahdi/dahdi.h +++ b/drivers/dahdi/dahdi.h @@ -34,4 +34,22 @@ 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_register_span(struct dahdi_span *span, unsigned int spanno, + unsigned int basechan, int prefmaster); +int dahdi_register_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); +} + #endif /* _DAHDI_H */ |