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
This commit is contained in:
parent
f9c52bb271
commit
39c4b93ed6
@ -776,19 +776,31 @@ static void __t1xxp_set_clear(struct t1 *wc)
|
|||||||
t1_info(wc, "Unable to set clear/rbs mode!\n");
|
t1_info(wc, "Unable to set clear/rbs mode!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* _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)
|
||||||
|
{
|
||||||
|
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)
|
static void free_wc(struct t1 *wc)
|
||||||
{
|
{
|
||||||
unsigned int x;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct command *cmd;
|
struct command *cmd;
|
||||||
LIST_HEAD(list);
|
LIST_HEAD(list);
|
||||||
|
|
||||||
for (x = 0; x < ARRAY_SIZE(wc->chans); x++) {
|
|
||||||
kfree(wc->chans[x]);
|
|
||||||
kfree(wc->ec[x]);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock_irqsave(&wc->reglock, flags);
|
spin_lock_irqsave(&wc->reglock, flags);
|
||||||
|
_t1_free_channels(wc);
|
||||||
list_splice_init(&wc->active_cmds, &list);
|
list_splice_init(&wc->active_cmds, &list);
|
||||||
list_splice_init(&wc->pending_cmds, &list);
|
list_splice_init(&wc->pending_cmds, &list);
|
||||||
spin_unlock_irqrestore(&wc->reglock, flags);
|
spin_unlock_irqrestore(&wc->reglock, flags);
|
||||||
@ -1589,6 +1601,7 @@ static int vpm_start_load(struct t1 *wc)
|
|||||||
work->wc = wc;
|
work->wc = wc;
|
||||||
|
|
||||||
queue_work(wc->wq, &work->work);
|
queue_work(wc->wq, &work->work);
|
||||||
|
wc->not_ready++;
|
||||||
return 0;
|
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... */
|
* done setting it up here, an hour should cover it... */
|
||||||
wc->vpm_check = jiffies + HZ*3600;
|
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);
|
vpmadt = vpmadt032_alloc(&options);
|
||||||
if (!vpmadt)
|
if (!vpmadt)
|
||||||
return;
|
return;
|
||||||
@ -1778,22 +1802,6 @@ static void t1xxp_disable_hw_preechocan(struct dahdi_chan *chan)
|
|||||||
vpmoct_preecho_disable(wc->vpmoct, chan->chanpos - 1);
|
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.
|
* t1_software_init - Initialize the board for the given type.
|
||||||
* @wc: The board to initialize.
|
* @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)
|
static int t1_software_init(struct t1 *wc, enum linemode type)
|
||||||
{
|
{
|
||||||
int x;
|
int x;
|
||||||
int res;
|
struct dahdi_chan *chans[32] = {NULL,};
|
||||||
int num;
|
struct dahdi_echocan_state *ec[32] = {NULL,};
|
||||||
struct pci_dev *pdev = wc->vb.pdev;
|
unsigned long flags;
|
||||||
|
int res = 0;
|
||||||
|
|
||||||
/* Find position */
|
/* We may already be setup properly. */
|
||||||
for (x = 0; x < ARRAY_SIZE(ifaces); ++x) {
|
if (wc->span.channels == ((E1 == type) ? 31 : 24))
|
||||||
if (ifaces[x] == wc) {
|
return 0;
|
||||||
debug_printk(wc, 1, "software init for card %d\n", x);
|
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x == ARRAY_SIZE(ifaces))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
|
|
||||||
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);
|
set_span_devicetype(wc);
|
||||||
|
|
||||||
wc->ddev->location = kasprintf(GFP_KERNEL, "PCI Bus %02d Slot %02d",
|
/* Because the interrupt handler is running, we need to atomically
|
||||||
pdev->bus->number,
|
* swap the channel arrays. */
|
||||||
PCI_SLOT(pdev->devfn) + 1);
|
spin_lock_irqsave(&wc->reglock, flags);
|
||||||
|
_t1_free_channels(wc);
|
||||||
if (!wc->ddev->location)
|
memcpy(wc->chans, chans, sizeof(wc->chans));
|
||||||
return -ENOMEM;
|
memcpy(wc->ec, ec, sizeof(wc->ec));
|
||||||
|
memset(chans, 0, sizeof(chans));
|
||||||
|
memset(ec, 0, sizeof(ec));
|
||||||
|
|
||||||
if (type == E1) {
|
if (type == E1) {
|
||||||
wc->span.channels = 31;
|
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;
|
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",
|
t1_info(wc, "Setting up global serial parameters for %s\n",
|
||||||
(dahdi_is_e1_span(&wc->span) ? "E1" : "T1"));
|
(dahdi_is_e1_span(&wc->span) ? "E1" : "T1"));
|
||||||
|
|
||||||
t4_serial_setup(wc);
|
t4_serial_setup(wc);
|
||||||
wc->span.chans = wc->chans;
|
|
||||||
set_bit(DAHDI_FLAGBIT_RBS, &wc->span.flags);
|
set_bit(DAHDI_FLAGBIT_RBS, &wc->span.flags);
|
||||||
for (x = 0; x < wc->span.channels; x++) {
|
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);
|
t1_chan_set_sigcap(&wc->span, x);
|
||||||
wc->chans[x]->pvt = wc;
|
wc->chans[x]->pvt = wc;
|
||||||
wc->chans[x]->chanpos = x + 1;
|
wc->chans[x]->chanpos = x + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_and_load_vpm(wc);
|
check_and_load_vpm(wc);
|
||||||
|
set_span_devicetype(wc);
|
||||||
|
|
||||||
wc->span.ops = &t1_span_ops;
|
return 0;
|
||||||
list_add_tail(&wc->span.device_node, &wc->ddev->spans);
|
|
||||||
res = dahdi_register_device(wc->ddev, &wc->vb.pdev->dev);
|
error_exit:
|
||||||
if (res) {
|
for (x = 0; x < ARRAY_SIZE(chans); ++x) {
|
||||||
t1_info(wc, "Unable to register with DAHDI\n");
|
kfree(chans[x]);
|
||||||
|
kfree(ec[x]);
|
||||||
|
}
|
||||||
return res;
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2574,11 +2638,27 @@ static inline void remove_sysfs_files(struct t1 *wc) { return; }
|
|||||||
|
|
||||||
#endif /* CONFIG_VOICEBUS_SYSFS */
|
#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)
|
static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
struct t1 *wc;
|
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;
|
unsigned int x;
|
||||||
int res;
|
int res;
|
||||||
unsigned int index = -1;
|
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. */
|
* not support collecting them. */
|
||||||
memset(&wc->span.count, -1, sizeof(wc->span.count));
|
memset(&wc->span.count, -1, sizeof(wc->span.count));
|
||||||
|
|
||||||
wc->not_ready = 1;
|
|
||||||
|
|
||||||
ifaces[index] = wc;
|
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->ledstate = -1;
|
||||||
wc->ddev = dahdi_create_device();
|
wc->variety = d->name;
|
||||||
|
wc->txident = 1;
|
||||||
spin_lock_init(&wc->reglock);
|
spin_lock_init(&wc->reglock);
|
||||||
INIT_LIST_HEAD(&wc->active_cmds);
|
INIT_LIST_HEAD(&wc->active_cmds);
|
||||||
INIT_LIST_HEAD(&wc->pending_cmds);
|
INIT_LIST_HEAD(&wc->pending_cmds);
|
||||||
|
|
||||||
wc->variety = d->name;
|
|
||||||
wc->txident = 1;
|
|
||||||
|
|
||||||
# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
|
# if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
|
||||||
wc->timer.function = te12xp_timer;
|
wc->timer.function = te12xp_timer;
|
||||||
wc->timer.data = (unsigned long)wc;
|
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);
|
INIT_WORK(&wc->vpm_check_work, vpm_check_func);
|
||||||
# endif
|
# 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
|
#ifdef CONFIG_VOICEBUS_ECREFERENCE
|
||||||
for (x = 0; x < ARRAY_SIZE(wc->ec_reference); ++x) {
|
for (x = 0; x < ARRAY_SIZE(wc->ec_reference); ++x) {
|
||||||
/* 256 is used here since it is the largest power of two that
|
/* 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);
|
wc->wq = create_singlethread_workqueue(wc->name);
|
||||||
if (!wc->wq) {
|
if (!wc->wq) {
|
||||||
kfree(wc);
|
kfree(wc);
|
||||||
|
ifaces[index] = NULL;
|
||||||
return -ENOMEM;
|
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)) {
|
if (voicebus_start(&wc->vb)) {
|
||||||
voicebus_release(&wc->vb);
|
voicebus_release(&wc->vb);
|
||||||
free_wc(wc);
|
free_wc(wc);
|
||||||
|
ifaces[index] = NULL;
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2689,29 +2786,25 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
|
|||||||
if (res) {
|
if (res) {
|
||||||
voicebus_release(&wc->vb);
|
voicebus_release(&wc->vb);
|
||||||
free_wc(wc);
|
free_wc(wc);
|
||||||
|
ifaces[index] = NULL;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (x = 0; x < ((E1 == type) ? 31 : 24); x++) {
|
wc->span.chans = wc->chans;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res = t1_software_init(wc, type);
|
res = t1_software_init(wc, type);
|
||||||
if (res) {
|
if (res) {
|
||||||
voicebus_release(&wc->vb);
|
voicebus_release(&wc->vb);
|
||||||
free_wc(wc);
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2721,9 +2814,6 @@ static int __devinit te12xp_init_one(struct pci_dev *pdev, const struct pci_devi
|
|||||||
t1_info(wc, "Found a %s\n", wc->variety);
|
t1_info(wc, "Found a %s\n", wc->variety);
|
||||||
voicebus_unlock_latency(&wc->vb);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user