From 3ccd4c6167d3b39d52631767ebbf8b5677c5855d Mon Sep 17 00:00:00 2001 From: Oliver Hartkopp Date: Tue, 12 Jan 2010 02:00:46 -0800 Subject: can: Unify droping of invalid tx skbs and netdev stats To prevent the CAN drivers to operate on invalid socketbuffers the skbs are now checked and silently dropped at the xmit-function consistently. Also the netdev stats are consistently using the CAN data length code (dlc) for [rx|tx]_bytes now. Signed-off-by: Oliver Hartkopp Acked-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/ti_hecc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/net/can/ti_hecc.c') diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 5c993c2da528..7d370e32a7a8 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -477,6 +477,9 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) u32 mbxno, mbx_mask, data; unsigned long flags; + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + mbxno = get_tx_head_mb(priv); mbx_mask = BIT(mbxno); spin_lock_irqsave(&priv->mbx_lock, flags); @@ -491,7 +494,6 @@ static netdev_tx_t ti_hecc_xmit(struct sk_buff *skb, struct net_device *ndev) spin_unlock_irqrestore(&priv->mbx_lock, flags); /* Prepare mailbox for transmission */ - data = min_t(u8, cf->can_dlc, 8); if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ data |= HECC_CANMCF_RTR; data |= get_tx_head_prio(priv) << 8; -- cgit v1.2.3 From ad72c347e56bf3a0231b9d686e17764157d2961c Mon Sep 17 00:00:00 2001 From: Christian Pellegrin Date: Thu, 14 Jan 2010 07:08:34 +0000 Subject: can: Proper ctrlmode handling for CAN devices This patch adds error checking of ctrlmode values for CAN devices. As an example all availabe bits are implemented in the mcp251x driver. Signed-off-by: Christian Pellegrin Acked-by: Wolfgang Grandegger Signed-off-by: David S. Miller --- drivers/net/can/at91_can.c | 1 + drivers/net/can/bfin_can.c | 1 + drivers/net/can/dev.c | 2 ++ drivers/net/can/mcp251x.c | 11 ++++++++++- drivers/net/can/mscan/mscan.c | 1 + drivers/net/can/sja1000/sja1000.c | 1 + drivers/net/can/ti_hecc.c | 1 + drivers/net/can/usb/ems_usb.c | 1 + include/linux/can/dev.h | 1 + 9 files changed, 19 insertions(+), 1 deletion(-) (limited to 'drivers/net/can/ti_hecc.c') diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index f7287497ba6e..a2f29a38798a 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1073,6 +1073,7 @@ static int __init at91_can_probe(struct platform_device *pdev) priv->can.bittiming_const = &at91_bittiming_const; priv->can.do_set_bittiming = at91_set_bittiming; priv->can.do_set_mode = at91_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; priv->reg_base = addr; priv->dev = dev; priv->clk = clk; diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index 7e1926e79e98..bf7f9ba2d903 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -603,6 +603,7 @@ struct net_device *alloc_bfin_candev(void) priv->can.bittiming_const = &bfin_can_bittiming_const; priv->can.do_set_bittiming = bfin_can_set_bittiming; priv->can.do_set_mode = bfin_can_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; return dev; } diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index c1bb29f0322b..f08f1202ff00 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -592,6 +592,8 @@ static int can_changelink(struct net_device *dev, if (dev->flags & IFF_UP) return -EBUSY; cm = nla_data(data[IFLA_CAN_CTRLMODE]); + if (cm->flags & ~priv->ctrlmode_supported) + return -EOPNOTSUPP; priv->ctrlmode &= ~cm->mask; priv->ctrlmode |= cm->flags; } diff --git a/drivers/net/can/mcp251x.c b/drivers/net/can/mcp251x.c index afa2fa45fed9..bbe186b5a0ed 100644 --- a/drivers/net/can/mcp251x.c +++ b/drivers/net/can/mcp251x.c @@ -539,9 +539,14 @@ static void mcp251x_set_normal_mode(struct spi_device *spi) if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { /* Put device into loopback mode */ mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK); + } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { + /* Put device into listen-only mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LISTEN_ONLY); } else { /* Put device into normal mode */ - mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL); + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL | + (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT ? + CANCTRL_OSM : 0)); /* Wait for the device to enter normal mode */ timeout = jiffies + HZ; @@ -948,6 +953,10 @@ static int __devinit mcp251x_can_probe(struct spi_device *spi) priv->can.bittiming_const = &mcp251x_bittiming_const; priv->can.do_set_mode = mcp251x_do_set_mode; priv->can.clock.freq = pdata->oscillator_frequency / 2; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; + if (pdata->model == CAN_MCP251X_MCP2515) + priv->can.ctrlmode_supported |= CAN_CTRLMODE_ONE_SHOT; priv->net = net; dev_set_drvdata(&spi->dev, priv); diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index 40827c128b65..6b7dd578d417 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -686,6 +686,7 @@ struct net_device *alloc_mscandev(void) priv->can.bittiming_const = &mscan_bittiming_const; priv->can.do_set_bittiming = mscan_do_set_bittiming; priv->can.do_set_mode = mscan_do_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; for (i = 0; i < TX_QUEUE_SIZE; i++) { priv->tx_queue[i].id = i; diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 345304d779b9..ace103a44833 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -567,6 +567,7 @@ struct net_device *alloc_sja1000dev(int sizeof_priv) priv->can.bittiming_const = &sja1000_bittiming_const; priv->can.do_set_bittiming = sja1000_set_bittiming; priv->can.do_set_mode = sja1000_set_mode; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; if (sizeof_priv) priv->priv = (void *)priv + sizeof(struct sja1000_priv); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 7d370e32a7a8..8332e242b0be 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -909,6 +909,7 @@ static int ti_hecc_probe(struct platform_device *pdev) priv->can.bittiming_const = &ti_hecc_bittiming_const; priv->can.do_set_mode = ti_hecc_do_set_mode; priv->can.do_get_state = ti_hecc_get_state; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; ndev->irq = irq->start; ndev->flags |= IFF_ECHO; diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index ddb17e256656..bfab283ba9b1 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -1022,6 +1022,7 @@ static int ems_usb_probe(struct usb_interface *intf, dev->can.bittiming_const = &ems_usb_bittiming_const; dev->can.do_set_bittiming = ems_usb_set_bittiming; dev->can.do_set_mode = ems_usb_set_mode; + dev->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; netdev->flags |= IFF_ECHO; /* we support local echo */ diff --git a/include/linux/can/dev.h b/include/linux/can/dev.h index 7e7c98a3e908..c8c660a79f90 100644 --- a/include/linux/can/dev.h +++ b/include/linux/can/dev.h @@ -38,6 +38,7 @@ struct can_priv { enum can_state state; u32 ctrlmode; + u32 ctrlmode_supported; int restart_ms; struct timer_list restart_timer; -- cgit v1.2.3 From 35e2da46d25a53e0e19956f533cc29272a6cceb2 Mon Sep 17 00:00:00 2001 From: Sriram Date: Mon, 22 Feb 2010 03:35:36 +0000 Subject: can:ti_hecc: Add pm hook-up Added the suspend and resume implementation in the HECC (CAN) driver. Signed-off-by: K R Baalaaji Signed-off-by: Sriramakrishnan Acked-by: Anant Gole Signed-off-by: David S. Miller --- drivers/net/can/ti_hecc.c | 51 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) (limited to 'drivers/net/can/ti_hecc.c') diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 8332e242b0be..b861cd561074 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -826,7 +826,6 @@ static int ti_hecc_open(struct net_device *ndev) return err; } - clk_enable(priv->clk); ti_hecc_start(ndev); napi_enable(&priv->napi); netif_start_queue(ndev); @@ -842,7 +841,6 @@ static int ti_hecc_close(struct net_device *ndev) napi_disable(&priv->napi); ti_hecc_stop(ndev); free_irq(ndev->irq, ndev); - clk_disable(priv->clk); close_candev(ndev); return 0; @@ -928,6 +926,7 @@ static int ti_hecc_probe(struct platform_device *pdev) netif_napi_add(ndev, &priv->napi, ti_hecc_rx_poll, HECC_DEF_NAPI_WEIGHT); + clk_enable(priv->clk); err = register_candev(ndev); if (err) { dev_err(&pdev->dev, "register_candev() failed\n"); @@ -956,6 +955,7 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct ti_hecc_priv *priv = netdev_priv(ndev); + clk_disable(priv->clk); clk_put(priv->clk); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); iounmap(priv->base); @@ -967,6 +967,48 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev) return 0; } + +#ifdef CONFIG_PM +static int ti_hecc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + hecc_set_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_SLEEPING; + + clk_disable(priv->clk); + + return 0; +} + +static int ti_hecc_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct ti_hecc_priv *priv = netdev_priv(dev); + + clk_enable(priv->clk); + + hecc_clear_bit(priv, HECC_CANMC, HECC_CANMC_PDR); + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#else +#define ti_hecc_suspend NULL +#define ti_hecc_resume NULL +#endif + /* TI HECC netdevice driver: platform driver structure */ static struct platform_driver ti_hecc_driver = { .driver = { @@ -975,6 +1017,8 @@ static struct platform_driver ti_hecc_driver = { }, .probe = ti_hecc_probe, .remove = __devexit_p(ti_hecc_remove), + .suspend = ti_hecc_suspend, + .resume = ti_hecc_resume, }; static int __init ti_hecc_init_driver(void) @@ -982,14 +1026,15 @@ static int __init ti_hecc_init_driver(void) printk(KERN_INFO DRV_DESC "\n"); return platform_driver_register(&ti_hecc_driver); } -module_init(ti_hecc_init_driver); static void __exit ti_hecc_exit_driver(void) { printk(KERN_INFO DRV_DESC " unloaded\n"); platform_driver_unregister(&ti_hecc_driver); } + module_exit(ti_hecc_exit_driver); +module_init(ti_hecc_init_driver); MODULE_AUTHOR("Anant Gole "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 773c3e75d1fc7ea5058bfeab5d82bac5b85f8cd8 Mon Sep 17 00:00:00 2001 From: Sriramakrishnan Date: Fri, 26 Feb 2010 05:22:03 -0800 Subject: can: ti hecc module : add platform specific initialization callback. CAN module on AM3517 requires programming of IO expander as part of init sequence - to enable CAN PHY. Added platform specific callback to handle phy control(switch on /off). Signed-off-by: Sriramakrishnan Signed-off-by: David S. Miller --- drivers/net/can/ti_hecc.c | 17 ++++++++++++++++- include/linux/can/platform/ti_hecc.h | 8 ++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'drivers/net/can/ti_hecc.c') diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index b861cd561074..0c3d2ba0d178 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -28,9 +28,11 @@ * .mbx_offset = 0x2000, * .int_line = 0, * .revision = 1, + * .transceiver_switch = hecc_phy_control, * }; * - * Please see include/can/platform/ti_hecc.h for description of above fields + * Please see include/linux/can/platform/ti_hecc.h for description of + * above fields. * */ @@ -220,6 +222,7 @@ struct ti_hecc_priv { u32 tx_head; u32 tx_tail; u32 rx_next; + void (*transceiver_switch)(int); }; static inline int get_tx_head_mb(struct ti_hecc_priv *priv) @@ -317,6 +320,13 @@ static int ti_hecc_set_btc(struct ti_hecc_priv *priv) return 0; } +static void ti_hecc_transceiver_switch(const struct ti_hecc_priv *priv, + int on) +{ + if (priv->transceiver_switch) + priv->transceiver_switch(on); +} + static void ti_hecc_reset(struct net_device *ndev) { u32 cnt; @@ -818,10 +828,13 @@ static int ti_hecc_open(struct net_device *ndev) return err; } + ti_hecc_transceiver_switch(priv, 1); + /* Open common can device */ err = open_candev(ndev); if (err) { dev_err(ndev->dev.parent, "open_candev() failed %d\n", err); + ti_hecc_transceiver_switch(priv, 0); free_irq(ndev->irq, ndev); return err; } @@ -842,6 +855,7 @@ static int ti_hecc_close(struct net_device *ndev) ti_hecc_stop(ndev); free_irq(ndev->irq, ndev); close_candev(ndev); + ti_hecc_transceiver_switch(priv, 0); return 0; } @@ -903,6 +917,7 @@ static int ti_hecc_probe(struct platform_device *pdev) priv->hecc_ram_offset = pdata->hecc_ram_offset; priv->mbx_offset = pdata->mbx_offset; priv->int_line = pdata->int_line; + priv->transceiver_switch = pdata->transceiver_switch; priv->can.bittiming_const = &ti_hecc_bittiming_const; priv->can.do_set_mode = ti_hecc_do_set_mode; diff --git a/include/linux/can/platform/ti_hecc.h b/include/linux/can/platform/ti_hecc.h index 4688c7bb1bd1..af17cb3f7a84 100644 --- a/include/linux/can/platform/ti_hecc.h +++ b/include/linux/can/platform/ti_hecc.h @@ -1,3 +1,6 @@ +#ifndef __CAN_PLATFORM_TI_HECC_H__ +#define __CAN_PLATFORM_TI_HECC_H__ + /* * TI HECC (High End CAN Controller) driver platform header * @@ -23,6 +26,7 @@ * @mbx_offset: Mailbox RAM offset * @int_line: Interrupt line to use - 0 or 1 * @version: version for future use + * @transceiver_switch: platform specific callback fn for transceiver control * * Platform data structure to get all platform specific settings. * this structure also accounts the fact that the IP may have different @@ -35,6 +39,6 @@ struct ti_hecc_platform_data { u32 mbx_offset; u32 int_line; u32 version; + void (*transceiver_switch) (int); }; - - +#endif -- cgit v1.2.3