diff options
author | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-07-06 13:47:05 +0000 |
---|---|---|
committer | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-07-06 13:47:05 +0000 |
commit | 39a1812c1ef76b6a792f00087f1e507616bbbb25 (patch) | |
tree | e50633c999779c514ef16f4a2ce7a70fc7511c9e /xpp/xbus-core.c | |
parent | 70ef1183eba2d2fe4f00668fd3438b7f1c842c94 (diff) |
Tons of updates to the Astribank (xpp) driver:
* xpd_fxo.ko (FXO span) is now operational
* Remove obsolete .inc initialization files (we use user-space init)
* Added an install target to the utils dir.
* Updated README.Astribank accordingly.
* Using RBS signalling, as caller ID did not work well otherwise.
* Better handling of USB protocol errors.
* Fixed some procfs-related races.
* per-card-module ioctls.
* fxotune support.
* opermode support (set through /etc/default/zaptel for now)
* Userspace initialization script can also read registers.
* Power calibration works (and implemented in perl)
* some fine-tuning to the regster initialization parameters.
* Leds turn on before registration and turn off after it.
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@1204 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/xbus-core.c')
-rw-r--r-- | xpp/xbus-core.c | 229 |
1 files changed, 213 insertions, 16 deletions
diff --git a/xpp/xbus-core.c b/xpp/xbus-core.c index 364d8e4..5a85f8b 100644 --- a/xpp/xbus-core.c +++ b/xpp/xbus-core.c @@ -30,6 +30,7 @@ #include <linux/errno.h> #include <linux/proc_fs.h> #include <linux/device.h> +#include <linux/delay.h> /* for mdelay() to debug */ #include "xpd.h" #include "xpp_zap.h" #include "xbus-core.h" @@ -38,13 +39,14 @@ static const char rcsid[] = "$Id$"; /* Defines */ +#define POLL_TIMEOUT (MAX_XPDS) /* in jiffies */ #define PROC_XBUSES "xbuses" #define PROC_XBUS_SUMMARY "summary" +#define PROC_XBUS_WAITFOR_XPDS "waitfor_xpds" /* Command line parameters */ extern int print_dbg; extern int max_queue_len; -extern int ignore_xpds; /* Forward declarations */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) @@ -58,10 +60,13 @@ extern int ignore_xpds; static DEVICE_ATTR_FUNC(connector_show, dev, buf); static DEVICE_ATTR_FUNC(status_show, dev, buf); +static int xbus_poll(void *data); static void xbus_release(struct device *dev); static int xbus_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data); +static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count, int *eof, void *data); /* Data structures */ +struct workqueue_struct *xpp_worker = NULL; static spinlock_t xbuses_lock = SPIN_LOCK_UNLOCKED; static xbus_t *xbuses_array[MAX_BUSES] = {}; static int bus_count = 0; @@ -234,29 +239,154 @@ xpd_t *xpd_of(xbus_t *xbus, int xpd_num) return xbus->xpds[xpd_num]; } -static void xbus_poll(xbus_t *xbus) +int xbus_register_xpd(xbus_t *xbus, xpd_t *xpd) { - int id; - int ret; - xpd_t **xpds; - xpd_t *xpd; + unsigned int xpd_num = xpd->id; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&xbus->lock, flags); + if(!VALID_XPD_NUM(xpd_num)) { + ERR("%s: Bad xpd_num = %d\n", xbus->busname, xpd_num); + ret = -EINVAL; + goto out; + } + if(xbus->xpds[xpd_num] != NULL) { + xpd_t *other = xbus->xpds[xpd_num]; + + ERR("%s: xpd_num=%d is occupied by %p (%s)\n", + xbus->busname, xpd_num, other, other->xpdname); + ret = -EINVAL; + goto out; + } + xbus->xpds[xpd_num] = xpd; + xbus->num_xpds++; +out: + spin_unlock_irqrestore(&xbus->lock, flags); + return ret; +} + +int xbus_unregister_xpd(xbus_t *xbus, xpd_t *xpd) +{ + unsigned int xpd_num = xpd->id; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&xbus->lock, flags); + if(!VALID_XPD_NUM(xpd_num)) { + ERR("%s: Bad xpd_num = %d\n", xbus->busname, xpd_num); + ret = -EINVAL; + goto out; + } + if(xbus->xpds[xpd_num] != xpd) { + xpd_t *other = xbus->xpds[xpd_num]; + + ERR("%s: xpd_num=%d is occupied by %p (%s)\n", + xbus->busname, xpd_num, other, other->xpdname); + ret = -EINVAL; + goto out; + } + xbus->xpds[xpd_num] = NULL; + xbus->num_xpds--; + xpd->xbus = NULL; +out: + spin_unlock_irqrestore(&xbus->lock, flags); + return ret; +} + +/* + * This must be called from synchronous (non-interrupt) context + * it returns only when all XPD's on the bus are detected and + * initialized. + */ +static int xbus_poll(void *data) +{ + int id; + int ret; + unsigned long flags; + struct list_head *card; + struct list_head *next_card; + struct list_head removal_list; + struct list_head additions_list; + int count_removed; + int count_added; + int xpd_num; + xbus_t *xbus = data; + spin_lock_irqsave(&xbus->lock, flags); DBG("%s\n", xbus->busname); - xpds = xbus->xpds; + + /* + * Send out the polls + */ + atomic_set(&xbus->count_poll_answers, 0); for(id = 0; id < MAX_XPDS; id++) { if(!xbus->hardware_exists) break; - xpd = xpd_of(xbus, id); - if(IS_SET(ignore_xpds, id)) { /* skip xpds */ - DBG(" Ignoring XPD #%d\n", id); - continue; - } - DBG(" Polling slot %d %s\n", id, xbus->busname); + // DBG(" Polling slot %d %s\n", id, xbus->busname); + spin_unlock_irqrestore(&xbus->lock, flags); ret = CALL_PROTO(GLOBAL, DESC_REQ, xbus, NULL, id); + spin_lock_irqsave(&xbus->lock, flags); if(ret < 0) { NOTICE("xpp: %s: Failed sending DESC_REQ to XPD #%d\n", __FUNCTION__, id); + break; + } + mdelay(1); /* FIXME: debugging for Dima */ + } + spin_unlock_irqrestore(&xbus->lock, flags); + DBG("%s: Polled %d XPD's. Waiting for replies\n", xbus->busname, MAX_XPDS); + ret = wait_event_timeout(xbus->wait_for_polls, atomic_read(&xbus->count_poll_answers) >= MAX_XPDS, POLL_TIMEOUT); + if(ret < 0) { + ERR("%s: Poll timeout %d\n", xbus->busname, ret); + return ret; + } + DBG("%s: Poll finished. Start processing.\n", xbus->busname); + spin_lock_irqsave(&xbus->lock, flags); + INIT_LIST_HEAD(&removal_list); + INIT_LIST_HEAD(&additions_list); + count_removed = 0; + count_added = 0; + list_for_each_safe(card, next_card, &xbus->poll_results) { + struct card_desc_struct *card_desc = list_entry(card, struct card_desc_struct, card_list); + byte type = card_desc->type; + xpd_t *xpd; + + BUG_ON(card_desc->magic != CARD_DESC_MAGIC); + xpd_num = xpd_addr2num(&card_desc->xpd_addr); + xpd = xpd_of(xbus, xpd_num); + + if(xpd && type == XPD_TYPE_NOMODULE) { /* card removal */ + list_move_tail(card, &removal_list); + count_removed++; + } else if(!xpd && type != XPD_TYPE_NOMODULE) { /* card detection */ + list_move_tail(card, &additions_list); + count_added++; + } else { /* same same */ + list_del(card); + kfree(card_desc); } } + spin_unlock_irqrestore(&xbus->lock, flags); + INFO("%s: Poll results: removals=%d additions=%d\n", xbus->busname, count_removed, count_added); + list_for_each_safe(card, next_card, &removal_list) { + struct card_desc_struct *card_desc = list_entry(card, struct card_desc_struct, card_list); + xpd_t *xpd; + + list_del(card); + xpd_num = xpd_addr2num(&card_desc->xpd_addr); + xpd = xpd_of(xbus, xpd_num); + if(xpd) + xpd_disconnect(xpd); + kfree(card); + } + list_for_each_safe(card, next_card, &additions_list) { + struct card_desc_struct *card_desc = list_entry(card, struct card_desc_struct, card_list); + + list_del(card); + card_detected(card_desc); + } + complete_all(&xbus->xpds_initialized); + return 0; } @@ -274,7 +404,11 @@ void xbus_activate(xbus_t *xbus) xbus->hardware_exists = 1; DBG("Activating: %s\n", xbus->busname); /* Poll it */ - xbus_poll(xbus); + INIT_WORK(&xbus->xpds_init_work, (void (*)(void *))xbus_poll, (void *)xbus); + if(!queue_work(xpp_worker, &xbus->xpds_init_work)) { + ERR("Failed to queue xpd initialization work\n"); + /* FIXME: need to return error */ + } } void xbus_disconnect(xbus_t *xbus) @@ -348,6 +482,11 @@ static void xbus_free(xbus_t *xbus) remove_proc_entry(PROC_XBUS_SUMMARY, xbus->proc_xbus_dir); xbus->proc_xbus_summary = NULL; } + if(xbus->proc_xbus_waitfor_xpds) { + DBG("Removing proc '%s' for %s\n", PROC_XBUS_WAITFOR_XPDS, xbus->busname); + remove_proc_entry(PROC_XBUS_WAITFOR_XPDS, xbus->proc_xbus_dir); + xbus->proc_xbus_waitfor_xpds = NULL; + } DBG("Removing proc directory %s\n", xbus->busname); remove_proc_entry(xbus->busname, xpp_proc_toplevel); xbus->proc_xbus_dir = NULL; @@ -385,7 +524,11 @@ xbus_t *xbus_new(xbus_ops_t *ops) INFO("New xbus: %s\n", xbus->busname); init_waitqueue_head(&xbus->packet_cache_empty); atomic_set(&xbus->packet_counter, 0); + atomic_set(&xbus->count_poll_answers, 0); + init_waitqueue_head(&xbus->wait_for_polls); init_rwsem(&xbus->in_use); + INIT_LIST_HEAD(&xbus->poll_results); + init_completion(&xbus->xpds_initialized); xbus->num_xpds = 0; xbus_reset_counters(xbus); @@ -425,6 +568,15 @@ xbus_t *xbus_new(xbus_ops_t *ops) err = -EIO; goto nobus; } + xbus->proc_xbus_summary->owner = THIS_MODULE; + xbus->proc_xbus_waitfor_xpds = create_proc_read_entry(PROC_XBUS_WAITFOR_XPDS, 0444, xbus->proc_xbus_dir, + xbus_read_waitfor_xpds, xbus); + if (!xbus->proc_xbus_waitfor_xpds) { + ERR("Failed to create '%s' proc file for xbus %s\n", PROC_XBUS_WAITFOR_XPDS, xbus->busname); + err = -EIO; + goto nobus; + } + xbus->proc_xbus_waitfor_xpds->owner = THIS_MODULE; #endif /* Sanity checks */ if(!ops->packet_send) { @@ -507,7 +659,9 @@ static int xbus_read_proc(char *page, char **start, off_t off, int count, int *e (xbus->hardware_exists) ? "connected" : "missing", xbus->bus_type ); - len += sprintf(page + len, "max_packet_size=%d open_counter=%d packet_count=%d\n", + len += sprintf(page + len, "POLLS: %d/%d\n", atomic_read(&xbus->count_poll_answers), MAX_XPDS); + len += sprintf(page + len, "XPDS_READY: %s\n", (xbus->xpds_initialized.done) ? "YES" : "NO"); + len += sprintf(page + len, "\nmax_packet_size=%d open_counter=%d packet_count=%d\n", xbus->max_packet_size, xbus->open_counter, atomic_read(&xbus->packet_counter) @@ -532,6 +686,36 @@ out: } +static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + xbus_t *xbus = data; + int i; + + if(!xbus) + goto out; + i = wait_for_completion_interruptible_timeout(&xbus->xpds_initialized, 40*HZ); + if(i < 0) { + NOTICE("PID=%d waiting for XPDS initialization failed: %d\n", current->pid, i); + return i; + } + spin_lock_irqsave(&xbus->lock, flags); + len += sprintf(page + len, "XPDS_READY: %s\n", (xbus->xpds_initialized.done) ? "YES" : "NO"); + spin_unlock_irqrestore(&xbus->lock, flags); +out: + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; + +} + static int read_proc_xbuses(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; @@ -611,14 +795,19 @@ static int xbus_hotplug(struct device *device, char **envp, int envnum, char *bu struct bus_type xbus_bus_type = { .name = "xbus", .match = xbus_match, +/* FIXME: Hotplug replaced with uevent in 2.6.16 */ #if 0 -/* Hotplug replaced with uevent in 2.6.16 */ .hotplug = xbus_hotplug, #endif }; static void xbus_core_cleanup(void) { + if (xpp_worker) { + flush_workqueue(xpp_worker); + destroy_workqueue(xpp_worker); + xpp_worker = NULL; + } #ifdef CONFIG_PROC_FS if(proc_xbuses) remove_proc_entry(PROC_XBUSES, xpp_proc_toplevel); @@ -638,12 +827,20 @@ int __init xbus_core_init(void) if(!packet_cache) { return -ENOMEM; } + xpp_worker = create_singlethread_workqueue("xppworker"); + if(!xpp_worker) { + ERR("Failed to create card detector workqueue.\n"); + xbus_core_cleanup(); + return -ENOMEM; + } #ifdef CONFIG_PROC_FS proc_xbuses = create_proc_read_entry(PROC_XBUSES, 0444, xpp_proc_toplevel, read_proc_xbuses, 0); if (!proc_xbuses) { + ERR("Failed to create proc file %s\n", PROC_XBUSES); xbus_core_cleanup(); return -EFAULT; } + proc_xbuses->owner = THIS_MODULE; #endif ret = bus_register(&xbus_bus_type); if(ret) { |