From patchwork Sat Apr 21 15:39:50 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Karsten Keil X-Patchwork-Id: 154502 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id D61A1B6EEB for ; Tue, 24 Apr 2012 03:09:35 +1000 (EST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754784Ab2DWRJc (ORCPT ); Mon, 23 Apr 2012 13:09:32 -0400 Received: from moutng.kundenserver.de ([212.227.126.187]:59923 "EHLO moutng.kundenserver.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754281Ab2DWRIX (ORCPT ); Mon, 23 Apr 2012 13:08:23 -0400 Received: from mailone.linux-pingi.de (p5497CD69.dip.t-dialin.net [84.151.205.105]) by mrelayeu.kundenserver.de (node=mreu3) with ESMTP (Nemesis) id 0MDJ3g-1SRu8X2UWK-00GY4b; Mon, 23 Apr 2012 19:08:22 +0200 Received: from pingi6.linux-pingi.de (pingi6.linux-pingi.de [10.23.200.6]) by mailone.linux-pingi.de (Postfix) with ESMTP id EE5F74FD0; Mon, 23 Apr 2012 19:08:17 +0200 (CEST) Received: by pingi6.linux-pingi.de (Postfix, from userid 1000) id 30CA59FB80; Mon, 23 Apr 2012 19:05:50 +0200 (CEST) Date: Sat, 21 Apr 2012 17:39:50 +0200 Subject: [PATCH 22/28] mISDN: More support for MISDN_CTRL_FILL_EMPTY and MISDN_CTRL_RX_OFF From: Karsten Keil To: David Miller Cc: netdev@vger.kernel.org, isdn4linux@listserv.isdn4linux.de Message-Id: <20120423170551.30CA59FB80@pingi6.linux-pingi.de> X-Provags-ID: V02:K0:RGoGa5U+bNmP+zD66YG0hNeBeoOkzPYj3I2r+CXp/xj rzYXhTZixByUbT5feRf7IO1PldC4l2xJb/xV8kgQeMYm/76kHu 2GmbHUaViGJDUxaJVLnuLYKvhoLa/9HTSR4euGdDw/X9oO3yfZ b7gNI9ZqhSFOdgFWHFkmtvHcYWP8ke56dnwBad9siDVADoCez3 N9kXJ4o9bs25AAnmGrYbuzvNf9bTjBQ5NM2o/uqne/5vJERDSY SFqzW4r7B4RpEhKcFs2hAvZHQvgr2eAR5gHz0uOIv1NP+Rz4xr EbpZfpjnXP3Ye5Q5WOYjKwUDaV+4/0E1Q321LU3nwJJ5BZBxix Yv1pxCRPjPOHIRrooYxZE2w6rPt0zRLvt1kplHb5JmzdWLl5Kn wc23Rf5UFWGUJDGzBg0Kgmf1byiZN07M24= Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add a common HW flag to support RX_OFF. Implement MISDN_CTRL_FILL_EMPTY and MISDN_CTRL_RX_OFF for the avmfritz driver. Signed-off-by: Karsten Keil --- drivers/isdn/hardware/mISDN/avmfritz.c | 169 ++++++++++++++++++++++++-------- drivers/isdn/mISDN/hwchannel.c | 1 + include/linux/mISDNhw.h | 4 +- include/linux/mISDNif.h | 10 ++- 4 files changed, 139 insertions(+), 45 deletions(-) diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c index 9cee744..3781446 100644 --- a/drivers/isdn/hardware/mISDN/avmfritz.c +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -141,6 +141,8 @@ struct fritzcard { struct isac_hw isac; struct hdlc_hw hdlc[2]; struct bchannel bch[2]; + int dropcnt[2]; + u8 fill[2 * HDLC_FIFO_SIZE_V2]; char log[LOG_SIZE + 1]; }; @@ -402,35 +404,45 @@ static void hdlc_empty_fifo(struct bchannel *bch, int count) { u32 *ptr; - u8 *p; + u8 *p, copy; u32 val, addr; int cnt; struct fritzcard *fc = bch->hw; pr_debug("%s: %s %d\n", fc->name, __func__, count); - if (!bch->rx_skb) { - if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { - if (count >= bch->minlen) - cnt = count; - else - cnt = 2 * bch->minlen; - } else - cnt = bch->maxlen; - bch->rx_skb = mI_alloc_skb(cnt, GFP_ATOMIC); + if (test_bit(FLG_RX_OFF, &bch->Flags)) { + /* We drop the content, but need to read all bytes from FIFO */ + copy = 0; + if (debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "Dropped %d bytes - RX off\n", count); + val = (bch->nr - 1) & 1; + fc->dropcnt[val] += count; + } else { + copy = 1; if (!bch->rx_skb) { - pr_info("%s: B receive out of memory\n", - fc->name); + if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + if (count >= bch->minlen) + cnt = count; + else + cnt = 2 * bch->minlen; + } else + cnt = bch->maxlen; + bch->rx_skb = mI_alloc_skb(cnt, GFP_ATOMIC); + if (!bch->rx_skb) { + pr_info("%s: B receive out of memory\n", + fc->name); + return; + } + } else + cnt = skb_tailroom(bch->rx_skb); + if (count > cnt) { + pr_debug("%s: overrun %d + %d free %d\n", fc->name, + bch->rx_skb->len, count, cnt); return; } - } else - cnt = skb_tailroom(bch->rx_skb); - if (count > cnt) { - pr_debug("%s: overrun %d + %d free %d\n", fc->name, - bch->rx_skb->len, count, cnt); - return; + p = skb_put(bch->rx_skb, count); + ptr = (u32 *)p; } - p = skb_put(bch->rx_skb, count); - ptr = (u32 *)p; if (AVM_FRITZ_PCIV2 == fc->type) addr = fc->addr + (bch->nr == 2 ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); @@ -441,11 +453,13 @@ hdlc_empty_fifo(struct bchannel *bch, int count) cnt = 0; while (cnt < count) { val = le32_to_cpu(inl(addr)); - put_unaligned(val, ptr); - ptr++; + if (copy) { + put_unaligned(val, ptr); + ptr++; + } cnt += 4; } - if (debug & DEBUG_HW_BFIFO) { + if (copy && (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); @@ -457,19 +471,25 @@ hdlc_fill_fifo(struct bchannel *bch) { struct fritzcard *fc = bch->hw; struct hdlc_hw *hdlc; - int count, fs, cnt = 0; + int count, fs, cnt = 0, idx; 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; + idx = (bch->nr - 1) & 1; + hdlc = &fc->hdlc[idx]; fs = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; - p = bch->tx_skb->data + bch->tx_idx; + if (!bch->tx_skb) { + if (!test_bit(FLG_FILLEMPTY, &bch->Flags)) + return; + count = fs; + p = fc->fill + (idx * HDLC_FIFO_SIZE_V2); + } 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 > fs) { count = fs; @@ -477,10 +497,13 @@ hdlc_fill_fifo(struct bchannel *bch) 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; + if (bch->tx_skb) { + pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count, + bch->tx_idx, bch->tx_skb->len); + bch->tx_idx += count; + } else + pr_debug("%s: %s fillempty %d\n", fc->name, __func__, count); hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); if (AVM_FRITZ_PCIV2 == fc->type) { __write_ctrl_pciv2(fc, hdlc, bch->nr); @@ -509,11 +532,10 @@ HDLC_irq_xpr(struct bchannel *bch) 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 (bch->tx_skb) dev_kfree_skb(bch->tx_skb); - } - if (get_next_bframe(bch)) + if (get_next_bframe(bch) || + test_bit(FLG_FILLEMPTY, &bch->Flags)) hdlc_fill_fifo(bch); } } @@ -523,8 +545,9 @@ HDLC_irq(struct bchannel *bch, u32 stat) { struct fritzcard *fc = bch->hw; int len, fs; - u32 rmlMask; + u32 rmlMask, err; struct hdlc_hw *hdlc; + struct sk_buff *skb; hdlc = &fc->hdlc[(bch->nr - 1) & 1]; pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); @@ -546,6 +569,12 @@ HDLC_irq(struct bchannel *bch, u32 stat) write_ctrl(bch, 1); if (bch->rx_skb) skb_trim(bch->rx_skb, 0); + if (test_bit(FLG_FIFO_STATUS, &bch->Flags)) { + skb = _alloc_mISDN_skb(PH_CONTROL_IND, + HW_FIFO_RDO, 0, NULL, GFP_KERNEL); + if (skb) + recv_Bchannel_skb(bch, skb); + } } else { len = (stat & rmlMask) >> 8; if (!len) @@ -575,13 +604,22 @@ handle_tx: */ 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) + 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 + err = HW_FIFO_XDU_DATA; + } else { pr_debug("%s: ch%d XDU no tx_skb Flags(%lx)\n", fc->name, bch->nr, bch->Flags); + err = HW_FIFO_XDU_NODATA; + } + if (test_bit(FLG_FIFO_STATUS, &bch->Flags)) { + skb = _alloc_mISDN_skb(PH_CONTROL_IND, err, + 0, NULL, GFP_KERNEL); + if (skb) + recv_Bchannel_skb(bch, skb); + } if (bch->tx_skb && bch->tx_skb->len) { if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) bch->tx_idx = 0; @@ -721,6 +759,24 @@ avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) NULL, GFP_KERNEL); ret = 0; break; + case PH_CONTROL_REQ: + switch (hh->id) { + case HW_FIFO_STATUS_ON: + test_and_set_bit(FLG_FIFO_STATUS, &bch->Flags); + id = 0; + break; + case HW_FIFO_STATUS_OFF: + test_and_clear_bit(FLG_FIFO_STATUS, &bch->Flags); + id = 0; + break; + default: + pr_info("PH_CONTROL_REQ %x not supported\n", hh->id); + id = -EINVAL; + break; + } + _queue_data(ch, PH_CONTROL_CNF, id, 0, NULL, GFP_KERNEL); + ret = 0; + break; } if (!ret) dev_kfree_skb(skb); @@ -835,12 +891,39 @@ init_card(struct fritzcard *fc) static int channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) { - int ret = 0, o1, o2; + int ret = 0, o1, o2, idx; + u8 *p; struct fritzcard *fc = bch->hw; + idx = (bch->nr - 1) & 1; switch (cq->op) { case MISDN_CTRL_GETOP: - cq->op = MISDN_CTRL_RX_BUFFER; + cq->op = MISDN_CTRL_RX_BUFFER | + MISDN_CTRL_RX_OFF | MISDN_CTRL_FILL_EMPTY; + break; + case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ + o1 = fc->dropcnt[idx]; + if (cq->p1) { + test_and_set_bit(FLG_RX_OFF, &bch->Flags); + fc->dropcnt[idx] = 0; + } else + test_and_clear_bit(FLG_RX_OFF, &bch->Flags); + cq->p2 = o1; + if (debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "Bch%d RX %s\n", + bch->nr, cq->p1 ? "off" : "on"); + break; + case MISDN_CTRL_FILL_EMPTY: /* fill fifo, if empty */ + p = fc->fill + (idx * HDLC_FIFO_SIZE_V2); + if (cq->p1) { + test_and_set_bit(FLG_FILLEMPTY, &bch->Flags); + if (cq->p2 > -1) + memset(p, cq->p2 & 0xff, HDLC_FIFO_SIZE_V2); + } else + test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); + if (debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "FILL_EMPTY Bch%d %s val %02x\n", + bch->nr, cq->p1 ? "on" : "off", *p); break; case MISDN_CTRL_RX_BUFFER: /* We return the old values */ diff --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c index 467518c..84fbcf1 100644 --- a/drivers/isdn/mISDN/hwchannel.c +++ b/drivers/isdn/mISDN/hwchannel.c @@ -135,6 +135,7 @@ mISDN_clear_bchannel(struct bchannel *ch) test_and_clear_bit(FLG_TX_BUSY, &ch->Flags); test_and_clear_bit(FLG_TX_NEXT, &ch->Flags); test_and_clear_bit(FLG_ACTIVE, &ch->Flags); + test_and_clear_bit(FLG_RX_OFF, &ch->Flags); } EXPORT_SYMBOL(mISDN_clear_bchannel); diff --git a/include/linux/mISDNhw.h b/include/linux/mISDNhw.h index 4f97bd3..9776edd 100644 --- a/include/linux/mISDNhw.h +++ b/include/linux/mISDNhw.h @@ -73,7 +73,9 @@ #define FLG_LL_OK 24 #define FLG_LL_CONN 25 #define FLG_DTMFSEND 26 - +/* stop sending received data upstream */ +#define FLG_RX_OFF 27 +#define FLG_FIFO_STATUS 28 /* workq events */ #define FLG_RECVQUEUE 30 #define FLG_PHCHANGE 31 diff --git a/include/linux/mISDNif.h b/include/linux/mISDNif.h index b26de1a..89ce4ec 100644 --- a/include/linux/mISDNif.h +++ b/include/linux/mISDNif.h @@ -37,7 +37,7 @@ */ #define MISDN_MAJOR_VERSION 1 #define MISDN_MINOR_VERSION 1 -#define MISDN_RELEASE 30 +#define MISDN_RELEASE 31 /* primitives for information exchange * generell format @@ -158,6 +158,7 @@ #define HFC_VOL_CHANGE_RX 0x2602 #define HFC_SPL_LOOP_ON 0x2603 #define HFC_SPL_LOOP_OFF 0x2604 + /* for T30 FAX and analog modem */ #define HW_MOD_FRM 0x4000 #define HW_MOD_FRH 0x4001 @@ -171,6 +172,13 @@ #define HW_MOD_READY 0x4014 #define HW_MOD_LASTDATA 0x4015 +/* Debug and status via PH_CONTROL */ +#define HW_FIFO_STATUS_OFF 0x8000 +#define HW_FIFO_STATUS_ON 0x8001 +#define HW_FIFO_RDO 0x8002 +#define HW_FIFO_XDU_DATA 0x8003 +#define HW_FIFO_XDU_NODATA 0x8004 + /* DSP_TONE_PATT_ON parameter */ #define TONE_OFF 0x0000 #define TONE_GERMAN_DIALTONE 0x0001