From 211a0d941b1924e667483f822a55e2cc694cd212 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 15 Mar 2010 15:23:30 -0700 Subject: e100: Fix ring parameter change handling regression. When the PCI pool changes were added to fix resume failures: commit 98468efddb101f8a29af974101c17ba513b07be1 e100: Use pci pool to work around GFP_ATOMIC order 5 memory allocation failu and commit 70abc8cb90e679d8519721e2761d8366a18212a6 e100: Fix broken cbs accounting due to missing memset. This introduced a problem that can happen if the TX ring size is increased. We need to size the PCI pool using cbs->max instead of the default cbs->count value. Signed-off-by: David S. Miller diff --git a/drivers/net/e100.c b/drivers/net/e100.c index a26ccab..b997e57 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -2858,7 +2858,7 @@ static int __devinit e100_probe(struct pci_dev *pdev, } nic->cbs_pool = pci_pool_create(netdev->name, nic->pdev, - nic->params.cbs.count * sizeof(struct cb), + nic->params.cbs.max * sizeof(struct cb), sizeof(u32), 0); DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, MAC addr %pM\n", -- cgit v0.10.2 From be5bce2bf5cfe021bc6bdff4d49fa18776bc293d Mon Sep 17 00:00:00 2001 From: Sekhar Nori Date: Tue, 9 Mar 2010 01:20:37 +0000 Subject: net: davinci emac: use dma_{map, unmap}_single API for cache coherency The davinci emac driver uses some ARM specific DMA APIs for cache coherency which have been removed from kernel with the 2.6.34 merge. Modify the driver to use the dma_{map, unmap}_single() APIs defined in dma-mapping.h Without this fix, the driver fails to compile on Linus's tree. Tested on DM365 and OMAP-L138 EVMs. Signed-off-by: Sekhar Nori Acked-by: Kevin Hilman Signed-off-by: David S. Miller diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 32960b9..491e64c 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -29,10 +29,6 @@ * PHY layer usage */ -/** Pending Items in this driver: - * 1. Use Linux cache infrastcture for DMA'ed memory (dma_xxx functions) - */ - #include #include #include @@ -504,12 +500,6 @@ static unsigned long mdio_max_freq; /* Cache macros - Packet buffers would be from skb pool which is cached */ #define EMAC_VIRT_NOCACHE(addr) (addr) -#define EMAC_CACHE_INVALIDATE(addr, size) \ - dma_cache_maint((void *)addr, size, DMA_FROM_DEVICE) -#define EMAC_CACHE_WRITEBACK(addr, size) \ - dma_cache_maint((void *)addr, size, DMA_TO_DEVICE) -#define EMAC_CACHE_WRITEBACK_INVALIDATE(addr, size) \ - dma_cache_maint((void *)addr, size, DMA_BIDIRECTIONAL) /* DM644x does not have BD's in cached memory - so no cache functions */ #define BD_CACHE_INVALIDATE(addr, size) @@ -1235,6 +1225,10 @@ static void emac_txch_teardown(struct emac_priv *priv, u32 ch) if (1 == txch->queue_active) { curr_bd = txch->active_queue_head; while (curr_bd != NULL) { + dma_unmap_single(emac_dev, curr_bd->buff_ptr, + curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE, + DMA_TO_DEVICE); + emac_net_tx_complete(priv, (void __force *) &curr_bd->buf_token, 1, ch); if (curr_bd != txch->active_queue_tail) @@ -1327,6 +1321,11 @@ static int emac_tx_bdproc(struct emac_priv *priv, u32 ch, u32 budget) txch->queue_active = 0; /* end of queue */ } } + + dma_unmap_single(emac_dev, curr_bd->buff_ptr, + curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE, + DMA_TO_DEVICE); + *tx_complete_ptr = (u32) curr_bd->buf_token; ++tx_complete_ptr; ++tx_complete_cnt; @@ -1387,8 +1386,8 @@ static int emac_send(struct emac_priv *priv, struct emac_netpktobj *pkt, u32 ch) txch->bd_pool_head = curr_bd->next; curr_bd->buf_token = buf_list->buf_token; - /* FIXME buff_ptr = dma_map_single(... data_ptr ...) */ - curr_bd->buff_ptr = virt_to_phys(buf_list->data_ptr); + curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buf_list->data_ptr, + buf_list->length, DMA_TO_DEVICE); curr_bd->off_b_len = buf_list->length; curr_bd->h_next = 0; curr_bd->next = NULL; @@ -1468,7 +1467,6 @@ static int emac_dev_xmit(struct sk_buff *skb, struct net_device *ndev) tx_buf.length = skb->len; tx_buf.buf_token = (void *)skb; tx_buf.data_ptr = skb->data; - EMAC_CACHE_WRITEBACK((unsigned long)skb->data, skb->len); ndev->trans_start = jiffies; ret_code = emac_send(priv, &tx_packet, EMAC_DEF_TX_CH); if (unlikely(ret_code != 0)) { @@ -1543,7 +1541,6 @@ static void *emac_net_alloc_rx_buf(struct emac_priv *priv, int buf_size, p_skb->dev = ndev; skb_reserve(p_skb, NET_IP_ALIGN); *data_token = (void *) p_skb; - EMAC_CACHE_WRITEBACK_INVALIDATE((unsigned long)p_skb->data, buf_size); return p_skb->data; } @@ -1612,8 +1609,8 @@ static int emac_init_rxch(struct emac_priv *priv, u32 ch, char *param) /* populate the hardware descriptor */ curr_bd->h_next = emac_virt_to_phys(rxch->active_queue_head, priv); - /* FIXME buff_ptr = dma_map_single(... data_ptr ...) */ - curr_bd->buff_ptr = virt_to_phys(curr_bd->data_ptr); + curr_bd->buff_ptr = dma_map_single(emac_dev, curr_bd->data_ptr, + rxch->buf_size, DMA_FROM_DEVICE); curr_bd->off_b_len = rxch->buf_size; curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT; @@ -1697,6 +1694,12 @@ static void emac_cleanup_rxch(struct emac_priv *priv, u32 ch) curr_bd = rxch->active_queue_head; while (curr_bd) { if (curr_bd->buf_token) { + dma_unmap_single(&priv->ndev->dev, + curr_bd->buff_ptr, + curr_bd->off_b_len + & EMAC_RX_BD_BUF_SIZE, + DMA_FROM_DEVICE); + dev_kfree_skb_any((struct sk_buff *)\ curr_bd->buf_token); } @@ -1871,8 +1874,8 @@ static void emac_addbd_to_rx_queue(struct emac_priv *priv, u32 ch, /* populate the hardware descriptor */ curr_bd->h_next = 0; - /* FIXME buff_ptr = dma_map_single(... buffer ...) */ - curr_bd->buff_ptr = virt_to_phys(buffer); + curr_bd->buff_ptr = dma_map_single(&priv->ndev->dev, buffer, + rxch->buf_size, DMA_FROM_DEVICE); curr_bd->off_b_len = rxch->buf_size; curr_bd->mode = EMAC_CPPI_OWNERSHIP_BIT; curr_bd->next = NULL; @@ -1927,7 +1930,6 @@ static int emac_net_rx_cb(struct emac_priv *priv, p_skb = (struct sk_buff *)net_pkt_list->pkt_token; /* set length of packet */ skb_put(p_skb, net_pkt_list->pkt_length); - EMAC_CACHE_INVALIDATE((unsigned long)p_skb->data, p_skb->len); p_skb->protocol = eth_type_trans(p_skb, priv->ndev); netif_receive_skb(p_skb); priv->net_dev_stats.rx_bytes += net_pkt_list->pkt_length; @@ -1990,6 +1992,11 @@ static int emac_rx_bdproc(struct emac_priv *priv, u32 ch, u32 budget) rx_buf_obj->data_ptr = (char *)curr_bd->data_ptr; rx_buf_obj->length = curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE; rx_buf_obj->buf_token = curr_bd->buf_token; + + dma_unmap_single(&priv->ndev->dev, curr_bd->buff_ptr, + curr_bd->off_b_len & EMAC_RX_BD_BUF_SIZE, + DMA_FROM_DEVICE); + curr_pkt->pkt_token = curr_pkt->buf_list->buf_token; curr_pkt->num_bufs = 1; curr_pkt->pkt_length = -- cgit v0.10.2 From d4fdcd923daf9d03d2e1b956d66f05c3f2ca4c43 Mon Sep 17 00:00:00 2001 From: "chaithrika@ti.com" Date: Wed, 10 Mar 2010 22:37:56 +0000 Subject: TI DaVinci EMAC: Convert to dev_pm_ops Migrate from the legacy PM hooks to use dev_pm_ops structure. Signed-off-by: Chaithrika U S Acked-by: Kevin Hilman Signed-off-by: David S. Miller diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 491e64c..5c4f5b1 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -2827,31 +2827,37 @@ static int __devexit davinci_emac_remove(struct platform_device *pdev) return 0; } -static -int davinci_emac_suspend(struct platform_device *pdev, pm_message_t state) +static int davinci_emac_suspend(struct device *dev) { - struct net_device *dev = platform_get_drvdata(pdev); + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); - if (netif_running(dev)) - emac_dev_stop(dev); + if (netif_running(ndev)) + emac_dev_stop(ndev); clk_disable(emac_clk); return 0; } -static int davinci_emac_resume(struct platform_device *pdev) +static int davinci_emac_resume(struct device *dev) { - struct net_device *dev = platform_get_drvdata(pdev); + struct platform_device *pdev = to_platform_device(dev); + struct net_device *ndev = platform_get_drvdata(pdev); clk_enable(emac_clk); - if (netif_running(dev)) - emac_dev_open(dev); + if (netif_running(ndev)) + emac_dev_open(ndev); return 0; } +static const struct dev_pm_ops davinci_emac_pm_ops = { + .suspend = davinci_emac_suspend, + .resume = davinci_emac_resume, +}; + /** * davinci_emac_driver: EMAC platform driver structure */ @@ -2859,11 +2865,10 @@ static struct platform_driver davinci_emac_driver = { .driver = { .name = "davinci_emac", .owner = THIS_MODULE, + .pm = &davinci_emac_pm_ops, }, .probe = davinci_emac_probe, .remove = __devexit_p(davinci_emac_remove), - .suspend = davinci_emac_suspend, - .resume = davinci_emac_resume, }; /** -- cgit v0.10.2 From d4a2ac3e802d9f598453a7854d0fdf67371ac2dd Mon Sep 17 00:00:00 2001 From: Ajit Khaparde Date: Thu, 11 Mar 2010 01:35:59 +0000 Subject: be2net: fix mccq create for big endian architectures The request to create an mccq was being dispatched without doing a byte swap of num_pages. This byte swap is necessary for Big Endian systems like PPC. Not having this fix leads mccq create to fail on BE ASICs running newer version of firmware, thereby causing driver initialization failure. Signed-off-by: Ajit Khaparde Signed-off-by: David S. Miller diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index c592153..50e6259 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -673,7 +673,7 @@ int be_cmd_mccq_create(struct be_adapter *adapter, be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_COMMON, OPCODE_COMMON_MCC_CREATE, sizeof(*req)); - req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size)); AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1); AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt, -- cgit v0.10.2 From 2d99cf16f42b1979a2c498bb6d09498dbd689978 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 9 Mar 2010 06:55:00 +0000 Subject: bnx2x: use smp_mb() to keep ordering of read write operations Since we want to keep ordering of write to fp->bd_tx_cons and netif_tx_queue_stopped(txq), what is read of txq->state, we have to use general memory barrier. Signed-off-by: Stanislaw Gruszka Signed-off-by: David S. Miller diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index ed785a3..9fc0f6a 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -963,7 +963,7 @@ static int bnx2x_tx_int(struct bnx2x_fastpath *fp) * start_xmit() will miss it and cause the queue to be stopped * forever. */ - smp_wmb(); + smp_mb(); /* TBD need a thresh? */ if (unlikely(netif_tx_queue_stopped(txq))) { -- cgit v0.10.2 From 0efc22f3afa5d8f070a33fea06162d7d9d518e38 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 9 Mar 2010 06:55:01 +0000 Subject: bnx2x: remove not necessary compiler barrier Access to fp->tx_bd_prod is protected by __netif_tx_lock, so we do not need any barrier for that. Update of fp->tx_bd_cons in bnx2x_tx_int() is not protected by lock, but barrier() nor smb_mb() in bnx2x_tx_avail() not guarantee we will see values that is written on other cpu. Ordering issues between netif_tx_stop_queue(), netif_tx_queue_stopped(), fp->tx_bd_cons = bd_cons and bnx2x_tx_avail() are already handled by smp_mb() in bnx2x_tx_int() and bnx2x_start_xmit(). Signed-off-by: Stanislaw Gruszka Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index 9fc0f6a..ae62b67 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -893,7 +893,6 @@ static inline u16 bnx2x_tx_avail(struct bnx2x_fastpath *fp) u16 prod; u16 cons; - barrier(); /* Tell compiler that prod and cons can change */ prod = fp->tx_bd_prod; cons = fp->tx_bd_cons; -- cgit v0.10.2 From 9baddeb8c6c7faa7da8706ad629f09ca221850c1 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 9 Mar 2010 06:55:02 +0000 Subject: bnx2x: change smp_mb() comment to conform the true Access to fp->tx_bp_prod is protected by __netif_tx_lock, smp_mb() is not needed for that. Signed-off-by: Stanislaw Gruszka Signed-off-by: Eilon Greenstein Signed-off-by: David S. Miller diff --git a/drivers/net/bnx2x_main.c b/drivers/net/bnx2x_main.c index ae62b67..6c042a7 100644 --- a/drivers/net/bnx2x_main.c +++ b/drivers/net/bnx2x_main.c @@ -11428,9 +11428,12 @@ static netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(bnx2x_tx_avail(fp) < MAX_SKB_FRAGS + 3)) { netif_tx_stop_queue(txq); - /* We want bnx2x_tx_int to "see" the updated tx_bd_prod - if we put Tx into XOFF state. */ + + /* paired memory barrier is in bnx2x_tx_int(), we have to keep + * ordering of set_bit() in netif_tx_stop_queue() and read of + * fp->bd_tx_cons */ smp_mb(); + fp->eth_q_stats.driver_xoff++; if (bnx2x_tx_avail(fp) >= MAX_SKB_FRAGS + 3) netif_tx_wake_queue(txq); -- cgit v0.10.2 From dec9951b8ad86c591af7b452966bf48b307a4010 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 11 Mar 2010 12:07:22 +0000 Subject: isdn: misplaced parenthesis in pof_handle_data() The parenthesis was misplaced. Signed-off-by: Roel Kluin Cc: Karsten Keil Signed-off-by: Andrew Morton Signed-off-by: David S. Miller diff --git a/drivers/isdn/hysdn/hysdn_boot.c b/drivers/isdn/hysdn/hysdn_boot.c index be787e1..4f541ef 100644 --- a/drivers/isdn/hysdn/hysdn_boot.c +++ b/drivers/isdn/hysdn/hysdn_boot.c @@ -143,7 +143,7 @@ pof_handle_data(hysdn_card * card, int datlen) (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA", datlen, boot->pof_recoffset); - if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0)) + if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen)) < 0) return (boot->last_error); /* error writing data */ if (boot->pof_recoffset + datlen >= boot->pof_reclen) -- cgit v0.10.2 From 8b4017d8c191822f1c93744e7876c9020e6209aa Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 11 Mar 2010 12:07:24 +0000 Subject: i4l: silence compiler warnings for array access in Eicon DIVA ISDN driver When compiling this driver, the compiler throws the following warnings: drivers/isdn/hardware/eicon/message.c:8426: warning: array subscript is above array bounds drivers/isdn/hardware/eicon/message.c:8427: warning: array subscript is above array bounds drivers/isdn/hardware/eicon/message.c:8434: warning: array subscript is above array bounds drivers/isdn/hardware/eicon/message.c:8435: warning: array subscript is above array bounds drivers/isdn/hardware/eicon/message.c:8436: warning: array subscript is above array bounds drivers/isdn/hardware/eicon/message.c:8447: warning: array subscript is above array bounds This arises from the particular semantics the driver is using to write to the nlc array (static byte[256]). The array has a length in byte 0 followed by a T30_INFO struct starting at byte 1. The T30_INFO struct has a number of variable length strings after the station_id entry, which cannot be explicitly defined in the struct and the driver accesses them with an array index to station_id beyond the length of station_id. This patch merely changes the semantics that the driver uses to access the entries after the station_id entry to use the original 256 byte nlc array taking the offset and length of the station_id entry to calculate where to write in the array, thereby silencing the warning. Signed-off-by: Ian Munsie Cc: Armin Schindler Cc: Karsten Keil Cc: Stoyan Gaydarov Signed-off-by: Andrew Morton Signed-off-by: David S. Miller diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c index ae89fb8..4bd469b 100644 --- a/drivers/isdn/hardware/eicon/message.c +++ b/drivers/isdn/hardware/eicon/message.c @@ -8423,17 +8423,17 @@ static word add_b23(PLCI *plci, API_PARSE *bp) pos = 0; else { - ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' '; - ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; len = (byte)b3_config_parms[2].length; if (len > 20) len = 20; if (CAPI_MAX_DATE_TIME_LENGTH + 2 + len + 2 + b3_config_parms[3].length <= CAPI_MAX_HEAD_LINE_SPACE) { for (i = 0; i < len; i++) - ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ((byte *)b3_config_parms[2].info)[1+i]; - ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' '; - ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ((byte *)b3_config_parms[2].info)[1+i]; + nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; } } } @@ -8444,9 +8444,8 @@ static word add_b23(PLCI *plci, API_PARSE *bp) ((T30_INFO *)&nlc[1])->head_line_len = (byte)(pos + len); nlc[0] += (byte)(pos + len); for (i = 0; i < len; i++) - ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ((byte *)b3_config_parms[3].info)[1+i]; - } - else + nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ((byte *)b3_config_parms[3].info)[1+i]; + } else ((T30_INFO *)&nlc[1])->head_line_len = 0; plci->nsf_control_bits = 0; -- cgit v0.10.2 From 255f5c327ec3a1d77fe54ef5773ef5eaf7c35a3e Mon Sep 17 00:00:00 2001 From: Ian Munsie Date: Thu, 11 Mar 2010 12:07:25 +0000 Subject: i4l: change magic numbers in Eicon DIVA ISDN driver to symbolic names Replace references to the '20' magic number found throughout the Eicon ISDN driver for the length of the station_id field in the T30_INFO struct with the T30_MAX_STATION_ID_LENGTH symbolic constant. Signed-off-by: Ian Munsie Cc: Armin Schindler Cc: Karsten Keil Cc: Stoyan Gaydarov Signed-off-by: Andrew Morton Signed-off-by: David S. Miller diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c index 4bd469b..341ef17 100644 --- a/drivers/isdn/hardware/eicon/message.c +++ b/drivers/isdn/hardware/eicon/message.c @@ -2754,7 +2754,7 @@ static byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER *a, for (i = 0; i < w; i++) ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id[i] = fax_parms[4].info[1+i]; ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0; - len = offsetof(T30_INFO, station_id) + 20; + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; w = fax_parms[5].length; if (w > 20) w = 20; @@ -2892,7 +2892,7 @@ static byte connect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF) && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)) { - len = offsetof(T30_INFO, station_id) + 20; + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; if (plci->fax_connect_info_length < len) { ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0; @@ -3802,7 +3802,7 @@ static byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER *a, break; } ncpi = &m_parms[1]; - len = offsetof(T30_INFO, station_id) + 20; + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; if (plci->fax_connect_info_length < len) { ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0; @@ -6830,7 +6830,7 @@ static void nl_ind(PLCI *plci) if(((T30_INFO *)plci->NL.RBuffer->P)->station_id_len) { plci->ncpi_buffer[len] = 20; - for (i = 0; i < 20; i++) + for (i = 0; i < T30_MAX_STATION_ID_LENGTH; i++) plci->ncpi_buffer[++len] = ((T30_INFO *)plci->NL.RBuffer->P)->station_id[i]; } if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK)) @@ -6844,7 +6844,7 @@ static void nl_ind(PLCI *plci) if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1]) & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD))) { - i = offsetof(T30_INFO, station_id) + 20 + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len; + i = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len; while (i < plci->NL.RBuffer->length) plci->ncpi_buffer[++len] = plci->NL.RBuffer->P[i++]; } @@ -8400,7 +8400,7 @@ static word add_b23(PLCI *plci, API_PARSE *bp) } } /* copy station id to NLC */ - for(i=0; i<20; i++) + for(i=0; i < T30_MAX_STATION_ID_LENGTH; i++) { if(istation_id[i] = ' '; } } - ((T30_INFO *)&nlc[1])->station_id_len = 20; + ((T30_INFO *)&nlc[1])->station_id_len = T30_MAX_STATION_ID_LENGTH; /* copy head line to NLC */ if(b3_config_parms[3].length) { - pos = (byte)(fax_head_line_time (&(((T30_INFO *)&nlc[1])->station_id[20]))); + pos = (byte)(fax_head_line_time (&(((T30_INFO *)&nlc[1])->station_id[T30_MAX_STATION_ID_LENGTH]))); if (pos != 0) { if (CAPI_MAX_DATE_TIME_LENGTH + 2 + b3_config_parms[3].length > CAPI_MAX_HEAD_LINE_SPACE) pos = 0; else { - nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; - nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; len = (byte)b3_config_parms[2].length; if (len > 20) len = 20; if (CAPI_MAX_DATE_TIME_LENGTH + 2 + len + 2 + b3_config_parms[3].length <= CAPI_MAX_HEAD_LINE_SPACE) { for (i = 0; i < len; i++) - nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ((byte *)b3_config_parms[2].info)[1+i]; - nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; - nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ((byte *)b3_config_parms[2].info)[1+i]; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ' '; } } } @@ -8444,7 +8444,7 @@ static word add_b23(PLCI *plci, API_PARSE *bp) ((T30_INFO *)&nlc[1])->head_line_len = (byte)(pos + len); nlc[0] += (byte)(pos + len); for (i = 0; i < len; i++) - nlc[1 + offsetof(T30_INFO, station_id) + 20 + pos++] = ((byte *)b3_config_parms[3].info)[1+i]; + nlc[1 + offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH + pos++] = ((byte *)b3_config_parms[3].info)[1+i]; } else ((T30_INFO *)&nlc[1])->head_line_len = 0; @@ -8472,7 +8472,7 @@ static word add_b23(PLCI *plci, API_PARSE *bp) fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING; } len = nlc[0]; - pos = offsetof(T30_INFO, station_id) + 20; + pos = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; if (pos < plci->fax_connect_info_length) { for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--) @@ -8524,7 +8524,7 @@ static word add_b23(PLCI *plci, API_PARSE *bp) } PUT_WORD(&(((T30_INFO *)&nlc[1])->control_bits_low), fax_control_bits); - len = offsetof(T30_INFO, station_id) + 20; + len = offsetof(T30_INFO, station_id) + T30_MAX_STATION_ID_LENGTH; for (i = 0; i < len; i++) plci->fax_connect_info_buffer[i] = nlc[1+i]; ((T30_INFO *) plci->fax_connect_info_buffer)->head_line_len = 0; -- cgit v0.10.2 From 876e956f207373f974f2808c36aabcd4e32c9ee4 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 9 Mar 2010 11:14:11 +0000 Subject: drivers/net: drop redundant memset The region set by the call to memset is immediately overwritten by the subsequent call to memcpy. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression e1,e2,e3,e4; @@ - memset(e1,e2,e3); memcpy(e1,e4,e3); // Signed-off-by: Julia Lawall Signed-off-by: David S. Miller diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index df70657..2eb7f8a 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -5819,10 +5819,8 @@ static void s2io_vpd_read(struct s2io_nic *nic) } } - if ((!fail) && (vpd_data[1] < VPD_STRING_LEN)) { - memset(nic->product_name, 0, vpd_data[1]); + if ((!fail) && (vpd_data[1] < VPD_STRING_LEN)) memcpy(nic->product_name, &vpd_data[3], vpd_data[1]); - } kfree(vpd_data); swstats->mem_freed += 256; } -- cgit v0.10.2 From f28f9e43b3d81d1e69da0ebf77c8a6780cb5e0c8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 12 Mar 2010 19:23:27 +0000 Subject: timestamping: fix example build Fix Makefiles so that Documentation/networking/timestamping/timestamping.c will build when using the CONFIG_BUILD_DOCSRC kconfig option. (timestamping.c does not build currently with its simple Makefile.) Also fix printf format warnings. Signed-off-by: Randy Dunlap Cc: Patrick Ohly Signed-off-by: David S. Miller diff --git a/Documentation/networking/Makefile b/Documentation/networking/Makefile index 6d8af1a..5aba7a3 100644 --- a/Documentation/networking/Makefile +++ b/Documentation/networking/Makefile @@ -6,3 +6,5 @@ hostprogs-y := ifenslave # Tell kbuild to always build the programs always := $(hostprogs-y) + +obj-m := timestamping/ diff --git a/Documentation/networking/timestamping/Makefile b/Documentation/networking/timestamping/Makefile index 2a1489f..e799734 100644 --- a/Documentation/networking/timestamping/Makefile +++ b/Documentation/networking/timestamping/Makefile @@ -1,6 +1,13 @@ -CPPFLAGS = -I../../../include +# kbuild trick to avoid linker error. Can be omitted if a module is built. +obj- := dummy.o -timestamping: timestamping.c +# List of programs to build +hostprogs-y := timestamping + +# Tell kbuild to always build the programs +always := $(hostprogs-y) + +HOSTCFLAGS_timestamping.o += -I$(objtree)/usr/include clean: rm -f timestamping diff --git a/Documentation/networking/timestamping/timestamping.c b/Documentation/networking/timestamping/timestamping.c index a7936fe..7b81d16 100644 --- a/Documentation/networking/timestamping/timestamping.c +++ b/Documentation/networking/timestamping/timestamping.c @@ -41,9 +41,9 @@ #include #include -#include "asm/types.h" -#include "linux/net_tstamp.h" -#include "linux/errqueue.h" +#include +#include +#include #ifndef SO_TIMESTAMPING # define SO_TIMESTAMPING 37 @@ -164,7 +164,7 @@ static void printpacket(struct msghdr *msg, int res, gettimeofday(&now, 0); - printf("%ld.%06ld: received %s data, %d bytes from %s, %d bytes control messages\n", + printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", (long)now.tv_sec, (long)now.tv_usec, (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", res, @@ -173,7 +173,7 @@ static void printpacket(struct msghdr *msg, int res, for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { - printf(" cmsg len %d: ", cmsg->cmsg_len); + printf(" cmsg len %zu: ", cmsg->cmsg_len); switch (cmsg->cmsg_level) { case SOL_SOCKET: printf("SOL_SOCKET "); -- cgit v0.10.2 From a1ca14ac54675d3bf48d442b5a7b9eba133f1888 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 11 Mar 2010 12:07:49 +0000 Subject: phonet: use for_each_set_bit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace open-coded loop with for_each_set_bit(). Signed-off-by: Akinobu Mita Cc: "David S. Miller" Signed-off-by: Andrew Morton Acked-by: RĂ©mi Denis-Courmont Signed-off-by: David S. Miller diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index c597cc5..5c6ae0c 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -107,8 +107,7 @@ static void phonet_device_destroy(struct net_device *dev) if (pnd) { u8 addr; - for (addr = find_first_bit(pnd->addrs, 64); addr < 64; - addr = find_next_bit(pnd->addrs, 64, 1+addr)) + for_each_set_bit(addr, pnd->addrs, 64) phonet_address_notify(RTM_DELADDR, dev, addr); kfree(pnd); } diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 2e6c7eb..fe2e708 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -141,8 +141,7 @@ static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb) continue; addr_idx = 0; - for (addr = find_first_bit(pnd->addrs, 64); addr < 64; - addr = find_next_bit(pnd->addrs, 64, 1+addr)) { + for_each_set_bit(addr, pnd->addrs, 64) { if (addr_idx++ < addr_start_idx) continue; -- cgit v0.10.2 From d287d66ee460b8d90b9ac840dd37f524a289bf97 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Thu, 11 Mar 2010 12:07:50 +0000 Subject: atm: use for_each_set_bit() Replace open-coded loop with for_each_set_bit(). Signed-off-by: Akinobu Mita Cc: Chas Williams Signed-off-by: Andrew Morton Signed-off-by: David S. Miller diff --git a/drivers/atm/lanai.c b/drivers/atm/lanai.c index 7fe7c32..23d9505 100644 --- a/drivers/atm/lanai.c +++ b/drivers/atm/lanai.c @@ -306,11 +306,10 @@ static void vci_bitfield_iterate(struct lanai_dev *lanai, const unsigned long *lp, void (*func)(struct lanai_dev *,vci_t vci)) { - vci_t vci = find_first_bit(lp, NUM_VCI); - while (vci < NUM_VCI) { + vci_t vci; + + for_each_set_bit(vci, lp, NUM_VCI) func(lanai, vci); - vci = find_next_bit(lp, NUM_VCI, vci + 1); - } } /* -------------------- BUFFER UTILITIES: */ -- cgit v0.10.2 From 6329da5f258ae752d1f33b549bae4f8a20b6871a Mon Sep 17 00:00:00 2001 From: Christoph Egger Date: Thu, 11 Mar 2010 12:07:52 +0000 Subject: obsolete config in kernel source: USE_INTERNAL_TIMER CONFIG_USE_INTERNAL_TIMER seems to be the remainings of some experiment. It is explicitely #undef-ed as not working, only referenced from one source file and rather aged. Hereby cleaning it from the kernel tree. Signed-off-by: Christoph Egger Acked-by: Roel Kluin Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: David S. Miller diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 551810f..980625f 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -65,7 +65,6 @@ #undef CONFIG_NETWINDER_TX_DMA_PROBLEMS /* Not needed */ #define CONFIG_NETWINDER_RX_DMA_PROBLEMS /* Must have this one! */ #endif -#undef CONFIG_USE_INTERNAL_TIMER /* Just cannot make that timer work */ #define CONFIG_USE_W977_PNP /* Currently needed */ #define PIO_MAX_SPEED 115200 @@ -533,25 +532,6 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, self->tx_buff.len = skb->len; mtt = irda_get_mtt(skb); -#ifdef CONFIG_USE_INTERNAL_TIMER - if (mtt > 50) { - /* Adjust for timer resolution */ - mtt /= 1000+1; - - /* Setup timer */ - switch_bank(iobase, SET4); - outb(mtt & 0xff, iobase+TMRL); - outb((mtt >> 8) & 0x0f, iobase+TMRH); - - /* Start timer */ - outb(IR_MSL_EN_TMR, iobase+IR_MSL); - self->io.direction = IO_XMIT; - - /* Enable timer interrupt */ - switch_bank(iobase, SET0); - outb(ICR_ETMRI, iobase+ICR); - } else { -#endif IRDA_DEBUG(4, "%s(%ld), mtt=%d\n", __func__ , jiffies, mtt); if (mtt) udelay(mtt); @@ -560,9 +540,6 @@ static netdev_tx_t w83977af_hard_xmit(struct sk_buff *skb, switch_bank(iobase, SET0); outb(ICR_EDMAI, iobase+ICR); w83977af_dma_write(self, iobase); -#ifdef CONFIG_USE_INTERNAL_TIMER - } -#endif } else { self->tx_buff.data = self->tx_buff.head; self->tx_buff.len = async_wrap_skb(skb, self->tx_buff.data, @@ -876,20 +853,7 @@ static int w83977af_dma_receive_complete(struct w83977af_ir *self) /* Check if we have transferred all data to memory */ switch_bank(iobase, SET0); if (inb(iobase+USR) & USR_RDR) { -#ifdef CONFIG_USE_INTERNAL_TIMER - /* Put this entry back in fifo */ - st_fifo->head--; - st_fifo->len++; - st_fifo->entries[st_fifo->head].status = status; - st_fifo->entries[st_fifo->head].len = len; - - /* Restore set register */ - outb(set, iobase+SSR); - - return FALSE; /* I'll be back! */ -#else udelay(80); /* Should be enough!? */ -#endif } skb = dev_alloc_skb(len+1); -- cgit v0.10.2 From 4d823be98c5b24d94c7f41a384a4bb60d7848ad5 Mon Sep 17 00:00:00 2001 From: Christoph Egger Date: Thu, 11 Mar 2010 12:07:54 +0000 Subject: obsolete config in kernel source: HSO_AUTOPM CONFIG_HSO_AUTOPM is set by KConfig / set in the Kernel source, makefiles and won't be ever set this way, therefor simply removing the protected code. Signed-off-by: Christoph Egger Cc: "David S. Miller" Signed-off-by: Andrew Morton Signed-off-by: David S. Miller diff --git a/drivers/net/usb/hso.c b/drivers/net/usb/hso.c index 6895f15..be0cc99 100644 --- a/drivers/net/usb/hso.c +++ b/drivers/net/usb/hso.c @@ -1155,9 +1155,6 @@ static void _hso_serial_set_termios(struct tty_struct *tty, static void hso_resubmit_rx_bulk_urb(struct hso_serial *serial, struct urb *urb) { int result; -#ifdef CONFIG_HSO_AUTOPM - usb_mark_last_busy(urb->dev); -#endif /* We are done with this URB, resubmit it. Prep the USB to wait for * another frame */ usb_fill_bulk_urb(urb, serial->parent->usb, -- cgit v0.10.2 From bc35b4e347c047fb1c665bb761ddb22482539f7f Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Sun, 14 Mar 2010 12:58:05 +0000 Subject: gigaset: avoid registering CAPI driver more than once Registering/unregistering the Gigaset CAPI driver when a device is connected/disconnected causes an Oops when disconnecting two Gigaset devices in a row, because the same capi_driver structure gets unregistered twice. Fix by making driver registration/unregistration a separate operation (empty in the ISDN4Linux case) called when the main module is loaded/unloaded. Impact: bugfix Signed-off-by: Tilman Schmidt Acked-by: Karsten Keil CC: stable@kernel.org Signed-off-by: David S. Miller diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c index 6643d65..4a31962 100644 --- a/drivers/isdn/gigaset/capi.c +++ b/drivers/isdn/gigaset/capi.c @@ -2191,36 +2191,24 @@ static const struct file_operations gigaset_proc_fops = { .release = single_release, }; -static struct capi_driver capi_driver_gigaset = { - .name = "gigaset", - .revision = "1.0", -}; - /** - * gigaset_isdn_register() - register to LL + * gigaset_isdn_regdev() - register device to LL * @cs: device descriptor structure. * @isdnid: device name. * - * Called by main module to register the device with the LL. - * * Return value: 1 for success, 0 for failure */ -int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) +int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid) { struct gigaset_capi_ctr *iif; int rc; - pr_info("Kernel CAPI interface\n"); - iif = kmalloc(sizeof(*iif), GFP_KERNEL); if (!iif) { pr_err("%s: out of memory\n", __func__); return 0; } - /* register driver with CAPI (ToDo: what for?) */ - register_capi_driver(&capi_driver_gigaset); - /* prepare controller structure */ iif->ctr.owner = THIS_MODULE; iif->ctr.driverdata = cs; @@ -2241,7 +2229,6 @@ int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) rc = attach_capi_ctr(&iif->ctr); if (rc) { pr_err("attach_capi_ctr failed (%d)\n", rc); - unregister_capi_driver(&capi_driver_gigaset); kfree(iif); return 0; } @@ -2252,17 +2239,36 @@ int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) } /** - * gigaset_isdn_unregister() - unregister from LL + * gigaset_isdn_unregdev() - unregister device from LL * @cs: device descriptor structure. - * - * Called by main module to unregister the device from the LL. */ -void gigaset_isdn_unregister(struct cardstate *cs) +void gigaset_isdn_unregdev(struct cardstate *cs) { struct gigaset_capi_ctr *iif = cs->iif; detach_capi_ctr(&iif->ctr); kfree(iif); cs->iif = NULL; +} + +static struct capi_driver capi_driver_gigaset = { + .name = "gigaset", + .revision = "1.0", +}; + +/** + * gigaset_isdn_regdrv() - register driver to LL + */ +void gigaset_isdn_regdrv(void) +{ + pr_info("Kernel CAPI interface\n"); + register_capi_driver(&capi_driver_gigaset); +} + +/** + * gigaset_isdn_unregdrv() - unregister driver from LL + */ +void gigaset_isdn_unregdrv(void) +{ unregister_capi_driver(&capi_driver_gigaset); } diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 85de339..bdc01cb 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -507,7 +507,7 @@ void gigaset_freecs(struct cardstate *cs) case 2: /* error in initcshw */ /* Deregister from LL */ make_invalid(cs, VALID_ID); - gigaset_isdn_unregister(cs); + gigaset_isdn_unregdev(cs); /* fall through */ case 1: /* error when registering to LL */ @@ -769,7 +769,7 @@ struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels, cs->cmdbytes = 0; gig_dbg(DEBUG_INIT, "setting up iif"); - if (!gigaset_isdn_register(cs, modulename)) { + if (!gigaset_isdn_regdev(cs, modulename)) { pr_err("error registering ISDN device\n"); goto error; } @@ -1205,11 +1205,13 @@ static int __init gigaset_init_module(void) gigaset_debuglevel = DEBUG_DEFAULT; pr_info(DRIVER_DESC DRIVER_DESC_DEBUG "\n"); + gigaset_isdn_regdrv(); return 0; } static void __exit gigaset_exit_module(void) { + gigaset_isdn_unregdrv(); } module_init(gigaset_init_module); diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h index 1875ab8..cdd144e 100644 --- a/drivers/isdn/gigaset/gigaset.h +++ b/drivers/isdn/gigaset/gigaset.h @@ -675,8 +675,10 @@ int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size); */ /* Called from common.c for setting up/shutting down with the ISDN subsystem */ -int gigaset_isdn_register(struct cardstate *cs, const char *isdnid); -void gigaset_isdn_unregister(struct cardstate *cs); +void gigaset_isdn_regdrv(void); +void gigaset_isdn_unregdrv(void); +int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid); +void gigaset_isdn_unregdev(struct cardstate *cs); /* Called from hardware module to indicate completion of an skb */ void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb); diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c index f0acb9d..c22e5ac 100644 --- a/drivers/isdn/gigaset/i4l.c +++ b/drivers/isdn/gigaset/i4l.c @@ -592,15 +592,13 @@ void gigaset_isdn_stop(struct cardstate *cs) } /** - * gigaset_isdn_register() - register to LL + * gigaset_isdn_regdev() - register to LL * @cs: device descriptor structure. * @isdnid: device name. * - * Called by main module to register the device with the LL. - * * Return value: 1 for success, 0 for failure */ -int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) +int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid) { isdn_if *iif; @@ -650,15 +648,29 @@ int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) } /** - * gigaset_isdn_unregister() - unregister from LL + * gigaset_isdn_unregdev() - unregister device from LL * @cs: device descriptor structure. - * - * Called by main module to unregister the device from the LL. */ -void gigaset_isdn_unregister(struct cardstate *cs) +void gigaset_isdn_unregdev(struct cardstate *cs) { gig_dbg(DEBUG_CMD, "sending UNLOAD"); gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD); kfree(cs->iif); cs->iif = NULL; } + +/** + * gigaset_isdn_regdrv() - register driver to LL + */ +void gigaset_isdn_regdrv(void) +{ + /* nothing to do */ +} + +/** + * gigaset_isdn_unregdrv() - unregister driver from LL + */ +void gigaset_isdn_unregdrv(void) +{ + /* nothing to do */ +} -- cgit v0.10.2 From 3a0a3a6b92edf181f849ebd8417122392ba73a96 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Sun, 14 Mar 2010 12:58:05 +0000 Subject: gigaset: correct clearing of at_state strings on RING In RING handling, clear the table of received parameter strings in a loop like everywhere else, instead of by enumeration which had already gotten out of sync. Impact: minor bugfix Signed-off-by: Tilman Schmidt Acked-by: Karsten Keil CC: stable@kernel.org Signed-off-by: David S. Miller diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index c8f89b7..206c380 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c @@ -1258,14 +1258,10 @@ static void do_action(int action, struct cardstate *cs, * note that bcs may be NULL if no B channel is free */ at_state2->ConState = 700; - kfree(at_state2->str_var[STR_NMBR]); - at_state2->str_var[STR_NMBR] = NULL; - kfree(at_state2->str_var[STR_ZCPN]); - at_state2->str_var[STR_ZCPN] = NULL; - kfree(at_state2->str_var[STR_ZBC]); - at_state2->str_var[STR_ZBC] = NULL; - kfree(at_state2->str_var[STR_ZHLC]); - at_state2->str_var[STR_ZHLC] = NULL; + for (i = 0; i < STR_NUM; ++i) { + kfree(at_state2->str_var[i]); + at_state2->str_var[i] = NULL; + } at_state2->int_var[VAR_ZCTP] = -1; spin_lock_irqsave(&cs->lock, flags); -- cgit v0.10.2 From 873a69a358a6b393fd8d9d92e193ec8895cac4d7 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Sun, 14 Mar 2010 12:58:05 +0000 Subject: gigaset: prune use of tty_buffer_request_room Calling tty_buffer_request_room() before tty_insert_flip_string() is unnecessary, costs CPU and for big buffers can mess up the multi-page allocation avoidance. Signed-off-by: Tilman Schmidt Acked-by: Karsten Keil CC: Alan Cox , stable@kernel.org Signed-off-by: David S. Miller diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c index a1bcbc2..f0dc6c9 100644 --- a/drivers/isdn/gigaset/interface.c +++ b/drivers/isdn/gigaset/interface.c @@ -628,7 +628,6 @@ void gigaset_if_receive(struct cardstate *cs, if (tty == NULL) gig_dbg(DEBUG_IF, "receive on closed device"); else { - tty_buffer_request_room(tty, len); tty_insert_flip_string(tty, buffer, len); tty_flip_buffer_push(tty); } -- cgit v0.10.2 From b8d689743106bab5c49dda87080e76aa78db8a56 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 14 Mar 2010 22:24:08 +0000 Subject: myri: remove dead code We can never reach the return statement. Signed-off-by: Dan Carpenter Signed-off-by: David S. Miller diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 676c513..e84dd3e 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -3687,7 +3687,6 @@ static void myri10ge_probe_slices(struct myri10ge_priv *mgp) if (status != 0) { dev_err(&mgp->pdev->dev, "failed reset\n"); goto abort_with_fw; - return; } mgp->max_intr_slots = cmd.data0 / sizeof(struct mcp_slot); -- cgit v0.10.2 From d14a0ebda7d3daede1a99c01527affb9ceaa4c22 Mon Sep 17 00:00:00 2001 From: Gerrit Renker Date: Sun, 14 Mar 2010 20:13:19 +0000 Subject: net-2.6 [Bug-Fix][dccp]: fix oops caused after failed initialisation dccp: fix panic caused by failed initialisation This fixes a kernel panic reported thanks to Andre Noll: if DCCP is compiled into the kernel and any out of the initialisation steps in net/dccp/proto.c:dccp_init() fail, a subsequent attempt to create a SOCK_DCCP socket will panic, since inet{,6}_create() are not prevented from creating DCCP sockets. This patch fixes the problem by propagating a failure in dccp_init() to dccp_v{4,6}_init_net(), and from there to dccp_v{4,6}_init(), so that the DCCP protocol is not made available if its initialisation fails. Signed-off-by: Gerrit Renker Signed-off-by: David S. Miller diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index b195c4f..4071eaf 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -998,11 +998,11 @@ static struct inet_protosw dccp_v4_protosw = { static int __net_init dccp_v4_init_net(struct net *net) { - int err; + if (dccp_hashinfo.bhash == NULL) + return -ESOCKTNOSUPPORT; - err = inet_ctl_sock_create(&net->dccp.v4_ctl_sk, PF_INET, - SOCK_DCCP, IPPROTO_DCCP, net); - return err; + return inet_ctl_sock_create(&net->dccp.v4_ctl_sk, PF_INET, + SOCK_DCCP, IPPROTO_DCCP, net); } static void __net_exit dccp_v4_exit_net(struct net *net) diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 1aec634..af3394d 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -1191,11 +1191,11 @@ static struct inet_protosw dccp_v6_protosw = { static int __net_init dccp_v6_init_net(struct net *net) { - int err; + if (dccp_hashinfo.bhash == NULL) + return -ESOCKTNOSUPPORT; - err = inet_ctl_sock_create(&net->dccp.v6_ctl_sk, PF_INET6, - SOCK_DCCP, IPPROTO_DCCP, net); - return err; + return inet_ctl_sock_create(&net->dccp.v6_ctl_sk, PF_INET6, + SOCK_DCCP, IPPROTO_DCCP, net); } static void __net_exit dccp_v6_exit_net(struct net *net) diff --git a/net/dccp/proto.c b/net/dccp/proto.c index 0ef7061..aa4cef3 100644 --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -1036,7 +1036,7 @@ static int __init dccp_init(void) FIELD_SIZEOF(struct sk_buff, cb)); rc = percpu_counter_init(&dccp_orphan_count, 0); if (rc) - goto out; + goto out_fail; rc = -ENOBUFS; inet_hashinfo_init(&dccp_hashinfo); dccp_hashinfo.bind_bucket_cachep = @@ -1125,8 +1125,9 @@ static int __init dccp_init(void) goto out_sysctl_exit; dccp_timestamping_init(); -out: - return rc; + + return 0; + out_sysctl_exit: dccp_sysctl_exit(); out_ackvec_exit: @@ -1135,18 +1136,19 @@ out_free_dccp_mib: dccp_mib_exit(); out_free_dccp_bhash: free_pages((unsigned long)dccp_hashinfo.bhash, bhash_order); - dccp_hashinfo.bhash = NULL; out_free_dccp_locks: inet_ehash_locks_free(&dccp_hashinfo); out_free_dccp_ehash: free_pages((unsigned long)dccp_hashinfo.ehash, ehash_order); - dccp_hashinfo.ehash = NULL; out_free_bind_bucket_cachep: kmem_cache_destroy(dccp_hashinfo.bind_bucket_cachep); - dccp_hashinfo.bind_bucket_cachep = NULL; out_free_percpu: percpu_counter_destroy(&dccp_orphan_count); - goto out; +out_fail: + dccp_hashinfo.bhash = NULL; + dccp_hashinfo.ehash = NULL; + dccp_hashinfo.bind_bucket_cachep = NULL; + return rc; } static void __exit dccp_fini(void) -- cgit v0.10.2 From d00561a2f64b381aefb41f4a140ff5dc373b52e7 Mon Sep 17 00:00:00 2001 From: Lars Ellenberg Date: Mon, 15 Mar 2010 19:09:28 -0700 Subject: ISDN: Add PCI ID for HFC-2S/4S Beronet Card PCIe A few subdevice IDs seem to have been dropped when hfc_multi was included upstream, just compare the list at http://www.openvox.cn/viewvc/misdn/trunk/hfc_multi.c?revision=75&view=annotate#l175 with the IDs in drivers/isdn/hardware/mISDN/hfcmulti.c Added PCIe 2 Port card and LED settings (same as PCI) Do not use /KKe Signed-off-by: Lars Ellenberg Signed-off-by: Karsten Keil Signed-off-by: David S. Miller diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c index ad36df9..8affba3 100644 --- a/drivers/isdn/hardware/mISDN/hfcmulti.c +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -5265,6 +5265,8 @@ static const struct hm_map hfcm_map[] = { /*31*/ {VENDOR_CCD, "XHFC-4S Speech Design", 5, 4, 0, 0, 0, 0, HFC_IO_MODE_EMBSD, XHFC_IRQ}, /*32*/ {VENDOR_JH, "HFC-8S (junghanns)", 8, 8, 1, 0, 0, 0, 0, 0}, +/*33*/ {VENDOR_BN, "HFC-2S Beronet Card PCIe", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, +/*34*/ {VENDOR_BN, "HFC-4S Beronet Card PCIe", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, }; #undef H @@ -5300,6 +5302,10 @@ static struct pci_device_id hfmultipci_ids[] __devinitdata = { PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + 0xb761, 0, 0, H(33)}, /* BN2S PCIe */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + 0xb762, 0, 0, H(34)}, /* BN4S PCIe */ /* Cards with HFC-8S Chip */ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, -- cgit v0.10.2 From 0821ec55bb1382dd3b9476dc15d5dbbb19f0c3fd Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 15 Mar 2010 20:38:25 -0700 Subject: bridge: Move NULL mdb check into br_mdb_ip_get Since all callers of br_mdb_ip_get need to check whether the hash table is NULL, this patch moves the check into the function. This fixes the two callers (query/leave handler) that didn't check it. Reported-by: Michael Braun Signed-off-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index fd96a8d..398221e 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -49,22 +49,23 @@ static struct net_bridge_mdb_entry *__br_mdb_ip_get( static struct net_bridge_mdb_entry *br_mdb_ip_get( struct net_bridge_mdb_htable *mdb, __be32 dst) { + if (!mdb) + return NULL; + return __br_mdb_ip_get(mdb, dst, br_ip_hash(mdb, dst)); } struct net_bridge_mdb_entry *br_mdb_get(struct net_bridge *br, struct sk_buff *skb) { - struct net_bridge_mdb_htable *mdb = br->mdb; - - if (!mdb || br->multicast_disabled) + if (br->multicast_disabled) return NULL; switch (skb->protocol) { case htons(ETH_P_IP): if (BR_INPUT_SKB_CB(skb)->igmp) break; - return br_mdb_ip_get(mdb, ip_hdr(skb)->daddr); + return br_mdb_ip_get(br->mdb, ip_hdr(skb)->daddr); } return NULL; -- cgit v0.10.2 From 7f7708f0055e49e331f267700aa8b2ee879f004c Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Tue, 16 Mar 2010 00:26:22 -0700 Subject: bridge: Fix br_forward crash in promiscuous mode From: Michael Braun bridge: Fix br_forward crash in promiscuous mode It's a linux-next kernel from 2010-03-12 on an x86 system and it OOPs in the bridge module in br_pass_frame_up (called by br_handle_frame_finish) because brdev cannot be dereferenced (its set to a non-null value). Adding some BUG_ON statements revealed that BR_INPUT_SKB_CB(skb)->brdev == br-dev (as set in br_handle_frame_finish first) only holds until br_forward is called. The next call to br_pass_frame_up then fails. Digging deeper it seems that br_forward either frees the skb or passes it to NF_HOOK which will in turn take care of freeing the skb. The same is holds for br_pass_frame_ip. So it seems as if two independent skb allocations are required. As far as I can see, commit b33084be192ee1e347d98bb5c9e38a53d98d35e2 ("bridge: Avoid unnecessary clone on forward path") removed skb duplication and so likely causes this crash. This crash does not happen on 2.6.33. I've therefore modified br_forward the same way br_flood has been modified so that the skb is not freed if skb0 is going to be used and I can confirm that the attached patch resolves the issue for me. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index d61e6f7..8347916 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -19,6 +19,10 @@ #include #include "br_private.h" +static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb, + void (*__packet_hook)(const struct net_bridge_port *p, + struct sk_buff *skb)); + /* Don't forward packets to originating port or forwarding diasabled */ static inline int should_deliver(const struct net_bridge_port *p, const struct sk_buff *skb) @@ -94,14 +98,18 @@ void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb) } /* called with rcu_read_lock */ -void br_forward(const struct net_bridge_port *to, struct sk_buff *skb) +void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk_buff *skb0) { if (should_deliver(to, skb)) { - __br_forward(to, skb); + if (skb0) + deliver_clone(to, skb, __br_forward); + else + __br_forward(to, skb); return; } - kfree_skb(skb); + if (!skb0) + kfree_skb(skb); } static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb, diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 53b3985..08a72e6 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -90,7 +90,7 @@ int br_handle_frame_finish(struct sk_buff *skb) if (skb) { if (dst) - br_forward(dst->dst, skb); + br_forward(dst->dst, skb, skb2); else br_flood_forward(br, skb, skb2); } diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index fef0384..bfb8feb 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -252,7 +252,7 @@ extern void br_deliver(const struct net_bridge_port *to, struct sk_buff *skb); extern int br_dev_queue_push_xmit(struct sk_buff *skb); extern void br_forward(const struct net_bridge_port *to, - struct sk_buff *skb); + struct sk_buff *skb, struct sk_buff *skb0); extern int br_forward_finish(struct sk_buff *skb); extern void br_flood_deliver(struct net_bridge *br, struct sk_buff *skb); extern void br_flood_forward(struct net_bridge *br, struct sk_buff *skb, -- cgit v0.10.2 From 6ad34145cf809384359fe513481d6e16638a57a3 Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Tue, 16 Mar 2010 07:04:01 +0000 Subject: gigaset: correct range checking off by one error Correct a potential array overrun due to an off by one error in the range check on the CAPI CONNECT_REQ CIPValue parameter. Found and reported by Dan Carpenter using smatch. Impact: bugfix Signed-off-by: Tilman Schmidt Signed-off-by: David S. Miller diff --git a/drivers/isdn/gigaset/capi.c b/drivers/isdn/gigaset/capi.c index 4a31962..0220c19 100644 --- a/drivers/isdn/gigaset/capi.c +++ b/drivers/isdn/gigaset/capi.c @@ -1301,7 +1301,7 @@ static void do_connect_req(struct gigaset_capi_ctr *iif, } /* check parameter: CIP Value */ - if (cmsg->CIPValue > ARRAY_SIZE(cip2bchlc) || + if (cmsg->CIPValue >= ARRAY_SIZE(cip2bchlc) || (cmsg->CIPValue > 0 && cip2bchlc[cmsg->CIPValue].bc == NULL)) { dev_notice(cs->dev, "%s: unknown CIP value %d\n", "CONNECT_REQ", cmsg->CIPValue); -- cgit v0.10.2 From edee39321be9f88c47627379e8abadfce0508768 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 16 Mar 2010 04:53:50 +0000 Subject: NET: ksz884x, fix lock imbalance Stanse found that one error path (when alloc_skb fails) in netdev_tx omits to unlock hw_priv->hwlock. Fix that. Signed-off-by: Jiri Slaby Cc: Tristram Ha Cc: David S. Miller Signed-off-by: David S. Miller diff --git a/drivers/net/ksz884x.c b/drivers/net/ksz884x.c index 7264a3e..0f59099 100644 --- a/drivers/net/ksz884x.c +++ b/drivers/net/ksz884x.c @@ -4899,8 +4899,10 @@ static int netdev_tx(struct sk_buff *skb, struct net_device *dev) struct sk_buff *org_skb = skb; skb = dev_alloc_skb(org_skb->len); - if (!skb) - return NETDEV_TX_BUSY; + if (!skb) { + rc = NETDEV_TX_BUSY; + goto unlock; + } skb_copy_and_csum_dev(org_skb, skb->data); org_skb->ip_summed = 0; skb->len = org_skb->len; @@ -4914,7 +4916,7 @@ static int netdev_tx(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); rc = NETDEV_TX_BUSY; } - +unlock: spin_unlock_irq(&hw_priv->hwlock); return rc; -- cgit v0.10.2 From c5e49fb5189dbce00bc37f27c83ab0f9e4748bc6 Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Tue, 16 Mar 2010 05:27:40 +0000 Subject: ne: Do not use slashes in irq name string This patch fixes following warning introduced by commit 12bac0d9f4dbf3445a0319beee848d15fa32775e ("proc: warn on non-existing proc entries"): WARNING: at /work/mips-linux/make/linux/fs/proc/generic.c:316 __xlate_proc_name+0xe0/0xe8() name 'RBHMA4X00/RTL8019' Signed-off-by: Atsushi Nemoto Signed-off-by: David S. Miller diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 992dbff..f4347f8 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -142,7 +142,7 @@ bad_clone_list[] __initdata = { {"PCM-4823", "PCM-4823", {0x00, 0xc0, 0x6c}}, /* Broken Advantech MoBo */ {"REALTEK", "RTL8019", {0x00, 0x00, 0xe8}}, /* no-name with Realtek chip */ #ifdef CONFIG_MACH_TX49XX - {"RBHMA4X00-RTL8019", "RBHMA4X00/RTL8019", {0x00, 0x60, 0x0a}}, /* Toshiba built-in */ + {"RBHMA4X00-RTL8019", "RBHMA4X00-RTL8019", {0x00, 0x60, 0x0a}}, /* Toshiba built-in */ #endif {"LCS-8834", "LCS-8836", {0x04, 0x04, 0x37}}, /* ShinyNet (SET) */ {NULL,} -- cgit v0.10.2 From d0cad871703b898a442e4049c532ec39168e5b57 Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Tue, 16 Mar 2010 08:46:46 +0000 Subject: smsc75xx: SMSC LAN75xx USB gigabit ethernet adapter driver This patch adds a driver for SMSC's LAN7500 family of USB 2.0 to gigabit ethernet adapters. It's loosely based on the smsc95xx driver but the device registers for LAN7500 are completely different. Signed-off-by: Steve Glendinning Signed-off-by: David S. Miller diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 32d9356..ba56ce4 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -204,6 +204,14 @@ config USB_NET_DM9601 This option adds support for Davicom DM9601 based USB 1.1 10/100 Ethernet adapters. +config USB_NET_SMSC75XX + tristate "SMSC LAN75XX based USB 2.0 gigabit ethernet devices" + depends on USB_USBNET + select CRC32 + help + This option adds support for SMSC LAN95XX based USB 2.0 + Gigabit Ethernet adapters. + config USB_NET_SMSC95XX tristate "SMSC LAN95XX based USB 2.0 10/100 ethernet devices" depends on USB_USBNET diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile index e17afb7..82ea629 100644 --- a/drivers/net/usb/Makefile +++ b/drivers/net/usb/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_NET_AX8817X) += asix.o obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o obj-$(CONFIG_USB_NET_CDC_EEM) += cdc_eem.o obj-$(CONFIG_USB_NET_DM9601) += dm9601.o +obj-$(CONFIG_USB_NET_SMSC75XX) += smsc75xx.o obj-$(CONFIG_USB_NET_SMSC95XX) += smsc95xx.o obj-$(CONFIG_USB_NET_GL620A) += gl620a.o obj-$(CONFIG_USB_NET_NET1080) += net1080.o diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c new file mode 100644 index 0000000..300e3e7 --- /dev/null +++ b/drivers/net/usb/smsc75xx.c @@ -0,0 +1,1288 @@ + /*************************************************************************** + * + * Copyright (C) 2007-2010 SMSC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + *****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "smsc75xx.h" + +#define SMSC_CHIPNAME "smsc75xx" +#define SMSC_DRIVER_VERSION "1.0.0" +#define HS_USB_PKT_SIZE (512) +#define FS_USB_PKT_SIZE (64) +#define DEFAULT_HS_BURST_CAP_SIZE (16 * 1024 + 5 * HS_USB_PKT_SIZE) +#define DEFAULT_FS_BURST_CAP_SIZE (6 * 1024 + 33 * FS_USB_PKT_SIZE) +#define DEFAULT_BULK_IN_DELAY (0x00002000) +#define MAX_SINGLE_PACKET_SIZE (9000) +#define LAN75XX_EEPROM_MAGIC (0x7500) +#define EEPROM_MAC_OFFSET (0x01) +#define DEFAULT_TX_CSUM_ENABLE (true) +#define DEFAULT_RX_CSUM_ENABLE (true) +#define DEFAULT_TSO_ENABLE (true) +#define SMSC75XX_INTERNAL_PHY_ID (1) +#define SMSC75XX_TX_OVERHEAD (8) +#define MAX_RX_FIFO_SIZE (20 * 1024) +#define MAX_TX_FIFO_SIZE (12 * 1024) +#define USB_VENDOR_ID_SMSC (0x0424) +#define USB_PRODUCT_ID_LAN7500 (0x7500) +#define USB_PRODUCT_ID_LAN7505 (0x7505) + +#define check_warn(ret, fmt, args...) \ + ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) + +#define check_warn_return(ret, fmt, args...) \ + ({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); return ret; } }) + +#define check_warn_goto_done(ret, fmt, args...) \ + ({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); goto done; } }) + +struct smsc75xx_priv { + struct usbnet *dev; + u32 rfe_ctl; + u32 multicast_hash_table[DP_SEL_VHF_HASH_LEN]; + bool use_rx_csum; + struct mutex dataport_mutex; + spinlock_t rfe_ctl_lock; + struct work_struct set_multicast; +}; + +struct usb_context { + struct usb_ctrlrequest req; + struct usbnet *dev; +}; + +static int turbo_mode = true; +module_param(turbo_mode, bool, 0644); +MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); + +static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index, + u32 *data) +{ + u32 *buf = kmalloc(4, GFP_KERNEL); + int ret; + + BUG_ON(!dev); + + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + USB_VENDOR_REQUEST_READ_REGISTER, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, buf, 4, USB_CTRL_GET_TIMEOUT); + + if (unlikely(ret < 0)) + netdev_warn(dev->net, + "Failed to read register index 0x%08x", index); + + le32_to_cpus(buf); + *data = *buf; + kfree(buf); + + return ret; +} + +static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, + u32 data) +{ + u32 *buf = kmalloc(4, GFP_KERNEL); + int ret; + + BUG_ON(!dev); + + if (!buf) + return -ENOMEM; + + *buf = data; + cpu_to_le32s(buf); + + ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + USB_VENDOR_REQUEST_WRITE_REGISTER, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 00, index, buf, 4, USB_CTRL_SET_TIMEOUT); + + if (unlikely(ret < 0)) + netdev_warn(dev->net, + "Failed to write register index 0x%08x", index); + + kfree(buf); + + return ret; +} + +/* Loop until the read is completed with timeout + * called with phy_mutex held */ +static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) +{ + unsigned long start_time = jiffies; + u32 val; + int ret; + + do { + ret = smsc75xx_read_reg(dev, MII_ACCESS, &val); + check_warn_return(ret, "Error reading MII_ACCESS"); + + if (!(val & MII_ACCESS_BUSY)) + return 0; + } while (!time_after(jiffies, start_time + HZ)); + + return -EIO; +} + +static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) +{ + struct usbnet *dev = netdev_priv(netdev); + u32 val, addr; + int ret; + + mutex_lock(&dev->phy_mutex); + + /* confirm MII not busy */ + ret = smsc75xx_phy_wait_not_busy(dev); + check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_read"); + + /* set the address, index & direction (read from PHY) */ + phy_id &= dev->mii.phy_id_mask; + idx &= dev->mii.reg_num_mask; + addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) + | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) + | MII_ACCESS_READ; + ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); + check_warn_goto_done(ret, "Error writing MII_ACCESS"); + + ret = smsc75xx_phy_wait_not_busy(dev); + check_warn_goto_done(ret, "Timed out reading MII reg %02X", idx); + + ret = smsc75xx_read_reg(dev, MII_DATA, &val); + check_warn_goto_done(ret, "Error reading MII_DATA"); + + ret = (u16)(val & 0xFFFF); + +done: + mutex_unlock(&dev->phy_mutex); + return ret; +} + +static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, + int regval) +{ + struct usbnet *dev = netdev_priv(netdev); + u32 val, addr; + int ret; + + mutex_lock(&dev->phy_mutex); + + /* confirm MII not busy */ + ret = smsc75xx_phy_wait_not_busy(dev); + check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_write"); + + val = regval; + ret = smsc75xx_write_reg(dev, MII_DATA, val); + check_warn_goto_done(ret, "Error writing MII_DATA"); + + /* set the address, index & direction (write to PHY) */ + phy_id &= dev->mii.phy_id_mask; + idx &= dev->mii.reg_num_mask; + addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) + | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) + | MII_ACCESS_WRITE; + ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); + check_warn_goto_done(ret, "Error writing MII_ACCESS"); + + ret = smsc75xx_phy_wait_not_busy(dev); + check_warn_goto_done(ret, "Timed out writing MII reg %02X", idx); + +done: + mutex_unlock(&dev->phy_mutex); +} + +static int smsc75xx_wait_eeprom(struct usbnet *dev) +{ + unsigned long start_time = jiffies; + u32 val; + int ret; + + do { + ret = smsc75xx_read_reg(dev, E2P_CMD, &val); + check_warn_return(ret, "Error reading E2P_CMD"); + + if (!(val & E2P_CMD_BUSY) || (val & E2P_CMD_TIMEOUT)) + break; + udelay(40); + } while (!time_after(jiffies, start_time + HZ)); + + if (val & (E2P_CMD_TIMEOUT | E2P_CMD_BUSY)) { + netdev_warn(dev->net, "EEPROM read operation timeout"); + return -EIO; + } + + return 0; +} + +static int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev) +{ + unsigned long start_time = jiffies; + u32 val; + int ret; + + do { + ret = smsc75xx_read_reg(dev, E2P_CMD, &val); + check_warn_return(ret, "Error reading E2P_CMD"); + + if (!(val & E2P_CMD_BUSY)) + return 0; + + udelay(40); + } while (!time_after(jiffies, start_time + HZ)); + + netdev_warn(dev->net, "EEPROM is busy"); + return -EIO; +} + +static int smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, + u8 *data) +{ + u32 val; + int i, ret; + + BUG_ON(!dev); + BUG_ON(!data); + + ret = smsc75xx_eeprom_confirm_not_busy(dev); + if (ret) + return ret; + + for (i = 0; i < length; i++) { + val = E2P_CMD_BUSY | E2P_CMD_READ | (offset & E2P_CMD_ADDR); + ret = smsc75xx_write_reg(dev, E2P_CMD, val); + check_warn_return(ret, "Error writing E2P_CMD"); + + ret = smsc75xx_wait_eeprom(dev); + if (ret < 0) + return ret; + + ret = smsc75xx_read_reg(dev, E2P_DATA, &val); + check_warn_return(ret, "Error reading E2P_DATA"); + + data[i] = val & 0xFF; + offset++; + } + + return 0; +} + +static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, + u8 *data) +{ + u32 val; + int i, ret; + + BUG_ON(!dev); + BUG_ON(!data); + + ret = smsc75xx_eeprom_confirm_not_busy(dev); + if (ret) + return ret; + + /* Issue write/erase enable command */ + val = E2P_CMD_BUSY | E2P_CMD_EWEN; + ret = smsc75xx_write_reg(dev, E2P_CMD, val); + check_warn_return(ret, "Error writing E2P_CMD"); + + ret = smsc75xx_wait_eeprom(dev); + if (ret < 0) + return ret; + + for (i = 0; i < length; i++) { + + /* Fill data register */ + val = data[i]; + ret = smsc75xx_write_reg(dev, E2P_DATA, val); + check_warn_return(ret, "Error writing E2P_DATA"); + + /* Send "write" command */ + val = E2P_CMD_BUSY | E2P_CMD_WRITE | (offset & E2P_CMD_ADDR); + ret = smsc75xx_write_reg(dev, E2P_CMD, val); + check_warn_return(ret, "Error writing E2P_CMD"); + + ret = smsc75xx_wait_eeprom(dev); + if (ret < 0) + return ret; + + offset++; + } + + return 0; +} + +static int smsc75xx_dataport_wait_not_busy(struct usbnet *dev) +{ + int i, ret; + + for (i = 0; i < 100; i++) { + u32 dp_sel; + ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); + check_warn_return(ret, "Error reading DP_SEL"); + + if (dp_sel & DP_SEL_DPRDY) + return 0; + + udelay(40); + } + + netdev_warn(dev->net, "smsc75xx_dataport_wait_not_busy timed out"); + + return -EIO; +} + +static int smsc75xx_dataport_write(struct usbnet *dev, u32 ram_select, u32 addr, + u32 length, u32 *buf) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u32 dp_sel; + int i, ret; + + mutex_lock(&pdata->dataport_mutex); + + ret = smsc75xx_dataport_wait_not_busy(dev); + check_warn_goto_done(ret, "smsc75xx_dataport_write busy on entry"); + + ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); + check_warn_goto_done(ret, "Error reading DP_SEL"); + + dp_sel &= ~DP_SEL_RSEL; + dp_sel |= ram_select; + ret = smsc75xx_write_reg(dev, DP_SEL, dp_sel); + check_warn_goto_done(ret, "Error writing DP_SEL"); + + for (i = 0; i < length; i++) { + ret = smsc75xx_write_reg(dev, DP_ADDR, addr + i); + check_warn_goto_done(ret, "Error writing DP_ADDR"); + + ret = smsc75xx_write_reg(dev, DP_DATA, buf[i]); + check_warn_goto_done(ret, "Error writing DP_DATA"); + + ret = smsc75xx_write_reg(dev, DP_CMD, DP_CMD_WRITE); + check_warn_goto_done(ret, "Error writing DP_CMD"); + + ret = smsc75xx_dataport_wait_not_busy(dev); + check_warn_goto_done(ret, "smsc75xx_dataport_write timeout"); + } + +done: + mutex_unlock(&pdata->dataport_mutex); + return ret; +} + +/* returns hash bit number for given MAC address */ +static u32 smsc75xx_hash(char addr[ETH_ALEN]) +{ + return (ether_crc(ETH_ALEN, addr) >> 23) & 0x1ff; +} + +static void smsc75xx_deferred_multicast_write(struct work_struct *param) +{ + struct smsc75xx_priv *pdata = + container_of(param, struct smsc75xx_priv, set_multicast); + struct usbnet *dev = pdata->dev; + int ret; + + netif_dbg(dev, drv, dev->net, "deferred multicast write 0x%08x", + pdata->rfe_ctl); + + smsc75xx_dataport_write(dev, DP_SEL_VHF, DP_SEL_VHF_VLAN_LEN, + DP_SEL_VHF_HASH_LEN, pdata->multicast_hash_table); + + ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); + check_warn(ret, "Error writing RFE_CRL"); +} + +static void smsc75xx_set_multicast(struct net_device *netdev) +{ + struct usbnet *dev = netdev_priv(netdev); + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + unsigned long flags; + int i; + + spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); + + pdata->rfe_ctl &= + ~(RFE_CTL_AU | RFE_CTL_AM | RFE_CTL_DPF | RFE_CTL_MHF); + pdata->rfe_ctl |= RFE_CTL_AB; + + for (i = 0; i < DP_SEL_VHF_HASH_LEN; i++) + pdata->multicast_hash_table[i] = 0; + + if (dev->net->flags & IFF_PROMISC) { + netif_dbg(dev, drv, dev->net, "promiscuous mode enabled"); + pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_AU; + } else if (dev->net->flags & IFF_ALLMULTI) { + netif_dbg(dev, drv, dev->net, "receive all multicast enabled"); + pdata->rfe_ctl |= RFE_CTL_AM | RFE_CTL_DPF; + } else if (!netdev_mc_empty(dev->net)) { + struct dev_mc_list *mc_list; + + netif_dbg(dev, drv, dev->net, "receive multicast hash filter"); + + pdata->rfe_ctl |= RFE_CTL_MHF | RFE_CTL_DPF; + + netdev_for_each_mc_addr(mc_list, netdev) { + u32 bitnum = smsc75xx_hash(mc_list->dmi_addr); + pdata->multicast_hash_table[bitnum / 32] |= + (1 << (bitnum % 32)); + } + } else { + netif_dbg(dev, drv, dev->net, "receive own packets only"); + pdata->rfe_ctl |= RFE_CTL_DPF; + } + + spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); + + /* defer register writes to a sleepable context */ + schedule_work(&pdata->set_multicast); +} + +static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex, + u16 lcladv, u16 rmtadv) +{ + u32 flow = 0, fct_flow = 0; + int ret; + + if (duplex == DUPLEX_FULL) { + u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); + + if (cap & FLOW_CTRL_TX) { + flow = (FLOW_TX_FCEN | 0xFFFF); + /* set fct_flow thresholds to 20% and 80% */ + fct_flow = (8 << 8) | 32; + } + + if (cap & FLOW_CTRL_RX) + flow |= FLOW_RX_FCEN; + + netif_dbg(dev, link, dev->net, "rx pause %s, tx pause %s", + (cap & FLOW_CTRL_RX ? "enabled" : "disabled"), + (cap & FLOW_CTRL_TX ? "enabled" : "disabled")); + } else { + netif_dbg(dev, link, dev->net, "half duplex"); + } + + ret = smsc75xx_write_reg(dev, FLOW, flow); + check_warn_return(ret, "Error writing FLOW"); + + ret = smsc75xx_write_reg(dev, FCT_FLOW, fct_flow); + check_warn_return(ret, "Error writing FCT_FLOW"); + + return 0; +} + +static int smsc75xx_link_reset(struct usbnet *dev) +{ + struct mii_if_info *mii = &dev->mii; + struct ethtool_cmd ecmd; + u16 lcladv, rmtadv; + int ret; + + /* clear interrupt status */ + ret = smsc75xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); + check_warn_return(ret, "Error reading PHY_INT_SRC"); + + ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); + check_warn_return(ret, "Error writing INT_STS"); + + mii_check_media(mii, 1, 1); + mii_ethtool_gset(&dev->mii, &ecmd); + lcladv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_ADVERTISE); + rmtadv = smsc75xx_mdio_read(dev->net, mii->phy_id, MII_LPA); + + netif_dbg(dev, link, dev->net, "speed: %d duplex: %d lcladv: %04x" + " rmtadv: %04x", ecmd.speed, ecmd.duplex, lcladv, rmtadv); + + return smsc75xx_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); +} + +static void smsc75xx_status(struct usbnet *dev, struct urb *urb) +{ + u32 intdata; + + if (urb->actual_length != 4) { + netdev_warn(dev->net, + "unexpected urb length %d", urb->actual_length); + return; + } + + memcpy(&intdata, urb->transfer_buffer, 4); + le32_to_cpus(&intdata); + + netif_dbg(dev, link, dev->net, "intdata: 0x%08X", intdata); + + if (intdata & INT_ENP_PHY_INT) + usbnet_defer_kevent(dev, EVENT_LINK_RESET); + else + netdev_warn(dev->net, + "unexpected interrupt, intdata=0x%08X", intdata); +} + +/* Enable or disable Rx checksum offload engine */ +static int smsc75xx_set_rx_csum_offload(struct usbnet *dev) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + unsigned long flags; + int ret; + + spin_lock_irqsave(&pdata->rfe_ctl_lock, flags); + + if (pdata->use_rx_csum) + pdata->rfe_ctl |= RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM; + else + pdata->rfe_ctl &= ~(RFE_CTL_TCPUDP_CKM | RFE_CTL_IP_CKM); + + spin_unlock_irqrestore(&pdata->rfe_ctl_lock, flags); + + ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); + check_warn_return(ret, "Error writing RFE_CTL"); + + return 0; +} + +static int smsc75xx_ethtool_get_eeprom_len(struct net_device *net) +{ + return MAX_EEPROM_SIZE; +} + +static int smsc75xx_ethtool_get_eeprom(struct net_device *netdev, + struct ethtool_eeprom *ee, u8 *data) +{ + struct usbnet *dev = netdev_priv(netdev); + + ee->magic = LAN75XX_EEPROM_MAGIC; + + return smsc75xx_read_eeprom(dev, ee->offset, ee->len, data); +} + +static int smsc75xx_ethtool_set_eeprom(struct net_device *netdev, + struct ethtool_eeprom *ee, u8 *data) +{ + struct usbnet *dev = netdev_priv(netdev); + + if (ee->magic != LAN75XX_EEPROM_MAGIC) { + netdev_warn(dev->net, + "EEPROM: magic value mismatch: 0x%x", ee->magic); + return -EINVAL; + } + + return smsc75xx_write_eeprom(dev, ee->offset, ee->len, data); +} + +static u32 smsc75xx_ethtool_get_rx_csum(struct net_device *netdev) +{ + struct usbnet *dev = netdev_priv(netdev); + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + + return pdata->use_rx_csum; +} + +static int smsc75xx_ethtool_set_rx_csum(struct net_device *netdev, u32 val) +{ + struct usbnet *dev = netdev_priv(netdev); + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + + pdata->use_rx_csum = !!val; + + return smsc75xx_set_rx_csum_offload(dev); +} + +static int smsc75xx_ethtool_set_tso(struct net_device *netdev, u32 data) +{ + if (data) + netdev->features |= NETIF_F_TSO | NETIF_F_TSO6; + else + netdev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6); + + return 0; +} + +static const struct ethtool_ops smsc75xx_ethtool_ops = { + .get_link = usbnet_get_link, + .nway_reset = usbnet_nway_reset, + .get_drvinfo = usbnet_get_drvinfo, + .get_msglevel = usbnet_get_msglevel, + .set_msglevel = usbnet_set_msglevel, + .get_settings = usbnet_get_settings, + .set_settings = usbnet_set_settings, + .get_eeprom_len = smsc75xx_ethtool_get_eeprom_len, + .get_eeprom = smsc75xx_ethtool_get_eeprom, + .set_eeprom = smsc75xx_ethtool_set_eeprom, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = ethtool_op_set_tx_hw_csum, + .get_rx_csum = smsc75xx_ethtool_get_rx_csum, + .set_rx_csum = smsc75xx_ethtool_set_rx_csum, + .get_tso = ethtool_op_get_tso, + .set_tso = smsc75xx_ethtool_set_tso, +}; + +static int smsc75xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) +{ + struct usbnet *dev = netdev_priv(netdev); + + if (!netif_running(netdev)) + return -EINVAL; + + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); +} + +static void smsc75xx_init_mac_address(struct usbnet *dev) +{ + /* try reading mac address from EEPROM */ + if (smsc75xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, + dev->net->dev_addr) == 0) { + if (is_valid_ether_addr(dev->net->dev_addr)) { + /* eeprom values are valid so use them */ + netif_dbg(dev, ifup, dev->net, + "MAC address read from EEPROM"); + return; + } + } + + /* no eeprom, or eeprom values are invalid. generate random MAC */ + random_ether_addr(dev->net->dev_addr); + netif_dbg(dev, ifup, dev->net, "MAC address set to random_ether_addr"); +} + +static int smsc75xx_set_mac_address(struct usbnet *dev) +{ + u32 addr_lo = dev->net->dev_addr[0] | dev->net->dev_addr[1] << 8 | + dev->net->dev_addr[2] << 16 | dev->net->dev_addr[3] << 24; + u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8; + + int ret = smsc75xx_write_reg(dev, RX_ADDRH, addr_hi); + check_warn_return(ret, "Failed to write RX_ADDRH: %d", ret); + + ret = smsc75xx_write_reg(dev, RX_ADDRL, addr_lo); + check_warn_return(ret, "Failed to write RX_ADDRL: %d", ret); + + addr_hi |= ADDR_FILTX_FB_VALID; + ret = smsc75xx_write_reg(dev, ADDR_FILTX, addr_hi); + check_warn_return(ret, "Failed to write ADDR_FILTX: %d", ret); + + ret = smsc75xx_write_reg(dev, ADDR_FILTX + 4, addr_lo); + check_warn_return(ret, "Failed to write ADDR_FILTX+4: %d", ret); + + return 0; +} + +static int smsc75xx_phy_initialize(struct usbnet *dev) +{ + int bmcr, timeout = 0; + + /* Initialize MII structure */ + dev->mii.dev = dev->net; + dev->mii.mdio_read = smsc75xx_mdio_read; + dev->mii.mdio_write = smsc75xx_mdio_write; + dev->mii.phy_id_mask = 0x1f; + dev->mii.reg_num_mask = 0x1f; + dev->mii.phy_id = SMSC75XX_INTERNAL_PHY_ID; + + /* reset phy and wait for reset to complete */ + smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); + + do { + msleep(10); + bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); + check_warn_return(bmcr, "Error reading MII_BMCR"); + timeout++; + } while ((bmcr & MII_BMCR) && (timeout < 100)); + + if (timeout >= 100) { + netdev_warn(dev->net, "timeout on PHY Reset"); + return -EIO; + } + + smsc75xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | + ADVERTISE_PAUSE_ASYM); + + /* read to clear */ + smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); + check_warn_return(bmcr, "Error reading PHY_INT_SRC"); + + smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, + PHY_INT_MASK_DEFAULT); + mii_nway_restart(&dev->mii); + + netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); + return 0; +} + +static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size) +{ + int ret = 0; + u32 buf; + bool rxenabled; + + ret = smsc75xx_read_reg(dev, MAC_RX, &buf); + check_warn_return(ret, "Failed to read MAC_RX: %d", ret); + + rxenabled = ((buf & MAC_RX_RXEN) != 0); + + if (rxenabled) { + buf &= ~MAC_RX_RXEN; + ret = smsc75xx_write_reg(dev, MAC_RX, buf); + check_warn_return(ret, "Failed to write MAC_RX: %d", ret); + } + + /* add 4 to size for FCS */ + buf &= ~MAC_RX_MAX_SIZE; + buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE); + + ret = smsc75xx_write_reg(dev, MAC_RX, buf); + check_warn_return(ret, "Failed to write MAC_RX: %d", ret); + + if (rxenabled) { + buf |= MAC_RX_RXEN; + ret = smsc75xx_write_reg(dev, MAC_RX, buf); + check_warn_return(ret, "Failed to write MAC_RX: %d", ret); + } + + return 0; +} + +static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu) +{ + struct usbnet *dev = netdev_priv(netdev); + + int ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu); + check_warn_return(ret, "Failed to set mac rx frame length"); + + return usbnet_change_mtu(netdev, new_mtu); +} + +static int smsc75xx_reset(struct usbnet *dev) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u32 buf; + int ret = 0, timeout; + + netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset"); + + ret = smsc75xx_read_reg(dev, HW_CFG, &buf); + check_warn_return(ret, "Failed to read HW_CFG: %d", ret); + + buf |= HW_CFG_LRST; + + ret = smsc75xx_write_reg(dev, HW_CFG, buf); + check_warn_return(ret, "Failed to write HW_CFG: %d", ret); + + timeout = 0; + do { + msleep(10); + ret = smsc75xx_read_reg(dev, HW_CFG, &buf); + check_warn_return(ret, "Failed to read HW_CFG: %d", ret); + timeout++; + } while ((buf & HW_CFG_LRST) && (timeout < 100)); + + if (timeout >= 100) { + netdev_warn(dev->net, "timeout on completion of Lite Reset"); + return -EIO; + } + + netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY"); + + ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); + check_warn_return(ret, "Failed to read PMT_CTL: %d", ret); + + buf |= PMT_CTL_PHY_RST; + + ret = smsc75xx_write_reg(dev, PMT_CTL, buf); + check_warn_return(ret, "Failed to write PMT_CTL: %d", ret); + + timeout = 0; + do { + msleep(10); + ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); + check_warn_return(ret, "Failed to read PMT_CTL: %d", ret); + timeout++; + } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); + + if (timeout >= 100) { + netdev_warn(dev->net, "timeout waiting for PHY Reset"); + return -EIO; + } + + netif_dbg(dev, ifup, dev->net, "PHY reset complete"); + + smsc75xx_init_mac_address(dev); + + ret = smsc75xx_set_mac_address(dev); + check_warn_return(ret, "Failed to set mac address"); + + netif_dbg(dev, ifup, dev->net, "MAC Address: %pM", dev->net->dev_addr); + + ret = smsc75xx_read_reg(dev, HW_CFG, &buf); + check_warn_return(ret, "Failed to read HW_CFG: %d", ret); + + netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x", buf); + + buf |= HW_CFG_BIR; + + ret = smsc75xx_write_reg(dev, HW_CFG, buf); + check_warn_return(ret, "Failed to write HW_CFG: %d", ret); + + ret = smsc75xx_read_reg(dev, HW_CFG, &buf); + check_warn_return(ret, "Failed to read HW_CFG: %d", ret); + + netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after " + "writing HW_CFG_BIR: 0x%08x", buf); + + if (!turbo_mode) { + buf = 0; + dev->rx_urb_size = MAX_SINGLE_PACKET_SIZE; + } else if (dev->udev->speed == USB_SPEED_HIGH) { + buf = DEFAULT_HS_BURST_CAP_SIZE / HS_USB_PKT_SIZE; + dev->rx_urb_size = DEFAULT_HS_BURST_CAP_SIZE; + } else { + buf = DEFAULT_FS_BURST_CAP_SIZE / FS_USB_PKT_SIZE; + dev->rx_urb_size = DEFAULT_FS_BURST_CAP_SIZE; + } + + netif_dbg(dev, ifup, dev->net, "rx_urb_size=%ld", + (ulong)dev->rx_urb_size); + + ret = smsc75xx_write_reg(dev, BURST_CAP, buf); + check_warn_return(ret, "Failed to write BURST_CAP: %d", ret); + + ret = smsc75xx_read_reg(dev, BURST_CAP, &buf); + check_warn_return(ret, "Failed to read BURST_CAP: %d", ret); + + netif_dbg(dev, ifup, dev->net, + "Read Value from BURST_CAP after writing: 0x%08x", buf); + + ret = smsc75xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); + check_warn_return(ret, "Failed to write BULK_IN_DLY: %d", ret); + + ret = smsc75xx_read_reg(dev, BULK_IN_DLY, &buf); + check_warn_return(ret, "Failed to read BULK_IN_DLY: %d", ret); + + netif_dbg(dev, ifup, dev->net, + "Read Value from BULK_IN_DLY after writing: 0x%08x", buf); + + if (turbo_mode) { + ret = smsc75xx_read_reg(dev, HW_CFG, &buf); + check_warn_return(ret, "Failed to read HW_CFG: %d", ret); + + netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x", buf); + + buf |= (HW_CFG_MEF | HW_CFG_BCE); + + ret = smsc75xx_write_reg(dev, HW_CFG, buf); + check_warn_return(ret, "Failed to write HW_CFG: %d", ret); + + ret = smsc75xx_read_reg(dev, HW_CFG, &buf); + check_warn_return(ret, "Failed to read HW_CFG: %d", ret); + + netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x", buf); + } + + /* set FIFO sizes */ + buf = (MAX_RX_FIFO_SIZE - 512) / 512; + ret = smsc75xx_write_reg(dev, FCT_RX_FIFO_END, buf); + check_warn_return(ret, "Failed to write FCT_RX_FIFO_END: %d", ret); + + netif_dbg(dev, ifup, dev->net, "FCT_RX_FIFO_END set to 0x%08x", buf); + + buf = (MAX_TX_FIFO_SIZE - 512) / 512; + ret = smsc75xx_write_reg(dev, FCT_TX_FIFO_END, buf); + check_warn_return(ret, "Failed to write FCT_TX_FIFO_END: %d", ret); + + netif_dbg(dev, ifup, dev->net, "FCT_TX_FIFO_END set to 0x%08x", buf); + + ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); + check_warn_return(ret, "Failed to write INT_STS: %d", ret); + + ret = smsc75xx_read_reg(dev, ID_REV, &buf); + check_warn_return(ret, "Failed to read ID_REV: %d", ret); + + netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x", buf); + + /* Configure GPIO pins as LED outputs */ + ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf); + check_warn_return(ret, "Failed to read LED_GPIO_CFG: %d", ret); + + buf &= ~(LED_GPIO_CFG_LED2_FUN_SEL | LED_GPIO_CFG_LED10_FUN_SEL); + buf |= LED_GPIO_CFG_LEDGPIO_EN | LED_GPIO_CFG_LED2_FUN_SEL; + + ret = smsc75xx_write_reg(dev, LED_GPIO_CFG, buf); + check_warn_return(ret, "Failed to write LED_GPIO_CFG: %d", ret); + + ret = smsc75xx_write_reg(dev, FLOW, 0); + check_warn_return(ret, "Failed to write FLOW: %d", ret); + + ret = smsc75xx_write_reg(dev, FCT_FLOW, 0); + check_warn_return(ret, "Failed to write FCT_FLOW: %d", ret); + + /* Don't need rfe_ctl_lock during initialisation */ + ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); + check_warn_return(ret, "Failed to read RFE_CTL: %d", ret); + + pdata->rfe_ctl |= RFE_CTL_AB | RFE_CTL_DPF; + + ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); + check_warn_return(ret, "Failed to write RFE_CTL: %d", ret); + + ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); + check_warn_return(ret, "Failed to read RFE_CTL: %d", ret); + + netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x", pdata->rfe_ctl); + + /* Enable or disable checksum offload engines */ + ethtool_op_set_tx_hw_csum(dev->net, DEFAULT_TX_CSUM_ENABLE); + ret = smsc75xx_set_rx_csum_offload(dev); + check_warn_return(ret, "Failed to set rx csum offload: %d", ret); + + smsc75xx_ethtool_set_tso(dev->net, DEFAULT_TSO_ENABLE); + + smsc75xx_set_multicast(dev->net); + + ret = smsc75xx_phy_initialize(dev); + check_warn_return(ret, "Failed to initialize PHY: %d", ret); + + ret = smsc75xx_read_reg(dev, INT_EP_CTL, &buf); + check_warn_return(ret, "Failed to read INT_EP_CTL: %d", ret); + + /* enable PHY interrupts */ + buf |= INT_ENP_PHY_INT; + + ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf); + check_warn_return(ret, "Failed to write INT_EP_CTL: %d", ret); + + ret = smsc75xx_read_reg(dev, MAC_TX, &buf); + check_warn_return(ret, "Failed to read MAC_TX: %d", ret); + + buf |= MAC_TX_TXEN; + + ret = smsc75xx_write_reg(dev, MAC_TX, buf); + check_warn_return(ret, "Failed to write MAC_TX: %d", ret); + + netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x", buf); + + ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf); + check_warn_return(ret, "Failed to read FCT_TX_CTL: %d", ret); + + buf |= FCT_TX_CTL_EN; + + ret = smsc75xx_write_reg(dev, FCT_TX_CTL, buf); + check_warn_return(ret, "Failed to write FCT_TX_CTL: %d", ret); + + netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x", buf); + + ret = smsc75xx_set_rx_max_frame_length(dev, 1514); + check_warn_return(ret, "Failed to set max rx frame length"); + + ret = smsc75xx_read_reg(dev, MAC_RX, &buf); + check_warn_return(ret, "Failed to read MAC_RX: %d", ret); + + buf |= MAC_RX_RXEN; + + ret = smsc75xx_write_reg(dev, MAC_RX, buf); + check_warn_return(ret, "Failed to write MAC_RX: %d", ret); + + netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x", buf); + + ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf); + check_warn_return(ret, "Failed to read FCT_RX_CTL: %d", ret); + + buf |= FCT_RX_CTL_EN; + + ret = smsc75xx_write_reg(dev, FCT_RX_CTL, buf); + check_warn_return(ret, "Failed to write FCT_RX_CTL: %d", ret); + + netif_dbg(dev, ifup, dev->net, "FCT_RX_CTL set to 0x%08x", buf); + + netif_dbg(dev, ifup, dev->net, "smsc75xx_reset, return 0"); + return 0; +} + +static const struct net_device_ops smsc75xx_netdev_ops = { + .ndo_open = usbnet_open, + .ndo_stop = usbnet_stop, + .ndo_start_xmit = usbnet_start_xmit, + .ndo_tx_timeout = usbnet_tx_timeout, + .ndo_change_mtu = smsc75xx_change_mtu, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = smsc75xx_ioctl, + .ndo_set_multicast_list = smsc75xx_set_multicast, +}; + +static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) +{ + struct smsc75xx_priv *pdata = NULL; + int ret; + + printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); + + ret = usbnet_get_endpoints(dev, intf); + check_warn_return(ret, "usbnet_get_endpoints failed: %d", ret); + + dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc75xx_priv), + GFP_KERNEL); + + pdata = (struct smsc75xx_priv *)(dev->data[0]); + if (!pdata) { + netdev_warn(dev->net, "Unable to allocate smsc75xx_priv"); + return -ENOMEM; + } + + pdata->dev = dev; + + spin_lock_init(&pdata->rfe_ctl_lock); + mutex_init(&pdata->dataport_mutex); + + INIT_WORK(&pdata->set_multicast, smsc75xx_deferred_multicast_write); + + pdata->use_rx_csum = DEFAULT_RX_CSUM_ENABLE; + + /* We have to advertise SG otherwise TSO cannot be enabled */ + dev->net->features |= NETIF_F_SG; + + /* Init all registers */ + ret = smsc75xx_reset(dev); + + dev->net->netdev_ops = &smsc75xx_netdev_ops; + dev->net->ethtool_ops = &smsc75xx_ethtool_ops; + dev->net->flags |= IFF_MULTICAST; + dev->net->hard_header_len += SMSC75XX_TX_OVERHEAD; + return 0; +} + +static void smsc75xx_unbind(struct usbnet *dev, struct usb_interface *intf) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + if (pdata) { + netif_dbg(dev, ifdown, dev->net, "free pdata"); + kfree(pdata); + pdata = NULL; + dev->data[0] = 0; + } +} + +static void smsc75xx_rx_csum_offload(struct sk_buff *skb, u32 rx_cmd_a, + u32 rx_cmd_b) +{ + if (unlikely(rx_cmd_a & RX_CMD_A_LCSM)) { + skb->ip_summed = CHECKSUM_NONE; + } else { + skb->csum = ntohs((u16)(rx_cmd_b >> RX_CMD_B_CSUM_SHIFT)); + skb->ip_summed = CHECKSUM_COMPLETE; + } +} + +static int smsc75xx_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + + while (skb->len > 0) { + u32 rx_cmd_a, rx_cmd_b, align_count, size; + struct sk_buff *ax_skb; + unsigned char *packet; + + memcpy(&rx_cmd_a, skb->data, sizeof(rx_cmd_a)); + le32_to_cpus(&rx_cmd_a); + skb_pull(skb, 4); + + memcpy(&rx_cmd_b, skb->data, sizeof(rx_cmd_b)); + le32_to_cpus(&rx_cmd_b); + skb_pull(skb, 4 + NET_IP_ALIGN); + + packet = skb->data; + + /* get the packet length */ + size = (rx_cmd_a & RX_CMD_A_LEN) - NET_IP_ALIGN; + align_count = (4 - ((size + NET_IP_ALIGN) % 4)) % 4; + + if (unlikely(rx_cmd_a & RX_CMD_A_RED)) { + netif_dbg(dev, rx_err, dev->net, + "Error rx_cmd_a=0x%08x", rx_cmd_a); + dev->net->stats.rx_errors++; + dev->net->stats.rx_dropped++; + + if (rx_cmd_a & RX_CMD_A_FCS) + dev->net->stats.rx_crc_errors++; + else if (rx_cmd_a & (RX_CMD_A_LONG | RX_CMD_A_RUNT)) + dev->net->stats.rx_frame_errors++; + } else { + /* ETH_FRAME_LEN + 4(CRC) + 2(COE) + 4(Vlan) */ + if (unlikely(size > (ETH_FRAME_LEN + 12))) { + netif_dbg(dev, rx_err, dev->net, + "size err rx_cmd_a=0x%08x", rx_cmd_a); + return 0; + } + + /* last frame in this batch */ + if (skb->len == size) { + if (pdata->use_rx_csum) + smsc75xx_rx_csum_offload(skb, rx_cmd_a, + rx_cmd_b); + else + skb->ip_summed = CHECKSUM_NONE; + + skb_trim(skb, skb->len - 4); /* remove fcs */ + skb->truesize = size + sizeof(struct sk_buff); + + return 1; + } + + ax_skb = skb_clone(skb, GFP_ATOMIC); + if (unlikely(!ax_skb)) { + netdev_warn(dev->net, "Error allocating skb"); + return 0; + } + + ax_skb->len = size; + ax_skb->data = packet; + skb_set_tail_pointer(ax_skb, size); + + if (pdata->use_rx_csum) + smsc75xx_rx_csum_offload(ax_skb, rx_cmd_a, + rx_cmd_b); + else + ax_skb->ip_summed = CHECKSUM_NONE; + + skb_trim(ax_skb, ax_skb->len - 4); /* remove fcs */ + ax_skb->truesize = size + sizeof(struct sk_buff); + + usbnet_skb_return(dev, ax_skb); + } + + skb_pull(skb, size); + + /* padding bytes before the next frame starts */ + if (skb->len) + skb_pull(skb, align_count); + } + + if (unlikely(skb->len < 0)) { + netdev_warn(dev->net, "invalid rx length<0 %d", skb->len); + return 0; + } + + return 1; +} + +static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev, + struct sk_buff *skb, gfp_t flags) +{ + u32 tx_cmd_a, tx_cmd_b; + + skb_linearize(skb); + + if (skb_headroom(skb) < SMSC75XX_TX_OVERHEAD) { + struct sk_buff *skb2 = + skb_copy_expand(skb, SMSC75XX_TX_OVERHEAD, 0, flags); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return NULL; + } + + tx_cmd_a = (u32)(skb->len & TX_CMD_A_LEN) | TX_CMD_A_FCS; + + if (skb->ip_summed == CHECKSUM_PARTIAL) + tx_cmd_a |= TX_CMD_A_IPE | TX_CMD_A_TPE; + + if (skb_is_gso(skb)) { + u16 mss = max(skb_shinfo(skb)->gso_size, TX_MSS_MIN); + tx_cmd_b = (mss << TX_CMD_B_MSS_SHIFT) & TX_CMD_B_MSS; + + tx_cmd_a |= TX_CMD_A_LSO; + } else { + tx_cmd_b = 0; + } + + skb_push(skb, 4); + cpu_to_le32s(&tx_cmd_b); + memcpy(skb->data, &tx_cmd_b, 4); + + skb_push(skb, 4); + cpu_to_le32s(&tx_cmd_a); + memcpy(skb->data, &tx_cmd_a, 4); + + return skb; +} + +static const struct driver_info smsc75xx_info = { + .description = "smsc75xx USB 2.0 Gigabit Ethernet", + .bind = smsc75xx_bind, + .unbind = smsc75xx_unbind, + .link_reset = smsc75xx_link_reset, + .reset = smsc75xx_reset, + .rx_fixup = smsc75xx_rx_fixup, + .tx_fixup = smsc75xx_tx_fixup, + .status = smsc75xx_status, + .flags = FLAG_ETHER | FLAG_SEND_ZLP, +}; + +static const struct usb_device_id products[] = { + { + /* SMSC7500 USB Gigabit Ethernet Device */ + USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7500), + .driver_info = (unsigned long) &smsc75xx_info, + }, + { + /* SMSC7500 USB Gigabit Ethernet Device */ + USB_DEVICE(USB_VENDOR_ID_SMSC, USB_PRODUCT_ID_LAN7505), + .driver_info = (unsigned long) &smsc75xx_info, + }, + { }, /* END */ +}; +MODULE_DEVICE_TABLE(usb, products); + +static struct usb_driver smsc75xx_driver = { + .name = SMSC_CHIPNAME, + .id_table = products, + .probe = usbnet_probe, + .suspend = usbnet_suspend, + .resume = usbnet_resume, + .disconnect = usbnet_disconnect, +}; + +static int __init smsc75xx_init(void) +{ + return usb_register(&smsc75xx_driver); +} +module_init(smsc75xx_init); + +static void __exit smsc75xx_exit(void) +{ + usb_deregister(&smsc75xx_driver); +} +module_exit(smsc75xx_exit); + +MODULE_AUTHOR("Nancy Lin"); +MODULE_AUTHOR("Steve Glendinning "); +MODULE_DESCRIPTION("SMSC75XX USB 2.0 Gigabit Ethernet Devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/usb/smsc75xx.h b/drivers/net/usb/smsc75xx.h new file mode 100644 index 0000000..16e98c7 --- /dev/null +++ b/drivers/net/usb/smsc75xx.h @@ -0,0 +1,421 @@ + /*************************************************************************** + * + * Copyright (C) 2007-2010 SMSC + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + *****************************************************************************/ + +#ifndef _SMSC75XX_H +#define _SMSC75XX_H + +/* Tx command words */ +#define TX_CMD_A_LSO (0x08000000) +#define TX_CMD_A_IPE (0x04000000) +#define TX_CMD_A_TPE (0x02000000) +#define TX_CMD_A_IVTG (0x01000000) +#define TX_CMD_A_RVTG (0x00800000) +#define TX_CMD_A_FCS (0x00400000) +#define TX_CMD_A_LEN (0x000FFFFF) + +#define TX_CMD_B_MSS (0x3FFF0000) +#define TX_CMD_B_MSS_SHIFT (16) +#define TX_MSS_MIN ((u16)8) +#define TX_CMD_B_VTAG (0x0000FFFF) + +/* Rx command words */ +#define RX_CMD_A_ICE (0x80000000) +#define RX_CMD_A_TCE (0x40000000) +#define RX_CMD_A_IPV (0x20000000) +#define RX_CMD_A_PID (0x18000000) +#define RX_CMD_A_PID_NIP (0x00000000) +#define RX_CMD_A_PID_TCP (0x08000000) +#define RX_CMD_A_PID_UDP (0x10000000) +#define RX_CMD_A_PID_PP (0x18000000) +#define RX_CMD_A_PFF (0x04000000) +#define RX_CMD_A_BAM (0x02000000) +#define RX_CMD_A_MAM (0x01000000) +#define RX_CMD_A_FVTG (0x00800000) +#define RX_CMD_A_RED (0x00400000) +#define RX_CMD_A_RWT (0x00200000) +#define RX_CMD_A_RUNT (0x00100000) +#define RX_CMD_A_LONG (0x00080000) +#define RX_CMD_A_RXE (0x00040000) +#define RX_CMD_A_DRB (0x00020000) +#define RX_CMD_A_FCS (0x00010000) +#define RX_CMD_A_UAM (0x00008000) +#define RX_CMD_A_LCSM (0x00004000) +#define RX_CMD_A_LEN (0x00003FFF) + +#define RX_CMD_B_CSUM (0xFFFF0000) +#define RX_CMD_B_CSUM_SHIFT (16) +#define RX_CMD_B_VTAG (0x0000FFFF) + +/* SCSRs */ +#define ID_REV (0x0000) + +#define FPGA_REV (0x0004) + +#define BOND_CTL (0x0008) + +#define INT_STS (0x000C) +#define INT_STS_RDFO_INT (0x00400000) +#define INT_STS_TXE_INT (0x00200000) +#define INT_STS_MACRTO_INT (0x00100000) +#define INT_STS_TX_DIS_INT (0x00080000) +#define INT_STS_RX_DIS_INT (0x00040000) +#define INT_STS_PHY_INT_ (0x00020000) +#define INT_STS_MAC_ERR_INT (0x00008000) +#define INT_STS_TDFU (0x00004000) +#define INT_STS_TDFO (0x00002000) +#define INT_STS_GPIOS (0x00000FFF) +#define INT_STS_CLEAR_ALL (0xFFFFFFFF) + +#define HW_CFG (0x0010) +#define HW_CFG_SMDET_STS (0x00008000) +#define HW_CFG_SMDET_EN (0x00004000) +#define HW_CFG_EEM (0x00002000) +#define HW_CFG_RST_PROTECT (0x00001000) +#define HW_CFG_PORT_SWAP (0x00000800) +#define HW_CFG_PHY_BOOST (0x00000600) +#define HW_CFG_PHY_BOOST_NORMAL (0x00000000) +#define HW_CFG_PHY_BOOST_4 (0x00002000) +#define HW_CFG_PHY_BOOST_8 (0x00004000) +#define HW_CFG_PHY_BOOST_12 (0x00006000) +#define HW_CFG_LEDB (0x00000100) +#define HW_CFG_BIR (0x00000080) +#define HW_CFG_SBP (0x00000040) +#define HW_CFG_IME (0x00000020) +#define HW_CFG_MEF (0x00000010) +#define HW_CFG_ETC (0x00000008) +#define HW_CFG_BCE (0x00000004) +#define HW_CFG_LRST (0x00000002) +#define HW_CFG_SRST (0x00000001) + +#define PMT_CTL (0x0014) +#define PMT_CTL_PHY_PWRUP (0x00000400) +#define PMT_CTL_RES_CLR_WKP_EN (0x00000100) +#define PMT_CTL_DEV_RDY (0x00000080) +#define PMT_CTL_SUS_MODE (0x00000060) +#define PMT_CTL_SUS_MODE_0 (0x00000000) +#define PMT_CTL_SUS_MODE_1 (0x00000020) +#define PMT_CTL_SUS_MODE_2 (0x00000040) +#define PMT_CTL_SUS_MODE_3 (0x00000060) +#define PMT_CTL_PHY_RST (0x00000010) +#define PMT_CTL_WOL_EN (0x00000008) +#define PMT_CTL_ED_EN (0x00000004) +#define PMT_CTL_WUPS (0x00000003) +#define PMT_CTL_WUPS_NO (0x00000000) +#define PMT_CTL_WUPS_ED (0x00000001) +#define PMT_CTL_WUPS_WOL (0x00000002) +#define PMT_CTL_WUPS_MULTI (0x00000003) + +#define LED_GPIO_CFG (0x0018) +#define LED_GPIO_CFG_LED2_FUN_SEL (0x80000000) +#define LED_GPIO_CFG_LED10_FUN_SEL (0x40000000) +#define LED_GPIO_CFG_LEDGPIO_EN (0x0000F000) +#define LED_GPIO_CFG_LEDGPIO_EN_0 (0x00001000) +#define LED_GPIO_CFG_LEDGPIO_EN_1 (0x00002000) +#define LED_GPIO_CFG_LEDGPIO_EN_2 (0x00004000) +#define LED_GPIO_CFG_LEDGPIO_EN_3 (0x00008000) +#define LED_GPIO_CFG_GPBUF (0x00000F00) +#define LED_GPIO_CFG_GPBUF_0 (0x00000100) +#define LED_GPIO_CFG_GPBUF_1 (0x00000200) +#define LED_GPIO_CFG_GPBUF_2 (0x00000400) +#define LED_GPIO_CFG_GPBUF_3 (0x00000800) +#define LED_GPIO_CFG_GPDIR (0x000000F0) +#define LED_GPIO_CFG_GPDIR_0 (0x00000010) +#define LED_GPIO_CFG_GPDIR_1 (0x00000020) +#define LED_GPIO_CFG_GPDIR_2 (0x00000040) +#define LED_GPIO_CFG_GPDIR_3 (0x00000080) +#define LED_GPIO_CFG_GPDATA (0x0000000F) +#define LED_GPIO_CFG_GPDATA_0 (0x00000001) +#define LED_GPIO_CFG_GPDATA_1 (0x00000002) +#define LED_GPIO_CFG_GPDATA_2 (0x00000004) +#define LED_GPIO_CFG_GPDATA_3 (0x00000008) + +#define GPIO_CFG (0x001C) +#define GPIO_CFG_SHIFT (24) +#define GPIO_CFG_GPEN (0xFF000000) +#define GPIO_CFG_GPBUF (0x00FF0000) +#define GPIO_CFG_GPDIR (0x0000FF00) +#define GPIO_CFG_GPDATA (0x000000FF) + +#define GPIO_WAKE (0x0020) +#define GPIO_WAKE_PHY_LINKUP_EN (0x80000000) +#define GPIO_WAKE_POL (0x0FFF0000) +#define GPIO_WAKE_POL_SHIFT (16) +#define GPIO_WAKE_WK (0x00000FFF) + +#define DP_SEL (0x0024) +#define DP_SEL_DPRDY (0x80000000) +#define DP_SEL_RSEL (0x0000000F) +#define DP_SEL_URX (0x00000000) +#define DP_SEL_VHF (0x00000001) +#define DP_SEL_VHF_HASH_LEN (16) +#define DP_SEL_VHF_VLAN_LEN (128) +#define DP_SEL_LSO_HEAD (0x00000002) +#define DP_SEL_FCT_RX (0x00000003) +#define DP_SEL_FCT_TX (0x00000004) +#define DP_SEL_DESCRIPTOR (0x00000005) +#define DP_SEL_WOL (0x00000006) + +#define DP_CMD (0x0028) +#define DP_CMD_WRITE (0x01) +#define DP_CMD_READ (0x00) + +#define DP_ADDR (0x002C) + +#define DP_DATA (0x0030) + +#define BURST_CAP (0x0034) +#define BURST_CAP_MASK (0x0000000F) + +#define INT_EP_CTL (0x0038) +#define INT_EP_CTL_INTEP_ON (0x80000000) +#define INT_EP_CTL_RDFO_EN (0x00400000) +#define INT_EP_CTL_TXE_EN (0x00200000) +#define INT_EP_CTL_MACROTO_EN (0x00100000) +#define INT_EP_CTL_TX_DIS_EN (0x00080000) +#define INT_EP_CTL_RX_DIS_EN (0x00040000) +#define INT_EP_CTL_PHY_EN_ (0x00020000) +#define INT_EP_CTL_MAC_ERR_EN (0x00008000) +#define INT_EP_CTL_TDFU_EN (0x00004000) +#define INT_EP_CTL_TDFO_EN (0x00002000) +#define INT_EP_CTL_RX_FIFO_EN (0x00001000) +#define INT_EP_CTL_GPIOX_EN (0x00000FFF) + +#define BULK_IN_DLY (0x003C) +#define BULK_IN_DLY_MASK (0xFFFF) + +#define E2P_CMD (0x0040) +#define E2P_CMD_BUSY (0x80000000) +#define E2P_CMD_MASK (0x70000000) +#define E2P_CMD_READ (0x00000000) +#define E2P_CMD_EWDS (0x10000000) +#define E2P_CMD_EWEN (0x20000000) +#define E2P_CMD_WRITE (0x30000000) +#define E2P_CMD_WRAL (0x40000000) +#define E2P_CMD_ERASE (0x50000000) +#define E2P_CMD_ERAL (0x60000000) +#define E2P_CMD_RELOAD (0x70000000) +#define E2P_CMD_TIMEOUT (0x00000400) +#define E2P_CMD_LOADED (0x00000200) +#define E2P_CMD_ADDR (0x000001FF) + +#define MAX_EEPROM_SIZE (512) + +#define E2P_DATA (0x0044) +#define E2P_DATA_MASK_ (0x000000FF) + +#define RFE_CTL (0x0060) +#define RFE_CTL_TCPUDP_CKM (0x00001000) +#define RFE_CTL_IP_CKM (0x00000800) +#define RFE_CTL_AB (0x00000400) +#define RFE_CTL_AM (0x00000200) +#define RFE_CTL_AU (0x00000100) +#define RFE_CTL_VS (0x00000080) +#define RFE_CTL_UF (0x00000040) +#define RFE_CTL_VF (0x00000020) +#define RFE_CTL_SPF (0x00000010) +#define RFE_CTL_MHF (0x00000008) +#define RFE_CTL_DHF (0x00000004) +#define RFE_CTL_DPF (0x00000002) +#define RFE_CTL_RST_RF (0x00000001) + +#define VLAN_TYPE (0x0064) +#define VLAN_TYPE_MASK (0x0000FFFF) + +#define FCT_RX_CTL (0x0090) +#define FCT_RX_CTL_EN (0x80000000) +#define FCT_RX_CTL_RST (0x40000000) +#define FCT_RX_CTL_SBF (0x02000000) +#define FCT_RX_CTL_OVERFLOW (0x01000000) +#define FCT_RX_CTL_FRM_DROP (0x00800000) +#define FCT_RX_CTL_RX_NOT_EMPTY (0x00400000) +#define FCT_RX_CTL_RX_EMPTY (0x00200000) +#define FCT_RX_CTL_RX_DISABLED (0x00100000) +#define FCT_RX_CTL_RXUSED (0x0000FFFF) + +#define FCT_TX_CTL (0x0094) +#define FCT_TX_CTL_EN (0x80000000) +#define FCT_TX_CTL_RST (0x40000000) +#define FCT_TX_CTL_TX_NOT_EMPTY (0x00400000) +#define FCT_TX_CTL_TX_EMPTY (0x00200000) +#define FCT_TX_CTL_TX_DISABLED (0x00100000) +#define FCT_TX_CTL_TXUSED (0x0000FFFF) + +#define FCT_RX_FIFO_END (0x0098) +#define FCT_RX_FIFO_END_MASK (0x0000007F) + +#define FCT_TX_FIFO_END (0x009C) +#define FCT_TX_FIFO_END_MASK (0x0000003F) + +#define FCT_FLOW (0x00A0) +#define FCT_FLOW_THRESHOLD_OFF (0x00007F00) +#define FCT_FLOW_THRESHOLD_OFF_SHIFT (8) +#define FCT_FLOW_THRESHOLD_ON (0x0000007F) + +/* MAC CSRs */ +#define MAC_CR (0x100) +#define MAC_CR_ADP (0x00002000) +#define MAC_CR_ADD (0x00001000) +#define MAC_CR_ASD (0x00000800) +#define MAC_CR_INT_LOOP (0x00000400) +#define MAC_CR_BOLMT (0x000000C0) +#define MAC_CR_FDPX (0x00000008) +#define MAC_CR_CFG (0x00000006) +#define MAC_CR_CFG_10 (0x00000000) +#define MAC_CR_CFG_100 (0x00000002) +#define MAC_CR_CFG_1000 (0x00000004) +#define MAC_CR_RST (0x00000001) + +#define MAC_RX (0x104) +#define MAC_RX_MAX_SIZE (0x3FFF0000) +#define MAC_RX_MAX_SIZE_SHIFT (16) +#define MAC_RX_FCS_STRIP (0x00000010) +#define MAC_RX_FSE (0x00000004) +#define MAC_RX_RXD (0x00000002) +#define MAC_RX_RXEN (0x00000001) + +#define MAC_TX (0x108) +#define MAC_TX_BFCS (0x00000004) +#define MAC_TX_TXD (0x00000002) +#define MAC_TX_TXEN (0x00000001) + +#define FLOW (0x10C) +#define FLOW_FORCE_FC (0x80000000) +#define FLOW_TX_FCEN (0x40000000) +#define FLOW_RX_FCEN (0x20000000) +#define FLOW_FPF (0x10000000) +#define FLOW_PAUSE_TIME (0x0000FFFF) + +#define RAND_SEED (0x110) +#define RAND_SEED_MASK (0x0000FFFF) + +#define ERR_STS (0x114) +#define ERR_STS_FCS_ERR (0x00000100) +#define ERR_STS_LFRM_ERR (0x00000080) +#define ERR_STS_RUNT_ERR (0x00000040) +#define ERR_STS_COLLISION_ERR (0x00000010) +#define ERR_STS_ALIGN_ERR (0x00000008) +#define ERR_STS_URUN_ERR (0x00000004) + +#define RX_ADDRH (0x118) +#define RX_ADDRH_MASK (0x0000FFFF) + +#define RX_ADDRL (0x11C) + +#define MII_ACCESS (0x120) +#define MII_ACCESS_PHY_ADDR (0x0000F800) +#define MII_ACCESS_PHY_ADDR_SHIFT (11) +#define MII_ACCESS_REG_ADDR (0x000007C0) +#define MII_ACCESS_REG_ADDR_SHIFT (6) +#define MII_ACCESS_READ (0x00000000) +#define MII_ACCESS_WRITE (0x00000002) +#define MII_ACCESS_BUSY (0x00000001) + +#define MII_DATA (0x124) +#define MII_DATA_MASK (0x0000FFFF) + +#define WUCSR (0x140) +#define WUCSR_PFDA_FR (0x00000080) +#define WUCSR_WUFR (0x00000040) +#define WUCSR_MPR (0x00000020) +#define WUCSR_BCAST_FR (0x00000010) +#define WUCSR_PFDA_EN (0x00000008) +#define WUCSR_WUEN (0x00000004) +#define WUCSR_MPEN (0x00000002) +#define WUCSR_BCST_EN (0x00000001) + +#define WUF_CFGX (0x144) +#define WUF_CFGX_EN (0x80000000) +#define WUF_CFGX_ATYPE (0x03000000) +#define WUF_CFGX_ATYPE_UNICAST (0x00000000) +#define WUF_CFGX_ATYPE_MULTICAST (0x02000000) +#define WUF_CFGX_ATYPE_ALL (0x03000000) +#define WUF_CFGX_PATTERN_OFFSET (0x007F0000) +#define WUF_CFGX_PATTERN_OFFSET_SHIFT (16) +#define WUF_CFGX_CRC16 (0x0000FFFF) +#define WUF_NUM (8) + +#define WUF_MASKX (0x170) +#define WUF_MASKX_AVALID (0x80000000) +#define WUF_MASKX_ATYPE (0x40000000) + +#define ADDR_FILTX (0x300) +#define ADDR_FILTX_FB_VALID (0x80000000) +#define ADDR_FILTX_FB_TYPE (0x40000000) +#define ADDR_FILTX_FB_ADDRHI (0x0000FFFF) +#define ADDR_FILTX_SB_ADDRLO (0xFFFFFFFF) + +#define WUCSR2 (0x500) +#define WUCSR2_NS_RCD (0x00000040) +#define WUCSR2_ARP_RCD (0x00000020) +#define WUCSR2_TCPSYN_RCD (0x00000010) +#define WUCSR2_NS_OFFLOAD (0x00000004) +#define WUCSR2_ARP_OFFLOAD (0x00000002) +#define WUCSR2_TCPSYN_OFFLOAD (0x00000001) + +#define WOL_FIFO_STS (0x504) + +#define IPV6_ADDRX (0x510) + +#define IPV4_ADDRX (0x590) + + +/* Vendor-specific PHY Definitions */ + +/* Mode Control/Status Register */ +#define PHY_MODE_CTRL_STS (17) +#define MODE_CTRL_STS_EDPWRDOWN ((u16)0x2000) +#define MODE_CTRL_STS_ENERGYON ((u16)0x0002) + +#define PHY_INT_SRC (29) +#define PHY_INT_SRC_ENERGY_ON ((u16)0x0080) +#define PHY_INT_SRC_ANEG_COMP ((u16)0x0040) +#define PHY_INT_SRC_REMOTE_FAULT ((u16)0x0020) +#define PHY_INT_SRC_LINK_DOWN ((u16)0x0010) + +#define PHY_INT_MASK (30) +#define PHY_INT_MASK_ENERGY_ON ((u16)0x0080) +#define PHY_INT_MASK_ANEG_COMP ((u16)0x0040) +#define PHY_INT_MASK_REMOTE_FAULT ((u16)0x0020) +#define PHY_INT_MASK_LINK_DOWN ((u16)0x0010) +#define PHY_INT_MASK_DEFAULT (PHY_INT_MASK_ANEG_COMP | \ + PHY_INT_MASK_LINK_DOWN) + +#define PHY_SPECIAL (31) +#define PHY_SPECIAL_SPD ((u16)0x001C) +#define PHY_SPECIAL_SPD_10HALF ((u16)0x0004) +#define PHY_SPECIAL_SPD_10FULL ((u16)0x0014) +#define PHY_SPECIAL_SPD_100HALF ((u16)0x0008) +#define PHY_SPECIAL_SPD_100FULL ((u16)0x0018) + +/* USB Vendor Requests */ +#define USB_VENDOR_REQUEST_WRITE_REGISTER 0xA0 +#define USB_VENDOR_REQUEST_READ_REGISTER 0xA1 +#define USB_VENDOR_REQUEST_GET_STATS 0xA2 + +/* Interrupt Endpoint status word bitfields */ +#define INT_ENP_RDFO_INT ((u32)BIT(22)) +#define INT_ENP_TXE_INT ((u32)BIT(21)) +#define INT_ENP_TX_DIS_INT ((u32)BIT(19)) +#define INT_ENP_RX_DIS_INT ((u32)BIT(18)) +#define INT_ENP_PHY_INT ((u32)BIT(17)) +#define INT_ENP_MAC_ERR_INT ((u32)BIT(15)) +#define INT_ENP_RX_FIFO_DATA_INT ((u32)BIT(12)) + +#endif /* _SMSC75XX_H */ -- cgit v0.10.2 From 3feec9095d12e311b7d4eb7fe7e5dfa75d4a72a5 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Tue, 16 Mar 2010 06:46:31 +0000 Subject: l2tp: Fix oops in pppol2tp_xmit When transmitting L2TP frames, we derive the outgoing interface's UDP checksum hardware assist capabilities from the tunnel dst dev. This can sometimes be NULL, especially when routing protocols are used and routing changes occur. This patch just checks for NULL dst or dev pointers when checking for netdev hardware assist features. BUG: unable to handle kernel NULL pointer dereference at 0000000c IP: [] pppol2tp_xmit+0x341/0x4da [pppol2tp] *pde = 00000000 Oops: 0000 [#1] SMP last sysfs file: /sys/class/net/lo/operstate Modules linked in: pppol2tp pppox ppp_generic slhc ipv6 dummy loop snd_hda_codec_atihdmi snd_hda_intel snd_hda_codec snd_pcm snd_timer snd soundcore snd_page_alloc evdev psmouse serio_raw processor button i2c_piix4 i2c_core ati_agp agpgart pcspkr ext3 jbd mbcache sd_mod ide_pci_generic atiixp ide_core ahci ata_generic floppy ehci_hcd ohci_hcd libata e1000e scsi_mod usbcore nls_base thermal fan thermal_sys [last unloaded: scsi_wait_scan] Pid: 0, comm: swapper Not tainted (2.6.32.8 #1) EIP: 0060:[] EFLAGS: 00010297 CPU: 3 EIP is at pppol2tp_xmit+0x341/0x4da [pppol2tp] EAX: 00000000 EBX: f64d1680 ECX: 000005b9 EDX: 00000000 ESI: f6b91850 EDI: f64d16ac EBP: f6a0c4c0 ESP: f70a9cac DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 Process swapper (pid: 0, ti=f70a8000 task=f70a31c0 task.ti=f70a8000) Stack: 000005a9 000005b9 f734c400 f66652c0 f7352e00 f67dc800 00000000 f6b91800 <0> 000005a3 f70ef6c4 f67dcda9 000005a3 f89b192e 00000246 000005a3 f64d1680 <0> f63633e0 f6363320 f64d1680 f65a7320 f65a7364 f65856c0 f64d1680 f679f02f Call Trace: [] ? ppp_push+0x459/0x50e [ppp_generic] [] ? ppp_xmit_process+0x3b6/0x430 [ppp_generic] [] ? ppp_start_xmit+0x10d/0x120 [ppp_generic] [] ? dev_hard_start_xmit+0x21f/0x2b2 [] ? sch_direct_xmit+0x48/0x10e [] ? dev_queue_xmit+0x263/0x3a6 [] ? ip_finish_output+0x1f7/0x221 [] ? ip_forward_finish+0x2e/0x30 [] ? ip_rcv_finish+0x295/0x2a9 [] ? netif_receive_skb+0x3e9/0x404 [] ? e1000_clean_rx_irq+0x253/0x2fc [e1000e] [] ? e1000_clean+0x63/0x1fc [e1000e] [] ? sched_clock_local+0x15/0x11b [] ? net_rx_action+0x96/0x195 [] ? __do_softirq+0xaa/0x151 [] ? do_softirq+0x31/0x3c [] ? irq_exit+0x26/0x58 [] ? do_IRQ+0x78/0x89 [] ? common_interrupt+0x29/0x30 [] ? native_safe_halt+0x2/0x3 [] ? default_idle+0x55/0x75 [] ? c1e_idle+0xd2/0xd5 [] ? cpu_idle+0x46/0x62 Code: 8d 45 08 f0 ff 45 08 89 6b 08 c7 43 68 7e fb 9c f8 8a 45 24 83 e0 0c 3c 04 75 09 80 63 64 f3 e9 b4 00 00 00 8b 43 18 8b 4c 24 04 <8b> 40 0c 8d 79 11 f6 40 44 0e 8a 43 64 75 51 6a 00 8b 4c 24 08 EIP: [] pppol2tp_xmit+0x341/0x4da [pppol2tp] SS:ESP 0068:f70a9cac CR2: 000000000000000c Signed-off-by: James Chapman Signed-off-by: David S. Miller diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 9fbb2eb..5861ee9 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -1180,7 +1180,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) /* Calculate UDP checksum if configured to do so */ if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT) skb->ip_summed = CHECKSUM_NONE; - else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) { + else if ((skb_dst(skb) && skb_dst(skb)->dev) && + (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { skb->ip_summed = CHECKSUM_COMPLETE; csum = skb_checksum(skb, 0, udp_len, 0); uh->check = csum_tcpudp_magic(inet->inet_saddr, -- cgit v0.10.2 From db443c441e204cecc1bcec490d40997db988ce3a Mon Sep 17 00:00:00 2001 From: Steve Glendinning Date: Tue, 16 Mar 2010 09:03:06 +0000 Subject: smsc95xx: wait for PHY to complete reset during init This patch ensures the PHY correctly completes its reset before setting register values. Signed-off-by: Steve Glendinning Signed-off-by: David S. Miller diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index df9179a..d222d7e 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -709,6 +709,8 @@ static void smsc95xx_start_rx_path(struct usbnet *dev) static int smsc95xx_phy_initialize(struct usbnet *dev) { + int bmcr, timeout = 0; + /* Initialize MII structure */ dev->mii.dev = dev->net; dev->mii.mdio_read = smsc95xx_mdio_read; @@ -717,7 +719,20 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) dev->mii.reg_num_mask = 0x1f; dev->mii.phy_id = SMSC95XX_INTERNAL_PHY_ID; + /* reset phy and wait for reset to complete */ smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); + + do { + msleep(10); + bmcr = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); + timeout++; + } while ((bmcr & MII_BMCR) && (timeout < 100)); + + if (timeout >= 100) { + netdev_warn(dev->net, "timeout on PHY Reset"); + return -EIO; + } + smsc95xx_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM); -- cgit v0.10.2 From c3259c8a7060d480e8eb2166da0a99d6879146b4 Mon Sep 17 00:00:00 2001 From: James Chapman Date: Tue, 16 Mar 2010 06:29:20 +0000 Subject: l2tp: Fix UDP socket reference count bugs in the pppol2tp driver This patch fixes UDP socket refcnt bugs in the pppol2tp driver. A bug can cause a kernel stack trace when a tunnel socket is closed. A way to reproduce the issue is to prepare the UDP socket for L2TP (by opening a tunnel pppol2tp socket) and then close it before any L2TP sessions are added to it. The sequence is Create UDP socket Create tunnel pppol2tp socket to prepare UDP socket for L2TP pppol2tp_connect: session_id=0, peer_session_id=0 L2TP SCCRP control frame received (tunnel_id==0) pppol2tp_recv_core: sock_hold() pppol2tp_recv_core: sock_put L2TP ZLB control frame received (tunnel_id=nnn) pppol2tp_recv_core: sock_hold() pppol2tp_recv_core: sock_put Close tunnel management socket pppol2tp_release: session_id=0, peer_session_id=0 Close UDP socket udp_lib_close: BUG The addition of sock_hold() in pppol2tp_connect() solves the problem. For data frames, two sock_put() calls were added to plug a refcnt leak per received data frame. The ref that is grabbed at the top of pppol2tp_recv_core() must always be released, but this wasn't done for accepted data frames or data frames discarded because of bad UDP checksums. This leak meant that any UDP socket that had passed L2TP data traffic (i.e. L2TP data frames, not just L2TP control frames) using pppol2tp would not be released by the kernel. WARNING: at include/net/sock.h:435 udp_lib_unhash+0x117/0x120() Pid: 1086, comm: openl2tpd Not tainted 2.6.33-rc1 #8 Call Trace: [] ? udp_lib_unhash+0x117/0x120 [] ? warn_slowpath_common+0x71/0xd0 [] ? udp_lib_unhash+0x117/0x120 [] ? warn_slowpath_null+0x13/0x20 [] ? udp_lib_unhash+0x117/0x120 [] ? sk_common_release+0x17/0x90 [] ? inet_release+0x33/0x60 [] ? sock_release+0x10/0x60 [] ? sock_close+0xf/0x30 [] ? __fput+0x52/0x150 [] ? filp_close+0x3e/0x70 [] ? put_files_struct+0x62/0xb0 [] ? do_exit+0x5e7/0x650 [] ? mntput_no_expire+0x13/0x70 [] ? filp_close+0x3e/0x70 [] ? do_group_exit+0x2a/0x70 [] ? sys_exit_group+0x11/0x20 [] ? sysenter_do_call+0x12/0x26 Signed-off-by: James Chapman Signed-off-by: David S. Miller diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c index 5861ee9..449a982 100644 --- a/drivers/net/pppol2tp.c +++ b/drivers/net/pppol2tp.c @@ -756,6 +756,7 @@ static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb) /* Try to dequeue as many skbs from reorder_q as we can. */ pppol2tp_recv_dequeue(session); + sock_put(sock); return 0; @@ -772,6 +773,7 @@ discard_bad_csum: UDP_INC_STATS_USER(&init_net, UDP_MIB_INERRORS, 0); tunnel->stats.rx_errors++; kfree_skb(skb); + sock_put(sock); return 0; @@ -1662,6 +1664,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, if (tunnel_sock == NULL) goto end; + sock_hold(tunnel_sock); tunnel = tunnel_sock->sk_user_data; } else { tunnel = pppol2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel); -- cgit v0.10.2 From a2f46ee1ba5ee249ce2ca1ee7a7a0ac46529fb4f Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 16 Mar 2010 08:14:33 +0000 Subject: tipc: fix lockdep warning on address assignment So in the forward porting of various tipc packages, I was constantly getting this lockdep warning everytime I used tipc-config to set a network address for the protocol: [ INFO: possible circular locking dependency detected ] 2.6.33 #1 tipc-config/1326 is trying to acquire lock: (ref_table_lock){+.-...}, at: [] tipc_ref_discard+0x53/0xd4 [tipc] but task is already holding lock: (&(&entry->lock)->rlock#2){+.-...}, at: [] tipc_ref_lock+0x43/0x63 [tipc] which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (&(&entry->lock)->rlock#2){+.-...}: [] __lock_acquire+0xb67/0xd0f [] lock_acquire+0xdc/0x102 [] _raw_spin_lock_bh+0x3b/0x6e [] tipc_ref_acquire+0xe8/0x11b [tipc] [] tipc_createport_raw+0x78/0x1b9 [tipc] [] tipc_createport+0x8b/0x125 [tipc] [] tipc_subscr_start+0xce/0x126 [tipc] [] process_signal_queue+0x47/0x7d [tipc] [] tasklet_action+0x8c/0xf4 [] __do_softirq+0xf8/0x1cd [] call_softirq+0x1c/0x30 [] _local_bh_enable_ip+0xb8/0xd7 [] local_bh_enable_ip+0xe/0x10 [] _raw_spin_unlock_bh+0x34/0x39 [] spin_unlock_bh.clone.0+0x15/0x17 [tipc] [] tipc_k_signal+0x8d/0xb1 [tipc] [] tipc_core_start+0x8a/0xad [tipc] [] 0xffffffffa01b1087 [] do_one_initcall+0x72/0x18a [] sys_init_module+0xd8/0x23a [] system_call_fastpath+0x16/0x1b -> #0 (ref_table_lock){+.-...}: [] __lock_acquire+0xa11/0xd0f [] lock_acquire+0xdc/0x102 [] _raw_write_lock_bh+0x3b/0x6e [] tipc_ref_discard+0x53/0xd4 [tipc] [] tipc_deleteport+0x40/0x119 [tipc] [] release+0xeb/0x137 [tipc] [] sock_release+0x1f/0x6f [] sock_close+0x27/0x2b [] __fput+0x12a/0x1df [] fput+0x1a/0x1c [] filp_close+0x68/0x72 [] sys_close+0xad/0xe7 [] system_call_fastpath+0x16/0x1b Finally decided I should fix this. Its a straightforward inversion, tipc_ref_acquire takes two locks in this order: ref_table_lock entry->lock while tipc_deleteport takes them in this order: entry->lock (via tipc_port_lock()) ref_table_lock (via tipc_ref_discard()) when the same entry is referenced, we get the above warning. The fix is equally straightforward. Theres no real relation between the entry->lock and the ref_table_lock (they just are needed at the same time), so move the entry->lock aquisition in tipc_ref_acquire down, after we unlock ref_table_lock (this is safe since the ref_table_lock guards changes to the reference table, and we've already claimed a slot there. I've tested the below fix and confirmed that it clears up the lockdep issue Signed-off-by: Neil Horman CC: Allan Stephens Signed-off-by: David S. Miller diff --git a/net/tipc/ref.c b/net/tipc/ref.c index 414fc34..8dea665 100644 --- a/net/tipc/ref.c +++ b/net/tipc/ref.c @@ -153,11 +153,11 @@ void tipc_ref_table_stop(void) u32 tipc_ref_acquire(void *object, spinlock_t **lock) { - struct reference *entry; u32 index; u32 index_mask; u32 next_plus_upper; u32 ref; + struct reference *entry = NULL; if (!object) { err("Attempt to acquire reference to non-existent object\n"); @@ -175,30 +175,36 @@ u32 tipc_ref_acquire(void *object, spinlock_t **lock) index = tipc_ref_table.first_free; entry = &(tipc_ref_table.entries[index]); index_mask = tipc_ref_table.index_mask; - /* take lock in case a previous user of entry still holds it */ - spin_lock_bh(&entry->lock); next_plus_upper = entry->ref; tipc_ref_table.first_free = next_plus_upper & index_mask; ref = (next_plus_upper & ~index_mask) + index; - entry->ref = ref; - entry->object = object; - *lock = &entry->lock; } else if (tipc_ref_table.init_point < tipc_ref_table.capacity) { index = tipc_ref_table.init_point++; entry = &(tipc_ref_table.entries[index]); spin_lock_init(&entry->lock); - spin_lock_bh(&entry->lock); ref = tipc_ref_table.start_mask + index; - entry->ref = ref; - entry->object = object; - *lock = &entry->lock; } else { ref = 0; } write_unlock_bh(&ref_table_lock); + /* + * Grab the lock so no one else can modify this entry + * While we assign its ref value & object pointer + */ + if (entry) { + spin_lock_bh(&entry->lock); + entry->ref = ref; + entry->object = object; + *lock = &entry->lock; + /* + * keep it locked, the caller is responsible + * for unlocking this when they're done with it + */ + } + return ref; } -- cgit v0.10.2 From 21edbb223ed2af88b090e7945af7d91d672e3aa6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Tue, 16 Mar 2010 05:29:54 +0000 Subject: NET: netpoll, fix potential NULL ptr dereference Stanse found that one error path in netpoll_setup dereferences npinfo even though it is NULL. Avoid that by adding new label and go to that instead. Signed-off-by: Jiri Slaby Cc: Daniel Borkmann Cc: David S. Miller Acked-by: chavey@google.com Acked-by: Matt Mackall Signed-off-by: David S. Miller diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 7aa6972..d4ec38f 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -735,7 +735,7 @@ int netpoll_setup(struct netpoll *np) npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL); if (!npinfo) { err = -ENOMEM; - goto release; + goto put; } npinfo->rx_flags = 0; @@ -845,7 +845,7 @@ int netpoll_setup(struct netpoll *np) kfree(npinfo); } - +put: dev_put(ndev); return err; } -- cgit v0.10.2 From 0ba8c9ec25465cd0680b80c0f5836f558e3b972d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?YOSHIFUJI=20Hideaki=20/=20=E5=90=89=E8=97=A4=E8=8B=B1?= =?UTF-8?q?=E6=98=8E?= Date: Mon, 15 Mar 2010 19:27:00 +0000 Subject: bridge br_multicast: Fix handling of Max Response Code in IGMPv3 message. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 398221e..19618f2 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -852,8 +852,8 @@ static int br_multicast_query(struct net_bridge *br, if (ih3->nsrcs) goto out; - max_delay = ih3->code ? 1 : - IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE); + max_delay = ih3->code ? + IGMPV3_MRC(ih3->code) * (HZ / IGMP_TIMER_SCALE) : 1; } if (!group) -- cgit v0.10.2 From 8440853bb70010f2f8edc26cb0f7af60da8b2c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?YOSHIFUJI=20Hideaki=20/=20=E5=90=89=E8=97=A4=E8=8B=B1?= =?UTF-8?q?=E6=98=8E?= Date: Mon, 15 Mar 2010 19:26:56 +0000 Subject: bridge br_multicast: Fix skb leakage in error path. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 19618f2..6980625 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -991,7 +991,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, err = pskb_trim_rcsum(skb2, len); if (err) - return err; + goto err_out; } len -= ip_hdrlen(skb2); @@ -1013,7 +1013,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, case CHECKSUM_NONE: skb2->csum = 0; if (skb_checksum_complete(skb2)) - return -EINVAL; + goto out; } err = 0; @@ -1040,6 +1040,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, out: __skb_push(skb2, offset); +err_out: if (skb2 != skb) kfree_skb(skb2); return err; -- cgit v0.10.2 From 858a18a6a2f74e8f0e5b2e9671d4b74694aba708 Mon Sep 17 00:00:00 2001 From: Vitaliy Gusev Date: Tue, 16 Mar 2010 01:07:51 +0000 Subject: route: Fix caught BUG_ON during rt_secret_rebuild_oneshot() route: Fix caught BUG_ON during rt_secret_rebuild_oneshot() Call rt_secret_rebuild can cause BUG_ON(timer_pending(&net->ipv4.rt_secret_timer)) in add_timer as there is not any synchronization for call rt_secret_rebuild_oneshot() for the same net namespace. Also this issue affects to rt_secret_reschedule(). Thus use mod_timer enstead. Signed-off-by: Vitaliy Gusev Acked-by: Neil Horman Signed-off-by: David S. Miller diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d9b4024..a770df24 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -932,10 +932,8 @@ static void rt_secret_rebuild_oneshot(struct net *net) { del_timer_sync(&net->ipv4.rt_secret_timer); rt_cache_invalidate(net); - if (ip_rt_secret_interval) { - net->ipv4.rt_secret_timer.expires += ip_rt_secret_interval; - add_timer(&net->ipv4.rt_secret_timer); - } + if (ip_rt_secret_interval) + mod_timer(&net->ipv4.rt_secret_timer, jiffies + ip_rt_secret_interval); } static void rt_emergency_hash_rebuild(struct net *net) @@ -3103,22 +3101,20 @@ static void rt_secret_reschedule(int old) rtnl_lock(); for_each_net(net) { int deleted = del_timer_sync(&net->ipv4.rt_secret_timer); + long time; if (!new) continue; if (deleted) { - long time = net->ipv4.rt_secret_timer.expires - jiffies; + time = net->ipv4.rt_secret_timer.expires - jiffies; if (time <= 0 || (time += diff) <= 0) time = 0; - - net->ipv4.rt_secret_timer.expires = time; } else - net->ipv4.rt_secret_timer.expires = new; + time = new; - net->ipv4.rt_secret_timer.expires += jiffies; - add_timer(&net->ipv4.rt_secret_timer); + mod_timer(&net->ipv4.rt_secret_timer, jiffies + time); } rtnl_unlock(); } -- cgit v0.10.2 From 32dec5dd0233ebffa9cae25ce7ba6daeb7df4467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?YOSHIFUJI=20Hideaki=20/=20=E5=90=89=E8=97=A4=E8=8B=B1?= =?UTF-8?q?=E6=98=8E?= Date: Mon, 15 Mar 2010 21:51:18 +0000 Subject: bridge br_multicast: Don't refer to BR_INPUT_SKB_CB(skb)->mrouters_only without IGMP snooping. Without CONFIG_BRIDGE_IGMP_SNOOPING, BR_INPUT_SKB_CB(skb)->mrouters_only is not appropriately initialized, so we can see garbage. A clear option to fix this is to set it even without that config, but we cannot optimize out the branch. Let's introduce a macro that returns value of mrouters_only and let it return 0 without CONFIG_BRIDGE_IGMP_SNOOPING. Signed-off-by: YOSHIFUJI Hideaki Signed-off-by: David S. Miller diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index eb7062d..90a9024 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -40,7 +40,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) goto out; mdst = br_mdb_get(br, skb); - if (mdst || BR_INPUT_SKB_CB(skb)->mrouters_only) + if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) br_multicast_deliver(mdst, skb); else br_flood_deliver(br, skb); diff --git a/net/bridge/br_input.c b/net/bridge/br_input.c index 08a72e6..d74d570 100644 --- a/net/bridge/br_input.c +++ b/net/bridge/br_input.c @@ -70,7 +70,7 @@ int br_handle_frame_finish(struct sk_buff *skb) if (is_multicast_ether_addr(dest)) { mdst = br_mdb_get(br, skb); - if (mdst || BR_INPUT_SKB_CB(skb)->mrouters_only) { + if (mdst || BR_INPUT_SKB_CB_MROUTERS_ONLY(skb)) { if ((mdst && !hlist_unhashed(&mdst->mglist)) || br_multicast_is_router(br)) skb2 = skb; diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index bfb8feb..846d7d1 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -206,12 +206,20 @@ struct net_bridge struct br_input_skb_cb { struct net_device *brdev; +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING int igmp; int mrouters_only; +#endif }; #define BR_INPUT_SKB_CB(__skb) ((struct br_input_skb_cb *)(__skb)->cb) +#ifdef CONFIG_BRIDGE_IGMP_SNOOPING +# define BR_INPUT_SKB_CB_MROUTERS_ONLY(__skb) (BR_INPUT_SKB_CB(__skb)->mrouters_only) +#else +# define BR_INPUT_SKB_CB_MROUTERS_ONLY(__skb) (0) +#endif + extern struct notifier_block br_device_notifier; extern const u8 br_group_address[ETH_ALEN]; -- cgit v0.10.2 From 87faf3ccf1c939938600ab57c6c9ed5bd2e5f4cc Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Tue, 16 Mar 2010 14:37:47 -0700 Subject: bridge: Make first arg to deliver_clone const. Otherwise we get a warning from the call in br_forward(). Signed-off-by: David S. Miller diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c index 8347916..8dbec83 100644 --- a/net/bridge/br_forward.c +++ b/net/bridge/br_forward.c @@ -19,7 +19,8 @@ #include #include "br_private.h" -static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb, +static int deliver_clone(const struct net_bridge_port *prev, + struct sk_buff *skb, void (*__packet_hook)(const struct net_bridge_port *p, struct sk_buff *skb)); @@ -112,7 +113,8 @@ void br_forward(const struct net_bridge_port *to, struct sk_buff *skb, struct sk kfree_skb(skb); } -static int deliver_clone(struct net_bridge_port *prev, struct sk_buff *skb, +static int deliver_clone(const struct net_bridge_port *prev, + struct sk_buff *skb, void (*__packet_hook)(const struct net_bridge_port *p, struct sk_buff *skb)) { -- cgit v0.10.2 From 22001a13d09d82772e831dcdac0553994a4bac5d Mon Sep 17 00:00:00 2001 From: Tilman Schmidt Date: Wed, 17 Mar 2010 14:22:07 -0700 Subject: gigaset: fix build failure Update the dummy LL interface to the LL interface change introduced by commit daab433c03c15fd642c71c94eb51bdd3f32602c8. This fixes the build failure occurring after that commit when enabling ISDN_DRV_GIGASET but neither ISDN_I4L nor ISDN_CAPI. Impact: bugfix Signed-off-by: Tilman Schmidt Signed-off-by: David S. Miller diff --git a/drivers/isdn/gigaset/dummyll.c b/drivers/isdn/gigaset/dummyll.c index 5b27c99..bd0b1ea 100644 --- a/drivers/isdn/gigaset/dummyll.c +++ b/drivers/isdn/gigaset/dummyll.c @@ -57,12 +57,20 @@ void gigaset_isdn_stop(struct cardstate *cs) { } -int gigaset_isdn_register(struct cardstate *cs, const char *isdnid) +int gigaset_isdn_regdev(struct cardstate *cs, const char *isdnid) { - pr_info("no ISDN subsystem interface\n"); return 1; } -void gigaset_isdn_unregister(struct cardstate *cs) +void gigaset_isdn_unregdev(struct cardstate *cs) +{ +} + +void gigaset_isdn_regdrv(void) +{ + pr_info("no ISDN subsystem interface\n"); +} + +void gigaset_isdn_unregdrv(void) { } -- cgit v0.10.2