* [PATCH 06/12] usbnet: mcs7830: apply introduced usb command APIs
From: Ming Lei @ 2012-10-02 6:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman
Cc: Oliver Neukum, netdev, linux-usb, Ming Lei
In-Reply-To: <1349160684-6627-1-git-send-email-ming.lei@canonical.com>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
drivers/net/usb/mcs7830.c | 85 ++++-----------------------------------------
1 file changed, 6 insertions(+), 79 deletions(-)
diff --git a/drivers/net/usb/mcs7830.c b/drivers/net/usb/mcs7830.c
index 03c2d8d..db46a68 100644
--- a/drivers/net/usb/mcs7830.c
+++ b/drivers/net/usb/mcs7830.c
@@ -123,93 +123,20 @@ static const char driver_name[] = "MOSCHIP usb-ethernet driver";
static int mcs7830_get_reg(struct usbnet *dev, u16 index, u16 size, void *data)
{
- struct usb_device *xdev = dev->udev;
- int ret;
- void *buffer;
-
- buffer = kmalloc(size, GFP_NOIO);
- if (buffer == NULL)
- return -ENOMEM;
-
- ret = usb_control_msg(xdev, usb_rcvctrlpipe(xdev, 0), MCS7830_RD_BREQ,
- MCS7830_RD_BMREQ, 0x0000, index, buffer,
- size, MCS7830_CTRL_TIMEOUT);
- memcpy(data, buffer, size);
- kfree(buffer);
-
- return ret;
+ return usbnet_read_cmd(dev, MCS7830_RD_BREQ, MCS7830_RD_BMREQ,
+ 0x0000, index, data, size);
}
static int mcs7830_set_reg(struct usbnet *dev, u16 index, u16 size, const void *data)
{
- struct usb_device *xdev = dev->udev;
- int ret;
- void *buffer;
-
- buffer = kmemdup(data, size, GFP_NOIO);
- if (buffer == NULL)
- return -ENOMEM;
-
- ret = usb_control_msg(xdev, usb_sndctrlpipe(xdev, 0), MCS7830_WR_BREQ,
- MCS7830_WR_BMREQ, 0x0000, index, buffer,
- size, MCS7830_CTRL_TIMEOUT);
- kfree(buffer);
- return ret;
-}
-
-static void mcs7830_async_cmd_callback(struct urb *urb)
-{
- struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context;
- int status = urb->status;
-
- if (status < 0)
- printk(KERN_DEBUG "%s() failed with %d\n",
- __func__, status);
-
- kfree(req);
- usb_free_urb(urb);
+ return usbnet_write_cmd(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ,
+ 0x0000, index, data, size);
}
static void mcs7830_set_reg_async(struct usbnet *dev, u16 index, u16 size, void *data)
{
- struct usb_ctrlrequest *req;
- int ret;
- struct urb *urb;
-
- urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!urb) {
- dev_dbg(&dev->udev->dev,
- "Error allocating URB in write_cmd_async!\n");
- return;
- }
-
- req = kmalloc(sizeof *req, GFP_ATOMIC);
- if (!req) {
- dev_err(&dev->udev->dev,
- "Failed to allocate memory for control request\n");
- goto out;
- }
- req->bRequestType = MCS7830_WR_BMREQ;
- req->bRequest = MCS7830_WR_BREQ;
- req->wValue = 0;
- req->wIndex = cpu_to_le16(index);
- req->wLength = cpu_to_le16(size);
-
- usb_fill_control_urb(urb, dev->udev,
- usb_sndctrlpipe(dev->udev, 0),
- (void *)req, data, size,
- mcs7830_async_cmd_callback, req);
-
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret < 0) {
- dev_err(&dev->udev->dev,
- "Error submitting the control message: ret=%d\n", ret);
- goto out;
- }
- return;
-out:
- kfree(req);
- usb_free_urb(urb);
+ usbnet_write_cmd_async(dev, MCS7830_WR_BREQ, MCS7830_WR_BMREQ,
+ 0x0000, index, data, size);
}
static int mcs7830_hif_get_mac_address(struct usbnet *dev, unsigned char *addr)
--
1.7.9.5
^ permalink raw reply related
* [PATCH 09/12] usbnet: sierra_net: apply introduced usb command APIs
From: Ming Lei @ 2012-10-02 6:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman
Cc: Oliver Neukum, netdev, linux-usb, Ming Lei
In-Reply-To: <1349160684-6627-1-git-send-email-ming.lei@canonical.com>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
drivers/net/usb/sierra_net.c | 45 ++++++++++++++++--------------------------
1 file changed, 17 insertions(+), 28 deletions(-)
diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c
index c27d277..eb5c7a8 100644
--- a/drivers/net/usb/sierra_net.c
+++ b/drivers/net/usb/sierra_net.c
@@ -311,10 +311,9 @@ static int sierra_net_send_cmd(struct usbnet *dev,
struct sierra_net_data *priv = sierra_net_get_private(dev);
int status;
- status = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
- USB_CDC_SEND_ENCAPSULATED_COMMAND,
- USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE, 0,
- priv->ifnum, cmd, cmdlen, USB_CTRL_SET_TIMEOUT);
+ status = usbnet_write_cmd(dev, USB_CDC_SEND_ENCAPSULATED_COMMAND,
+ USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE,
+ 0, priv->ifnum, cmd, cmdlen);
if (status != cmdlen && status != -ENODEV)
netdev_err(dev->net, "Submit %s failed %d\n", cmd_name, status);
@@ -632,32 +631,22 @@ static int sierra_net_change_mtu(struct net_device *net, int new_mtu)
static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap)
{
int result = 0;
- u16 *attrdata;
-
- attrdata = kmalloc(sizeof(*attrdata), GFP_KERNEL);
- if (!attrdata)
- return -ENOMEM;
-
- result = usb_control_msg(
- dev->udev,
- usb_rcvctrlpipe(dev->udev, 0),
- /* _u8 vendor specific request */
- SWI_USB_REQUEST_GET_FW_ATTR,
- USB_DIR_IN | USB_TYPE_VENDOR, /* __u8 request type */
- 0x0000, /* __u16 value not used */
- 0x0000, /* __u16 index not used */
- attrdata, /* char *data */
- sizeof(*attrdata), /* __u16 size */
- USB_CTRL_SET_TIMEOUT); /* int timeout */
-
- if (result < 0) {
- kfree(attrdata);
+ u16 attrdata;
+
+ result = usbnet_read_cmd(dev,
+ /* _u8 vendor specific request */
+ SWI_USB_REQUEST_GET_FW_ATTR,
+ USB_DIR_IN | USB_TYPE_VENDOR, /* __u8 request type */
+ 0x0000, /* __u16 value not used */
+ 0x0000, /* __u16 index not used */
+ &attrdata, /* char *data */
+ sizeof(attrdata) /* __u16 size */
+ );
+
+ if (result < 0)
return -EIO;
- }
-
- *datap = le16_to_cpu(*attrdata);
- kfree(attrdata);
+ *datap = le16_to_cpu(attrdata);
return result;
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 10/12] usbnet: smsc75xx: apply introduced usb command APIs
From: Ming Lei @ 2012-10-02 6:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman
Cc: Oliver Neukum, netdev, linux-usb, Ming Lei
In-Reply-To: <1349160684-6627-1-git-send-email-ming.lei@canonical.com>
Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
drivers/net/usb/smsc75xx.c | 39 ++++++++++++++-------------------------
1 file changed, 14 insertions(+), 25 deletions(-)
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index b77ae76..1baa53a 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -85,26 +85,21 @@ 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);
+ u32 buf;
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);
-
+ ret = usbnet_read_cmd(dev, USB_VENDOR_REQUEST_READ_REGISTER,
+ USB_DIR_IN | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ 0, index, &buf, 4);
if (unlikely(ret < 0))
netdev_warn(dev->net,
"Failed to read reg index 0x%08x: %d", index, ret);
- le32_to_cpus(buf);
- *data = *buf;
- kfree(buf);
+ le32_to_cpus(&buf);
+ *data = buf;
return ret;
}
@@ -112,28 +107,22 @@ static int __must_check smsc75xx_read_reg(struct usbnet *dev, u32 index,
static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index,
u32 data)
{
- u32 *buf = kmalloc(4, GFP_KERNEL);
+ u32 buf;
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);
+ buf = data;
+ cpu_to_le32s(&buf);
+ ret = usbnet_write_cmd(dev, USB_VENDOR_REQUEST_WRITE_REGISTER,
+ USB_DIR_OUT | USB_TYPE_VENDOR |
+ USB_RECIP_DEVICE,
+ 0, index, &buf, 4);
if (unlikely(ret < 0))
netdev_warn(dev->net,
"Failed to write reg index 0x%08x: %d", index, ret);
- kfree(buf);
-
return ret;
}
--
1.7.9.5
^ permalink raw reply related
* [PATCH 12/12] usbnet: make device out of suspend before calling usbnet_read/write_cmd
From: Ming Lei @ 2012-10-02 6:51 UTC (permalink / raw)
To: David S. Miller, Greg Kroah-Hartman
Cc: Oliver Neukum, netdev, linux-usb, Ming Lei
In-Reply-To: <1349160684-6627-1-git-send-email-ming.lei@canonical.com>
This patche gets the runtime PM reference count before calling
usb_control_msg, and puts it after completion of the
usb_control_msg, so that the usb control message can always be
sent to one active device.
Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
drivers/net/usb/usbnet.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 3b51554..3f4bc69 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -1609,9 +1609,11 @@ int usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
goto out;
}
+ usb_autopm_get_interface(dev->intf);
err = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
cmd, reqtype, value, index, buf, size,
USB_CTRL_GET_TIMEOUT);
+ usb_autopm_put_interface(dev->intf);
if (err > 0 && err <= size)
memcpy(data, buf, err);
kfree(buf);
@@ -1636,9 +1638,11 @@ int usbnet_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
goto out;
}
+ usb_autopm_get_interface(dev->intf);
err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
cmd, reqtype, value, index, buf, size,
USB_CTRL_SET_TIMEOUT);
+ usb_autopm_put_interface(dev->intf);
kfree(buf);
out:
--
1.7.9.5
^ permalink raw reply related
* Re: [RFC PATCH net-next] tcp: introduce tcp_tw_interval to specifiy the time of TIME-WAIT
From: Cong Wang @ 2012-10-02 7:04 UTC (permalink / raw)
To: Neil Horman
Cc: netdev, David S. Miller, Alexey Kuznetsov, Patrick McHardy,
Eric Dumazet
In-Reply-To: <20120928131642.GA31568@hmsreliant.think-freely.org>
On Fri, 2012-09-28 at 09:16 -0400, Neil Horman wrote:
> On Fri, Sep 28, 2012 at 02:33:07PM +0800, Cong Wang wrote:
> > On Thu, 2012-09-27 at 10:23 -0400, Neil Horman wrote:
> > > On Thu, Sep 27, 2012 at 04:41:01PM +0800, Cong Wang wrote:
> > > > Some customer requests this feature, as they stated:
> > > >
> > > > "This parameter is necessary, especially for software that continually
> > > > creates many ephemeral processes which open sockets, to avoid socket
> > > > exhaustion. In many cases, the risk of the exhaustion can be reduced by
> > > > tuning reuse interval to allow sockets to be reusable earlier.
> > > >
> > > > In commercial Unix systems, this kind of parameters, such as
> > > > tcp_timewait in AIX and tcp_time_wait_interval in HP-UX, have
> > > > already been available. Their implementations allow users to tune
> > > > how long they keep TCP connection as TIME-WAIT state on the
> > > > millisecond time scale."
> > > >
> > > > We indeed have "tcp_tw_reuse" and "tcp_tw_recycle", but these tunings
> > > > are not equivalent in that they cannot be tuned directly on the time
> > > > scale nor in a safe way, as some combinations of tunings could still
> > > > cause some problem in NAT. And, I think second scale is enough, we don't
> > > > have to make it in millisecond time scale.
> > > >
> > > I think I have a little difficultly seeing how this does anything other than
> > > pay lip service to actually having sockets spend time in TIME_WAIT state. That
> > > is to say, while I see users using this to just make the pain stop. If we wait
> > > less time than it takes to be sure that a connection isn't being reused (either
> > > by waiting two segment lifetimes, or by checking timestamps), then you might as
> > > well not wait at all. I see how its tempting to be able to say "Just don't wait
> > > as long", but it seems that theres no difference between waiting half as long as
> > > the RFC mandates, and waiting no time at all. Neither is a good idea.
> >
> > I don't think reducing TIME_WAIT is a good idea either, but there must
> > be some reason behind as several UNIX provides a microsecond-scale
> > tuning interface, or maybe in non-recycle mode, their RTO is much less
> > than 2*MSL?
> >
> My guess? Cash was the reason. I certainly wasn't there for any of those
> developments, but a setting like this just smells to me like some customer waved
> some cash under IBM's/HP's/Sun's nose and said, "We'd like to get our tcp
> sockets back to CLOSED state faster, what can you do for us?"
Yeah, maybe. But it still doesn't make sense even if they are sure their
packets are impossible to linger in their high-speed LAN for 2*MSL?
>
> > >
> > > Given the problem you're trying to solve here, I'll ask the standard question in
> > > response: How does using SO_REUSEADDR not solve the problem? Alternatively, in
> > > a pinch, why not reduce the tcp_max_tw_buckets sufficiently to start forcing
> > > TIME_WAIT sockets back into CLOSED state?
> > >
> > > The code looks fine, but the idea really doesn't seem like a good plan to me.
> > > I'm sure HPUX/Solaris/AIX/etc have done this in response to customer demand, but
> > > that doesn't make it the right solution.
> > >
> >
> > *I think* the customer doesn't want to modify their applications, so
> > that is why they don't use SO_REUSERADDR.
> >
> Well, ok, thats a legitimate distro problem. What its not is an upstream
> problem. Fixing the appilcation is the right thing to do, wether or not they
> want to.
>
> > I didn't know tcp_max_tw_buckets can do the trick, nor the customer, so
> > this is a side effect of tcp_max_tw_buckets? Is it documented?
> man 7 tcp:
> tcp_max_tw_buckets (integer; default: see below; since Linux 2.4)
> The maximum number of sockets in TIME_WAIT state allowed in the
> system. This limit exists only to prevent simple
> denial-of-service attacks. The default value of NR_FILE*2 is
> adjusted depending on the memory in the system. If this number
> is exceeded, the socket is closed and a warning is printed.
>
Hey, "a warning is printed" seems not very friendly. ;)
Thanks!
^ permalink raw reply
* Re: [PATCH v4] can: kvaser_usb: Add support for Kvaser CAN/USB devices
From: Olivier Sobrie @ 2012-10-02 7:14 UTC (permalink / raw)
To: Wolfgang Grandegger
Cc: Marc Kleine-Budde, linux-can-u79uwXL29TY76Z2rM5mHXA,
Kvaser Support, netdev-u79uwXL29TY76Z2rM5mHXA,
linux-usb-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <505DE106.6060405-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
Hi Marc and Wolfgang,
On Sat, Sep 22, 2012 at 06:02:14PM +0200, Wolfgang Grandegger wrote:
> On 09/21/2012 11:54 AM, Marc Kleine-Budde wrote:
> > On 09/20/2012 07:06 AM, Olivier Sobrie wrote:
> >> This driver provides support for several Kvaser CAN/USB devices.
> >> Such kind of devices supports up to three CAN network interfaces.
> >>
> >> It has been tested with a Kvaser USB Leaf Light (one network interface)
> >> connected to a pch_can interface.
> >> The firmware version of the Kvaser device was 2.5.205.
> >
> > I don't remember, have the USB people already had a look on your driver
> > and gave some comments?
>
> IIRC, Oliver Neukum commented on v2. Actually we ignored v3 :(, sorry!
No problem. Thanks for the review.
>
> > From the CAN and network point of view looks good, some comments inline.
> > Would be fine, if Wolfgang can give comments or Ack about the error
> > frame generation.
>
> Olivier already sent candump traces for no cable connected and
> short-circuiting CAN high and low. The error handling looked good, IIRC,
> at least as good as the firmware can do... more comments inline...
>
> >> List of Kvaser devices supported by the driver:
> >> - Kvaser Leaf prototype (P010v2 and v3)
> >> - Kvaser Leaf Light (P010v3)
> >> - Kvaser Leaf Professional HS
> >> - Kvaser Leaf SemiPro HS
> >> - Kvaser Leaf Professional LS
> >> - Kvaser Leaf Professional SWC
> >> - Kvaser Leaf Professional LIN
> >> - Kvaser Leaf SemiPro LS
> >> - Kvaser Leaf SemiPro SWC
> >> - Kvaser Memorator II, Prototype
> >> - Kvaser Memorator II HS/HS
> >> - Kvaser USBcan Professional HS/HS
> >> - Kvaser Leaf Light GI
> >> - Kvaser Leaf Professional HS (OBD-II connector)
> >> - Kvaser Memorator Professional HS/LS
> >> - Kvaser Leaf Light "China"
> >> - Kvaser BlackBird SemiPro
> >> - Kvaser OEM Mercury
> >> - Kvaser OEM Leaf
> >> - Kvaser USBcan R
> >>
> >> Signed-off-by: Olivier Sobrie <olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
> >> ---
> >> Changes since v3:
> >> - add support for CMD_LOG_MESSAGE
> >>
> >> drivers/net/can/usb/Kconfig | 33 +
> >> drivers/net/can/usb/Makefile | 1 +
> >> drivers/net/can/usb/kvaser_usb.c | 1555 ++++++++++++++++++++++++++++++++++++++
> >> 3 files changed, 1589 insertions(+)
> >> create mode 100644 drivers/net/can/usb/kvaser_usb.c
> >>
> >> diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
> >> index 0a68768..578955f 100644
> >> --- a/drivers/net/can/usb/Kconfig
> >> +++ b/drivers/net/can/usb/Kconfig
> >> @@ -13,6 +13,39 @@ config CAN_ESD_USB2
> >> This driver supports the CAN-USB/2 interface
> >> from esd electronic system design gmbh (http://www.esd.eu).
> >>
> >> +config CAN_KVASER_USB
> >> + tristate "Kvaser CAN/USB interface"
> >> + ---help---
> >> + This driver adds support for Kvaser CAN/USB devices like Kvaser
> >> + Leaf Light.
> >> +
> >> + The driver gives support for the following devices:
> >> + - Kvaser Leaf prototype (P010v2 and v3)
> >> + - Kvaser Leaf Light (P010v3)
> >> + - Kvaser Leaf Professional HS
> >> + - Kvaser Leaf SemiPro HS
> >> + - Kvaser Leaf Professional LS
> >> + - Kvaser Leaf Professional SWC
> >> + - Kvaser Leaf Professional LIN
> >> + - Kvaser Leaf SemiPro LS
> >> + - Kvaser Leaf SemiPro SWC
> >> + - Kvaser Memorator II, Prototype
> >> + - Kvaser Memorator II HS/HS
> >> + - Kvaser USBcan Professional HS/HS
> >> + - Kvaser Leaf Light GI
> >> + - Kvaser Leaf Professional HS (OBD-II connector)
> >> + - Kvaser Memorator Professional HS/LS
> >> + - Kvaser Leaf Light "China"
> >> + - Kvaser BlackBird SemiPro
> >> + - Kvaser OEM Mercury
> >> + - Kvaser OEM Leaf
> >> + - Kvaser USBcan R
> >> +
> >> + If unsure, say N.
> >> +
> >> + To compile this driver as a module, choose M here: the
> >> + module will be called kvaser_usb.
> >> +
> >> config CAN_PEAK_USB
> >> tristate "PEAK PCAN-USB/USB Pro interfaces"
> >> ---help---
> >> diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
> >> index da6d1d3..80a2ee4 100644
> >> --- a/drivers/net/can/usb/Makefile
> >> +++ b/drivers/net/can/usb/Makefile
> >> @@ -4,6 +4,7 @@
> >>
> >> obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
> >> obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
> >> +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
> >> obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
> >>
> >> ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> >> diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
> >> new file mode 100644
> >> index 0000000..3509ca5
> >> --- /dev/null
> >> +++ b/drivers/net/can/usb/kvaser_usb.c
> >> @@ -0,0 +1,1555 @@
> >> +/*
> >> + * 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 version 2.
> >> + *
> >> + * Parts of this driver are based on the following:
> >> + * - Kvaser linux leaf driver (version 4.78)
> >> + * - CAN driver for esd CAN-USB/2
> >> + *
> >> + * Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved.
> >> + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs-iOnpLzIbIdM@public.gmane.org>, esd gmbh
> >> + * Copyright (C) 2012 Olivier Sobrie <olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>
> >> + */
> >> +
> >> +#include <linux/init.h>
> >> +#include <linux/completion.h>
> >> +#include <linux/module.h>
> >> +#include <linux/netdevice.h>
> >> +#include <linux/usb.h>
> >> +
> >> +#include <linux/can.h>
> >> +#include <linux/can/dev.h>
> >> +#include <linux/can/error.h>
> >> +
> >> +#define MAX_TX_URBS 16
> >> +#define MAX_RX_URBS 4
> >> +#define START_TIMEOUT 1000 /* msecs */
> >> +#define STOP_TIMEOUT 1000 /* msecs */
> >> +#define USB_SEND_TIMEOUT 1000 /* msecs */
> >> +#define USB_RECV_TIMEOUT 1000 /* msecs */
> >> +#define RX_BUFFER_SIZE 3072
> >> +#define CAN_USB_CLOCK 8000000
> >> +#define MAX_NET_DEVICES 3
> >> +
> >> +/* Kvaser USB devices */
> >> +#define KVASER_VENDOR_ID 0x0bfd
> >> +#define USB_LEAF_DEVEL_PRODUCT_ID 10
> >> +#define USB_LEAF_LITE_PRODUCT_ID 11
> >> +#define USB_LEAF_PRO_PRODUCT_ID 12
> >> +#define USB_LEAF_SPRO_PRODUCT_ID 14
> >> +#define USB_LEAF_PRO_LS_PRODUCT_ID 15
> >> +#define USB_LEAF_PRO_SWC_PRODUCT_ID 16
> >> +#define USB_LEAF_PRO_LIN_PRODUCT_ID 17
> >> +#define USB_LEAF_SPRO_LS_PRODUCT_ID 18
> >> +#define USB_LEAF_SPRO_SWC_PRODUCT_ID 19
> >> +#define USB_MEMO2_DEVEL_PRODUCT_ID 22
> >> +#define USB_MEMO2_HSHS_PRODUCT_ID 23
> >> +#define USB_UPRO_HSHS_PRODUCT_ID 24
> >> +#define USB_LEAF_LITE_GI_PRODUCT_ID 25
> >> +#define USB_LEAF_PRO_OBDII_PRODUCT_ID 26
> >> +#define USB_MEMO2_HSLS_PRODUCT_ID 27
> >> +#define USB_LEAF_LITE_CH_PRODUCT_ID 28
> >> +#define USB_BLACKBIRD_SPRO_PRODUCT_ID 29
> >> +#define USB_OEM_MERCURY_PRODUCT_ID 34
> >> +#define USB_OEM_LEAF_PRODUCT_ID 35
> >> +#define USB_CAN_R_PRODUCT_ID 39
> >> +
> >> +/* USB devices features */
> >> +#define KVASER_HAS_SILENT_MODE BIT(0)
> >> +#define KVASER_HAS_TXRX_ERRORS BIT(1)
> >> +
> >> +/* Message header size */
> >> +#define MSG_HEADER_LEN 2
> >> +
> >> +/* Can message flags */
> >> +#define MSG_FLAG_ERROR_FRAME BIT(0)
> >> +#define MSG_FLAG_OVERRUN BIT(1)
> >> +#define MSG_FLAG_NERR BIT(2)
> >> +#define MSG_FLAG_WAKEUP BIT(3)
> >> +#define MSG_FLAG_REMOTE_FRAME BIT(4)
> >> +#define MSG_FLAG_RESERVED BIT(5)
> >> +#define MSG_FLAG_TX_ACK BIT(6)
> >> +#define MSG_FLAG_TX_REQUEST BIT(7)
> >> +
> >> +/* Can states */
> >> +#define M16C_STATE_BUS_RESET BIT(0)
> >> +#define M16C_STATE_BUS_ERROR BIT(4)
> >> +#define M16C_STATE_BUS_PASSIVE BIT(5)
> >> +#define M16C_STATE_BUS_OFF BIT(6)
> >> +
> >> +/* Can msg ids */
> >> +#define CMD_RX_STD_MESSAGE 12
> >> +#define CMD_TX_STD_MESSAGE 13
> >> +#define CMD_RX_EXT_MESSAGE 14
> >> +#define CMD_TX_EXT_MESSAGE 15
> >> +#define CMD_SET_BUS_PARAMS 16
> >> +#define CMD_GET_BUS_PARAMS 17
> >> +#define CMD_GET_BUS_PARAMS_REPLY 18
> >> +#define CMD_GET_CHIP_STATE 19
> >> +#define CMD_CHIP_STATE_EVENT 20
> >> +#define CMD_SET_CTRL_MODE 21
> >> +#define CMD_GET_CTRL_MODE 22
> >> +#define CMD_GET_CTRL_MODE_REPLY 23
> >> +#define CMD_RESET_CHIP 24
> >> +#define CMD_RESET_CARD 25
> >> +#define CMD_START_CHIP 26
> >> +#define CMD_START_CHIP_REPLY 27
> >> +#define CMD_STOP_CHIP 28
> >> +#define CMD_STOP_CHIP_REPLY 29
> >> +#define CMD_GET_CARD_INFO2 32
> >> +#define CMD_GET_CARD_INFO 34
> >> +#define CMD_GET_CARD_INFO_REPLY 35
> >> +#define CMD_GET_SOFTWARE_INFO 38
> >> +#define CMD_GET_SOFTWARE_INFO_REPLY 39
> >> +#define CMD_ERROR_EVENT 45
> >> +#define CMD_FLUSH_QUEUE 48
> >> +#define CMD_RESET_ERROR_COUNTER 49
> >> +#define CMD_TX_ACKNOWLEDGE 50
> >> +#define CMD_CAN_ERROR_EVENT 51
> >> +#define CMD_USB_THROTTLE 77
> >> +#define CMD_LOG_MESSAGE 106
> >> +
> >> +/* error factors */
> >> +#define M16C_EF_ACKE BIT(0)
> >> +#define M16C_EF_CRCE BIT(1)
> >> +#define M16C_EF_FORME BIT(2)
> >> +#define M16C_EF_STFE BIT(3)
> >> +#define M16C_EF_BITE0 BIT(4)
> >> +#define M16C_EF_BITE1 BIT(5)
> >> +#define M16C_EF_RCVE BIT(6)
> >> +#define M16C_EF_TRE BIT(7)
> >> +
> >> +/* bittiming parameters */
> >> +#define KVASER_USB_TSEG1_MIN 1
> >> +#define KVASER_USB_TSEG1_MAX 16
> >> +#define KVASER_USB_TSEG2_MIN 1
> >> +#define KVASER_USB_TSEG2_MAX 8
> >> +#define KVASER_USB_SJW_MAX 4
> >> +#define KVASER_USB_BRP_MIN 1
> >> +#define KVASER_USB_BRP_MAX 64
> >> +#define KVASER_USB_BRP_INC 1
> >> +
> >> +/* ctrl modes */
> >> +#define KVASER_CTRL_MODE_NORMAL 1
> >> +#define KVASER_CTRL_MODE_SILENT 2
> >> +#define KVASER_CTRL_MODE_SELFRECEPTION 3
> >> +#define KVASER_CTRL_MODE_OFF 4
> >> +
> >> +struct kvaser_msg_simple {
> >> + u8 tid;
> >> + u8 channel;
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_cardinfo {
> >> + u8 tid;
> >> + u8 nchannels;
> >> + __le32 serial_number;
> >> + __le32 padding;
> >> + __le32 clock_resolution;
> >> + __le32 mfgdate;
> >> + u8 ean[8];
> >> + u8 hw_revision;
> >> + u8 usb_hs_mode;
> >> + __le16 padding2;
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_cardinfo2 {
> >> + u8 tid;
> >> + u8 channel;
> >> + u8 pcb_id[24];
> >> + __le32 oem_unlock_code;
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_softinfo {
> >> + u8 tid;
> >> + u8 channel;
> >> + __le32 sw_options;
> >> + __le32 fw_version;
> >> + __le16 max_outstanding_tx;
> >> + __le16 padding[9];
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_busparams {
> >> + u8 tid;
> >> + u8 channel;
> >> + __le32 bitrate;
> >> + u8 tseg1;
> >> + u8 tseg2;
> >> + u8 sjw;
> >> + u8 no_samp;
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_tx_can {
> >> + u8 channel;
> >> + u8 tid;
> >> + u8 msg[14];
> >> + u8 padding;
> >> + u8 flags;
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_rx_can {
> >> + u8 channel;
> >> + u8 flag;
> >> + __le16 time[3];
> >> + u8 msg[14];
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_chip_state_event {
> >> + u8 tid;
> >> + u8 channel;
> >> + __le16 time[3];
> >> + u8 tx_errors_count;
> >> + u8 rx_errors_count;
> >> + u8 status;
> >> + u8 padding[3];
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_tx_acknowledge {
> >> + u8 channel;
> >> + u8 tid;
> >> + __le16 time[3];
> >> + u8 flags;
> >> + u8 time_offset;
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_error_event {
> >> + u8 tid;
> >> + u8 flags;
> >> + __le16 time[3];
> >> + u8 channel;
> >> + u8 padding;
> >> + u8 tx_errors_count;
> >> + u8 rx_errors_count;
> >> + u8 status;
> >> + u8 error_factor;
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_ctrl_mode {
> >> + u8 tid;
> >> + u8 channel;
> >> + u8 ctrl_mode;
> >> + u8 padding[3];
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_flush_queue {
> >> + u8 tid;
> >> + u8 channel;
> >> + u8 flags;
> >> + u8 padding[3];
> >> +} __packed;
> >> +
> >> +struct kvaser_msg_log_message {
> >> + u8 channel;
> >> + u8 flags;
> >> + __le16 time[3];
> >> + u8 dlc;
> >> + u8 time_offset;
> >> + __le32 id;
> >> + u8 data[8];
> >> +} __packed;
> >> +
> >> +struct kvaser_msg {
> >> + u8 len;
> >> + u8 id;
> >> + union {
> >> + struct kvaser_msg_simple simple;
> >> + struct kvaser_msg_cardinfo cardinfo;
> >> + struct kvaser_msg_cardinfo2 cardinfo2;
> >> + struct kvaser_msg_softinfo softinfo;
> >> + struct kvaser_msg_busparams busparams;
> >> + struct kvaser_msg_tx_can tx_can;
> >> + struct kvaser_msg_rx_can rx_can;
> >> + struct kvaser_msg_chip_state_event chip_state_event;
> >> + struct kvaser_msg_tx_acknowledge tx_acknowledge;
> >> + struct kvaser_msg_error_event error_event;
> >> + struct kvaser_msg_ctrl_mode ctrl_mode;
> >> + struct kvaser_msg_flush_queue flush_queue;
> >> + struct kvaser_msg_log_message log_message;
> >> + } u;
> >> +} __packed;
> >> +
> >> +struct kvaser_usb_tx_urb_context {
> >> + struct kvaser_usb_net_priv *priv;
> >> + u32 echo_index;
> >> + int dlc;
> >> +};
> >> +
> >> +struct kvaser_usb {
> >> + struct usb_device *udev;
> >> + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
> >> +
> >> + struct usb_endpoint_descriptor *bulk_in, *bulk_out;
> >> + struct usb_anchor rx_submitted;
> >> +
> >> + u32 fw_version;
> >> + unsigned int nchannels;
> >> +
> >> + bool rxinitdone;
> >> + void *rxbuf[MAX_RX_URBS];
> >> + dma_addr_t rxbuf_dma[MAX_RX_URBS];
> >> +};
> >> +
> >> +struct kvaser_usb_net_priv {
> >> + struct can_priv can;
> >> +
> >> + atomic_t active_tx_urbs;
> >> + struct usb_anchor tx_submitted;
> >> + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
> >> +
> >> + struct completion start_comp, stop_comp;
> >> +
> >> + struct kvaser_usb *dev;
> >> + struct net_device *netdev;
> >> + int channel;
> >> +
> >> + struct can_berr_counter bec;
> >> +};
> >> +
> >> +static struct usb_device_id kvaser_usb_table[] = {
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS |
> >> + KVASER_HAS_SILENT_MODE },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> >> + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
> >> + .driver_info = KVASER_HAS_TXRX_ERRORS },
> >> + { }
> >> +};
> >> +MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
> >> +
> >> +static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
> >> + struct kvaser_msg *msg)
> >> +{
> >> + int actual_len;
> >> +
> >> + return usb_bulk_msg(dev->udev,
> >> + usb_sndbulkpipe(dev->udev,
> >> + dev->bulk_out->bEndpointAddress),
> >> + msg, msg->len, &actual_len,
> >> + USB_SEND_TIMEOUT);
> >> +}
> >> +
> >> +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
> >> + struct kvaser_msg *msg)
> >> +{
> >> + struct kvaser_msg *tmp;
> >> + void *buf;
> >> + int actual_len;
> >> + int err;
> >> + int pos = 0;
> >> +
> >> + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
> >> + if (!buf)
> >> + return -ENOMEM;
> >> +
> >> + err = usb_bulk_msg(dev->udev,
> >> + usb_rcvbulkpipe(dev->udev,
> >> + dev->bulk_in->bEndpointAddress),
> >> + buf, RX_BUFFER_SIZE, &actual_len,
> >> + USB_RECV_TIMEOUT);
> >> + if (err < 0)
> >> + goto end;
> >> +
> >> + while (pos <= actual_len - MSG_HEADER_LEN) {
> >> + tmp = buf + pos;
> >> +
> >> + if (!tmp->len)
> >> + break;
> >> +
> >> + if (pos + tmp->len > actual_len) {
> >> + dev_err(dev->udev->dev.parent, "Format error\n");
> >> + break;
> >> + }
> >> +
> >> + if (tmp->id == id) {
> >> + memcpy(msg, tmp, tmp->len);
> >> + goto end;
> >> + }
> >> +
> >> + pos += tmp->len;
> >> + }
> >> +
> >> + err = -EINVAL;
> >> +
> >> +end:
> >> + kfree(buf);
> >> +
> >> + return err;
> >> +}
> >> +
> >> +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
> >> + u8 msg_id, int channel)
> >> +{
> >> + struct kvaser_msg msg = {
> >> + .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
> >> + .id = msg_id,
> >> + .u.simple.channel = channel,
> >> + .u.simple.tid = 0xff,
> >> + };
> >> +
> >> + return kvaser_usb_send_msg(dev, &msg);
> >> +}
> >> +
> >> +static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
> >> +{
> >> + struct kvaser_msg msg;
> >> + int err;
> >> +
> >> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
> >> + if (err)
> >> + return err;
> >> +
> >> + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
> >> + if (err)
> >> + return err;
> >> +
> >> + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
> >> +{
> >> + struct kvaser_msg msg;
> >> + int err;
> >> +
> >> + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
> >> + if (err)
> >> + return err;
> >> +
> >> + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
> >> + if (err)
> >> + return err;
> >> +
> >> + dev->nchannels = msg.u.cardinfo.nchannels;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
> >> + const struct kvaser_msg *msg)
> >> +{
> >> + struct net_device_stats *stats;
> >> + struct kvaser_usb_tx_urb_context *context;
> >> + struct kvaser_usb_net_priv *priv;
> >> + struct sk_buff *skb;
> >> + struct can_frame *cf;
> >> + u8 channel = msg->u.tx_acknowledge.channel;
> >> + u8 tid = msg->u.tx_acknowledge.tid;
> >> +
> >> + if (channel >= dev->nchannels) {
> >> + dev_err(dev->udev->dev.parent,
> >> + "Invalid channel number (%d)\n", channel);
> >> + return;
> >> + }
> >> +
> >> + priv = dev->nets[channel];
> >> +
> >> + if (!netif_device_present(priv->netdev))
> >> + return;
> >> +
> >> + stats = &priv->netdev->stats;
> >> +
> >> + context = &priv->tx_contexts[tid % MAX_TX_URBS];
> >> +
> >> + /* Sometimes the state change doesn't come after a bus-off event */
> >> + if (priv->can.restart_ms &&
> >> + (priv->can.state >= CAN_STATE_BUS_OFF)) {
> >> + skb = alloc_can_err_skb(priv->netdev, &cf);
> >> + if (skb) {
> >> + cf->can_id |= CAN_ERR_RESTARTED;
> >> + netif_rx(skb);
> >> +
> >> + stats->rx_packets++;
> >> + stats->rx_bytes += cf->can_dlc;
> >> + } else {
> >> + netdev_err(priv->netdev,
> >> + "No memory left for err_skb\n");
> >> + }
> >> +
> >> + priv->can.can_stats.restarts++;
> >> + netif_carrier_on(priv->netdev);
> >> +
> >> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> >> + }
> >> +
> >> + stats->tx_packets++;
> >> + stats->tx_bytes += context->dlc;
> >> + can_get_echo_skb(priv->netdev, context->echo_index);
> >> +
> >> + context->echo_index = MAX_TX_URBS;
> >> + atomic_dec(&priv->active_tx_urbs);
> >> +
> >> + netif_wake_queue(priv->netdev);
> >> +}
> >> +
> >> +static void kvaser_usb_simple_msg_callback(struct urb *urb)
> >> +{
> >> + struct net_device *netdev = urb->context;
> >> +
> >> + kfree(urb->transfer_buffer);
> >> +
> >> + if (urb->status)
> >> + netdev_warn(netdev, "urb status received: %d\n",
> >> + urb->status);
> >> +}
> >> +
> >> +static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
> >> + u8 msg_id)
> >> +{
> >> + struct kvaser_usb *dev = priv->dev;
> >> + struct net_device *netdev = priv->netdev;
> >> + struct kvaser_msg *msg;
> >> + struct urb *urb;
> >> + void *buf;
> >> + int err;
> >> +
> >> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> >> + if (!urb) {
> >> + netdev_err(netdev, "No memory left for URBs\n");
> >> + return -ENOMEM;
> >> + }
> >> +
> >> + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> >> + if (!buf) {
> >
> > Do you have to free the usb you just allocated?
Yes it's missing.
> >
> >> + netdev_err(netdev, "No memory left for USB buffer\n");
> >> + return -ENOMEM;
> >> + }
> >> +
> >> + msg = (struct kvaser_msg *)buf;
> >> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
> >> + msg->id = msg_id;
> >> + msg->u.simple.channel = priv->channel;
> >> +
> >> + usb_fill_bulk_urb(urb, dev->udev,
> >> + usb_sndbulkpipe(dev->udev,
> >> + dev->bulk_out->bEndpointAddress),
> >> + buf, msg->len,
> >> + kvaser_usb_simple_msg_callback, priv);
> >> + usb_anchor_urb(urb, &priv->tx_submitted);
> >> +
> >> + err = usb_submit_urb(urb, GFP_ATOMIC);
> >> + if (err) {
> >> + netdev_err(netdev, "Error transmitting URB\n");
> >> + usb_unanchor_urb(urb);
> >> + kfree(buf);
> >> + return err;
> >
> > and here?
missing too.
> >
> >> + }
> >> +
> >> + usb_free_urb(urb);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
> >> +{
> >> + int i;
> >> +
> >> + usb_kill_anchored_urbs(&priv->tx_submitted);
> >> + atomic_set(&priv->active_tx_urbs, 0);
> >> +
> >> + for (i = 0; i < MAX_TX_URBS; i++)
> >> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> >> +}
> >> +
> >> +static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
> >> + const struct kvaser_msg *msg)
> >> +{
> >> + struct can_frame *cf;
> >> + struct sk_buff *skb;
> >> + struct net_device_stats *stats;
> >> + struct kvaser_usb_net_priv *priv;
> >> + unsigned int new_state;
> >> + u8 channel, status, txerr, rxerr, error_factor;
> >> +
> >> + switch (msg->id) {
> >> + case CMD_CAN_ERROR_EVENT:
> >> + channel = msg->u.error_event.channel;
> >> + status = msg->u.error_event.status;
> >> + txerr = msg->u.error_event.tx_errors_count;
> >> + rxerr = msg->u.error_event.rx_errors_count;
> >> + error_factor = msg->u.error_event.error_factor;
> >> + break;
> >> + case CMD_LOG_MESSAGE:
> >> + channel = msg->u.log_message.channel;
> >> + status = msg->u.log_message.data[0];
> >> + txerr = msg->u.log_message.data[2];
> >> + rxerr = msg->u.log_message.data[3];
> >> + error_factor = msg->u.log_message.data[1];
> >> + break;
> >> + case CMD_CHIP_STATE_EVENT:
> >> + channel = msg->u.chip_state_event.channel;
> >> + status = msg->u.chip_state_event.status;
> >> + txerr = msg->u.chip_state_event.tx_errors_count;
> >> + rxerr = msg->u.chip_state_event.rx_errors_count;
> >> + error_factor = 0;
> >> + break;
> >> + default:
> >> + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
> >> + msg->id);
> >> + return;
> >> + }
> >> +
> >> + if (channel >= dev->nchannels) {
> >> + dev_err(dev->udev->dev.parent,
> >> + "Invalid channel number (%d)\n", channel);
> >> + return;
> >> + }
> >> +
> >> + priv = dev->nets[channel];
> >> + stats = &priv->netdev->stats;
> >> +
> >> + if (status & M16C_STATE_BUS_RESET) {
> >> + kvaser_usb_unlink_tx_urbs(priv);
> >> + return;
> >> + }
> >> +
> >> + skb = alloc_can_err_skb(priv->netdev, &cf);
> >> + if (!skb) {
> >> + stats->rx_dropped++;
> >> + return;
> >> + }
> >> +
> >> + cf->can_id |= CAN_ERR_BUSERROR;
>
> At state change is *not* a bus error. The line above should e moved into
> the "if (error_factor)" block below.
ok
>
> >> +
> >> + new_state = priv->can.state;
> >> +
> >> + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
> >> +
> >> + if (status & M16C_STATE_BUS_OFF) {
> >> + cf->can_id |= CAN_ERR_BUSOFF;
> >> +
> >> + priv->can.can_stats.bus_off++;
> >> + if (!priv->can.restart_ms)
> >> + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
> >> +
> >> + netif_carrier_off(priv->netdev);
> >> +
> >> + new_state = CAN_STATE_BUS_OFF;
> >> + }
> >> +
> >> + if (status & M16C_STATE_BUS_PASSIVE) {
> >
> > else if ()
> >
> > as bus passive and bus off is mutually exclusive.
ok
> >
> >> + if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
> >> + cf->can_id |= CAN_ERR_CRTL;
> >> +
> >> + if ((txerr > 0) || (rxerr > 0))
>
> if (txerr | rxerr) ?
ok
>
> >> + cf->data[1] = (txerr > rxerr)
> >> + ? CAN_ERR_CRTL_TX_PASSIVE
> >> + : CAN_ERR_CRTL_RX_PASSIVE;
> >> + else
> >> + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
> >> + CAN_ERR_CRTL_RX_PASSIVE;
> >> +
> >> + priv->can.can_stats.error_passive++;
> >> + }
> >> +
> >> + new_state = CAN_STATE_ERROR_PASSIVE;
> >> + }
> >> +
> >> + if (status == M16C_STATE_BUS_ERROR) {
> >> + if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
> >> + ((txerr > 96) || (rxerr > 96))) {
> >
> > Is is >= 96 ?
>
> Yep.
ok
>
> >> + cf->can_id |= CAN_ERR_CRTL;
> >> + cf->data[1] = (txerr > rxerr)
> >> + ? CAN_ERR_CRTL_TX_WARNING
> >> + : CAN_ERR_CRTL_RX_WARNING;
> >> +
> >> + priv->can.can_stats.error_warning++;
> >> + new_state = CAN_STATE_ERROR_WARNING;
> >> + } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
> >> + cf->can_id |= CAN_ERR_PROT;
> >> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> >> +
> >> + new_state = CAN_STATE_ERROR_ACTIVE;
> >> + }
> >> + }
> >> +
> >> + if (!status) {
> >> + cf->can_id |= CAN_ERR_PROT;
> >> + cf->data[2] = CAN_ERR_PROT_ACTIVE;
> >> +
> >> + new_state = CAN_STATE_ERROR_ACTIVE;
> >> + }
> >> +
> >> + if (priv->can.restart_ms &&
> >> + (priv->can.state >= CAN_STATE_BUS_OFF) &&
> >> + (new_state < CAN_STATE_BUS_OFF)) {
> >> + cf->can_id |= CAN_ERR_RESTARTED;
> >> + netif_carrier_on(priv->netdev);
> >> +
> >> + priv->can.can_stats.restarts++;
> >> + }
> >> +
> >> + if (error_factor) {
> >> + priv->can.can_stats.bus_error++;
> >> + stats->rx_errors++;
> >> +
> >> + cf->can_id |= CAN_ERR_PROT;
> >> +
> >> + if (error_factor & M16C_EF_ACKE)
> >> + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
> >> + if (error_factor & M16C_EF_CRCE)
> >> + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
> >> + CAN_ERR_PROT_LOC_CRC_DEL);
> >> + if (error_factor & M16C_EF_FORME)
> >> + cf->data[2] |= CAN_ERR_PROT_FORM;
> >> + if (error_factor & M16C_EF_STFE)
> >> + cf->data[2] |= CAN_ERR_PROT_STUFF;
> >> + if (error_factor & M16C_EF_BITE0)
> >> + cf->data[2] |= CAN_ERR_PROT_BIT0;
> >> + if (error_factor & M16C_EF_BITE1)
> >> + cf->data[2] |= CAN_ERR_PROT_BIT1;
> >> + if (error_factor & M16C_EF_TRE)
> >> + cf->data[2] |= CAN_ERR_PROT_TX;
> >> + }
> >> +
> >> + cf->data[6] = txerr;
> >> + cf->data[7] = rxerr;
> >> +
> >> + priv->bec.txerr = txerr;
> >> + priv->bec.rxerr = rxerr;
> >> +
> >> + priv->can.state = new_state;
> >> +
> >> + netif_rx(skb);
> >> +
> >> + stats->rx_packets++;
> >> + stats->rx_bytes += cf->can_dlc;
> >> +}
> >> +
> >> +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> >> + const struct kvaser_msg *msg)
> >> +{
> >> + struct kvaser_usb_net_priv *priv;
> >> + struct can_frame *cf;
> >> + struct sk_buff *skb;
> >> + struct net_device_stats *stats;
> >> + u8 channel = msg->u.rx_can.channel;
> >> +
> >> + if (channel >= dev->nchannels) {
> >> + dev_err(dev->udev->dev.parent,
> >> + "Invalid channel number (%d)\n", channel);
> >> + return;
> >> + }
> >> +
> >> + priv = dev->nets[channel];
> >> + stats = &priv->netdev->stats;
> >> +
> >> + skb = alloc_can_skb(priv->netdev, &cf);
> >> + if (!skb) {
> >> + stats->tx_dropped++;
> >> + return;
> >> + }
> >> +
> >> + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
> >> + (msg->u.rx_can.msg[1] & 0x3f);
> >> + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
> >> +
> >> + if (msg->id == CMD_RX_EXT_MESSAGE) {
> >> + cf->can_id <<= 18;
> >> + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
> >> + ((msg->u.rx_can.msg[3] & 0xff) << 6) |
> >> + (msg->u.rx_can.msg[4] & 0x3f);
> >> + cf->can_id |= CAN_EFF_FLAG;
> >> + }
> >> +
> >> + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) {
> >> + cf->can_id |= CAN_RTR_FLAG;
> >> + } else if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
> >> + MSG_FLAG_NERR)) {
> >> + cf->can_id |= CAN_ERR_FLAG;
> >> + cf->can_dlc = CAN_ERR_DLC;
>
> > Please move the error skb creation handling into a subfunction, use
> > can_alloc_err_skb() in that function. Please move the:
> >
> > if (msg->u.rx_can.flag & ...)
> >
> > up in this function, before the alloc_can_skb().
> >
> >> +
> >> + netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
> >> + msg->u.rx_can.flag);
> >> +
> >> + stats->rx_errors++;
>
> This an *error* which does normally not happen, IIRC. Therefore I do not
> see a need to pass it to the user as error message.
ok I do not pass an error frame for such kind of message.
>
> >> + } else if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
> >> + cf->can_id = CAN_ERR_FLAG | CAN_ERR_CRTL;
> >> + cf->can_dlc = CAN_ERR_DLC;
> >> + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> >> +
> >> + stats->rx_over_errors++;
> >> + stats->rx_errors++;
> >
> > This should go into the error skb generation function, too.
> >
> >> + } else if (!msg->u.rx_can.flag) {
> >> + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
> >
> > Please don't copy the contents of RTR frames.
It's not copied. When it's a RTR frame, "msg->u.rx_can.flag" is set to
MSG_FLAG_REMOTE_FRAME.
> >
> >> + } else {
> >> + kfree_skb(skb);
> >
> > After you have moved the error skb generation into a seperate function,
> > you should get rid of the kfree(skb), too. The function should look like
> > this (pseude code):
> >
> > static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
> > const struct kvaser_msg *msg)
> > {
> > if (channel_invalid())
> > return;
> >
> > if (msg->u.rx_can.flag & ERROR) {
> > kvaser_usb_rx_can_err_msg();
> > return;
> > } else if (msg->u.rx_can.flag & INVALID_FRAME) {
> > return;
> > }
> >
> > skb = alloc_can_skb();
> >
> > /* existing dlc, rtr and data handling code */
> > ...
> > }
ok
> >
> >
> >> + return;
> >> + }
> >> +
> >> + netif_rx(skb);
> >> +
> >> + stats->rx_packets++;
> >> + stats->rx_bytes += cf->can_dlc;
> >> +}
> >> +
> >> +static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
> >> + const struct kvaser_msg *msg)
> >> +{
> >> + struct kvaser_usb_net_priv *priv;
> >> + u8 channel = msg->u.simple.channel;
> >> +
> >> + if (channel >= dev->nchannels) {
> >> + dev_err(dev->udev->dev.parent,
> >> + "Invalid channel number (%d)\n", channel);
> >> + return;
> >> + }
> >> +
> >> + priv = dev->nets[channel];
> >> +
> >> + if (completion_done(&priv->start_comp) &&
> >> + netif_queue_stopped(priv->netdev)) {
> >> + netif_wake_queue(priv->netdev);
> >> + } else {
> >> + netif_start_queue(priv->netdev);
> >> + complete(&priv->start_comp);
> >> + }
> >> +}
> >> +
> >> +static void kvaser_usb_stop_chip_reply(const struct kvaser_usb *dev,
> >> + const struct kvaser_msg *msg)
> >> +{
> >> + struct kvaser_usb_net_priv *priv;
> >> + u8 channel = msg->u.simple.channel;
> >> +
> >> + if (channel >= dev->nchannels) {
> >> + dev_err(dev->udev->dev.parent,
> >> + "Invalid channel number (%d)\n", channel);
> >> + return;
> >> + }
> >> +
> >> + priv = dev->nets[channel];
> >> +
> >> + complete(&priv->stop_comp);
> >> +}
> >> +
> >> +static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
> >> + const struct kvaser_msg *msg)
> >> +{
> >> + switch (msg->id) {
> >> + case CMD_START_CHIP_REPLY:
> >> + kvaser_usb_start_chip_reply(dev, msg);
> >> + break;
> >> +
> >> + case CMD_STOP_CHIP_REPLY:
> >> + kvaser_usb_stop_chip_reply(dev, msg);
> >> + break;
> >> +
> >> + case CMD_RX_STD_MESSAGE:
> >> + case CMD_RX_EXT_MESSAGE:
> >> + kvaser_usb_rx_can_msg(dev, msg);
> >> + break;
> >> +
> >> + case CMD_CHIP_STATE_EVENT:
> >> + case CMD_CAN_ERROR_EVENT:
> >> + kvaser_usb_rx_error(dev, msg);
> >> + break;
> >> +
> >> + case CMD_LOG_MESSAGE:
> >> + if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
> >> + kvaser_usb_rx_error(dev, msg);
> >> + break;
> >> +
> >> + case CMD_TX_ACKNOWLEDGE:
> >> + kvaser_usb_tx_acknowledge(dev, msg);
> >> + break;
> >> +
> >> + default:
> >> + dev_warn(dev->udev->dev.parent,
> >> + "Unhandled message (%d)\n", msg->id);
> >> + break;
> >> + }
> >> +}
> >> +
> >> +static void kvaser_usb_read_bulk_callback(struct urb *urb)
> >> +{
> >> + struct kvaser_usb *dev = urb->context;
> >> + struct kvaser_msg *msg;
> >> + int pos = 0;
> >> + int err, i;
> >> +
> >> + switch (urb->status) {
> >> + case 0:
> >> + break;
> >> + case -ENOENT:
> >> + case -ESHUTDOWN:
> >> + return;
> >> + default:
> >> + dev_info(dev->udev->dev.parent, "Rx URB aborted (%d)\n",
> >> + urb->status);
> >> + goto resubmit_urb;
> >> + }
> >> +
> >> + while (pos <= urb->actual_length - MSG_HEADER_LEN) {
> >> + msg = urb->transfer_buffer + pos;
> >> +
> >> + if (!msg->len)
> >> + break;
> >> +
> >> + if (pos + msg->len > urb->actual_length) {
> >> + dev_err(dev->udev->dev.parent, "Format error\n");
> >> + break;
> >> + }
> >> +
> >> + kvaser_usb_handle_message(dev, msg);
> >> +
> >> + pos += msg->len;
> >> + }
> >> +
> >> +resubmit_urb:
> >> + usb_fill_bulk_urb(urb, dev->udev,
> >> + usb_rcvbulkpipe(dev->udev,
> >> + dev->bulk_in->bEndpointAddress),
> >> + urb->transfer_buffer, RX_BUFFER_SIZE,
> >> + kvaser_usb_read_bulk_callback, dev);
> >> +
> >> + err = usb_submit_urb(urb, GFP_ATOMIC);
> >> + if (err == -ENODEV) {
> >> + for (i = 0; i < dev->nchannels; i++) {
> >> + if (!dev->nets[i])
> >> + continue;
> >> +
> >> + netif_device_detach(dev->nets[i]->netdev);
> >> + }
> >> + } else if (err) {
> >> + dev_err(dev->udev->dev.parent,
> >> + "Failed resubmitting read bulk urb: %d\n", err);
> >> + }
> >> +
> >> + return;
> >> +}
> >> +
> >> +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev)
> >> +{
> >> + int i, err = 0;
> >> +
> >> + if (dev->rxinitdone)
> >> + return 0;
> >> +
> >> + for (i = 0; i < MAX_RX_URBS; i++) {
> >> + struct urb *urb = NULL;
> >> + u8 *buf = NULL;
> >> + dma_addr_t buf_dma;
> >> +
> >> + urb = usb_alloc_urb(0, GFP_KERNEL);
> >> + if (!urb) {
> >> + dev_warn(dev->udev->dev.parent,
> >> + "No memory left for URBs\n");
> >> + err = -ENOMEM;
> >> + break;
> >> + }
> >> +
> >> + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
> >> + GFP_KERNEL, &buf_dma);
> >> + if (!buf) {
> >> + dev_warn(dev->udev->dev.parent,
> >> + "No memory left for USB buffer\n");
> >> + usb_free_urb(urb);
> >> + err = -ENOMEM;
> >> + break;
> >> + }
> >> +
> >> + usb_fill_bulk_urb(urb, dev->udev,
> >> + usb_rcvbulkpipe(dev->udev,
> >> + dev->bulk_in->bEndpointAddress),
> >> + buf, RX_BUFFER_SIZE,
> >> + kvaser_usb_read_bulk_callback,
> >> + dev);
> >> + urb->transfer_dma = buf_dma;
> >> + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
> >> + usb_anchor_urb(urb, &dev->rx_submitted);
> >> +
> >> + err = usb_submit_urb(urb, GFP_KERNEL);
> >> + if (err) {
> >> + usb_unanchor_urb(urb);
> >> + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf,
> >> + buf_dma);
> >
> > Do you have to call usb_free_usb() here, or does the usb frame work take
> > care of this?
It's missing. Sorry.
> >
> >> + break;
> >> + }
> >> +
> >> + dev->rxbuf[i] = buf;
> >> + dev->rxbuf_dma[i] = buf_dma;
> >> +
> >> + usb_free_urb(urb);
> >> + }
> >> +
> >> + if (i == 0) {
> >> + dev_warn(dev->udev->dev.parent,
> >> + "Cannot setup read URBs, error %d\n", err);
> >> + return err;
> >> + } else if (i < MAX_RX_URBS) {
> >> + dev_warn(dev->udev->dev.parent,
> >> + "RX performances may be slow\n");
> >> + }
> >> +
> >> + dev->rxinitdone = true;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
> >> +{
> >> + struct kvaser_msg msg = {
> >> + .id = CMD_SET_CTRL_MODE,
> >> + .len = MSG_HEADER_LEN +
> >> + sizeof(struct kvaser_msg_ctrl_mode),
> >> + .u.ctrl_mode.tid = 0xff,
> >> + .u.ctrl_mode.channel = priv->channel,
> >> + };
> >> +
> >> + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> >> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
> >> + else
> >> + msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
> >> +
> >> + return kvaser_usb_send_msg(priv->dev, &msg);
> >> +}
> >> +
> >> +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
> >> +{
> >> + int err;
> >> +
> >> + init_completion(&priv->start_comp);
> >> +
> >> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
> >> + priv->channel);
> >> + if (err)
> >> + return err;
> >> +
> >> + if (!wait_for_completion_timeout(&priv->start_comp,
> >> + msecs_to_jiffies(START_TIMEOUT)))
> >> + return -ETIMEDOUT;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int kvaser_usb_open(struct net_device *netdev)
> >> +{
> >> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >> + struct kvaser_usb *dev = priv->dev;
> >> + int err;
> >> +
> >> + err = open_candev(netdev);
> >> + if (err)
> >> + return err;
> >> +
> >> + err = kvaser_usb_setup_rx_urbs(dev);
> >> + if (err)
> >> + goto error;
> >> +
> >> + err = kvaser_usb_set_opt_mode(priv);
> >> + if (err)
> >> + goto error;
> >> +
> >> + err = kvaser_usb_start_chip(priv);
> >> + if (err) {
> >> + netdev_warn(netdev, "Cannot start device, error %d\n", err);
> >> + goto error;
> >> + }
> >> +
> >> + priv->can.state = CAN_STATE_ERROR_ACTIVE;
> >> +
> >> + return 0;
> >> +
> >> +error:
> >> + close_candev(netdev);
> >> + return err;
> >> +}
> >> +
> >> +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
> >> +{
> >> + int i;
> >> +
> >> + usb_kill_anchored_urbs(&dev->rx_submitted);
> >> +
> >> + for (i = 0; i < MAX_RX_URBS; i++)
> >> + usb_free_coherent(dev->udev, RX_BUFFER_SIZE,
> >> + dev->rxbuf[i],
> >> + dev->rxbuf_dma[i]);
> >> +
> >> + for (i = 0; i < MAX_NET_DEVICES; i++) {
> >> + struct kvaser_usb_net_priv *priv = dev->nets[i];
> >> +
> >> + if (priv)
> >> + kvaser_usb_unlink_tx_urbs(priv);
> >> + }
> >> +}
> >> +
> >> +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
> >> +{
> >> + int err;
> >> +
> >> + init_completion(&priv->stop_comp);
> >> +
> >> + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
> >> + priv->channel);
> >> + if (err)
> >> + return err;
> >> +
> >> + if (!wait_for_completion_timeout(&priv->stop_comp,
> >> + msecs_to_jiffies(STOP_TIMEOUT)))
> >> + return -ETIMEDOUT;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
> >> +{
> >> + struct kvaser_msg msg = {
> >> + .id = CMD_FLUSH_QUEUE,
> >> + .len = MSG_HEADER_LEN +
> >> + sizeof(struct kvaser_msg_flush_queue),
> >> + .u.flush_queue.channel = priv->channel,
> >> + .u.flush_queue.flags = 0x00,
> >> + };
> >> +
> >> + return kvaser_usb_send_msg(priv->dev, &msg);
> >> +}
> >> +
> >> +static int kvaser_usb_close(struct net_device *netdev)
> >> +{
> >> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >> + struct kvaser_usb *dev = priv->dev;
> >> + int err;
> >> +
> >> + netif_stop_queue(netdev);
> >> +
> >> + err = kvaser_usb_flush_queue(priv);
> >> + if (err)
> >> + netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
> >> +
> >> + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
> >> + netdev_warn(netdev, "Cannot reset card, error %d\n", err);
> >> +
> >> + err = kvaser_usb_stop_chip(priv);
> >> + if (err)
> >> + netdev_warn(netdev, "Cannot stop device, error %d\n", err);
> >> +
> >> + priv->can.state = CAN_STATE_STOPPED;
> >> + close_candev(priv->netdev);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static void kvaser_usb_write_bulk_callback(struct urb *urb)
> >> +{
> >> + struct kvaser_usb_tx_urb_context *context = urb->context;
> >> + struct kvaser_usb_net_priv *priv;
> >> + struct net_device *netdev;
> >> +
> >> + if (WARN_ON(!context))
> >> + return;
> >> +
> >> + priv = context->priv;
> >> + netdev = priv->netdev;
> >> +
> >> + kfree(urb->transfer_buffer);
> >> +
> >> + if (!netif_device_present(netdev))
> >> + return;
> >> +
> >> + if (urb->status)
> >> + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
> >> +}
> >> +
> >> +static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
> >> + struct net_device *netdev)
> >> +{
> >> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >> + struct kvaser_usb *dev = priv->dev;
> >> + struct net_device_stats *stats = &netdev->stats;
> >> + struct can_frame *cf = (struct can_frame *)skb->data;
> >> + struct kvaser_usb_tx_urb_context *context = NULL;
> >> + struct urb *urb;
> >> + void *buf;
> >> + struct kvaser_msg *msg;
> >> + int i, err;
> >> + int ret = NETDEV_TX_OK;
> >> +
> >> + if (can_dropped_invalid_skb(netdev, skb))
> >> + return NETDEV_TX_OK;
> >> +
> >> + urb = usb_alloc_urb(0, GFP_ATOMIC);
> >> + if (!urb) {
> >> + netdev_err(netdev, "No memory left for URBs\n");
> >> + stats->tx_dropped++;
> >> + dev_kfree_skb(skb);
> >> + return NETDEV_TX_OK;
> >> + }
> >> +
> >> + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
> >> + if (!buf) {
> >> + netdev_err(netdev, "No memory left for USB buffer\n");
> >> + stats->tx_dropped++;
> >> + dev_kfree_skb(skb);
> > What about the urb?
Same as above.
> >> + goto nobufmem;
> >> + }
> >> +
> >> + msg = (struct kvaser_msg *)buf;
> >
> > nitpick: cast is not needed, as buf is void *
Indeed.
> >
> >> + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
> >> + msg->u.tx_can.flags = 0;
> >> + msg->u.tx_can.channel = priv->channel;
> >> +
> >> + if (cf->can_id & CAN_EFF_FLAG) {
> >> + msg->id = CMD_TX_EXT_MESSAGE;
> >> + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
> >> + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
> >> + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
> >> + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
> >> + msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
> >> + } else {
> >> + msg->id = CMD_TX_STD_MESSAGE;
> >> + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
> >> + msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
> >> + }
> >> +
> >> + msg->u.tx_can.msg[5] = cf->can_dlc;
> >> + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
> >> +
> >> + if (cf->can_id & CAN_RTR_FLAG)
> >> + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
> >> + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
> >> + context = &priv->tx_contexts[i];
> >> + break;
> >> + }
> >> + }
> >> +
> >> + if (!context) {
> >> + netdev_warn(netdev, "cannot find free context\n");
> >> + ret = NETDEV_TX_BUSY;
> >> + goto releasebuf;
> >> + }
> >> +
> >> + context->priv = priv;
> >> + context->echo_index = i;
> >> + context->dlc = cf->can_dlc;
> >> +
> >> + msg->u.tx_can.tid = context->echo_index;
> >> +
> >> + usb_fill_bulk_urb(urb, dev->udev,
> >> + usb_sndbulkpipe(dev->udev,
> >> + dev->bulk_out->bEndpointAddress),
> >> + buf, msg->len,
> >> + kvaser_usb_write_bulk_callback, context);
> >> + usb_anchor_urb(urb, &priv->tx_submitted);
> >> +
> >> + can_put_echo_skb(skb, netdev, context->echo_index);
> >> +
> >> + atomic_inc(&priv->active_tx_urbs);
> >> +
> >> + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
> >> + netif_stop_queue(netdev);
> >> +
> >> + err = usb_submit_urb(urb, GFP_ATOMIC);
> >> + if (unlikely(err)) {
> >> + can_free_echo_skb(netdev, context->echo_index);
> >> +
> >> + atomic_dec(&priv->active_tx_urbs);
> >> + usb_unanchor_urb(urb);
> >> +
> >> + stats->tx_dropped++;
> >> +
> >> + if (err == -ENODEV)
> >> + netif_device_detach(netdev);
> >> + else
> >> + netdev_warn(netdev, "Failed tx_urb %d\n", err);
> >> +
> >> + goto releasebuf;
> >> + }
> >> +
> >> + netdev->trans_start = jiffies;
> >
> > Is this still needed?
No.
> >
> >> +
> >> + usb_free_urb(urb);
> >> +
> >> + return NETDEV_TX_OK;
> >> +
> >> +releasebuf:
> >> + kfree(buf);
> >> +nobufmem:
> >> + usb_free_urb(urb);
> >> + return ret;
> >> +}
> >> +
> >> +static const struct net_device_ops kvaser_usb_netdev_ops = {
> >> + .ndo_open = kvaser_usb_open,
> >> + .ndo_stop = kvaser_usb_close,
> >> + .ndo_start_xmit = kvaser_usb_start_xmit,
> >> +};
> >> +
> >> +static struct can_bittiming_const kvaser_usb_bittiming_const = {
> >> + .name = "kvaser_usb",
> >> + .tseg1_min = KVASER_USB_TSEG1_MIN,
> >> + .tseg1_max = KVASER_USB_TSEG1_MAX,
> >> + .tseg2_min = KVASER_USB_TSEG2_MIN,
> >> + .tseg2_max = KVASER_USB_TSEG2_MAX,
> >> + .sjw_max = KVASER_USB_SJW_MAX,
> >> + .brp_min = KVASER_USB_BRP_MIN,
> >> + .brp_max = KVASER_USB_BRP_MAX,
> >> + .brp_inc = KVASER_USB_BRP_INC,
> >> +};
> >> +
> >> +static int kvaser_usb_set_bittiming(struct net_device *netdev)
> >> +{
> >> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >> + struct can_bittiming *bt = &priv->can.bittiming;
> >> + struct kvaser_usb *dev = priv->dev;
> >> + struct kvaser_msg msg = {
> >> + .id = CMD_SET_BUS_PARAMS,
> >> + .len = MSG_HEADER_LEN +
> >> + sizeof(struct kvaser_msg_busparams),
> >> + .u.busparams.channel = priv->channel,
> >> + .u.busparams.tid = 0xff,
> >> + .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
> >> + .u.busparams.sjw = bt->sjw,
> >> + .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
> >> + .u.busparams.tseg2 = bt->phase_seg2,
> >> + };
> >> +
> >> + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> >> + msg.u.busparams.no_samp = 3;
> >> + else
> >> + msg.u.busparams.no_samp = 1;
> >> +
> >> + return kvaser_usb_send_msg(dev, &msg);
> >> +}
> >> +
> >> +static int kvaser_usb_set_mode(struct net_device *netdev,
> >> + enum can_mode mode)
> >> +{
> >> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >> + int err;
> >> +
> >> + switch (mode) {
> >> + case CAN_MODE_START:
> >> + err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
> >> + if (err)
> >> + return err;
> >> + break;
> >> + default:
> >> + return -EOPNOTSUPP;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
> >> + struct can_berr_counter *bec)
> >> +{
> >> + struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
> >> +
> >> + *bec = priv->bec;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int kvaser_usb_init_one(struct usb_interface *intf,
> >> + const struct usb_device_id *id, int channel)
> >> +{
> >> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> >> + struct net_device *netdev;
> >> + struct kvaser_usb_net_priv *priv;
> >> + int i, err;
> >> +
> >> + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
> >> + if (!netdev) {
> >> + dev_err(&intf->dev, "Cannot alloc candev\n");
> >> + return -ENOMEM;
> >> + }
> >> +
> >> + priv = netdev_priv(netdev);
> >> +
> >> + init_completion(&priv->start_comp);
> >> + init_completion(&priv->stop_comp);
> >> +
> >> + init_usb_anchor(&priv->tx_submitted);
> >> + atomic_set(&priv->active_tx_urbs, 0);
> >> +
> >> + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
> >> + priv->tx_contexts[i].echo_index = MAX_TX_URBS;
> >> +
> >> + priv->dev = dev;
> >> + priv->netdev = netdev;
> >> + priv->channel = channel;
> >> +
> >> + priv->can.state = CAN_STATE_STOPPED;
> >> + priv->can.clock.freq = CAN_USB_CLOCK;
> >> + priv->can.bittiming_const = &kvaser_usb_bittiming_const;
> >> + priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
> >> + priv->can.do_set_mode = kvaser_usb_set_mode;
> >> + if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
> >> + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
> >> + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
> >> + if (id->driver_info & KVASER_HAS_SILENT_MODE)
> >> + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
> >> +
> >> + netdev->flags |= IFF_ECHO;
> >> +
> >> + netdev->netdev_ops = &kvaser_usb_netdev_ops;
> >> +
> >> + SET_NETDEV_DEV(netdev, &intf->dev);
> >> +
> >> + dev->nets[channel] = priv;
> >> +
> >> + err = register_candev(netdev);
> >> + if (err) {
> >> + dev_err(&intf->dev, "Failed to register can device\n");
> >> + free_candev(netdev);
> >> + return err;
> >> + }
> >> +
> >> + netdev_dbg(netdev, "device registered\n");
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
> >> + struct usb_endpoint_descriptor **in,
> >> + struct usb_endpoint_descriptor **out)
> >> +{
> >> + const struct usb_host_interface *iface_desc;
> >> + struct usb_endpoint_descriptor *endpoint;
> >> + int i;
> >> +
> >> + iface_desc = &intf->altsetting[0];
> >> +
> >> + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
> >> + endpoint = &iface_desc->endpoint[i].desc;
> >> +
> >> + if (usb_endpoint_is_bulk_in(endpoint))
> >> + *in = endpoint;
> >> +
> >> + if (usb_endpoint_is_bulk_out(endpoint))
> >> + *out = endpoint;
> >> + }
> >> +}
> >> +
> >> +static int kvaser_usb_probe(struct usb_interface *intf,
> >> + const struct usb_device_id *id)
> >> +{
> >> + struct kvaser_usb *dev;
> >> + int err = -ENOMEM;
> >> + int i;
> >> +
> >> + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
> >> + if (!dev)
> >> + return -ENOMEM;
> >> +
> >> + kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
> >> + if (!dev->bulk_in || !dev->bulk_out) {
> >> + dev_err(&intf->dev, "Cannot get usb endpoint(s)");
> >> + return err;
> >> + }
> >> +
> >> + dev->udev = interface_to_usbdev(intf);
> >> +
> >> + init_usb_anchor(&dev->rx_submitted);
> >> +
> >> + usb_set_intfdata(intf, dev);
> >> +
> >> + for (i = 0; i < MAX_NET_DEVICES; i++)
> >> + kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
> >> +
> >> + err = kvaser_usb_get_software_info(dev);
> >> + if (err) {
> >> + dev_err(&intf->dev,
> >> + "Cannot get software infos, error %d\n", err);
> >> + return err;
> >> + }
> >> +
> >> + err = kvaser_usb_get_card_info(dev);
> >> + if (err) {
> >> + dev_err(&intf->dev,
> >> + "Cannot get card infos, error %d\n", err);
> >> + return err;
> >> + }
> >> +
> >> + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
> >> + ((dev->fw_version >> 24) & 0xff),
> >> + ((dev->fw_version >> 16) & 0xff),
> >> + (dev->fw_version & 0xffff));
> >> +
> >> + for (i = 0; i < dev->nchannels; i++)
> >> + kvaser_usb_init_one(intf, id, i);
>
> Error checking is not needed?
Yes it is better to check that, I added it in the new version.
If I cannot initialize one of the "dev->nchannels" net interfaces, I
return an error in the probing function.
>
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static void kvaser_usb_disconnect(struct usb_interface *intf)
> >> +{
> >> + struct kvaser_usb *dev = usb_get_intfdata(intf);
> >> + int i;
> >> +
> >> + usb_set_intfdata(intf, NULL);
> >> +
> >> + if (!dev)
> >> + return;
> >> +
> >> + for (i = 0; i < dev->nchannels; i++) {
> >> + if (!dev->nets[i])
> >> + continue;
> >> +
> >> + unregister_netdev(dev->nets[i]->netdev);
> >> + }
> >> +
> >> + kvaser_usb_unlink_all_urbs(dev);
> >> +
> >> + for (i = 0; i < dev->nchannels; i++)
> >> + free_candev(dev->nets[i]->netdev);
> >> +}
> >> +
> >> +static struct usb_driver kvaser_usb_driver = {
> >> + .name = "kvaser_usb",
> >> + .probe = kvaser_usb_probe,
> >> + .disconnect = kvaser_usb_disconnect,
> >> + .id_table = kvaser_usb_table
> >> +};
> >> +
> >> +module_usb_driver(kvaser_usb_driver);
> >> +
> >> +MODULE_AUTHOR("Olivier Sobrie <olivier-Ui3EtX6WB9GzQB+pC5nmwQ@public.gmane.org>");
> >> +MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
> >> +MODULE_LICENSE("GPL v2");
>
> Wolfgang.
>
Sorry for the delay... I was quite busy these last days.
I did the changes and fixes you proposed and will post right now a new
version of the driver. I hope I didn't forget anything.
Thank you,
--
Olivier
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v5] can: kvaser_usb: Add support for Kvaser CAN/USB devices
From: Olivier Sobrie @ 2012-10-02 7:16 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can, Kvaser Support
Cc: netdev, linux-usb, Olivier Sobrie, Daniel Berglund
In-Reply-To: <1343626352-24760-1-git-send-email-olivier@sobrie.be>
This driver provides support for several Kvaser CAN/USB devices.
Such kind of devices supports up to three CAN network interfaces.
It has been tested with a Kvaser USB Leaf Light (one network interface)
connected to a pch_can interface.
The firmware version of the Kvaser device was 2.5.205.
List of Kvaser devices supported by the driver:
- Kvaser Leaf Light
- Kvaser Leaf Professional HS
- Kvaser Leaf SemiPro HS
- Kvaser Leaf Professional LS
- Kvaser Leaf Professional SWC
- Kvaser Leaf Professional LIN
- Kvaser Leaf SemiPro LS
- Kvaser Leaf SemiPro SWC
- Kvaser Memorator II HS/HS
- Kvaser USBcan Professional HS/HS
- Kvaser Leaf Light GI
- Kvaser Leaf Professional HS (OBD-II connector)
- Kvaser Memorator Professional HS/LS
- Kvaser Leaf Light "China"
- Kvaser BlackBird SemiPro
- Kvaser USBcan R
Signed-off-by: Daniel Berglund <db@kvaser.com>
Signed-off-by: Olivier Sobrie <olivier@sobrie.be>
---
Hi,
This new patch fixes the errors pointed out by Marc and Wolfgang.
Changes since v4:
- add missing usb_free_urb()
- put error message in a separate function
- handle return code of kvaser_usb_init_one() in probe function
Olivier
drivers/net/can/usb/Kconfig | 29 +
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1596 ++++++++++++++++++++++++++++++++++++++
3 files changed, 1626 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig
index 0a68768..a4e4bee 100644
--- a/drivers/net/can/usb/Kconfig
+++ b/drivers/net/can/usb/Kconfig
@@ -13,6 +13,35 @@ config CAN_ESD_USB2
This driver supports the CAN-USB/2 interface
from esd electronic system design gmbh (http://www.esd.eu).
+config CAN_KVASER_USB
+ tristate "Kvaser CAN/USB interface"
+ ---help---
+ This driver adds support for Kvaser CAN/USB devices like Kvaser
+ Leaf Light.
+
+ The driver gives support for the following devices:
+ - Kvaser Leaf Light
+ - Kvaser Leaf Professional HS
+ - Kvaser Leaf SemiPro HS
+ - Kvaser Leaf Professional LS
+ - Kvaser Leaf Professional SWC
+ - Kvaser Leaf Professional LIN
+ - Kvaser Leaf SemiPro LS
+ - Kvaser Leaf SemiPro SWC
+ - Kvaser Memorator II HS/HS
+ - Kvaser USBcan Professional HS/HS
+ - Kvaser Leaf Light GI
+ - Kvaser Leaf Professional HS (OBD-II connector)
+ - Kvaser Memorator Professional HS/LS
+ - Kvaser Leaf Light "China"
+ - Kvaser BlackBird SemiPro
+ - Kvaser USBcan R
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called kvaser_usb.
+
config CAN_PEAK_USB
tristate "PEAK PCAN-USB/USB Pro interfaces"
---help---
diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile
index da6d1d3..80a2ee4 100644
--- a/drivers/net/can/usb/Makefile
+++ b/drivers/net/can/usb/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o
obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o
+obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o
obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/
ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c
new file mode 100644
index 0000000..bd48807
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1596 @@
+/*
+ * 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 version 2.
+ *
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ *
+ * Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved.
+ * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh
+ * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be>
+ */
+
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000 /* msecs */
+#define STOP_TIMEOUT 1000 /* msecs */
+#define USB_SEND_TIMEOUT 1000 /* msecs */
+#define USB_RECV_TIMEOUT 1000 /* msecs */
+#define RX_BUFFER_SIZE 3072
+#define CAN_USB_CLOCK 8000000
+#define MAX_NET_DEVICES 3
+
+/* Kvaser USB devices */
+#define KVASER_VENDOR_ID 0x0bfd
+#define USB_LEAF_DEVEL_PRODUCT_ID 10
+#define USB_LEAF_LITE_PRODUCT_ID 11
+#define USB_LEAF_PRO_PRODUCT_ID 12
+#define USB_LEAF_SPRO_PRODUCT_ID 14
+#define USB_LEAF_PRO_LS_PRODUCT_ID 15
+#define USB_LEAF_PRO_SWC_PRODUCT_ID 16
+#define USB_LEAF_PRO_LIN_PRODUCT_ID 17
+#define USB_LEAF_SPRO_LS_PRODUCT_ID 18
+#define USB_LEAF_SPRO_SWC_PRODUCT_ID 19
+#define USB_MEMO2_DEVEL_PRODUCT_ID 22
+#define USB_MEMO2_HSHS_PRODUCT_ID 23
+#define USB_UPRO_HSHS_PRODUCT_ID 24
+#define USB_LEAF_LITE_GI_PRODUCT_ID 25
+#define USB_LEAF_PRO_OBDII_PRODUCT_ID 26
+#define USB_MEMO2_HSLS_PRODUCT_ID 27
+#define USB_LEAF_LITE_CH_PRODUCT_ID 28
+#define USB_BLACKBIRD_SPRO_PRODUCT_ID 29
+#define USB_OEM_MERCURY_PRODUCT_ID 34
+#define USB_OEM_LEAF_PRODUCT_ID 35
+#define USB_CAN_R_PRODUCT_ID 39
+
+/* USB devices features */
+#define KVASER_HAS_SILENT_MODE BIT(0)
+#define KVASER_HAS_TXRX_ERRORS BIT(1)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* Can message flags */
+#define MSG_FLAG_ERROR_FRAME BIT(0)
+#define MSG_FLAG_OVERRUN BIT(1)
+#define MSG_FLAG_NERR BIT(2)
+#define MSG_FLAG_WAKEUP BIT(3)
+#define MSG_FLAG_REMOTE_FRAME BIT(4)
+#define MSG_FLAG_RESERVED BIT(5)
+#define MSG_FLAG_TX_ACK BIT(6)
+#define MSG_FLAG_TX_REQUEST BIT(7)
+
+/* Can states */
+#define M16C_STATE_BUS_RESET BIT(0)
+#define M16C_STATE_BUS_ERROR BIT(4)
+#define M16C_STATE_BUS_PASSIVE BIT(5)
+#define M16C_STATE_BUS_OFF BIT(6)
+
+/* Can msg ids */
+#define CMD_RX_STD_MESSAGE 12
+#define CMD_TX_STD_MESSAGE 13
+#define CMD_RX_EXT_MESSAGE 14
+#define CMD_TX_EXT_MESSAGE 15
+#define CMD_SET_BUS_PARAMS 16
+#define CMD_GET_BUS_PARAMS 17
+#define CMD_GET_BUS_PARAMS_REPLY 18
+#define CMD_GET_CHIP_STATE 19
+#define CMD_CHIP_STATE_EVENT 20
+#define CMD_SET_CTRL_MODE 21
+#define CMD_GET_CTRL_MODE 22
+#define CMD_GET_CTRL_MODE_REPLY 23
+#define CMD_RESET_CHIP 24
+#define CMD_RESET_CARD 25
+#define CMD_START_CHIP 26
+#define CMD_START_CHIP_REPLY 27
+#define CMD_STOP_CHIP 28
+#define CMD_STOP_CHIP_REPLY 29
+#define CMD_GET_CARD_INFO2 32
+#define CMD_GET_CARD_INFO 34
+#define CMD_GET_CARD_INFO_REPLY 35
+#define CMD_GET_SOFTWARE_INFO 38
+#define CMD_GET_SOFTWARE_INFO_REPLY 39
+#define CMD_ERROR_EVENT 45
+#define CMD_FLUSH_QUEUE 48
+#define CMD_RESET_ERROR_COUNTER 49
+#define CMD_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+#define CMD_LOG_MESSAGE 106
+
+/* error factors */
+#define M16C_EF_ACKE BIT(0)
+#define M16C_EF_CRCE BIT(1)
+#define M16C_EF_FORME BIT(2)
+#define M16C_EF_STFE BIT(3)
+#define M16C_EF_BITE0 BIT(4)
+#define M16C_EF_BITE1 BIT(5)
+#define M16C_EF_RCVE BIT(6)
+#define M16C_EF_TRE BIT(7)
+
+/* bittiming parameters */
+#define KVASER_USB_TSEG1_MIN 1
+#define KVASER_USB_TSEG1_MAX 16
+#define KVASER_USB_TSEG2_MIN 1
+#define KVASER_USB_TSEG2_MAX 8
+#define KVASER_USB_SJW_MAX 4
+#define KVASER_USB_BRP_MIN 1
+#define KVASER_USB_BRP_MAX 64
+#define KVASER_USB_BRP_INC 1
+
+/* ctrl modes */
+#define KVASER_CTRL_MODE_NORMAL 1
+#define KVASER_CTRL_MODE_SILENT 2
+#define KVASER_CTRL_MODE_SELFRECEPTION 3
+#define KVASER_CTRL_MODE_OFF 4
+
+struct kvaser_msg_simple {
+ u8 tid;
+ u8 channel;
+} __packed;
+
+struct kvaser_msg_cardinfo {
+ u8 tid;
+ u8 nchannels;
+ __le32 serial_number;
+ __le32 padding;
+ __le32 clock_resolution;
+ __le32 mfgdate;
+ u8 ean[8];
+ u8 hw_revision;
+ u8 usb_hs_mode;
+ __le16 padding2;
+} __packed;
+
+struct kvaser_msg_cardinfo2 {
+ u8 tid;
+ u8 channel;
+ u8 pcb_id[24];
+ __le32 oem_unlock_code;
+} __packed;
+
+struct kvaser_msg_softinfo {
+ u8 tid;
+ u8 channel;
+ __le32 sw_options;
+ __le32 fw_version;
+ __le16 max_outstanding_tx;
+ __le16 padding[9];
+} __packed;
+
+struct kvaser_msg_busparams {
+ u8 tid;
+ u8 channel;
+ __le32 bitrate;
+ u8 tseg1;
+ u8 tseg2;
+ u8 sjw;
+ u8 no_samp;
+} __packed;
+
+struct kvaser_msg_tx_can {
+ u8 channel;
+ u8 tid;
+ u8 msg[14];
+ u8 padding;
+ u8 flags;
+} __packed;
+
+struct kvaser_msg_rx_can {
+ u8 channel;
+ u8 flag;
+ __le16 time[3];
+ u8 msg[14];
+} __packed;
+
+struct kvaser_msg_chip_state_event {
+ u8 tid;
+ u8 channel;
+ __le16 time[3];
+ u8 tx_errors_count;
+ u8 rx_errors_count;
+ u8 status;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_tx_acknowledge {
+ u8 channel;
+ u8 tid;
+ __le16 time[3];
+ u8 flags;
+ u8 time_offset;
+} __packed;
+
+struct kvaser_msg_error_event {
+ u8 tid;
+ u8 flags;
+ __le16 time[3];
+ u8 channel;
+ u8 padding;
+ u8 tx_errors_count;
+ u8 rx_errors_count;
+ u8 status;
+ u8 error_factor;
+} __packed;
+
+struct kvaser_msg_ctrl_mode {
+ u8 tid;
+ u8 channel;
+ u8 ctrl_mode;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_flush_queue {
+ u8 tid;
+ u8 channel;
+ u8 flags;
+ u8 padding[3];
+} __packed;
+
+struct kvaser_msg_log_message {
+ u8 channel;
+ u8 flags;
+ __le16 time[3];
+ u8 dlc;
+ u8 time_offset;
+ __le32 id;
+ u8 data[8];
+} __packed;
+
+struct kvaser_msg {
+ u8 len;
+ u8 id;
+ union {
+ struct kvaser_msg_simple simple;
+ struct kvaser_msg_cardinfo cardinfo;
+ struct kvaser_msg_cardinfo2 cardinfo2;
+ struct kvaser_msg_softinfo softinfo;
+ struct kvaser_msg_busparams busparams;
+ struct kvaser_msg_tx_can tx_can;
+ struct kvaser_msg_rx_can rx_can;
+ struct kvaser_msg_chip_state_event chip_state_event;
+ struct kvaser_msg_tx_acknowledge tx_acknowledge;
+ struct kvaser_msg_error_event error_event;
+ struct kvaser_msg_ctrl_mode ctrl_mode;
+ struct kvaser_msg_flush_queue flush_queue;
+ struct kvaser_msg_log_message log_message;
+ } u;
+} __packed;
+
+struct kvaser_usb_tx_urb_context {
+ struct kvaser_usb_net_priv *priv;
+ u32 echo_index;
+ int dlc;
+};
+
+struct kvaser_usb {
+ struct usb_device *udev;
+ struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES];
+
+ struct usb_endpoint_descriptor *bulk_in, *bulk_out;
+ struct usb_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+ void *rxbuf[MAX_RX_URBS];
+ dma_addr_t rxbuf_dma[MAX_RX_URBS];
+};
+
+struct kvaser_usb_net_priv {
+ struct can_priv can;
+
+ atomic_t active_tx_urbs;
+ struct usb_anchor tx_submitted;
+ struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS];
+
+ struct completion start_comp, stop_comp;
+
+ struct kvaser_usb *dev;
+ struct net_device *netdev;
+ int channel;
+
+ struct can_berr_counter bec;
+};
+
+static struct usb_device_id kvaser_usb_table[] = {
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS |
+ KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID),
+ .driver_info = KVASER_HAS_TXRX_ERRORS },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev,
+ struct kvaser_msg *msg)
+{
+ int actual_len;
+
+ return usb_bulk_msg(dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ msg, msg->len, &actual_len,
+ USB_SEND_TIMEOUT);
+}
+
+static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id,
+ struct kvaser_msg *msg)
+{
+ struct kvaser_msg *tmp;
+ void *buf;
+ int actual_len;
+ int err;
+ int pos = 0;
+
+ buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ err = usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECV_TIMEOUT);
+ if (err < 0)
+ goto end;
+
+ while (pos <= actual_len - MSG_HEADER_LEN) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (pos + tmp->len > actual_len) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, tmp->len);
+ goto end;
+ }
+
+ pos += tmp->len;
+ }
+
+ err = -EINVAL;
+
+end:
+ kfree(buf);
+
+ return err;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg = {
+ .len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple),
+ .id = msg_id,
+ .u.simple.channel = channel,
+ .u.simple.tid = 0xff,
+ };
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_get_software_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version);
+
+ return 0;
+}
+
+static int kvaser_usb_get_card_info(struct kvaser_usb *dev)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0);
+ if (err)
+ return err;
+
+ err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg);
+ if (err)
+ return err;
+
+ dev->nchannels = msg.u.cardinfo.nchannels;
+
+ return 0;
+}
+
+static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct net_device_stats *stats;
+ struct kvaser_usb_tx_urb_context *context;
+ struct kvaser_usb_net_priv *priv;
+ struct sk_buff *skb;
+ struct can_frame *cf;
+ u8 channel = msg->u.tx_acknowledge.channel;
+ u8 tid = msg->u.tx_acknowledge.tid;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ if (!netif_device_present(priv->netdev))
+ return;
+
+ stats = &priv->netdev->stats;
+
+ context = &priv->tx_contexts[tid % MAX_TX_URBS];
+
+ /* Sometimes the state change doesn't come after a bus-off event */
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF)) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (skb) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ } else {
+ netdev_err(priv->netdev,
+ "No memory left for err_skb\n");
+ }
+
+ priv->can.can_stats.restarts++;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ stats->tx_packets++;
+ stats->tx_bytes += context->dlc;
+ can_get_echo_skb(priv->netdev, context->echo_index);
+
+ context->echo_index = MAX_TX_URBS;
+ atomic_dec(&priv->active_tx_urbs);
+
+ netif_wake_queue(priv->netdev);
+}
+
+static void kvaser_usb_simple_msg_callback(struct urb *urb)
+{
+ struct net_device *netdev = urb->context;
+
+ kfree(urb->transfer_buffer);
+
+ if (urb->status)
+ netdev_warn(netdev, "urb status received: %d\n",
+ urb->status);
+}
+
+static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv,
+ u8 msg_id)
+{
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device *netdev = priv->netdev;
+ struct kvaser_msg *msg;
+ struct urb *urb;
+ void *buf;
+ int err;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ return -ENOMEM;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+
+ msg = (struct kvaser_msg *)buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg->id = msg_id;
+ msg->u.simple.channel = priv->channel;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_simple_msg_callback, priv);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err) {
+ netdev_err(netdev, "Error transmitting URB\n");
+ usb_unanchor_urb(urb);
+ usb_free_urb(urb);
+ kfree(buf);
+ return err;
+ }
+
+ usb_free_urb(urb);
+
+ return 0;
+}
+
+static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv)
+{
+ int i;
+
+ usb_kill_anchored_urbs(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < MAX_TX_URBS; i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+}
+
+static void kvaser_usb_rx_error(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ struct kvaser_usb_net_priv *priv;
+ unsigned int new_state;
+ u8 channel, status, txerr, rxerr, error_factor;
+
+ switch (msg->id) {
+ case CMD_CAN_ERROR_EVENT:
+ channel = msg->u.error_event.channel;
+ status = msg->u.error_event.status;
+ txerr = msg->u.error_event.tx_errors_count;
+ rxerr = msg->u.error_event.rx_errors_count;
+ error_factor = msg->u.error_event.error_factor;
+ break;
+ case CMD_LOG_MESSAGE:
+ channel = msg->u.log_message.channel;
+ status = msg->u.log_message.data[0];
+ txerr = msg->u.log_message.data[2];
+ rxerr = msg->u.log_message.data[3];
+ error_factor = msg->u.log_message.data[1];
+ break;
+ case CMD_CHIP_STATE_EVENT:
+ channel = msg->u.chip_state_event.channel;
+ status = msg->u.chip_state_event.status;
+ txerr = msg->u.chip_state_event.tx_errors_count;
+ rxerr = msg->u.chip_state_event.rx_errors_count;
+ error_factor = 0;
+ break;
+ default:
+ dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n",
+ msg->id);
+ return;
+ }
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (status & M16C_STATE_BUS_RESET) {
+ kvaser_usb_unlink_tx_urbs(priv);
+ return;
+ }
+
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ new_state = priv->can.state;
+
+ netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status);
+
+ if (status & M16C_STATE_BUS_OFF) {
+ cf->can_id |= CAN_ERR_BUSOFF;
+
+ priv->can.can_stats.bus_off++;
+ if (!priv->can.restart_ms)
+ kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP);
+
+ netif_carrier_off(priv->netdev);
+
+ new_state = CAN_STATE_BUS_OFF;
+ } else if (status & M16C_STATE_BUS_PASSIVE) {
+ if (priv->can.state != CAN_STATE_ERROR_PASSIVE) {
+ cf->can_id |= CAN_ERR_CRTL;
+
+ if (txerr || rxerr)
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_PASSIVE
+ : CAN_ERR_CRTL_RX_PASSIVE;
+ else
+ cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE |
+ CAN_ERR_CRTL_RX_PASSIVE;
+
+ priv->can.can_stats.error_passive++;
+ }
+
+ new_state = CAN_STATE_ERROR_PASSIVE;
+ }
+
+ if (status == M16C_STATE_BUS_ERROR) {
+ if ((priv->can.state < CAN_STATE_ERROR_WARNING) &&
+ ((txerr >= 96) || (rxerr >= 96))) {
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = (txerr > rxerr)
+ ? CAN_ERR_CRTL_TX_WARNING
+ : CAN_ERR_CRTL_RX_WARNING;
+
+ priv->can.can_stats.error_warning++;
+ new_state = CAN_STATE_ERROR_WARNING;
+ } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+ }
+
+ if (!status) {
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+
+ new_state = CAN_STATE_ERROR_ACTIVE;
+ }
+
+ if (priv->can.restart_ms &&
+ (priv->can.state >= CAN_STATE_BUS_OFF) &&
+ (new_state < CAN_STATE_BUS_OFF)) {
+ cf->can_id |= CAN_ERR_RESTARTED;
+ netif_carrier_on(priv->netdev);
+
+ priv->can.can_stats.restarts++;
+ }
+
+ if (error_factor) {
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT;
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK);
+ if (error_factor & M16C_EF_CRCE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ |
+ CAN_ERR_PROT_LOC_CRC_DEL);
+ if (error_factor & M16C_EF_FORME)
+ cf->data[2] |= CAN_ERR_PROT_FORM;
+ if (error_factor & M16C_EF_STFE)
+ cf->data[2] |= CAN_ERR_PROT_STUFF;
+ if (error_factor & M16C_EF_BITE0)
+ cf->data[2] |= CAN_ERR_PROT_BIT0;
+ if (error_factor & M16C_EF_BITE1)
+ cf->data[2] |= CAN_ERR_PROT_BIT1;
+ if (error_factor & M16C_EF_TRE)
+ cf->data[2] |= CAN_ERR_PROT_TX;
+ }
+
+ cf->data[6] = txerr;
+ cf->data[7] = rxerr;
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ priv->can.state = new_state;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv,
+ const struct kvaser_msg *msg)
+{
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats = &priv->netdev->stats;
+
+ if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME |
+ MSG_FLAG_NERR)) {
+ netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n",
+ msg->u.rx_can.flag);
+
+ stats->rx_errors++;
+ return;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) {
+ skb = alloc_can_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id |= CAN_ERR_CRTL;
+ cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+ stats->rx_over_errors++;
+ stats->rx_errors++;
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+ }
+}
+
+static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ struct net_device_stats *stats;
+ u8 channel = msg->u.rx_can.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+ stats = &priv->netdev->stats;
+
+ if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR |
+ MSG_FLAG_OVERRUN)) {
+ kvaser_usb_rx_can_err(priv, msg);
+ return;
+ } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) {
+ netdev_warn(priv->netdev,
+ "Unhandled frame (flags: 0x%02x)",
+ msg->u.rx_can.flag);
+ return;
+ }
+
+ skb = alloc_can_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->tx_dropped++;
+ return;
+ }
+
+ cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) |
+ (msg->u.rx_can.msg[1] & 0x3f);
+ cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]);
+
+ if (msg->id == CMD_RX_EXT_MESSAGE) {
+ cf->can_id <<= 18;
+ cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) |
+ ((msg->u.rx_can.msg[3] & 0xff) << 6) |
+ (msg->u.rx_can.msg[4] & 0x3f);
+ cf->can_id |= CAN_EFF_FLAG;
+ }
+
+ if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME)
+ cf->can_id |= CAN_RTR_FLAG;
+ else
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ if (completion_done(&priv->start_comp) &&
+ netif_queue_stopped(priv->netdev)) {
+ netif_wake_queue(priv->netdev);
+ } else {
+ netif_start_queue(priv->netdev);
+ complete(&priv->start_comp);
+ }
+}
+
+static void kvaser_usb_stop_chip_reply(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ struct kvaser_usb_net_priv *priv;
+ u8 channel = msg->u.simple.channel;
+
+ if (channel >= dev->nchannels) {
+ dev_err(dev->udev->dev.parent,
+ "Invalid channel number (%d)\n", channel);
+ return;
+ }
+
+ priv = dev->nets[channel];
+
+ complete(&priv->stop_comp);
+}
+
+static void kvaser_usb_handle_message(const struct kvaser_usb *dev,
+ const struct kvaser_msg *msg)
+{
+ switch (msg->id) {
+ case CMD_START_CHIP_REPLY:
+ kvaser_usb_start_chip_reply(dev, msg);
+ break;
+
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_stop_chip_reply(dev, msg);
+ break;
+
+ case CMD_RX_STD_MESSAGE:
+ case CMD_RX_EXT_MESSAGE:
+ kvaser_usb_rx_can_msg(dev, msg);
+ break;
+
+ case CMD_CHIP_STATE_EVENT:
+ case CMD_CAN_ERROR_EVENT:
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_LOG_MESSAGE:
+ if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME)
+ kvaser_usb_rx_error(dev, msg);
+ break;
+
+ case CMD_TX_ACKNOWLEDGE:
+ kvaser_usb_tx_acknowledge(dev, msg);
+ break;
+
+ default:
+ dev_warn(dev->udev->dev.parent,
+ "Unhandled message (%d)\n", msg->id);
+ break;
+ }
+}
+
+static void kvaser_usb_read_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb *dev = urb->context;
+ struct kvaser_msg *msg;
+ int pos = 0;
+ int err, i;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ default:
+ dev_info(dev->udev->dev.parent, "Rx URB aborted (%d)\n",
+ urb->status);
+ goto resubmit_urb;
+ }
+
+ while (pos <= urb->actual_length - MSG_HEADER_LEN) {
+ msg = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ if (pos + msg->len > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ kvaser_usb_handle_message(dev, msg);
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ urb->transfer_buffer, RX_BUFFER_SIZE,
+ kvaser_usb_read_bulk_callback, dev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err == -ENODEV) {
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ netif_device_detach(dev->nets[i]->netdev);
+ }
+ } else if (err) {
+ dev_err(dev->udev->dev.parent,
+ "Failed resubmitting read bulk urb: %d\n", err);
+ }
+
+ return;
+}
+
+static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev)
+{
+ int i, err = 0;
+
+ if (dev->rxinitdone)
+ return 0;
+
+ for (i = 0; i < MAX_RX_URBS; i++) {
+ struct urb *urb = NULL;
+ u8 *buf = NULL;
+ dma_addr_t buf_dma;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for URBs\n");
+ err = -ENOMEM;
+ break;
+ }
+
+ buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE,
+ GFP_KERNEL, &buf_dma);
+ if (!buf) {
+ dev_warn(dev->udev->dev.parent,
+ "No memory left for USB buffer\n");
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ break;
+ }
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in->bEndpointAddress),
+ buf, RX_BUFFER_SIZE,
+ kvaser_usb_read_bulk_callback,
+ dev);
+ urb->transfer_dma = buf_dma;
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(urb, &dev->rx_submitted);
+
+ err = usb_submit_urb(urb, GFP_KERNEL);
+ if (err) {
+ usb_unanchor_urb(urb);
+ usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf,
+ buf_dma);
+ usb_free_urb(urb);
+ break;
+ }
+
+ dev->rxbuf[i] = buf;
+ dev->rxbuf_dma[i] = buf_dma;
+
+ usb_free_urb(urb);
+ }
+
+ if (i == 0) {
+ dev_warn(dev->udev->dev.parent,
+ "Cannot setup read URBs, error %d\n", err);
+ return err;
+ } else if (i < MAX_RX_URBS) {
+ dev_warn(dev->udev->dev.parent,
+ "RX performances may be slow\n");
+ }
+
+ dev->rxinitdone = true;
+
+ return 0;
+}
+
+static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_SET_CTRL_MODE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_ctrl_mode),
+ .u.ctrl_mode.tid = 0xff,
+ .u.ctrl_mode.channel = priv->channel,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT;
+ else
+ msg.u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL;
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->start_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->start_comp,
+ msecs_to_jiffies(START_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_open(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ err = open_candev(netdev);
+ if (err)
+ return err;
+
+ err = kvaser_usb_setup_rx_urbs(dev);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ goto error;
+
+ err = kvaser_usb_start_chip(priv);
+ if (err) {
+ netdev_warn(netdev, "Cannot start device, error %d\n", err);
+ goto error;
+ }
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+
+error:
+ close_candev(netdev);
+ return err;
+}
+
+static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+
+ for (i = 0; i < MAX_RX_URBS; i++)
+ usb_free_coherent(dev->udev, RX_BUFFER_SIZE,
+ dev->rxbuf[i],
+ dev->rxbuf_dma[i]);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++) {
+ struct kvaser_usb_net_priv *priv = dev->nets[i];
+
+ if (priv)
+ kvaser_usb_unlink_tx_urbs(priv);
+ }
+}
+
+static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv)
+{
+ int err;
+
+ init_completion(&priv->stop_comp);
+
+ err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP,
+ priv->channel);
+ if (err)
+ return err;
+
+ if (!wait_for_completion_timeout(&priv->stop_comp,
+ msecs_to_jiffies(STOP_TIMEOUT)))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv)
+{
+ struct kvaser_msg msg = {
+ .id = CMD_FLUSH_QUEUE,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_flush_queue),
+ .u.flush_queue.channel = priv->channel,
+ .u.flush_queue.flags = 0x00,
+ };
+
+ return kvaser_usb_send_msg(priv->dev, &msg);
+}
+
+static int kvaser_usb_close(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel))
+ netdev_warn(netdev, "Cannot reset card, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+
+ return 0;
+}
+
+static void kvaser_usb_write_bulk_callback(struct urb *urb)
+{
+ struct kvaser_usb_tx_urb_context *context = urb->context;
+ struct kvaser_usb_net_priv *priv;
+ struct net_device *netdev;
+
+ if (WARN_ON(!context))
+ return;
+
+ priv = context->priv;
+ netdev = priv->netdev;
+
+ kfree(urb->transfer_buffer);
+
+ if (!netif_device_present(netdev))
+ return;
+
+ if (urb->status)
+ netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
+}
+
+static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb,
+ struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct kvaser_usb *dev = priv->dev;
+ struct net_device_stats *stats = &netdev->stats;
+ struct can_frame *cf = (struct can_frame *)skb->data;
+ struct kvaser_usb_tx_urb_context *context = NULL;
+ struct urb *urb;
+ void *buf;
+ struct kvaser_msg *msg;
+ int i, err;
+ int ret = NETDEV_TX_OK;
+
+ if (can_dropped_invalid_skb(netdev, skb))
+ return NETDEV_TX_OK;
+
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ netdev_err(netdev, "No memory left for URBs\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ return NETDEV_TX_OK;
+ }
+
+ buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ usb_free_urb(urb);
+ goto nobufmem;
+ }
+
+ msg = buf;
+ msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can);
+ msg->u.tx_can.flags = 0;
+ msg->u.tx_can.channel = priv->channel;
+
+ if (cf->can_id & CAN_EFF_FLAG) {
+ msg->id = CMD_TX_EXT_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f;
+ msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f;
+ msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f;
+ msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff;
+ msg->u.tx_can.msg[4] = cf->can_id & 0x3f;
+ } else {
+ msg->id = CMD_TX_STD_MESSAGE;
+ msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f;
+ msg->u.tx_can.msg[1] = cf->can_id & 0x3f;
+ }
+
+ msg->u.tx_can.msg[5] = cf->can_dlc;
+ memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc);
+
+ if (cf->can_id & CAN_RTR_FLAG)
+ msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME;
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) {
+ if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) {
+ context = &priv->tx_contexts[i];
+ break;
+ }
+ }
+
+ if (!context) {
+ netdev_warn(netdev, "cannot find free context\n");
+ ret = NETDEV_TX_BUSY;
+ goto releasebuf;
+ }
+
+ context->priv = priv;
+ context->echo_index = i;
+ context->dlc = cf->can_dlc;
+
+ msg->u.tx_can.tid = context->echo_index;
+
+ usb_fill_bulk_urb(urb, dev->udev,
+ usb_sndbulkpipe(dev->udev,
+ dev->bulk_out->bEndpointAddress),
+ buf, msg->len,
+ kvaser_usb_write_bulk_callback, context);
+ usb_anchor_urb(urb, &priv->tx_submitted);
+
+ can_put_echo_skb(skb, netdev, context->echo_index);
+
+ atomic_inc(&priv->active_tx_urbs);
+
+ if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS)
+ netif_stop_queue(netdev);
+
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(err)) {
+ can_free_echo_skb(netdev, context->echo_index);
+
+ atomic_dec(&priv->active_tx_urbs);
+ usb_unanchor_urb(urb);
+
+ stats->tx_dropped++;
+
+ if (err == -ENODEV)
+ netif_device_detach(netdev);
+ else
+ netdev_warn(netdev, "Failed tx_urb %d\n", err);
+
+ goto releasebuf;
+ }
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ kfree(buf);
+nobufmem:
+ usb_free_urb(urb);
+ return ret;
+}
+
+static const struct net_device_ops kvaser_usb_netdev_ops = {
+ .ndo_open = kvaser_usb_open,
+ .ndo_stop = kvaser_usb_close,
+ .ndo_start_xmit = kvaser_usb_start_xmit,
+};
+
+static struct can_bittiming_const kvaser_usb_bittiming_const = {
+ .name = "kvaser_usb",
+ .tseg1_min = KVASER_USB_TSEG1_MIN,
+ .tseg1_max = KVASER_USB_TSEG1_MAX,
+ .tseg2_min = KVASER_USB_TSEG2_MIN,
+ .tseg2_max = KVASER_USB_TSEG2_MAX,
+ .sjw_max = KVASER_USB_SJW_MAX,
+ .brp_min = KVASER_USB_BRP_MIN,
+ .brp_max = KVASER_USB_BRP_MAX,
+ .brp_inc = KVASER_USB_BRP_INC,
+};
+
+static int kvaser_usb_set_bittiming(struct net_device *netdev)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ struct can_bittiming *bt = &priv->can.bittiming;
+ struct kvaser_usb *dev = priv->dev;
+ struct kvaser_msg msg = {
+ .id = CMD_SET_BUS_PARAMS,
+ .len = MSG_HEADER_LEN +
+ sizeof(struct kvaser_msg_busparams),
+ .u.busparams.channel = priv->channel,
+ .u.busparams.tid = 0xff,
+ .u.busparams.bitrate = cpu_to_le32(bt->bitrate),
+ .u.busparams.sjw = bt->sjw,
+ .u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1,
+ .u.busparams.tseg2 = bt->phase_seg2,
+ };
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+ msg.u.busparams.no_samp = 3;
+ else
+ msg.u.busparams.no_samp = 1;
+
+ return kvaser_usb_send_msg(dev, &msg);
+}
+
+static int kvaser_usb_set_mode(struct net_device *netdev,
+ enum can_mode mode)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+ int err;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP);
+ if (err)
+ return err;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int kvaser_usb_get_berr_counter(const struct net_device *netdev,
+ struct can_berr_counter *bec)
+{
+ struct kvaser_usb_net_priv *priv = netdev_priv(netdev);
+
+ *bec = priv->bec;
+
+ return 0;
+}
+
+static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev)
+{
+ int i;
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ unregister_netdev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(dev);
+
+ for (i = 0; i < dev->nchannels; i++) {
+ if (!dev->nets[i])
+ continue;
+
+ free_candev(dev->nets[i]->netdev);
+ }
+}
+
+static int kvaser_usb_init_one(struct usb_interface *intf,
+ const struct usb_device_id *id, int channel)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+ struct net_device *netdev;
+ struct kvaser_usb_net_priv *priv;
+ int i, err;
+
+ netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS);
+ if (!netdev) {
+ dev_err(&intf->dev, "Cannot alloc candev\n");
+ return -ENOMEM;
+ }
+
+ priv = netdev_priv(netdev);
+
+ init_completion(&priv->start_comp);
+ init_completion(&priv->stop_comp);
+
+ init_usb_anchor(&priv->tx_submitted);
+ atomic_set(&priv->active_tx_urbs, 0);
+
+ for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++)
+ priv->tx_contexts[i].echo_index = MAX_TX_URBS;
+
+ priv->dev = dev;
+ priv->netdev = netdev;
+ priv->channel = channel;
+
+ priv->can.state = CAN_STATE_STOPPED;
+ priv->can.clock.freq = CAN_USB_CLOCK;
+ priv->can.bittiming_const = &kvaser_usb_bittiming_const;
+ priv->can.do_set_bittiming = kvaser_usb_set_bittiming;
+ priv->can.do_set_mode = kvaser_usb_set_mode;
+ if (id->driver_info & KVASER_HAS_TXRX_ERRORS)
+ priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter;
+ priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES;
+ if (id->driver_info & KVASER_HAS_SILENT_MODE)
+ priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY;
+
+ netdev->flags |= IFF_ECHO;
+
+ netdev->netdev_ops = &kvaser_usb_netdev_ops;
+
+ SET_NETDEV_DEV(netdev, &intf->dev);
+
+ dev->nets[channel] = priv;
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ dev->nets[channel] = NULL;
+ return err;
+ }
+
+ netdev_dbg(netdev, "device registered\n");
+
+ return 0;
+}
+
+static void kvaser_usb_get_endpoints(const struct usb_interface *intf,
+ struct usb_endpoint_descriptor **in,
+ struct usb_endpoint_descriptor **out)
+{
+ const struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ iface_desc = &intf->altsetting[0];
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_bulk_in(endpoint))
+ *in = endpoint;
+
+ if (usb_endpoint_is_bulk_out(endpoint))
+ *out = endpoint;
+ }
+}
+
+static int kvaser_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct kvaser_usb *dev;
+ int err = -ENOMEM;
+ int i;
+
+ dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out);
+ if (!dev->bulk_in || !dev->bulk_out) {
+ dev_err(&intf->dev, "Cannot get usb endpoint(s)");
+ return err;
+ }
+
+ dev->udev = interface_to_usbdev(intf);
+
+ init_usb_anchor(&dev->rx_submitted);
+
+ usb_set_intfdata(intf, dev);
+
+ for (i = 0; i < MAX_NET_DEVICES; i++)
+ kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i);
+
+ err = kvaser_usb_get_software_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get software infos, error %d\n", err);
+ return err;
+ }
+
+ err = kvaser_usb_get_card_info(dev);
+ if (err) {
+ dev_err(&intf->dev,
+ "Cannot get card infos, error %d\n", err);
+ return err;
+ }
+
+ dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n",
+ ((dev->fw_version >> 24) & 0xff),
+ ((dev->fw_version >> 16) & 0xff),
+ (dev->fw_version & 0xffff));
+
+ for (i = 0; i < dev->nchannels; i++) {
+ err = kvaser_usb_init_one(intf, id, i);
+ if (err) {
+ kvaser_usb_remove_interfaces(dev);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static void kvaser_usb_disconnect(struct usb_interface *intf)
+{
+ struct kvaser_usb *dev = usb_get_intfdata(intf);
+
+ usb_set_intfdata(intf, NULL);
+
+ if (!dev)
+ return;
+
+ kvaser_usb_remove_interfaces(dev);
+}
+
+static struct usb_driver kvaser_usb_driver = {
+ .name = "kvaser_usb",
+ .probe = kvaser_usb_probe,
+ .disconnect = kvaser_usb_disconnect,
+ .id_table = kvaser_usb_table
+};
+
+module_usb_driver(kvaser_usb_driver);
+
+MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
+MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices");
+MODULE_LICENSE("GPL v2");
--
1.7.9.5
^ permalink raw reply related
* RE: [PATCH 3/3] net/mlx4_en: Add HW timestamping (TS) support
From: Yevgeny Petrilin @ 2012-10-02 7:32 UTC (permalink / raw)
To: Ben Hutchings
Cc: davem@davemloft.net, netdev@vger.kernel.org, Eugenia Emantayev
In-Reply-To: <1349104125.2577.7.camel@bwh-desktop.uk.solarflarecom.com>
> > +
> > + if (rx_filter != HWTSTAMP_FILTER_NONE)
> > + dev->features &= ~NETIF_F_HW_VLAN_RX;
> > + else
> > + dev->features |= NETIF_F_HW_VLAN_RX;
> [...]
>
> If you change dev->features you should also call
> netdev_features_change(dev) (holding only the RTNL lock, so after you
> unlock mdev->state_lock).
>
> Ben.
>
Thanks Ben,
Will fix.
Yevgeny
^ permalink raw reply
* Re: [PATCH net, 3/3] hyperv: Fix page buffer handling in rndis_filter_send_request()
From: Dan Carpenter @ 2012-10-02 8:38 UTC (permalink / raw)
To: Haiyang Zhang; +Cc: olaf, netdev, jasowang, linux-kernel, devel, davem
In-Reply-To: <1349130657-7987-3-git-send-email-haiyangz@microsoft.com>
On Mon, Oct 01, 2012 at 03:30:57PM -0700, Haiyang Zhang wrote:
> Add another page buffer if the request message crossed page boundary.
>
What are the user visible effects of this bug fix? Please put that
in the commit message.
regards,
dan carpenter
^ permalink raw reply
* Re: drivers/net/ethernet/chelsio/cxgb4/t4_hw.c:363:64: sparse: incorrect type in argument 3 (different base types)
From: Dan Carpenter @ 2012-10-02 8:46 UTC (permalink / raw)
To: Jay Hernandez; +Cc: Fengguang Wu, Vipul Pandya, kernel-janitors, netdev
In-Reply-To: <8A71B368A89016469F72CD08050AD3340C527934@maui.asicdesigners.com>
On Mon, Oct 01, 2012 at 05:48:55PM -0700, Jay Hernandez wrote:
> Hi Fengguang,
>
> Thanks for pointing this out we have a fix which addresses these issues.
> What's interesting is I did not see these warnings I tried to reproduce
> the errors on my setup. Is there a special flag we can use to reproduce
> the sparse warnings.
>
Yup.
http://lwn.net/Articles/205624/
regards,
dan carpenter
^ permalink raw reply
* Re: net/ipv4/ipconfig.c:1590:2: error: implicit declaration of function 'ic_nameservers_predef'
From: Fengguang Wu @ 2012-10-02 8:54 UTC (permalink / raw)
To: Rami Rosen; +Cc: Christoph Fritz, kernel-janitors, netdev, andriy.shevchenko
In-Reply-To: <CAKoUArkDK8sQ=DRgrPhEnnWPsdMkgNtZE6S2o5rx6tXhfB3SsA@mail.gmail.com>
On Tue, Oct 02, 2012 at 08:24:05AM +0200, Rami Rosen wrote:
> Hi,
> commit 842b08bbee448b2069aaebb7f18b40942ad2a1bd
> fixes it.
>
> git show 842b08bbee448b2069aaebb7f18b40942ad2a1bd
>
> commit 842b08bbee448b2069aaebb7f18b40942ad2a1bd
>
> Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Date: Mon Sep 24 22:09:58 2012 +0000
>
> ipconfig: fix trivial build error
>
> The commit 5e953778a2aab04929a5e7b69f53dc26e39b079e ("ipconfig: add nameserv
> IPs to kernel-parameter ip=") introduces ic_nameservers_predef() that define
> only for BOOTP. However it is used by ip_auto_config_setup() as well. This
> patch moves it outside of #ifdef BOOTP.
Sorry, I get this bug while doing bisectibility tests on the linux-can
tree. Obviously I should not count the net tree's commits as part of
the linux-can tree. Fixed.
Thanks,
Fengguang
> On Tue, Oct 2, 2012 at 5:27 AM, Fengguang Wu <fengguang.wu@intel.com> wrote:
> > Hi Christoph,
> >
> > FYI, kernel build failed on
> >
> > commit: 5e953778a2aab04929a5e7b69f53dc26e39b079e ipconfig: add nameserver IPs to kernel-parameter ip=
> > config: i386-randconfig-b106 (attached as .config)
> >
> > All error/warnings:
> >
> > net/ipv4/ipconfig.c: In function 'ip_auto_config_setup':
> > net/ipv4/ipconfig.c:1590:2: error: implicit declaration of function 'ic_nameservers_predef' [-Werror=implicit-function-declaration]
> > cc1: some warnings being treated as errors
> >
> > vim +1590 net/ipv4/ipconfig.c
> >
> > 92ffb85d (Amos Waterland 2008-01-05 1578) */
> > ^1da177e (Linus Torvalds 2005-04-16 1579) if (ic_proto_name(addrs))
> > ^1da177e (Linus Torvalds 2005-04-16 1580) return 1;
> > ^1da177e (Linus Torvalds 2005-04-16 1581)
> > 92ffb85d (Amos Waterland 2008-01-05 1582) /* If no static IP is given, turn off autoconfig and bail. */
> > 92ffb85d (Amos Waterland 2008-01-05 1583) if (*addrs == 0 ||
> > 92ffb85d (Amos Waterland 2008-01-05 1584) strcmp(addrs, "off") == 0 ||
> > 92ffb85d (Amos Waterland 2008-01-05 1585) strcmp(addrs, "none") == 0) {
> > 92ffb85d (Amos Waterland 2008-01-05 1586) ic_enable = 0;
> > 92ffb85d (Amos Waterland 2008-01-05 1587) return 1;
> > 92ffb85d (Amos Waterland 2008-01-05 1588) }
> > 92ffb85d (Amos Waterland 2008-01-05 1589)
> > 5e953778 (Christoph Fritz 2012-09-21 @1590) ic_nameservers_predef();
> > 5e953778 (Christoph Fritz 2012-09-21 1591)
> > 92ffb85d (Amos Waterland 2008-01-05 1592) /* Parse string for static IP assignment. */
> > ^1da177e (Linus Torvalds 2005-04-16 1593) ip = addrs;
> > ^1da177e (Linus Torvalds 2005-04-16 1594) while (ip && *ip) {
> > ^1da177e (Linus Torvalds 2005-04-16 1595) if ((cp = strchr(ip, ':')))
> > ^1da177e (Linus Torvalds 2005-04-16 1596) *cp++ = '\0';
> >
> > ---
> > 0-DAY kernel build testing backend Open Source Technology Centre
> > Fengguang Wu, Yuanhan Liu Intel Corporation
^ permalink raw reply
* [PATCH v2] ipv6: don't add link local route when there is no link local address
From: Nicolas Dichtel @ 2012-10-02 9:19 UTC (permalink / raw)
To: davem; +Cc: netdev, yoshfuji, Nicolas Dichtel
In-Reply-To: <20121001.165527.1025640918619977740.davem@davemloft.net>
When an address is added on loopback (ip -6 a a 2002::1/128 dev lo), a route
to fe80::/64 is added in the main table:
unreachable fe80::/64 dev lo proto kernel metric 256 error -101
This route does not match any prefix (no fe80:: address on lo). In fact,
addrconf_dev_config() will not add link local address because this function
filters interfaces by type. If the link local address is added manually, the
route to the link local prefix will be automatically added by
addrconf_add_linklocal().
Note also, that this route is not deleted when the address is removed.
After looking at the code, it seems that addrconf_add_lroute() is redundant with
addrconf_add_linklocal(), because this function will add the link local route
when the link local address is configured.
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
---
v2: remove useless curly braces after the if statement in
addrconf_sit_config()
net/ipv6/addrconf.c | 15 ++-------------
1 file changed, 2 insertions(+), 13 deletions(-)
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index ea3e9af..caa8035 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1782,14 +1782,6 @@ static void sit_route_add(struct net_device *dev)
}
#endif
-static void addrconf_add_lroute(struct net_device *dev)
-{
- struct in6_addr addr;
-
- ipv6_addr_set(&addr, htonl(0xFE800000), 0, 0, 0);
- addrconf_prefix_route(&addr, 64, dev, 0, 0);
-}
-
static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
{
struct inet6_dev *idev;
@@ -1807,8 +1799,6 @@ static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
if (!(dev->flags & IFF_LOOPBACK))
addrconf_add_mroute(dev);
- /* Add link local route */
- addrconf_add_lroute(dev);
return idev;
}
@@ -2487,10 +2477,9 @@ static void addrconf_sit_config(struct net_device *dev)
sit_add_v4_addrs(idev);
- if (dev->flags&IFF_POINTOPOINT) {
+ if (dev->flags&IFF_POINTOPOINT)
addrconf_add_mroute(dev);
- addrconf_add_lroute(dev);
- } else
+ else
sit_route_add(dev);
}
#endif
--
1.7.12
^ permalink raw reply related
* [PATCH net-next v2] netxen: write IP address to firmware when using bonding
From: Nikolay Aleksandrov @ 2012-10-02 9:16 UTC (permalink / raw)
To: sony.chacko; +Cc: agospoda, rajesh.borundia, davem, netdev
In-Reply-To: <1348562883-14780-1-git-send-email-nikolay@redhat.com>
This patch allows LRO aggregation on bonded devices that contain an NX3031
device. It also adds a for_each_netdev_in_bond_rcu(bond, slave) macro
which executes for each slave that has bond as master.
V2: Remove local ip caching, retrieve addresses dynamically and
restore them if necessary.
Note: Tested with NX3031 adapter.
Signed-off-by: Andy Gospodarek <agospoda@redhat.com>
Signed-off-by: Nikolay Aleksandrov <nikolay@redhat.com>
---
drivers/net/ethernet/qlogic/netxen/netxen_nic.h | 6 -
.../net/ethernet/qlogic/netxen/netxen_nic_main.c | 192 +++++++++++----------
include/linux/netdevice.h | 3 +
3 files changed, 100 insertions(+), 101 deletions(-)
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h
index eb3dfdb..cb4f2ce 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h
@@ -955,11 +955,6 @@ typedef struct nx_mac_list_s {
uint8_t mac_addr[ETH_ALEN+2];
} nx_mac_list_t;
-struct nx_vlan_ip_list {
- struct list_head list;
- __be32 ip_addr;
-};
-
/*
* Interrupt coalescing defaults. The defaults are for 1500 MTU. It is
* adjusted based on configured MTU.
@@ -1605,7 +1600,6 @@ struct netxen_adapter {
struct net_device *netdev;
struct pci_dev *pdev;
struct list_head mac_list;
- struct list_head vlan_ip_list;
spinlock_t tx_clean_lock;
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index e2a4858..8e3eb61 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -90,7 +90,6 @@ static irqreturn_t netxen_intr(int irq, void *data);
static irqreturn_t netxen_msi_intr(int irq, void *data);
static irqreturn_t netxen_msix_intr(int irq, void *data);
-static void netxen_free_vlan_ip_list(struct netxen_adapter *);
static void netxen_restore_indev_addr(struct net_device *dev, unsigned long);
static struct rtnl_link_stats64 *netxen_nic_get_stats(struct net_device *dev,
struct rtnl_link_stats64 *stats);
@@ -1451,7 +1450,6 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
spin_lock_init(&adapter->tx_clean_lock);
INIT_LIST_HEAD(&adapter->mac_list);
- INIT_LIST_HEAD(&adapter->vlan_ip_list);
err = netxen_setup_pci_map(adapter);
if (err)
@@ -1586,7 +1584,7 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev)
cancel_work_sync(&adapter->tx_timeout_task);
- netxen_free_vlan_ip_list(adapter);
+ netxen_restore_indev_addr(netdev, NETDEV_DOWN);
netxen_nic_detach(adapter);
nx_decr_dev_ref_cnt(adapter);
@@ -3135,66 +3133,22 @@ netxen_destip_supported(struct netxen_adapter *adapter)
return 1;
}
-static void
-netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
+static inline __be32
+netxen_get_addr(struct net_device *dev)
{
- struct nx_vlan_ip_list *cur;
- struct list_head *head = &adapter->vlan_ip_list;
-
- while (!list_empty(head)) {
- cur = list_entry(head->next, struct nx_vlan_ip_list, list);
- netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN);
- list_del(&cur->list);
- kfree(cur);
- }
-
-}
-static void
-netxen_list_config_vlan_ip(struct netxen_adapter *adapter,
- struct in_ifaddr *ifa, unsigned long event)
-{
- struct net_device *dev;
- struct nx_vlan_ip_list *cur, *tmp_cur;
- struct list_head *head;
-
- dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL;
-
- if (dev == NULL)
- return;
-
- if (!is_vlan_dev(dev))
- return;
+ struct in_device *in_dev;
+ __be32 addr = 0;
- switch (event) {
- case NX_IP_UP:
- list_for_each(head, &adapter->vlan_ip_list) {
- cur = list_entry(head, struct nx_vlan_ip_list, list);
+ rcu_read_lock();
+ in_dev = __in_dev_get_rcu(dev);
- if (cur->ip_addr == ifa->ifa_address)
- return;
- }
+ if (in_dev)
+ addr = inet_confirm_addr(in_dev, 0, 0, RT_SCOPE_HOST);
- cur = kzalloc(sizeof(struct nx_vlan_ip_list), GFP_ATOMIC);
- if (cur == NULL) {
- printk(KERN_ERR "%s: failed to add vlan ip to list\n",
- adapter->netdev->name);
- return;
- }
-
- cur->ip_addr = ifa->ifa_address;
- list_add_tail(&cur->list, &adapter->vlan_ip_list);
- break;
- case NX_IP_DOWN:
- list_for_each_entry_safe(cur, tmp_cur,
- &adapter->vlan_ip_list, list) {
- if (cur->ip_addr == ifa->ifa_address) {
- list_del(&cur->list);
- kfree(cur);
- break;
- }
- }
- }
+ rcu_read_unlock();
+ return addr;
}
+
static void
netxen_config_indev_addr(struct netxen_adapter *adapter,
struct net_device *dev, unsigned long event)
@@ -3213,12 +3167,10 @@ netxen_config_indev_addr(struct netxen_adapter *adapter,
case NETDEV_UP:
netxen_config_ipaddr(adapter,
ifa->ifa_address, NX_IP_UP);
- netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP);
break;
case NETDEV_DOWN:
netxen_config_ipaddr(adapter,
ifa->ifa_address, NX_IP_DOWN);
- netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN);
break;
default:
break;
@@ -3233,15 +3185,54 @@ netxen_restore_indev_addr(struct net_device *netdev, unsigned long event)
{
struct netxen_adapter *adapter = netdev_priv(netdev);
- struct nx_vlan_ip_list *pos, *tmp_pos;
+ struct net_device *vlan_dev, *real_dev;
unsigned long ip_event;
+ __be32 addr = 0;
ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN;
netxen_config_indev_addr(adapter, netdev, event);
- list_for_each_entry_safe(pos, tmp_pos, &adapter->vlan_ip_list, list) {
- netxen_config_ipaddr(adapter, pos->ip_addr, ip_event);
+ rcu_read_lock();
+ for_each_netdev_rcu(&init_net, vlan_dev) {
+ if (vlan_dev->priv_flags & IFF_802_1Q_VLAN) {
+ real_dev = vlan_dev_real_dev(vlan_dev);
+
+ if (real_dev == netdev) {
+ addr = netxen_get_addr(vlan_dev);
+
+ if (addr) {
+ netxen_config_ipaddr(adapter, addr,
+ ip_event);
+ }
+ }
+ }
}
+
+ if (netdev->master) {
+ addr = netxen_get_addr(netdev->master);
+ if (addr)
+ netxen_config_ipaddr(adapter, addr, ip_event);
+ }
+ rcu_read_unlock();
+}
+
+static inline int
+netxen_config_checkdev(struct net_device *dev)
+{
+ struct netxen_adapter *adapter;
+
+ if (!is_netxen_netdev(dev))
+ return -ENODEV;
+
+ adapter = netdev_priv(dev);
+
+ if (!adapter)
+ return -ENODEV;
+
+ if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
+ return -ENODEV;
+
+ return 0;
}
static int netxen_netdev_event(struct notifier_block *this,
@@ -3260,18 +3251,26 @@ recheck:
goto recheck;
}
- if (!is_netxen_netdev(dev))
- goto done;
+ /* If this is a bonding device, look for netxen-based slaves*/
+ if (dev->priv_flags & IFF_BONDING) {
+ struct net_device *slave;
- adapter = netdev_priv(dev);
+ rcu_read_lock();
+ for_each_netdev_in_bond_rcu(dev, slave) {
+ if (netxen_config_checkdev(slave) < 0)
+ continue;
- if (!adapter)
- goto done;
-
- if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
- goto done;
+ adapter = netdev_priv(slave);
+ netxen_config_indev_addr(adapter, orig_dev, event);
+ }
+ rcu_read_unlock();
+ } else {
+ if (netxen_config_checkdev(dev) < 0)
+ goto done;
- netxen_config_indev_addr(adapter, orig_dev, event);
+ adapter = netdev_priv(dev);
+ netxen_config_indev_addr(adapter, orig_dev, event);
+ }
done:
return NOTIFY_DONE;
}
@@ -3282,10 +3281,11 @@ netxen_inetaddr_event(struct notifier_block *this,
{
struct netxen_adapter *adapter;
struct net_device *dev;
-
+ unsigned long ip_event;
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL;
+ ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN;
recheck:
if (dev == NULL)
@@ -3296,30 +3296,35 @@ recheck:
goto recheck;
}
- if (!is_netxen_netdev(dev))
- goto done;
+ /* If this is a bonding device, look for netxen-based slaves*/
+ if (dev->priv_flags & IFF_BONDING) {
+ struct net_device *slave;
- adapter = netdev_priv(dev);
+ rcu_read_lock();
+ for_each_netdev_in_bond_rcu(dev, slave) {
+ if (netxen_config_checkdev(slave) < 0)
+ continue;
- if (!adapter || !netxen_destip_supported(adapter))
- goto done;
+ adapter = netdev_priv(slave);
- if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC)
- goto done;
+ if (!netxen_destip_supported(adapter))
+ continue;
- switch (event) {
- case NETDEV_UP:
- netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP);
- netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP);
- break;
- case NETDEV_DOWN:
- netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_DOWN);
- netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN);
- break;
- default:
- break;
- }
+ netxen_config_ipaddr(adapter, ifa->ifa_address,
+ ip_event);
+ }
+ rcu_read_unlock();
+ } else {
+ if (netxen_config_checkdev(dev) < 0)
+ goto done;
+
+ adapter = netdev_priv(dev);
+ if (!netxen_destip_supported(adapter))
+ goto done;
+
+ netxen_config_ipaddr(adapter, ifa->ifa_address, ip_event);
+ }
done:
return NOTIFY_DONE;
}
@@ -3335,9 +3340,6 @@ static struct notifier_block netxen_inetaddr_cb = {
static void
netxen_restore_indev_addr(struct net_device *dev, unsigned long event)
{ }
-static void
-netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
-{ }
#endif
static struct pci_error_handlers netxen_err_handler = {
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 59dc05f3..463bb40 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1578,6 +1578,9 @@ extern rwlock_t dev_base_lock; /* Device list lock */
list_for_each_entry_continue(d, &(net)->dev_base_head, dev_list)
#define for_each_netdev_continue_rcu(net, d) \
list_for_each_entry_continue_rcu(d, &(net)->dev_base_head, dev_list)
+#define for_each_netdev_in_bond_rcu(bond, slave) \
+ for_each_netdev_rcu(&init_net, slave) \
+ if (slave->master == bond)
#define net_device_entry(lh) list_entry(lh, struct net_device, dev_list)
static inline struct net_device *next_net_device(struct net_device *dev)
--
1.7.11.4
^ permalink raw reply related
* Re: tg3 driver upgrade (Linux 2.6.32 -> 3.2) breaks IBM Bladecenter SoL
From: Ferenc Wagner @ 2012-10-02 9:31 UTC (permalink / raw)
To: Michael Chan
Cc: netdev, Matt Carlson, Grant Likely, Rob Herring, linux-kernel
In-Reply-To: <1349083982.5420.16.camel@LTIRV-MCHAN1.corp.ad.broadcom.com>
"Michael Chan" <mchan@broadcom.com> writes:
> On Fri, 2012-09-28 at 22:45 +0200, Ferenc Wagner wrote:
>
>> Upgrading the kernel on our HS20 blades resulted in their SoL (serial
>> over LAN) connection being broken. The disconnection happens when eth0
>> (the interface involved in SoL) is brought up during the boot sequence.
>> If I later "ip link set eth0 down", then the connection is restored, but
>> "ip link set eth0 up" breaks it again on 3.2. ethtool -a, -c, -g, -k
>> and -u show no difference; ethtool -i on the 2.6.32 kernel reports:
>>
>> driver: tg3
>> version: 3.116
>> firmware-version: 5704s-v3.38, ASFIPMIs v2.47
>> bus-info: 0000:05:01.0
>>
>> In the 3.2 kernel the driver version is 3.121.
>
> 2.6.32 to 3.2 is a big jump. Can you narrow this down further? It will
> be hard for us to find a HS20 with 5704 to test this. Thanks.
Certainly, I'm bisecting it now, but I thought I would drop in the
question in case it rings some bells somewhere. Given the nature of the
problem, it isn't much fun to bisect, and the stripped down kernel I'm
testing with breaks the SoL connection for a couple of seconds even in
the "good" cases. I'm already down to 13 steps...
--
Thanks,
Feri.
^ permalink raw reply
* [PATCH 1/2] net: ethernet: clean out braces / old code (found via checkpatch)
From: matthew @ 2012-10-02 11:41 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev, davem, Matthew Walster
In-Reply-To: <1349178105-28269-1-git-send-email-matthew@walster.org>
From: Matthew Walster <matthew@walster.org>
Remove an old commented out piece of code.
Remove an if(true) statement.
Remove a set of braces that weren't strictly necessary.
All found by running checkpatch.pl against the code.
Signed-off-by: Matthew Walster <matthew@walster.org>
---
net/ethernet/eth.c | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index 4efad53..a9f8531 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -178,11 +178,8 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
* seems to set IFF_PROMISC.
*/
- else if (1 /*dev->flags&IFF_PROMISC */ ) {
- if (unlikely(!ether_addr_equal_64bits(eth->h_dest,
- dev->dev_addr)))
- skb->pkt_type = PACKET_OTHERHOST;
- }
+ else if (unlikely(!ether_addr_equal_64bits(eth->h_dest, dev->dev_addr)))
+ skb->pkt_type = PACKET_OTHERHOST;
/*
* Some variants of DSA tagging don't have an ethertype field
--
1.7.10.4
^ permalink raw reply related
* checkpatch run on net/ethernet
From: matthew @ 2012-10-02 11:41 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev, davem
This is my first patch, it's just a checkpatch run. I figured I would also clean up the code as there appears to be long-since-obsoleted comments and code in there.
A fairly trivial change, but worthy of inclusion IMO all the same!
Matthew Walster
^ permalink raw reply
* [PATCH 2/2] net: ethernet: Remove obsolete comment
From: matthew @ 2012-10-02 11:41 UTC (permalink / raw)
To: linux-kernel; +Cc: netdev, davem, Matthew Walster
In-Reply-To: <1349178105-28269-1-git-send-email-matthew@walster.org>
From: Matthew Walster <matthew@walster.org>
The deleted comment was related to code I've just removed in the previous patch.
---
net/ethernet/eth.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/net/ethernet/eth.c b/net/ethernet/eth.c
index a9f8531..7d72108 100644
--- a/net/ethernet/eth.c
+++ b/net/ethernet/eth.c
@@ -170,14 +170,6 @@ __be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
skb->pkt_type = PACKET_MULTICAST;
}
- /*
- * This ALLMULTI check should be redundant by 1.4
- * so don't forget to remove it.
- *
- * Seems, you forgot to remove it. All silly devices
- * seems to set IFF_PROMISC.
- */
-
else if (unlikely(!ether_addr_equal_64bits(eth->h_dest, dev->dev_addr)))
skb->pkt_type = PACKET_OTHERHOST;
--
1.7.10.4
^ permalink raw reply related
* [patch] bnx2x: use strlcpy() to copy a string
From: Dan Carpenter @ 2012-10-02 11:47 UTC (permalink / raw)
To: Eilon Greenstein; +Cc: netdev, kernel-janitors
DRV_MODULE_VERSION is smaller than the ->version buffer so the memcpy()
copies 1 byte past the end of the string. It's not super harmful, but
it makes the static checkers complain.
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index f7ed122..d5648fc 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -3052,9 +3052,8 @@ static void bnx2x_drv_info_ether_stat(struct bnx2x *bp)
struct eth_stats_info *ether_stat =
&bp->slowpath->drv_info_to_mcp.ether_stat;
- /* leave last char as NULL */
- memcpy(ether_stat->version, DRV_MODULE_VERSION,
- ETH_STAT_INFO_VERSION_LEN - 1);
+ strlcpy(ether_stat->version, DRV_MODULE_VERSION,
+ ETH_STAT_INFO_VERSION_LEN);
bp->sp_objs[0].mac_obj.get_n_elements(bp, &bp->sp_objs[0].mac_obj,
DRV_INFO_ETH_STAT_NUM_MACS_REQUIRED,
^ permalink raw reply related
* Re: tg3 driver upgrade (Linux 2.6.32 -> 3.2) breaks IBM Bladecenter SoL
From: Ferenc Wagner @ 2012-10-02 12:07 UTC (permalink / raw)
To: Michael Chan
Cc: netdev, Matt Carlson, Grant Likely, Rob Herring, linux-kernel,
wferi
In-Reply-To: <1349083982.5420.16.camel@LTIRV-MCHAN1.corp.ad.broadcom.com>
"Michael Chan" <mchan@broadcom.com> writes:
> On Fri, 2012-09-28 at 22:45 +0200, Ferenc Wagner wrote:
>
>> Upgrading the kernel on our HS20 blades resulted in their SoL (serial
>> over LAN) connection being broken. The disconnection happens when eth0
>> (the interface involved in SoL) is brought up during the boot sequence.
>> If I later "ip link set eth0 down", then the connection is restored, but
>> "ip link set eth0 up" breaks it again on 3.2. ethtool -a, -c, -g, -k
>> and -u show no difference; ethtool -i on the 2.6.32 kernel reports:
>>
>> driver: tg3
>> version: 3.116
>> firmware-version: 5704s-v3.38, ASFIPMIs v2.47
>> bus-info: 0000:05:01.0
>>
>> In the 3.2 kernel the driver version is 3.121.
>
> 2.6.32 to 3.2 is a big jump. Can you narrow this down further? It will
> be hard for us to find a HS20 with 5704 to test this. Thanks.
I'm done with bisecting it: the first bad commit is:
commit dabc5c670d3f86d15ee4f42ab38ec5bd2682487d
Author: Matt Carlson <mcarlson@broadcom.com>
Date: Thu May 19 12:12:52 2011 +0000
tg3: Move TSO_CAPABLE assignment
This patch moves the code that asserts the TSO_CAPABLE flag closer to
where the TSO capabilities flags are set. There isn't a good enough
reason for the code to be separated.
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Reviewed-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
On the other hand, losing the SoL console even temporarily during boot
(as it happens with a minimal kernel before this commit) isn't nice
either. I'll try to look after that, too, just mentioning it here...
--
Regards,
Feri.
^ permalink raw reply
* Re: [RFC PATCH net-next] tcp: introduce tcp_tw_interval to specifiy the time of TIME-WAIT
From: Neil Horman @ 2012-10-02 12:09 UTC (permalink / raw)
To: Cong Wang
Cc: netdev, David S. Miller, Alexey Kuznetsov, Patrick McHardy,
Eric Dumazet
In-Reply-To: <1349161479.22107.17.camel@cr0>
On Tue, Oct 02, 2012 at 03:04:39PM +0800, Cong Wang wrote:
> On Fri, 2012-09-28 at 09:16 -0400, Neil Horman wrote:
> > On Fri, Sep 28, 2012 at 02:33:07PM +0800, Cong Wang wrote:
> > > On Thu, 2012-09-27 at 10:23 -0400, Neil Horman wrote:
> > > > On Thu, Sep 27, 2012 at 04:41:01PM +0800, Cong Wang wrote:
> > > > > Some customer requests this feature, as they stated:
> > > > >
> > > > > "This parameter is necessary, especially for software that continually
> > > > > creates many ephemeral processes which open sockets, to avoid socket
> > > > > exhaustion. In many cases, the risk of the exhaustion can be reduced by
> > > > > tuning reuse interval to allow sockets to be reusable earlier.
> > > > >
> > > > > In commercial Unix systems, this kind of parameters, such as
> > > > > tcp_timewait in AIX and tcp_time_wait_interval in HP-UX, have
> > > > > already been available. Their implementations allow users to tune
> > > > > how long they keep TCP connection as TIME-WAIT state on the
> > > > > millisecond time scale."
> > > > >
> > > > > We indeed have "tcp_tw_reuse" and "tcp_tw_recycle", but these tunings
> > > > > are not equivalent in that they cannot be tuned directly on the time
> > > > > scale nor in a safe way, as some combinations of tunings could still
> > > > > cause some problem in NAT. And, I think second scale is enough, we don't
> > > > > have to make it in millisecond time scale.
> > > > >
> > > > I think I have a little difficultly seeing how this does anything other than
> > > > pay lip service to actually having sockets spend time in TIME_WAIT state. That
> > > > is to say, while I see users using this to just make the pain stop. If we wait
> > > > less time than it takes to be sure that a connection isn't being reused (either
> > > > by waiting two segment lifetimes, or by checking timestamps), then you might as
> > > > well not wait at all. I see how its tempting to be able to say "Just don't wait
> > > > as long", but it seems that theres no difference between waiting half as long as
> > > > the RFC mandates, and waiting no time at all. Neither is a good idea.
> > >
> > > I don't think reducing TIME_WAIT is a good idea either, but there must
> > > be some reason behind as several UNIX provides a microsecond-scale
> > > tuning interface, or maybe in non-recycle mode, their RTO is much less
> > > than 2*MSL?
> > >
> > My guess? Cash was the reason. I certainly wasn't there for any of those
> > developments, but a setting like this just smells to me like some customer waved
> > some cash under IBM's/HP's/Sun's nose and said, "We'd like to get our tcp
> > sockets back to CLOSED state faster, what can you do for us?"
>
> Yeah, maybe. But it still doesn't make sense even if they are sure their
> packets are impossible to linger in their high-speed LAN for 2*MSL?
>
No it doesn't make sense, but the universal rule is that the business people
will focus more on revenue recognition than on sound design pracice.
> >
> > > >
> > > > Given the problem you're trying to solve here, I'll ask the standard question in
> > > > response: How does using SO_REUSEADDR not solve the problem? Alternatively, in
> > > > a pinch, why not reduce the tcp_max_tw_buckets sufficiently to start forcing
> > > > TIME_WAIT sockets back into CLOSED state?
> > > >
> > > > The code looks fine, but the idea really doesn't seem like a good plan to me.
> > > > I'm sure HPUX/Solaris/AIX/etc have done this in response to customer demand, but
> > > > that doesn't make it the right solution.
> > > >
> > >
> > > *I think* the customer doesn't want to modify their applications, so
> > > that is why they don't use SO_REUSERADDR.
> > >
> > Well, ok, thats a legitimate distro problem. What its not is an upstream
> > problem. Fixing the appilcation is the right thing to do, wether or not they
> > want to.
> >
> > > I didn't know tcp_max_tw_buckets can do the trick, nor the customer, so
> > > this is a side effect of tcp_max_tw_buckets? Is it documented?
> > man 7 tcp:
> > tcp_max_tw_buckets (integer; default: see below; since Linux 2.4)
> > The maximum number of sockets in TIME_WAIT state allowed in the
> > system. This limit exists only to prevent simple
> > denial-of-service attacks. The default value of NR_FILE*2 is
> > adjusted depending on the memory in the system. If this number
> > is exceeded, the socket is closed and a warning is printed.
> >
>
> Hey, "a warning is printed" seems not very friendly. ;)
>
No, its not very friendly, but the people using this are violating the RFC,
which isn't very friendly. :)
> Thanks!
>
>
^ permalink raw reply
* Re: GPF in ip6_dst_lookup_tail
From: Dave Jones @ 2012-10-02 13:59 UTC (permalink / raw)
To: netdev
In-Reply-To: <20120927140323.GA1459@redhat.com>
On Thu, Sep 27, 2012 at 10:03:23AM -0400, Dave Jones wrote:
> general protection fault: 0000 [#1] SMP
> Modules linked in: ipt_ULOG tun fuse binfmt_misc nfnetlink nfc caif_socket caif phonet can llc2 pppoe pppox ppp_generic slhc irda crc_ccitt rds af_key decnet rose x25 atm netrom appletalk ipx p8023 psnap p8022 llc ax25 lockd sunrpc bluetooth rfkill ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack kvm_intel kvm usb_debug crc32c_intel ghash_clmulni_intel microcode pcspkr i2c_i801 e1000e uinput i915 video i2c_algo_bit drm_kms_helper drm i2c_core
> CPU 4
> Pid: 21651, comm: trinity-child4 Not tainted 3.6.0-rc7+ #55
> RIP: 0010:[<ffffffff81628797>] [<ffffffff81628797>] ip6_dst_lookup_tail+0x147/0x380
> RSP: 0018:ffff8800144c3a48 EFLAGS: 00010286
> RAX: 8000000000000011 RBX: ffff8800144c3b00 RCX: 0000000000000001
> RDX: ffff8800144c2000 RSI: 0000000000000000 RDI: 0000000000000282
> RBP: ffff8800144c3ae8 R08: 0000000000000014 R09: ffff8800034708c0
> R10: ffffffff81c34a60 R11: 0000000000000000 R12: 0000000000000000
> R13: ffff8800144c3bf0 R14: ffffffff81cd9580 R15: ffff8801052e4200
> FS: 00007f74949af740(0000) GS:ffff880148400000(0000) knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 00000000051f7000 CR3: 00000000a8f72000 CR4: 00000000001407e0
> DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
> DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
> Process trinity-child4 (pid: 21651, threadinfo ffff8800144c2000, task ffff880003470000)
> Stack:
> ffffffff81628730 ffffffff810dafbf ffff880003470000 0000000000000000
> ffff8800144c3aa8 0000000000000282 ffff8800144c3aa8 ffff88013d79dd40
> ffff8801052e4200 0000000000000003 0000000000000001 0000000000000000
> Call Trace:
> [<ffffffff81628730>] ? ip6_dst_lookup_tail+0xe0/0x380
> [<ffffffff810dafbf>] ? lock_release_holdtime.part.26+0xf/0x180
> [<ffffffff81556b20>] ? sock_def_wakeup+0x1b0/0x1b0
> [<ffffffff81628a9b>] ip6_sk_dst_lookup_flow+0xcb/0x1b0
> [<ffffffff81649d8d>] udpv6_sendmsg+0x6ad/0xc40
> [<ffffffff81023af9>] ? native_sched_clock+0x19/0x80
> [<ffffffff810db438>] ? trace_hardirqs_off_caller+0x28/0xd0
> [<ffffffff810db4ed>] ? trace_hardirqs_off+0xd/0x10
> [<ffffffff815e613a>] inet_sendmsg+0x12a/0x240
> [<ffffffff815e6010>] ? inet_create+0x6f0/0x6f0
> [<ffffffff815562a1>] ? sock_update_classid+0xb1/0x390
> [<ffffffff81556340>] ? sock_update_classid+0x150/0x390
> [<ffffffff8155115c>] sock_sendmsg+0xbc/0xf0
> [<ffffffff810b46c9>] ? local_clock+0x99/0xc0
> [<ffffffff810dff4f>] ? lock_release_non_nested+0x2df/0x320
> [<ffffffff810dafbf>] ? lock_release_holdtime.part.26+0xf/0x180
> [<ffffffff81553740>] sys_sendto+0x130/0x180
> [<ffffffff816af8ad>] system_call_fastpath+0x1a/0x1f
> Code: c0 74 19 80 3d 0e 4d 6d 00 00 75 10 e8 73 cb af ff 85 c0 0f 85 e3 01 00 00 0f 1f 00 48 8b 03 48 8b 80 98 00 00 00 48 85 c0 74 09 <f6> 80 6d 01 00 00 de 74 48 e8 3b db a6 ff 85 c0 74 0d 80 3d d5
>
> The disassembly points here..
>
> 983 rcu_read_lock();
> 984 rt = (struct rt6_info *) *dst;
> 985 n = rt->n;
> 986 if (n && !(n->nud_state & NUD_VALID)) {
> db2: 48 85 c0 test %rax,%rax
> db5: 74 09 je dc0 <ip6_dst_lookup_tail+0x150>
> db7: f6 80 6d 01 00 00 de testb $0xde,0x16d(%rax)
> dbe: 74 48 je e08 <ip6_dst_lookup_tail+0x198>
>
> 'rt->n' is 0x8000000000000011, which looks like one of the garbage values trinity generates
I hit this a few more times last night. I'm starting to doubt my theory of where
that value came from, as every instance is always 0x8000000000000011, which seems
a little too lucky. Anyone have any idea what that number resembles ?
Working on some code to make this (and other bugs) more reproducable today..
Dave
^ permalink raw reply
* [PATCHv4] rtlwifi: rtl8192ce: rtl8192cu: use %*phC to dump small buffers
From: Andy Shevchenko @ 2012-10-02 14:19 UTC (permalink / raw)
To: Larry Finger, Chaoming Li, David S . Miller,
linux-wireless-u79uwXL29TY76Z2rM5mHXA,
netdev-u79uwXL29TY76Z2rM5mHXA, Joe Perches
Cc: Andy Shevchenko
The patch changes a bit trace output format in the rtl_cam_program_entry() to
print prefix and the actual data on the same line. Moreover the %*phC outputs
each byte as 2 hex digits, which is slightly different to the original %x.
Signed-off-by: Andy Shevchenko <andriy.shevchenko-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
ACKed-by: Larry Finger <Larry.Finger-tQ5ms3gMjBLk1uMJSBkQmQ@public.gmane.org>
---
drivers/net/wireless/rtlwifi/cam.c | 7 ++-----
drivers/net/wireless/rtlwifi/rtl8192ce/hw.c | 6 ++----
drivers/net/wireless/rtlwifi/rtl8192cu/hw.c | 6 ++----
3 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/drivers/net/wireless/rtlwifi/cam.c b/drivers/net/wireless/rtlwifi/cam.c
index 5b4b4d4..ca69e35 100644
--- a/drivers/net/wireless/rtlwifi/cam.c
+++ b/drivers/net/wireless/rtlwifi/cam.c
@@ -52,11 +52,8 @@ static void rtl_cam_program_entry(struct ieee80211_hw *hw, u32 entry_no,
u32 target_content = 0;
u8 entry_i;
- RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD,
- "key_cont_128:\n %x:%x:%x:%x:%x:%x\n",
- key_cont_128[0], key_cont_128[1],
- key_cont_128[2], key_cont_128[3],
- key_cont_128[4], key_cont_128[5]);
+ RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "key_cont_128: %6phC\n",
+ key_cont_128);
for (entry_i = 0; entry_i < CAM_CONTENT_COUNT; entry_i++) {
target_command = entry_i + CAM_CONTENT_COUNT * entry_no;
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index 86d73b3..038c02c 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -1918,10 +1918,8 @@ static void rtl92ce_update_hal_rate_mask(struct ieee80211_hw *hw,
(ratr_index << 28);
rate_mask[4] = macid | (shortgi ? 0x20 : 0x00) | 0x80;
RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
- "Rate_index:%x, ratr_val:%x, %x:%x:%x:%x:%x\n",
- ratr_index, ratr_bitmap,
- rate_mask[0], rate_mask[1], rate_mask[2], rate_mask[3],
- rate_mask[4]);
+ "Rate_index:%x, ratr_val:%x, %5phC\n",
+ ratr_index, ratr_bitmap, rate_mask);
rtl92c_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask);
if (macid != 0)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
index 4bbb711..7d36a94 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c
@@ -2169,10 +2169,8 @@ void rtl92cu_update_hal_rate_mask(struct ieee80211_hw *hw, u8 rssi_level)
ratr_index << 28);
rate_mask[4] = macid | (shortgi ? 0x20 : 0x00) | 0x80;
RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG,
- "Rate_index:%x, ratr_val:%x, %x:%x:%x:%x:%x\n",
- ratr_index, ratr_bitmap,
- rate_mask[0], rate_mask[1], rate_mask[2], rate_mask[3],
- rate_mask[4]);
+ "Rate_index:%x, ratr_val:%x, %5phC\n",
+ ratr_index, ratr_bitmap, rate_mask);
rtl92c_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask);
}
--
1.7.10.4
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* RE: [PATCH net, 3/3] hyperv: Fix page buffer handling in rndis_filter_send_request()
From: Haiyang Zhang @ 2012-10-02 14:28 UTC (permalink / raw)
To: Dan Carpenter
Cc: olaf@aepfle.de, netdev@vger.kernel.org, jasowang@redhat.com,
linux-kernel@vger.kernel.org, devel@linuxdriverproject.org,
davem@davemloft.net
In-Reply-To: <20121002083847.GR4587@mwanda>
> -----Original Message-----
> From: Dan Carpenter [mailto:dan.carpenter@oracle.com]
> Sent: Tuesday, October 02, 2012 4:39 AM
> To: Haiyang Zhang
> Cc: davem@davemloft.net; netdev@vger.kernel.org; olaf@aepfle.de;
> jasowang@redhat.com; linux-kernel@vger.kernel.org;
> devel@linuxdriverproject.org
> Subject: Re: [PATCH net, 3/3] hyperv: Fix page buffer handling in
> rndis_filter_send_request()
>
> On Mon, Oct 01, 2012 at 03:30:57PM -0700, Haiyang Zhang wrote:
> > Add another page buffer if the request message crossed page boundary.
> >
>
> What are the user visible effects of this bug fix? Please put that
> in the commit message.
Will do.
Thanks,
- Haiyang
^ permalink raw reply
* Re: tg3 driver upgrade (Linux 2.6.32 -> 3.2) breaks IBM Bladecenter SoL
From: Michael Chan @ 2012-10-02 15:03 UTC (permalink / raw)
To: Ferenc Wagner
Cc: netdev, Matt Carlson, Grant Likely, Rob Herring, linux-kernel
In-Reply-To: <87a9w5dqle.fsf@lant.ki.iif.hu>
On Tue, 2012-10-02 at 14:07 +0200, Ferenc Wagner wrote:
> I'm done with bisecting it: the first bad commit is:
>
> commit dabc5c670d3f86d15ee4f42ab38ec5bd2682487d
> Author: Matt Carlson <mcarlson@broadcom.com>
> Date: Thu May 19 12:12:52 2011 +0000
>
> tg3: Move TSO_CAPABLE assignment
>
> This patch moves the code that asserts the TSO_CAPABLE flag closer
> to
> where the TSO capabilities flags are set. There isn't a good
> enough
> reason for the code to be separated.
>
> Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
> Reviewed-by: Michael Chan <mchan@broadcom.com>
> Signed-off-by: David S. Miller <davem@davemloft.net>
Thanks, I'll look into this.
>
> On the other hand, losing the SoL console even temporarily during boot
> (as it happens with a minimal kernel before this commit) isn't nice
> either. I'll try to look after that, too, just mentioning it here...
This is expected as the driver has to reset the link and you'll lose SoL
for a few seconds until link comes back up. We can look into an
enhancement to not touch the link if it is already in a good state when
the driver comes up.
^ permalink raw reply
* [PATCH net-next,4/6] hyperv: Remove extra allocated space for recv_pkt_list elements
From: Haiyang Zhang @ 2012-10-02 15:30 UTC (permalink / raw)
To: davem, netdev; +Cc: haiyangz, kys, olaf, jasowang, linux-kernel, devel
In-Reply-To: <1349191824-14001-1-git-send-email-haiyangz@microsoft.com>
The receive code path doesn't use the page buffer, so remove the
extra allocated space here.
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Reviewed-by: K. Y. Srinivasan <kys@microsoft.com>
---
drivers/net/hyperv/hyperv_net.h | 2 --
drivers/net/hyperv/netvsc.c | 4 +---
2 files changed, 1 insertions(+), 5 deletions(-)
diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h
index 95ceb35..d58f28c 100644
--- a/drivers/net/hyperv/hyperv_net.h
+++ b/drivers/net/hyperv/hyperv_net.h
@@ -465,8 +465,6 @@ struct nvsp_message {
#define NETVSC_RECEIVE_BUFFER_ID 0xcafe
-#define NETVSC_RECEIVE_SG_COUNT 1
-
/* Preallocated receive packets */
#define NETVSC_RECEIVE_PACKETLIST_COUNT 256
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 4a1a5f5..d9c4c03 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -904,9 +904,7 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)
INIT_LIST_HEAD(&net_device->recv_pkt_list);
for (i = 0; i < NETVSC_RECEIVE_PACKETLIST_COUNT; i++) {
- packet = kzalloc(sizeof(struct hv_netvsc_packet) +
- (NETVSC_RECEIVE_SG_COUNT *
- sizeof(struct hv_page_buffer)), GFP_KERNEL);
+ packet = kzalloc(sizeof(struct hv_netvsc_packet), GFP_KERNEL);
if (!packet)
break;
--
1.7.4.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox