summaryrefslogtreecommitdiff
path: root/xpp/xbus-core.c
diff options
context:
space:
mode:
authortzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2006-07-06 13:47:05 +0000
committertzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2006-07-06 13:47:05 +0000
commit39a1812c1ef76b6a792f00087f1e507616bbbb25 (patch)
treee50633c999779c514ef16f4a2ce7a70fc7511c9e /xpp/xbus-core.c
parent70ef1183eba2d2fe4f00668fd3438b7f1c842c94 (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.c229
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) {