From 76975f8a3186dae501584d0155ea410464f62815 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 1 Jan 2008 19:30:30 -0800 Subject: [PATCH 01/12] [X25]: Add missing x25_neigh_put The function x25_get_neigh increments a reference count. At the point of the second goto out, the result of calling x25_get_neigh is only stored in a local variable, and thus no one outside the function will be able to decrease the reference count. Thus, x25_neigh_put should be called before the return in this case. The problem was found using the following semantic match. (http://www.emn.fr/x-info/coccinelle/) // @@ type T,T1,T2; identifier E; statement S; expression x1,x2,x3; int ret; @@ T E; ... * if ((E = x25_get_neigh(...)) == NULL) S ... when != x25_neigh_put(...,(T1)E,...) when != if (E != NULL) { ... x25_neigh_put(...,(T1)E,...); ...} when != x1 = (T1)E when != E = x3; when any if (...) { ... when != x25_neigh_put(...,(T2)E,...) when != if (E != NULL) { ... x25_neigh_put(...,(T2)E,...); ...} when != x2 = (T2)E ( * return; | * return ret; ) } // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- net/x25/x25_forward.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/x25/x25_forward.c b/net/x25/x25_forward.c index 8738ec7ce693..34478035e05e 100644 --- a/net/x25/x25_forward.c +++ b/net/x25/x25_forward.c @@ -118,13 +118,14 @@ int x25_forward_data(int lci, struct x25_neigh *from, struct sk_buff *skb) { goto out; if ( (skbn = pskb_copy(skb, GFP_ATOMIC)) == NULL){ - goto out; + goto output; } x25_transmit_link(skbn, nb); - x25_neigh_put(nb); rc = 1; +output: + x25_neigh_put(nb); out: return rc; } From 2d60abc2a937bf77575c3b8c83faeeb84a84e654 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 3 Jan 2008 20:43:21 -0800 Subject: [PATCH 02/12] [XFRM]: Do not define km_migrate() if !CONFIG_XFRM_MIGRATE In include/net/xfrm.h we find : #ifdef CONFIG_XFRM_MIGRATE extern int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_bundles); ... #endif We can also guard the function body itself in net/xfrm/xfrm_state.c with same condition. (Problem spoted by sparse checker) make C=2 net/xfrm/xfrm_state.o ... net/xfrm/xfrm_state.c:1765:5: warning: symbol 'km_migrate' was not declared. Should it be static? ... Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/xfrm/xfrm_state.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index ee1e69753932..f26aaaca1fae 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -1749,6 +1749,7 @@ void km_policy_expired(struct xfrm_policy *pol, int dir, int hard, u32 pid) } EXPORT_SYMBOL(km_policy_expired); +#ifdef CONFIG_XFRM_MIGRATE int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, struct xfrm_migrate *m, int num_migrate) { @@ -1768,6 +1769,7 @@ int km_migrate(struct xfrm_selector *sel, u8 dir, u8 type, return err; } EXPORT_SYMBOL(km_migrate); +#endif int km_report(u8 proto, struct xfrm_selector *sel, xfrm_address_t *addr) { From e5e025401f6e926c1d9dc3f3f2813cf98a2d8708 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Thu, 3 Jan 2008 18:49:00 -0800 Subject: [PATCH 03/12] [CASSINI]: Fix endianness bug. Here's proposed fix for RX checksum handling in cassini; it affects little-endian working with half-duplex gigabit, but obviously needs testing on big-endian too. The problem is, we need to convert checksum to fixed-endian *before* correcting for (unstripped) FCS. On big-endian it won't matter (conversion is no-op), on little-endian it will, but only if FCS is not stripped by hardware; i.e. in half-duplex gigabit mode when ->crc_size is set. cassini.c part is that fix, cassini.h one consists of trivial endianness annotations. With that applied the sucker is endian-clean, according to sparse. Signed-off-by: Al Viro Signed-off-by: David S. Miller --- drivers/net/cassini.c | 8 +++++--- drivers/net/cassini.h | 18 +++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 7df31b5561cc..9030ca54a5bd 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -1979,6 +1979,7 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, struct cas_page *page; struct sk_buff *skb; void *addr, *crcaddr; + __sum16 csum; char *p; hlen = CAS_VAL(RX_COMP2_HDR_SIZE, words[1]); @@ -2158,14 +2159,15 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, skb_put(skb, alloclen); } - i = CAS_VAL(RX_COMP4_TCP_CSUM, words[3]); + csum = (__force __sum16)htons(CAS_VAL(RX_COMP4_TCP_CSUM, words[3])); if (cp->crc_size) { /* checksum includes FCS. strip it out. */ - i = csum_fold(csum_partial(crcaddr, cp->crc_size, i)); + csum = csum_fold(csum_partial(crcaddr, cp->crc_size, + csum_unfold(csum))); if (addr) cas_page_unmap(addr); } - skb->csum = ntohs(i ^ 0xffff); + skb->csum = csum_unfold(~csum); skb->ip_summed = CHECKSUM_COMPLETE; skb->protocol = eth_type_trans(skb, cp->dev); return len; diff --git a/drivers/net/cassini.h b/drivers/net/cassini.h index 2f93f83342d2..552af89ca1cf 100644 --- a/drivers/net/cassini.h +++ b/drivers/net/cassini.h @@ -4122,8 +4122,8 @@ cas_saturn_patch_t cas_saturn_patch[] = { inserted into outgoing frame. */ struct cas_tx_desc { - u64 control; - u64 buffer; + __le64 control; + __le64 buffer; }; /* descriptor ring for free buffers contains page-sized buffers. the index @@ -4131,8 +4131,8 @@ struct cas_tx_desc { * the completion ring. */ struct cas_rx_desc { - u64 index; - u64 buffer; + __le64 index; + __le64 buffer; }; /* received packets are put on the completion ring. */ @@ -4210,10 +4210,10 @@ struct cas_rx_desc { #define RX_INDEX_RELEASE 0x0000000000002000ULL struct cas_rx_comp { - u64 word1; - u64 word2; - u64 word3; - u64 word4; + __le64 word1; + __le64 word2; + __le64 word3; + __le64 word4; }; enum link_state { @@ -4252,7 +4252,7 @@ struct cas_init_block { struct cas_rx_comp rxcs[N_RX_COMP_RINGS][INIT_BLOCK_RX_COMP]; struct cas_rx_desc rxds[N_RX_DESC_RINGS][INIT_BLOCK_RX_DESC]; struct cas_tx_desc txds[N_TX_RINGS][INIT_BLOCK_TX]; - u64 tx_compwb; + __le64 tx_compwb; }; /* tiny buffers to deal with target abort issue. we allocate a bit From 9de4dfb4c7176e5bb232a21cdd8df78da2b15cac Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 3 Jan 2008 19:33:50 -0800 Subject: [PATCH 04/12] [CASSINI]: Revert 'dont touch page_count'. This reverts changeset fa4f0774d7c6cccb4d1fda76b91dd8eddcb2dd6a ([CASSINI]: dont touch page_count) because it breaks the driver. The local page counting added by this changeset did not account for the asynchronous page count changes done by kfree_skb() and friends. The change adds extra atomics and on top of it all appears to be totally unnecessary as well. Signed-off-by: David S. Miller Acked-by: Nick Piggin --- drivers/net/cassini.c | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 9030ca54a5bd..9c77eadb6767 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -336,30 +336,6 @@ static inline void cas_mask_intr(struct cas *cp) cas_disable_irq(cp, i); } -static inline void cas_buffer_init(cas_page_t *cp) -{ - struct page *page = cp->buffer; - atomic_set((atomic_t *)&page->lru.next, 1); -} - -static inline int cas_buffer_count(cas_page_t *cp) -{ - struct page *page = cp->buffer; - return atomic_read((atomic_t *)&page->lru.next); -} - -static inline void cas_buffer_inc(cas_page_t *cp) -{ - struct page *page = cp->buffer; - atomic_inc((atomic_t *)&page->lru.next); -} - -static inline void cas_buffer_dec(cas_page_t *cp) -{ - struct page *page = cp->buffer; - atomic_dec((atomic_t *)&page->lru.next); -} - static void cas_enable_irq(struct cas *cp, const int ring) { if (ring == 0) { /* all but TX_DONE */ @@ -497,7 +473,6 @@ static int cas_page_free(struct cas *cp, cas_page_t *page) { pci_unmap_page(cp->pdev, page->dma_addr, cp->page_size, PCI_DMA_FROMDEVICE); - cas_buffer_dec(page); __free_pages(page->buffer, cp->page_order); kfree(page); return 0; @@ -527,7 +502,6 @@ static cas_page_t *cas_page_alloc(struct cas *cp, const gfp_t flags) page->buffer = alloc_pages(flags, cp->page_order); if (!page->buffer) goto page_err; - cas_buffer_init(page); page->dma_addr = pci_map_page(cp->pdev, page->buffer, 0, cp->page_size, PCI_DMA_FROMDEVICE); return page; @@ -606,7 +580,7 @@ static void cas_spare_recover(struct cas *cp, const gfp_t flags) list_for_each_safe(elem, tmp, &list) { cas_page_t *page = list_entry(elem, cas_page_t, list); - if (cas_buffer_count(page) > 1) + if (page_count(page->buffer) > 1) continue; list_del(elem); @@ -1374,7 +1348,7 @@ static inline cas_page_t *cas_page_spare(struct cas *cp, const int index) cas_page_t *page = cp->rx_pages[1][index]; cas_page_t *new; - if (cas_buffer_count(page) == 1) + if (page_count(page->buffer) == 1) return page; new = cas_page_dequeue(cp); @@ -1394,7 +1368,7 @@ static cas_page_t *cas_page_swap(struct cas *cp, const int ring, cas_page_t **page1 = cp->rx_pages[1]; /* swap if buffer is in use */ - if (cas_buffer_count(page0[index]) > 1) { + if (page_count(page0[index]->buffer) > 1) { cas_page_t *new = cas_page_spare(cp, index); if (new) { page1[index] = page0[index]; @@ -2066,7 +2040,6 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, skb->len += hlen - swivel; get_page(page->buffer); - cas_buffer_inc(page); frag->page = page->buffer; frag->page_offset = off; frag->size = hlen - swivel; @@ -2091,7 +2064,6 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, frag++; get_page(page->buffer); - cas_buffer_inc(page); frag->page = page->buffer; frag->page_offset = 0; frag->size = hlen; @@ -2255,7 +2227,7 @@ static int cas_post_rxds_ringN(struct cas *cp, int ring, int num) released = 0; while (entry != last) { /* make a new buffer if it's still in use */ - if (cas_buffer_count(page[entry]) > 1) { + if (page_count(page[entry]->buffer) > 1) { cas_page_t *new = cas_page_dequeue(cp); if (!new) { /* let the timer know that we need to From 9e1848b60d09a715ff1e19aa6fda47e061d04965 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 3 Jan 2008 20:11:31 -0800 Subject: [PATCH 05/12] [CASSINI]: Program parent Intel31154 bridge when necessary. Signed-off-by: David S. Miller --- drivers/net/cassini.c | 86 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 9c77eadb6767..33ac2ef67778 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -4846,6 +4846,90 @@ static int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return rc; } +/* When this chip sits underneath an Intel 31154 bridge, it is the + * only subordinate device and we can tweak the bridge settings to + * reflect that fact. + */ +static void __devinit cas_program_bridge(struct pci_dev *cas_pdev) +{ + struct pci_dev *pdev = cas_pdev->bus->self; + u32 val; + + if (!pdev) + return; + + if (pdev->vendor != 0x8086 || pdev->device != 0x537c) + return; + + /* Clear bit 10 (Bus Parking Control) in the Secondary + * Arbiter Control/Status Register which lives at offset + * 0x41. Using a 32-bit word read/modify/write at 0x40 + * is much simpler so that's how we do this. + */ + pci_read_config_dword(pdev, 0x40, &val); + val &= ~0x00040000; + pci_write_config_dword(pdev, 0x40, val); + + /* Max out the Multi-Transaction Timer settings since + * Cassini is the only device present. + * + * The register is 16-bit and lives at 0x50. When the + * settings are enabled, it extends the GRANT# signal + * for a requestor after a transaction is complete. This + * allows the next request to run without first needing + * to negotiate the GRANT# signal back. + * + * Bits 12:10 define the grant duration: + * + * 1 -- 16 clocks + * 2 -- 32 clocks + * 3 -- 64 clocks + * 4 -- 128 clocks + * 5 -- 256 clocks + * + * All other values are illegal. + * + * Bits 09:00 define which REQ/GNT signal pairs get the + * GRANT# signal treatment. We set them all. + */ + pci_write_config_word(pdev, 0x50, (5 << 10) | 0x3ff); + + /* The Read Prefecth Policy register is 16-bit and sits at + * offset 0x52. It enables a "smart" pre-fetch policy. We + * enable it and max out all of the settings since only one + * device is sitting underneath and thus bandwidth sharing is + * not an issue. + * + * The register has several 3 bit fields, which indicates a + * multiplier applied to the base amount of prefetching the + * chip would do. These fields are at: + * + * 15:13 --- ReRead Primary Bus + * 12:10 --- FirstRead Primary Bus + * 09:07 --- ReRead Secondary Bus + * 06:04 --- FirstRead Secondary Bus + * + * Bits 03:00 control which REQ/GNT pairs the prefetch settings + * get enabled on. Bit 3 is a grouped enabler which controls + * all of the REQ/GNT pairs from [8:3]. Bits 2 to 0 control + * the individual REQ/GNT pairs [2:0]. + */ + pci_write_config_word(pdev, 0x52, + (0x7 << 13) | + (0x7 << 10) | + (0x7 << 7) | + (0x7 << 4) | + (0xf << 0)); + + /* Force cacheline size to 0x8 */ + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08); + + /* Force latency timer to maximum setting so Cassini can + * sit on the bus as long as it likes. + */ + pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xff); +} + static int __devinit cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -4901,6 +4985,8 @@ static int __devinit cas_init_one(struct pci_dev *pdev, printk(KERN_WARNING PFX "Could not enable MWI for %s\n", pci_name(pdev)); + cas_program_bridge(pdev); + /* * On some architectures, the default cache line size set * by pci_try_set_mwi reduces perforamnce. We have to increase From d011a231675b240157a3c335dd53e9b849d7d30d Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 4 Jan 2008 00:03:56 -0800 Subject: [PATCH 06/12] [CASSINI]: Set skb->truesize properly on receive packets. skb->truesize was not being incremented at all to reflect the page based data added to RX SKBs. Signed-off-by: David S. Miller --- drivers/net/cassini.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 33ac2ef67778..544ab0bb174c 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -2037,6 +2037,7 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, skb_shinfo(skb)->nr_frags++; skb->data_len += hlen - swivel; + skb->truesize += hlen - swivel; skb->len += hlen - swivel; get_page(page->buffer); From 86216268b9cdad57f9aa540ebf49cbae2f38b583 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 4 Jan 2008 00:23:18 -0800 Subject: [PATCH 07/12] [CASSINI]: Fix two obvious NAPI bugs. 1) close should do napi_disable() not napi_enable 2) remove unused local var 'todo' Signed-off-by: David S. Miller --- drivers/net/cassini.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 544ab0bb174c..53237ac0f83a 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -2586,7 +2586,7 @@ static int cas_poll(struct napi_struct *napi, int budget) { struct cas *cp = container_of(napi, struct cas, napi); struct net_device *dev = cp->dev; - int i, enable_intr, todo, credits; + int i, enable_intr, credits; u32 status = readl(cp->regs + REG_INTR_STATUS); unsigned long flags; @@ -4350,7 +4350,7 @@ static int cas_close(struct net_device *dev) struct cas *cp = netdev_priv(dev); #ifdef USE_NAPI - napi_enable(&cp->napi); + napi_disable(&cp->napi); #endif /* Make sure we don't get distracted by suspend/resume */ mutex_lock(&cp->pm_mutex); From 14be85f555fa53a3b80119569e4c2fe88aa9fd3e Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 4 Jan 2008 00:16:58 -0800 Subject: [PATCH 08/12] [CASSINI]: Bump driver version and release date. Signed-off-by: David S. Miller --- drivers/net/cassini.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index 53237ac0f83a..d66915d82b24 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -142,8 +142,8 @@ #define DRV_MODULE_NAME "cassini" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "1.4" -#define DRV_MODULE_RELDATE "1 July 2004" +#define DRV_MODULE_VERSION "1.5" +#define DRV_MODULE_RELDATE "4 Jan 2008" #define CAS_DEF_MSG_ENABLE \ (NETIF_MSG_DRV | \ From 44344b2a85f03326c7047a8c861b0c625c674839 Mon Sep 17 00:00:00 2001 From: Mark McLoughlin Date: Fri, 4 Jan 2008 00:56:25 -0800 Subject: [PATCH 09/12] [INET]: Fix netdev renaming and inet address labels When re-naming an interface, the previous secondary address labels get lost e.g. $> brctl addbr foo $> ip addr add 192.168.0.1 dev foo $> ip addr add 192.168.0.2 dev foo label foo:00 $> ip addr show dev foo | grep inet inet 192.168.0.1/32 scope global foo inet 192.168.0.2/32 scope global foo:00 $> ip link set foo name bar $> ip addr show dev bar | grep inet inet 192.168.0.1/32 scope global bar inet 192.168.0.2/32 scope global bar:2 Turns out to be a simple thinko in inetdev_changename() - clearly we want to look at the address label, rather than the device name, for a suffix to retain. Signed-off-by: Mark McLoughlin Signed-off-by: David S. Miller --- net/ipv4/devinet.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 3168c3de4919..b42f74617bac 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -1027,7 +1027,7 @@ static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) memcpy(ifa->ifa_label, dev->name, IFNAMSIZ); if (named++ == 0) continue; - dot = strchr(ifa->ifa_label, ':'); + dot = strchr(old, ':'); if (dot == NULL) { sprintf(old, ":%d", named); dot = old; From 134d99e302618eeb102c2a5be1f9e98696288bdd Mon Sep 17 00:00:00 2001 From: Li Zefan Date: Fri, 4 Jan 2008 01:52:02 -0800 Subject: [PATCH 10/12] [CONNECTOR]: Return proper error code in cn_call_callback() Error code should be set to EINVAL instead of ENODEV if !queue_work(). There's another call of queue_work() which may set err to EINVAL. Signed-off-by: Li Zefan Signed-off-by: David S. Miller --- drivers/connector/connector.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index 6883fcb79ad3..bf9716b75513 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -145,6 +145,8 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v if (queue_work(dev->cbdev->cn_queue, &__cbq->work)) err = 0; + else + err = -EINVAL; } else { struct cn_callback_data *d; From 00409bb045887ec5e7b9e351bc080c38ab6bfd33 Mon Sep 17 00:00:00 2001 From: Matthias Goebl Date: Fri, 4 Jan 2008 02:19:30 -0800 Subject: [PATCH 11/12] [ISDN] i4l: 'NO CARRIER' message lost after ldisc flush The ISDN tty layer doesn't produce a 'NO CARRIER' message after hangup. I suppose it broke when tty_buffer_flush() has been added to tty_ldisc_flush() in the commit below. For isdn_tty_modem_result(RESULT_NO_CARRIER..) the message inserted via isdn_tty_at_cout() -> tty_insert_flip_char() is flushed immediately by tty_ldisc_flush() -> tty_buffer_flush(). More annoyingly, the audio abort sequence DLE-ETX is also lost. This patch fixes only active audio connections, because I assume that nobody changes the line discipline for audio. For non-audio connections the problem remains. Maybe we can remove the tty_ldisc_flush() in isdn_tty_modem_result() at all because it's done at tty_close? On Mon, May 07, 2007 at 04:05:57PM -0500, Paul Fulghum wrote: > Flush the tty flip buffer when the line discipline > input queue is flushed, including the user call > tcflush(TCIFLUSH/TCIOFLUSH). This prevents unexpected > stale data after a user application calls tcflush(). > > Cc: Alan Cox > Cc: Antonino Ingargiola > Signed-off-by: Paul Fulghum > > --- a/drivers/char/tty_io.c 2007-05-04 05:46:55.000000000 -0500 > +++ b/drivers/char/tty_io.c 2007-05-05 03:23:46.000000000 -0500 > @@ -1240,6 +1263,7 @@ void tty_ldisc_flush(struct tty_struct * > ld->flush_buffer(tty); > tty_ldisc_deref(ld); > } > + tty_buffer_flush(tty); [..] Signed-off-by: Matthias Goebl Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_tty.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 4e5f87c1e714..24679a3f5596 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -2645,7 +2645,12 @@ isdn_tty_modem_result(int code, modem_info * info) if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { return; } +#ifdef CONFIG_ISDN_AUDIO + if ( !info->vonline ) + tty_ldisc_flush(info->tty); +#else tty_ldisc_flush(info->tty); +#endif if ((info->flags & ISDN_ASYNC_CHECK_CD) && (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) { From 7fde4d779b83898851959f837c9b26fe07ee91c9 Mon Sep 17 00:00:00 2001 From: Matthias Goebl Date: Fri, 4 Jan 2008 03:45:28 -0800 Subject: [PATCH 12/12] [ISDN]: i4l: Fix DLE handling for i4l-audio The DLE handling in i4l-audio seems to be broken. It produces spurious DLEs so asterisk 1.2.24 with chan_modem_i4l gets irritated, the error message is: "chan_modem_i4l.c:450 i4l_read: Value of escape is ^ (17)". -> There shouldn't be a DLE-^. If a spurious DLE-ETX occurs, the audio connection even dies. I use a "AVM Fritz!PCI" isdn card. I found two issues that only appear if ISDN_AUDIO_SKB_DLECOUNT(skb) > 0: - The loop in isdn_tty.c:isdn_tty_try_read() doesn't escape a DLE if it's the last character. - The loop in isdn_common.c:isdn_readbchan_tty() doesn't copy its characters, it only remembers the last one ("last = *p;"). Compare it with the loop in isdn_common.c:isdn_readbchan(), that *does* copy them ("*cp++ = *p;") correctly. The special handling of the "last" character made it more difficult. I compared it to linux-2.4.19: There was no "last"-handling and both loops did escape and copy all characters correctly. Signed-off-by: Matthias Goebl Signed-off-by: David S. Miller --- drivers/isdn/i4l/isdn_common.c | 3 +++ drivers/isdn/i4l/isdn_tty.c | 2 ++ 2 files changed, 5 insertions(+) diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index d6952959d72a..9cef6fcf587b 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -914,6 +914,9 @@ isdn_readbchan_tty(int di, int channel, struct tty_struct *tty, int cisco_hack) dflag = 0; count_pull = count_put = 0; while ((count_pull < skb->len) && (len > 0)) { + /* push every character but the last to the tty buffer directly */ + if ( count_put ) + tty_insert_flip_char(tty, last, TTY_NORMAL); len--; if (dev->drv[di]->DLEflag & DLEmask) { last = DLE; diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c index 24679a3f5596..9cb6e5021adb 100644 --- a/drivers/isdn/i4l/isdn_tty.c +++ b/drivers/isdn/i4l/isdn_tty.c @@ -85,6 +85,8 @@ isdn_tty_try_read(modem_info * info, struct sk_buff *skb) tty_insert_flip_char(tty, DLE, 0); tty_insert_flip_char(tty, *dp++, 0); } + if (*dp == DLE) + tty_insert_flip_char(tty, DLE, 0); last = *dp; } else { #endif