diff options
author | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-02-28 01:23:19 +0000 |
---|---|---|
committer | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-02-28 01:23:19 +0000 |
commit | ff8823199f375d709a689dd017950d575b649df6 (patch) | |
tree | 7244e80bb45da5f27b747c8bc9aee36b984dd5cd /xpp/xbus-core.c | |
parent | 121cb4b570046fe612938d1eb401181c970b4636 (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.c | 223 |
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 |