summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTzafrir Cohen <tzafrir.cohen@xorcom.com>2011-10-26 19:04:04 +0000
committerTzafrir Cohen <tzafrir.cohen@xorcom.com>2011-10-26 19:04:04 +0000
commitf421d5dd8d33917f9994785b7348cffca35e5732 (patch)
tree95ba6e00db173e9a2593bed0f9bc2632b03cdf9f
parent7f136e308d607a9b3e1d82137dbdee0b36c90de7 (diff)
wcte12xp: Allow linemode (T1/E1) to be changed via sysfs attribute.
Allowing the linemode to be configured with sysfs before the spans are assigned opens the eventualy capability for line mode to be configured with the other physical layer settings per card. Currently linemode is set with either physical jumpers or with a module parameter to the wcte12xp driver that is global for all cards. Default behavior is not changed with this commit. Signed-off-by: Shaun Ruffell <sruffell@digium.com> git-svn-id: http://svn.asterisk.org/svn/dahdi/linux/trunk@10278 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--drivers/dahdi/wcte12xp/base.c248
1 files changed, 169 insertions, 79 deletions
diff --git a/drivers/dahdi/wcte12xp/base.c b/drivers/dahdi/wcte12xp/base.c
index 53c4f7a..e7f3dca 100644
--- a/drivers/dahdi/wcte12xp/base.c
+++ b/drivers/dahdi/wcte12xp/base.c
@@ -776,19 +776,31 @@ static void __t1xxp_set_clear(struct t1 *wc)
t1_info(wc, "Unable to set clear/rbs mode!\n");
}
-static void free_wc(struct t1 *wc)
+/**
+ * _t1_free_channels - Free the memory allocated for the channels.
+ *
+ * Must be called with wc->reglock held.
+ *
+ */
+static void _t1_free_channels(struct t1 *wc)
{
- unsigned int x;
- unsigned long flags;
- struct command *cmd;
- LIST_HEAD(list);
-
+ int x;
for (x = 0; x < ARRAY_SIZE(wc->chans); x++) {
kfree(wc->chans[x]);
kfree(wc->ec[x]);
+ wc->chans[x] = NULL;
+ wc->ec[x] = NULL;
}
+}
+
+static void free_wc(struct t1 *wc)
+{
+ unsigned long flags;
+ struct command *cmd;
+ LIST_HEAD(list);
spin_lock_irqsave(&wc->reglock, flags);
+ _t1_free_channels(wc);
list_splice_init(&wc->active_cmds, &list);
list_splice_init(&wc->pending_cmds, &list);
spin_unlock_irqrestore(&wc->reglock, flags);
@@ -1589,6 +1601,7 @@ static int vpm_start_load(struct t1 *wc)
work->wc = wc;
queue_work(wc->wq, &work->work);
+ wc->not_ready++;
return 0;
}
@@ -1641,6 +1654,17 @@ static void check_and_load_vpm(struct t1 *wc)
* done setting it up here, an hour should cover it... */
wc->vpm_check = jiffies + HZ*3600;
+ /* If there was one already allocated, let's free it. */
+ if (wc->vpmadt032) {
+ vpmadt = wc->vpmadt032;
+ clear_bit(VPM150M_ACTIVE, &vpmadt->control);
+ flush_workqueue(vpmadt->wq);
+ spin_lock_irqsave(&wc->reglock, flags);
+ wc->vpmadt032 = NULL;
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ vpmadt032_free(vpmadt);
+ }
+
vpmadt = vpmadt032_alloc(&options);
if (!vpmadt)
return;
@@ -1778,22 +1802,6 @@ static void t1xxp_disable_hw_preechocan(struct dahdi_chan *chan)
vpmoct_preecho_disable(wc->vpmoct, chan->chanpos - 1);
}
-static const struct dahdi_span_ops t1_span_ops = {
- .owner = THIS_MODULE,
- .spanconfig = t1xxp_spanconfig,
- .chanconfig = t1xxp_chanconfig,
- .startup = t1xxp_startup,
- .rbsbits = t1xxp_rbsbits,
- .maint = t1xxp_maint,
- .ioctl = t1xxp_ioctl,
-#ifdef VPM_SUPPORT
- .enable_hw_preechocan = t1xxp_enable_hw_preechocan,
- .disable_hw_preechocan = t1xxp_disable_hw_preechocan,
- .echocan_create = t1xxp_echocan_create,
- .echocan_name = t1xxp_echocan_name,
-#endif
-};
-
/**
* t1_software_init - Initialize the board for the given type.
* @wc: The board to initialize.
@@ -1806,34 +1814,32 @@ static const struct dahdi_span_ops t1_span_ops = {
static int t1_software_init(struct t1 *wc, enum linemode type)
{
int x;
- int res;
- int num;
- struct pci_dev *pdev = wc->vb.pdev;
-
- /* Find position */
- for (x = 0; x < ARRAY_SIZE(ifaces); ++x) {
- if (ifaces[x] == wc) {
- debug_printk(wc, 1, "software init for card %d\n", x);
- break;
- }
- }
+ struct dahdi_chan *chans[32] = {NULL,};
+ struct dahdi_echocan_state *ec[32] = {NULL,};
+ unsigned long flags;
+ int res = 0;
- if (x == ARRAY_SIZE(ifaces))
- return -1;
+ /* We may already be setup properly. */
+ if (wc->span.channels == ((E1 == type) ? 31 : 24))
+ return 0;
+ for (x = 0; x < ((E1 == type) ? 31 : 24); x++) {
+ chans[x] = kzalloc(sizeof(*chans[x]), GFP_KERNEL);
+ ec[x] = kzalloc(sizeof(*ec[x]), GFP_KERNEL);
+ if (!chans[x] || !ec[x])
+ goto error_exit;
+ }
- num = x;
- sprintf(wc->span.name, "WCT1/%d", num);
- snprintf(wc->span.desc, sizeof(wc->span.desc) - 1, "%s Card %d", wc->variety, num);
- wc->ddev->manufacturer = "Digium";
set_span_devicetype(wc);
- wc->ddev->location = kasprintf(GFP_KERNEL, "PCI Bus %02d Slot %02d",
- pdev->bus->number,
- PCI_SLOT(pdev->devfn) + 1);
-
- if (!wc->ddev->location)
- return -ENOMEM;
+ /* Because the interrupt handler is running, we need to atomically
+ * swap the channel arrays. */
+ spin_lock_irqsave(&wc->reglock, flags);
+ _t1_free_channels(wc);
+ memcpy(wc->chans, chans, sizeof(wc->chans));
+ memcpy(wc->ec, ec, sizeof(wc->ec));
+ memset(chans, 0, sizeof(chans));
+ memset(ec, 0, sizeof(ec));
if (type == E1) {
wc->span.channels = 31;
@@ -1849,29 +1855,87 @@ static int t1_software_init(struct t1 *wc, enum linemode type)
wc->span.deflaw = DAHDI_LAW_MULAW;
}
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+ if (!wc->ddev->location)
+ return -ENOMEM;
+
t1_info(wc, "Setting up global serial parameters for %s\n",
(dahdi_is_e1_span(&wc->span) ? "E1" : "T1"));
t4_serial_setup(wc);
- wc->span.chans = wc->chans;
set_bit(DAHDI_FLAGBIT_RBS, &wc->span.flags);
for (x = 0; x < wc->span.channels; x++) {
- sprintf(wc->chans[x]->name, "WCT1/%d/%d", num, x + 1);
+ sprintf(wc->chans[x]->name, "%s/%d", wc->span.name, x + 1);
t1_chan_set_sigcap(&wc->span, x);
wc->chans[x]->pvt = wc;
wc->chans[x]->chanpos = x + 1;
}
check_and_load_vpm(wc);
+ set_span_devicetype(wc);
- wc->span.ops = &t1_span_ops;
- list_add_tail(&wc->span.device_node, &wc->ddev->spans);
- res = dahdi_register_device(wc->ddev, &wc->vb.pdev->dev);
- if (res) {
- t1_info(wc, "Unable to register with DAHDI\n");
+ return 0;
+
+error_exit:
+ for (x = 0; x < ARRAY_SIZE(chans); ++x) {
+ kfree(chans[x]);
+ kfree(ec[x]);
+ }
+ return res;
+}
+
+/**
+ * t1xx_set_linemode - Change the type of span before assignment.
+ * @span: The span to change.
+ * @linemode: Text string for the line mode.
+ *
+ * This function may be called after the dahdi_device is registered but
+ * before the spans are assigned numbers (and are visible to the rest of
+ * DAHDI).
+ *
+ */
+static int t1xxp_set_linemode(struct dahdi_span *span, const char *linemode)
+{
+ int res;
+ struct t1 *wc = container_of(span, struct t1, span);
+
+ /* We may already be set to the requested type. */
+ if (!strcasecmp(span->spantype, linemode))
+ return 0;
+
+ res = t1_wait_for_ready(wc);
+ if (res)
return res;
+
+ /* Stop the processing of the channels since we're going to change
+ * them. */
+ clear_bit(INITIALIZED, &wc->bit_flags);
+ smp_mb__after_clear_bit();
+ del_timer_sync(&wc->timer);
+ flush_workqueue(wc->wq);
+
+ if (!strcasecmp(linemode, "t1")) {
+ dev_info(&wc->vb.pdev->dev,
+ "Changing from E1 to T1 line mode.\n");
+ res = t1_software_init(wc, T1);
+ } else if (!strcasecmp(linemode, "e1")) {
+ dev_info(&wc->vb.pdev->dev,
+ "Changing from T1 to E1 line mode.\n");
+ res = t1_software_init(wc, E1);
+ } else {
+ dev_err(&wc->vb.pdev->dev,
+ "'%s' is an unknown linemode.\n", linemode);
+ res = -EINVAL;
}
+ /* Since we probably reallocated the channels we need to make
+ * sure they are configured before setting INITIALIZED again. */
+ if (!res) {
+ dahdi_init_span(span);
+ set_bit(INITIALIZED, &wc->bit_flags);
+ mod_timer(&wc->timer, jiffies + HZ/5);
+ }
return res;
}
@@ -2574,11 +2638,27 @@ static inline void remove_sysfs_files(struct t1 *wc) { return; }
#endif /* CONFIG_VOICEBUS_SYSFS */
+static const struct dahdi_span_ops t1_span_ops = {
+ .owner = THIS_MODULE,
+ .spanconfig = t1xxp_spanconfig,
+ .chanconfig = t1xxp_chanconfig,
+ .startup = t1xxp_startup,
+ .rbsbits = t1xxp_rbsbits,
+ .maint = t1xxp_maint,
+ .ioctl = t1xxp_ioctl,
+ .set_spantype = t1xxp_set_linemode,
+#ifdef VPM_SUPPORT
+ .enable_hw_preechocan = t1xxp_enable_hw_preechocan,
+ .disable_hw_preechocan = t1xxp_disable_hw_preechocan,
+ .echocan_create = t1xxp_echocan_create,
+ .echocan_name = t1xxp_echocan_name,
+#endif
+};
static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct t1 *wc;
- struct t1_desc *d = (struct t1_desc *) ent->driver_data;
+ const struct t1_desc *d = (struct t1_desc *) ent->driver_data;
unsigned int x;
int res;
unsigned int index = -1;
@@ -2605,19 +2685,18 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
* not support collecting them. */
memset(&wc->span.count, -1, sizeof(wc->span.count));
- wc->not_ready = 1;
-
ifaces[index] = wc;
+ sprintf(wc->span.name, "WCT1/%d", index);
+ snprintf(wc->span.desc, sizeof(wc->span.desc) - 1, "%s Card %d",
+ d->name, index);
+ wc->not_ready = 1;
wc->ledstate = -1;
- wc->ddev = dahdi_create_device();
+ wc->variety = d->name;
+ wc->txident = 1;
spin_lock_init(&wc->reglock);
INIT_LIST_HEAD(&wc->active_cmds);
INIT_LIST_HEAD(&wc->pending_cmds);
-
- wc->variety = d->name;
- wc->txident = 1;
-
# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
wc->timer.function = te12xp_timer;
wc->timer.data = (unsigned long)wc;
@@ -2638,6 +2717,22 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
INIT_WORK(&wc->vpm_check_work, vpm_check_func);
# endif
+ wc->ddev = dahdi_create_device();
+ if (!wc->ddev) {
+ ifaces[index] = NULL;
+ kfree(wc);
+ return -ENOMEM;
+ }
+ wc->ddev->manufacturer = "Digium";
+ wc->ddev->location = kasprintf(GFP_KERNEL, "PCI Bus %02d Slot %02d",
+ pdev->bus->number,
+ PCI_SLOT(pdev->devfn) + 1);
+ if (!wc->ddev->location) {
+ ifaces[index] = NULL;
+ kfree(wc);
+ return -ENOMEM;
+ }
+
#ifdef CONFIG_VOICEBUS_ECREFERENCE
for (x = 0; x < ARRAY_SIZE(wc->ec_reference); ++x) {
/* 256 is used here since it is the largest power of two that
@@ -2669,6 +2764,7 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
wc->wq = create_singlethread_workqueue(wc->name);
if (!wc->wq) {
kfree(wc);
+ ifaces[index] = NULL;
return -ENOMEM;
}
@@ -2682,6 +2778,7 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
if (voicebus_start(&wc->vb)) {
voicebus_release(&wc->vb);
free_wc(wc);
+ ifaces[index] = NULL;
return -EIO;
}
@@ -2689,29 +2786,25 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
if (res) {
voicebus_release(&wc->vb);
free_wc(wc);
+ ifaces[index] = NULL;
return res;
}
- for (x = 0; x < ((E1 == type) ? 31 : 24); x++) {
- wc->chans[x] = kzalloc(sizeof(*wc->chans[x]), GFP_KERNEL);
- if (!wc->chans[x]) {
- free_wc(wc);
- ifaces[index] = NULL;
- return -ENOMEM;
- }
-
- wc->ec[x] = kzalloc(sizeof(*wc->ec[x]), GFP_KERNEL);
- if (!wc->ec[x]) {
- free_wc(wc);
- ifaces[index] = NULL;
- return -ENOMEM;
- }
- }
+ wc->span.chans = wc->chans;
res = t1_software_init(wc, type);
if (res) {
voicebus_release(&wc->vb);
free_wc(wc);
+ ifaces[index] = NULL;
+ return res;
+ }
+
+ wc->span.ops = &t1_span_ops;
+ list_add_tail(&wc->span.device_node, &wc->ddev->spans);
+ res = dahdi_register_device(wc->ddev, &wc->vb.pdev->dev);
+ if (res) {
+ t1_info(wc, "Unable to register with DAHDI\n");
return res;
}
@@ -2721,10 +2814,7 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
t1_info(wc, "Found a %s\n", wc->variety);
voicebus_unlock_latency(&wc->vb);
- /* If there is VPMADT032 module attached to this device, it will
- * signal ready after the channels are configured and ready for use. */
- if (!wc->vpmadt032)
- wc->not_ready--;
+ wc->not_ready--;
return 0;
}