* Re: [PATCH] sctp: Make "Invalid Stream Identifier" ERROR follows SACK when bundling
From: Xufeng Zhang @ 2012-07-30 4:58 UTC (permalink / raw)
To: Vlad Yasevich
Cc: Neil Horman, xufeng zhang, sri, davem, linux-sctp, netdev,
linux-kernel
In-Reply-To: <5013069F.3080306@gmail.com>
On 7/28/12, Vlad Yasevich <vyasevich@gmail.com> wrote:
> here is an untested prototype of what I was talking about. This should
> handle multiple data chunks.
Yes, it works if only the end of the DATA chunk in a packet has
invalid stream identifier
and I have verified this patch by my test case, but what happens if
there are multiple
DATA chunks which have invalid stream identifier in a packet?
Consider the below example:
A packet has several chunks bundling together: "COOKIE_ECHO DATA DATA", both
of the two DATA chunks have invalid stream identifier, then the
response will be
"COOKIE_ACK ERROR SACK ERROR", right?
Thanks,
Xufeng Zhang
>
> -vlad
>
> ---
> include/net/sctp/command.h | 1 +
> net/sctp/sm_sideeffect.c | 22 ++++++++++++++++++++++
> net/sctp/sm_statefuns.c | 18 ++++++++++--------
> 3 files changed, 33 insertions(+), 8 deletions(-)
>
> diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h
> index 712b3be..4043445 100644
> --- a/include/net/sctp/command.h
> +++ b/include/net/sctp/command.h
> @@ -110,6 +110,7 @@ typedef enum {
> SCTP_CMD_SEND_NEXT_ASCONF, /* Send the next ASCONF after ACK */
> SCTP_CMD_PURGE_ASCONF_QUEUE, /* Purge all asconf queues.*/
> SCTP_CMD_SET_ASOC, /* Restore association context */
> + SCTP_CMD_GEN_BAD_STREAM, /* Issue an Invalid Stream error */
> SCTP_CMD_LAST
> } sctp_verb_t;
>
> diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
> index 1ff51c9..c5a1322 100644
> --- a/net/sctp/sm_sideeffect.c
> +++ b/net/sctp/sm_sideeffect.c
> @@ -1036,6 +1036,22 @@ static void sctp_cmd_send_asconf(struct
> sctp_association *asoc)
> }
> }
>
> +static void sctp_cmd_make_inv_stream_err(sctp_cmd_seq_t *commands,
> + struct sctp_association *asoc,
> + struct sctp_chunk *chunk,
> + struct sctp_datahdr *data_hdr)
> +{
> + struct sctp_chunk *err;
> +
> + err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM,
> + &data_hdr->stream,
> + sizeof(data_hdr->stream),
> + sizeof(u16));
> + if (err)
> + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
> + SCTP_CHUNK(err));
> +}
> +
>
> /* These three macros allow us to pull the debugging code out of the
> * main flow of sctp_do_sm() to keep attention focused on the real
> @@ -1700,6 +1716,12 @@ static int sctp_cmd_interpreter(sctp_event_t
> event_type,
> asoc = cmd->obj.asoc;
> break;
>
> + case SCTP_CMD_GEN_BAD_STREAM:
> + sctp_cmd_make_inv_stream_err(commands,
> + asoc, chunk,
> + (struct sctp_datahdr *)cmd->obj.ptr);
> + break;
> +
> default:
> pr_warn("Impossible command: %u, %p\n",
> cmd->verb, cmd->obj.ptr);
> diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
> index 891f5db..57532e3 100644
> --- a/net/sctp/sm_statefuns.c
> +++ b/net/sctp/sm_statefuns.c
> @@ -2972,6 +2972,12 @@ discard_noforce:
> if (chunk->end_of_packet)
> sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, force);
>
> + /* Queue the INVALID STREAM error after the SACK if one is needed. */
> + if (SCTP_IERROR_BAD_STREAM == error) {
> + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_BAD_STREAM,
> + SCTP_PTR(chunk->subh.data_hdr));
> + }
> +
> return SCTP_DISPOSITION_DISCARD;
> consume:
> return SCTP_DISPOSITION_CONSUME;
> @@ -3044,6 +3050,10 @@ sctp_disposition_t
> sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
> */
> sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SHUTDOWN, SCTP_NULL());
> sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE());
> + if (SCTP_IERROR_BAD_STREAM == error) {
> + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_BAD_STREAM,
> + SCTP_PTR(chunk->subh.data_hdr));
> + }
> sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
> SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
> }
> @@ -6140,14 +6150,6 @@ static int sctp_eat_data(const struct
> sctp_association *asoc,
> if (sid >= asoc->c.sinit_max_instreams) {
> /* Mark tsn as received even though we drop it */
> sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
> -
> - err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM,
> - &data_hdr->stream,
> - sizeof(data_hdr->stream),
> - sizeof(u16));
> - if (err)
> - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
> - SCTP_CHUNK(err));
> return SCTP_IERROR_BAD_STREAM;
> }
>
> -- 1.7.7.6
>
>
>
^ permalink raw reply
* [PATCH net 1/5] tg3: Request APE_LOCK_PHY before PHY access
From: Michael Chan @ 2012-07-30 5:15 UTC (permalink / raw)
To: davem; +Cc: netdev
to prevent PHY access conflict with APE firmware.
Signed-off-by: Michael Chan <mchan@broadcom.com>
---
drivers/net/ethernet/broadcom/tg3.c | 37 +++++++++++++++++++++++++++++++++++
drivers/net/ethernet/broadcom/tg3.h | 1 +
2 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 9a009fd..a528f9a 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -672,6 +672,12 @@ static int tg3_ape_lock(struct tg3 *tp, int locknum)
else
bit = 1 << tp->pci_fn;
break;
+ case TG3_APE_LOCK_PHY0:
+ case TG3_APE_LOCK_PHY1:
+ case TG3_APE_LOCK_PHY2:
+ case TG3_APE_LOCK_PHY3:
+ bit = APE_LOCK_REQ_DRIVER;
+ break;
default:
return -EINVAL;
}
@@ -723,6 +729,12 @@ static void tg3_ape_unlock(struct tg3 *tp, int locknum)
else
bit = 1 << tp->pci_fn;
break;
+ case TG3_APE_LOCK_PHY0:
+ case TG3_APE_LOCK_PHY1:
+ case TG3_APE_LOCK_PHY2:
+ case TG3_APE_LOCK_PHY3:
+ bit = APE_LOCK_GRANT_DRIVER;
+ break;
default:
return;
}
@@ -1052,6 +1064,8 @@ static int tg3_readphy(struct tg3 *tp, int reg, u32 *val)
udelay(80);
}
+ tg3_ape_lock(tp, tp->phy_ape_lock);
+
*val = 0x0;
frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) &
@@ -1086,6 +1100,8 @@ static int tg3_readphy(struct tg3 *tp, int reg, u32 *val)
udelay(80);
}
+ tg3_ape_unlock(tp, tp->phy_ape_lock);
+
return ret;
}
@@ -1105,6 +1121,8 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
udelay(80);
}
+ tg3_ape_lock(tp, tp->phy_ape_lock);
+
frame_val = ((tp->phy_addr << MI_COM_PHY_ADDR_SHIFT) &
MI_COM_PHY_ADDR_MASK);
frame_val |= ((reg << MI_COM_REG_ADDR_SHIFT) &
@@ -1135,6 +1153,8 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
udelay(80);
}
+ tg3_ape_unlock(tp, tp->phy_ape_lock);
+
return ret;
}
@@ -13648,6 +13668,23 @@ static int __devinit tg3_phy_probe(struct tg3 *tp)
tg3_flag_set(tp, PAUSE_AUTONEG);
tp->link_config.flowctrl = FLOW_CTRL_TX | FLOW_CTRL_RX;
+ if (tg3_flag(tp, ENABLE_APE)) {
+ switch (tp->pci_fn) {
+ case 0:
+ tp->phy_ape_lock = TG3_APE_LOCK_PHY0;
+ break;
+ case 1:
+ tp->phy_ape_lock = TG3_APE_LOCK_PHY1;
+ break;
+ case 2:
+ tp->phy_ape_lock = TG3_APE_LOCK_PHY2;
+ break;
+ case 3:
+ tp->phy_ape_lock = TG3_APE_LOCK_PHY3;
+ break;
+ }
+ }
+
if (tg3_flag(tp, USE_PHYLIB))
return tg3_phy_init(tp);
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index a1b75cd..6fb45a5 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -3107,6 +3107,7 @@ struct tg3 {
int old_link;
u8 phy_addr;
+ u8 phy_ape_lock;
/* PHY info */
u32 phy_id;
--
1.7.1
^ permalink raw reply related
* [PATCH net 2/5] tg3: Fix Read DMA workaround for 5719 A0.
From: Michael Chan @ 2012-07-30 5:15 UTC (permalink / raw)
To: davem; +Cc: netdev
In-Reply-To: <1343625345-5155-1-git-send-email-mchan@broadcom.com>
The workaround was mis-applied to all 5719 and 5720 chips.
Signed-off-by: Michael Chan <mchan@broadcom.com>
---
drivers/net/ethernet/broadcom/tg3.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index a528f9a..f0434df 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -9086,8 +9086,7 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_57780 ||
tg3_flag(tp, 57765_PLUS)) {
val = tr32(TG3_RDMA_RSRVCTRL_REG);
- if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5720) {
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5719_A0) {
val &= ~(TG3_RDMA_RSRVCTRL_TXMRGN_MASK |
TG3_RDMA_RSRVCTRL_FIFO_LWM_MASK |
TG3_RDMA_RSRVCTRL_FIFO_HWM_MASK);
--
1.7.1
^ permalink raw reply related
* [PATCH net 3/5] tg3: Add New 5719 Read DMA workaround
From: Michael Chan @ 2012-07-30 5:15 UTC (permalink / raw)
To: davem; +Cc: netdev
In-Reply-To: <1343625345-5155-2-git-send-email-mchan@broadcom.com>
After Power-on-reset, the 5719's TX DMA length registers may contain
uninitialized values and cause TX DMA to stall. Check for invalid
values and set a register bit to flush the TX channels. The bit
needs to be turned off after the DMA channels have been flushed.
Signed-off-by: Michael Chan <mchan@broadcom.com>
---
drivers/net/ethernet/broadcom/tg3.c | 23 +++++++++++++++++++++++
drivers/net/ethernet/broadcom/tg3.h | 7 ++++++-
2 files changed, 29 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index f0434df..50045ed 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -9276,6 +9276,19 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
tw32_f(RDMAC_MODE, rdmac_mode);
udelay(40);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719) {
+ for (i = 0; i < TG3_NUM_RDMA_CHANNELS; i++) {
+ if (tr32(TG3_RDMA_LENGTH + (i << 2)) > TG3_MAX_MTU(tp))
+ break;
+ }
+ if (i < TG3_NUM_RDMA_CHANNELS) {
+ val = tr32(TG3_LSO_RD_DMA_CRPTEN_CTRL);
+ val |= TG3_LSO_RD_DMA_TX_LENGTH_WA;
+ tw32(TG3_LSO_RD_DMA_CRPTEN_CTRL, val);
+ tg3_flag_set(tp, 5719_RDMA_BUG);
+ }
+ }
+
tw32(RCVDCC_MODE, RCVDCC_MODE_ENABLE | RCVDCC_MODE_ATTN_ENABLE);
if (!tg3_flag(tp, 5705_PLUS))
tw32(MBFREE_MODE, MBFREE_MODE_ENABLE);
@@ -9635,6 +9648,16 @@ static void tg3_periodic_fetch_stats(struct tg3 *tp)
TG3_STAT_ADD32(&sp->tx_ucast_packets, MAC_TX_STATS_UCAST);
TG3_STAT_ADD32(&sp->tx_mcast_packets, MAC_TX_STATS_MCAST);
TG3_STAT_ADD32(&sp->tx_bcast_packets, MAC_TX_STATS_BCAST);
+ if (unlikely(tg3_flag(tp, 5719_RDMA_BUG) &&
+ (sp->tx_ucast_packets.low + sp->tx_mcast_packets.low +
+ sp->tx_bcast_packets.low) > TG3_NUM_RDMA_CHANNELS)) {
+ u32 val;
+
+ val = tr32(TG3_LSO_RD_DMA_CRPTEN_CTRL);
+ val &= ~TG3_LSO_RD_DMA_TX_LENGTH_WA;
+ tw32(TG3_LSO_RD_DMA_CRPTEN_CTRL, val);
+ tg3_flag_clear(tp, 5719_RDMA_BUG);
+ }
TG3_STAT_ADD32(&sp->rx_octets, MAC_RX_STATS_OCTETS);
TG3_STAT_ADD32(&sp->rx_fragments, MAC_RX_STATS_FRAGMENTS);
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 6fb45a5..6d52cb2 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -1376,7 +1376,11 @@
#define TG3_LSO_RD_DMA_CRPTEN_CTRL 0x00004910
#define TG3_LSO_RD_DMA_CRPTEN_CTRL_BLEN_BD_4K 0x00030000
#define TG3_LSO_RD_DMA_CRPTEN_CTRL_BLEN_LSO_4K 0x000c0000
-/* 0x4914 --> 0x4c00 unused */
+#define TG3_LSO_RD_DMA_TX_LENGTH_WA 0x02000000
+/* 0x4914 --> 0x4be0 unused */
+
+#define TG3_NUM_RDMA_CHANNELS 4
+#define TG3_RDMA_LENGTH 0x00004be0
/* Write DMA control registers */
#define WDMAC_MODE 0x00004c00
@@ -2959,6 +2963,7 @@ enum TG3_FLAGS {
TG3_FLAG_L1PLLPD_EN,
TG3_FLAG_APE_HAS_NCSI,
TG3_FLAG_4K_FIFO_LIMIT,
+ TG3_FLAG_5719_RDMA_BUG,
TG3_FLAG_RESET_TASK_PENDING,
TG3_FLAG_5705_PLUS,
TG3_FLAG_IS_5788,
--
1.7.1
^ permalink raw reply related
* [PATCH net 4/5] tg3: Fix race condition in tg3_get_stats64()
From: Michael Chan @ 2012-07-30 5:15 UTC (permalink / raw)
To: davem; +Cc: netdev
In-Reply-To: <1343625345-5155-3-git-send-email-mchan@broadcom.com>
Spinlock should be taken before checking for tp->hw_stats.
Signed-off-by: Michael Chan <mchan@broadcom.com>
---
drivers/net/ethernet/broadcom/tg3.c | 6 ++++--
1 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index 50045ed..f03614b 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -12524,10 +12524,12 @@ static struct rtnl_link_stats64 *tg3_get_stats64(struct net_device *dev,
{
struct tg3 *tp = netdev_priv(dev);
- if (!tp->hw_stats)
+ spin_lock_bh(&tp->lock);
+ if (!tp->hw_stats) {
+ spin_unlock_bh(&tp->lock);
return &tp->net_stats_prev;
+ }
- spin_lock_bh(&tp->lock);
tg3_get_nstats(tp, stats);
spin_unlock_bh(&tp->lock);
--
1.7.1
^ permalink raw reply related
* [PATCH net 5/5] tg3: Update version to 3.124
From: Michael Chan @ 2012-07-30 5:15 UTC (permalink / raw)
To: davem; +Cc: netdev
In-Reply-To: <1343625345-5155-4-git-send-email-mchan@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>
---
drivers/net/ethernet/broadcom/tg3.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index f03614b..bf906c5 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -92,7 +92,7 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
#define DRV_MODULE_NAME "tg3"
#define TG3_MAJ_NUM 3
-#define TG3_MIN_NUM 123
+#define TG3_MIN_NUM 124
#define DRV_MODULE_VERSION \
__stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
#define DRV_MODULE_RELDATE "March 21, 2012"
--
1.7.1
^ permalink raw reply related
* Re: [net-next RFC V5 4/5] virtio_net: multiqueue support
From: Jason Wang @ 2012-07-30 5:15 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: krkumar2, habanero, mashirle, kvm, netdev, linux-kernel,
virtualization, edumazet, tahm, jwhan, davem, sri
In-Reply-To: <20120729095021.GC8977@redhat.com>
On 07/29/2012 05:50 PM, Michael S. Tsirkin wrote:
> On Mon, Jul 23, 2012 at 01:48:35PM +0800, Jason Wang wrote:
>>>> + }
>>>>
>>>> - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)) {
>>>> + ret = vi->vdev->config->find_vqs(vi->vdev, total_vqs, vqs, callbacks,
>>>> + (const char **)names);
>>>> + if (ret)
>>>> + goto err;
>>>> +
>>>> + if (vi->has_cvq)
>>>> vi->cvq = vqs[2];
>>>>
>>>> - if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN))
>>>> - vi->dev->features |= NETIF_F_HW_VLAN_FILTER;
>>>> + for (i = 0; i< vi->num_queue_pairs * 2; i += 2) {
>>>> + int j = i == 0 ? i : i + vi->has_cvq;
>>>> + vi->rq[i / 2]->vq = vqs[j];
>>>> + vi->sq[i / 2]->vq = vqs[j + 1];
>>> Same here.
>> Consider the code is really simple, seem no need to use helpers.
> Well it was not simple to at least one reader :)
> The problem is not this logic is complex,
> it is that it is spread all over the code.
>
> If we had e.g. vnet_tx_vqn_to_queuenum vnet_tx_queuenum_to_vqn
> and same for rx, then the logic would all be
> in one place, and have a tidy comment on top explaining
> the VQ numbering scheme.
>
Looks reasonable, thanks.
^ permalink raw reply
* [PATCH] can: kvaser_usb: Add support for Kvaser CAN/USB devices
From: Olivier Sobrie @ 2012-07-30 5:32 UTC (permalink / raw)
To: Wolfgang Grandegger, Marc Kleine-Budde, linux-can; +Cc: netdev, Olivier Sobrie
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 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@sobrie.be>
---
drivers/net/can/usb/Kconfig | 33 ++
drivers/net/can/usb/Makefile | 1 +
drivers/net/can/usb/kvaser_usb.c | 1062 ++++++++++++++++++++++++++++++++++++++
drivers/net/can/usb/kvaser_usb.h | 237 +++++++++
4 files changed, 1333 insertions(+)
create mode 100644 drivers/net/can/usb/kvaser_usb.c
create mode 100644 drivers/net/can/usb/kvaser_usb.h
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..4965480
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.c
@@ -0,0 +1,1062 @@
+/*
+ * Parts of this driver are based on the following:
+ * - Kvaser linux leaf driver (version 4.78)
+ * - CAN driver for esd CAN-USB/2
+ */
+
+#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>
+
+#include "kvaser_usb.h"
+
+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_anchor rx_submitted;
+
+ u32 fw_version;
+ unsigned int nchannels;
+
+ bool rxinitdone;
+};
+
+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];
+
+ int open_time;
+ struct completion start_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_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID),
+ .driver_info = KVASER_HAS_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID) },
+ { 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_SILENT_MODE },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID) },
+ { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, kvaser_usb_table);
+
+static 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, 1),
+ 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, 129),
+ buf, RX_BUFFER_SIZE, &actual_len,
+ USB_RECEIVE_TIMEOUT);
+ if (err < 0) {
+ kfree(buf);
+ return err;
+ }
+
+ while (pos < actual_len) {
+ tmp = buf + pos;
+
+ if (!tmp->len)
+ break;
+
+ if (tmp->id == id) {
+ memcpy(msg, tmp, sizeof(*msg));
+ kfree(buf);
+ return 0;
+ }
+
+ pos += tmp->len;
+ }
+
+ kfree(buf);
+
+ return -EINVAL;
+}
+
+static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev,
+ u8 msg_id, int channel)
+{
+ struct kvaser_msg msg;
+ int err;
+
+ msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple);
+ msg.id = msg_id;
+ msg.u.simple.channel = channel;
+ msg.u.simple.tid = 0xff;
+
+ err = kvaser_usb_send_msg(dev, &msg);
+ if (err)
+ return err;
+
+ return 0;
+}
+
+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;
+ 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];
+
+ /*
+ * It looks like the firmware never sets the flags field of the
+ * tx_acknowledge frame and never reports a transmit failure.
+ * If the can message can't be transmited (e.g. incompatible
+ * bitrates), a frame CMD_CAN_ERROR_EVENT is sent (with a null
+ * tid) and the firmware tries to transmit again the packet until
+ * it succeeds. Once the packet is successfully transmitted, then
+ * the tx_acknowledge frame is sent.
+ */
+
+ 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_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;
+ u8 channel, status, txerr, rxerr;
+
+ if (msg->id == 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;
+ } else {
+ 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;
+ }
+
+ 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_err_skb(priv->netdev, &cf);
+ if (!skb) {
+ stats->rx_dropped++;
+ return;
+ }
+
+ if ((status & M16C_STATE_BUS_OFF) ||
+ (status & M16C_STATE_BUS_RESET)) {
+ priv->can.state = CAN_STATE_BUS_OFF;
+ cf->can_id |= CAN_ERR_BUSOFF;
+ can_bus_off(priv->netdev);
+ } else if (status & M16C_STATE_BUS_ERROR) {
+ priv->can.state = CAN_STATE_ERROR_WARNING;
+ priv->can.can_stats.error_warning++;
+ } else if (status & M16C_STATE_BUS_PASSIVE) {
+ priv->can.state = CAN_STATE_ERROR_PASSIVE;
+ priv->can.can_stats.error_passive++;
+ } else {
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ cf->can_id |= CAN_ERR_PROT;
+ cf->data[2] = CAN_ERR_PROT_ACTIVE;
+ }
+
+ if (msg->id == CMD_CAN_ERROR_EVENT) {
+ u8 error_factor = msg->u.error_event.error_factor;
+
+ priv->can.can_stats.bus_error++;
+ stats->rx_errors++;
+
+ cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+ if ((priv->can.state == CAN_STATE_ERROR_WARNING) ||
+ (priv->can.state == CAN_STATE_ERROR_PASSIVE)) {
+ cf->data[1] = (txerr > rxerr) ?
+ CAN_ERR_CRTL_TX_PASSIVE
+ : CAN_ERR_CRTL_RX_PASSIVE;
+ }
+
+ if (error_factor & M16C_EF_ACKE)
+ cf->data[3] |= (CAN_ERR_PROT_LOC_ACK |
+ CAN_ERR_PROT_LOC_ACK_DEL);
+ 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;
+
+ netif_rx(skb);
+
+ priv->bec.txerr = txerr;
+ priv->bec.rxerr = rxerr;
+
+ 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 == NULL) {
+ 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;
+ } 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++;
+ } else if (!msg->u.rx_can.flag) {
+ memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc);
+ } else {
+ kfree_skb(skb);
+ return;
+ }
+
+ netif_rx(skb);
+
+ stats->rx_packets++;
+ stats->rx_bytes += cf->can_dlc;
+}
+
+static void kvaser_usb_start_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->start_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:
+ case CMD_STOP_CHIP_REPLY:
+ kvaser_usb_start_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_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 = urb->transfer_buffer + pos;
+
+ if (!msg->len)
+ break;
+
+ kvaser_usb_handle_message(dev, msg);
+
+ if (pos > urb->actual_length) {
+ dev_err(dev->udev->dev.parent, "Format error\n");
+ break;
+ }
+
+ pos += msg->len;
+ }
+
+resubmit_urb:
+ usb_fill_bulk_urb(urb, dev->udev, usb_rcvbulkpipe(dev->udev, 129),
+ 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;
+
+ 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, &urb->transfer_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, 129),
+ buf, RX_BUFFER_SIZE,
+ kvaser_usb_read_bulk_callback, dev);
+ 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,
+ urb->transfer_dma);
+ break;
+ }
+
+ 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;
+
+ memset(&msg, 0x00, sizeof(msg));
+ msg.id = CMD_SET_CTRL_MODE;
+ msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode);
+ msg.u.ctrl_mode.tid = 0xff;
+ msg.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_stop_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_stop_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)
+ return err;
+
+ err = kvaser_usb_set_opt_mode(priv);
+ if (err)
+ return err;
+
+ err = kvaser_usb_start_chip(priv);
+ if (err) {
+ netdev_warn(netdev, "Cannot start device, error %d\n", err);
+ close_candev(netdev);
+ return err;
+ }
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+ priv->open_time = jiffies;
+ netif_start_queue(netdev);
+
+ 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_unlink_all_urbs(struct kvaser_usb *dev)
+{
+ int i;
+
+ usb_kill_anchored_urbs(&dev->rx_submitted);
+
+ 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->start_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->start_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;
+
+ memset(&msg, 0x00, sizeof(msg));
+ msg.id = CMD_FLUSH_QUEUE;
+ msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue);
+ msg.u.flush_queue.channel = priv->channel;
+
+ 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);
+ int err;
+
+ netif_stop_queue(netdev);
+
+ err = kvaser_usb_flush_queue(priv);
+ if (err)
+ netdev_warn(netdev, "Cannot flush queue, error %d\n", err);
+
+ err = kvaser_usb_stop_chip(priv);
+ if (err) {
+ netdev_warn(netdev, "Cannot stop device, error %d\n", err);
+ return err;
+ }
+
+ kvaser_usb_unlink_tx_urbs(priv);
+
+ priv->can.state = CAN_STATE_STOPPED;
+ close_candev(priv->netdev);
+ priv->open_time = 0;
+
+ 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;
+
+ usb_free_coherent(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+
+ if (!netif_device_present(netdev))
+ return;
+
+ if (urb->status)
+ netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status);
+
+ netdev->trans_start = jiffies;
+}
+
+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);
+ goto nourbmem;
+ }
+
+ buf = usb_alloc_coherent(dev->udev, sizeof(struct kvaser_msg),
+ GFP_ATOMIC, &urb->transfer_dma);
+ if (!buf) {
+ netdev_err(netdev, "No memory left for USB buffer\n");
+ stats->tx_dropped++;
+ dev_kfree_skb(skb);
+ goto nobufmem;
+ }
+
+ msg = (struct kvaser_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 < MAX_TX_URBS; 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, 1),
+ buf, msg->len,
+ kvaser_usb_write_bulk_callback, context);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ 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;
+
+ usb_free_urb(urb);
+
+ return NETDEV_TX_OK;
+
+releasebuf:
+ usb_free_coherent(dev->udev, sizeof(struct kvaser_msg),
+ buf, urb->transfer_dma);
+nobufmem:
+ usb_free_urb(urb);
+nourbmem:
+ 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;
+
+ msg.id = CMD_SET_BUS_PARAMS;
+ msg.len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams);
+ msg.u.busparams.channel = priv->channel;
+ msg.u.busparams.tid = 0xff;
+ msg.u.busparams.bitrate = bt->bitrate;
+ msg.u.busparams.sjw = bt->sjw;
+ msg.u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1;
+ msg.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);
+
+ if (!priv->open_time)
+ return -EINVAL;
+
+ switch (mode) {
+ case CAN_MODE_START:
+ if (netif_queue_stopped(netdev))
+ netif_wake_queue(netdev);
+ 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->txerr = priv->bec.txerr;
+ bec->rxerr = priv->bec.rxerr;
+
+ 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_usb_anchor(&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;
+
+ 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;
+ 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);
+
+ err = register_candev(netdev);
+ if (err) {
+ dev_err(&intf->dev, "Failed to register can device\n");
+ free_candev(netdev);
+ return err;
+ }
+
+ dev->nets[channel] = priv;
+ netdev_info(netdev, "device %s registered\n", netdev->name);
+
+ return 0;
+}
+
+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 = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->udev = interface_to_usbdev(intf);
+
+ init_usb_anchor(&dev->rx_submitted);
+
+ usb_set_intfdata(intf, dev);
+
+ if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, 0)) {
+ dev_err(&intf->dev, "Cannot reset kvaser\n");
+ goto error;
+ }
+
+ if (kvaser_usb_get_software_info(dev)) {
+ dev_err(&intf->dev, "Cannot get software infos\n");
+ goto error;
+ }
+
+ if (kvaser_usb_get_card_info(dev)) {
+ dev_err(&intf->dev, "Cannot get card infos\n");
+ goto error;
+ }
+
+ 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);
+
+ return 0;
+
+error:
+ kfree(dev);
+ return err;
+}
+
+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);
+ free_candev(dev->nets[i]->netdev);
+ }
+
+ kvaser_usb_unlink_all_urbs(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");
diff --git a/drivers/net/can/usb/kvaser_usb.h b/drivers/net/can/usb/kvaser_usb.h
new file mode 100644
index 0000000..8e0b6ab
--- /dev/null
+++ b/drivers/net/can/usb/kvaser_usb.h
@@ -0,0 +1,237 @@
+#ifndef _KVASER_USB_H_
+#define _KVASER_USB_H_
+
+#define MAX_TX_URBS 16
+#define MAX_RX_URBS 4
+#define START_TIMEOUT 1000
+#define STOP_TIMEOUT 1000
+#define USB_SEND_TIMEOUT 1000
+#define USB_RECEIVE_TIMEOUT 1000
+#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 (1 << 0)
+
+/* Message header size */
+#define MSG_HEADER_LEN 2
+
+/* Can message flags */
+#define MSG_FLAG_ERROR_FRAME (1 << 0)
+#define MSG_FLAG_OVERRUN (1 << 1)
+#define MSG_FLAG_NERR (1 << 2)
+#define MSG_FLAG_WAKEUP (1 << 3)
+#define MSG_FLAG_REMOTE_FRAME (1 << 4)
+#define MSG_FLAG_RESERVED (1 << 5)
+#define MSG_FLAG_TX_ACK (1 << 6)
+#define MSG_FLAG_TX_REQUEST (1 << 7)
+
+/* Can states */
+#define M16C_STATE_BUS_RESET (1 << 0)
+#define M16C_STATE_BUS_ERROR (1 << 4)
+#define M16C_STATE_BUS_PASSIVE (1 << 5)
+#define M16C_STATE_BUS_OFF (1 << 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_CHIP_REPLY 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_TX_ACKNOWLEDGE 50
+#define CMD_CAN_ERROR_EVENT 51
+#define CMD_USB_THROTTLE 77
+
+/* error factors */
+#define M16C_EF_ACKE (1 << 0)
+#define M16C_EF_CRCE (1 << 1)
+#define M16C_EF_FORME (1 << 2)
+#define M16C_EF_STFE (1 << 3)
+#define M16C_EF_BITE0 (1 << 4)
+#define M16C_EF_BITE1 (1 << 5)
+#define M16C_EF_RCVE (1 << 6)
+#define M16C_EF_TRE (1 << 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 {
+ 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_ctrl_mode flush_queue;
+ } u;
+} __packed;
+
+#endif
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH] net/tun: fix ioctl() based info leaks
From: Mathias Krause @ 2012-07-30 5:38 UTC (permalink / raw)
To: richard -rw- weinberger; +Cc: David S. Miller, netdev
In-Reply-To: <CAFLxGvwUNxOn3vRLdJeS6hVZ_Oko0_QPZfLWAe+ztCHGdqBFOw@mail.gmail.com>
On Mon, Jul 30, 2012 at 1:11 AM, richard -rw- weinberger
<richard.weinberger@gmail.com> wrote:
> On Sun, Jul 29, 2012 at 10:58 PM, Mathias Krause <minipli@googlemail.com> wrote:
>> - if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
>> + if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) {
>> if (copy_from_user(&ifr, argp, ifreq_len))
>> return -EFAULT;
>> + } else {
>> + memset(&ifr, 0, sizeof(ifr));
>> + }
>>
>> if (cmd == TUNGETFEATURES) {
>> /* Currently this just means: "what IFF flags are valid?".
>
> The fix makes sense to me.
>
> Beside of the fix, why are you adding braces to if and else?
The pair of braces around the "if" is needed to attach the "else"
branch to the right "if". The braces around the "else" are just for
consistency.
> We don't use braces on single statements.
Even tun.c isn't consistently following this rule but I'll send a new
patch without the "else" braces.
Thanks,
Mathias
^ permalink raw reply
* [PATCH v2] net/tun: fix ioctl() based info leaks
From: Mathias Krause @ 2012-07-30 5:45 UTC (permalink / raw)
To: David S. Miller; +Cc: richard -rw- weinberger, netdev, Mathias Krause
In-Reply-To: <CAFLxGvwUNxOn3vRLdJeS6hVZ_Oko0_QPZfLWAe+ztCHGdqBFOw@mail.gmail.com>
The tun module leaks up to 36 bytes of memory by not fully initializing
a structure located on the stack that gets copied to user memory by the
TUNGETIFF and SIOCGIFHWADDR ioctl()s.
Signed-off-by: Mathias Krause <minipli@googlemail.com>
---
v2:
- removed braces around else branch
- minor adjustment of the commit message
drivers/net/tun.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 987aeef..01255ff 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1252,9 +1252,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
int vnet_hdr_sz;
int ret;
- if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
+ if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) {
if (copy_from_user(&ifr, argp, ifreq_len))
return -EFAULT;
+ } else
+ memset(&ifr, 0, sizeof(ifr));
if (cmd == TUNGETFEATURES) {
/* Currently this just means: "what IFF flags are valid?".
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCH] sctp: Make "Invalid Stream Identifier" ERROR follows SACK when bundling
From: Xufeng Zhang @ 2012-07-30 5:47 UTC (permalink / raw)
To: Vlad Yasevich
Cc: Neil Horman, xufeng zhang, sri, davem, linux-sctp, netdev,
linux-kernel
In-Reply-To: <CA+=dFzh4dFatOy42FydbVvoXB8=5CvLu5AwPvUA16h+kymq0Lw@mail.gmail.com>
On 7/30/12, Xufeng Zhang <xufengzhang.main@gmail.com> wrote:
> On 7/28/12, Vlad Yasevich <vyasevich@gmail.com> wrote:
>> here is an untested prototype of what I was talking about. This should
>> handle multiple data chunks.
>
> Yes, it works if only the end of the DATA chunk in a packet has
> invalid stream identifier
> and I have verified this patch by my test case, but what happens if
> there are multiple
> DATA chunks which have invalid stream identifier in a packet?
>
> Consider the below example:
> A packet has several chunks bundling together: "COOKIE_ECHO DATA DATA",
> both
> of the two DATA chunks have invalid stream identifier, then the
> response will be
> "COOKIE_ACK ERROR SACK ERROR", right?
I just wrote a test case for my above assumption and have verified
that SACK always
bundled before the end of the ERROR chunk if multiple error DATA
chunks happened.
So this patch didn't handle all the situations and this is really what
I suspected before.
Thanks,
Xufeng Zhang
>
>
>
> Thanks,
> Xufeng Zhang
>
>>
>> -vlad
>>
>> ---
>> include/net/sctp/command.h | 1 +
>> net/sctp/sm_sideeffect.c | 22 ++++++++++++++++++++++
>> net/sctp/sm_statefuns.c | 18 ++++++++++--------
>> 3 files changed, 33 insertions(+), 8 deletions(-)
>>
>> diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h
>> index 712b3be..4043445 100644
>> --- a/include/net/sctp/command.h
>> +++ b/include/net/sctp/command.h
>> @@ -110,6 +110,7 @@ typedef enum {
>> SCTP_CMD_SEND_NEXT_ASCONF, /* Send the next ASCONF after ACK */
>> SCTP_CMD_PURGE_ASCONF_QUEUE, /* Purge all asconf queues.*/
>> SCTP_CMD_SET_ASOC, /* Restore association context */
>> + SCTP_CMD_GEN_BAD_STREAM, /* Issue an Invalid Stream error */
>> SCTP_CMD_LAST
>> } sctp_verb_t;
>>
>> diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
>> index 1ff51c9..c5a1322 100644
>> --- a/net/sctp/sm_sideeffect.c
>> +++ b/net/sctp/sm_sideeffect.c
>> @@ -1036,6 +1036,22 @@ static void sctp_cmd_send_asconf(struct
>> sctp_association *asoc)
>> }
>> }
>>
>> +static void sctp_cmd_make_inv_stream_err(sctp_cmd_seq_t *commands,
>> + struct sctp_association *asoc,
>> + struct sctp_chunk *chunk,
>> + struct sctp_datahdr *data_hdr)
>> +{
>> + struct sctp_chunk *err;
>> +
>> + err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM,
>> + &data_hdr->stream,
>> + sizeof(data_hdr->stream),
>> + sizeof(u16));
>> + if (err)
>> + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
>> + SCTP_CHUNK(err));
>> +}
>> +
>>
>> /* These three macros allow us to pull the debugging code out of the
>> * main flow of sctp_do_sm() to keep attention focused on the real
>> @@ -1700,6 +1716,12 @@ static int sctp_cmd_interpreter(sctp_event_t
>> event_type,
>> asoc = cmd->obj.asoc;
>> break;
>>
>> + case SCTP_CMD_GEN_BAD_STREAM:
>> + sctp_cmd_make_inv_stream_err(commands,
>> + asoc, chunk,
>> + (struct sctp_datahdr *)cmd->obj.ptr);
>> + break;
>> +
>> default:
>> pr_warn("Impossible command: %u, %p\n",
>> cmd->verb, cmd->obj.ptr);
>> diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
>> index 891f5db..57532e3 100644
>> --- a/net/sctp/sm_statefuns.c
>> +++ b/net/sctp/sm_statefuns.c
>> @@ -2972,6 +2972,12 @@ discard_noforce:
>> if (chunk->end_of_packet)
>> sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, force);
>>
>> + /* Queue the INVALID STREAM error after the SACK if one is needed. */
>> + if (SCTP_IERROR_BAD_STREAM == error) {
>> + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_BAD_STREAM,
>> + SCTP_PTR(chunk->subh.data_hdr));
>> + }
>> +
>> return SCTP_DISPOSITION_DISCARD;
>> consume:
>> return SCTP_DISPOSITION_CONSUME;
>> @@ -3044,6 +3050,10 @@ sctp_disposition_t
>> sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep,
>> */
>> sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SHUTDOWN, SCTP_NULL());
>> sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE());
>> + if (SCTP_IERROR_BAD_STREAM == error) {
>> + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_BAD_STREAM,
>> + SCTP_PTR(chunk->subh.data_hdr));
>> + }
>> sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART,
>> SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN));
>> }
>> @@ -6140,14 +6150,6 @@ static int sctp_eat_data(const struct
>> sctp_association *asoc,
>> if (sid >= asoc->c.sinit_max_instreams) {
>> /* Mark tsn as received even though we drop it */
>> sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn));
>> -
>> - err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM,
>> - &data_hdr->stream,
>> - sizeof(data_hdr->stream),
>> - sizeof(u16));
>> - if (err)
>> - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
>> - SCTP_CHUNK(err));
>> return SCTP_IERROR_BAD_STREAM;
>> }
>>
>> -- 1.7.7.6
>>
>>
>>
>
^ permalink raw reply
* Re: [PATCH] [XFRM] Fix unexpected SA hard expiration after changing date
From: David Miller @ 2012-07-30 6:15 UTC (permalink / raw)
To: fdu; +Cc: herbert, netdev
In-Reply-To: <1343356759-24767-2-git-send-email-fdu@windriver.com>
From: Fan Du <fdu@windriver.com>
Date: Fri, 27 Jul 2012 10:39:19 +0800
> + x->curlft.add_time = now - x->saved_tmo - 1;
> + tmo = x->lft.hard_add_expires_seconds - x->saved_tmo;
> +
^^^^^^^^^^^^^^^^
> + } else
Unnecessary empty line, please remove it.
^ permalink raw reply
* Re: [PATCH] qlge: Add offload features to vlan interfaces
From: David Miller @ 2012-07-30 6:18 UTC (permalink / raw)
To: brenohl; +Cc: jitendra.kalsaria, ron.mercer, netdev
In-Reply-To: <1343415292-49323-1-git-send-email-brenohl@br.ibm.com>
From: brenohl@br.ibm.com
Date: Fri, 27 Jul 2012 13:54:52 -0500
> This patch fills the net_device vlan_features with the proper hardware features,
> thus, improving the vlan interface performance.
>
> With the patch applied, I can see around 148% improvement on a TCP_STREAM test,
> from 3.5 Gb/s to 8.7 Gb/s. On TCP_RR, I see a 11% improvement, from 18k
> to 20. The CPU utilization is almost the same on both cases, from the comparison
> above.
>
> Signed-off-by: Breno Leitao <brenohl@br.ibm.com>
Applied.
^ permalink raw reply
* Re: [PATCH] ipv4: fix debug info in tnode_new
From: David Miller @ 2012-07-30 6:19 UTC (permalink / raw)
To: mlin; +Cc: netdev
In-Reply-To: <1343560795.5975.1.camel@monkey32>
From: Lin Ming <mlin@ss.pku.edu.cn>
Date: Sun, 29 Jul 2012 19:19:55 +0800
> It should print size of struct rt_trie_node * allocated instead of size
> of struct rt_trie_node.
>
> Signed-off-by: Lin Ming <mlin@ss.pku.edu.cn>
Applied.
^ permalink raw reply
* Re: [PATCH] ipv4: clean up put_child
From: David Miller @ 2012-07-30 6:19 UTC (permalink / raw)
To: mlin; +Cc: netdev
In-Reply-To: <1343563203.16365.1.camel@monkey32>
From: Lin Ming <mlin@ss.pku.edu.cn>
Date: Sun, 29 Jul 2012 20:00:03 +0800
> The first parameter struct trie *t is not used anymore.
> Remove it.
>
> Signed-off-by: Lin Ming <mlin@ss.pku.edu.cn>
Applied.
^ permalink raw reply
* Re: [PATCH] bnx2x: remove cast around the kmalloc in bnx2x_prev_mark_path
From: David Miller @ 2012-07-30 6:19 UTC (permalink / raw)
To: develkernel412222; +Cc: eilong, netdev
In-Reply-To: <1343567963-12389-1-git-send-email-develkernel412222@gmail.com>
From: Devendra Naga <develkernel412222@gmail.com>
Date: Sun, 29 Jul 2012 19:04:23 +0545
> casting the void pointer is redundant (Documentation/CodingStyle)
>
> Signed-off-by: Devendra Naga <develkernel412222@gmail.com>
Applied.
^ permalink raw reply
* Re: [PATCH] seeq: use PTR_RET at init_module of driver
From: David Miller @ 2012-07-30 6:19 UTC (permalink / raw)
To: develkernel412222; +Cc: jeffrey.t.kirsher, dhowells, jpirko, netdev, netdev
In-Reply-To: <1343568527-12729-1-git-send-email-develkernel412222@gmail.com>
From: Devendra Naga <develkernel412222@gmail.com>
Date: Sun, 29 Jul 2012 19:13:47 +0545
> the driver sees wether the dev_seeq pointer is having a error that can be
> read by using the PTR_ERR, and returns it at error case, other wise 0 at
> success case.
>
> the PTR_RET does the same thing, and use PTR_RET instead of redoing the
> code of PTR_RET
>
> Signed-off-by: Devendra Naga <develkernel412222@gmail.com>
Applied.
^ permalink raw reply
* Re: [PATCH 1/1] mISDN: Bugfix only few bytes are transfered on a connection
From: David Miller @ 2012-07-30 6:19 UTC (permalink / raw)
To: keil; +Cc: netdev, tobias.powalowski
In-Reply-To: <1343582113-14752-2-git-send-email-keil@b1-systems.de>
From: Karsten Keil <keil@b1-systems.de>
Date: Sun, 29 Jul 2012 19:15:13 +0200
> The test for the fillempty condition was wrong in one place.
> Changed the variable to the right boolean type.
>
> Signed-off-by: Karsten Keil <keil@b1-systems.de>
Applied.
^ permalink raw reply
* Re: [PATCH v4] ipv6: fix incorrect route 'expires' value passed to userspace
From: David Miller @ 2012-07-30 6:20 UTC (permalink / raw)
To: lw; +Cc: eric.dumazet, netdev
In-Reply-To: <1343613690-3236-1-git-send-email-lw@cn.fujitsu.com>
From: Li Wei <lw@cn.fujitsu.com>
Date: Mon, 30 Jul 2012 10:01:30 +0800
> When userspace use RTM_GETROUTE to dump route table, with an already
> expired route entry, we always got an 'expires' value(2147157)
> calculated base on INT_MAX.
>
> The reason of this problem is in the following satement:
> rt->dst.expires - jiffies < INT_MAX
> gcc promoted the type of both sides of '<' to unsigned long, thus
> a small negative value would be considered greater than INT_MAX.
>
> With the help of Eric Dumazet, do the out of bound checks in
> rtnl_put_cacheinfo(), _after_ conversion to clock_t.
>
> Signed-off-by: Li Wei <lw@cn.fujitsu.com>
Applied.
^ permalink raw reply
* Re: [PATCH net 1/5] tg3: Request APE_LOCK_PHY before PHY access
From: David Miller @ 2012-07-30 6:20 UTC (permalink / raw)
To: mchan; +Cc: netdev
In-Reply-To: <1343625345-5155-1-git-send-email-mchan@broadcom.com>
All 5 patches applied, thanks Michael.
^ permalink raw reply
* Re: [PATCH v2] net/tun: fix ioctl() based info leaks
From: David Miller @ 2012-07-30 6:20 UTC (permalink / raw)
To: minipli; +Cc: richard.weinberger, netdev
In-Reply-To: <1343627114-17899-1-git-send-email-minipli@googlemail.com>
From: Mathias Krause <minipli@googlemail.com>
Date: Mon, 30 Jul 2012 07:45:14 +0200
> The tun module leaks up to 36 bytes of memory by not fully initializing
> a structure located on the stack that gets copied to user memory by the
> TUNGETIFF and SIOCGIFHWADDR ioctl()s.
>
> Signed-off-by: Mathias Krause <minipli@googlemail.com>
Applied and queued up for -stable, thanks.
^ permalink raw reply
* Re: [PATCH v2] net/tun: fix ioctl() based info leaks
From: Eric Dumazet @ 2012-07-30 6:22 UTC (permalink / raw)
To: Mathias Krause; +Cc: David S. Miller, richard -rw- weinberger, netdev
In-Reply-To: <1343627114-17899-1-git-send-email-minipli@googlemail.com>
On Mon, 2012-07-30 at 07:45 +0200, Mathias Krause wrote:
> The tun module leaks up to 36 bytes of memory by not fully initializing
> a structure located on the stack that gets copied to user memory by the
> TUNGETIFF and SIOCGIFHWADDR ioctl()s.
>
> Signed-off-by: Mathias Krause <minipli@googlemail.com>
> ---
> v2:
> - removed braces around else branch
> - minor adjustment of the commit message
>
> drivers/net/tun.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
> index 987aeef..01255ff 100644
> --- a/drivers/net/tun.c
> +++ b/drivers/net/tun.c
> @@ -1252,9 +1252,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
> int vnet_hdr_sz;
> int ret;
>
> - if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
> + if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) {
> if (copy_from_user(&ifr, argp, ifreq_len))
> return -EFAULT;
> + } else
> + memset(&ifr, 0, sizeof(ifr));
>
> if (cmd == TUNGETFEATURES) {
> /* Currently this just means: "what IFF flags are valid?".
Actually braces were better
vi +169 Documentation/CodingStyle
This does not apply if only one branch of a conditional statement is a
single
statement; in the latter case use braces in both branches:
if (condition) {
do_this();
do_that();
} else {
otherwise();
}
^ permalink raw reply
* Re: [net-next RFC V5 3/5] virtio: intorduce an API to set affinity for a virtqueue
From: Paolo Bonzini @ 2012-07-30 6:27 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: krkumar2, habanero, kvm, netdev, mashirle, linux-kernel,
virtualization, edumazet, tahm, jwhan, davem, sri
In-Reply-To: <20120729204030.GB13802@redhat.com>
Il 29/07/2012 22:40, Michael S. Tsirkin ha scritto:
>> > Did you set the affinity manually in your experiments, or perhaps there
>> > is a difference between scsi and networking... (interrupt mitigation?)
>
> You need to run irqbalancer in guest to make it actually work. Do you?
Yes, of course, now on to debugging that one. I just wanted to ask
before the weekend if I was missing something as obvious as that.
Paolo
^ permalink raw reply
* Re: [PATCH v2] net/tun: fix ioctl() based info leaks
From: David Miller @ 2012-07-30 6:34 UTC (permalink / raw)
To: eric.dumazet; +Cc: minipli, richard.weinberger, netdev
In-Reply-To: <1343629340.2626.13268.camel@edumazet-glaptop>
From: Eric Dumazet <eric.dumazet@gmail.com>
Date: Mon, 30 Jul 2012 08:22:20 +0200
> On Mon, 2012-07-30 at 07:45 +0200, Mathias Krause wrote:
>> The tun module leaks up to 36 bytes of memory by not fully initializing
>> a structure located on the stack that gets copied to user memory by the
>> TUNGETIFF and SIOCGIFHWADDR ioctl()s.
>>
>> Signed-off-by: Mathias Krause <minipli@googlemail.com>
>> ---
>> v2:
>> - removed braces around else branch
>> - minor adjustment of the commit message
>>
>> drivers/net/tun.c | 4 +++-
>> 1 file changed, 3 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/net/tun.c b/drivers/net/tun.c
>> index 987aeef..01255ff 100644
>> --- a/drivers/net/tun.c
>> +++ b/drivers/net/tun.c
>> @@ -1252,9 +1252,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,
>> int vnet_hdr_sz;
>> int ret;
>>
>> - if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89)
>> + if (cmd == TUNSETIFF || _IOC_TYPE(cmd) == 0x89) {
>> if (copy_from_user(&ifr, argp, ifreq_len))
>> return -EFAULT;
>> + } else
>> + memset(&ifr, 0, sizeof(ifr));
>>
>> if (cmd == TUNGETFEATURES) {
>> /* Currently this just means: "what IFF flags are valid?".
>
>
> Actually braces were better
>
> vi +169 Documentation/CodingStyle
>
> This does not apply if only one branch of a conditional statement is a
> single
> statement; in the latter case use braces in both branches:
>
> if (condition) {
> do_this();
> do_that();
> } else {
> otherwise();
> }
Ok I'll fix this up myself.
^ permalink raw reply
* Surprise! A new friend is searching 4 you;)
From: Roseanna Vida @ 2012-07-30 6:50 UTC (permalink / raw)
To: netdev@vger.kernel.org
Hello gorgeous!
I really want to make u my friend!
My name is Roseanna.
Waiting for your answer! (:
^ permalink raw reply
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