summaryrefslogtreecommitdiffstats
path: root/drivers/isdn/hardware/mISDN/avmfritz.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isdn/hardware/mISDN/avmfritz.c')
-rw-r--r--drivers/isdn/hardware/mISDN/avmfritz.c228
1 files changed, 124 insertions, 104 deletions
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
index c0b8c960ee3..c08fc605e56 100644
--- a/drivers/isdn/hardware/mISDN/avmfritz.c
+++ b/drivers/isdn/hardware/mISDN/avmfritz.c
@@ -30,7 +30,7 @@
#include "ipac.h"
-#define AVMFRITZ_REV "2.1"
+#define AVMFRITZ_REV "2.3"
static int AVM_cnt;
static int debug;
@@ -69,6 +69,7 @@ enum {
#define HDLC_MODE_TRANS 0x02
#define HDLC_MODE_CCR_7 0x04
#define HDLC_MODE_CCR_16 0x08
+#define HDLC_FIFO_SIZE_128 0x20
#define HDLC_MODE_TESTLOOP 0x80
#define HDLC_INT_XPR 0x80
@@ -80,13 +81,16 @@ enum {
#define HDLC_STAT_RDO 0x10
#define HDLC_STAT_CRCVFRRAB 0x0E
#define HDLC_STAT_CRCVFR 0x06
-#define HDLC_STAT_RML_MASK 0x3f00
+#define HDLC_STAT_RML_MASK_V1 0x3f00
+#define HDLC_STAT_RML_MASK_V2 0x7f00
#define HDLC_CMD_XRS 0x80
#define HDLC_CMD_XME 0x01
#define HDLC_CMD_RRS 0x20
#define HDLC_CMD_XML_MASK 0x3f00
-#define HDLC_FIFO_SIZE 32
+
+#define HDLC_FIFO_SIZE_V1 32
+#define HDLC_FIFO_SIZE_V2 128
/* Fritz PCI v2.0 */
@@ -346,11 +350,14 @@ modehdlc(struct bchannel *bch, int protocol)
{
struct fritzcard *fc = bch->hw;
struct hdlc_hw *hdlc;
+ u8 mode;
hdlc = &fc->hdlc[(bch->nr - 1) & 1];
pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name,
'@' + bch->nr, bch->state, protocol, bch->nr);
hdlc->ctrl.ctrl = 0;
+ mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0;
+
switch (protocol) {
case -1: /* used for init */
bch->state = -1;
@@ -358,7 +365,7 @@ modehdlc(struct bchannel *bch, int protocol)
if (bch->state == ISDN_P_NONE)
break;
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
- hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+ hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS;
write_ctrl(bch, 5);
bch->state = ISDN_P_NONE;
test_and_clear_bit(FLG_HDLC, &bch->Flags);
@@ -367,7 +374,7 @@ modehdlc(struct bchannel *bch, int protocol)
case ISDN_P_B_RAW:
bch->state = protocol;
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
- hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+ hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS;
write_ctrl(bch, 5);
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
write_ctrl(bch, 1);
@@ -377,7 +384,7 @@ modehdlc(struct bchannel *bch, int protocol)
case ISDN_P_B_HDLC:
bch->state = protocol;
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
- hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+ hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG;
write_ctrl(bch, 5);
hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
write_ctrl(bch, 1);
@@ -397,39 +404,40 @@ hdlc_empty_fifo(struct bchannel *bch, int count)
u32 *ptr;
u8 *p;
u32 val, addr;
- int cnt = 0;
+ int cnt;
struct fritzcard *fc = bch->hw;
pr_debug("%s: %s %d\n", fc->name, __func__, count);
- if (!bch->rx_skb) {
- bch->rx_skb = mI_alloc_skb(bch->maxlen, GFP_ATOMIC);
- if (!bch->rx_skb) {
- pr_info("%s: B receive out of memory\n",
- fc->name);
+ if (test_bit(FLG_RX_OFF, &bch->Flags)) {
+ p = NULL;
+ bch->dropcnt += count;
+ } else {
+ cnt = bchannel_get_rxbuf(bch, count);
+ if (cnt < 0) {
+ pr_warning("%s.B%d: No bufferspace for %d bytes\n",
+ fc->name, bch->nr, count);
return;
}
+ p = skb_put(bch->rx_skb, count);
}
- if ((bch->rx_skb->len + count) > bch->maxlen) {
- pr_debug("%s: overrun %d\n", fc->name,
- bch->rx_skb->len + count);
- return;
- }
- p = skb_put(bch->rx_skb, count);
ptr = (u32 *)p;
- if (AVM_FRITZ_PCIV2 == fc->type)
+ if (fc->type == AVM_FRITZ_PCIV2)
addr = fc->addr + (bch->nr == 2 ?
AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
else {
addr = fc->addr + CHIP_WINDOW;
outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr);
}
+ cnt = 0;
while (cnt < count) {
val = le32_to_cpu(inl(addr));
- put_unaligned(val, ptr);
- ptr++;
+ if (p) {
+ put_unaligned(val, ptr);
+ ptr++;
+ }
cnt += 4;
}
- if (debug & DEBUG_HW_BFIFO) {
+ if (p && (debug & DEBUG_HW_BFIFO)) {
snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ",
bch->nr, fc->name, count);
print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
@@ -441,30 +449,43 @@ hdlc_fill_fifo(struct bchannel *bch)
{
struct fritzcard *fc = bch->hw;
struct hdlc_hw *hdlc;
- int count, cnt = 0;
+ int count, fs, cnt = 0, idx, fillempty = 0;
u8 *p;
u32 *ptr, val, addr;
- hdlc = &fc->hdlc[(bch->nr - 1) & 1];
- if (!bch->tx_skb)
- return;
- count = bch->tx_skb->len - bch->tx_idx;
- if (count <= 0)
- return;
- p = bch->tx_skb->data + bch->tx_idx;
+ idx = (bch->nr - 1) & 1;
+ hdlc = &fc->hdlc[idx];
+ fs = (fc->type == AVM_FRITZ_PCIV2) ?
+ HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1;
+ if (!bch->tx_skb) {
+ if (!test_bit(FLG_TX_EMPTY, &bch->Flags))
+ return;
+ count = fs;
+ p = bch->fill;
+ fillempty = 1;
+ } else {
+ count = bch->tx_skb->len - bch->tx_idx;
+ if (count <= 0)
+ return;
+ p = bch->tx_skb->data + bch->tx_idx;
+ }
hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
- if (count > HDLC_FIFO_SIZE) {
- count = HDLC_FIFO_SIZE;
+ if (count > fs) {
+ count = fs;
} else {
if (test_bit(FLG_HDLC, &bch->Flags))
hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
}
- pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count,
- bch->tx_idx, bch->tx_skb->len);
ptr = (u32 *)p;
- bch->tx_idx += count;
- hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count);
- if (AVM_FRITZ_PCIV2 == fc->type) {
+ if (fillempty) {
+ pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count,
+ bch->tx_idx, bch->tx_skb->len);
+ bch->tx_idx += count;
+ } else {
+ pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count);
+ }
+ hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count);
+ if (fc->type == AVM_FRITZ_PCIV2) {
__write_ctrl_pciv2(fc, hdlc, bch->nr);
addr = fc->addr + (bch->nr == 2 ?
AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
@@ -472,13 +493,21 @@ hdlc_fill_fifo(struct bchannel *bch)
__write_ctrl_pci(fc, hdlc, bch->nr);
addr = fc->addr + CHIP_WINDOW;
}
- while (cnt < count) {
- val = get_unaligned(ptr);
- outl(cpu_to_le32(val), addr);
- ptr++;
- cnt += 4;
+ if (fillempty) {
+ while (cnt < count) {
+ /* all bytes the same - no worry about endian */
+ outl(*ptr, addr);
+ cnt += 4;
+ }
+ } else {
+ while (cnt < count) {
+ val = get_unaligned(ptr);
+ outl(cpu_to_le32(val), addr);
+ ptr++;
+ cnt += 4;
+ }
}
- if (debug & DEBUG_HW_BFIFO) {
+ if ((debug & DEBUG_HW_BFIFO) && !fillempty) {
snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ",
bch->nr, fc->name, count);
print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
@@ -488,17 +517,17 @@ hdlc_fill_fifo(struct bchannel *bch)
static void
HDLC_irq_xpr(struct bchannel *bch)
{
- if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+ if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) {
hdlc_fill_fifo(bch);
- else {
- if (bch->tx_skb) {
- /* send confirm, on trans, free on hdlc. */
- if (test_bit(FLG_TRANSPARENT, &bch->Flags))
- confirm_Bsend(bch);
+ } else {
+ if (bch->tx_skb)
dev_kfree_skb(bch->tx_skb);
- }
- if (get_next_bframe(bch))
+ if (get_next_bframe(bch)) {
hdlc_fill_fifo(bch);
+ test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags);
+ } else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) {
+ hdlc_fill_fifo(bch);
+ }
}
}
@@ -506,13 +535,23 @@ static void
HDLC_irq(struct bchannel *bch, u32 stat)
{
struct fritzcard *fc = bch->hw;
- int len;
+ int len, fs;
+ u32 rmlMask;
struct hdlc_hw *hdlc;
hdlc = &fc->hdlc[(bch->nr - 1) & 1];
pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat);
+ if (fc->type == AVM_FRITZ_PCIV2) {
+ rmlMask = HDLC_STAT_RML_MASK_V2;
+ fs = HDLC_FIFO_SIZE_V2;
+ } else {
+ rmlMask = HDLC_STAT_RML_MASK_V1;
+ fs = HDLC_FIFO_SIZE_V1;
+ }
if (stat & HDLC_INT_RPR) {
if (stat & HDLC_STAT_RDO) {
+ pr_warning("%s: ch%d stat %x RDO\n",
+ fc->name, bch->nr, stat);
hdlc->ctrl.sr.xml = 0;
hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS;
write_ctrl(bch, 1);
@@ -521,21 +560,21 @@ HDLC_irq(struct bchannel *bch, u32 stat)
if (bch->rx_skb)
skb_trim(bch->rx_skb, 0);
} else {
- len = (stat & HDLC_STAT_RML_MASK) >> 8;
+ len = (stat & rmlMask) >> 8;
if (!len)
- len = 32;
+ len = fs;
hdlc_empty_fifo(bch, len);
if (!bch->rx_skb)
goto handle_tx;
- if ((stat & HDLC_STAT_RME) || test_bit(FLG_TRANSPARENT,
- &bch->Flags)) {
- if (((stat & HDLC_STAT_CRCVFRRAB) ==
- HDLC_STAT_CRCVFR) ||
- test_bit(FLG_TRANSPARENT, &bch->Flags)) {
- recv_Bchannel(bch, 0);
+ if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+ recv_Bchannel(bch, 0, false);
+ } else if (stat & HDLC_STAT_RME) {
+ if ((stat & HDLC_STAT_CRCVFRRAB) ==
+ HDLC_STAT_CRCVFR) {
+ recv_Bchannel(bch, 0, false);
} else {
- pr_debug("%s: got invalid frame\n",
- fc->name);
+ pr_warning("%s: got invalid frame\n",
+ fc->name);
skb_trim(bch->rx_skb, 0);
}
}
@@ -547,16 +586,13 @@ handle_tx:
* restart transmitting the whole frame on HDLC
* in transparent mode we send the next data
*/
- if (bch->tx_skb)
- pr_debug("%s: ch%d XDU len(%d) idx(%d) Flags(%lx)\n",
- fc->name, bch->nr, bch->tx_skb->len,
- bch->tx_idx, bch->Flags);
- else
- pr_debug("%s: ch%d XDU no tx_skb Flags(%lx)\n",
- fc->name, bch->nr, bch->Flags);
+ pr_warning("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr,
+ stat, bch->tx_skb ? "tx_skb" : "no tx_skb");
if (bch->tx_skb && bch->tx_skb->len) {
if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
bch->tx_idx = 0;
+ } else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) {
+ test_and_set_bit(FLG_TX_EMPTY, &bch->Flags);
}
hdlc->ctrl.sr.xml = 0;
hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS;
@@ -659,22 +695,17 @@ avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
struct fritzcard *fc = bch->hw;
int ret = -EINVAL;
struct mISDNhead *hh = mISDN_HEAD_P(skb);
- u32 id;
- u_long flags;
+ unsigned long flags;
switch (hh->prim) {
case PH_DATA_REQ:
spin_lock_irqsave(&fc->lock, flags);
ret = bchannel_senddata(bch, skb);
if (ret > 0) { /* direct TX */
- id = hh->id; /* skb can be freed */
hdlc_fill_fifo(bch);
ret = 0;
- spin_unlock_irqrestore(&fc->lock, flags);
- if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
- queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
- } else
- spin_unlock_irqrestore(&fc->lock, flags);
+ }
+ spin_unlock_irqrestore(&fc->lock, flags);
return ret;
case PH_ACTIVATE_REQ:
spin_lock_irqsave(&fc->lock, flags);
@@ -783,7 +814,7 @@ init_card(struct fritzcard *fc)
inithdlc(fc);
enable_hwirq(fc);
/* RESET Receiver and Transmitter */
- if (AVM_FRITZ_PCIV2 == fc->type) {
+ if (fc->type == AVM_FRITZ_PCIV2) {
WriteISAC_V2(fc, ISACX_MASK, 0);
WriteISAC_V2(fc, ISACX_CMDRD, 0x41);
} else {
@@ -810,21 +841,7 @@ init_card(struct fritzcard *fc)
static int
channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
{
- int ret = 0;
- struct fritzcard *fc = bch->hw;
-
- switch (cq->op) {
- case MISDN_CTRL_GETOP:
- cq->op = 0;
- break;
- /* Nothing implemented yet */
- case MISDN_CTRL_FILL_EMPTY:
- default:
- pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
- ret = -EINVAL;
- break;
- }
- return ret;
+ return mISDN_ctrl_bchannel(bch, cq);
}
static int
@@ -839,14 +856,10 @@ avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
switch (cmd) {
case CLOSE_CHANNEL:
test_and_clear_bit(FLG_OPEN, &bch->Flags);
- if (test_bit(FLG_ACTIVE, &bch->Flags)) {
- spin_lock_irqsave(&fc->lock, flags);
- mISDN_freebchannel(bch);
- test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
- test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
- modehdlc(bch, ISDN_P_NONE);
- spin_unlock_irqrestore(&fc->lock, flags);
- }
+ spin_lock_irqsave(&fc->lock, flags);
+ mISDN_freebchannel(bch);
+ modehdlc(bch, ISDN_P_NONE);
+ spin_unlock_irqrestore(&fc->lock, flags);
ch->protocol = ISDN_P_NONE;
ch->peer = NULL;
module_put(THIS_MODULE);
@@ -868,7 +881,7 @@ channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq)
switch (cq->op) {
case MISDN_CTRL_GETOP:
- cq->op = MISDN_CTRL_LOOP;
+ cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
break;
case MISDN_CTRL_LOOP:
/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
@@ -878,6 +891,9 @@ channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq)
}
ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel);
break;
+ case MISDN_CTRL_L1_TIMER3:
+ ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1);
+ break;
default:
pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
ret = -EINVAL;
@@ -898,7 +914,6 @@ open_bchannel(struct fritzcard *fc, struct channel_req *rq)
bch = &fc->bch[rq->adr.channel - 1];
if (test_and_set_bit(FLG_OPEN, &bch->Flags))
return -EBUSY; /* b-channel can be only open once */
- test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
bch->ch.protocol = rq->protocol;
rq->ch = &bch->ch;
return 0;
@@ -1021,6 +1036,7 @@ static int __devinit
setup_instance(struct fritzcard *card)
{
int i, err;
+ unsigned short minsize;
u_long flags;
snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1);
@@ -1040,7 +1056,11 @@ setup_instance(struct fritzcard *card)
for (i = 0; i < 2; i++) {
card->bch[i].nr = i + 1;
set_channelmap(i + 1, card->isac.dch.dev.channelmap);
- mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+ if (AVM_FRITZ_PCIV2 == card->type)
+ minsize = HDLC_FIFO_SIZE_V2;
+ else
+ minsize = HDLC_FIFO_SIZE_V1;
+ mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize);
card->bch[i].hw = card;
card->bch[i].ch.send = avm_l2l1B;
card->bch[i].ch.ctrl = avm_bctrl;