wct4xxp: Allow linemode (T1/E1/J1) 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
wct4xxp 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@10279 a0bf4364-ded3-4de4-8d8a-66a801d63aff
This commit is contained in:
Shaun Ruffell 2011-10-26 19:05:24 +00:00 committed by Tzafrir Cohen
parent 39c4b93ed6
commit d91b3c53d3

View File

@ -27,7 +27,6 @@
* Free Software Foundation. See the LICENSE file included with
* this program for more details.
*/
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/module.h>
@ -273,11 +272,13 @@ static struct devtype wct210 = { "Wildcard TE210P ", FLAG_2NDGEN | FLAG_2PORT };
struct t4;
enum linemode {T1, E1, J1};
struct t4_span {
struct t4 *owner;
u32 *writechunk; /* Double-word aligned write memory */
u32 *readchunk; /* Double-word aligned read memory */
enum {T1, E1, J1} linemode;
enum linemode linemode;
int sync;
int alarmtimer;
int notclear;
@ -323,7 +324,6 @@ struct t4 {
struct pci_dev *dev; /* Pointer to PCI device */
unsigned int intcount;
int num; /* Which card we are */
int t1e1; /* T1/E1 select pins */
int syncsrc; /* active sync source */
struct dahdi_device *ddev;
struct t4_span *tspans[4]; /* Individual spans */
@ -336,6 +336,7 @@ struct t4 {
int order; /* Order */
const struct devtype *devtype;
unsigned int falc31:1; /* are we falc v3.1 (atomic not necessary) */
unsigned int t1e1:4; /* T1 / E1 select pins */
int ledreg; /* LED Register */
unsigned int gpio;
unsigned int gpioctl;
@ -1754,6 +1755,323 @@ static void setup_chunks(struct t4 *wc, int which)
}
}
/**
* t4_serial_setup - Setup serial parameters and system interface.
* @wc: The card to configure.
*
*/
static void t4_serial_setup(struct t4 *wc)
{
unsigned long flags;
unsigned int unit;
if (debug) {
dev_info(&wc->dev->dev,
"TE%dXXP: Setting up global serial parameters\n",
wc->numspans);
}
spin_lock_irqsave(&wc->reglock, flags);
/* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from
* channel 0 */
/* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from
* channel 0 */
__t4_framer_out(wc, 0, 0x85, 0xe0);
/* IPC: Interrupt push/pull active low */
__t4_framer_out(wc, 0, 0x08, 0x01);
/* Global clocks (8.192 Mhz CLK) */
__t4_framer_out(wc, 0, 0x92, 0x00);
__t4_framer_out(wc, 0, 0x93, 0x18);
__t4_framer_out(wc, 0, 0x94, 0xfb);
__t4_framer_out(wc, 0, 0x95, 0x0b);
__t4_framer_out(wc, 0, 0x96, 0x00);
__t4_framer_out(wc, 0, 0x97, 0x0b);
__t4_framer_out(wc, 0, 0x98, 0xdb);
__t4_framer_out(wc, 0, 0x99, 0xdf);
spin_unlock_irqrestore(&wc->reglock, flags);
for (unit = 0; unit < PORTS_PER_FRAMER; ++unit) {
spin_lock_irqsave(&wc->reglock, flags);
/* Configure interrupts */
/* GCR: Interrupt on Activation/Deactivation of each */
__t4_framer_out(wc, unit, FRMR_GCR, 0x00);
/* Configure system interface */
/* SIC1: 8.192 Mhz clock/bus, double buffer receive /
* transmit, byte interleaved */
__t4_framer_out(wc, unit, FRMR_SIC1, 0xc2);
/* SIC2: No FFS, no center receive eliastic buffer, phase */
__t4_framer_out(wc, unit, FRMR_SIC2, 0x20 | (unit << 1));
/* SIC3: Edges for capture */
__t4_framer_out(wc, unit, FRMR_SIC3, 0x04);
/* CMR2: We provide sync and clock for tx and rx. */
__t4_framer_out(wc, unit, FRMR_CMR2, 0x00);
if (!has_e1_span(wc)) { /* T1/J1 mode */
__t4_framer_out(wc, unit, FRMR_XC0, 0x03);
__t4_framer_out(wc, unit, FRMR_XC1, 0x84);
if (J1 == wc->tspans[unit]->linemode)
__t4_framer_out(wc, unit, FRMR_RC0, 0x83);
else
__t4_framer_out(wc, unit, FRMR_RC0, 0x03);
__t4_framer_out(wc, unit, FRMR_RC1, 0x84);
} else { /* E1 mode */
__t4_framer_out(wc, unit, FRMR_XC0, 0x00);
__t4_framer_out(wc, unit, FRMR_XC1, 0x04);
__t4_framer_out(wc, unit, FRMR_RC0, 0x04);
__t4_framer_out(wc, unit, FRMR_RC1, 0x04);
}
/* Configure ports */
/* PC1: SPYR/SPYX input on RPA/XPA */
__t4_framer_out(wc, unit, 0x80, 0x00);
/* PC2: RMFB/XSIG output/input on RPB/XPB */
/* PC3: Some unused stuff */
/* PC4: Some more unused stuff */
if (wc->falc31) {
__t4_framer_out(wc, unit, 0x81, 0xBB);
__t4_framer_out(wc, unit, 0x82, 0xBB);
__t4_framer_out(wc, unit, 0x83, 0xBB);
} else {
__t4_framer_out(wc, unit, 0x81, 0x22);
__t4_framer_out(wc, unit, 0x82, 0x65);
__t4_framer_out(wc, unit, 0x83, 0x35);
}
/* PC5: XMFS active low, SCLKR is input, RCLK is output */
__t4_framer_out(wc, unit, 0x84, 0x01);
if (debug & DEBUG_MAIN) {
dev_notice(&wc->dev->dev,
"Successfully initialized serial bus "
"for unit %d\n", unit);
}
spin_unlock_irqrestore(&wc->reglock, flags);
}
}
/**
* t4_span_assigned - Called when the span is assigned by DAHDI.
* @span: Span that has been assigned.
*
* When this function is called, the span has a valid spanno and all the
* channels on the span have valid channel numbers assigned.
*
* This function is necessary because a device may be registered, and
* then user space may then later decide to assign span numbers and the
* channel numbers.
*
*/
static void t4_span_assigned(struct dahdi_span *span)
{
struct t4_span *tspan = container_of(span, struct t4_span, span);
struct t4 *wc = tspan->owner;
struct dahdi_span *pos;
unsigned int unassigned_spans = 0;
/* We use this to make sure all the spans are assigned before
* running the serial setup. */
list_for_each_entry(pos, &wc->ddev->spans, device_node) {
if (!test_bit(DAHDI_FLAGBIT_REGISTERED, &span->flags))
++unassigned_spans;
}
if (0 == unassigned_spans)
t4_serial_setup(wc);
}
static void free_wc(struct t4 *wc)
{
unsigned int x, y;
for (x = 0; x < ARRAY_SIZE(wc->tspans); x++) {
if (!wc->tspans[x])
continue;
for (y = 0; y < ARRAY_SIZE(wc->tspans[x]->chans); y++) {
kfree(wc->tspans[x]->chans[y]);
kfree(wc->tspans[x]->ec[y]);
}
kfree(wc->tspans[x]);
}
kfree(wc->ddev->devicetype);
kfree(wc->ddev->location);
dahdi_free_device(wc->ddev);
kfree(wc);
}
/**
* t4_alloc_channels - Allocate the channels on a span.
* @wc: The board we're allocating for.
* @ts: The span we're allocating for.
* @linemode: Which mode (T1/E1/J1) to use for this span.
*
* This function must only be called before the span is assigned it's
* possible for user processes to have an open reference to the
* channels.
*
*/
static int t4_alloc_channels(struct t4 *wc, struct t4_span *ts,
enum linemode linemode)
{
int i;
if (test_bit(DAHDI_FLAGBIT_REGISTERED, &ts->span.flags)) {
dev_dbg(&wc->dev->dev,
"Cannot allocate channels on a span that is already "
"assigned.\n");
return -EINVAL;
}
/* Cleanup any previously allocated channels. */
for (i = 0; i < ARRAY_SIZE(ts->chans); ++i) {
kfree(ts->chans[i]);
kfree(ts->ec[i]);
}
ts->linemode = linemode;
for (i = 0; i < ((E1 == ts->linemode) ? 31 : 24); i++) {
struct dahdi_chan *chan;
struct dahdi_echocan_state *ec;
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan) {
free_wc(wc);
return -ENOMEM;
}
ts->chans[i] = chan;
ec = kzalloc(sizeof(*ec), GFP_KERNEL);
if (!ec) {
free_wc(wc);
return -ENOMEM;
}
ts->ec[i] = ec;
}
return 0;
}
static void t4_init_one_span(struct t4 *wc, struct t4_span *ts)
{
unsigned long flags;
unsigned int reg;
int i;
snprintf(ts->span.name, sizeof(ts->span.name) - 1,
"TE%d/%d/%d", wc->numspans, wc->num, ts->span.offset + 1);
snprintf(ts->span.desc, sizeof(ts->span.desc) - 1,
"T%dXXP (PCI) Card %d Span %d", wc->numspans, wc->num,
ts->span.offset + 1);
switch (ts->linemode) {
case T1:
ts->span.spantype = "T1";
break;
case E1:
ts->span.spantype = "E1";
break;
case J1:
ts->span.spantype = "J1";
break;
}
/* HDLC Specific init */
ts->sigchan = NULL;
ts->sigmode = sigmode;
ts->sigactive = 0;
if (E1 != ts->linemode) {
ts->span.channels = 24;
ts->span.deflaw = DAHDI_LAW_MULAW;
ts->span.linecompat = DAHDI_CONFIG_AMI |
DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 |
DAHDI_CONFIG_ESF;
} else {
ts->span.channels = 31;
ts->span.deflaw = DAHDI_LAW_ALAW;
ts->span.linecompat = DAHDI_CONFIG_AMI |
DAHDI_CONFIG_HDB3 | DAHDI_CONFIG_CCS |
DAHDI_CONFIG_CRC4;
}
ts->span.chans = ts->chans;
ts->span.flags = DAHDI_FLAG_RBS;
for (i = 0; i < ts->span.channels; i++) {
struct dahdi_chan *const chan = ts->chans[i];
chan->pvt = wc;
snprintf(chan->name, sizeof(chan->name) - 1,
"%s/%d", ts->span.name, i + 1);
t4_chan_set_sigcap(&ts->span, i);
chan->chanpos = i + 1;
}
/* Enable 1sec timer interrupt */
spin_lock_irqsave(&wc->reglock, flags);
reg = __t4_framer_in(wc, ts->span.offset, FMR1_T);
__t4_framer_out(wc, ts->span.offset, FMR1_T, (reg | FMR1_ECM));
/* Enable Errored Second interrupt */
__t4_framer_out(wc, ts->span.offset, ESM, 0);
spin_unlock_irqrestore(&wc->reglock, flags);
t4_reset_counters(&ts->span);
}
/**
* t4_set_linemode - Allows user space to change the linemode before spans are assigned.
* @span: span on which to change the linemode.
* @linemode: Textual description of the new linemode.
*
* This callback is used to override the E1/T1 mode jumper settings and set
* the linemode on for each span. Called when the "spantype" attribute
* is written in sysfs under the dahdi_device.
*
*/
static int t4_set_linemode(struct dahdi_span *span, const char *linemode)
{
struct t4_span *ts = container_of(span, struct t4_span, span);
struct t4 *wc = ts->owner;
int res = 0;
enum linemode mode;
dev_dbg(&wc->dev->dev, "Setting '%s' to '%s'\n", span->name, linemode);
if (!strcasecmp(span->spantype, linemode))
return 0;
if (!strcasecmp(linemode, "t1")) {
dev_info(&wc->dev->dev,
"Changing from %s to T1 line mode.\n", span->spantype);
mode = T1;
} else if (!strcasecmp(linemode, "e1")) {
dev_info(&wc->dev->dev,
"Changing from %s to E1 line mode.\n", span->spantype);
mode = E1;
} else if (!strcasecmp(linemode, "j1")) {
dev_info(&wc->dev->dev,
"Changing from %s to J1 line mode.\n", span->spantype);
mode = J1;
} else {
dev_err(&wc->dev->dev,
"'%s' is an unknown linemode.\n", linemode);
res = -EINVAL;
}
if (!res) {
t4_alloc_channels(wc, ts, mode);
t4_init_one_span(wc, ts);
dahdi_init_span(span);
}
return res;
}
static const struct dahdi_span_ops t4_gen1_span_ops = {
.owner = THIS_MODULE,
.spanconfig = t4_spanconfig,
@ -1766,6 +2084,8 @@ static const struct dahdi_span_ops t4_gen1_span_ops = {
.close = t4_close,
.ioctl = t4_ioctl,
.hdlc_hard_xmit = t4_hdlc_hard_xmit,
.assigned = t4_span_assigned,
.set_spantype = t4_set_linemode,
};
static const struct dahdi_span_ops t4_gen2_span_ops = {
@ -1781,12 +2101,24 @@ static const struct dahdi_span_ops t4_gen2_span_ops = {
.ioctl = t4_ioctl,
.hdlc_hard_xmit = t4_hdlc_hard_xmit,
.dacs = t4_dacs,
.assigned = t4_span_assigned,
.set_spantype = t4_set_linemode,
#ifdef VPM_SUPPORT
.echocan_create = t4_echocan_create,
.echocan_name = t4_echocan_name,
#endif
};
/**
* init_spans - Do first initialization on all the spans
* @wc: Card to initialize the spans on.
*
* This function is called *before* the dahdi_device is first registered
* with the system. What happens in t4_init_one_span can happen between
* when the device is registered and when the spans are assigned via
* sysfs (or automatically).
*
*/
static void init_spans(struct t4 *wc)
{
int x, y;
@ -1875,104 +2207,6 @@ static void init_spans(struct t4 *wc)
wc->lastindex = 0;
}
/**
* t4_serial_setup - Setup serial parameters and system interface.
* @wc: The card to configure.
*
*/
static void t4_serial_setup(struct t4 *wc)
{
unsigned long flags;
unsigned int unit;
if (debug) {
dev_info(&wc->dev->dev,
"TE%dXXP: Setting up global serial parameters\n",
wc->numspans);
}
spin_lock_irqsave(&wc->reglock, flags);
/* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from
* channel 0 */
__t4_framer_out(wc, 0, 0x85, 0xe0); /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */
/* IPC: Interrupt push/pull active low */
__t4_framer_out(wc, 0, 0x08, 0x01);
/* Global clocks (8.192 Mhz CLK) */
__t4_framer_out(wc, 0, 0x92, 0x00);
__t4_framer_out(wc, 0, 0x93, 0x18);
__t4_framer_out(wc, 0, 0x94, 0xfb);
__t4_framer_out(wc, 0, 0x95, 0x0b);
__t4_framer_out(wc, 0, 0x96, 0x00);
__t4_framer_out(wc, 0, 0x97, 0x0b);
__t4_framer_out(wc, 0, 0x98, 0xdb);
__t4_framer_out(wc, 0, 0x99, 0xdf);
spin_unlock_irqrestore(&wc->reglock, flags);
for (unit = 0; unit < PORTS_PER_FRAMER; ++unit) {
spin_lock_irqsave(&wc->reglock, flags);
/* Configure interrupts */
/* GCR: Interrupt on Activation/Deactivation of each */
__t4_framer_out(wc, unit, FRMR_GCR, 0x00);
/* Configure system interface */
/* SIC1: 8.192 Mhz clock/bus, double buffer receive /
* transmit, byte interleaved */
__t4_framer_out(wc, unit, FRMR_SIC1, 0xc2);
/* SIC2: No FFS, no center receive eliastic buffer, phase */
__t4_framer_out(wc, unit, FRMR_SIC2, 0x20 | (unit << 1));
/* SIC3: Edges for capture */
__t4_framer_out(wc, unit, FRMR_SIC3, 0x04);
/* CMR2: We provide sync and clock for tx and rx. */
__t4_framer_out(wc, unit, FRMR_CMR2, 0x00);
if (!has_e1_span(wc)) { /* T1/J1 mode */
__t4_framer_out(wc, unit, FRMR_XC0, 0x03);
__t4_framer_out(wc, unit, FRMR_XC1, 0x84);
if (J1 == wc->tspans[unit]->linemode)
__t4_framer_out(wc, unit, FRMR_RC0, 0x83);
else
__t4_framer_out(wc, unit, FRMR_RC0, 0x03);
__t4_framer_out(wc, unit, FRMR_RC1, 0x84);
} else { /* E1 mode */
__t4_framer_out(wc, unit, FRMR_XC0, 0x00);
__t4_framer_out(wc, unit, FRMR_XC1, 0x04);
__t4_framer_out(wc, unit, FRMR_RC0, 0x04);
__t4_framer_out(wc, unit, FRMR_RC1, 0x04);
}
/* Configure ports */
/* PC1: SPYR/SPYX input on RPA/XPA */
__t4_framer_out(wc, unit, 0x80, 0x00);
/* PC2: RMFB/XSIG output/input on RPB/XPB */
/* PC3: Some unused stuff */
/* PC4: Some more unused stuff */
if (wc->falc31) {
__t4_framer_out(wc, unit, 0x81, 0xBB);
__t4_framer_out(wc, unit, 0x82, 0xBB);
__t4_framer_out(wc, unit, 0x83, 0xBB);
} else {
__t4_framer_out(wc, unit, 0x81, 0x22);
__t4_framer_out(wc, unit, 0x82, 0x65);
__t4_framer_out(wc, unit, 0x83, 0x35);
}
/* PC5: XMFS active low, SCLKR is input, RCLK is output */
__t4_framer_out(wc, unit, 0x84, 0x01);
if (debug & DEBUG_MAIN) {
dev_notice(&wc->dev->dev,
"Successfully initialized serial bus "
"for unit %d\n", unit);
}
spin_unlock_irqrestore(&wc->reglock, flags);
}
}
static int syncsrc = 0;
static int syncnum = 0 /* -1 */;
static int syncspan = 0;
@ -4042,8 +4276,6 @@ static int __devinit t4_launch(struct t4 *wc)
wc->order);
}
t4_serial_setup(wc);
wc->ddev->manufacturer = "Digium";
if (!ignore_rotary && (1 == order_index[wc->order])) {
wc->ddev->location = kasprintf(GFP_KERNEL,
@ -4079,26 +4311,6 @@ static int __devinit t4_launch(struct t4 *wc)
return 0;
}
static void free_wc(struct t4 *wc)
{
unsigned int x, y;
for (x = 0; x < ARRAY_SIZE(wc->tspans); x++) {
if (!wc->tspans[x])
continue;
for (y = 0; y < ARRAY_SIZE(wc->tspans[x]->chans); y++) {
kfree(wc->tspans[x]->chans[y]);
kfree(wc->tspans[x]->ec[y]);
}
kfree(wc->tspans[x]);
}
kfree(wc->ddev->devicetype);
kfree(wc->ddev->location);
dahdi_free_device(wc->ddev);
kfree(wc);
}
/**
* wct4xxp_sort_cards - Sort the cards in card array by rotary switch settings.
*
@ -4130,7 +4342,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
{
int res;
struct t4 *wc;
unsigned int x, f;
unsigned int x;
int init_latency;
if (pci_enable_device(pdev)) {
@ -4229,6 +4441,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
/* Allocate pieces we need here */
for (x = 0; x < PORTS_PER_FRAMER; x++) {
struct t4_span *ts;
enum linemode linemode;
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
if (!ts) {
@ -4237,34 +4450,12 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i
}
wc->tspans[x] = ts;
if (wc->t1e1 & (1 << x))
ts->linemode = E1;
else
ts->linemode = (j1mode) ? J1 : T1;
for (f = 0; f < ((E1 == ts->linemode) ? 31 : 24); f++) {
struct dahdi_chan *chan;
struct dahdi_echocan_state *ec;
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan) {
free_wc(wc);
return -ENOMEM;
}
ts->chans[f] = chan;
ec = kzalloc(sizeof(*ec), GFP_KERNEL);
if (!ec) {
free_wc(wc);
return -ENOMEM;
}
ts->ec[f] = ec;
}
#ifdef ENABLE_WORKQUEUES
INIT_WORK(&ts->swork, workq_handlespan, ts);
#endif
ts->spanflags |= wc->devtype->flags;
linemode = (wc->t1e1 & (1 << x)) ? E1 : ((j1mode) ? J1 : T1);
t4_alloc_channels(wc, wc->tspans[x], linemode);
}
/* Continue hardware intiialization */
@ -4466,19 +4657,28 @@ static int __init t4_init(void)
{
int i;
int res;
if (-1 != t1e1override) {
pr_info("'t1e1override' module parameter is deprecated. "
"Please use 'default_linemode' instead.\n");
}
res = dahdi_pci_module(&t4_driver);
if (res)
return -ENODEV;
/* initialize cards since we have all of them */
/* warn for missing zero and duplicate numbers */
/* If we're ignoring the rotary switch settings, then we've already
* registered in the context of .probe */
if (!ignore_rotary) {
/* Initialize cards since we have all of them. Warn for
* missing zero and duplicate numbers. */
if (cards[0] && cards[0]->order != 0) {
printk(KERN_NOTICE "wct4xxp: Ident of first card is not zero (%d)\n",
cards[0]->order);
}
/* If we're ignoring the rotary switch settings, then we've already
* registered in the context of .probe */
if (!ignore_rotary) {
for (i = 0; cards[i]; i++) {
/* warn the user of duplicate ident values it is
* probably unintended */
@ -4504,7 +4704,6 @@ static void __exit t4_cleanup(void)
pci_unregister_driver(&t4_driver);
}
MODULE_AUTHOR("Digium Incorporated <support@digium.com>");
MODULE_DESCRIPTION("Wildcard Dual-/Quad-port Digital Card Driver");
MODULE_ALIAS("wct2xxp");