summaryrefslogtreecommitdiff
path: root/xpp/xbus-core.c
diff options
context:
space:
mode:
authortzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2007-02-28 01:23:19 +0000
committertzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2007-02-28 01:23:19 +0000
commitff8823199f375d709a689dd017950d575b649df6 (patch)
tree7244e80bb45da5f27b747c8bc9aee36b984dd5cd /xpp/xbus-core.c
parent121cb4b570046fe612938d1eb401181c970b4636 (diff)
Merge xpp rev. 3495:
------------------------------------------------------------------------ r2243 | tzafrir | 2007-02-28 02:05:59 +0200 (Wed, 28 Feb 2007) | 4 lines * xpp rev. 3495: fix a race in the FXO driver of recent weeks. * Add the Astribank BRI driver (though still needs bristuffed zaptel to build and thus will not build by default) ------------------------------------------------------------------------ r2239 | tzafrir | 2007-02-27 08:14:18 +0200 (Tue, 27 Feb 2007) | 18 lines Xorcom rev. 3491: * Version of xpp modules is set from xpp/.version, rather than "unknown". * Astribank devices are now initialized in parallel: faster startup when there are multiple Astribanks. * Re-added support for the old format of /proc/xpp/sync write: (echo N 0 > /proc/xpp/sync ) . The new format (SYNC=NN) is preffered. * Firmware update to fix a PCM issue. * Fixed a build issue with kernel 2.6.8 . * Fixed missing initialization in Zaptel::Xpp::Xbus . * genzaptelconf will now set FXS ports as LS by default. To set them as KS, use fxs_default_start=ks in /etc/default/zaptel / /etc/sysconfig/zaptel (Also a workaround for #7755 ). * Groundwork for sync from zaptel master span: if zaptel is built with ZAPTEL_SYNC_TIC (see zaptel/team/tzafrir/sync ), xpp will report its drift from the zaptel sync master. * USB firmware update: had bad lines checksums (and fxload did not report). * fpga_load can now better report bad hex file checksum ;-) . ------------------------------------------------------------------------ r2223 | tzafrir | 2007-02-24 03:05:05 +0200 (Sat, 24 Feb 2007) | 3 lines Add the Zaptel and Zaptel::Xpp perl modules, and some simple utilities that use them. disabled by default for now. ------------------------------------------------------------------------ r2222 | tzafrir | 2007-02-24 02:55:05 +0200 (Sat, 24 Feb 2007) | 2 lines Make the xpp/utils/Makefile in 1.2 closer to the one in 1.4 . git-svn-id: http://svn.digium.com/svn/zaptel/trunk@2247 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/xbus-core.c')
-rw-r--r--xpp/xbus-core.c223
1 files changed, 166 insertions, 57 deletions
diff --git a/xpp/xbus-core.c b/xpp/xbus-core.c
index 84951d4..a65e201 100644
--- a/xpp/xbus-core.c
+++ b/xpp/xbus-core.c
@@ -32,6 +32,7 @@
#ifdef PROTOCOL_DEBUG
#include <linux/ctype.h>
#endif
+#include <linux/workqueue.h>
#include <linux/device.h>
#include <linux/delay.h> /* for msleep() to debug */
#include "xpd.h"
@@ -77,7 +78,6 @@ static int xbus_read_proc(char *page, char **start, off_t off, int count, int *e
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;
@@ -264,15 +264,22 @@ void xbus_xframe_free(xbus_t *xbus, xframe_t *p)
/*
* Return pointer to next packet slot in the frame
* or NULL if the frame is full.
+ *
+ * FIXME: we do not use atomic_add_return() because kernel-2.6.8
+ * does not have it. This make this code a little racy,
+ * but we currently call xframe_next_packet() only in the
+ * PCM loop (xbus_tick() etc.)
*/
xpacket_t *xframe_next_packet(xframe_t *frm, int len)
{
- int newlen = atomic_add_return(len, &frm->frame_len);
+ int newlen = atomic_read(&frm->frame_len);
+
+ newlen += len;
// DBG("len=%d, newlen=%d, frm->frame_len=%d\n", len, newlen, XFRAME_LEN(frm));
if (newlen > XFRAME_DATASIZE) {
- atomic_sub(len, &frm->frame_len);
return NULL;
}
+ atomic_add(len, &frm->frame_len);
return (xpacket_t *)(frm->packets + newlen - len);
}
@@ -435,13 +442,12 @@ out:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
static void xbus_poll(struct work_struct *work)
{
- xbus_t *xbus = container_of(work, xbus_t, xpds_init_work);
+ struct xbus_poller *poller = container_of(work, struct xbus_poller, xpds_init_work);
#else
static void xbus_poll(void *data)
{
- xbus_t *xbus = data;
+ struct xbus_poller *poller = data;
#endif
-
int id;
int ret = 0;
unsigned long flags;
@@ -451,7 +457,11 @@ static void xbus_poll(void *data)
struct list_head additions_list;
int count_removed;
int count_added;
+ xbus_t *xbus;
+ BUG_ON(!poller);
+ xbus = poller->xbus;
+ BUG_ON(!xbus);
if(!down_read_trylock(&xbus->in_use)) {
ERR("%s is being removed...\n", xbus->busname);
return;
@@ -459,6 +469,7 @@ static void xbus_poll(void *data)
msleep(2); /* roundtrip for older polls */
spin_lock_irqsave(&xbus->lock, flags);
DBG("%s\n", xbus->busname);
+ poller->is_polling = 1;
/*
* Send out the polls
@@ -481,7 +492,7 @@ static void xbus_poll(void *data)
* Wait for replies
*/
DBG("%s: Polled %d XPD's. Waiting for replies max %d jiffies\n", xbus->busname, MAX_XPDS, poll_timeout);
- ret = wait_event_interruptible_timeout(xbus->wait_for_polls, atomic_read(&xbus->count_poll_answers) >= MAX_XPDS, poll_timeout);
+ ret = wait_event_interruptible_timeout(poller->wait_for_polls, atomic_read(&poller->count_poll_answers) >= MAX_XPDS, poll_timeout);
if(ret == 0) {
ERR("%s: Poll timeout. Continuing anyway.\n", xbus->busname);
/*
@@ -500,7 +511,7 @@ static void xbus_poll(void *data)
INIT_LIST_HEAD(&additions_list);
count_removed = 0;
count_added = 0;
- list_for_each_safe(card, next_card, &xbus->poll_results) {
+ list_for_each_safe(card, next_card, &poller->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;
@@ -523,7 +534,7 @@ static void xbus_poll(void *data)
* We set this *after* poll is finished, so wait_for_xpd_initialization can
* tell we already know how many units we have.
*/
- atomic_set(&xbus->count_xpds_to_initialize, count_added);
+ atomic_set(&poller->count_xpds_to_initialize, count_added);
spin_unlock_irqrestore(&xbus->lock, flags);
INFO("%s: Poll results: removals=%d additions=%d\n", xbus->busname, count_removed, count_added);
/*
@@ -548,39 +559,144 @@ static void xbus_poll(void *data)
list_del(card);
/* FIXME: card_detected() should have a return value for count_xpds_initialized */
card_detected(card_desc);
- atomic_inc(&xbus->count_xpds_initialized);
+ atomic_inc(&poller->count_xpds_initialized);
}
- wake_up(&xbus->wait_for_xpd_initialization);
+ wake_up(&poller->wait_for_xpd_initialization);
out:
+ poller->is_polling = 0;
up_read(&xbus->in_use);
return;
}
+void xbus_poller_notify(xbus_t *xbus, struct card_desc_struct *card_desc)
+{
+ struct xbus_poller *poller;
+ unsigned long flags;
+
+ BUG_ON(!xbus);
+ poller = xbus->poller;
+ BUG_ON(!poller);
+ if(!poller->is_polling) {
+ NOTICE("%s: %d-%d replied not during poll. Ignore\n",
+ xbus->busname,
+ card_desc->xpd_addr.unit,
+ card_desc->xpd_addr.subunit);
+ kfree(card_desc);
+ return;
+ }
+ spin_lock_irqsave(&xbus->lock, flags);
+ if(card_desc->type == XPD_TYPE_NOMODULE)
+ XBUS_COUNTER(xbus, DEV_DESC_EMPTY)++;
+ else
+ XBUS_COUNTER(xbus, DEV_DESC_FULL)++;
+ atomic_inc(&poller->count_poll_answers);
+ list_add_tail(&card_desc->card_list, &poller->poll_results);
+ spin_unlock_irqrestore(&xbus->lock, flags);
+ /*
+ * wake_up only after exiting our critical section.
+ * We suspect that otherwise a spinlock nesting may occur
+ * and cause a panic (if spinlock debugging is compiled in).
+ */
+ wake_up(&poller->wait_for_polls);
+ return;
+}
+
+static void poller_destroy(struct xbus_poller *poller)
+{
+ if(!poller)
+ return;
+ if(poller->xbus) {
+ DBG("%s: detach poller\n", poller->xbus->busname);
+ poller->xbus->poller = NULL;
+ }
+ if (poller->wq) {
+ DBG("%s: destroy workqueue\n", poller->xbus->busname);
+ flush_workqueue(poller->wq);
+ destroy_workqueue(poller->wq);
+ poller->wq = NULL;
+ }
+ kfree(poller);
+}
+
+/*
+ * Allocate a poller for the xbus including the nessessary workqueue.
+ * May call blocking operations, but only briefly (as we are called
+ * from xbus_new() which is called from khubd.
+ */
+static struct xbus_poller *poller_new(xbus_t *xbus)
+{
+ struct xbus_poller *poller;
+
+ BUG_ON(xbus->busname[0] == '\0'); /* No name? */
+ BUG_ON(xbus->poller); /* Hmmm... overrun pollers? */
+ DBG("%s\n", xbus->busname);
+ poller = kmalloc(sizeof(*poller), SLAB_KERNEL);
+ if(!poller)
+ goto err;
+ memset(poller, 0, sizeof(*poller));
+ poller->xbus = xbus;
+ xbus->poller = poller;
+ /* poll related variables */
+ atomic_set(&poller->count_poll_answers, 0);
+ atomic_set(&poller->count_xpds_to_initialize, 0);
+ atomic_set(&poller->count_xpds_initialized, 0);
+ INIT_LIST_HEAD(&poller->poll_results);
+ init_waitqueue_head(&poller->wait_for_polls);
+ init_waitqueue_head(&poller->wait_for_xpd_initialization);
+ poller->wq = create_singlethread_workqueue(xbus->busname);
+ if(!poller->wq) {
+ ERR("%s: Failed to create poller workqueue.\n", xbus->busname);
+ goto err;
+ }
+ return poller;
+err:
+ poller_destroy(poller);
+ return NULL;
+}
+
+/*
+ * Sends an xbus_poll() work to the poller workqueue of the given xbus.
+ */
+static int poller_dispatch(xbus_t *xbus)
+{
+ struct xbus_poller *poller = xbus->poller;
+
+ if(!poller) {
+ ERR("%s: missing poller\n", xbus->busname);
+ return 0;
+ }
+ /* Initialize the work. (adapt to kernel API changes). */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
+ INIT_WORK(&poller->xpds_init_work, xbus_poll);
+#else
+ INIT_WORK(&poller->xpds_init_work, xbus_poll, poller);
+#endif
+ /* Now send it */
+ if(!queue_work(poller->wq, &poller->xpds_init_work)) {
+ ERR("%s: Failed to queue xpd initialization work\n",
+ xbus->busname);
+ return 0;
+ }
+ return 1;
+}
void xbus_activate(xbus_t *xbus)
{
- xbus_ops_t *ops;
+ xbus_ops_t *ops;
+ struct xbus_poller *poller;
BUG_ON(!xbus);
ops = xbus->ops;
BUG_ON(!ops);
+ poller = xbus->poller;
+ BUG_ON(!poller);
/* Sanity checks */
BUG_ON(!ops->xframe_send);
BUG_ON(!ops->xframe_new || !ops->xframe_free);
xbus->hardware_exists = 1;
DBG("Activating: %s\n", xbus->busname);
-
/* Poll it */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
- INIT_WORK(&xbus->xpds_init_work, xbus_poll);
-#else
- INIT_WORK(&xbus->xpds_init_work, xbus_poll, xbus);
-#endif
-
- if(!queue_work(xpp_worker, &xbus->xpds_init_work)) {
- ERR("Failed to queue xpd initialization work\n");
- /* FIXME: need to return error */
- }
+ poller_dispatch(xbus);
}
void xbus_disconnect(xbus_t *xbus)
@@ -685,6 +801,7 @@ static void xbus_free(xbus_t *xbus)
device_remove_file(&xbus->the_bus, &dev_attr_status);
device_remove_file(&xbus->the_bus, &dev_attr_connector);
device_unregister(&xbus->the_bus);
+ poller_destroy(xbus->poller);
kfree(xbus);
}
@@ -697,17 +814,16 @@ static void xbus_release(struct device *dev)
DBG("%s\n", xbus->busname);
}
-
xbus_t *xbus_new(xbus_ops_t *ops)
{
- int err;
- xbus_t *xbus = NULL;
+ int err;
+ xbus_t *xbus = NULL;
+ struct xbus_poller *poller;
BUG_ON(!ops);
xbus = xbus_alloc();
if(!xbus)
return NULL;
-
/* Init data structures */
spin_lock_init(&xbus->lock);
snprintf(xbus->busname, XBUS_NAMELEN, "XBUS-%02d", xbus->num);
@@ -715,14 +831,13 @@ xbus_t *xbus_new(xbus_ops_t *ops)
init_waitqueue_head(&xbus->packet_cache_empty);
atomic_set(&xbus->packet_counter, 0);
- /* poll related variables */
- atomic_set(&xbus->count_poll_answers, 0);
- atomic_set(&xbus->count_xpds_to_initialize, 0);
- atomic_set(&xbus->count_xpds_initialized, 0);
- INIT_LIST_HEAD(&xbus->poll_results);
- init_waitqueue_head(&xbus->wait_for_polls);
- init_waitqueue_head(&xbus->wait_for_xpd_initialization);
xbus->num_xpds = 0;
+ poller = poller_new(xbus);
+ if(!poller) {
+ ERR("Failed to allocate poller\n");
+ xbus_free(xbus);
+ return NULL;
+ }
init_rwsem(&xbus->in_use);
xbus_reset_counters(xbus);
@@ -866,14 +981,16 @@ void xbus_reset_counters(xbus_t *xbus)
#if CONFIG_PROC_FS
static int xbus_read_proc(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;
+ xbus_t *xbus = data;
+ struct xbus_poller *poller;
+ unsigned long flags;
+ int len = 0;
+ int i;
if(!xbus)
goto out;
spin_lock_irqsave(&xbus->lock, flags);
+ poller = xbus->poller;
len += sprintf(page + len, "%s: CONNECTOR=%s STATUS=%s bus_type=%d\n",
xbus->busname,
@@ -882,10 +999,10 @@ static int xbus_read_proc(char *page, char **start, off_t off, int count, int *e
xbus->bus_type
);
len += sprintf(page + len, "POLLS: %d/%d\n",
- atomic_read(&xbus->count_poll_answers), MAX_XPDS);
+ atomic_read(&poller->count_poll_answers), MAX_XPDS);
len += sprintf(page + len, "XPDS_READY: %d/%d\n",
- atomic_read(&xbus->count_xpds_initialized),
- atomic_read(&xbus->count_xpds_to_initialize));
+ atomic_read(&poller->count_xpds_initialized),
+ atomic_read(&poller->count_xpds_to_initialize));
len += sprintf(page + len, "\nmax_packet_size=%d open_counter=%d packet_count=%d\n",
xbus->max_packet_size,
xbus->open_counter,
@@ -916,10 +1033,12 @@ static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count
int len = 0;
unsigned long flags;
xbus_t *xbus = data;
+ struct xbus_poller *poller;
int ret;
if(!xbus)
goto out;
+ poller = xbus->poller;
DBG("%s: Waiting for card initialization of %d XPD's max %d seconds\n", xbus->busname, MAX_XPDS, INITIALIZATION_TIMEOUT/HZ);
/*
* xbus_poll sets count_xpds_to_initialize only when polling is finished.
@@ -927,9 +1046,9 @@ static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count
* - It is none zero -- meaning we already have the poll results.
* - And all units have finished initialization.
*/
- ret = wait_event_interruptible_timeout(xbus->wait_for_xpd_initialization,
- atomic_read(&xbus->count_xpds_to_initialize) &&
- atomic_read(&xbus->count_xpds_initialized) >= atomic_read(&xbus->count_xpds_to_initialize),
+ ret = wait_event_interruptible_timeout(poller->wait_for_xpd_initialization,
+ atomic_read(&poller->count_xpds_to_initialize) &&
+ atomic_read(&poller->count_xpds_initialized) >= atomic_read(&poller->count_xpds_to_initialize),
INITIALIZATION_TIMEOUT);
if(ret == 0) {
ERR("%s: Card Initialization Timeout\n", xbus->busname);
@@ -942,8 +1061,8 @@ static int xbus_read_waitfor_xpds(char *page, char **start, off_t off, int count
spin_lock_irqsave(&xbus->lock, flags);
len += sprintf(page + len, "XPDS_READY: %s: %d/%d\n",
xbus->busname,
- atomic_read(&xbus->count_xpds_initialized),
- atomic_read(&xbus->count_xpds_to_initialize));
+ atomic_read(&poller->count_xpds_initialized),
+ atomic_read(&poller->count_xpds_to_initialize));
spin_unlock_irqrestore(&xbus->lock, flags);
out:
if (len <= off+count)
@@ -1105,11 +1224,6 @@ struct bus_type xbus_bus_type = {
static void xbus_core_cleanup(void)
{
- if (xpp_worker) {
- flush_workqueue(xpp_worker);
- destroy_workqueue(xpp_worker);
- xpp_worker = NULL;
- }
#ifdef XPP_DEBUGFS
if(debugfs_root) {
DBG("Removing xpp from debugfs\n");
@@ -1144,12 +1258,6 @@ int __init xbus_core_init(void)
if(!xframes_cache) {
return -ENOMEM;
}
- xpp_worker = create_singlethread_workqueue("xppworker");
- if(!xpp_worker) {
- ERR("Failed to create card detector workqueue.\n");
- ret = -ENOMEM;
- goto err;
- }
#ifdef CONFIG_PROC_FS
proc_xbuses = create_proc_read_entry(PROC_XBUSES, 0444, xpp_proc_toplevel, read_proc_xbuses, NULL);
if (!proc_xbuses) {
@@ -1201,6 +1309,7 @@ EXPORT_SYMBOL(xbus_disconnect);
EXPORT_SYMBOL(xbus_reset_counters);
EXPORT_SYMBOL(xframe_next_packet);
EXPORT_SYMBOL(dump_xframe);
+EXPORT_SYMBOL(xbus_poller_notify);
#ifdef XPP_DEBUGFS
EXPORT_SYMBOL(xbus_log);
#endif